1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package web
4
5import (
6 "strconv"
7 "strings"
8)
9
10// OverlayHelp is the keyboard map panel; the keys of the merge request page
11// show on that page only.
12templ OverlayHelp(mr bool) {
13 <dialog id="panel-help" class="panel modal" aria-label="Keyboard shortcuts" hx-on:keydown="event.key === 'Escape' && (event.preventDefault(), this.close())" hx-on:click="event.target === this && this.close()">
14 <div class="panel-head"><h2>Keyboard shortcuts</h2><button type="button" class="panel-x" aria-label="Close" hx-on:click="closest('dialog').close()"><kbd>esc</kbd> close</button></div>
15 <div class="keys-pref">
16 <label><input type="checkbox" id="keys-off" aria-describedby="keys-note" hx-on:change="ermite.setKeysOff(this.checked)"/> Turn off single-key shortcuts</label>
17 <p id="keys-note" class="dim">Single-key shortcuts are on. ctrl k and esc work whatever this says.</p>
18 </div>
19 <dl class="keys">
20 <div><dt><kbd>ctrl k</kbd> <kbd>:</kbd></dt><dd>go to a section, branch, tag, file or repository</dd></div>
21 <div><dt><kbd>?</kbd></dt><dd>this list</dd></div>
22 <div><dt><kbd>j</kbd> <kbd>k</kbd></dt><dd>move the row cursor down / up</dd></div>
23 <div><dt><kbd>enter</kbd></dt><dd>open the row under the cursor; on Files, a directory opens or closes in place</dd></div>
24 <div><dt><kbd>↑</kbd> <kbd>↓</kbd> <kbd>←</kbd> <kbd>→</kbd> <kbd>home</kbd> <kbd>end</kbd></dt><dd>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</dd></div>
25 <div><dt><kbd>l</kbd> <kbd>h</kbd></dt><dd>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</dd></div>
26 <div><dt><kbd>g g</kbd> <kbd>G</kbd></dt><dd>first / last row</dd></div>
27 <div><dt><kbd>/</kbd></dt><dd>filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)</dd></div>
28 <div><dt><kbd>b</kbd></dt><dd>{ "switch" } branch or tag</dd></div>
29 <div><dt><kbd>s</kbd> <kbd>t</kbd> <kbd>c</kbd> <kbd>r</kbd> <kbd>m</kbd> <kbd>i</kbd></dt><dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd></div>
30 if SignedIn(ctx) {
31 <div><dt><kbd>I</kbd></dt><dd>inbox</dd></div>
32 }
33 <div><dt><kbd>g h</kbd></dt><dd>home</dd></div>
34 <div><dt><kbd>w</kbd></dt><dd>wide mode: use the whole width of the screen, or not (remembered)</dd></div>
35 <div><dt><kbd>y</kbd></dt><dd>copy a permalink pinned to the commit</dd></div>
36 <div><dt><kbd>d</kbd> <kbd>u</kbd> <kbd>x</kbd></dt><dd>diff: split / unified / collapse or expand all</dd></div>
37 <div><dt><kbd>e</kbd> <kbd>E</kbd></dt><dd>diff: the whole file in view / every file, with the lines the hunks hide</dd></div>
38 <div><dt><kbd>n</kbd></dt><dd>merge request: next note in the diff</dd></div>
39 if mr {
40 <div><dt><kbd>g f</kbd></dt><dd>merge request: the list of files (j k move, enter opens, esc back)</dd></div>
41 <div><dt><kbd>M</kbd></dt><dd>merge request: the merge menu; there 1–3 pick the mode, D deletes the branch, enter merges</dd></div>
42 <div><dt><kbd>R</kbd></dt><dd>merge request: mark the head read</dd></div>
43 }
44 <div><dt><kbd>esc</kbd></dt><dd>close a dialog, leave a field</dd></div>
45 </dl>
46 </dialog>
47}
48
49// OverlayRefs is the ref switcher. It exists only inside a repository.
50templ OverlayRefs(l Layout) {
51 if l.Ref != "" {
52 <dialog id="panel-refs" class="panel modal" aria-label="Switch ref" hx-on:keydown="event.key === 'Escape' && (event.preventDefault(), this.close())" hx-on:click="event.target === this && this.close()">
53 <div class="panel-head">
54 <label for="refs-filter" class="sr">filter branches and tags</label>
55 <input
56 id="refs-filter"
57 type="search"
58 placeholder="Switch branch or tag…"
59 autocomplete="off"
60 data-pick="#panel-refs li"
61 hx-on:input="ermite.filter(this)"
62 />
63 <button type="button" class="panel-x" aria-label="Close" hx-on:click="closest('dialog').close()"><kbd>esc</kbd></button>
64 </div>
65 <div class="panel-cols" data-picklist>
66 <section aria-label="Branches">
67 <h2>Branches <span class="count">{ itoa(len(l.Heads)) }</span></h2>
68 <ul>
69 for _, r := range l.Heads {
70 @refItem(r)
71 }
72 </ul>
73 </section>
74 <section aria-label="Tags">
75 <h2>Tags <span class="count">{ itoa(len(l.Tags)) }</span></h2>
76 <ul>
77 for _, r := range l.Tags {
78 @refItem(r)
79 }
80 </ul>
81 </section>
82 </div>
83 </dialog>
84 }
85}
86
87templ refItem(r RefItem) {
88 <li>
89 <a
90 href={ templ.SafeURL(r.Href) }
91 if r.Current {
92 aria-current="true"
93 }
94 >
95 <span class="mark" aria-hidden="true">
96 if r.Current {
97 ●
98 } else {
99
100 }
101 </span>{ r.Name }
102 </a>
103 </li>
104}
105
106// OverlayCmd is the ":" command line: one filterable list mixing sections,
107// refs and repos.
108templ OverlayCmd(items []CmdItem) {
109 <dialog id="panel-cmd" class="panel modal" aria-label="Go to" hx-on:close="ermiteFinder.paletteClose()" hx-on:keydown="event.key === 'Escape' && (event.preventDefault(), this.close())" hx-on:click="event.target === this && this.close()">
110 <div class="panel-head">
111 <label for="cmd-q" class="sr">go to</label>
112 <input
113 id="cmd-q"
114 type="search"
115 placeholder="Go to a section, branch, tag, file or repository…"
116 autocomplete="off"
117 spellcheck="false"
118 role="combobox"
119 aria-autocomplete="list"
120 aria-expanded="true"
121 aria-controls="cmd-list"
122 aria-activedescendant="cmd-o0"
123 hx-on:input="ermite.cmdFilter(this); ermiteFinder.palette(this)"
124 hx-on:keydown="ermite.cmdKey(event)"
125 />
126 <button type="button" class="panel-x" aria-label="Close" hx-on:click="closest('dialog').close()"><kbd>esc</kbd></button>
127 </div>
128 <ul id="cmd-list" class="cmd-list" role="listbox" aria-label="Targets">
129 for i, c := range items {
130 <li id={ "cmd-o" + itoa(i) } role="option" aria-selected={ strconv.FormatBool(i == 0) } data-k={ strings.ToLower(c.Kind + " " + c.Label) } data-cursor?={ i == 0 }><a href={ templ.SafeURL(c.Href) } tabindex="-1"><span class="lbl">{ c.Label }</span><span class="k">{ c.Kind }</span></a></li>
131 }
132 <li id={ "cmd-o" + itoa(len(items)) } role="option" aria-selected="false" data-k="action turn single-key shortcuts on off keyboard"><a href="#" data-fire="keys" tabindex="-1"><span class="lbl">turn single-key shortcuts off</span><span class="k">action</span></a></li>
133 </ul>
134 <p class="panel-foot"><span><kbd>↑</kbd><kbd>↓</kbd> move</span><span><kbd>enter</kbd> open</span><span><kbd>esc</kbd> close</span></p>
135 </dialog>
136}