rin
Public Open-source lightweight git hosting.
base.html · 428 lines · 25.5 KB
Raw
1 <!DOCTYPE html>
2 <html lang="{{.Lang}}" data-theme="{{if .Theme}}{{.Theme}}{{else}}system{{end}}" data-copied-text="{{t .Lang "common.copied"}}" data-copy-failed-text="{{t .Lang "common.copy_failed"}}" data-shortcut-unavailable="{{t .Lang "shortcuts.unavailable"}}">
3 <head>
4 <meta charset="utf-8">
5 <meta name="viewport" content="width=device-width, initial-scale=1">
6 <link rel="icon" type="image/svg+xml" href="/static/favicon.svg">
7 <title>{{if .Title}}{{.Title}} · Polar{{else}}Polar{{end}}</title>
8 <meta name="description" content="{{t .Lang "app.tagline"}}">
9 <script>
10 (function(){
11 try {
12 var saved = localStorage.getItem('polar_theme');
13 if (saved && ['light','dark','system'].indexOf(saved) !== -1) {
14 document.documentElement.setAttribute('data-theme', saved);
15 }
16 var sb = localStorage.getItem('polar_sidebar');
17 if (sb === 'collapsed') {
18 document.documentElement.setAttribute('data-sidebar', 'collapsed');
19 }
20 } catch(e) {}
21 })();
22 </script>
23 <style>{{inlineCSS}}</style>
24 <link rel="stylesheet" href="/static/css/app.css?v={{assetVer}}">
25 </head>
26 <body{{if .CurrentUser}} data-current-user="{{.CurrentUser.Username}}"{{end}}{{if .Repo}} data-repo-path="/{{.Repo.OwnerUsername}}/{{.Repo.Name}}" data-repo-can-write="{{if .CanWrite}}true{{else}}false{{end}}" data-repo-issues="{{if .Repo.EnableIssues}}true{{else}}false{{end}}" data-repo-pulls="{{if .Repo.EnablePulls}}true{{else}}false{{end}}" data-repo-releases="{{if .Repo.EnableReleases}}true{{else}}false{{end}}" data-repo-activity="{{if .Repo.EnableActivity}}true{{else}}false{{end}}"{{end}}>
27 {{sprite}}
28 {{$pageBackground := ""}}
29 {{with .ProfileUser}}{{$pageBackground = imageURL .BackgroundURL}}{{end}}
30 {{with .Repo}}{{$pageBackground = imageURL .BackgroundURL}}{{end}}
31
32 <!-- Floating button to expand sidebar when collapsed -->
33 <button type="button" class="sidebar-expand-btn" data-toggle-sidebar title="{{t .Lang "nav.toggle_sidebar"}} (Ctrl+B)" aria-label="{{t .Lang "nav.toggle_sidebar"}}">
34 {{icon "sidebar" "size-4"}}
35 </button>
36
37 <div class="app-shell">
38 <!-- Left Sidebar -->
39 <aside class="app-sidebar" aria-label="Sidebar">
40 <div class="sidebar-inner">
41 <div class="flex flex-col gap-2 min-w-0">
42 <!-- Brand, Language & Collapse Sidebar -->
43 <div class="flex items-center justify-between px-3 py-2" style="padding:0.25rem 0.35rem;">
44 <a href="/" class="font-semibold text-base tracking-tight" style="letter-spacing:-0.02em;">Polar</a>
45 <div class="flex items-center gap-1">
46 {{if eq .Lang "en"}}
47 <a href="?lang=ru" class="btn btn-ghost btn-sm font-mono" title="{{t .Lang "nav.lang"}}">RU</a>
48 {{else}}
49 <a href="?lang=en" class="btn btn-ghost btn-sm font-mono" title="{{t .Lang "nav.lang"}}">EN</a>
50 {{end}}
51 <button type="button" class="btn btn-ghost btn-icon" style="width:1.65rem;height:1.65rem;" data-toggle-sidebar title="{{t .Lang "nav.toggle_sidebar"}} (Ctrl+B)" aria-label="{{t .Lang "nav.toggle_sidebar"}}">
52 {{icon "sidebar" "size-4"}}
53 </button>
54 </div>
55 </div>
56
57 <!-- Search / Command Palette Trigger -->
58 <button type="button" class="btn btn-outline w-full justify-between text-muted" style="padding:0 0.55rem;height:1.95rem;" data-open-palette="">
59 <span class="flex items-center gap-2 text-xs">
60 {{icon "search" "size-3.5"}}
61 <span>{{t .Lang "nav.search_placeholder"}}</span>
62 </span>
63 <kbd class="kbd">⌘K</kbd>
64 </button>
65
66 <!-- Primary Navigation -->
67 <nav class="flex flex-col gap-1 mt-1">
68 <a href="/" class="nav-item">
69 <span class="flex items-center gap-2">{{icon "home" "size-4"}} {{t .Lang "nav.home"}}</span>
70 </a>
71 {{if .ExploreEnabled}}
72 <a href="/explore" class="nav-item">
73 <span class="flex items-center gap-2">{{icon "globe" "size-4"}} {{t .Lang "nav.explore"}}</span>
74 </a>
75 {{end}}
76 {{if .CurrentUser}}
77 <a href="/settings" class="nav-item">
78 <span class="flex items-center gap-2">{{icon "settings" "size-4"}} {{t .Lang "nav.settings"}}</span>
79 </a>
80 {{if .CurrentUser.IsAdmin}}
81 <a href="/admin" class="nav-item">
82 <span class="flex items-center gap-2">{{icon "shield" "size-4"}} {{t .Lang "nav.admin"}}</span>
83 </a>
84 {{end}}
85 {{end}}
86 </nav>
87
88 </div>
89
90 <!-- Sidebar Bottom: 3-Theme Switcher + Account -->
91 <div class="flex flex-col gap-2 pt-2" style="border-top:1px solid hsl(var(--border));">
92 <div class="segmented" role="radiogroup" aria-label="{{t .Lang "nav.theme"}}">
93 <button type="button" class="segmented-btn {{if eq .Theme "light"}}active{{end}}" data-set-theme="light" title="{{t .Lang "theme.light"}}">
94 {{icon "sun" "size-3.5"}}
95 <span>{{t .Lang "theme.light"}}</span>
96 </button>
97 <button type="button" class="segmented-btn {{if eq .Theme "dark"}}active{{end}}" data-set-theme="dark" title="{{t .Lang "theme.dark"}}">
98 {{icon "moon" "size-3.5"}}
99 <span>{{t .Lang "theme.dark"}}</span>
100 </button>
101 <button type="button" class="segmented-btn {{if eq .Theme "system"}}active{{end}}" data-set-theme="system" title="{{t .Lang "theme.system"}}">
102 {{icon "laptop" "size-3.5"}}
103 <span>{{t .Lang "theme.system"}}</span>
104 </button>
105 </div>
106
107 {{if .CurrentUser}}
108 <div class="flex items-center justify-between gap-1" style="padding:0.15rem 0.25rem;">
109 <a href="/{{.CurrentUser.Username}}" class="flex items-center gap-2 min-w-0 truncate text-sm font-medium">
110 {{avatar .CurrentUser.Username .CurrentUser.AvatarURL}}
111 <span class="truncate">{{.CurrentUser.Username}}</span>
112 </a>
113 <div class="flex items-center gap-1">
114 <form method="post" action="/logout" style="margin:0;">
115 <input type="hidden" name="_csrf" value="{{.CSRFToken}}">
116 <button type="submit" class="btn btn-ghost btn-icon" style="width:1.65rem;height:1.65rem;" title="{{t .Lang "nav.logout"}}" aria-label="{{t .Lang "nav.logout"}}">
117 {{icon "log-out" "size-3.5"}}
118 </button>
119 </form>
120 </div>
121 </div>
122 {{else}}
123 <div class="flex flex-col gap-1.5">
124 <a href="/login" class="btn btn-outline btn-sm w-full">{{t .Lang "nav.login"}}</a>
125 {{if .RegistrationEnabled}}
126 <a href="/register" class="btn btn-primary btn-sm w-full">{{t .Lang "nav.register"}}</a>
127 {{end}}
128 </div>
129 {{end}}
130 <div style="padding:0 0.35rem;">
131 <a href="/agents" class="muted-link flex items-center gap-1.5 text-xs" style="padding:0.1rem 0;">
132 {{icon "book-open" "size-3.5"}}
133 <span>{{t .Lang "nav.agents"}}</span>
134 </a>
135 </div>
136 </div>
137 </div>
138 </aside>
139
140 <!-- Main Content Column -->
141 <div class="app-col{{if $pageBackground}} has-page-bg{{end}}">
142 {{if $pageBackground}}
143 <div class="page-bg" aria-hidden="true"><img src="{{$pageBackground}}" alt=""></div>
144 {{end}}
145
146 <!-- Mobile Topbar (only visible on small screens) -->
147 <div class="mobile-topbar">
148 <div class="flex items-center gap-2">
149 <button type="button" class="btn btn-ghost btn-icon" data-drawer-open aria-label="Menu">
150 {{icon "menu" "size-4"}}
151 </button>
152 <a href="/" class="font-semibold text-base tracking-tight" style="letter-spacing:-0.02em;">Polar</a>
153 </div>
154 <div class="flex items-center gap-1.5">
155 <button type="button" class="btn btn-ghost btn-icon" data-open-palette="" aria-label="{{t .Lang "nav.command_palette"}}">
156 {{icon "search" "size-4"}}
157 </button>
158 {{if eq .Lang "en"}}
159 <a href="?lang=ru" class="btn btn-ghost btn-sm font-mono" title="{{t .Lang "nav.lang"}}" aria-label="{{t .Lang "nav.lang"}}">RU</a>
160 {{else}}
161 <a href="?lang=en" class="btn btn-ghost btn-sm font-mono" title="{{t .Lang "nav.lang"}}" aria-label="{{t .Lang "nav.lang"}}">EN</a>
162 {{end}}
163 {{if .CurrentUser}}
164 <a href="/new" class="btn btn-primary btn-sm">{{icon "plus" "size-3.5"}}</a>
165 {{else}}
166 <a href="/login" class="btn btn-outline btn-sm">{{t .Lang "nav.login"}}</a>
167 {{end}}
168 </div>
169 </div>
170
171 <main class="app-main">
172 {{if .Flash}}
173 <div class="container mb-4">
174 <div class="card p-3 flex items-center justify-between gap-2" role="status" style="border-color:hsl(var(--primary) / 0.4);">
175 <span class="flex items-center gap-2 text-sm">{{icon "check" "size-4"}} {{.Flash}}</span>
176 </div>
177 </div>
178 {{end}}
179 {{if .Error}}
180 <div class="container mb-4">
181 <div class="card p-3 flex items-center justify-between gap-2" role="alert" style="border-color:hsl(var(--destructive) / 0.6);color:hsl(var(--destructive));">
182 <span class="flex items-center gap-2 text-sm">{{icon "alert-circle" "size-4"}} {{.Error}}</span>
183 </div>
184 </div>
185 {{end}}
186
187 {{template "content" .}}
188 </main>
189 </div>
190 </div>
191
192 <!-- Mobile Drawer -->
193 <div id="mobile-drawer" class="mobile-drawer" aria-hidden="true">
194 <div class="mobile-drawer-panel">
195 <div class="flex flex-col gap-2">
196 <div class="flex items-center justify-between" style="padding-bottom:0.5rem;border-bottom:1px solid hsl(var(--border));">
197 <span class="font-semibold text-base tracking-tight" style="letter-spacing:-0.02em;">Polar</span>
198 <button type="button" class="btn btn-ghost btn-icon" data-drawer-close aria-label="{{t .Lang "common.close"}}">
199 {{icon "close" "size-4"}}
200 </button>
201 </div>
202 <nav class="flex flex-col gap-1">
203 <a href="/" class="nav-item">{{icon "home" "size-4"}} {{t .Lang "nav.home"}}</a>
204 {{if .CurrentUser}}
205 <a href="/new" class="nav-item">{{icon "plus" "size-4"}} {{t .Lang "repo.new.title"}}</a>
206 <a href="/settings" class="nav-item">{{icon "settings" "size-4"}} {{t .Lang "nav.settings"}}</a>
207 {{if .CurrentUser.IsAdmin}}
208 <a href="/admin" class="nav-item">{{icon "shield" "size-4"}} {{t .Lang "nav.admin"}}</a>
209 {{end}}
210 {{end}}
211 {{if .ExploreEnabled}}
212 <a href="/explore" class="nav-item">{{icon "globe" "size-4"}} {{t .Lang "nav.explore"}}</a>
213 {{end}}
214 </nav>
215 </div>
216 <div class="flex flex-col gap-2">
217 <div class="segmented" role="group" aria-label="{{t .Lang "nav.lang"}}">
218 <a href="?lang=ru" class="segmented-btn {{if eq .Lang "ru"}}active{{end}}">RU</a>
219 <a href="?lang=en" class="segmented-btn {{if eq .Lang "en"}}active{{end}}">EN</a>
220 </div>
221 <div class="segmented" role="group" aria-label="{{t .Lang "nav.theme"}}">
222 <button type="button" class="segmented-btn {{if eq .Theme "light"}}active{{end}}" data-set-theme="light">{{t .Lang "theme.light"}}</button>
223 <button type="button" class="segmented-btn {{if eq .Theme "dark"}}active{{end}}" data-set-theme="dark">{{t .Lang "theme.dark"}}</button>
224 <button type="button" class="segmented-btn {{if eq .Theme "system"}}active{{end}}" data-set-theme="system">{{t .Lang "theme.system"}}</button>
225 </div>
226 </div>
227 </div>
228 </div>
229
230 <!-- Command Palette (Ctrl/Cmd+K) -->
231 <div id="command-palette" class="backdrop" role="dialog" aria-modal="true" aria-label="{{t .Lang "nav.command_palette"}}" aria-hidden="true">
232 <div class="palette-box">
233 <div class="palette-input-wrap">
234 {{icon "search" "size-4"}}
235 <input id="command-palette-input" type="text" class="palette-input" placeholder="{{t .Lang "palette.placeholder"}}" autocomplete="off">
236 <kbd class="kbd">ESC</kbd>
237 </div>
238 <div class="palette-list" role="listbox">
239 <div data-palette-group>
240 <div class="palette-group-title">{{t .Lang "palette.group.nav"}}</div>
241 <button type="button" class="palette-item" data-palette-item data-palette-href="/" data-palette-keywords="home главная домой">
242 <span class="flex items-center gap-2">{{icon "home" "size-4"}} {{t .Lang "nav.home"}}</span>
243 <span class="flex items-center gap-1"><kbd class="kbd">g</kbd><kbd class="kbd">h</kbd></span>
244 </button>
245 {{if .ExploreEnabled}}
246 <button type="button" class="palette-item" data-palette-item data-palette-href="/explore" data-palette-keywords="explore обзор публичные">
247 <span class="flex items-center gap-2">{{icon "globe" "size-4"}} {{t .Lang "nav.explore"}}</span>
248 <span class="flex items-center gap-1"><kbd class="kbd">g</kbd><kbd class="kbd">r</kbd></span>
249 </button>
250 {{end}}
251 <button type="button" class="palette-item" data-palette-item data-palette-href="/agents" data-palette-keywords="agents ai api guide агенты документация">
252 <span class="flex items-center gap-2">{{icon "book-open" "size-4"}} {{t .Lang "nav.agents"}}</span>
253 </button>
254 {{if .CurrentUser}}
255 <button type="button" class="palette-item" data-palette-item data-palette-href="/new" data-palette-keywords="new create создать репозиторий">
256 <span class="flex items-center gap-2">{{icon "plus" "size-4"}} {{t .Lang "repo.new.title"}}</span>
257 </button>
258 <button type="button" class="palette-item" data-palette-item data-palette-href="/settings" data-palette-keywords="settings tokens настройки токены">
259 <span class="flex items-center gap-2">{{icon "settings" "size-4"}} {{t .Lang "nav.settings"}}</span>
260 </button>
261 {{if .CurrentUser.IsAdmin}}
262 <button type="button" class="palette-item" data-palette-item data-palette-href="/admin" data-palette-keywords="admin админка">
263 <span class="flex items-center gap-2">{{icon "shield" "size-4"}} {{t .Lang "nav.admin"}}</span>
264 </button>
265 {{end}}
266 {{else}}
267 <button type="button" class="palette-item" data-palette-item data-palette-href="/login" data-palette-keywords="login войти">
268 <span class="flex items-center gap-2">{{icon "user" "size-4"}} {{t .Lang "nav.login"}}</span>
269 </button>
270 <button type="button" class="palette-item" data-palette-item data-palette-href="/register" data-palette-keywords="register регистрация">
271 <span class="flex items-center gap-2">{{icon "plus" "size-4"}} {{t .Lang "nav.register"}}</span>
272 </button>
273 {{end}}
274 <button type="button" class="palette-item" data-palette-item data-palette-action="toggle-sidebar" data-palette-keywords="sidebar toggle боковая панель скрыть">
275 <span class="flex items-center gap-2">{{icon "sidebar" "size-4"}} {{t .Lang "nav.toggle_sidebar"}}</span>
276 <span class="flex items-center gap-1"><kbd class="kbd">Ctrl</kbd><kbd class="kbd">B</kbd></span>
277 </button>
278 </div>
279
280 {{if .PaletteFiles}}
281 <div data-palette-group>
282 <div class="palette-group-title">{{t .Lang "repo.files"}} ({{.Repo.OwnerUsername}}/{{.Repo.Name}})</div>
283 {{range .PaletteFiles}}
284 <button type="button" class="palette-item" data-palette-item data-palette-href="/{{$.Repo.OwnerUsername}}/{{$.Repo.Name}}/blob/{{$.Ref}}/{{.}}" data-palette-keywords="file: {{.}}">
285 <span class="flex items-center gap-2 font-mono text-xs">{{icon "file-code" "size-4"}} {{.}}</span>
286 </button>
287 {{end}}
288 </div>
289 {{end}}
290
291 {{if .PaletteRepos}}
292 <div data-palette-group>
293 <div class="palette-group-title">{{t .Lang "palette.group.repos"}}</div>
294 {{range .PaletteRepos}}
295 <button type="button" class="palette-item" data-palette-item data-palette-href="/{{.OwnerUsername}}/{{.Name}}" data-palette-keywords="repo {{.OwnerUsername}}/{{.Name}} {{.Description}}">
296 <span class="flex items-center gap-2">
297 {{if .IsPrivate}}{{icon "lock" "size-4"}}{{else}}{{icon "git-branch" "size-4"}}{{end}}
298 <span>{{.OwnerUsername}}/<strong>{{.Name}}</strong></span>
299 </span>
300 <span class="text-xs text-muted">{{if .IsPrivate}}{{t $.Lang "repo.private"}}{{else}}{{t $.Lang "repo.public"}}{{end}}</span>
301 </button>
302 {{end}}
303 </div>
304 {{end}}
305
306 <div data-palette-group>
307 <div class="palette-group-title">{{t .Lang "palette.group.themes"}}</div>
308 <button type="button" class="palette-item" data-palette-item data-palette-theme="light" data-palette-keywords="theme light светлая">
309 <span class="flex items-center gap-2">{{icon "sun" "size-4"}} {{t .Lang "theme.light"}}</span>
310 </button>
311 <button type="button" class="palette-item" data-palette-item data-palette-theme="dark" data-palette-keywords="theme dark темная тёмная">
312 <span class="flex items-center gap-2">{{icon "moon" "size-4"}} {{t .Lang "theme.dark"}}</span>
313 </button>
314 <button type="button" class="palette-item" data-palette-item data-palette-theme="system" data-palette-keywords="theme system авто системная">
315 <span class="flex items-center gap-2">{{icon "laptop" "size-4"}} {{t .Lang "theme.system"}}</span>
316 </button>
317 </div>
318 </div>
319 </div>
320 </div>
321
322 <!-- Keyboard Shortcuts Dialog (?) -->
323 <div id="shortcuts-dialog" class="backdrop" role="dialog" aria-modal="true" aria-label="{{t .Lang "shortcuts.title"}}" aria-hidden="true">
324 <div class="dialog-box">
325 <div class="card-header">
326 <span class="font-semibold flex items-center gap-2">{{icon "keyboard" "size-4"}} {{t .Lang "shortcuts.title"}}</span>
327 <button type="button" class="btn btn-ghost btn-icon" data-dialog-close aria-label="{{t .Lang "common.close"}}">
328 {{icon "close" "size-4"}}
329 </button>
330 </div>
331 <div class="card-body shortcuts-list">
332 <div class="shortcuts-group-label">{{t .Lang "shortcuts.group.general"}}</div>
333 <div class="shortcut-row"><span>{{t .Lang "shortcuts.palette"}}</span><span class="shortcut-keys"><kbd class="kbd">Ctrl</kbd><kbd class="kbd">K</kbd></span></div>
334 <div class="shortcut-row"><span>{{t .Lang "shortcuts.shortcuts"}}</span><kbd class="kbd">?</kbd></div>
335 <div class="shortcut-row"><span>{{t .Lang "shortcuts.context_menu"}}</span><span class="shortcut-keys"><kbd class="kbd">Shift</kbd><kbd class="kbd">F10</kbd></span></div>
336 <div class="shortcut-row"><span>{{t .Lang "shortcuts.theme"}}</span><span class="shortcut-keys"><kbd class="kbd">Ctrl</kbd><kbd class="kbd">Shift</kbd><kbd class="kbd">L</kbd></span></div>
337 <div class="shortcut-row"><span>{{t .Lang "shortcuts.copy_url"}}</span><kbd class="kbd">y</kbd></div>
338 <div class="shortcut-row"><span>{{t .Lang "shortcuts.sidebar"}}</span><span class="shortcut-keys"><kbd class="kbd">Ctrl</kbd><kbd class="kbd">B</kbd></span></div>
339 <div class="shortcut-row"><span>{{t .Lang "shortcuts.search"}}</span><kbd class="kbd">/</kbd></div>
340 <div class="shortcut-row"><span>{{t .Lang "shortcuts.close"}}</span><kbd class="kbd">Esc</kbd></div>
341
342 <div class="shortcuts-group-label">{{t .Lang "shortcuts.group.navigation"}}</div>
343 <div class="shortcut-row"><span>{{t .Lang "shortcuts.home"}}</span><span class="shortcut-keys"><kbd class="kbd">g</kbd><kbd class="kbd">h</kbd></span></div>
344 <div class="shortcut-row"><span>{{t .Lang "nav.explore"}}</span><span class="shortcut-keys"><kbd class="kbd">g</kbd><kbd class="kbd">r</kbd></span></div>
345 {{if .CurrentUser}}
346 <div class="shortcut-row"><span>{{t .Lang "shortcuts.profile"}}</span><span class="shortcut-keys"><kbd class="kbd">g</kbd><kbd class="kbd">o</kbd></span></div>
347 <div class="shortcut-row"><span>{{t .Lang "shortcuts.new_repo"}}</span><span class="shortcut-keys"><kbd class="kbd">g</kbd><kbd class="kbd">n</kbd></span></div>
348 {{end}}
349
350 {{if .Repo}}
351 <div class="shortcuts-group-label">{{t .Lang "shortcuts.group.repo"}}</div>
352 <div class="shortcut-row"><span>{{t .Lang "repo.tab.code"}}</span><span class="shortcut-keys"><kbd class="kbd">g</kbd><kbd class="kbd">c</kbd></span></div>
353 {{if .Repo.EnableIssues}}<div class="shortcut-row"><span>{{t .Lang "repo.tab.issues"}}</span><span class="shortcut-keys"><kbd class="kbd">g</kbd><kbd class="kbd">i</kbd></span></div>{{end}}
354 {{if .Repo.EnablePulls}}<div class="shortcut-row"><span>{{t .Lang "repo.tab.pulls"}}</span><span class="shortcut-keys"><kbd class="kbd">g</kbd><kbd class="kbd">p</kbd></span></div>{{end}}
355 {{if .Repo.EnableReleases}}<div class="shortcut-row"><span>{{t .Lang "repo.tab.releases"}}</span><span class="shortcut-keys"><kbd class="kbd">g</kbd><kbd class="kbd">l</kbd></span></div>{{end}}
356 {{if .Repo.EnableActivity}}<div class="shortcut-row"><span>{{t .Lang "repo.tab.activity"}}</span><span class="shortcut-keys"><kbd class="kbd">g</kbd><kbd class="kbd">a</kbd></span></div>{{end}}
357 <div class="shortcut-row"><span>{{t .Lang "repo.tab.branches"}}</span><span class="shortcut-keys"><kbd class="kbd">g</kbd><kbd class="kbd">b</kbd></span></div>
358 {{if .CanWrite}}<div class="shortcut-row"><span>{{t .Lang "repo.tab.settings"}}</span><span class="shortcut-keys"><kbd class="kbd">g</kbd><kbd class="kbd">s</kbd></span></div>{{end}}
359 <div class="shortcut-row"><span>{{t .Lang "shortcuts.files"}}</span><kbd class="kbd">t</kbd></div>
360 {{end}}
361 </div>
362 </div>
363 </div>
364
365
366 <!-- Custom Context Menu -->
367 <div id="context-menu" class="context-menu" role="menu" aria-label="{{t .Lang "contextmenu.title"}}" aria-hidden="true">
368 <div class="context-menu-section" data-context-section="selection" hidden>
369 <div class="context-menu-label">{{t .Lang "contextmenu.selection"}}</div>
370 <button type="button" class="context-menu-item" data-context-action="copy-selection" role="menuitem">
371 {{icon "copy" "size-3.5"}}<span>{{t .Lang "contextmenu.copy_selection"}}</span>
372 </button>
373 <button type="button" class="context-menu-item" data-context-action="search-selection" role="menuitem">
374 {{icon "search" "size-3.5"}}<span>{{t .Lang "contextmenu.search_selection"}}</span>
375 </button>
376 </div>
377
378 <div class="context-menu-section" data-context-section="link" hidden>
379 <div class="context-menu-label">{{t .Lang "contextmenu.link"}}</div>
380 <button type="button" class="context-menu-item" data-context-action="link-open" role="menuitem">
381 {{icon "link" "size-3.5"}}<span>{{t .Lang "contextmenu.open_link"}}</span>
382 </button>
383 <button type="button" class="context-menu-item" data-context-action="link-copy" role="menuitem">
384 {{icon "copy" "size-3.5"}}<span>{{t .Lang "contextmenu.copy_link"}}</span>
385 </button>
386 <button type="button" class="context-menu-item" data-context-action="link-text" role="menuitem">
387 {{icon "file" "size-3.5"}}<span>{{t .Lang "contextmenu.copy_link_text"}}</span>
388 </button>
389 </div>
390
391 <div class="context-menu-section" data-context-section="image" hidden>
392 <div class="context-menu-label">{{t .Lang "contextmenu.image"}}</div>
393 <button type="button" class="context-menu-item" data-context-action="image-open" role="menuitem">
394 {{icon "image" "size-3.5"}}<span>{{t .Lang "contextmenu.open_image"}}</span>
395 </button>
396 <button type="button" class="context-menu-item" data-context-action="image-copy" role="menuitem">
397 {{icon "copy" "size-3.5"}}<span>{{t .Lang "contextmenu.copy_image"}}</span>
398 </button>
399 <button type="button" class="context-menu-item" data-context-action="image-save" role="menuitem">
400 {{icon "download" "size-3.5"}}<span>{{t .Lang "contextmenu.save_image"}}</span>
401 </button>
402 </div>
403
404 <div class="context-menu-section" data-context-section="page">
405 <div class="context-menu-label">{{t .Lang "contextmenu.page"}}</div>
406 <button type="button" class="context-menu-item" data-context-action="page-back" role="menuitem">
407 {{icon "arrow-left" "size-3.5"}}<span>{{t .Lang "contextmenu.back"}}</span>
408 </button>
409 <button type="button" class="context-menu-item" data-context-action="page-forward" role="menuitem">
410 {{icon "arrow-right" "size-3.5"}}<span>{{t .Lang "contextmenu.forward"}}</span>
411 </button>
412 <button type="button" class="context-menu-item" data-context-action="page-reload" role="menuitem">
413 {{icon "history" "size-3.5"}}<span>{{t .Lang "contextmenu.reload"}}</span>
414 </button>
415 <button type="button" class="context-menu-item" data-context-action="page-copy" role="menuitem">
416 {{icon "copy" "size-3.5"}}<span>{{t .Lang "contextmenu.copy_url"}}</span><kbd class="context-menu-kbd">y</kbd>
417 </button>
418 </div>
419
420
421 </div>
422
423 <div id="toast-container" class="toast-container" aria-live="polite"></div>
424
425 <script src="/static/js/htmx.min.js" defer></script>
426 <script src="/static/js/app.js?v={{assetVer}}" defer></script>
427 </body>
428 </html>