v0.35.1 2aca1df

blob.templ RawHistoryFolder

1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package web
4
5import "html/template"
6
7// Blob is a file: its title with the actions, the notice of a state (too
8// large, binary, empty), the code, the rendered Markdown with its source
9// under a fold, or the image; the rail holds the facts, the last change, the
10// outline and the line selection.
11templ Blob(l Layout, p BlobPage) {
12	@Shell(l, blobRail(p)) {
13		<h1 class="file-h">
14			<span>{ p.Name }</span>
15			<span class="acts"><a class="btn" href={ templ.SafeURL(p.RawHref) }>Raw</a><a class="btn" href={ templ.SafeURL(p.HistHref) }>History</a><a class="btn" href={ templ.SafeURL(p.DirHref) }>Folder</a></span>
16		</h1>
17		if p.Notice != "" {
18			<p class="notice" role="note">{ p.Notice }</p>
19		}
20		if p.Image {
21			<p class="img-view"><img src={ p.RawHref } alt={ p.Name }/></p>
22		}
23		if p.Rendered != nil {
24			@ReadmeView(*p.Rendered)
25		}
26		if p.HasCode {
27			if p.Rendered != nil {
28				<details class="source">
29					<summary>source</summary>
30					@CodeView(p.Code)
31				</details>
32			} else {
33				@CodeView(p.Code)
34			}
35		}
36	}
37}
38
39// CodeView is the highlighted lines with a link on each number. A click on a
40// number selects the line, shift-click extends the selection to a range; the
41// URL hash (#L10 or #L10-L24) is the state, so a page loaded with a hash is
42// painted the same way. body[data-sel] feeds the hint badge and the rail.
43templ CodeView(h template.HTML) {
44	<pre
45		class="chroma code"
46		data-anchor=""
47		hx-on:click="let a = event.target.closest('a.ln'); if (!a) return; event.preventDefault();
48    let n = +a.textContent; if (!event.shiftKey || !data.anchor) data.anchor = n;
49    let lo = Math.min(+data.anchor, n), hi = Math.max(+data.anchor, n);
50    history.replaceState(null, '', '#L' + lo + (hi > lo ? '-L' + hi : '')); trigger('sel')"
51		hx-on:sel="let m = location.hash.match(/^#L(\d+)(?:-L(\d+))?$/); let lo = m ? +m[1] : 0, hi = m ? +(m[2] || m[1]) : 0;
52    q('.line in this').arr().forEach(l => { let k = +l.id.slice(1); l.classList.toggle('sel', k >= lo && k <= hi) });
53    document.body.dataset.sel = lo ? 'L' + lo + (hi > lo ? '-L' + hi + ' · ' + (hi - lo + 1) + ' lines' : '') : '';
54    if (lo && event.detail?.scroll) document.getElementById('L' + lo)?.scrollIntoView({ block: 'center' })"
55		hx-live="if (!data.ready) { data.ready = '1'; trigger('sel', { scroll: true }) }"
56		data-ready=""
57	><code hx-ignore>@templ.Raw(string(h))</code></pre>
58}
59
60templ blobRail(p BlobPage) {
61	<section aria-labelledby="r-file">
62		<h2 id="r-file">File</h2>
63		@Facts(p.Facts)
64	</section>
65	if p.Last != nil {
66		<section aria-labelledby="r-last">
67			<h2 id="r-last">Last change <a href={ templ.SafeURL(p.HistHref) }>History</a></h2>
68			<p><a href={ templ.SafeURL(p.Last.CHref) }>{ p.Last.Short }</a> { p.Last.Subject }</p>
69			<p class="dim">
70				{ p.Last.Author } ·
71				if p.Last.WhenAt != "" {
72					<time datetime={ p.Last.WhenAt } title={ p.Last.WhenAt }>{ p.Last.When }</time>
73				}
74			</p>
75		</section>
76	}
77	if len(p.Outline) > 0 {
78		<section aria-labelledby="r-outline">
79			<h2 id="r-outline">On this page</h2>
80			@Outline(p.Outline)
81		</section>
82	}
83	if p.HasCode {
84		<section aria-labelledby="r-sel">
85			<h2 id="r-sel">Selection</h2>
86			<p :text="document.body.dataset.sel || 'none · click a line number, shift-click extends'">none · click a line number, shift-click extends</p>
87			<p class="dim">y copies a permalink pinned to the commit sha</p>
88		</section>
89	}
90}

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 4

Tags 45

↑↓ moveenter openesc close