v0.3.0 9fdf386

shell.templ RawHistoryFolder

1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package web
4
5// Shell is the page: head, the two bars, the work area (content and rail),
6// the overlays and the key map. The content is the children.
7templ Shell(l Layout, rail templ.Component) {
8	<!DOCTYPE html>
9	<html lang="en">
10		<head>
11			<meta charset="utf-8"/>
12			<meta name="viewport" content="width=device-width, initial-scale=1"/>
13			<meta name="color-scheme" content="light dark"/>
14			<title>{ l.Title }</title>
15			<link rel="icon" type="image/svg+xml" href={ templ.SafeURL(l.Assets.Icon) }/>
16			<link rel="stylesheet" href={ templ.SafeURL(l.Assets.CSS) }/>
17			<script src={ l.Assets.HTMX } defer></script>
18			<script src={ l.Assets.Live } defer></script>
19			<script src={ l.Assets.SSE } defer></script>
20			<script src={ l.Assets.App } defer></script>
21		</head>
22		<body data-ref={ l.RefPath } data-sha={ l.SHA } data-sel="">
23			<a class="skip" href="#main">skip to content</a>
24			@Topbar(l)
25			<div class={ "work", templ.KV("has-rail", l.HasRail), templ.KV("read", l.Read) }>
26				<main id="main" tabindex="-1" data-y="0" hx-on:scroll="this.dataset.y = Math.round(this.scrollTop / 60)">
27					{ children... }
28				</main>
29				if l.HasRail && rail != nil {
30					<aside class="rail" aria-label="Context">
31						<details class="rail-d" open data-init="" hx-live="if (!data.init) { data.init = '1'; this.open = matchMedia('(min-width: 1100px)').matches }">
32							<summary>{ l.RailSum }</summary>
33							@rail
34						</details>
35					</aside>
36				}
37			</div>
38			@OverlayHelp()
39			@OverlayRefs(l)
40			@OverlayCmd(l.Cmd)
41			@Statusbar(l)
42			@Keys()
43		</body>
44	</html>
45}
46
47// Topbar is the top bar, neferbar style: the one accent block (instance)
48// fading into the black bar, the path, the repo sections, the ref switcher.
49templ Topbar(l Layout) {
50	<header class="bar top">
51		<a class="blk" href="/" data-g="h" title="home (g h)">{ l.Instance }</a><span class="fade" aria-hidden="true"></span>
52		<nav class="crumbs" aria-label="Path">
53			<ol>
54				for _, c := range l.Crumbs {
55					<li>
56						if c.Href != "" {
57							<a
58								href={ templ.SafeURL(c.Href) }
59								if c.Current {
60									aria-current="page"
61								}
62							>{ c.Label }</a>
63						} else {
64							<span aria-current="page">{ c.Label }</span>
65						}
66					</li>
67				}
68			</ol>
69		</nav>
70		if len(l.Sections) > 0 {
71			<nav class="secs" aria-label="Sections">
72				<ul>
73					for _, s := range l.Sections {
74						<li>
75							<a
76								href={ templ.SafeURL(s.Href) }
77								data-key={ s.Key }
78								aria-keyshortcuts={ "g " + s.Key }
79								if s.Current {
80									aria-current="page"
81								}
82							>{ s.Key }{ s.Post }</a>
83						</li>
84					}
85				</ul>
86			</nav>
87		}
88		if l.Ref != "" {
89			<button
90				type="button"
91				class="ref"
92				aria-haspopup="dialog"
93				aria-controls="panel-refs"
94				aria-keyshortcuts="b"
95				title="switch ref (b)"
96				hx-on:click="document.body.dispatchEvent(new CustomEvent('k-refs'))"
97			>{ l.Ref }<span class="sha">{ "@" + l.Short }</span></button>
98		}
99	</header>
100}
101
102// Statusbar is the bottom bar: mode, context, the ":" command prompt, the
103// source link (AGPL §13), hints, live state and the cursor position. Plain
104// text only: the top bar carries the accent.
105templ Statusbar(l Layout) {
106	<footer class="bar status" aria-label="Status">
107		<span class="mode">{ l.Status.Mode }</span>
108		<span class="st-ctx">{ l.Status.Context }</span>
109		<button
110			type="button"
111			class="st-cmd"
112			aria-haspopup="dialog"
113			aria-controls="panel-cmd"
114			aria-keyshortcuts=":"
115			hx-on:click="document.body.dispatchEvent(new CustomEvent('k-cmd'))"
116		><span class="hi">:</span> go to…</button>
117		if l.Status.Blob {
118			<span class="st-sel" :text="document.body.dataset.sel ? document.body.dataset.sel + ' · y permalink' : 'click a line number · shift-click extends'">click a line number · shift-click extends</span>
119		}
120		<span class="st-flash" aria-live="polite" :text="document.body.dataset.flash || ''"></span>
121		<span class="st-hints">{ l.Status.Hints }</span>
122		if SignedIn(ctx) {
123			<form class="st-owner" method="post" action="/-/logout">
124				<span>owner</span>
125				<button type="submit" title="sign out of this browser">[sign out]</button>
126			</form>
127		}
128		@Footer(l)
129		@Live(l)
130		if l.Status.Cursor {
131			<span
132				class="st-pos num"
133				title="cursor position"
134				:text="(() => { let all = q('[data-rows] > :not([hidden])').arr(); let i = all.findIndex(r => r.hasAttribute('data-cursor')); return (i + 1) + '/' + all.length })()"
135			>{ cursorText(l.Status.Count) }</span>
136		}
137	</footer>
138}
139
140// Footer is the version and the link to the source of the running version,
141// which the AGPL §13 asks every user of the service to be offered.
142templ Footer(l Layout) {
143	<span class="st-src"><span class="st-ver">ermite { l.Version } · </span><a href={ templ.SafeURL(l.SourceURL) } rel="noopener" title="source of this version (AGPL-3.0-or-later)">source</a></span>
144}

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 2

Tags 45

↑↓ moveenter openesc close