_listing.html (9006B)
1 {#- 2 Page fragments shared by the server-rendered pages and the client-side "instant folder" renderer 3 (static/fasttree.js). Each fragment is written once, here. The server calls these macros with real 4 values; /api/shell calls them with @@placeholders@@ that the browser fills in from the cached tree. 5 Change markup here and both stay in step (tests/test_fast_tree.py checks they do). 6 7 Rules for editing: keep every macro's signature; only put data in via the macro arguments; keep the 8 hooks the scripts use (ids, data-name / data-full-name / data-path, data-action). 9 -#} 10 {% from "_icons.html" import icon %} 11 12 {% macro root_header() %} 13 <section class="hero"> 14 <h1>A small file shelf for <a href="https://amit.is-a.dev">my</a> projects.</h1> 15 <p class="site-notice" role="note"> 16 {{ icon('info', 16) }} 17 <span>Files here can now be synced straight from GitHub on every push — click 18 <a href="/gh-sync">Sync</a> at the top to see how to set it up. Project assets live here; 19 files and paths may change over time, so use direct links with that in mind.</span> 20 <button type="button" class="notice-close" data-action="dismiss-notice" aria-label="Dismiss this notice" title="Dismiss">{{ icon('close', 14, 2.5) }}</button> 21 </p> 22 </section> 23 {% endmacro %} 24 25 {% macro count(n, word, s) %}<strong>{{ n }}</strong> {{ word }}{{ s }}{% endmacro %} 26 27 {% macro counts(dir_count, file_count) %} 28 {% if dir_count > 0 %} 29 {{ count(dir_count, 'folder', 's' if dir_count != 1 else '') }} 30 {% endif %} 31 {% if dir_count > 0 and file_count > 0 %} & {% endif %} 32 {% if file_count > 0 %} 33 {{ count(file_count, 'file', 's' if file_count != 1 else '') }} 34 {% endif %} 35 {% if dir_count == 0 and file_count == 0 %} 36 0 items 37 {% endif %} 38 {% endmacro %} 39 40 {% macro toolbar(counts_html, path) %} 41 <div class="listing-toolbar"> 42 <p class="listing-toolbar__counts"> 43 {{ counts_html }} 44 </p> 45 <div class="listing-toolbar__actions"> 46 <label class="select-wrap" title="Sort this folder"> 47 {{ icon('sort', 14) }} 48 <span class="sr-only">Sort by</span> 49 <select id="sort-select" class="select" data-action="sort"> 50 <option value="default">Default</option> 51 <option value="name">Name A–Z</option> 52 <option value="name-desc">Name Z–A</option> 53 <option value="size-desc">Largest</option> 54 <option value="size-asc">Smallest</option> 55 </select> 56 </label> 57 <button id="download-zip-btn" type="button" class="btn" data-path="{{ path }}" title="Download everything in this folder as a .zip" aria-label="Download everything in this folder as a .zip"> 58 <svg id="zip-spinner" class="spin-anim" style="display: none;" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg> 59 {{ icon('download') }} 60 <span id="zip-btn-text"><span class="zip-long">Download all (.zip)</span><span class="zip-short">.zip</span></span> 61 </button> 62 </div> 63 </div> 64 {% endmacro %} 65 66 {% macro go_up(parent_path) %} 67 <div class="list-item" data-name=".."> 68 <div class="item-name"> 69 <svg class="folder-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="m15 18-6-6 6-6"/></svg> 70 <a href="{{ parent_path }}" class="folder-link">Go up a directory</a> 71 </div> 72 </div> 73 {% endmacro %} 74 75 {% macro row_dir(name, path) %} 76 <div class="list-item" data-name="{{ name }}"> 77 <div class="item-name"> 78 <svg class="folder-icon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg> 79 <a href="/{{ path }}" class="folder-link" data-full-name="{{ name }}/">{{ name }}/</a> 80 </div> 81 <div class="item-meta"> 82 </div> 83 </div> 84 {% endmacro %} 85 86 {% macro row_file(name, path, size_str) %} 87 <div class="list-item" data-name="{{ name }}"> 88 <div class="item-name"> 89 <svg class="file-icon-list" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14.5 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7.5L14.5 2z"/><polyline points="14 2 14 8 20 8"/></svg> 90 <a href="/{{ path }}" class="file-link" data-full-name="{{ name }}">{{ name }}</a> 91 </div> 92 <div class="item-meta"> 93 <span class="size-label">{{ size_str }}</span> 94 <a href="/api/get/{{ path }}" class="download-icon" title="Download" aria-label="Download {{ name }}" data-turbo="false"> 95 <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M12 15V3"/><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><path d="m7 10 5 5 5-5"/></svg> 96 </a> 97 </div> 98 </div> 99 {% endmacro %} 100 101 {% macro empty_state() %} 102 <div class="empty-state"> 103 <svg width="40" height="40" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg> 104 <p>This folder is empty.</p> 105 <a href="/new" class="btn btn-sm">Upload a file</a> 106 </div> 107 {% endmacro %} 108 109 {% macro footer_stats(n, s, size_str) %} 110 <div class="footer-stats" title="Across the whole repo"> 111 <strong>{{ n }}</strong> file{{ s }} · {{ size_str }} total 112 </div> 113 {% endmacro %} 114 115 {% macro crumb_container(inner, cdn_base, tail) %} 116 <div class="breadcrumb-container" data-cdn-base="{{ cdn_base }}"> 117 <nav class="breadcrumb" id="breadcrumb" aria-label="Breadcrumb"> 118 {{ inner }} 119 {{ tail }} 120 </nav> 121 </div> 122 {% endmacro %} 123 124 {% macro crumb_link(href, name) %}<a href="{{ href }}">{{ name }}</a>{% endmacro %} 125 126 {% macro crumb_collapsed(menu) %} 127 <div class="breadcrumb-collapsed"> 128 <button type="button" class="breadcrumb-ellipsis" data-action="crumb-menu" aria-expanded="false" aria-label="Show hidden path segments">…</button> 129 <div class="breadcrumb-menu"> 130 {{ menu }} 131 </div> 132 </div> 133 {% endmacro %} 134 135 {% macro copy_btn(path) %} 136 <button type="button" class="copy-path-btn" title="Copy link to this location" aria-label="Copy link to this location" data-action="copy-path" data-path="{{ path }}"> 137 <svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg> 138 </button> 139 {% endmacro %} 140 141 {% macro visit_script(fields) %} 142 (function () { 143 try { 144 var KEY = 'cdn_visited_v1'; 145 var MAX = 50; 146 var entry = { 147 {{ fields }} 148 ts: Date.now() 149 }; 150 var entries = JSON.parse(localStorage.getItem(KEY) || '[]').filter(function (e) { return e.path !== entry.path; }); 151 entries.unshift(entry); 152 localStorage.setItem(KEY, JSON.stringify(entries.slice(0, MAX))); 153 } catch (e) {} 154 })(); 155 {% endmacro %}