aboutsummaryrefslogtreecommitdiff
path: root/browse.html
diff options
context:
space:
mode:
Diffstat (limited to 'browse.html')
-rw-r--r--browse.html1181
1 files changed, 1181 insertions, 0 deletions
diff --git a/browse.html b/browse.html
new file mode 100644
index 0000000..45d3915
--- /dev/null
+++ b/browse.html
@@ -0,0 +1,1181 @@
1{{ $nonce := uuidv4 -}}
2{{ $nonceAttribute := print "nonce=" (quote $nonce) -}}
3{{ $csp := printf "default-src 'none'; img-src 'self' data:; object-src 'none'; base-uri 'none'; script-src 'nonce-%s'; style-src 'nonce-%s' fonts.googleapis.com; font-src fonts.gstatic.com; frame-ancestors 'none'; form-action 'none';" $nonce $nonce -}}
4{{/* To disable the Content-Security-Policy, set this to false */}}{{ $enableCsp := true -}}
5{{ if $enableCsp -}}
6 {{- .RespHeader.Set "Content-Security-Policy" $csp -}}
7{{ end -}}
8{{- /* Helper: emit &limit=…&offset=… only when set. Use as {{template "qs" .}} */ -}}
9{{- define "qs" -}}
10{{- if ne 0 .Limit}}&limit={{.Limit}}{{end -}}
11{{- if ne 0 .Offset}}&offset={{.Offset}}{{end -}}
12{{- end -}}
13{{- define "icon"}}
14 {{- if .IsDir}}
15 {{- if .IsSymlink}}
16 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-folder-filled" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
17 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
18 <path d="M9 3a1 1 0 0 1 .608 .206l.1 .087l2.706 2.707h6.586a3 3 0 0 1 2.995 2.824l.005 .176v8a3 3 0 0 1 -2.824 2.995l-.176 .005h-14a3 3 0 0 1 -2.995 -2.824l-.005 -.176v-11a3 3 0 0 1 2.824 -2.995l.176 -.005h4z" stroke-width="0" fill="currentColor"/>
19 <path fill="var(--color-surface-container)" d="M2.795 17.306c0-2.374 1.792-4.314 4.078-4.538v-1.104a.38.38 0 0 1 .651-.272l2.45 2.492a.132.132 0 0 1 0 .188l-2.45 2.492a.381.381 0 0 1-.651-.272V15.24c-1.889.297-3.436 1.39-3.817 3.26a2.809 2.809 0 0 1-.261-1.193Z" stroke-width=".127478"/>
20 </svg>
21 {{- else}}
22 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-folder-filled" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
23 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
24 <path d="M9 3a1 1 0 0 1 .608 .206l.1 .087l2.706 2.707h6.586a3 3 0 0 1 2.995 2.824l.005 .176v8a3 3 0 0 1 -2.824 2.995l-.176 .005h-14a3 3 0 0 1 -2.995 -2.824l-.005 -.176v-11a3 3 0 0 1 2.824 -2.995l.176 -.005h4z" stroke-width="0" fill="currentColor"/>
25 </svg>
26 {{- end}}
27 {{- else if or (eq .Name "LICENSE") (eq .Name "README")}}
28 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-license" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
29 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
30 <path d="M15 21h-9a3 3 0 0 1 -3 -3v-1h10v2a2 2 0 0 0 4 0v-14a2 2 0 1 1 2 2h-2m2 -4h-11a3 3 0 0 0 -3 3v11"/>
31 <path d="M9 7l4 0"/>
32 <path d="M9 11l4 0"/>
33 </svg>
34 {{- else if .HasExt ".jpg" ".jpeg" ".png" ".gif" ".webp" ".tiff" ".bmp" ".heif" ".heic" ".svg" ".avif"}}
35 {{- if eq .Tpl.Layout "grid"}}
36 <img loading="lazy" src="{{.Name | pathEscape}}">
37 {{- else}}
38 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-photo" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
39 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
40 <path d="M15 8h.01"/>
41 <path d="M3 6a3 3 0 0 1 3 -3h12a3 3 0 0 1 3 3v12a3 3 0 0 1 -3 3h-12a3 3 0 0 1 -3 -3v-12z"/>
42 <path d="M3 16l5 -5c.928 -.893 2.072 -.893 3 0l5 5"/>
43 <path d="M14 14l1 -1c.928 -.893 2.072 -.893 3 0l3 3"/>
44 </svg>
45 {{- end}}
46 {{- else if .HasExt ".mp4" ".mov" ".m4v" ".mpeg" ".mpg" ".avi" ".ogg" ".webm" ".mkv" ".vob" ".gifv" ".3gp"}}
47 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-movie" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
48 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
49 <path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v12a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"/>
50 <path d="M8 4l0 16"/>
51 <path d="M16 4l0 16"/>
52 <path d="M4 8l4 0"/>
53 <path d="M4 16l4 0"/>
54 <path d="M4 12l16 0"/>
55 <path d="M16 8l4 0"/>
56 <path d="M16 16l4 0"/>
57 </svg>
58 {{- else if .HasExt ".mp3" ".m4a" ".aac" ".ogg" ".flac" ".wav" ".wma" ".midi" ".cda"}}
59 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-music" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
60 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
61 <path d="M6 17m-3 0a3 3 0 1 0 6 0a3 3 0 1 0 -6 0"/>
62 <path d="M16 17m-3 0a3 3 0 1 0 6 0a3 3 0 1 0 -6 0"/>
63 <path d="M9 17l0 -13l10 0l0 13"/>
64 <path d="M9 8l10 0"/>
65 </svg>
66 {{- else if .HasExt ".pdf"}}
67 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-file-type-pdf" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
68 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
69 <path d="M14 3v4a1 1 0 0 0 1 1h4"/>
70 <path d="M5 12v-7a2 2 0 0 1 2 -2h7l5 5v4"/>
71 <path d="M5 18h1.5a1.5 1.5 0 0 0 0 -3h-1.5v6"/>
72 <path d="M17 18h2"/>
73 <path d="M20 15h-3v6"/>
74 <path d="M11 15v6h1a2 2 0 0 0 2 -2v-2a2 2 0 0 0 -2 -2h-1z"/>
75 </svg>
76 {{- else if .HasExt ".csv" ".tsv"}}
77 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-file-type-csv" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
78 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
79 <path d="M14 3v4a1 1 0 0 0 1 1h4"/>
80 <path d="M5 12v-7a2 2 0 0 1 2 -2h7l5 5v4"/>
81 <path d="M7 16.5a1.5 1.5 0 0 0 -3 0v3a1.5 1.5 0 0 0 3 0"/>
82 <path d="M10 20.25c0 .414 .336 .75 .75 .75h1.25a1 1 0 0 0 1 -1v-1a1 1 0 0 0 -1 -1h-1a1 1 0 0 1 -1 -1v-1a1 1 0 0 1 1 -1h1.25a.75 .75 0 0 1 .75 .75"/>
83 <path d="M16 15l2 6l2 -6"/>
84 </svg>
85 {{- else if .HasExt ".txt" ".doc" ".docx" ".odt" ".fodt" ".rtf"}}
86 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-file-text" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
87 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
88 <path d="M14 3v4a1 1 0 0 0 1 1h4"/>
89 <path d="M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2z"/>
90 <path d="M9 9l1 0"/>
91 <path d="M9 13l6 0"/>
92 <path d="M9 17l6 0"/>
93 </svg>
94 {{- else if .HasExt ".xls" ".xlsx" ".ods" ".fods"}}
95 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-file-spreadsheet" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
96 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
97 <path d="M14 3v4a1 1 0 0 0 1 1h4"/>
98 <path d="M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2z"/>
99 <path d="M8 11h8v7h-8z"/>
100 <path d="M8 15h8"/>
101 <path d="M11 11v7"/>
102 </svg>
103 {{- else if .HasExt ".ppt" ".pptx" ".odp" ".fodp"}}
104 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-presentation-analytics" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
105 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
106 <path d="M9 12v-4"/>
107 <path d="M15 12v-2"/>
108 <path d="M12 12v-1"/>
109 <path d="M3 4h18"/>
110 <path d="M4 4v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2 -2v-10"/>
111 <path d="M12 16v4"/>
112 <path d="M9 20h6"/>
113 </svg>
114 {{- else if .HasExt ".zip" ".gz" ".xz" ".tar" ".7z" ".rar" ".xz" ".zst"}}
115 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-file-zip" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
116 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
117 <path d="M6 20.735a2 2 0 0 1 -1 -1.735v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2h-1"/>
118 <path d="M11 17a2 2 0 0 1 2 2v2a1 1 0 0 1 -1 1h-2a1 1 0 0 1 -1 -1v-2a2 2 0 0 1 2 -2z"/>
119 <path d="M11 5l-1 0"/>
120 <path d="M13 7l-1 0"/>
121 <path d="M11 9l-1 0"/>
122 <path d="M13 11l-1 0"/>
123 <path d="M11 13l-1 0"/>
124 <path d="M13 15l-1 0"/>
125 </svg>
126 {{- else if .HasExt ".deb" ".dpkg"}}
127 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-brand-debian" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
128 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
129 <path d="M12 17c-2.397 -.943 -4 -3.153 -4 -5.635c0 -2.19 1.039 -3.14 1.604 -3.595c2.646 -2.133 6.396 -.27 6.396 3.23c0 2.5 -2.905 2.121 -3.5 1.5c-.595 -.621 -1 -1.5 -.5 -2.5"/>
130 <path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0"/>
131 </svg>
132 {{- else if .HasExt ".rpm" ".exe" ".flatpak" ".appimage" ".jar" ".msi" ".apk"}}
133 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-package" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
134 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
135 <path d="M12 3l8 4.5l0 9l-8 4.5l-8 -4.5l0 -9l8 -4.5"/>
136 <path d="M12 12l8 -4.5"/>
137 <path d="M12 12l0 9"/>
138 <path d="M12 12l-8 -4.5"/>
139 <path d="M16 5.25l-8 4.5"/>
140 </svg>
141 {{- else if .HasExt ".ps1"}}
142 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-brand-powershell" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
143 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
144 <path d="M4.887 20h11.868c.893 0 1.664 -.665 1.847 -1.592l2.358 -12c.212 -1.081 -.442 -2.14 -1.462 -2.366a1.784 1.784 0 0 0 -.385 -.042h-11.868c-.893 0 -1.664 .665 -1.847 1.592l-2.358 12c-.212 1.081 .442 2.14 1.462 2.366c.127 .028 .256 .042 .385 .042z"/>
145 <path d="M9 8l4 4l-6 4"/>
146 <path d="M12 16h3"/>
147 </svg>
148 {{- else if .HasExt ".py" ".pyc" ".pyo"}}
149 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-brand-python" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
150 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
151 <path d="M12 9h-7a2 2 0 0 0 -2 2v4a2 2 0 0 0 2 2h3"/>
152 <path d="M12 15h7a2 2 0 0 0 2 -2v-4a2 2 0 0 0 -2 -2h-3"/>
153 <path d="M8 9v-4a2 2 0 0 1 2 -2h4a2 2 0 0 1 2 2v5a2 2 0 0 1 -2 2h-4a2 2 0 0 0 -2 2v5a2 2 0 0 0 2 2h4a2 2 0 0 0 2 -2v-4"/>
154 <path d="M11 6l0 .01"/>
155 <path d="M13 18l0 .01"/>
156 </svg>
157 {{- else if .HasExt ".bash" ".sh" ".com" ".bat" ".dll" ".so"}}
158 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-script" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
159 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
160 <path d="M17 20h-11a3 3 0 0 1 0 -6h11a3 3 0 0 0 0 6h1a3 3 0 0 0 3 -3v-11a2 2 0 0 0 -2 -2h-10a2 2 0 0 0 -2 2v8"/>
161 </svg>
162 {{- else if .HasExt ".dmg"}}
163 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-brand-finder" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
164 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
165 <path d="M3 4m0 1a1 1 0 0 1 1 -1h16a1 1 0 0 1 1 1v14a1 1 0 0 1 -1 1h-16a1 1 0 0 1 -1 -1z"/>
166 <path d="M7 8v1"/>
167 <path d="M17 8v1"/>
168 <path d="M12.5 4c-.654 1.486 -1.26 3.443 -1.5 9h2.5c-.19 2.867 .094 5.024 .5 7"/>
169 <path d="M7 15.5c3.667 2 6.333 2 10 0"/>
170 </svg>
171 {{- else if .HasExt ".iso" ".img"}}
172 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-disc" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
173 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
174 <path d="M12 12m-9 0a9 9 0 1 0 18 0a9 9 0 1 0 -18 0"/>
175 <path d="M12 12m-1 0a1 1 0 1 0 2 0a1 1 0 1 0 -2 0"/>
176 <path d="M7 12a5 5 0 0 1 5 -5"/>
177 <path d="M12 17a5 5 0 0 0 5 -5"/>
178 </svg>
179 {{- else if .HasExt ".md" ".mdown" ".markdown"}}
180 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-markdown" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
181 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
182 <path d="M3 5m0 2a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v10a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2z"/>
183 <path d="M7 15v-6l2 2l2 -2v6"/>
184 <path d="M14 13l2 2l2 -2m-2 2v-6"/>
185 </svg>
186 {{- else if .HasExt ".ttf" ".otf" ".woff" ".woff2" ".eof"}}
187 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-file-typography" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
188 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
189 <path d="M14 3v4a1 1 0 0 0 1 1h4"/>
190 <path d="M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2z"/>
191 <path d="M11 18h2"/>
192 <path d="M12 18v-7"/>
193 <path d="M9 12v-1h6v1"/>
194 </svg>
195 {{- else if .HasExt ".go"}}
196 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-brand-golang" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
197 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
198 <path d="M15.695 14.305c1.061 1.06 2.953 .888 4.226 -.384c1.272 -1.273 1.444 -3.165 .384 -4.226c-1.061 -1.06 -2.953 -.888 -4.226 .384c-1.272 1.273 -1.444 3.165 -.384 4.226z"/>
199 <path d="M12.68 9.233c-1.084 -.497 -2.545 -.191 -3.591 .846c-1.284 1.273 -1.457 3.165 -.388 4.226c1.07 1.06 2.978 .888 4.261 -.384a3.669 3.669 0 0 0 1.038 -1.921h-2.427"/>
200 <path d="M5.5 15h-1.5"/>
201 <path d="M6 9h-2"/>
202 <path d="M5 12h-3"/>
203 </svg>
204 {{- else if .HasExt ".html" ".htm"}}
205 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-file-type-html" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
206 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
207 <path d="M14 3v4a1 1 0 0 0 1 1h4"/>
208 <path d="M5 12v-7a2 2 0 0 1 2 -2h7l5 5v4"/>
209 <path d="M2 21v-6"/>
210 <path d="M5 15v6"/>
211 <path d="M2 18h3"/>
212 <path d="M20 15v6h2"/>
213 <path d="M13 21v-6l2 3l2 -3v6"/>
214 <path d="M7.5 15h3"/>
215 <path d="M9 15v6"/>
216 </svg>
217 {{- else if .HasExt ".js"}}
218 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-file-type-js" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
219 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
220 <path d="M14 3v4a1 1 0 0 0 1 1h4"/>
221 <path d="M3 15h3v4.5a1.5 1.5 0 0 1 -3 0"/>
222 <path d="M9 20.25c0 .414 .336 .75 .75 .75h1.25a1 1 0 0 0 1 -1v-1a1 1 0 0 0 -1 -1h-1a1 1 0 0 1 -1 -1v-1a1 1 0 0 1 1 -1h1.25a.75 .75 0 0 1 .75 .75"/>
223 <path d="M5 12v-7a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2h-1"/>
224 </svg>
225 {{- else if .HasExt ".css"}}
226 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-file-type-css" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
227 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
228 <path d="M14 3v4a1 1 0 0 0 1 1h4"/>
229 <path d="M5 12v-7a2 2 0 0 1 2 -2h7l5 5v4"/>
230 <path d="M8 16.5a1.5 1.5 0 0 0 -3 0v3a1.5 1.5 0 0 0 3 0"/>
231 <path d="M11 20.25c0 .414 .336 .75 .75 .75h1.25a1 1 0 0 0 1 -1v-1a1 1 0 0 0 -1 -1h-1a1 1 0 0 1 -1 -1v-1a1 1 0 0 1 1 -1h1.25a.75 .75 0 0 1 .75 .75"/>
232 <path d="M17 20.25c0 .414 .336 .75 .75 .75h1.25a1 1 0 0 0 1 -1v-1a1 1 0 0 0 -1 -1h-1a1 1 0 0 1 -1 -1v-1a1 1 0 0 1 1 -1h1.25a.75 .75 0 0 1 .75 .75"/>
233 </svg>
234 {{- else if .HasExt ".json" ".json5" ".jsonc"}}
235 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-json" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
236 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
237 <path d="M20 16v-8l3 8v-8"/>
238 <path d="M15 8a2 2 0 0 1 2 2v4a2 2 0 1 1 -4 0v-4a2 2 0 0 1 2 -2z"/>
239 <path d="M1 8h3v6.5a1.5 1.5 0 0 1 -3 0v-.5"/>
240 <path d="M7 15a1 1 0 0 0 1 1h1a1 1 0 0 0 1 -1v-2a1 1 0 0 0 -1 -1h-1a1 1 0 0 1 -1 -1v-2a1 1 0 0 1 1 -1h1a1 1 0 0 1 1 1"/>
241 </svg>
242 {{- else if .HasExt ".ts"}}
243 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-file-type-ts" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
244 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
245 <path d="M14 3v4a1 1 0 0 0 1 1h4"/>
246 <path d="M5 12v-7a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2h-1"/>
247 <path d="M14 3v4a1 1 0 0 0 1 1h4"/>
248 <path d="M9 20.25c0 .414 .336 .75 .75 .75h1.25a1 1 0 0 0 1 -1v-1a1 1 0 0 0 -1 -1h-1a1 1 0 0 1 -1 -1v-1a1 1 0 0 1 1 -1h1.25a.75 .75 0 0 1 .75 .75"/>
249 <path d="M3.5 15h3"/>
250 <path d="M5 15v6"/>
251 </svg>
252 {{- else if .HasExt ".sql"}}
253 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-file-type-sql" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
254 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
255 <path d="M14 3v4a1 1 0 0 0 1 1h4"/>
256 <path d="M14 3v4a1 1 0 0 0 1 1h4"/>
257 <path d="M5 20.25c0 .414 .336 .75 .75 .75h1.25a1 1 0 0 0 1 -1v-1a1 1 0 0 0 -1 -1h-1a1 1 0 0 1 -1 -1v-1a1 1 0 0 1 1 -1h1.25a.75 .75 0 0 1 .75 .75"/>
258 <path d="M5 12v-7a2 2 0 0 1 2 -2h7l5 5v4"/>
259 <path d="M18 15v6h2"/>
260 <path d="M13 15a2 2 0 0 1 2 2v2a2 2 0 1 1 -4 0v-2a2 2 0 0 1 2 -2z"/>
261 <path d="M14 20l1.5 1.5"/>
262 </svg>
263 {{- else if .HasExt ".db" ".sqlite" ".bak" ".mdb"}}
264 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-database" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
265 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
266 <path d="M12 6m-8 0a8 3 0 1 0 16 0a8 3 0 1 0 -16 0"/>
267 <path d="M4 6v6a8 3 0 0 0 16 0v-6"/>
268 <path d="M4 12v6a8 3 0 0 0 16 0v-6"/>
269 </svg>
270 {{- else if .HasExt ".eml" ".email" ".mailbox" ".mbox" ".msg"}}
271 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-mail" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
272 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
273 <path d="M3 7a2 2 0 0 1 2 -2h14a2 2 0 0 1 2 2v10a2 2 0 0 1 -2 2h-14a2 2 0 0 1 -2 -2v-10z"/>
274 <path d="M3 7l9 6l9 -6"/>
275 </svg>
276 {{- else if .HasExt ".crt" ".pem" ".x509" ".cer" ".ca-bundle"}}
277 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-certificate" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
278 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
279 <path d="M15 15m-3 0a3 3 0 1 0 6 0a3 3 0 1 0 -6 0"/>
280 <path d="M13 17.5v4.5l2 -1.5l2 1.5v-4.5"/>
281 <path d="M10 19h-5a2 2 0 0 1 -2 -2v-10c0 -1.1 .9 -2 2 -2h14a2 2 0 0 1 2 2v10a2 2 0 0 1 -1 1.73"/>
282 <path d="M6 9l12 0"/>
283 <path d="M6 12l3 0"/>
284 <path d="M6 15l2 0"/>
285 </svg>
286 {{- else if .HasExt ".key" ".keystore" ".jks" ".p12" ".pfx" ".pub"}}
287 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-key" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
288 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
289 <path d="M16.555 3.843l3.602 3.602a2.877 2.877 0 0 1 0 4.069l-2.643 2.643a2.877 2.877 0 0 1 -4.069 0l-.301 -.301l-6.558 6.558a2 2 0 0 1 -1.239 .578l-.175 .008h-1.172a1 1 0 0 1 -.993 -.883l-.007 -.117v-1.172a2 2 0 0 1 .467 -1.284l.119 -.13l.414 -.414h2v-2h2v-2l2.144 -2.144l-.301 -.301a2.877 2.877 0 0 1 0 -4.069l2.643 -2.643a2.877 2.877 0 0 1 4.069 0z"/>
290 <path d="M15 9h.01"/>
291 </svg>
292 {{- else}}
293 {{- if .IsSymlink}}
294 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-file-symlink" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
295 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
296 <path d="M4 21v-4a3 3 0 0 1 3 -3h5"/>
297 <path d="M9 17l3 -3l-3 -3"/>
298 <path d="M14 3v4a1 1 0 0 0 1 1h4"/>
299 <path d="M5 11v-6a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2h-9.5"/>
300 </svg>
301 {{- else}}
302 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-file" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
303 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
304 <path d="M14 3v4a1 1 0 0 0 1 1h4"/>
305 <path d="M17 21h-10a2 2 0 0 1 -2 -2v-14a2 2 0 0 1 2 -2h7l5 5v11a2 2 0 0 1 -2 2z"/>
306 </svg>
307 {{- end}}
308 {{- end}}
309{{- end}}
310<!DOCTYPE html>
311<html lang="en">
312 <head>
313 <title>~{{html .Path}}</title>
314 <link rel="canonical" href="{{.Path}}/" />
315 <meta charset="utf-8">
316 <meta name="color-scheme" content="light dark">
317 <meta name="viewport" content="width=device-width, initial-scale=1.0">
318 <link rel="icon" href="data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20viewBox%3D%220%200%20512%20512%22%3E%3Ccircle%20cx%3D%22256%22%20cy%3D%22256%22%20r%3D%22256%22%20fill%3D%22%23cde7ed%22/%3E%3Ctext%20x%3D%2265.45%22%20y%3D%22335.83%22%20font-family%3D%22Noto%20Sans%20Mono%2Cmonospace%22%20font-size%3D%22210.084%22%20fill%3D%22%23051f24%22%3E~/i%3C/text%3E%3C/svg%3E" type="image/svg+xml" />
319 <link rel="stylesheet" {{ $nonceAttribute }} href="https://fonts.googleapis.com/css2?family=Noto+Sans:ital,wdth,wght@0,62.5..100,100..900;1,62.5..100,100..900&display=swap">
320<style {{ $nonceAttribute }}>
321@media (prefers-color-scheme: light) {
322 :root {
323 --color-primary: rgb(0 104 119);
324 --color-on-primary: rgb(255 255 255);
325
326 --color-secondary: rgb(75 98 104);
327
328 --color-surface: rgb(245 250 252);
329 --color-surface-container: rgb(233 239 240);
330 --color-on-surface: rgb(23 29 30);
331 }
332}
333
334@media (prefers-color-scheme: dark) {
335 :root {
336 --color-primary: rgb(131 210 228);
337 --color-on-primary: rgb(0 54 63);
338
339 --color-secondary: rgb(178 203 209);
340
341 --color-surface: rgb(14 20 22);
342 --color-surface-container: rgb(27 33 34);
343 --color-on-surface: rgb(222 227 229);
344 }
345}
346
347* {
348 box-sizing: border-box;
349}
350
351/* Apply theme-change transitions only to top-level coloured surfaces so
352 switching prefers-color-scheme stays smooth without touching every node. */
353body,
354main, section, footer,
355nav.breadcrumbs > a,
356.layout, .sort-control,
357#filter, #summary, #summary b,
358tbody tr, td a, th a {
359 transition: background-color 0.2s ease-in-out, color 0.2s ease-in-out;
360}
361
362html, body {
363 margin: 0;
364 padding: 0;
365}
366
367body {
368 font-family: "Noto Sans", sans-serif;
369 background-color: var(--color-surface);
370 color: var(--color-on-surface);
371 margin: 0 auto;
372 padding: 0 0.5rem;
373 max-width: 60rem;
374 min-height: 100vh;
375}
376
377a {
378 color: var(--color-primary);
379 text-decoration: none;
380}
381
382/* Keyboard-only focus ring. No outline-offset so it sits flush with the
383 element edge — otherwise inputs (which have their own border) appear
384 to have a double ring. */
385a:focus-visible,
386button:focus-visible,
387input:focus-visible {
388 outline: 0.15rem solid var(--color-primary);
389 border-radius: 0.25rem;
390}
391
392svg.icon {
393 vertical-align: middle;
394 flex-shrink: 0;
395}
396
397img {
398 max-width: 100%;
399 max-height: 100%;
400 border-radius: 0.5rem;
401 vertical-align: middle;
402}
403
404td img {
405 max-width: 1.5em;
406 max-height: 2em;
407 object-fit: cover;
408 border-radius: 0.25rem;
409}
410
411/* Breadcrumb-навигация в виде pill-ссылок (как nav на сайте) */
412nav.breadcrumbs {
413 margin-top: 1rem;
414 display: flex;
415 align-items: center;
416 gap: 0.5rem;
417 overflow-x: auto;
418 padding: 0.15rem 0.15rem;
419 scrollbar-width: thin;
420 scrollbar-color: var(--color-secondary) transparent;
421}
422
423nav.breadcrumbs::-webkit-scrollbar {
424 height: 0.3rem;
425}
426
427nav.breadcrumbs::-webkit-scrollbar-thumb {
428 background-color: var(--color-secondary);
429 border-radius: 1rem;
430}
431
432nav.breadcrumbs::-webkit-scrollbar-track {
433 background: transparent;
434}
435
436nav.breadcrumbs > a {
437 background-color: var(--color-surface-container);
438 border-radius: 2rem;
439 padding: 0.5rem 1rem;
440 text-align: center;
441 text-wrap: nowrap;
442 flex-shrink: 0;
443}
444
445nav.breadcrumbs > a:last-child {
446 outline: 0.1rem solid var(--color-primary);
447}
448
449main, section, footer {
450 background-color: var(--color-surface-container);
451 border-radius: 2rem;
452 margin: 1rem 0;
453 padding: 1.5rem;
454}
455
456main > :first-child,
457section > :first-child,
458footer > :first-child { margin-top: 0; }
459
460main > :last-child,
461section > :last-child,
462footer > :last-child { margin-bottom: 0; }
463
464/* ===== Meta-секция (сводка + переключатели + поиск) ===== */
465.meta {
466 display: flex;
467 gap: 1rem;
468 align-items: center;
469 flex-wrap: wrap;
470}
471
472#summary {
473 display: flex;
474 gap: 1rem;
475 align-items: center;
476 margin-right: auto;
477 color: var(--color-on-surface);
478 font-size: 0.95rem;
479}
480
481#summary b { color: var(--color-primary); }
482
483.meta-controls {
484 display: flex;
485 gap: 0.5rem;
486 align-items: center;
487 flex-wrap: wrap;
488}
489
490.layout,
491.sort-control {
492 color: var(--color-secondary);
493 cursor: pointer;
494 display: inline-flex;
495 align-items: center;
496 gap: 0.25rem;
497 padding: 0.4rem 0.7rem;
498 border-radius: 1rem;
499 background: var(--color-surface);
500 font-size: 0.85rem;
501}
502
503/* Reset native button styles so .layout works on <button> and <a> alike. */
504button.layout {
505 border: none;
506 font-family: inherit;
507 line-height: inherit;
508}
509
510.layout svg,
511.sort-control svg {
512 width: 1rem;
513 height: 1rem;
514 flex-shrink: 0;
515}
516
517.layout.current {
518 color: var(--color-primary);
519 outline: 0.1rem solid var(--color-primary);
520}
521
522.filter-container {
523 position: relative;
524 display: inline-flex;
525 align-items: center;
526}
527
528#search-icon {
529 color: var(--color-secondary);
530 position: absolute;
531 left: 0.6rem;
532 width: 1rem;
533 height: 1rem;
534 pointer-events: none;
535}
536
537#filter {
538 padding: 0.45rem 1rem 0.45rem 2rem;
539 border: 0.1rem solid var(--color-primary);
540 border-radius: 1rem;
541 font-family: inherit;
542 color: var(--color-on-surface);
543 background: var(--color-surface);
544 font-size: 0.9rem;
545 outline: none;
546}
547
548/* Match the input's border-radius so the focus ring lines up cleanly. */
549#filter:focus-visible {
550 border-radius: 1rem;
551}
552
553/* ===== Таблица ===== */
554table {
555 width: 100%;
556 border-collapse: collapse;
557}
558
559th, td { text-align: left; }
560
561th {
562 position: sticky;
563 top: 0;
564 background: var(--color-surface-container);
565 white-space: nowrap;
566 z-index: 2;
567 text-transform: uppercase;
568 font-size: 0.8rem;
569 letter-spacing: 0.05rem;
570 padding: 0.75rem 0.5rem;
571 color: var(--color-secondary);
572 border-bottom: 0.1rem solid var(--color-surface);
573}
574
575th a {
576 color: var(--color-on-surface);
577 display: inline-flex;
578 align-items: center;
579 gap: 0.25rem;
580}
581
582th a:hover { color: var(--color-primary); }
583
584th .icon-tabler { width: 1rem; height: 1rem; }
585
586td {
587 white-space: nowrap;
588 padding: 0 0.5rem;
589}
590
591td:nth-child(1) {
592 width: 75%;
593 max-width: 0; /* enables ellipsis on flex child below */
594}
595
596td:nth-child(1) a {
597 padding: 0.75rem 0;
598 display: flex;
599 align-items: center;
600 gap: 0.5rem;
601 min-width: 0;
602}
603
604td:nth-child(1) .icon-tabler {
605 flex-shrink: 0;
606}
607
608td:nth-child(2),
609th:nth-child(2) {
610 padding-right: 1rem;
611 min-width: 8rem;
612}
613
614th .dirfirst {
615 padding: 0;
616 margin-right: 0.25rem;
617 opacity: 0.6;
618}
619
620th .dirfirst:hover { opacity: 1; }
621
622tbody tr:hover {
623 background-color: var(--color-surface);
624}
625
626td a:hover { color: var(--color-secondary); }
627
628td a:visited { color: var(--color-secondary); }
629
630td .go-up {
631 text-transform: uppercase;
632 font-size: 0.75rem;
633 font-weight: bold;
634 letter-spacing: 0.05rem;
635}
636
637.name, .go-up {
638 overflow: hidden;
639 text-overflow: ellipsis;
640 white-space: nowrap;
641 min-width: 0;
642}
643
644.listing .icon-tabler { color: var(--color-secondary); }
645.listing a:hover .icon-tabler { color: var(--color-primary); }
646.listing .icon-tabler-folder-filled { color: var(--color-primary) !important; }
647
648/* Полоса размера */
649.size, .timestamp { font-size: 0.85rem; color: var(--color-secondary); }
650
651.size.dash {
652 text-align: center;
653}
654
655.sizebar {
656 position: relative;
657 padding: 0.25rem 0.5rem;
658 display: flex;
659 border-radius: 0.5rem;
660 overflow: hidden;
661}
662
663.sizebar-bar {
664 background-color: var(--color-surface);
665 position: absolute;
666 top: 0; right: 0; bottom: 0; left: 0;
667 z-index: 0;
668 pointer-events: none;
669 border-radius: 0.5rem;
670}
671
672.sizebar-text {
673 position: relative;
674 z-index: 1;
675 overflow: hidden;
676 text-overflow: ellipsis;
677 white-space: nowrap;
678}
679
680/* ===== Сетка ===== */
681.grid {
682 display: grid;
683 grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
684 gap: 0.75rem;
685}
686
687.grid .entry {
688 position: relative;
689 width: 100%;
690}
691
692.grid .entry a {
693 display: flex;
694 flex-direction: column;
695 align-items: center;
696 justify-content: center;
697 padding: 1rem;
698 height: 100%;
699 background-color: var(--color-surface);
700 border-radius: 1rem;
701 color: var(--color-on-surface);
702 gap: 0.5rem;
703}
704
705.grid .entry a:hover {
706 outline: 0.1rem solid var(--color-primary);
707 color: var(--color-primary);
708}
709
710.grid .entry svg {
711 width: 3rem;
712 height: 3rem;
713}
714
715.grid .entry img {
716 max-height: 10rem;
717 object-fit: cover;
718 border-radius: 0.5rem;
719}
720
721.grid .entry .name {
722 margin-top: 0.25rem;
723 text-align: center;
724 font-size: 0.9rem;
725 width: 100%;
726}
727
728.grid .entry .size {
729 font-size: 0.75rem;
730 color: var(--color-secondary);
731}
732
733/* ===== Empty state ===== */
734.empty-state {
735 display: flex;
736 flex-direction: column;
737 align-items: center;
738 justify-content: center;
739 padding: 2rem 1rem;
740 gap: 0.5rem;
741 color: var(--color-secondary);
742 text-align: center;
743 grid-column: 1 / -1; /* span full width in grid layout */
744}
745
746.empty-state svg {
747 width: 3rem;
748 height: 3rem;
749 opacity: 0.6;
750}
751
752/* ===== Footer ===== */
753footer.site-footer {
754 background: none;
755 border-radius: 0;
756 padding: 0.75rem 0 1.5rem;
757 margin: 0.5rem 0 0;
758 text-align: center;
759 font-size: 0.7rem;
760 color: var(--color-secondary);
761}
762
763footer.site-footer a {
764 color: inherit;
765}
766
767@media (max-width: 600px) {
768 .hideable { display: none; }
769 td:nth-child(1) { width: auto; }
770 th:nth-child(2),
771 td:nth-child(2) {
772 padding-right: 0.5rem;
773 text-align: right;
774 }
775 #filter { max-width: 7rem; }
776 main, section, footer { padding: 1rem; border-radius: 1.5rem; }
777 nav.breadcrumbs > a { padding: 0.4rem 0.75rem; font-size: 0.9rem; }
778}
779</style>
780{{- if eq .Layout "grid"}}
781<style {{ $nonceAttribute }}>body { max-width: 80rem; }</style>
782{{- end}}
783</head>
784<body>
785 <nav class="breadcrumbs">
786 {{- range $i, $crumb := .Breadcrumbs}}
787 <a href="{{html $crumb.Link}}">{{html $crumb.Text}}</a>
788 {{- end}}
789 </nav>
790
791 <section>
792 <div class="meta">
793 <div id="summary">
794 <span class="meta-item">
795 <b>{{.NumDirs}}</b> director{{if eq 1 .NumDirs}}y{{else}}ies{{end}}
796 </span>
797 <span class="meta-item">
798 <b>{{.NumFiles}}</b> file{{if ne 1 .NumFiles}}s{{end}}
799 </span>
800 <span class="meta-item">
801 <b>{{.HumanTotalFileSize}}</b> total
802 </span>
803 {{- if ne 0 .Limit}}
804 <span class="meta-item">
805 (showing <b>{{.Limit}}</b>)
806 </span>
807 {{- end}}
808 </div>
809 <div class="meta-controls">
810 <button type="button" id="layout-list" class='layout{{if eq $.Layout "list" ""}} current{{end}}' title="List view" aria-label="List view"{{if eq $.Layout "list" ""}} aria-pressed="true"{{else}} aria-pressed="false"{{end}}>
811 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-layout-list" width="16" height="16" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
812 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
813 <path d="M4 4m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v2a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"/>
814 <path d="M4 14m0 2a2 2 0 0 1 2 -2h12a2 2 0 0 1 2 2v2a2 2 0 0 1 -2 2h-12a2 2 0 0 1 -2 -2z"/>
815 </svg>
816 List
817 </button>
818 <button type="button" id="layout-grid" class='layout{{if eq $.Layout "grid"}} current{{end}}' title="Grid view" aria-label="Grid view"{{if eq $.Layout "grid"}} aria-pressed="true"{{else}} aria-pressed="false"{{end}}>
819 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-layout-grid" width="16" height="16" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
820 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
821 <path d="M4 4m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z"/>
822 <path d="M14 4m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z"/>
823 <path d="M4 14m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z"/>
824 <path d="M14 14m0 1a1 1 0 0 1 1 -1h4a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1h-4a1 1 0 0 1 -1 -1z"/>
825 </svg>
826 Grid
827 </button>
828 {{- if and (eq .Layout "grid") (eq .Sort "name") (ne .Order "asc")}}
829 <a href="?sort=name&order=asc{{template "qs" .}}&layout=grid" class="sort-control" title="Sort by name (Z→A)">
830 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler" width="16" height="16" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
831 <text x="2" y="10" font-size="9" fill="currentColor">Z</text>
832 <text x="2" y="20" font-size="9" fill="currentColor">A</text>
833 <path d="M13 4v12"></path>
834 <path d="M12 16l1 2l1 -2"></path>
835 </svg>
836 </a>
837 {{- else if and (eq .Layout "grid") (eq .Sort "name") (ne .Order "desc")}}
838 <a href="?sort=name&order=desc{{template "qs" .}}&layout=grid" class="sort-control" title="Sort by name (A→Z)">
839 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler" width="16" height="16" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
840 <text x="2" y="10" font-size="9" fill="currentColor">A</text>
841 <text x="2" y="20" font-size="9" fill="currentColor">Z</text>
842 <path d="M13 4v12"></path>
843 <path d="M12 16l1 2l1 -2"></path>
844 </svg>
845 </a>
846 {{- else if and (eq .Layout "grid")}}
847 <a href="?sort=name&order=asc{{template "qs" .}}&layout=grid" class="sort-control" title="Sort by name">
848 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler" width="16" height="16" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
849 <text x="2" y="20" font-size="9" fill="currentColor">A</text>
850 <text x="2" y="10" font-size="9" fill="currentColor">Z</text>
851 <path d="M13 4v12"></path>
852 <path d="M12 16l1 2l1 -2"></path>
853 </svg>
854 </a>
855 {{- end}}
856 {{- if and (eq .Layout "grid") (eq .Sort "size") (ne .Order "asc")}}
857 <a href="?sort=size&order=asc{{template "qs" .}}&layout=grid" class="sort-control" title="Sort by size (asc)">
858 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler" width="16" height="16" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
859 <rect x="2" y="4" width="4" height="3" rx="0.4" ry="0.4"></rect>
860 <rect x="2" y="10" width="8" height="3" rx="0.4" ry="0.4"></rect>
861 <rect x="2" y="16" width="12" height="3" rx="0.4" ry="0.4"></rect>
862 <path d="M18 4v12"></path>
863 <path d="M17 16l1 2l1 -2"></path>
864 </svg>
865 </a>
866 {{- else if and (eq .Layout "grid") (eq .Sort "size") (ne .Order "desc")}}
867 <a href="?sort=size&order=desc{{template "qs" .}}&layout=grid" class="sort-control" title="Sort by size (desc)">
868 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler" width="16" height="16" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
869 <rect x="2" y="4" width="12" height="3" rx="0.4" ry="0.4"></rect>
870 <rect x="2" y="10" width="8" height="3" rx="0.4" ry="0.4"></rect>
871 <rect x="2" y="16" width="4" height="3" rx="0.4" ry="0.4"></rect>
872 <path d="M18 4v12"></path>
873 <path d="M17 16l1 2l1 -2"></path>
874 </svg>
875 </a>
876 {{- else if and (eq .Layout "grid")}}
877 <a href="?sort=size&order=asc{{template "qs" .}}&layout=grid" class="sort-control" title="Sort by size">
878 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler" width="16" height="16" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
879 <rect x="2" y="4" width="4" height="3" rx="0.4" ry="0.4"></rect>
880 <rect x="2" y="10" width="8" height="3" rx="0.4" ry="0.4"></rect>
881 <rect x="2" y="16" width="12" height="3" rx="0.4" ry="0.4"></rect>
882 <path d="M18 4v12"></path>
883 <path d="M17 16l1 2l1 -2"></path>
884 </svg>
885 </a>
886 {{- end}}
887 {{- if and (eq .Layout "grid") (eq .Sort "time") (ne .Order "asc")}}
888 <a href="?sort=time&order=asc{{template "qs" .}}&layout=grid" class="sort-control" title="Sort by time (asc)">
889 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler" width="16" height="16" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
890 <circle cx="9" cy="11" r="8"></circle>
891 <line x1="9" y1="12" x2="9" y2="7" stroke-linecap="round"></line>
892 <line x1="9" y1="12" x2="12" y2="12" stroke-linecap="round"></line>
893 <path d="M20 4v12"></path>
894 <path d="M19 16l1 2l1 -2"></path>
895 </svg>
896 </a>
897 {{- else if and (eq .Layout "grid") (eq .Sort "time") (ne .Order "desc")}}
898 <a href="?sort=time&order=desc{{template "qs" .}}&layout=grid" class="sort-control" title="Sort by time (desc)">
899 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler" width="16" height="16" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
900 <circle cx="9" cy="11" r="8"></circle>
901 <line x1="9" y1="12" x2="9" y2="7" stroke-linecap="round"></line>
902 <line x1="9" y1="12" x2="12" y2="12" stroke-linecap="round"></line>
903 <path d="M20 4v12"></path>
904 <path d="M19 5l1 -2l1 2"></path>
905 </svg>
906 </a>
907 {{- else if and (eq .Layout "grid")}}
908 <a href="?sort=time&order=asc{{template "qs" .}}&layout=grid" class="sort-control" title="Sort by time">
909 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler" width="16" height="16" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
910 <circle cx="9" cy="11" r="8"></circle>
911 <line x1="9" y1="12" x2="9" y2="7" stroke-linecap="round"></line>
912 <line x1="9" y1="12" x2="12" y2="12" stroke-linecap="round"></line>
913 <path d="M20 4v12"></path>
914 <path d="M19 16l1 2l1 -2"></path>
915 </svg>
916 </a>
917 {{- end}}
918 <div class="filter-container">
919 <svg id="search-icon" xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-search" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
920 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
921 <path d="M10 10m-7 0a7 7 0 1 0 14 0a7 7 0 1 0 -14 0"/>
922 <path d="M21 21l-6 -6"/>
923 </svg>
924 <input type="search" placeholder="Search" id="filter" aria-label="Filter files by name" autocomplete="off">
925 </div>
926 </div>
927 </div>
928 </section>
929
930 <section>
931 <div class='listing{{if eq .Layout "grid"}} grid{{end}}'>
932 {{- if eq (len .Items) 0}}
933 <div class="empty-state" role="status">
934 <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
935 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
936 <path d="M9 3a1 1 0 0 1 .608 .206l.1 .087l2.706 2.707h6.586a3 3 0 0 1 2.995 2.824l.005 .176v8a3 3 0 0 1 -2.824 2.995l-.176 .005h-14a3 3 0 0 1 -2.995 -2.824l-.005 -.176v-11a3 3 0 0 1 2.824 -2.995l.176 -.005h4z"/>
937 </svg>
938 <div>This directory is empty.</div>
939 </div>
940 {{- else if eq .Layout "grid"}}
941 {{- range .Items}}
942 <div class="entry">
943 <a href="{{html .URL}}" title="{{html .Name}}{{if not .IsDir}} — {{.HumanSize}}{{end}} — {{.HumanModTime "02-Jan-2006 15:04"}}">
944 {{template "icon" .}}
945 <div class="name">{{html .Name}}</div>
946 <div class="size">{{.HumanSize}}</div>
947 </a>
948 </div>
949 {{- end}}
950 {{- else}}
951 <table aria-describedby="summary">
952 <thead>
953 <tr>
954 <th>
955 {{- if and (eq .Sort "namedirfirst") (ne .Order "desc")}}
956 <a href="?sort=namedirfirst&order=desc{{template "qs" .}}" title="Dirs first (desc)" class="dirfirst">
957 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
958 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
959 <path d="M18 14l-6 -6l-6 6h12"/>
960 </svg>
961 </a>
962 {{- else if and (eq .Sort "namedirfirst") (ne .Order "asc")}}
963 <a href="?sort=namedirfirst&order=asc{{template "qs" .}}" title="Dirs first (asc)" class="dirfirst">
964 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
965 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
966 <path d="M6 10l6 6l6 -6h-12"/>
967 </svg>
968 </a>
969 {{- else}}
970 <a href="?sort=namedirfirst&order=asc{{template "qs" .}}" title="Dirs first" class="dirfirst">
971 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
972 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
973 <path d="M18 14l-6 -6l-6 6h12"/>
974 </svg>
975 </a>
976 {{- end}}
977 {{- if and (eq .Sort "name") (ne .Order "desc")}}
978 <a href="?sort=name&order=desc{{template "qs" .}}">
979 Name
980 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
981 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
982 <path d="M18 14l-6 -6l-6 6h12"/>
983 </svg>
984 </a>
985 {{- else if and (eq .Sort "name") (ne .Order "asc")}}
986 <a href="?sort=name&order=asc{{template "qs" .}}">
987 Name
988 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
989 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
990 <path d="M6 10l6 6l6 -6h-12"/>
991 </svg>
992 </a>
993 {{- else}}
994 <a href="?sort=name&order=asc{{template "qs" .}}">
995 Name
996 </a>
997 {{- end}}
998 </th>
999 <th>
1000 {{- if and (eq .Sort "size") (ne .Order "desc")}}
1001 <a href="?sort=size&order=desc{{template "qs" .}}">
1002 Size
1003 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1004 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
1005 <path d="M18 14l-6 -6l-6 6h12"/>
1006 </svg>
1007 </a>
1008 {{- else if and (eq .Sort "size") (ne .Order "asc")}}
1009 <a href="?sort=size&order=asc{{template "qs" .}}">
1010 Size
1011 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1012 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
1013 <path d="M6 10l6 6l6 -6h-12"/>
1014 </svg>
1015 </a>
1016 {{- else}}
1017 <a href="?sort=size&order=asc{{template "qs" .}}">
1018 Size
1019 </a>
1020 {{- end}}
1021 </th>
1022 <th class="hideable">
1023 {{- if and (eq .Sort "time") (ne .Order "desc")}}
1024 <a href="?sort=time&order=desc{{template "qs" .}}">
1025 Modified
1026 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1027 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
1028 <path d="M18 14l-6 -6l-6 6h12"/>
1029 </svg>
1030 </a>
1031 {{- else if and (eq .Sort "time") (ne .Order "asc")}}
1032 <a href="?sort=time&order=asc{{template "qs" .}}">
1033 Modified
1034 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1035 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
1036 <path d="M6 10l6 6l6 -6h-12"/>
1037 </svg>
1038 </a>
1039 {{- else}}
1040 <a href="?sort=time&order=asc{{template "qs" .}}">
1041 Modified
1042 </a>
1043 {{- end}}
1044 </th>
1045 </tr>
1046 </thead>
1047 <tbody>
1048 {{- if .CanGoUp}}
1049 <tr>
1050 <td>
1051 <a href="..">
1052 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-corner-left-up" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1053 <path stroke="none" d="M0 0h24v24H0z" fill="none"/>
1054 <path d="M18 18h-6a3 3 0 0 1 -3 -3v-10l-4 4m8 0l-4 -4"/>
1055 </svg>
1056 <span class="go-up">Up</span>
1057 </a>
1058 </td>
1059 <td class="size dash">-</td>
1060 <td class="hideable"></td>
1061 </tr>
1062 {{- end}}
1063 {{- range .Items}}
1064 <tr class="file">
1065 <td>
1066 <a href="{{html .URL}}" title="{{html .Name}}{{if not .IsDir}} — {{.HumanSize}}{{end}} — {{.HumanModTime "02-Jan-2006 15:04"}}">
1067 {{template "icon" .}}
1068 {{- if not .SymlinkPath}}
1069 <span class="name">{{html .Name}}</span>
1070 {{- else}}
1071 <span class="name">{{html .Name}} <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-arrow-narrow-right" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
1072 <path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M5 12l14 0" />
1073 <path d="M15 16l4 -4" />
1074 <path d="M15 8l4 4" />
1075 </svg> {{html .SymlinkPath}}</span>
1076 {{- end}}
1077 </a>
1078 </td>
1079 {{- if .IsDir}}
1080 <td class="size dash">-</td>
1081 {{- else}}
1082 <td class="size" data-size="{{.Size}}">
1083 <div class="sizebar">
1084 <div class="sizebar-bar"></div>
1085 <div class="sizebar-text">
1086 {{if .IsSymlink}}↱&nbsp;{{end}}{{.HumanSize}}
1087 </div>
1088 </div>
1089 </td>
1090 {{- end}}
1091 <td class="timestamp hideable">
1092 <time datetime="{{.HumanModTime "2006-01-02T15:04:05Z"}}">{{.HumanModTime "02-Jan-2006 15:04"}}</time>
1093 </td>
1094 </tr>
1095 {{- end}}
1096 </tbody>
1097 </table>
1098 {{- end}}
1099 </div>
1100 </section>
1101
1102 <footer class="site-footer">
1103 Served with <a rel="noopener noreferrer" href="https://caddyserver.com">Caddy</a>
1104 </footer>
1105
1106 <script {{ $nonceAttribute }}>
1107 // @license magnet:?xt=urn:btih:8e4f440f4c65981c5bf93c76d35135ba5064d8b7&dn=apache-2.0.txt Apache-2.0
1108 (() => {
1109 const filterEl = document.getElementById('filter');
1110 const layoutListBtn = document.getElementById('layout-list');
1111 const layoutGridBtn = document.getElementById('layout-grid');
1112
1113 // --- Filter -------------------------------------------------------
1114 function applyFilter() {
1115 if (!filterEl) return;
1116 const q = filterEl.value.trim().toLowerCase();
1117 // Works for both list rows and grid entries.
1118 document.querySelectorAll('tr.file, .grid .entry').forEach((el) => {
1119 if (!q) { el.style.display = ''; return; }
1120 const nameEl = el.querySelector('.name');
1121 const name = (nameEl?.textContent || '').trim().toLowerCase();
1122 el.style.display = name.includes(q) ? '' : 'none';
1123 });
1124 }
1125
1126 filterEl?.addEventListener('input', applyFilter);
1127
1128 // --- Layout switch -----------------------------------------------
1129 function setQueryParam(k, v) {
1130 const qs = new URLSearchParams(window.location.search);
1131 if (!v) { qs.delete(k); } else { qs.set(k, v); }
1132 const s = qs.toString();
1133 window.location = s ? (window.location.pathname + '?' + s) : window.location.pathname;
1134 }
1135
1136 layoutListBtn?.addEventListener('click', () => setQueryParam('layout', ''));
1137 layoutGridBtn?.addEventListener('click', () => setQueryParam('layout', 'grid'));
1138
1139 // --- Localized timestamps ----------------------------------------
1140 // Use the browser's locale instead of hardcoded English month names.
1141 const dateFmt = new Intl.DateTimeFormat(undefined, {
1142 year: 'numeric', month: 'short', day: '2-digit',
1143 hour: '2-digit', minute: '2-digit', hour12: false,
1144 });
1145 function localizeDates() {
1146 document.querySelectorAll('time[datetime]').forEach((e) => {
1147 const iso = e.getAttribute('datetime');
1148 const d = new Date(iso);
1149 if (Number.isNaN(d.getTime())) return;
1150 e.textContent = dateFmt.format(d);
1151 e.title = d.toLocaleString();
1152 });
1153 }
1154
1155 // --- Init ---------------------------------------------------------
1156 function init() {
1157 // Populate filter from URL ?filter= if user navigated with one.
1158 if (filterEl && !filterEl.value) {
1159 const param = new URLSearchParams(window.location.search).get('filter');
1160 if (param) filterEl.value = param;
1161 }
1162 applyFilter();
1163 localizeDates();
1164
1165 // Scroll breadcrumbs to the end so the current folder is visible.
1166 const breadcrumbs = document.querySelector('nav.breadcrumbs');
1167 if (breadcrumbs) breadcrumbs.scrollLeft = breadcrumbs.scrollWidth;
1168
1169 filterEl?.focus({ preventScroll: true });
1170 }
1171
1172 if (document.readyState === 'loading') {
1173 document.addEventListener('DOMContentLoaded', init, { once: true });
1174 } else {
1175 init();
1176 }
1177 })();
1178 // @license-end
1179 </script>
1180</body>
1181</html>