shell.templ RawHistoryFolder
1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package web
4
5// Shell is the page: head, the header, the work area (content and rail), the
6// footer, the hint badge, the overlays and the key map. The content is the
7// children.
8templ Shell(l Layout, rail templ.Component) {
9 <!DOCTYPE html>
10 <html lang="en">
11 <head>
12 <meta charset="utf-8"/>
13 <meta name="viewport" content="width=device-width, initial-scale=1"/>
14 <meta name="color-scheme" content="light dark"/>
15 <title>{ l.Title }</title>
16 <link rel="icon" type="image/svg+xml" href={ templ.SafeURL(l.Assets.Icon) }/>
17 <link rel="preload" href={ templ.SafeURL(l.Assets.Font) } as="font" type="font/woff2" crossorigin/>
18 <link rel="stylesheet" href={ templ.SafeURL(l.Assets.CSS) }/>
19 <script src={ l.Assets.HTMX } defer></script>
20 <script src={ l.Assets.Live } defer></script>
21 <script src={ l.Assets.SSE } defer></script>
22 <script src={ l.Assets.App } defer></script>
23 </head>
24 <body data-ref={ l.RefPath } data-sha={ l.SHA } data-sel="" data-nav="">
25 <a class="skip" href="#main">skip to content</a>
26 @Header(l)
27 <div class={ "work", templ.KV("has-rail", l.HasRail && rail != nil) }>
28 if l.Lede != "" {
29 <section class="work-lede" aria-label="Description"><p class="lede">{ l.Lede }</p></section>
30 }
31 <main id="main" tabindex="-1">
32 { children... }
33 </main>
34 if l.HasRail && rail != nil {
35 <aside class="rail" aria-label="Context">
36 @rail
37 </aside>
38 }
39 </div>
40 @Footer(l)
41 @Hint(l)
42 @OverlayHelp()
43 @OverlayRefs(l)
44 @OverlayCmd(l.Cmd)
45 @Keys()
46 </body>
47 </html>
48}
49
50// Header is the page header: the instance, the path, the ref switcher, the
51// "go to" palette and the owner; inside a repository, the tabs below.
52templ Header(l Layout) {
53 <header class="hdr">
54 <div class="hdr-top">
55 <a class="brand" href="/" data-g="h" title="home (g h)">
56 <span class="brand-mark" aria-hidden="true"></span>
57 <span class="brand-name">{ l.Instance }</span>
58 </a>
59 if len(l.Crumbs) > 0 {
60 <nav class="crumbs" aria-label="Path">
61 <ol>
62 for _, c := range l.Crumbs {
63 <li>
64 if c.Href != "" {
65 <a
66 href={ templ.SafeURL(c.Href) }
67 if c.Current {
68 aria-current="page"
69 }
70 >{ c.Label }</a>
71 } else {
72 <span
73 if c.Current {
74 aria-current="page"
75 }
76 >{ c.Label }</span>
77 }
78 </li>
79 }
80 </ol>
81 </nav>
82 }
83 <div class="hdr-acts">
84 if l.Ref != "" {
85 <button
86 type="button"
87 class="btn ref"
88 aria-haspopup="dialog"
89 aria-controls="panel-refs"
90 aria-keyshortcuts="b"
91 title="switch branch or tag (b)"
92 hx-on:click="document.body.dispatchEvent(new CustomEvent('k-refs'))"
93 >
94 <span class="ico i-branch" aria-hidden="true"></span>
95 <span class="ref-name">{ l.Ref }</span>
96 <span class="sha">{ l.Short }</span>
97 </button>
98 }
99 <button
100 type="button"
101 class="btn search"
102 aria-haspopup="dialog"
103 aria-controls="panel-cmd"
104 aria-keyshortcuts="Control+K :"
105 title="go to a section, ref, file or repo (ctrl k or :)"
106 hx-on:click="document.body.dispatchEvent(new CustomEvent('k-cmd'))"
107 >
108 <span class="ico i-search" aria-hidden="true"></span>
109 <span class="search-label">Go to…</span>
110 <kbd aria-hidden="true">ctrl k</kbd>
111 </button>
112 <button type="button" class="btn icon" aria-haspopup="dialog" aria-controls="panel-help" aria-keyshortcuts="?" aria-label="Keyboard shortcuts" title="keyboard shortcuts (?)" hx-on:click="document.body.dispatchEvent(new CustomEvent('k-help'))">?</button>
113 if SignedIn(ctx) {
114 <form class="owner" method="post" action="/-/logout">
115 <span class="owner-badge" title="signed in as the owner">owner</span>
116 <button type="submit" class="btn" title="sign out of this browser">Sign out</button>
117 </form>
118 }
119 </div>
120 </div>
121 if len(l.Sections) > 0 {
122 <nav class="tabs" aria-label="Sections">
123 <ul>
124 for _, s := range l.Sections {
125 <li>
126 <a
127 href={ templ.SafeURL(s.Href) }
128 data-key={ s.Key }
129 aria-keyshortcuts={ s.Key }
130 if s.Current {
131 aria-current="page"
132 }
133 >{ s.Label }<kbd aria-hidden="true">{ s.Key }</kbd></a>
134 </li>
135 }
136 </ul>
137 </nav>
138 }
139 </header>
140}
141
142// Footer is the version and the link to the source of the running version,
143// which the AGPL §13 asks every user of the service to be offered.
144templ Footer(l Layout) {
145 <footer class="foot">
146 <span>ermite { l.Version }</span>
147 <a href={ templ.SafeURL(l.SourceURL) } rel="noopener" title="source of this version (AGPL-3.0-or-later)">source</a>
148 <span class="foot-keys"><kbd>?</kbd> keyboard shortcuts</span>
149 </footer>
150}
151
152// Hint is the floating badge at the bottom right. It shows only what the
153// keyboard just did or what needs attention: the row position while j/k
154// move, the selected lines of a file, a flash message (permalink copied) and
155// a lost event stream. Empty, it takes no room.
156templ Hint(l Layout) {
157 <div class="hint" role="status" aria-live="polite">
158 <span class="hint-nav" :text="document.body.dataset.nav || ''"></span>
159 <span class="hint-sel" :text="document.body.dataset.sel ? document.body.dataset.sel + ' · y permalink' : ''"></span>
160 <span class="hint-flash" :text="document.body.dataset.flash || ''"></span>
161 @Live(l)
162 </div>
163}