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>@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}