detached 5e16168

#30 feat(web): release card, latest release in About, g-sequence shortcuts

state
merged · 7c1886f · squash
branch
feat/release-view-ux → main
opened
bnema ·
updated
CommitSubjectAuthorDateSignatureStatus
5e16168fix(releases): resolve the latest release from one snapshot and keep the index cheap to maintainbriceGPG Verified
879e0cfperf(releases): index the latest release per repositorybriceGPG Verified
3b03a2etest(app): update web goldens for g sequence shortcutsbriceGPG Verified
eb30609fix(web): drop sequence aria-keyshortcuts and use fake timers in keyboard testsbriceGPG Verified
a8c4b10test(web): cover g sequences in the jstest suitebriceGPG Verified
c3ae830feat(web): navigate sections with g sequences and fix uppercase action keysbriceGPG Verified
e08572cfeat(web): show releases as one card with prose notes and the latest release in AboutbriceGPG Verified

Checks 4

73 files · +2058 -1284
4/4
M README.md +1 -0
M docs/issues.md +1 -1
A docs/keyboard-shortcuts.md +30 -0
M docs/merge-requests.md +1 -1
M internal/adapters/kvstore/issues_test.go +1 -1
M internal/adapters/kvstore/releases.go +28 -2
A internal/adapters/kvstore/releases_index.go +67 -0
A internal/adapters/kvstore/releases_index_test.go +97 -0
M internal/adapters/kvstore/store.go +6 -1
M internal/adapters/web/fragments.go +1 -1
M internal/adapters/web/fragments_test.go +3 -3
M internal/adapters/web/issues_test.go +3 -3
A internal/adapters/web/jstest/keyboard.test.mjs +76 -0
D internal/adapters/web/jstest/keys.test.mjs +0 -29
M internal/adapters/web/keys.templ +3 -2
M internal/adapters/web/keys_templ.go +1 -1
A internal/adapters/web/keys_test.go +26 -0
M internal/adapters/web/mr.templ +1 -1
M internal/adapters/web/mr_templ.go +1 -1
M internal/adapters/web/mr_test.go +1 -1
M internal/adapters/web/overlays.templ +2 -2
M internal/adapters/web/overlays_templ.go +2 -2
M internal/adapters/web/pages.templ +8 -0
M internal/adapters/web/pages_templ.go +371 -261
M internal/adapters/web/releases.templ +16 -9
M internal/adapters/web/releases_templ.go +32 -28 generated file: collapsed
M internal/adapters/web/releases_test.go +12 -2
@@ -38,7 +38,7 @@ func newReleaseFixture(t *testing.T) *releaseFixture {
3838 t.Helper()
3939 repo := repoName(t, "proj")
4040 f := &releaseFixture{svc: mocks.NewReleaseService(t), data: "0123456789"}
41- f.published = domain.Release{ID: 1, Repo: repo, Tag: "stable/v1", Title: "First release", Notes: "# Notes\n\n**Ready** [bad](javascript:alert(1)) <script>alert(1)</script>\n\n[guide](guide.md)", TargetCommitish: strings.Repeat("a", 40), PublishedAt: fixedNow, Prerelease: true}
41+ f.published = domain.Release{ID: 1, Repo: repo, Tag: "stable/v1", Title: "First release", Notes: "# Notes\n\n**Ready** [bad](javascript:alert(1)) <script>alert(1)</script>\n\n[guide](guide.md)\n\n```sh\ncurl https://example.com\n```", TargetCommitish: strings.Repeat("a", 40), PublishedAt: fixedNow, Prerelease: true}
4242 f.draft = domain.Release{ID: 2, Repo: repo, Tag: "unpublished", Title: "Secret draft", Draft: true, MakeLatest: true}
4343 f.asset = domain.ReleaseAsset{ID: 7, ReleaseID: 1, Repo: repo, Name: "build.zip", Size: 10, ContentType: "application/zip", Digest: "sha256:" + strings.Repeat("b", 64)}
4444 f.svc.EXPECT().GetRelease(mock.Anything, mock.Anything, repo, mock.Anything).RunAndReturn(func(_ context.Context, p domain.Principal, _ domain.RepoName, ref ports.ReleaseRef) (domain.Release, error) {
@@ -120,7 +120,8 @@ func TestReleasePages(t *testing.T) {
120120 assert.Contains(t, body, "2026-10-12")
121121 assert.Contains(t, body, `<td>1</td>`)
122122 assert.Contains(t, body, `?page=next%2B%2F%3D`)
123- assert.Contains(t, body, `aria-keyshortcuts="l"`)
123+ assert.Contains(t, body, `data-key="g r"`)
124+ assert.NotContains(t, body, `aria-keyshortcuts="g r"`)
124125 assert.Contains(t, body, "view=releases")
125126 }
126127 owner := f.request("GET", "/proj/releases?page=cursor%2B%2F%3D", "tok", "")
@@ -137,6 +138,15 @@ func TestReleasePages(t *testing.T) {
137138 for _, text := range []string{"<strong>Ready</strong>", "build.zip", humanSize(10), "application/zip", `href="/proj/releases/assets/7"`, `href="/proj/tree/stable/v1"`, `href="/proj/commit/` + strings.Repeat("a", 40) + `"`, "view=release"} {
138139 assert.Contains(t, body, text)
139140 }
141+ notes := strings.SplitN(body, `<article class="mr-desc prose">`, 2)
142+ require.Len(t, notes, 2)
143+ notesBody := strings.SplitN(notes[1], "</article>", 2)[0]
144+ assert.Contains(t, notesBody, "<pre")
145+ assert.Contains(t, notesBody, "<code")
146+ assert.Contains(t, notesBody, "curl")
147+ assert.Contains(t, notesBody, "</code></pre>")
148+ assert.Contains(t, notesBody, `<h1 id="md-notes">Notes</h1>`)
149+ assert.Contains(t, body, `<section class="mr-card">`)
140150 assert.NotContains(t, body, "<script>alert")
141151 assert.NotContains(t, body, `href="javascript:`)
142152 assert.Contains(t, body, `href="/proj/blob/stable/v1/guide.md"`)
@@ -38,7 +38,7 @@ func newReleaseFixture(t *testing.T) *releaseFixture {
38 t.Helper()38 t.Helper()
39 repo := repoName(t, "proj")39 repo := repoName(t, "proj")
40 f := &releaseFixture{svc: mocks.NewReleaseService(t), data: "0123456789"}40 f := &releaseFixture{svc: mocks.NewReleaseService(t), data: "0123456789"}
41- f.published = domain.Release{ID: 1, Repo: repo, Tag: "stable/v1", Title: "First release", Notes: "# Notes\n\n**Ready** [bad](javascript:alert(1)) <script>alert(1)</script>\n\n[guide](guide.md)", TargetCommitish: strings.Repeat("a", 40), PublishedAt: fixedNow, Prerelease: true}41+ f.published = domain.Release{ID: 1, Repo: repo, Tag: "stable/v1", Title: "First release", Notes: "# Notes\n\n**Ready** [bad](javascript:alert(1)) <script>alert(1)</script>\n\n[guide](guide.md)\n\n```sh\ncurl https://example.com\n```", TargetCommitish: strings.Repeat("a", 40), PublishedAt: fixedNow, Prerelease: true}
42 f.draft = domain.Release{ID: 2, Repo: repo, Tag: "unpublished", Title: "Secret draft", Draft: true, MakeLatest: true}42 f.draft = domain.Release{ID: 2, Repo: repo, Tag: "unpublished", Title: "Secret draft", Draft: true, MakeLatest: true}
43 f.asset = domain.ReleaseAsset{ID: 7, ReleaseID: 1, Repo: repo, Name: "build.zip", Size: 10, ContentType: "application/zip", Digest: "sha256:" + strings.Repeat("b", 64)}43 f.asset = domain.ReleaseAsset{ID: 7, ReleaseID: 1, Repo: repo, Name: "build.zip", Size: 10, ContentType: "application/zip", Digest: "sha256:" + strings.Repeat("b", 64)}
44 f.svc.EXPECT().GetRelease(mock.Anything, mock.Anything, repo, mock.Anything).RunAndReturn(func(_ context.Context, p domain.Principal, _ domain.RepoName, ref ports.ReleaseRef) (domain.Release, error) {44 f.svc.EXPECT().GetRelease(mock.Anything, mock.Anything, repo, mock.Anything).RunAndReturn(func(_ context.Context, p domain.Principal, _ domain.RepoName, ref ports.ReleaseRef) (domain.Release, error) {
@@ -120,7 +120,8 @@ func TestReleasePages(t *testing.T) {
120 assert.Contains(t, body, "2026-10-12")120 assert.Contains(t, body, "2026-10-12")
121 assert.Contains(t, body, `<td>1</td>`)121 assert.Contains(t, body, `<td>1</td>`)
122 assert.Contains(t, body, `?page=next%2B%2F%3D`)122 assert.Contains(t, body, `?page=next%2B%2F%3D`)
123- assert.Contains(t, body, `aria-keyshortcuts="l"`)123+ assert.Contains(t, body, `data-key="g r"`)
124+ assert.NotContains(t, body, `aria-keyshortcuts="g r"`)
124 assert.Contains(t, body, "view=releases")125 assert.Contains(t, body, "view=releases")
125 }126 }
126 owner := f.request("GET", "/proj/releases?page=cursor%2B%2F%3D", "tok", "")127 owner := f.request("GET", "/proj/releases?page=cursor%2B%2F%3D", "tok", "")
@@ -137,6 +138,15 @@ func TestReleasePages(t *testing.T) {
137 for _, text := range []string{"<strong>Ready</strong>", "build.zip", humanSize(10), "application/zip", `href="/proj/releases/assets/7"`, `href="/proj/tree/stable/v1"`, `href="/proj/commit/` + strings.Repeat("a", 40) + `"`, "view=release"} {138 for _, text := range []string{"<strong>Ready</strong>", "build.zip", humanSize(10), "application/zip", `href="/proj/releases/assets/7"`, `href="/proj/tree/stable/v1"`, `href="/proj/commit/` + strings.Repeat("a", 40) + `"`, "view=release"} {
138 assert.Contains(t, body, text)139 assert.Contains(t, body, text)
139 }140 }
141+ notes := strings.SplitN(body, `<article class="mr-desc prose">`, 2)
142+ require.Len(t, notes, 2)
143+ notesBody := strings.SplitN(notes[1], "</article>", 2)[0]
144+ assert.Contains(t, notesBody, "<pre")
145+ assert.Contains(t, notesBody, "<code")
146+ assert.Contains(t, notesBody, "curl")
147+ assert.Contains(t, notesBody, "</code></pre>")
148+ assert.Contains(t, notesBody, `<h1 id="md-notes">Notes</h1>`)
149+ assert.Contains(t, body, `<section class="mr-card">`)
140 assert.NotContains(t, body, "<script>alert")150 assert.NotContains(t, body, "<script>alert")
141 assert.NotContains(t, body, `href="javascript:`)151 assert.NotContains(t, body, `href="javascript:`)
142 assert.Contains(t, body, `href="/proj/blob/stable/v1/guide.md"`)152 assert.Contains(t, body, `href="/proj/blob/stable/v1/guide.md"`)
M internal/adapters/web/repo.go +4 -4
@@ -144,9 +144,9 @@ func (s *Server) repoLayout(ctx context.Context, d *repoData, section string) La
144144 }
145145 l.Sections = []Section{
146146 mk("s", "Summary", repoHref(d.name()), "summary"),
147- mk("t", "Files", repoHref(d.name(), "tree", def), "tree"),
147+ mk("f", "Files", repoHref(d.name(), "tree", def), "tree"),
148148 mk("c", "Commits", repoHref(d.name(), "commits", def), "commits"),
149- mk("r", "Branches & tags", repoHref(d.name(), "refs"), "refs"),
149+ mk("b", "Branches & tags", repoHref(d.name(), "refs"), "refs"),
150150 }
151151 if s.cfg.MergeRequests != nil {
152152 mrs := mk("m", "Merge requests", mrBase(d.name()), "merge-requests")
@@ -157,14 +157,14 @@ func (s *Server) repoLayout(ctx context.Context, d *repoData, section string) La
157157 l.Sections = append(l.Sections, mk("i", "Issues", issuesBase(d.name()), "issues"))
158158 }
159159 if s.cfg.Releases != nil {
160- l.Sections = append(l.Sections, mk("l", "Releases", releasesBase(d.name()), "releases"))
160+ l.Sections = append(l.Sections, mk("r", "Releases", releasesBase(d.name()), "releases"))
161161 }
162162 l.Sections = s.withActions(l.Sections, d.name(), section)
163163 l.Crumbs = []Crumb{{Label: d.name(), Href: repoHref(d.name()), Current: section == "summary"}}
164164
165165 l.Cmd = append(l.Cmd, CmdItem{Kind: "go", Label: "home", Href: "/"})
166166 for _, sec := range l.Sections {
167- l.Cmd = append(l.Cmd, CmdItem{Kind: "section", Label: strings.ToLower(sec.Label), Href: sec.Href})
167+ l.Cmd = append(l.Cmd, CmdItem{Kind: "section", Label: strings.ToLower(sec.Label) + " (g " + sec.Key + ")", Href: sec.Href})
168168 }
169169 if l.Ref != "" {
170170 for _, b := range d.branches {
@@ -144,9 +144,9 @@ func (s *Server) repoLayout(ctx context.Context, d *repoData, section string) La
144 }144 }
145 l.Sections = []Section{145 l.Sections = []Section{
146 mk("s", "Summary", repoHref(d.name()), "summary"),146 mk("s", "Summary", repoHref(d.name()), "summary"),
147- mk("t", "Files", repoHref(d.name(), "tree", def), "tree"),147+ mk("f", "Files", repoHref(d.name(), "tree", def), "tree"),
148 mk("c", "Commits", repoHref(d.name(), "commits", def), "commits"),148 mk("c", "Commits", repoHref(d.name(), "commits", def), "commits"),
149- mk("r", "Branches & tags", repoHref(d.name(), "refs"), "refs"),149+ mk("b", "Branches & tags", repoHref(d.name(), "refs"), "refs"),
150 }150 }
151 if s.cfg.MergeRequests != nil {151 if s.cfg.MergeRequests != nil {
152 mrs := mk("m", "Merge requests", mrBase(d.name()), "merge-requests")152 mrs := mk("m", "Merge requests", mrBase(d.name()), "merge-requests")
@@ -157,14 +157,14 @@ func (s *Server) repoLayout(ctx context.Context, d *repoData, section string) La
157 l.Sections = append(l.Sections, mk("i", "Issues", issuesBase(d.name()), "issues"))157 l.Sections = append(l.Sections, mk("i", "Issues", issuesBase(d.name()), "issues"))
158 }158 }
159 if s.cfg.Releases != nil {159 if s.cfg.Releases != nil {
160- l.Sections = append(l.Sections, mk("l", "Releases", releasesBase(d.name()), "releases"))160+ l.Sections = append(l.Sections, mk("r", "Releases", releasesBase(d.name()), "releases"))
161 }161 }
162 l.Sections = s.withActions(l.Sections, d.name(), section)162 l.Sections = s.withActions(l.Sections, d.name(), section)
163 l.Crumbs = []Crumb{{Label: d.name(), Href: repoHref(d.name()), Current: section == "summary"}}163 l.Crumbs = []Crumb{{Label: d.name(), Href: repoHref(d.name()), Current: section == "summary"}}
164 164
165 l.Cmd = append(l.Cmd, CmdItem{Kind: "go", Label: "home", Href: "/"})165 l.Cmd = append(l.Cmd, CmdItem{Kind: "go", Label: "home", Href: "/"})
166 for _, sec := range l.Sections {166 for _, sec := range l.Sections {
167- l.Cmd = append(l.Cmd, CmdItem{Kind: "section", Label: strings.ToLower(sec.Label), Href: sec.Href})167+ l.Cmd = append(l.Cmd, CmdItem{Kind: "section", Label: strings.ToLower(sec.Label) + " (g " + sec.Key + ")", Href: sec.Href})
168 }168 }
169 if l.Ref != "" {169 if l.Ref != "" {
170 for _, b := range d.branches {170 for _, b := range d.branches {
M internal/adapters/web/shell.templ +2 -3
@@ -172,8 +172,7 @@ templ Header(l Layout) {
172172 <li>
173173 <a
174174 href={ templ.SafeURL(s.Href) }
175- data-key={ s.Key }
176- aria-keyshortcuts={ s.Key }
175+ data-key={ "g " + s.Key }
177176 if s.Current {
178177 aria-current="page"
179178 }
@@ -182,7 +181,7 @@ templ Header(l Layout) {
182181 if s.Count != "" {
183182 <span class="count">{ s.Count }<span class="sr">{ " open" }</span></span>
184183 }
185- <kbd aria-hidden="true">{ s.Key }</kbd>
184+ <kbd aria-hidden="true">{ "g " + s.Key }</kbd>
186185 </a>
187186 </li>
188187 }
@@ -172,8 +172,7 @@ templ Header(l Layout) {
172 <li>172 <li>
173 <a173 <a
174 href={ templ.SafeURL(s.Href) }174 href={ templ.SafeURL(s.Href) }
175- data-key={ s.Key }175+ data-key={ "g " + s.Key }
176- aria-keyshortcuts={ s.Key }
177 if s.Current {176 if s.Current {
178 aria-current="page"177 aria-current="page"
179 }178 }
@@ -182,7 +181,7 @@ templ Header(l Layout) {
182 if s.Count != "" {181 if s.Count != "" {
183 <span class="count">{ s.Count }<span class="sr">{ " open" }</span></span>182 <span class="count">{ s.Count }<span class="sr">{ " open" }</span></span>
184 }183 }
185- <kbd aria-hidden="true">{ s.Key }</kbd>184+ <kbd aria-hidden="true">{ "g " + s.Key }</kbd>
186 </a>185 </a>
187 </li>186 </li>
188 }187 }
M internal/adapters/web/shell_templ.go +48 -61 generated file: collapsed
M internal/adapters/web/static/js/ermite.js +13 -4
@@ -112,7 +112,7 @@
112112 $(sel)?.click();
113113 },
114114 tab(key) {
115- $(`.tabs [data-key=${key}]`)?.click();
115+ $(`.tabs [data-key="g ${key}"]`)?.click();
116116 },
117117
118118 // The preference "single-key shortcuts" (ermiteKeys): the checkbox of the
@@ -617,11 +617,11 @@
617617
618618 // 1. Keys.
619619 const single = { j: 'j', k: 'k', G: 'bottom', '/': 'filter', '?': 'help', b: 'refs', f: 'cmd', ':': 'cmd', y: 'permalink',
620- s: 's', t: 't', c: 'c', r: 'r', m: 'm', i: 'i', I: 'inbox', n: 'next', h: 'left', l: 'right', d: 'split', u: 'unified', x: 'collapse', w: 'wide', e: 'expand', E: 'expandAll', Enter: 'open', Escape: 'esc',
620+ I: 'inbox', n: 'next', h: 'left', l: 'right', d: 'split', u: 'unified', x: 'collapse', w: 'wide', e: 'expand', E: 'expandAll', Enter: 'open', Escape: 'esc',
621621 // the merge request page (mr.js): the merge menu, mark read
622622 M: 'merge', R: 'read' };
623- // "g t" is the key "t": the tabs have one event each.
624- const goto = { h: 'gh', t: 't', c: 'c', r: 'r', m: 'm', i: 'i', g: 'top', f: 'gf' };
623+ // Section keys are the second letter of the g sequence.
624+ const goto = { h: 'gh', s: 's', f: 'f', c: 'c', b: 'b', m: 'm', i: 'i', r: 'r', a: 'a', g: 'top' };
625625 let pending = 0;
626626 let pendingTimer = 0;
627627 addEventListener('keydown', (e) => {
@@ -647,6 +647,15 @@
647647 // behind it does not react (scripted clicks would go through inert).
648648 if ($('dialog[open]') && !['?', 'b', ':', 'f', 'Enter'].includes(e.key)) { pending = 0; return; }
649649 if (e.key === 'Enter' && e.target.closest('a, button, summary')) return;
650+ // Some layouts report lowercase e.key even with Shift. Uppercase actions
651+ // also interrupt a pending g rather than being swallowed by it.
652+ const actionKey = e.shiftKey ? e.key.toUpperCase() : e.key;
653+ if (['G', 'M', 'R', 'E', 'I'].includes(actionKey)) {
654+ pending = 0;
655+ if (actionKey === 'I' && !$('[data-inbox]')) return;
656+ e.preventDefault();
657+ return ermite.fire(single[actionKey]);
658+ }
650659 if (pending) {
651660 const name = goto[e.key];
652661 pending = 0;
@@ -112,7 +112,7 @@
112 $(sel)?.click();112 $(sel)?.click();
113 },113 },
114 tab(key) {114 tab(key) {
115- $(`.tabs [data-key=${key}]`)?.click();115+ $(`.tabs [data-key="g ${key}"]`)?.click();
116 },116 },
117 117
118 // The preference "single-key shortcuts" (ermiteKeys): the checkbox of the118 // The preference "single-key shortcuts" (ermiteKeys): the checkbox of the
@@ -617,11 +617,11 @@
617 617
618 // 1. Keys.618 // 1. Keys.
619 const single = { j: 'j', k: 'k', G: 'bottom', '/': 'filter', '?': 'help', b: 'refs', f: 'cmd', ':': 'cmd', y: 'permalink',619 const single = { j: 'j', k: 'k', G: 'bottom', '/': 'filter', '?': 'help', b: 'refs', f: 'cmd', ':': 'cmd', y: 'permalink',
620- s: 's', t: 't', c: 'c', r: 'r', m: 'm', i: 'i', I: 'inbox', n: 'next', h: 'left', l: 'right', d: 'split', u: 'unified', x: 'collapse', w: 'wide', e: 'expand', E: 'expandAll', Enter: 'open', Escape: 'esc',620+ I: 'inbox', n: 'next', h: 'left', l: 'right', d: 'split', u: 'unified', x: 'collapse', w: 'wide', e: 'expand', E: 'expandAll', Enter: 'open', Escape: 'esc',
621 // the merge request page (mr.js): the merge menu, mark read621 // the merge request page (mr.js): the merge menu, mark read
622 M: 'merge', R: 'read' };622 M: 'merge', R: 'read' };
623- // "g t" is the key "t": the tabs have one event each.623+ // Section keys are the second letter of the g sequence.
624- const goto = { h: 'gh', t: 't', c: 'c', r: 'r', m: 'm', i: 'i', g: 'top', f: 'gf' };624+ const goto = { h: 'gh', s: 's', f: 'f', c: 'c', b: 'b', m: 'm', i: 'i', r: 'r', a: 'a', g: 'top' };
625 let pending = 0;625 let pending = 0;
626 let pendingTimer = 0;626 let pendingTimer = 0;
627 addEventListener('keydown', (e) => {627 addEventListener('keydown', (e) => {
@@ -647,6 +647,15 @@
647 // behind it does not react (scripted clicks would go through inert).647 // behind it does not react (scripted clicks would go through inert).
648 if ($('dialog[open]') && !['?', 'b', ':', 'f', 'Enter'].includes(e.key)) { pending = 0; return; }648 if ($('dialog[open]') && !['?', 'b', ':', 'f', 'Enter'].includes(e.key)) { pending = 0; return; }
649 if (e.key === 'Enter' && e.target.closest('a, button, summary')) return;649 if (e.key === 'Enter' && e.target.closest('a, button, summary')) return;
650+ // Some layouts report lowercase e.key even with Shift. Uppercase actions
651+ // also interrupt a pending g rather than being swallowed by it.
652+ const actionKey = e.shiftKey ? e.key.toUpperCase() : e.key;
653+ if (['G', 'M', 'R', 'E', 'I'].includes(actionKey)) {
654+ pending = 0;
655+ if (actionKey === 'I' && !$('[data-inbox]')) return;
656+ e.preventDefault();
657+ return ermite.fire(single[actionKey]);
658+ }
650 if (pending) {659 if (pending) {
651 const name = goto[e.key];660 const name = goto[e.key];
652 pending = 0;661 pending = 0;
M internal/adapters/web/static/js/mr.js +2 -2
@@ -8,7 +8,7 @@
88 // on its button, 1-9 pick a mode, D toggles the branch deletion, Enter
99 // submits (the browser does it: the focus is on the button), Esc closes.
1010 // R marks the head read. Nothing merges on one key.
11-// 3. g f puts the focus in the list of files (opening it when folded); j / k
11+// 3. l puts the focus in the list of files (opening it when folded); j / k
1212 // move in it, Enter opens the file, Esc goes back to the diff. The button
1313 // of its header folds the list, and the choice is remembered.
1414 //
@@ -186,7 +186,7 @@ window.ermiteMR = (() => {
186186 return true;
187187 }
188188
189- // The list of files by keyboard: g f focuses the current file.
189+ // The list of files by keyboard: l focuses the current file.
190190 const links = () => $$('.r-files .files > li:not([hidden]) > a');
191191 function focusFiles() {
192192 foldFiles(false);
@@ -8,7 +8,7 @@
8 // on its button, 1-9 pick a mode, D toggles the branch deletion, Enter8 // on its button, 1-9 pick a mode, D toggles the branch deletion, Enter
9 // submits (the browser does it: the focus is on the button), Esc closes.9 // submits (the browser does it: the focus is on the button), Esc closes.
10 // R marks the head read. Nothing merges on one key.10 // R marks the head read. Nothing merges on one key.
11-// 3. g f puts the focus in the list of files (opening it when folded); j / k11+// 3. l puts the focus in the list of files (opening it when folded); j / k
12 // move in it, Enter opens the file, Esc goes back to the diff. The button12 // move in it, Enter opens the file, Esc goes back to the diff. The button
13 // of its header folds the list, and the choice is remembered.13 // of its header folds the list, and the choice is remembered.
14 //14 //
@@ -186,7 +186,7 @@ window.ermiteMR = (() => {
186 return true;186 return true;
187 }187 }
188 188
189- // The list of files by keyboard: g f focuses the current file.189+ // The list of files by keyboard: l focuses the current file.
190 const links = () => $$('.r-files .files > li:not([hidden]) > a');190 const links = () => $$('.r-files .files > li:not([hidden]) > a');
191 function focusFiles() {191 function focusFiles() {
192 foldFiles(false);192 foldFiles(false);
M internal/adapters/web/summary.go +21 -0
@@ -68,6 +68,7 @@ func (s *Server) summary(ctx context.Context, r *http.Request) (templ.Component,
6868
6969 p.Facts = s.facts(d, now)
7070 p.Latest = s.latest(ctx, d)
71+ p.Release = s.summaryRelease(ctx, d.repo.Name, now)
7172 p.Actions = s.recentActions(ctx, d.repo.Name, now)
7273
7374 if rd, err := s.readme(ctx, d, ref); err != nil {
@@ -272,3 +273,23 @@ func (s *Server) listRecentActions(ctx context.Context, repo domain.RepoName, no
272273 }
273274 return a, nil
274275 }
276+
277+// summaryRelease uses the same Latest selection as the releases page. A
278+// missing service or an unavailable release must not prevent repository browsing.
279+func (s *Server) summaryRelease(ctx context.Context, repo domain.RepoName, now time.Time) *LatestRelease {
280+ if s.cfg.Releases == nil {
281+ return nil
282+ }
283+ rel, err := s.cfg.Releases.GetRelease(ctx, principal(ctx), repo, ports.ReleaseRef{Latest: true})
284+ if err != nil {
285+ if !errors.Is(err, domain.ErrNotFound) {
286+ log := zerowrap.FromCtx(ctx)
287+ log.Warn().Err(err).Str("repo", repo.String()).Msg("read the latest release")
288+ }
289+ return nil
290+ }
291+ if rel.Draft || rel.PublishedAt.IsZero() {
292+ return nil
293+ }
294+ return &LatestRelease{Tag: rel.Tag, Href: releaseHref(repo.String(), rel.Tag), When: ago(now, rel.PublishedAt), WhenAt: rfc3339(rel.PublishedAt), AllHref: releasesBase(repo.String())}
295+}
@@ -68,6 +68,7 @@ func (s *Server) summary(ctx context.Context, r *http.Request) (templ.Component,
68 68
69 p.Facts = s.facts(d, now)69 p.Facts = s.facts(d, now)
70 p.Latest = s.latest(ctx, d)70 p.Latest = s.latest(ctx, d)
71+ p.Release = s.summaryRelease(ctx, d.repo.Name, now)
71 p.Actions = s.recentActions(ctx, d.repo.Name, now)72 p.Actions = s.recentActions(ctx, d.repo.Name, now)
72 73
73 if rd, err := s.readme(ctx, d, ref); err != nil {74 if rd, err := s.readme(ctx, d, ref); err != nil {
@@ -272,3 +273,23 @@ func (s *Server) listRecentActions(ctx context.Context, repo domain.RepoName, no
272 }273 }
273 return a, nil274 return a, nil
274 }275 }
276+
277+// summaryRelease uses the same Latest selection as the releases page. A
278+// missing service or an unavailable release must not prevent repository browsing.
279+func (s *Server) summaryRelease(ctx context.Context, repo domain.RepoName, now time.Time) *LatestRelease {
280+ if s.cfg.Releases == nil {
281+ return nil
282+ }
283+ rel, err := s.cfg.Releases.GetRelease(ctx, principal(ctx), repo, ports.ReleaseRef{Latest: true})
284+ if err != nil {
285+ if !errors.Is(err, domain.ErrNotFound) {
286+ log := zerowrap.FromCtx(ctx)
287+ log.Warn().Err(err).Str("repo", repo.String()).Msg("read the latest release")
288+ }
289+ return nil
290+ }
291+ if rel.Draft || rel.PublishedAt.IsZero() {
292+ return nil
293+ }
294+ return &LatestRelease{Tag: rel.Tag, Href: releaseHref(repo.String(), rel.Tag), When: ago(now, rel.PublishedAt), WhenAt: rfc3339(rel.PublishedAt), AllHref: releasesBase(repo.String())}
295+}
A internal/adapters/web/summary_releases_test.go +49 -0
@@ -0,0 +1,49 @@
1+// SPDX-License-Identifier: AGPL-3.0-or-later
2+
3+package web
4+
5+import (
6+ "strings"
7+ "testing"
8+ "time"
9+
10+ "github.com/stretchr/testify/assert"
11+ "github.com/stretchr/testify/mock"
12+ "github.com/stretchr/testify/require"
13+
14+ "bnema.dev/ermite/internal/domain"
15+ "bnema.dev/ermite/internal/ports"
16+ "bnema.dev/ermite/internal/ports/mocks"
17+)
18+
19+func TestSummaryRelease(t *testing.T) {
20+ repo := repoName(t, "proj")
21+ for _, tc := range []struct {
22+ name string
23+ release domain.Release
24+ err error
25+ visible bool
26+ }{
27+ {name: "published", release: domain.Release{Tag: "v0.9.1", PublishedAt: fixedNow.Add(-6 * time.Minute)}, visible: true},
28+ {name: "none", err: domain.ErrNotFound},
29+ {name: "draft", release: domain.Release{Tag: "secret", Draft: true, PublishedAt: fixedNow}},
30+ } {
31+ t.Run(tc.name, func(t *testing.T) {
32+ svc := mocks.NewReleaseService(t)
33+ svc.EXPECT().GetRelease(mock.Anything, mock.Anything, repo, ports.ReleaseRef{Latest: true}).Return(tc.release, tc.err).Once()
34+ srv := newTestServer(t, Config{Repos: reposServing(t, publicRepo(t, "proj")), Browse: mocks.NewBrowseService(t), Releases: svc})
35+ rel := srv.summaryRelease(t.Context(), repo, fixedNow)
36+ out := strings.Join(strings.Fields(renderRail(t, SummaryPage{Latest: &LatestTag{Name: "v1", Count: 1}, Release: rel})), " ")
37+ if !tc.visible {
38+ assert.Nil(t, rel)
39+ assert.NotContains(t, out, "<dt>release</dt>")
40+ return
41+ }
42+ require.NotNil(t, rel)
43+ assert.Contains(t, out, `<a href="/proj/releases/tag/v0.9.1">v0.9.1</a> · <time`)
44+ assert.Contains(t, out, `datetime="`+rfc3339(tc.release.PublishedAt)+`"`)
45+ assert.Contains(t, out, `>`+ago(fixedNow, tc.release.PublishedAt)+`</time> · <a href="/proj/releases">all releases</a>`)
46+ assert.Regexp(t, `(?s)<dt>latest tag</dt>.*<dt>release</dt>`, out)
47+ })
48+ }
49+}
@@ -0,0 +1,49 @@
1+// SPDX-License-Identifier: AGPL-3.0-or-later
2+
3+package web
4+
5+import (
6+ "strings"
7+ "testing"
8+ "time"
9+
10+ "github.com/stretchr/testify/assert"
11+ "github.com/stretchr/testify/mock"
12+ "github.com/stretchr/testify/require"
13+
14+ "bnema.dev/ermite/internal/domain"
15+ "bnema.dev/ermite/internal/ports"
16+ "bnema.dev/ermite/internal/ports/mocks"
17+)
18+
19+func TestSummaryRelease(t *testing.T) {
20+ repo := repoName(t, "proj")
21+ for _, tc := range []struct {
22+ name string
23+ release domain.Release
24+ err error
25+ visible bool
26+ }{
27+ {name: "published", release: domain.Release{Tag: "v0.9.1", PublishedAt: fixedNow.Add(-6 * time.Minute)}, visible: true},
28+ {name: "none", err: domain.ErrNotFound},
29+ {name: "draft", release: domain.Release{Tag: "secret", Draft: true, PublishedAt: fixedNow}},
30+ } {
31+ t.Run(tc.name, func(t *testing.T) {
32+ svc := mocks.NewReleaseService(t)
33+ svc.EXPECT().GetRelease(mock.Anything, mock.Anything, repo, ports.ReleaseRef{Latest: true}).Return(tc.release, tc.err).Once()
34+ srv := newTestServer(t, Config{Repos: reposServing(t, publicRepo(t, "proj")), Browse: mocks.NewBrowseService(t), Releases: svc})
35+ rel := srv.summaryRelease(t.Context(), repo, fixedNow)
36+ out := strings.Join(strings.Fields(renderRail(t, SummaryPage{Latest: &LatestTag{Name: "v1", Count: 1}, Release: rel})), " ")
37+ if !tc.visible {
38+ assert.Nil(t, rel)
39+ assert.NotContains(t, out, "<dt>release</dt>")
40+ return
41+ }
42+ require.NotNil(t, rel)
43+ assert.Contains(t, out, `<a href="/proj/releases/tag/v0.9.1">v0.9.1</a> · <time`)
44+ assert.Contains(t, out, `datetime="`+rfc3339(tc.release.PublishedAt)+`"`)
45+ assert.Contains(t, out, `>`+ago(fixedNow, tc.release.PublishedAt)+`</time> · <a href="/proj/releases">all releases</a>`)
46+ assert.Regexp(t, `(?s)<dt>latest tag</dt>.*<dt>release</dt>`, out)
47+ })
48+ }
49+}
M internal/adapters/web/view.go +7 -1
@@ -211,12 +211,18 @@ type SummaryPage struct {
211211 Outline []Heading
212212 AllHref string
213213 // Latest is the newest tag, nil when the repository has none.
214- Latest *LatestTag
214+ Latest *LatestTag
215+ Release *LatestRelease
215216 // Actions is the recent runs of the rail, nil when the viewer cannot read
216217 // CI (or the server runs none).
217218 Actions *RecentActions
218219 }
219220
221+// LatestRelease is the published release selected as Latest by the release service.
222+type LatestRelease struct {
223+ Tag, Href, When, WhenAt, AllHref string
224+}
225+
220226 // RecentActions is the "Recent actions" block of the summary rail: the newest
221227 // runs. With no run the block is in the page but hidden, so that the first
222228 // run reaches it live.
@@ -211,12 +211,18 @@ type SummaryPage struct {
211 Outline []Heading211 Outline []Heading
212 AllHref string212 AllHref string
213 // Latest is the newest tag, nil when the repository has none.213 // Latest is the newest tag, nil when the repository has none.
214- Latest *LatestTag214+ Latest *LatestTag
215+ Release *LatestRelease
215 // Actions is the recent runs of the rail, nil when the viewer cannot read216 // Actions is the recent runs of the rail, nil when the viewer cannot read
216 // CI (or the server runs none).217 // CI (or the server runs none).
217 Actions *RecentActions218 Actions *RecentActions
218 }219 }
219 220
221+// LatestRelease is the published release selected as Latest by the release service.
222+type LatestRelease struct {
223+ Tag, Href, When, WhenAt, AllHref string
224+}
225+
220 // RecentActions is the "Recent actions" block of the summary rail: the newest226 // RecentActions is the "Recent actions" block of the summary rail: the newest
221 // runs. With no run the block is in the page but hidden, so that the first227 // runs. With no run the block is in the page but hidden, so that the first
222 // run reaches it live.228 // run reaches it live.
M internal/app/testdata/web/blob-binary.html +33 -30
@@ -73,31 +73,31 @@
7373 <nav class="tabs" aria-label="Sections">
7474 <ul>
7575 <li>
76-<a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>
76+<a href="/files" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
7777 </a>
7878 </li>
7979 <li>
80-<a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd>
80+<a href="/files/tree/main" data-key="g f" aria-current="page">Files <kbd aria-hidden="true">g f</kbd>
8181 </a>
8282 </li>
8383 <li>
84-<a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd>
84+<a href="/files/commits/main" data-key="g c">Commits <kbd aria-hidden="true">g c</kbd>
8585 </a>
8686 </li>
8787 <li>
88-<a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>
88+<a href="/files/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
8989 </a>
9090 </li>
9191 <li>
92-<a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>
92+<a href="/files/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
9393 </a>
9494 </li>
9595 <li>
96-<a href="/files/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>
96+<a href="/files/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
9797 </a>
9898 </li>
9999 <li>
100-<a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>
100+<a href="/files/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
101101 </a>
102102 </li>
103103 </ul>
@@ -228,14 +228,16 @@
228228 </div>
229229 <div>
230230 <dt>
231-<kbd>s</kbd>
232-<kbd>t</kbd>
233-<kbd>c</kbd>
234-<kbd>r</kbd>
235-<kbd>m</kbd>
236-<kbd>i</kbd>
231+<kbd>g s</kbd>
232+<kbd>g f</kbd>
233+<kbd>g c</kbd>
234+<kbd>g b</kbd>
235+<kbd>g m</kbd>
236+<kbd>g i</kbd>
237+<kbd>g r</kbd>
238+<kbd>g a</kbd>
237239 </dt>
238-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>
240+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
239241 </div>
240242 <div>
241243 <dt>
@@ -334,45 +336,45 @@
334336 <span class="k">go</span>
335337 </a>
336338 </li>
337-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">
339+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
338340 <a role="presentation" data-href="/files">
339-<span class="lbl">summary</span>
341+<span class="lbl">summary (g s)</span>
340342 <span class="k">section</span>
341343 </a>
342344 </li>
343-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">
345+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
344346 <a role="presentation" data-href="/files/tree/main">
345-<span class="lbl">files</span>
347+<span class="lbl">files (g f)</span>
346348 <span class="k">section</span>
347349 </a>
348350 </li>
349-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">
351+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
350352 <a role="presentation" data-href="/files/commits/main">
351-<span class="lbl">commits</span>
353+<span class="lbl">commits (g c)</span>
352354 <span class="k">section</span>
353355 </a>
354356 </li>
355-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">
357+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
356358 <a role="presentation" data-href="/files/refs">
357-<span class="lbl">branches &amp; tags</span>
359+<span class="lbl">branches &amp; tags (g b)</span>
358360 <span class="k">section</span>
359361 </a>
360362 </li>
361-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">
363+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
362364 <a role="presentation" data-href="/files/merge-requests">
363-<span class="lbl">merge requests</span>
365+<span class="lbl">merge requests (g m)</span>
364366 <span class="k">section</span>
365367 </a>
366368 </li>
367-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">
369+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
368370 <a role="presentation" data-href="/files/releases">
369-<span class="lbl">releases</span>
371+<span class="lbl">releases (g r)</span>
370372 <span class="k">section</span>
371373 </a>
372374 </li>
373-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">
375+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
374376 <a role="presentation" data-href="/files/actions">
375-<span class="lbl">actions</span>
377+<span class="lbl">actions (g a)</span>
376378 <span class="k">section</span>
377379 </a>
378380 </li>
@@ -425,7 +427,9 @@
425427 k-filter from:body -> ermite.focusFilter();
426428 k-gh from:body -> ermite.go('.brand');
427429 k-s from:body -> ermite.tab('s');
428- k-t from:body -> ermite.tab('t');
430+ k-f from:body -> ermite.tab('f');
431+ k-b from:body -> ermite.tab('b');
432+ k-a from:body -> ermite.tab('a');
429433 k-c from:body -> ermite.tab('c');
430434 k-r from:body -> ermite.tab('r');
431435 k-m from:body -> ermite.tab('m');
@@ -441,7 +445,6 @@
441445 k-permalink from:body -> ermite.permalink();
442446 k-merge from:body -> window.ermiteMR?.openMerge();
443447 k-read from:body -> window.ermiteMR?.markRead();
444- k-gf from:body -> window.ermiteMR?.focusFiles();
445448 k-keys from:body -> ermite.toggleKeys()">
446449 </div>
447450 </body>
@@ -73,31 +73,31 @@
73 <nav class="tabs" aria-label="Sections">73 <nav class="tabs" aria-label="Sections">
74 <ul>74 <ul>
75 <li>75 <li>
76-<a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>76+<a href="/files" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
77 </a>77 </a>
78 </li>78 </li>
79 <li>79 <li>
80-<a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd>80+<a href="/files/tree/main" data-key="g f" aria-current="page">Files <kbd aria-hidden="true">g f</kbd>
81 </a>81 </a>
82 </li>82 </li>
83 <li>83 <li>
84-<a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd>84+<a href="/files/commits/main" data-key="g c">Commits <kbd aria-hidden="true">g c</kbd>
85 </a>85 </a>
86 </li>86 </li>
87 <li>87 <li>
88-<a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>88+<a href="/files/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
89 </a>89 </a>
90 </li>90 </li>
91 <li>91 <li>
92-<a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>92+<a href="/files/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
93 </a>93 </a>
94 </li>94 </li>
95 <li>95 <li>
96-<a href="/files/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>96+<a href="/files/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
97 </a>97 </a>
98 </li>98 </li>
99 <li>99 <li>
100-<a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>100+<a href="/files/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
101 </a>101 </a>
102 </li>102 </li>
103 </ul>103 </ul>
@@ -228,14 +228,16 @@
228 </div>228 </div>
229 <div>229 <div>
230 <dt>230 <dt>
231-<kbd>s</kbd>231+<kbd>g s</kbd>
232-<kbd>t</kbd>232+<kbd>g f</kbd>
233-<kbd>c</kbd>233+<kbd>g c</kbd>
234-<kbd>r</kbd>234+<kbd>g b</kbd>
235-<kbd>m</kbd>235+<kbd>g m</kbd>
236-<kbd>i</kbd>236+<kbd>g i</kbd>
237+<kbd>g r</kbd>
238+<kbd>g a</kbd>
237 </dt>239 </dt>
238-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>240+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
239 </div>241 </div>
240 <div>242 <div>
241 <dt>243 <dt>
@@ -334,45 +336,45 @@
334 <span class="k">go</span>336 <span class="k">go</span>
335 </a>337 </a>
336 </li>338 </li>
337-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">339+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
338 <a role="presentation" data-href="/files">340 <a role="presentation" data-href="/files">
339-<span class="lbl">summary</span>341+<span class="lbl">summary (g s)</span>
340 <span class="k">section</span>342 <span class="k">section</span>
341 </a>343 </a>
342 </li>344 </li>
343-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">345+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
344 <a role="presentation" data-href="/files/tree/main">346 <a role="presentation" data-href="/files/tree/main">
345-<span class="lbl">files</span>347+<span class="lbl">files (g f)</span>
346 <span class="k">section</span>348 <span class="k">section</span>
347 </a>349 </a>
348 </li>350 </li>
349-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">351+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
350 <a role="presentation" data-href="/files/commits/main">352 <a role="presentation" data-href="/files/commits/main">
351-<span class="lbl">commits</span>353+<span class="lbl">commits (g c)</span>
352 <span class="k">section</span>354 <span class="k">section</span>
353 </a>355 </a>
354 </li>356 </li>
355-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">357+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
356 <a role="presentation" data-href="/files/refs">358 <a role="presentation" data-href="/files/refs">
357-<span class="lbl">branches &amp; tags</span>359+<span class="lbl">branches &amp; tags (g b)</span>
358 <span class="k">section</span>360 <span class="k">section</span>
359 </a>361 </a>
360 </li>362 </li>
361-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">363+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
362 <a role="presentation" data-href="/files/merge-requests">364 <a role="presentation" data-href="/files/merge-requests">
363-<span class="lbl">merge requests</span>365+<span class="lbl">merge requests (g m)</span>
364 <span class="k">section</span>366 <span class="k">section</span>
365 </a>367 </a>
366 </li>368 </li>
367-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">369+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
368 <a role="presentation" data-href="/files/releases">370 <a role="presentation" data-href="/files/releases">
369-<span class="lbl">releases</span>371+<span class="lbl">releases (g r)</span>
370 <span class="k">section</span>372 <span class="k">section</span>
371 </a>373 </a>
372 </li>374 </li>
373-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">375+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
374 <a role="presentation" data-href="/files/actions">376 <a role="presentation" data-href="/files/actions">
375-<span class="lbl">actions</span>377+<span class="lbl">actions (g a)</span>
376 <span class="k">section</span>378 <span class="k">section</span>
377 </a>379 </a>
378 </li>380 </li>
@@ -425,7 +427,9 @@
425 k-filter from:body -> ermite.focusFilter();427 k-filter from:body -> ermite.focusFilter();
426 k-gh from:body -> ermite.go('.brand');428 k-gh from:body -> ermite.go('.brand');
427 k-s from:body -> ermite.tab('s');429 k-s from:body -> ermite.tab('s');
428- k-t from:body -> ermite.tab('t');430+ k-f from:body -> ermite.tab('f');
431+ k-b from:body -> ermite.tab('b');
432+ k-a from:body -> ermite.tab('a');
429 k-c from:body -> ermite.tab('c');433 k-c from:body -> ermite.tab('c');
430 k-r from:body -> ermite.tab('r');434 k-r from:body -> ermite.tab('r');
431 k-m from:body -> ermite.tab('m');435 k-m from:body -> ermite.tab('m');
@@ -441,7 +445,6 @@
441 k-permalink from:body -> ermite.permalink();445 k-permalink from:body -> ermite.permalink();
442 k-merge from:body -> window.ermiteMR?.openMerge();446 k-merge from:body -> window.ermiteMR?.openMerge();
443 k-read from:body -> window.ermiteMR?.markRead();447 k-read from:body -> window.ermiteMR?.markRead();
444- k-gf from:body -> window.ermiteMR?.focusFiles();
445 k-keys from:body -> ermite.toggleKeys()">448 k-keys from:body -> ermite.toggleKeys()">
446 </div>449 </div>
447 </body>450 </body>
M internal/app/testdata/web/blob-code.html +33 -30
@@ -73,31 +73,31 @@
7373 <nav class="tabs" aria-label="Sections">
7474 <ul>
7575 <li>
76-<a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>
76+<a href="/files" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
7777 </a>
7878 </li>
7979 <li>
80-<a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd>
80+<a href="/files/tree/main" data-key="g f" aria-current="page">Files <kbd aria-hidden="true">g f</kbd>
8181 </a>
8282 </li>
8383 <li>
84-<a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd>
84+<a href="/files/commits/main" data-key="g c">Commits <kbd aria-hidden="true">g c</kbd>
8585 </a>
8686 </li>
8787 <li>
88-<a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>
88+<a href="/files/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
8989 </a>
9090 </li>
9191 <li>
92-<a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>
92+<a href="/files/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
9393 </a>
9494 </li>
9595 <li>
96-<a href="/files/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>
96+<a href="/files/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
9797 </a>
9898 </li>
9999 <li>
100-<a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>
100+<a href="/files/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
101101 </a>
102102 </li>
103103 </ul>
@@ -298,14 +298,16 @@
298298 </div>
299299 <div>
300300 <dt>
301-<kbd>s</kbd>
302-<kbd>t</kbd>
303-<kbd>c</kbd>
304-<kbd>r</kbd>
305-<kbd>m</kbd>
306-<kbd>i</kbd>
301+<kbd>g s</kbd>
302+<kbd>g f</kbd>
303+<kbd>g c</kbd>
304+<kbd>g b</kbd>
305+<kbd>g m</kbd>
306+<kbd>g i</kbd>
307+<kbd>g r</kbd>
308+<kbd>g a</kbd>
307309 </dt>
308-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>
310+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
309311 </div>
310312 <div>
311313 <dt>
@@ -404,45 +406,45 @@
404406 <span class="k">go</span>
405407 </a>
406408 </li>
407-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">
409+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
408410 <a role="presentation" data-href="/files">
409-<span class="lbl">summary</span>
411+<span class="lbl">summary (g s)</span>
410412 <span class="k">section</span>
411413 </a>
412414 </li>
413-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">
415+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
414416 <a role="presentation" data-href="/files/tree/main">
415-<span class="lbl">files</span>
417+<span class="lbl">files (g f)</span>
416418 <span class="k">section</span>
417419 </a>
418420 </li>
419-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">
421+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
420422 <a role="presentation" data-href="/files/commits/main">
421-<span class="lbl">commits</span>
423+<span class="lbl">commits (g c)</span>
422424 <span class="k">section</span>
423425 </a>
424426 </li>
425-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">
427+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
426428 <a role="presentation" data-href="/files/refs">
427-<span class="lbl">branches &amp; tags</span>
429+<span class="lbl">branches &amp; tags (g b)</span>
428430 <span class="k">section</span>
429431 </a>
430432 </li>
431-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">
433+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
432434 <a role="presentation" data-href="/files/merge-requests">
433-<span class="lbl">merge requests</span>
435+<span class="lbl">merge requests (g m)</span>
434436 <span class="k">section</span>
435437 </a>
436438 </li>
437-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">
439+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
438440 <a role="presentation" data-href="/files/releases">
439-<span class="lbl">releases</span>
441+<span class="lbl">releases (g r)</span>
440442 <span class="k">section</span>
441443 </a>
442444 </li>
443-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">
445+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
444446 <a role="presentation" data-href="/files/actions">
445-<span class="lbl">actions</span>
447+<span class="lbl">actions (g a)</span>
446448 <span class="k">section</span>
447449 </a>
448450 </li>
@@ -495,7 +497,9 @@
495497 k-filter from:body -> ermite.focusFilter();
496498 k-gh from:body -> ermite.go('.brand');
497499 k-s from:body -> ermite.tab('s');
498- k-t from:body -> ermite.tab('t');
500+ k-f from:body -> ermite.tab('f');
501+ k-b from:body -> ermite.tab('b');
502+ k-a from:body -> ermite.tab('a');
499503 k-c from:body -> ermite.tab('c');
500504 k-r from:body -> ermite.tab('r');
501505 k-m from:body -> ermite.tab('m');
@@ -511,7 +515,6 @@
511515 k-permalink from:body -> ermite.permalink();
512516 k-merge from:body -> window.ermiteMR?.openMerge();
513517 k-read from:body -> window.ermiteMR?.markRead();
514- k-gf from:body -> window.ermiteMR?.focusFiles();
515518 k-keys from:body -> ermite.toggleKeys()">
516519 </div>
517520 </body>
@@ -73,31 +73,31 @@
73 <nav class="tabs" aria-label="Sections">73 <nav class="tabs" aria-label="Sections">
74 <ul>74 <ul>
75 <li>75 <li>
76-<a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>76+<a href="/files" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
77 </a>77 </a>
78 </li>78 </li>
79 <li>79 <li>
80-<a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd>80+<a href="/files/tree/main" data-key="g f" aria-current="page">Files <kbd aria-hidden="true">g f</kbd>
81 </a>81 </a>
82 </li>82 </li>
83 <li>83 <li>
84-<a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd>84+<a href="/files/commits/main" data-key="g c">Commits <kbd aria-hidden="true">g c</kbd>
85 </a>85 </a>
86 </li>86 </li>
87 <li>87 <li>
88-<a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>88+<a href="/files/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
89 </a>89 </a>
90 </li>90 </li>
91 <li>91 <li>
92-<a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>92+<a href="/files/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
93 </a>93 </a>
94 </li>94 </li>
95 <li>95 <li>
96-<a href="/files/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>96+<a href="/files/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
97 </a>97 </a>
98 </li>98 </li>
99 <li>99 <li>
100-<a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>100+<a href="/files/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
101 </a>101 </a>
102 </li>102 </li>
103 </ul>103 </ul>
@@ -298,14 +298,16 @@
298 </div>298 </div>
299 <div>299 <div>
300 <dt>300 <dt>
301-<kbd>s</kbd>301+<kbd>g s</kbd>
302-<kbd>t</kbd>302+<kbd>g f</kbd>
303-<kbd>c</kbd>303+<kbd>g c</kbd>
304-<kbd>r</kbd>304+<kbd>g b</kbd>
305-<kbd>m</kbd>305+<kbd>g m</kbd>
306-<kbd>i</kbd>306+<kbd>g i</kbd>
307+<kbd>g r</kbd>
308+<kbd>g a</kbd>
307 </dt>309 </dt>
308-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>310+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
309 </div>311 </div>
310 <div>312 <div>
311 <dt>313 <dt>
@@ -404,45 +406,45 @@
404 <span class="k">go</span>406 <span class="k">go</span>
405 </a>407 </a>
406 </li>408 </li>
407-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">409+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
408 <a role="presentation" data-href="/files">410 <a role="presentation" data-href="/files">
409-<span class="lbl">summary</span>411+<span class="lbl">summary (g s)</span>
410 <span class="k">section</span>412 <span class="k">section</span>
411 </a>413 </a>
412 </li>414 </li>
413-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">415+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
414 <a role="presentation" data-href="/files/tree/main">416 <a role="presentation" data-href="/files/tree/main">
415-<span class="lbl">files</span>417+<span class="lbl">files (g f)</span>
416 <span class="k">section</span>418 <span class="k">section</span>
417 </a>419 </a>
418 </li>420 </li>
419-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">421+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
420 <a role="presentation" data-href="/files/commits/main">422 <a role="presentation" data-href="/files/commits/main">
421-<span class="lbl">commits</span>423+<span class="lbl">commits (g c)</span>
422 <span class="k">section</span>424 <span class="k">section</span>
423 </a>425 </a>
424 </li>426 </li>
425-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">427+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
426 <a role="presentation" data-href="/files/refs">428 <a role="presentation" data-href="/files/refs">
427-<span class="lbl">branches &amp; tags</span>429+<span class="lbl">branches &amp; tags (g b)</span>
428 <span class="k">section</span>430 <span class="k">section</span>
429 </a>431 </a>
430 </li>432 </li>
431-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">433+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
432 <a role="presentation" data-href="/files/merge-requests">434 <a role="presentation" data-href="/files/merge-requests">
433-<span class="lbl">merge requests</span>435+<span class="lbl">merge requests (g m)</span>
434 <span class="k">section</span>436 <span class="k">section</span>
435 </a>437 </a>
436 </li>438 </li>
437-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">439+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
438 <a role="presentation" data-href="/files/releases">440 <a role="presentation" data-href="/files/releases">
439-<span class="lbl">releases</span>441+<span class="lbl">releases (g r)</span>
440 <span class="k">section</span>442 <span class="k">section</span>
441 </a>443 </a>
442 </li>444 </li>
443-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">445+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
444 <a role="presentation" data-href="/files/actions">446 <a role="presentation" data-href="/files/actions">
445-<span class="lbl">actions</span>447+<span class="lbl">actions (g a)</span>
446 <span class="k">section</span>448 <span class="k">section</span>
447 </a>449 </a>
448 </li>450 </li>
@@ -495,7 +497,9 @@
495 k-filter from:body -> ermite.focusFilter();497 k-filter from:body -> ermite.focusFilter();
496 k-gh from:body -> ermite.go('.brand');498 k-gh from:body -> ermite.go('.brand');
497 k-s from:body -> ermite.tab('s');499 k-s from:body -> ermite.tab('s');
498- k-t from:body -> ermite.tab('t');500+ k-f from:body -> ermite.tab('f');
501+ k-b from:body -> ermite.tab('b');
502+ k-a from:body -> ermite.tab('a');
499 k-c from:body -> ermite.tab('c');503 k-c from:body -> ermite.tab('c');
500 k-r from:body -> ermite.tab('r');504 k-r from:body -> ermite.tab('r');
501 k-m from:body -> ermite.tab('m');505 k-m from:body -> ermite.tab('m');
@@ -511,7 +515,6 @@
511 k-permalink from:body -> ermite.permalink();515 k-permalink from:body -> ermite.permalink();
512 k-merge from:body -> window.ermiteMR?.openMerge();516 k-merge from:body -> window.ermiteMR?.openMerge();
513 k-read from:body -> window.ermiteMR?.markRead();517 k-read from:body -> window.ermiteMR?.markRead();
514- k-gf from:body -> window.ermiteMR?.focusFiles();
515 k-keys from:body -> ermite.toggleKeys()">518 k-keys from:body -> ermite.toggleKeys()">
516 </div>519 </div>
517 </body>520 </body>
M internal/app/testdata/web/blob-empty.html +33 -30
@@ -73,31 +73,31 @@
7373 <nav class="tabs" aria-label="Sections">
7474 <ul>
7575 <li>
76-<a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>
76+<a href="/files" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
7777 </a>
7878 </li>
7979 <li>
80-<a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd>
80+<a href="/files/tree/main" data-key="g f" aria-current="page">Files <kbd aria-hidden="true">g f</kbd>
8181 </a>
8282 </li>
8383 <li>
84-<a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd>
84+<a href="/files/commits/main" data-key="g c">Commits <kbd aria-hidden="true">g c</kbd>
8585 </a>
8686 </li>
8787 <li>
88-<a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>
88+<a href="/files/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
8989 </a>
9090 </li>
9191 <li>
92-<a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>
92+<a href="/files/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
9393 </a>
9494 </li>
9595 <li>
96-<a href="/files/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>
96+<a href="/files/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
9797 </a>
9898 </li>
9999 <li>
100-<a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>
100+<a href="/files/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
101101 </a>
102102 </li>
103103 </ul>
@@ -228,14 +228,16 @@
228228 </div>
229229 <div>
230230 <dt>
231-<kbd>s</kbd>
232-<kbd>t</kbd>
233-<kbd>c</kbd>
234-<kbd>r</kbd>
235-<kbd>m</kbd>
236-<kbd>i</kbd>
231+<kbd>g s</kbd>
232+<kbd>g f</kbd>
233+<kbd>g c</kbd>
234+<kbd>g b</kbd>
235+<kbd>g m</kbd>
236+<kbd>g i</kbd>
237+<kbd>g r</kbd>
238+<kbd>g a</kbd>
237239 </dt>
238-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>
240+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
239241 </div>
240242 <div>
241243 <dt>
@@ -334,45 +336,45 @@
334336 <span class="k">go</span>
335337 </a>
336338 </li>
337-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">
339+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
338340 <a role="presentation" data-href="/files">
339-<span class="lbl">summary</span>
341+<span class="lbl">summary (g s)</span>
340342 <span class="k">section</span>
341343 </a>
342344 </li>
343-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">
345+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
344346 <a role="presentation" data-href="/files/tree/main">
345-<span class="lbl">files</span>
347+<span class="lbl">files (g f)</span>
346348 <span class="k">section</span>
347349 </a>
348350 </li>
349-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">
351+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
350352 <a role="presentation" data-href="/files/commits/main">
351-<span class="lbl">commits</span>
353+<span class="lbl">commits (g c)</span>
352354 <span class="k">section</span>
353355 </a>
354356 </li>
355-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">
357+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
356358 <a role="presentation" data-href="/files/refs">
357-<span class="lbl">branches &amp; tags</span>
359+<span class="lbl">branches &amp; tags (g b)</span>
358360 <span class="k">section</span>
359361 </a>
360362 </li>
361-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">
363+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
362364 <a role="presentation" data-href="/files/merge-requests">
363-<span class="lbl">merge requests</span>
365+<span class="lbl">merge requests (g m)</span>
364366 <span class="k">section</span>
365367 </a>
366368 </li>
367-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">
369+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
368370 <a role="presentation" data-href="/files/releases">
369-<span class="lbl">releases</span>
371+<span class="lbl">releases (g r)</span>
370372 <span class="k">section</span>
371373 </a>
372374 </li>
373-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">
375+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
374376 <a role="presentation" data-href="/files/actions">
375-<span class="lbl">actions</span>
377+<span class="lbl">actions (g a)</span>
376378 <span class="k">section</span>
377379 </a>
378380 </li>
@@ -425,7 +427,9 @@
425427 k-filter from:body -> ermite.focusFilter();
426428 k-gh from:body -> ermite.go('.brand');
427429 k-s from:body -> ermite.tab('s');
428- k-t from:body -> ermite.tab('t');
430+ k-f from:body -> ermite.tab('f');
431+ k-b from:body -> ermite.tab('b');
432+ k-a from:body -> ermite.tab('a');
429433 k-c from:body -> ermite.tab('c');
430434 k-r from:body -> ermite.tab('r');
431435 k-m from:body -> ermite.tab('m');
@@ -441,7 +445,6 @@
441445 k-permalink from:body -> ermite.permalink();
442446 k-merge from:body -> window.ermiteMR?.openMerge();
443447 k-read from:body -> window.ermiteMR?.markRead();
444- k-gf from:body -> window.ermiteMR?.focusFiles();
445448 k-keys from:body -> ermite.toggleKeys()">
446449 </div>
447450 </body>
@@ -73,31 +73,31 @@
73 <nav class="tabs" aria-label="Sections">73 <nav class="tabs" aria-label="Sections">
74 <ul>74 <ul>
75 <li>75 <li>
76-<a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>76+<a href="/files" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
77 </a>77 </a>
78 </li>78 </li>
79 <li>79 <li>
80-<a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd>80+<a href="/files/tree/main" data-key="g f" aria-current="page">Files <kbd aria-hidden="true">g f</kbd>
81 </a>81 </a>
82 </li>82 </li>
83 <li>83 <li>
84-<a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd>84+<a href="/files/commits/main" data-key="g c">Commits <kbd aria-hidden="true">g c</kbd>
85 </a>85 </a>
86 </li>86 </li>
87 <li>87 <li>
88-<a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>88+<a href="/files/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
89 </a>89 </a>
90 </li>90 </li>
91 <li>91 <li>
92-<a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>92+<a href="/files/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
93 </a>93 </a>
94 </li>94 </li>
95 <li>95 <li>
96-<a href="/files/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>96+<a href="/files/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
97 </a>97 </a>
98 </li>98 </li>
99 <li>99 <li>
100-<a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>100+<a href="/files/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
101 </a>101 </a>
102 </li>102 </li>
103 </ul>103 </ul>
@@ -228,14 +228,16 @@
228 </div>228 </div>
229 <div>229 <div>
230 <dt>230 <dt>
231-<kbd>s</kbd>231+<kbd>g s</kbd>
232-<kbd>t</kbd>232+<kbd>g f</kbd>
233-<kbd>c</kbd>233+<kbd>g c</kbd>
234-<kbd>r</kbd>234+<kbd>g b</kbd>
235-<kbd>m</kbd>235+<kbd>g m</kbd>
236-<kbd>i</kbd>236+<kbd>g i</kbd>
237+<kbd>g r</kbd>
238+<kbd>g a</kbd>
237 </dt>239 </dt>
238-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>240+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
239 </div>241 </div>
240 <div>242 <div>
241 <dt>243 <dt>
@@ -334,45 +336,45 @@
334 <span class="k">go</span>336 <span class="k">go</span>
335 </a>337 </a>
336 </li>338 </li>
337-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">339+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
338 <a role="presentation" data-href="/files">340 <a role="presentation" data-href="/files">
339-<span class="lbl">summary</span>341+<span class="lbl">summary (g s)</span>
340 <span class="k">section</span>342 <span class="k">section</span>
341 </a>343 </a>
342 </li>344 </li>
343-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">345+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
344 <a role="presentation" data-href="/files/tree/main">346 <a role="presentation" data-href="/files/tree/main">
345-<span class="lbl">files</span>347+<span class="lbl">files (g f)</span>
346 <span class="k">section</span>348 <span class="k">section</span>
347 </a>349 </a>
348 </li>350 </li>
349-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">351+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
350 <a role="presentation" data-href="/files/commits/main">352 <a role="presentation" data-href="/files/commits/main">
351-<span class="lbl">commits</span>353+<span class="lbl">commits (g c)</span>
352 <span class="k">section</span>354 <span class="k">section</span>
353 </a>355 </a>
354 </li>356 </li>
355-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">357+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
356 <a role="presentation" data-href="/files/refs">358 <a role="presentation" data-href="/files/refs">
357-<span class="lbl">branches &amp; tags</span>359+<span class="lbl">branches &amp; tags (g b)</span>
358 <span class="k">section</span>360 <span class="k">section</span>
359 </a>361 </a>
360 </li>362 </li>
361-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">363+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
362 <a role="presentation" data-href="/files/merge-requests">364 <a role="presentation" data-href="/files/merge-requests">
363-<span class="lbl">merge requests</span>365+<span class="lbl">merge requests (g m)</span>
364 <span class="k">section</span>366 <span class="k">section</span>
365 </a>367 </a>
366 </li>368 </li>
367-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">369+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
368 <a role="presentation" data-href="/files/releases">370 <a role="presentation" data-href="/files/releases">
369-<span class="lbl">releases</span>371+<span class="lbl">releases (g r)</span>
370 <span class="k">section</span>372 <span class="k">section</span>
371 </a>373 </a>
372 </li>374 </li>
373-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">375+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
374 <a role="presentation" data-href="/files/actions">376 <a role="presentation" data-href="/files/actions">
375-<span class="lbl">actions</span>377+<span class="lbl">actions (g a)</span>
376 <span class="k">section</span>378 <span class="k">section</span>
377 </a>379 </a>
378 </li>380 </li>
@@ -425,7 +427,9 @@
425 k-filter from:body -> ermite.focusFilter();427 k-filter from:body -> ermite.focusFilter();
426 k-gh from:body -> ermite.go('.brand');428 k-gh from:body -> ermite.go('.brand');
427 k-s from:body -> ermite.tab('s');429 k-s from:body -> ermite.tab('s');
428- k-t from:body -> ermite.tab('t');430+ k-f from:body -> ermite.tab('f');
431+ k-b from:body -> ermite.tab('b');
432+ k-a from:body -> ermite.tab('a');
429 k-c from:body -> ermite.tab('c');433 k-c from:body -> ermite.tab('c');
430 k-r from:body -> ermite.tab('r');434 k-r from:body -> ermite.tab('r');
431 k-m from:body -> ermite.tab('m');435 k-m from:body -> ermite.tab('m');
@@ -441,7 +445,6 @@
441 k-permalink from:body -> ermite.permalink();445 k-permalink from:body -> ermite.permalink();
442 k-merge from:body -> window.ermiteMR?.openMerge();446 k-merge from:body -> window.ermiteMR?.openMerge();
443 k-read from:body -> window.ermiteMR?.markRead();447 k-read from:body -> window.ermiteMR?.markRead();
444- k-gf from:body -> window.ermiteMR?.focusFiles();
445 k-keys from:body -> ermite.toggleKeys()">448 k-keys from:body -> ermite.toggleKeys()">
446 </div>449 </div>
447 </body>450 </body>
M internal/app/testdata/web/blob-image.html +33 -30
@@ -73,31 +73,31 @@
7373 <nav class="tabs" aria-label="Sections">
7474 <ul>
7575 <li>
76-<a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>
76+<a href="/files" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
7777 </a>
7878 </li>
7979 <li>
80-<a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd>
80+<a href="/files/tree/main" data-key="g f" aria-current="page">Files <kbd aria-hidden="true">g f</kbd>
8181 </a>
8282 </li>
8383 <li>
84-<a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd>
84+<a href="/files/commits/main" data-key="g c">Commits <kbd aria-hidden="true">g c</kbd>
8585 </a>
8686 </li>
8787 <li>
88-<a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>
88+<a href="/files/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
8989 </a>
9090 </li>
9191 <li>
92-<a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>
92+<a href="/files/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
9393 </a>
9494 </li>
9595 <li>
96-<a href="/files/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>
96+<a href="/files/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
9797 </a>
9898 </li>
9999 <li>
100-<a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>
100+<a href="/files/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
101101 </a>
102102 </li>
103103 </ul>
@@ -230,14 +230,16 @@
230230 </div>
231231 <div>
232232 <dt>
233-<kbd>s</kbd>
234-<kbd>t</kbd>
235-<kbd>c</kbd>
236-<kbd>r</kbd>
237-<kbd>m</kbd>
238-<kbd>i</kbd>
233+<kbd>g s</kbd>
234+<kbd>g f</kbd>
235+<kbd>g c</kbd>
236+<kbd>g b</kbd>
237+<kbd>g m</kbd>
238+<kbd>g i</kbd>
239+<kbd>g r</kbd>
240+<kbd>g a</kbd>
239241 </dt>
240-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>
242+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
241243 </div>
242244 <div>
243245 <dt>
@@ -336,45 +338,45 @@
336338 <span class="k">go</span>
337339 </a>
338340 </li>
339-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">
341+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
340342 <a role="presentation" data-href="/files">
341-<span class="lbl">summary</span>
343+<span class="lbl">summary (g s)</span>
342344 <span class="k">section</span>
343345 </a>
344346 </li>
345-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">
347+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
346348 <a role="presentation" data-href="/files/tree/main">
347-<span class="lbl">files</span>
349+<span class="lbl">files (g f)</span>
348350 <span class="k">section</span>
349351 </a>
350352 </li>
351-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">
353+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
352354 <a role="presentation" data-href="/files/commits/main">
353-<span class="lbl">commits</span>
355+<span class="lbl">commits (g c)</span>
354356 <span class="k">section</span>
355357 </a>
356358 </li>
357-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">
359+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
358360 <a role="presentation" data-href="/files/refs">
359-<span class="lbl">branches &amp; tags</span>
361+<span class="lbl">branches &amp; tags (g b)</span>
360362 <span class="k">section</span>
361363 </a>
362364 </li>
363-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">
365+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
364366 <a role="presentation" data-href="/files/merge-requests">
365-<span class="lbl">merge requests</span>
367+<span class="lbl">merge requests (g m)</span>
366368 <span class="k">section</span>
367369 </a>
368370 </li>
369-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">
371+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
370372 <a role="presentation" data-href="/files/releases">
371-<span class="lbl">releases</span>
373+<span class="lbl">releases (g r)</span>
372374 <span class="k">section</span>
373375 </a>
374376 </li>
375-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">
377+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
376378 <a role="presentation" data-href="/files/actions">
377-<span class="lbl">actions</span>
379+<span class="lbl">actions (g a)</span>
378380 <span class="k">section</span>
379381 </a>
380382 </li>
@@ -427,7 +429,9 @@
427429 k-filter from:body -> ermite.focusFilter();
428430 k-gh from:body -> ermite.go('.brand');
429431 k-s from:body -> ermite.tab('s');
430- k-t from:body -> ermite.tab('t');
432+ k-f from:body -> ermite.tab('f');
433+ k-b from:body -> ermite.tab('b');
434+ k-a from:body -> ermite.tab('a');
431435 k-c from:body -> ermite.tab('c');
432436 k-r from:body -> ermite.tab('r');
433437 k-m from:body -> ermite.tab('m');
@@ -443,7 +447,6 @@
443447 k-permalink from:body -> ermite.permalink();
444448 k-merge from:body -> window.ermiteMR?.openMerge();
445449 k-read from:body -> window.ermiteMR?.markRead();
446- k-gf from:body -> window.ermiteMR?.focusFiles();
447450 k-keys from:body -> ermite.toggleKeys()">
448451 </div>
449452 </body>
@@ -73,31 +73,31 @@
73 <nav class="tabs" aria-label="Sections">73 <nav class="tabs" aria-label="Sections">
74 <ul>74 <ul>
75 <li>75 <li>
76-<a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>76+<a href="/files" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
77 </a>77 </a>
78 </li>78 </li>
79 <li>79 <li>
80-<a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd>80+<a href="/files/tree/main" data-key="g f" aria-current="page">Files <kbd aria-hidden="true">g f</kbd>
81 </a>81 </a>
82 </li>82 </li>
83 <li>83 <li>
84-<a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd>84+<a href="/files/commits/main" data-key="g c">Commits <kbd aria-hidden="true">g c</kbd>
85 </a>85 </a>
86 </li>86 </li>
87 <li>87 <li>
88-<a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>88+<a href="/files/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
89 </a>89 </a>
90 </li>90 </li>
91 <li>91 <li>
92-<a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>92+<a href="/files/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
93 </a>93 </a>
94 </li>94 </li>
95 <li>95 <li>
96-<a href="/files/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>96+<a href="/files/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
97 </a>97 </a>
98 </li>98 </li>
99 <li>99 <li>
100-<a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>100+<a href="/files/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
101 </a>101 </a>
102 </li>102 </li>
103 </ul>103 </ul>
@@ -230,14 +230,16 @@
230 </div>230 </div>
231 <div>231 <div>
232 <dt>232 <dt>
233-<kbd>s</kbd>233+<kbd>g s</kbd>
234-<kbd>t</kbd>234+<kbd>g f</kbd>
235-<kbd>c</kbd>235+<kbd>g c</kbd>
236-<kbd>r</kbd>236+<kbd>g b</kbd>
237-<kbd>m</kbd>237+<kbd>g m</kbd>
238-<kbd>i</kbd>238+<kbd>g i</kbd>
239+<kbd>g r</kbd>
240+<kbd>g a</kbd>
239 </dt>241 </dt>
240-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>242+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
241 </div>243 </div>
242 <div>244 <div>
243 <dt>245 <dt>
@@ -336,45 +338,45 @@
336 <span class="k">go</span>338 <span class="k">go</span>
337 </a>339 </a>
338 </li>340 </li>
339-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">341+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
340 <a role="presentation" data-href="/files">342 <a role="presentation" data-href="/files">
341-<span class="lbl">summary</span>343+<span class="lbl">summary (g s)</span>
342 <span class="k">section</span>344 <span class="k">section</span>
343 </a>345 </a>
344 </li>346 </li>
345-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">347+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
346 <a role="presentation" data-href="/files/tree/main">348 <a role="presentation" data-href="/files/tree/main">
347-<span class="lbl">files</span>349+<span class="lbl">files (g f)</span>
348 <span class="k">section</span>350 <span class="k">section</span>
349 </a>351 </a>
350 </li>352 </li>
351-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">353+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
352 <a role="presentation" data-href="/files/commits/main">354 <a role="presentation" data-href="/files/commits/main">
353-<span class="lbl">commits</span>355+<span class="lbl">commits (g c)</span>
354 <span class="k">section</span>356 <span class="k">section</span>
355 </a>357 </a>
356 </li>358 </li>
357-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">359+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
358 <a role="presentation" data-href="/files/refs">360 <a role="presentation" data-href="/files/refs">
359-<span class="lbl">branches &amp; tags</span>361+<span class="lbl">branches &amp; tags (g b)</span>
360 <span class="k">section</span>362 <span class="k">section</span>
361 </a>363 </a>
362 </li>364 </li>
363-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">365+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
364 <a role="presentation" data-href="/files/merge-requests">366 <a role="presentation" data-href="/files/merge-requests">
365-<span class="lbl">merge requests</span>367+<span class="lbl">merge requests (g m)</span>
366 <span class="k">section</span>368 <span class="k">section</span>
367 </a>369 </a>
368 </li>370 </li>
369-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">371+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
370 <a role="presentation" data-href="/files/releases">372 <a role="presentation" data-href="/files/releases">
371-<span class="lbl">releases</span>373+<span class="lbl">releases (g r)</span>
372 <span class="k">section</span>374 <span class="k">section</span>
373 </a>375 </a>
374 </li>376 </li>
375-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">377+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
376 <a role="presentation" data-href="/files/actions">378 <a role="presentation" data-href="/files/actions">
377-<span class="lbl">actions</span>379+<span class="lbl">actions (g a)</span>
378 <span class="k">section</span>380 <span class="k">section</span>
379 </a>381 </a>
380 </li>382 </li>
@@ -427,7 +429,9 @@
427 k-filter from:body -> ermite.focusFilter();429 k-filter from:body -> ermite.focusFilter();
428 k-gh from:body -> ermite.go('.brand');430 k-gh from:body -> ermite.go('.brand');
429 k-s from:body -> ermite.tab('s');431 k-s from:body -> ermite.tab('s');
430- k-t from:body -> ermite.tab('t');432+ k-f from:body -> ermite.tab('f');
433+ k-b from:body -> ermite.tab('b');
434+ k-a from:body -> ermite.tab('a');
431 k-c from:body -> ermite.tab('c');435 k-c from:body -> ermite.tab('c');
432 k-r from:body -> ermite.tab('r');436 k-r from:body -> ermite.tab('r');
433 k-m from:body -> ermite.tab('m');437 k-m from:body -> ermite.tab('m');
@@ -443,7 +447,6 @@
443 k-permalink from:body -> ermite.permalink();447 k-permalink from:body -> ermite.permalink();
444 k-merge from:body -> window.ermiteMR?.openMerge();448 k-merge from:body -> window.ermiteMR?.openMerge();
445 k-read from:body -> window.ermiteMR?.markRead();449 k-read from:body -> window.ermiteMR?.markRead();
446- k-gf from:body -> window.ermiteMR?.focusFiles();
447 k-keys from:body -> ermite.toggleKeys()">450 k-keys from:body -> ermite.toggleKeys()">
448 </div>451 </div>
449 </body>452 </body>
M internal/app/testdata/web/blob-markdown.html +33 -30
@@ -76,31 +76,31 @@
7676 <nav class="tabs" aria-label="Sections">
7777 <ul>
7878 <li>
79-<a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>
79+<a href="/files" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
8080 </a>
8181 </li>
8282 <li>
83-<a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd>
83+<a href="/files/tree/main" data-key="g f" aria-current="page">Files <kbd aria-hidden="true">g f</kbd>
8484 </a>
8585 </li>
8686 <li>
87-<a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd>
87+<a href="/files/commits/main" data-key="g c">Commits <kbd aria-hidden="true">g c</kbd>
8888 </a>
8989 </li>
9090 <li>
91-<a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>
91+<a href="/files/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
9292 </a>
9393 </li>
9494 <li>
95-<a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>
95+<a href="/files/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
9696 </a>
9797 </li>
9898 <li>
99-<a href="/files/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>
99+<a href="/files/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
100100 </a>
101101 </li>
102102 <li>
103-<a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>
103+<a href="/files/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
104104 </a>
105105 </li>
106106 </ul>
@@ -295,14 +295,16 @@
295295 </div>
296296 <div>
297297 <dt>
298-<kbd>s</kbd>
299-<kbd>t</kbd>
300-<kbd>c</kbd>
301-<kbd>r</kbd>
302-<kbd>m</kbd>
303-<kbd>i</kbd>
298+<kbd>g s</kbd>
299+<kbd>g f</kbd>
300+<kbd>g c</kbd>
301+<kbd>g b</kbd>
302+<kbd>g m</kbd>
303+<kbd>g i</kbd>
304+<kbd>g r</kbd>
305+<kbd>g a</kbd>
304306 </dt>
305-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>
307+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
306308 </div>
307309 <div>
308310 <dt>
@@ -401,45 +403,45 @@
401403 <span class="k">go</span>
402404 </a>
403405 </li>
404-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">
406+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
405407 <a role="presentation" data-href="/files">
406-<span class="lbl">summary</span>
408+<span class="lbl">summary (g s)</span>
407409 <span class="k">section</span>
408410 </a>
409411 </li>
410-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">
412+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
411413 <a role="presentation" data-href="/files/tree/main">
412-<span class="lbl">files</span>
414+<span class="lbl">files (g f)</span>
413415 <span class="k">section</span>
414416 </a>
415417 </li>
416-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">
418+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
417419 <a role="presentation" data-href="/files/commits/main">
418-<span class="lbl">commits</span>
420+<span class="lbl">commits (g c)</span>
419421 <span class="k">section</span>
420422 </a>
421423 </li>
422-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">
424+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
423425 <a role="presentation" data-href="/files/refs">
424-<span class="lbl">branches &amp; tags</span>
426+<span class="lbl">branches &amp; tags (g b)</span>
425427 <span class="k">section</span>
426428 </a>
427429 </li>
428-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">
430+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
429431 <a role="presentation" data-href="/files/merge-requests">
430-<span class="lbl">merge requests</span>
432+<span class="lbl">merge requests (g m)</span>
431433 <span class="k">section</span>
432434 </a>
433435 </li>
434-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">
436+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
435437 <a role="presentation" data-href="/files/releases">
436-<span class="lbl">releases</span>
438+<span class="lbl">releases (g r)</span>
437439 <span class="k">section</span>
438440 </a>
439441 </li>
440-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">
442+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
441443 <a role="presentation" data-href="/files/actions">
442-<span class="lbl">actions</span>
444+<span class="lbl">actions (g a)</span>
443445 <span class="k">section</span>
444446 </a>
445447 </li>
@@ -492,7 +494,9 @@
492494 k-filter from:body -> ermite.focusFilter();
493495 k-gh from:body -> ermite.go('.brand');
494496 k-s from:body -> ermite.tab('s');
495- k-t from:body -> ermite.tab('t');
497+ k-f from:body -> ermite.tab('f');
498+ k-b from:body -> ermite.tab('b');
499+ k-a from:body -> ermite.tab('a');
496500 k-c from:body -> ermite.tab('c');
497501 k-r from:body -> ermite.tab('r');
498502 k-m from:body -> ermite.tab('m');
@@ -508,7 +512,6 @@
508512 k-permalink from:body -> ermite.permalink();
509513 k-merge from:body -> window.ermiteMR?.openMerge();
510514 k-read from:body -> window.ermiteMR?.markRead();
511- k-gf from:body -> window.ermiteMR?.focusFiles();
512515 k-keys from:body -> ermite.toggleKeys()">
513516 </div>
514517 </body>
@@ -76,31 +76,31 @@
76 <nav class="tabs" aria-label="Sections">76 <nav class="tabs" aria-label="Sections">
77 <ul>77 <ul>
78 <li>78 <li>
79-<a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>79+<a href="/files" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
80 </a>80 </a>
81 </li>81 </li>
82 <li>82 <li>
83-<a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd>83+<a href="/files/tree/main" data-key="g f" aria-current="page">Files <kbd aria-hidden="true">g f</kbd>
84 </a>84 </a>
85 </li>85 </li>
86 <li>86 <li>
87-<a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd>87+<a href="/files/commits/main" data-key="g c">Commits <kbd aria-hidden="true">g c</kbd>
88 </a>88 </a>
89 </li>89 </li>
90 <li>90 <li>
91-<a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>91+<a href="/files/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
92 </a>92 </a>
93 </li>93 </li>
94 <li>94 <li>
95-<a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>95+<a href="/files/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
96 </a>96 </a>
97 </li>97 </li>
98 <li>98 <li>
99-<a href="/files/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>99+<a href="/files/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
100 </a>100 </a>
101 </li>101 </li>
102 <li>102 <li>
103-<a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>103+<a href="/files/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
104 </a>104 </a>
105 </li>105 </li>
106 </ul>106 </ul>
@@ -295,14 +295,16 @@
295 </div>295 </div>
296 <div>296 <div>
297 <dt>297 <dt>
298-<kbd>s</kbd>298+<kbd>g s</kbd>
299-<kbd>t</kbd>299+<kbd>g f</kbd>
300-<kbd>c</kbd>300+<kbd>g c</kbd>
301-<kbd>r</kbd>301+<kbd>g b</kbd>
302-<kbd>m</kbd>302+<kbd>g m</kbd>
303-<kbd>i</kbd>303+<kbd>g i</kbd>
304+<kbd>g r</kbd>
305+<kbd>g a</kbd>
304 </dt>306 </dt>
305-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>307+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
306 </div>308 </div>
307 <div>309 <div>
308 <dt>310 <dt>
@@ -401,45 +403,45 @@
401 <span class="k">go</span>403 <span class="k">go</span>
402 </a>404 </a>
403 </li>405 </li>
404-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">406+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
405 <a role="presentation" data-href="/files">407 <a role="presentation" data-href="/files">
406-<span class="lbl">summary</span>408+<span class="lbl">summary (g s)</span>
407 <span class="k">section</span>409 <span class="k">section</span>
408 </a>410 </a>
409 </li>411 </li>
410-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">412+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
411 <a role="presentation" data-href="/files/tree/main">413 <a role="presentation" data-href="/files/tree/main">
412-<span class="lbl">files</span>414+<span class="lbl">files (g f)</span>
413 <span class="k">section</span>415 <span class="k">section</span>
414 </a>416 </a>
415 </li>417 </li>
416-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">418+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
417 <a role="presentation" data-href="/files/commits/main">419 <a role="presentation" data-href="/files/commits/main">
418-<span class="lbl">commits</span>420+<span class="lbl">commits (g c)</span>
419 <span class="k">section</span>421 <span class="k">section</span>
420 </a>422 </a>
421 </li>423 </li>
422-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">424+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
423 <a role="presentation" data-href="/files/refs">425 <a role="presentation" data-href="/files/refs">
424-<span class="lbl">branches &amp; tags</span>426+<span class="lbl">branches &amp; tags (g b)</span>
425 <span class="k">section</span>427 <span class="k">section</span>
426 </a>428 </a>
427 </li>429 </li>
428-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">430+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
429 <a role="presentation" data-href="/files/merge-requests">431 <a role="presentation" data-href="/files/merge-requests">
430-<span class="lbl">merge requests</span>432+<span class="lbl">merge requests (g m)</span>
431 <span class="k">section</span>433 <span class="k">section</span>
432 </a>434 </a>
433 </li>435 </li>
434-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">436+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
435 <a role="presentation" data-href="/files/releases">437 <a role="presentation" data-href="/files/releases">
436-<span class="lbl">releases</span>438+<span class="lbl">releases (g r)</span>
437 <span class="k">section</span>439 <span class="k">section</span>
438 </a>440 </a>
439 </li>441 </li>
440-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">442+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
441 <a role="presentation" data-href="/files/actions">443 <a role="presentation" data-href="/files/actions">
442-<span class="lbl">actions</span>444+<span class="lbl">actions (g a)</span>
443 <span class="k">section</span>445 <span class="k">section</span>
444 </a>446 </a>
445 </li>447 </li>
@@ -492,7 +494,9 @@
492 k-filter from:body -> ermite.focusFilter();494 k-filter from:body -> ermite.focusFilter();
493 k-gh from:body -> ermite.go('.brand');495 k-gh from:body -> ermite.go('.brand');
494 k-s from:body -> ermite.tab('s');496 k-s from:body -> ermite.tab('s');
495- k-t from:body -> ermite.tab('t');497+ k-f from:body -> ermite.tab('f');
498+ k-b from:body -> ermite.tab('b');
499+ k-a from:body -> ermite.tab('a');
496 k-c from:body -> ermite.tab('c');500 k-c from:body -> ermite.tab('c');
497 k-r from:body -> ermite.tab('r');501 k-r from:body -> ermite.tab('r');
498 k-m from:body -> ermite.tab('m');502 k-m from:body -> ermite.tab('m');
@@ -508,7 +512,6 @@
508 k-permalink from:body -> ermite.permalink();512 k-permalink from:body -> ermite.permalink();
509 k-merge from:body -> window.ermiteMR?.openMerge();513 k-merge from:body -> window.ermiteMR?.openMerge();
510 k-read from:body -> window.ermiteMR?.markRead();514 k-read from:body -> window.ermiteMR?.markRead();
511- k-gf from:body -> window.ermiteMR?.focusFiles();
512 k-keys from:body -> ermite.toggleKeys()">515 k-keys from:body -> ermite.toggleKeys()">
513 </div>516 </div>
514 </body>517 </body>
M internal/app/testdata/web/blob-svg.html +33 -30
@@ -73,31 +73,31 @@
7373 <nav class="tabs" aria-label="Sections">
7474 <ul>
7575 <li>
76-<a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>
76+<a href="/files" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
7777 </a>
7878 </li>
7979 <li>
80-<a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd>
80+<a href="/files/tree/main" data-key="g f" aria-current="page">Files <kbd aria-hidden="true">g f</kbd>
8181 </a>
8282 </li>
8383 <li>
84-<a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd>
84+<a href="/files/commits/main" data-key="g c">Commits <kbd aria-hidden="true">g c</kbd>
8585 </a>
8686 </li>
8787 <li>
88-<a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>
88+<a href="/files/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
8989 </a>
9090 </li>
9191 <li>
92-<a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>
92+<a href="/files/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
9393 </a>
9494 </li>
9595 <li>
96-<a href="/files/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>
96+<a href="/files/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
9797 </a>
9898 </li>
9999 <li>
100-<a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>
100+<a href="/files/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
101101 </a>
102102 </li>
103103 </ul>
@@ -253,14 +253,16 @@
253253 </div>
254254 <div>
255255 <dt>
256-<kbd>s</kbd>
257-<kbd>t</kbd>
258-<kbd>c</kbd>
259-<kbd>r</kbd>
260-<kbd>m</kbd>
261-<kbd>i</kbd>
256+<kbd>g s</kbd>
257+<kbd>g f</kbd>
258+<kbd>g c</kbd>
259+<kbd>g b</kbd>
260+<kbd>g m</kbd>
261+<kbd>g i</kbd>
262+<kbd>g r</kbd>
263+<kbd>g a</kbd>
262264 </dt>
263-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>
265+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
264266 </div>
265267 <div>
266268 <dt>
@@ -359,45 +361,45 @@
359361 <span class="k">go</span>
360362 </a>
361363 </li>
362-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">
364+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
363365 <a role="presentation" data-href="/files">
364-<span class="lbl">summary</span>
366+<span class="lbl">summary (g s)</span>
365367 <span class="k">section</span>
366368 </a>
367369 </li>
368-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">
370+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
369371 <a role="presentation" data-href="/files/tree/main">
370-<span class="lbl">files</span>
372+<span class="lbl">files (g f)</span>
371373 <span class="k">section</span>
372374 </a>
373375 </li>
374-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">
376+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
375377 <a role="presentation" data-href="/files/commits/main">
376-<span class="lbl">commits</span>
378+<span class="lbl">commits (g c)</span>
377379 <span class="k">section</span>
378380 </a>
379381 </li>
380-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">
382+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
381383 <a role="presentation" data-href="/files/refs">
382-<span class="lbl">branches &amp; tags</span>
384+<span class="lbl">branches &amp; tags (g b)</span>
383385 <span class="k">section</span>
384386 </a>
385387 </li>
386-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">
388+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
387389 <a role="presentation" data-href="/files/merge-requests">
388-<span class="lbl">merge requests</span>
390+<span class="lbl">merge requests (g m)</span>
389391 <span class="k">section</span>
390392 </a>
391393 </li>
392-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">
394+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
393395 <a role="presentation" data-href="/files/releases">
394-<span class="lbl">releases</span>
396+<span class="lbl">releases (g r)</span>
395397 <span class="k">section</span>
396398 </a>
397399 </li>
398-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">
400+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
399401 <a role="presentation" data-href="/files/actions">
400-<span class="lbl">actions</span>
402+<span class="lbl">actions (g a)</span>
401403 <span class="k">section</span>
402404 </a>
403405 </li>
@@ -450,7 +452,9 @@
450452 k-filter from:body -> ermite.focusFilter();
451453 k-gh from:body -> ermite.go('.brand');
452454 k-s from:body -> ermite.tab('s');
453- k-t from:body -> ermite.tab('t');
455+ k-f from:body -> ermite.tab('f');
456+ k-b from:body -> ermite.tab('b');
457+ k-a from:body -> ermite.tab('a');
454458 k-c from:body -> ermite.tab('c');
455459 k-r from:body -> ermite.tab('r');
456460 k-m from:body -> ermite.tab('m');
@@ -466,7 +470,6 @@
466470 k-permalink from:body -> ermite.permalink();
467471 k-merge from:body -> window.ermiteMR?.openMerge();
468472 k-read from:body -> window.ermiteMR?.markRead();
469- k-gf from:body -> window.ermiteMR?.focusFiles();
470473 k-keys from:body -> ermite.toggleKeys()">
471474 </div>
472475 </body>
@@ -73,31 +73,31 @@
73 <nav class="tabs" aria-label="Sections">73 <nav class="tabs" aria-label="Sections">
74 <ul>74 <ul>
75 <li>75 <li>
76-<a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>76+<a href="/files" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
77 </a>77 </a>
78 </li>78 </li>
79 <li>79 <li>
80-<a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd>80+<a href="/files/tree/main" data-key="g f" aria-current="page">Files <kbd aria-hidden="true">g f</kbd>
81 </a>81 </a>
82 </li>82 </li>
83 <li>83 <li>
84-<a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd>84+<a href="/files/commits/main" data-key="g c">Commits <kbd aria-hidden="true">g c</kbd>
85 </a>85 </a>
86 </li>86 </li>
87 <li>87 <li>
88-<a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>88+<a href="/files/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
89 </a>89 </a>
90 </li>90 </li>
91 <li>91 <li>
92-<a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>92+<a href="/files/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
93 </a>93 </a>
94 </li>94 </li>
95 <li>95 <li>
96-<a href="/files/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>96+<a href="/files/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
97 </a>97 </a>
98 </li>98 </li>
99 <li>99 <li>
100-<a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>100+<a href="/files/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
101 </a>101 </a>
102 </li>102 </li>
103 </ul>103 </ul>
@@ -253,14 +253,16 @@
253 </div>253 </div>
254 <div>254 <div>
255 <dt>255 <dt>
256-<kbd>s</kbd>256+<kbd>g s</kbd>
257-<kbd>t</kbd>257+<kbd>g f</kbd>
258-<kbd>c</kbd>258+<kbd>g c</kbd>
259-<kbd>r</kbd>259+<kbd>g b</kbd>
260-<kbd>m</kbd>260+<kbd>g m</kbd>
261-<kbd>i</kbd>261+<kbd>g i</kbd>
262+<kbd>g r</kbd>
263+<kbd>g a</kbd>
262 </dt>264 </dt>
263-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>265+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
264 </div>266 </div>
265 <div>267 <div>
266 <dt>268 <dt>
@@ -359,45 +361,45 @@
359 <span class="k">go</span>361 <span class="k">go</span>
360 </a>362 </a>
361 </li>363 </li>
362-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">364+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
363 <a role="presentation" data-href="/files">365 <a role="presentation" data-href="/files">
364-<span class="lbl">summary</span>366+<span class="lbl">summary (g s)</span>
365 <span class="k">section</span>367 <span class="k">section</span>
366 </a>368 </a>
367 </li>369 </li>
368-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">370+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
369 <a role="presentation" data-href="/files/tree/main">371 <a role="presentation" data-href="/files/tree/main">
370-<span class="lbl">files</span>372+<span class="lbl">files (g f)</span>
371 <span class="k">section</span>373 <span class="k">section</span>
372 </a>374 </a>
373 </li>375 </li>
374-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">376+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
375 <a role="presentation" data-href="/files/commits/main">377 <a role="presentation" data-href="/files/commits/main">
376-<span class="lbl">commits</span>378+<span class="lbl">commits (g c)</span>
377 <span class="k">section</span>379 <span class="k">section</span>
378 </a>380 </a>
379 </li>381 </li>
380-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">382+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
381 <a role="presentation" data-href="/files/refs">383 <a role="presentation" data-href="/files/refs">
382-<span class="lbl">branches &amp; tags</span>384+<span class="lbl">branches &amp; tags (g b)</span>
383 <span class="k">section</span>385 <span class="k">section</span>
384 </a>386 </a>
385 </li>387 </li>
386-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">388+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
387 <a role="presentation" data-href="/files/merge-requests">389 <a role="presentation" data-href="/files/merge-requests">
388-<span class="lbl">merge requests</span>390+<span class="lbl">merge requests (g m)</span>
389 <span class="k">section</span>391 <span class="k">section</span>
390 </a>392 </a>
391 </li>393 </li>
392-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">394+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
393 <a role="presentation" data-href="/files/releases">395 <a role="presentation" data-href="/files/releases">
394-<span class="lbl">releases</span>396+<span class="lbl">releases (g r)</span>
395 <span class="k">section</span>397 <span class="k">section</span>
396 </a>398 </a>
397 </li>399 </li>
398-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">400+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
399 <a role="presentation" data-href="/files/actions">401 <a role="presentation" data-href="/files/actions">
400-<span class="lbl">actions</span>402+<span class="lbl">actions (g a)</span>
401 <span class="k">section</span>403 <span class="k">section</span>
402 </a>404 </a>
403 </li>405 </li>
@@ -450,7 +452,9 @@
450 k-filter from:body -> ermite.focusFilter();452 k-filter from:body -> ermite.focusFilter();
451 k-gh from:body -> ermite.go('.brand');453 k-gh from:body -> ermite.go('.brand');
452 k-s from:body -> ermite.tab('s');454 k-s from:body -> ermite.tab('s');
453- k-t from:body -> ermite.tab('t');455+ k-f from:body -> ermite.tab('f');
456+ k-b from:body -> ermite.tab('b');
457+ k-a from:body -> ermite.tab('a');
454 k-c from:body -> ermite.tab('c');458 k-c from:body -> ermite.tab('c');
455 k-r from:body -> ermite.tab('r');459 k-r from:body -> ermite.tab('r');
456 k-m from:body -> ermite.tab('m');460 k-m from:body -> ermite.tab('m');
@@ -466,7 +470,6 @@
466 k-permalink from:body -> ermite.permalink();470 k-permalink from:body -> ermite.permalink();
467 k-merge from:body -> window.ermiteMR?.openMerge();471 k-merge from:body -> window.ermiteMR?.openMerge();
468 k-read from:body -> window.ermiteMR?.markRead();472 k-read from:body -> window.ermiteMR?.markRead();
469- k-gf from:body -> window.ermiteMR?.focusFiles();
470 k-keys from:body -> ermite.toggleKeys()">473 k-keys from:body -> ermite.toggleKeys()">
471 </div>474 </div>
472 </body>475 </body>
M internal/app/testdata/web/blob-text.html +33 -30
@@ -73,31 +73,31 @@
7373 <nav class="tabs" aria-label="Sections">
7474 <ul>
7575 <li>
76-<a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>
76+<a href="/files" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
7777 </a>
7878 </li>
7979 <li>
80-<a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd>
80+<a href="/files/tree/main" data-key="g f" aria-current="page">Files <kbd aria-hidden="true">g f</kbd>
8181 </a>
8282 </li>
8383 <li>
84-<a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd>
84+<a href="/files/commits/main" data-key="g c">Commits <kbd aria-hidden="true">g c</kbd>
8585 </a>
8686 </li>
8787 <li>
88-<a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>
88+<a href="/files/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
8989 </a>
9090 </li>
9191 <li>
92-<a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>
92+<a href="/files/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
9393 </a>
9494 </li>
9595 <li>
96-<a href="/files/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>
96+<a href="/files/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
9797 </a>
9898 </li>
9999 <li>
100-<a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>
100+<a href="/files/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
101101 </a>
102102 </li>
103103 </ul>
@@ -248,14 +248,16 @@
248248 </div>
249249 <div>
250250 <dt>
251-<kbd>s</kbd>
252-<kbd>t</kbd>
253-<kbd>c</kbd>
254-<kbd>r</kbd>
255-<kbd>m</kbd>
256-<kbd>i</kbd>
251+<kbd>g s</kbd>
252+<kbd>g f</kbd>
253+<kbd>g c</kbd>
254+<kbd>g b</kbd>
255+<kbd>g m</kbd>
256+<kbd>g i</kbd>
257+<kbd>g r</kbd>
258+<kbd>g a</kbd>
257259 </dt>
258-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>
260+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
259261 </div>
260262 <div>
261263 <dt>
@@ -354,45 +356,45 @@
354356 <span class="k">go</span>
355357 </a>
356358 </li>
357-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">
359+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
358360 <a role="presentation" data-href="/files">
359-<span class="lbl">summary</span>
361+<span class="lbl">summary (g s)</span>
360362 <span class="k">section</span>
361363 </a>
362364 </li>
363-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">
365+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
364366 <a role="presentation" data-href="/files/tree/main">
365-<span class="lbl">files</span>
367+<span class="lbl">files (g f)</span>
366368 <span class="k">section</span>
367369 </a>
368370 </li>
369-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">
371+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
370372 <a role="presentation" data-href="/files/commits/main">
371-<span class="lbl">commits</span>
373+<span class="lbl">commits (g c)</span>
372374 <span class="k">section</span>
373375 </a>
374376 </li>
375-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">
377+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
376378 <a role="presentation" data-href="/files/refs">
377-<span class="lbl">branches &amp; tags</span>
379+<span class="lbl">branches &amp; tags (g b)</span>
378380 <span class="k">section</span>
379381 </a>
380382 </li>
381-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">
383+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
382384 <a role="presentation" data-href="/files/merge-requests">
383-<span class="lbl">merge requests</span>
385+<span class="lbl">merge requests (g m)</span>
384386 <span class="k">section</span>
385387 </a>
386388 </li>
387-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">
389+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
388390 <a role="presentation" data-href="/files/releases">
389-<span class="lbl">releases</span>
391+<span class="lbl">releases (g r)</span>
390392 <span class="k">section</span>
391393 </a>
392394 </li>
393-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">
395+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
394396 <a role="presentation" data-href="/files/actions">
395-<span class="lbl">actions</span>
397+<span class="lbl">actions (g a)</span>
396398 <span class="k">section</span>
397399 </a>
398400 </li>
@@ -445,7 +447,9 @@
445447 k-filter from:body -> ermite.focusFilter();
446448 k-gh from:body -> ermite.go('.brand');
447449 k-s from:body -> ermite.tab('s');
448- k-t from:body -> ermite.tab('t');
450+ k-f from:body -> ermite.tab('f');
451+ k-b from:body -> ermite.tab('b');
452+ k-a from:body -> ermite.tab('a');
449453 k-c from:body -> ermite.tab('c');
450454 k-r from:body -> ermite.tab('r');
451455 k-m from:body -> ermite.tab('m');
@@ -461,7 +465,6 @@
461465 k-permalink from:body -> ermite.permalink();
462466 k-merge from:body -> window.ermiteMR?.openMerge();
463467 k-read from:body -> window.ermiteMR?.markRead();
464- k-gf from:body -> window.ermiteMR?.focusFiles();
465468 k-keys from:body -> ermite.toggleKeys()">
466469 </div>
467470 </body>
@@ -73,31 +73,31 @@
73 <nav class="tabs" aria-label="Sections">73 <nav class="tabs" aria-label="Sections">
74 <ul>74 <ul>
75 <li>75 <li>
76-<a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>76+<a href="/files" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
77 </a>77 </a>
78 </li>78 </li>
79 <li>79 <li>
80-<a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd>80+<a href="/files/tree/main" data-key="g f" aria-current="page">Files <kbd aria-hidden="true">g f</kbd>
81 </a>81 </a>
82 </li>82 </li>
83 <li>83 <li>
84-<a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd>84+<a href="/files/commits/main" data-key="g c">Commits <kbd aria-hidden="true">g c</kbd>
85 </a>85 </a>
86 </li>86 </li>
87 <li>87 <li>
88-<a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>88+<a href="/files/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
89 </a>89 </a>
90 </li>90 </li>
91 <li>91 <li>
92-<a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>92+<a href="/files/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
93 </a>93 </a>
94 </li>94 </li>
95 <li>95 <li>
96-<a href="/files/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>96+<a href="/files/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
97 </a>97 </a>
98 </li>98 </li>
99 <li>99 <li>
100-<a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>100+<a href="/files/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
101 </a>101 </a>
102 </li>102 </li>
103 </ul>103 </ul>
@@ -248,14 +248,16 @@
248 </div>248 </div>
249 <div>249 <div>
250 <dt>250 <dt>
251-<kbd>s</kbd>251+<kbd>g s</kbd>
252-<kbd>t</kbd>252+<kbd>g f</kbd>
253-<kbd>c</kbd>253+<kbd>g c</kbd>
254-<kbd>r</kbd>254+<kbd>g b</kbd>
255-<kbd>m</kbd>255+<kbd>g m</kbd>
256-<kbd>i</kbd>256+<kbd>g i</kbd>
257+<kbd>g r</kbd>
258+<kbd>g a</kbd>
257 </dt>259 </dt>
258-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>260+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
259 </div>261 </div>
260 <div>262 <div>
261 <dt>263 <dt>
@@ -354,45 +356,45 @@
354 <span class="k">go</span>356 <span class="k">go</span>
355 </a>357 </a>
356 </li>358 </li>
357-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">359+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
358 <a role="presentation" data-href="/files">360 <a role="presentation" data-href="/files">
359-<span class="lbl">summary</span>361+<span class="lbl">summary (g s)</span>
360 <span class="k">section</span>362 <span class="k">section</span>
361 </a>363 </a>
362 </li>364 </li>
363-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">365+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
364 <a role="presentation" data-href="/files/tree/main">366 <a role="presentation" data-href="/files/tree/main">
365-<span class="lbl">files</span>367+<span class="lbl">files (g f)</span>
366 <span class="k">section</span>368 <span class="k">section</span>
367 </a>369 </a>
368 </li>370 </li>
369-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">371+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
370 <a role="presentation" data-href="/files/commits/main">372 <a role="presentation" data-href="/files/commits/main">
371-<span class="lbl">commits</span>373+<span class="lbl">commits (g c)</span>
372 <span class="k">section</span>374 <span class="k">section</span>
373 </a>375 </a>
374 </li>376 </li>
375-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">377+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
376 <a role="presentation" data-href="/files/refs">378 <a role="presentation" data-href="/files/refs">
377-<span class="lbl">branches &amp; tags</span>379+<span class="lbl">branches &amp; tags (g b)</span>
378 <span class="k">section</span>380 <span class="k">section</span>
379 </a>381 </a>
380 </li>382 </li>
381-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">383+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
382 <a role="presentation" data-href="/files/merge-requests">384 <a role="presentation" data-href="/files/merge-requests">
383-<span class="lbl">merge requests</span>385+<span class="lbl">merge requests (g m)</span>
384 <span class="k">section</span>386 <span class="k">section</span>
385 </a>387 </a>
386 </li>388 </li>
387-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">389+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
388 <a role="presentation" data-href="/files/releases">390 <a role="presentation" data-href="/files/releases">
389-<span class="lbl">releases</span>391+<span class="lbl">releases (g r)</span>
390 <span class="k">section</span>392 <span class="k">section</span>
391 </a>393 </a>
392 </li>394 </li>
393-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">395+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
394 <a role="presentation" data-href="/files/actions">396 <a role="presentation" data-href="/files/actions">
395-<span class="lbl">actions</span>397+<span class="lbl">actions (g a)</span>
396 <span class="k">section</span>398 <span class="k">section</span>
397 </a>399 </a>
398 </li>400 </li>
@@ -445,7 +447,9 @@
445 k-filter from:body -> ermite.focusFilter();447 k-filter from:body -> ermite.focusFilter();
446 k-gh from:body -> ermite.go('.brand');448 k-gh from:body -> ermite.go('.brand');
447 k-s from:body -> ermite.tab('s');449 k-s from:body -> ermite.tab('s');
448- k-t from:body -> ermite.tab('t');450+ k-f from:body -> ermite.tab('f');
451+ k-b from:body -> ermite.tab('b');
452+ k-a from:body -> ermite.tab('a');
449 k-c from:body -> ermite.tab('c');453 k-c from:body -> ermite.tab('c');
450 k-r from:body -> ermite.tab('r');454 k-r from:body -> ermite.tab('r');
451 k-m from:body -> ermite.tab('m');455 k-m from:body -> ermite.tab('m');
@@ -461,7 +465,6 @@
461 k-permalink from:body -> ermite.permalink();465 k-permalink from:body -> ermite.permalink();
462 k-merge from:body -> window.ermiteMR?.openMerge();466 k-merge from:body -> window.ermiteMR?.openMerge();
463 k-read from:body -> window.ermiteMR?.markRead();467 k-read from:body -> window.ermiteMR?.markRead();
464- k-gf from:body -> window.ermiteMR?.focusFiles();
465 k-keys from:body -> ermite.toggleKeys()">468 k-keys from:body -> ermite.toggleKeys()">
466 </div>469 </div>
467 </body>470 </body>
M internal/app/testdata/web/blob-too-large.html +33 -30
@@ -73,31 +73,31 @@
7373 <nav class="tabs" aria-label="Sections">
7474 <ul>
7575 <li>
76-<a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>
76+<a href="/files" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
7777 </a>
7878 </li>
7979 <li>
80-<a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd>
80+<a href="/files/tree/main" data-key="g f" aria-current="page">Files <kbd aria-hidden="true">g f</kbd>
8181 </a>
8282 </li>
8383 <li>
84-<a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd>
84+<a href="/files/commits/main" data-key="g c">Commits <kbd aria-hidden="true">g c</kbd>
8585 </a>
8686 </li>
8787 <li>
88-<a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>
88+<a href="/files/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
8989 </a>
9090 </li>
9191 <li>
92-<a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>
92+<a href="/files/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
9393 </a>
9494 </li>
9595 <li>
96-<a href="/files/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>
96+<a href="/files/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
9797 </a>
9898 </li>
9999 <li>
100-<a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>
100+<a href="/files/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
101101 </a>
102102 </li>
103103 </ul>
@@ -228,14 +228,16 @@
228228 </div>
229229 <div>
230230 <dt>
231-<kbd>s</kbd>
232-<kbd>t</kbd>
233-<kbd>c</kbd>
234-<kbd>r</kbd>
235-<kbd>m</kbd>
236-<kbd>i</kbd>
231+<kbd>g s</kbd>
232+<kbd>g f</kbd>
233+<kbd>g c</kbd>
234+<kbd>g b</kbd>
235+<kbd>g m</kbd>
236+<kbd>g i</kbd>
237+<kbd>g r</kbd>
238+<kbd>g a</kbd>
237239 </dt>
238-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>
240+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
239241 </div>
240242 <div>
241243 <dt>
@@ -334,45 +336,45 @@
334336 <span class="k">go</span>
335337 </a>
336338 </li>
337-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">
339+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
338340 <a role="presentation" data-href="/files">
339-<span class="lbl">summary</span>
341+<span class="lbl">summary (g s)</span>
340342 <span class="k">section</span>
341343 </a>
342344 </li>
343-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">
345+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
344346 <a role="presentation" data-href="/files/tree/main">
345-<span class="lbl">files</span>
347+<span class="lbl">files (g f)</span>
346348 <span class="k">section</span>
347349 </a>
348350 </li>
349-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">
351+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
350352 <a role="presentation" data-href="/files/commits/main">
351-<span class="lbl">commits</span>
353+<span class="lbl">commits (g c)</span>
352354 <span class="k">section</span>
353355 </a>
354356 </li>
355-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">
357+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
356358 <a role="presentation" data-href="/files/refs">
357-<span class="lbl">branches &amp; tags</span>
359+<span class="lbl">branches &amp; tags (g b)</span>
358360 <span class="k">section</span>
359361 </a>
360362 </li>
361-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">
363+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
362364 <a role="presentation" data-href="/files/merge-requests">
363-<span class="lbl">merge requests</span>
365+<span class="lbl">merge requests (g m)</span>
364366 <span class="k">section</span>
365367 </a>
366368 </li>
367-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">
369+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
368370 <a role="presentation" data-href="/files/releases">
369-<span class="lbl">releases</span>
371+<span class="lbl">releases (g r)</span>
370372 <span class="k">section</span>
371373 </a>
372374 </li>
373-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">
375+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
374376 <a role="presentation" data-href="/files/actions">
375-<span class="lbl">actions</span>
377+<span class="lbl">actions (g a)</span>
376378 <span class="k">section</span>
377379 </a>
378380 </li>
@@ -425,7 +427,9 @@
425427 k-filter from:body -> ermite.focusFilter();
426428 k-gh from:body -> ermite.go('.brand');
427429 k-s from:body -> ermite.tab('s');
428- k-t from:body -> ermite.tab('t');
430+ k-f from:body -> ermite.tab('f');
431+ k-b from:body -> ermite.tab('b');
432+ k-a from:body -> ermite.tab('a');
429433 k-c from:body -> ermite.tab('c');
430434 k-r from:body -> ermite.tab('r');
431435 k-m from:body -> ermite.tab('m');
@@ -441,7 +445,6 @@
441445 k-permalink from:body -> ermite.permalink();
442446 k-merge from:body -> window.ermiteMR?.openMerge();
443447 k-read from:body -> window.ermiteMR?.markRead();
444- k-gf from:body -> window.ermiteMR?.focusFiles();
445448 k-keys from:body -> ermite.toggleKeys()">
446449 </div>
447450 </body>
@@ -73,31 +73,31 @@
73 <nav class="tabs" aria-label="Sections">73 <nav class="tabs" aria-label="Sections">
74 <ul>74 <ul>
75 <li>75 <li>
76-<a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>76+<a href="/files" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
77 </a>77 </a>
78 </li>78 </li>
79 <li>79 <li>
80-<a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd>80+<a href="/files/tree/main" data-key="g f" aria-current="page">Files <kbd aria-hidden="true">g f</kbd>
81 </a>81 </a>
82 </li>82 </li>
83 <li>83 <li>
84-<a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd>84+<a href="/files/commits/main" data-key="g c">Commits <kbd aria-hidden="true">g c</kbd>
85 </a>85 </a>
86 </li>86 </li>
87 <li>87 <li>
88-<a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>88+<a href="/files/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
89 </a>89 </a>
90 </li>90 </li>
91 <li>91 <li>
92-<a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>92+<a href="/files/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
93 </a>93 </a>
94 </li>94 </li>
95 <li>95 <li>
96-<a href="/files/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>96+<a href="/files/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
97 </a>97 </a>
98 </li>98 </li>
99 <li>99 <li>
100-<a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>100+<a href="/files/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
101 </a>101 </a>
102 </li>102 </li>
103 </ul>103 </ul>
@@ -228,14 +228,16 @@
228 </div>228 </div>
229 <div>229 <div>
230 <dt>230 <dt>
231-<kbd>s</kbd>231+<kbd>g s</kbd>
232-<kbd>t</kbd>232+<kbd>g f</kbd>
233-<kbd>c</kbd>233+<kbd>g c</kbd>
234-<kbd>r</kbd>234+<kbd>g b</kbd>
235-<kbd>m</kbd>235+<kbd>g m</kbd>
236-<kbd>i</kbd>236+<kbd>g i</kbd>
237+<kbd>g r</kbd>
238+<kbd>g a</kbd>
237 </dt>239 </dt>
238-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>240+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
239 </div>241 </div>
240 <div>242 <div>
241 <dt>243 <dt>
@@ -334,45 +336,45 @@
334 <span class="k">go</span>336 <span class="k">go</span>
335 </a>337 </a>
336 </li>338 </li>
337-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">339+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
338 <a role="presentation" data-href="/files">340 <a role="presentation" data-href="/files">
339-<span class="lbl">summary</span>341+<span class="lbl">summary (g s)</span>
340 <span class="k">section</span>342 <span class="k">section</span>
341 </a>343 </a>
342 </li>344 </li>
343-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">345+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
344 <a role="presentation" data-href="/files/tree/main">346 <a role="presentation" data-href="/files/tree/main">
345-<span class="lbl">files</span>347+<span class="lbl">files (g f)</span>
346 <span class="k">section</span>348 <span class="k">section</span>
347 </a>349 </a>
348 </li>350 </li>
349-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">351+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
350 <a role="presentation" data-href="/files/commits/main">352 <a role="presentation" data-href="/files/commits/main">
351-<span class="lbl">commits</span>353+<span class="lbl">commits (g c)</span>
352 <span class="k">section</span>354 <span class="k">section</span>
353 </a>355 </a>
354 </li>356 </li>
355-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">357+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
356 <a role="presentation" data-href="/files/refs">358 <a role="presentation" data-href="/files/refs">
357-<span class="lbl">branches &amp; tags</span>359+<span class="lbl">branches &amp; tags (g b)</span>
358 <span class="k">section</span>360 <span class="k">section</span>
359 </a>361 </a>
360 </li>362 </li>
361-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">363+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
362 <a role="presentation" data-href="/files/merge-requests">364 <a role="presentation" data-href="/files/merge-requests">
363-<span class="lbl">merge requests</span>365+<span class="lbl">merge requests (g m)</span>
364 <span class="k">section</span>366 <span class="k">section</span>
365 </a>367 </a>
366 </li>368 </li>
367-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">369+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
368 <a role="presentation" data-href="/files/releases">370 <a role="presentation" data-href="/files/releases">
369-<span class="lbl">releases</span>371+<span class="lbl">releases (g r)</span>
370 <span class="k">section</span>372 <span class="k">section</span>
371 </a>373 </a>
372 </li>374 </li>
373-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">375+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
374 <a role="presentation" data-href="/files/actions">376 <a role="presentation" data-href="/files/actions">
375-<span class="lbl">actions</span>377+<span class="lbl">actions (g a)</span>
376 <span class="k">section</span>378 <span class="k">section</span>
377 </a>379 </a>
378 </li>380 </li>
@@ -425,7 +427,9 @@
425 k-filter from:body -> ermite.focusFilter();427 k-filter from:body -> ermite.focusFilter();
426 k-gh from:body -> ermite.go('.brand');428 k-gh from:body -> ermite.go('.brand');
427 k-s from:body -> ermite.tab('s');429 k-s from:body -> ermite.tab('s');
428- k-t from:body -> ermite.tab('t');430+ k-f from:body -> ermite.tab('f');
431+ k-b from:body -> ermite.tab('b');
432+ k-a from:body -> ermite.tab('a');
429 k-c from:body -> ermite.tab('c');433 k-c from:body -> ermite.tab('c');
430 k-r from:body -> ermite.tab('r');434 k-r from:body -> ermite.tab('r');
431 k-m from:body -> ermite.tab('m');435 k-m from:body -> ermite.tab('m');
@@ -441,7 +445,6 @@
441 k-permalink from:body -> ermite.permalink();445 k-permalink from:body -> ermite.permalink();
442 k-merge from:body -> window.ermiteMR?.openMerge();446 k-merge from:body -> window.ermiteMR?.openMerge();
443 k-read from:body -> window.ermiteMR?.markRead();447 k-read from:body -> window.ermiteMR?.markRead();
444- k-gf from:body -> window.ermiteMR?.focusFiles();
445 k-keys from:body -> ermite.toggleKeys()">448 k-keys from:body -> ermite.toggleKeys()">
446 </div>449 </div>
447 </body>450 </body>
M internal/app/testdata/web/blob-trojan.html +33 -30
@@ -73,31 +73,31 @@
7373 <nav class="tabs" aria-label="Sections">
7474 <ul>
7575 <li>
76-<a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>
76+<a href="/files" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
7777 </a>
7878 </li>
7979 <li>
80-<a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd>
80+<a href="/files/tree/main" data-key="g f" aria-current="page">Files <kbd aria-hidden="true">g f</kbd>
8181 </a>
8282 </li>
8383 <li>
84-<a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd>
84+<a href="/files/commits/main" data-key="g c">Commits <kbd aria-hidden="true">g c</kbd>
8585 </a>
8686 </li>
8787 <li>
88-<a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>
88+<a href="/files/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
8989 </a>
9090 </li>
9191 <li>
92-<a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>
92+<a href="/files/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
9393 </a>
9494 </li>
9595 <li>
96-<a href="/files/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>
96+<a href="/files/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
9797 </a>
9898 </li>
9999 <li>
100-<a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>
100+<a href="/files/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
101101 </a>
102102 </li>
103103 </ul>
@@ -295,14 +295,16 @@
295295 </div>
296296 <div>
297297 <dt>
298-<kbd>s</kbd>
299-<kbd>t</kbd>
300-<kbd>c</kbd>
301-<kbd>r</kbd>
302-<kbd>m</kbd>
303-<kbd>i</kbd>
298+<kbd>g s</kbd>
299+<kbd>g f</kbd>
300+<kbd>g c</kbd>
301+<kbd>g b</kbd>
302+<kbd>g m</kbd>
303+<kbd>g i</kbd>
304+<kbd>g r</kbd>
305+<kbd>g a</kbd>
304306 </dt>
305-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>
307+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
306308 </div>
307309 <div>
308310 <dt>
@@ -401,45 +403,45 @@
401403 <span class="k">go</span>
402404 </a>
403405 </li>
404-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">
406+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
405407 <a role="presentation" data-href="/files">
406-<span class="lbl">summary</span>
408+<span class="lbl">summary (g s)</span>
407409 <span class="k">section</span>
408410 </a>
409411 </li>
410-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">
412+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
411413 <a role="presentation" data-href="/files/tree/main">
412-<span class="lbl">files</span>
414+<span class="lbl">files (g f)</span>
413415 <span class="k">section</span>
414416 </a>
415417 </li>
416-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">
418+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
417419 <a role="presentation" data-href="/files/commits/main">
418-<span class="lbl">commits</span>
420+<span class="lbl">commits (g c)</span>
419421 <span class="k">section</span>
420422 </a>
421423 </li>
422-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">
424+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
423425 <a role="presentation" data-href="/files/refs">
424-<span class="lbl">branches &amp; tags</span>
426+<span class="lbl">branches &amp; tags (g b)</span>
425427 <span class="k">section</span>
426428 </a>
427429 </li>
428-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">
430+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
429431 <a role="presentation" data-href="/files/merge-requests">
430-<span class="lbl">merge requests</span>
432+<span class="lbl">merge requests (g m)</span>
431433 <span class="k">section</span>
432434 </a>
433435 </li>
434-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">
436+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
435437 <a role="presentation" data-href="/files/releases">
436-<span class="lbl">releases</span>
438+<span class="lbl">releases (g r)</span>
437439 <span class="k">section</span>
438440 </a>
439441 </li>
440-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">
442+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
441443 <a role="presentation" data-href="/files/actions">
442-<span class="lbl">actions</span>
444+<span class="lbl">actions (g a)</span>
443445 <span class="k">section</span>
444446 </a>
445447 </li>
@@ -492,7 +494,9 @@
492494 k-filter from:body -> ermite.focusFilter();
493495 k-gh from:body -> ermite.go('.brand');
494496 k-s from:body -> ermite.tab('s');
495- k-t from:body -> ermite.tab('t');
497+ k-f from:body -> ermite.tab('f');
498+ k-b from:body -> ermite.tab('b');
499+ k-a from:body -> ermite.tab('a');
496500 k-c from:body -> ermite.tab('c');
497501 k-r from:body -> ermite.tab('r');
498502 k-m from:body -> ermite.tab('m');
@@ -508,7 +512,6 @@
508512 k-permalink from:body -> ermite.permalink();
509513 k-merge from:body -> window.ermiteMR?.openMerge();
510514 k-read from:body -> window.ermiteMR?.markRead();
511- k-gf from:body -> window.ermiteMR?.focusFiles();
512515 k-keys from:body -> ermite.toggleKeys()">
513516 </div>
514517 </body>
@@ -73,31 +73,31 @@
73 <nav class="tabs" aria-label="Sections">73 <nav class="tabs" aria-label="Sections">
74 <ul>74 <ul>
75 <li>75 <li>
76-<a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>76+<a href="/files" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
77 </a>77 </a>
78 </li>78 </li>
79 <li>79 <li>
80-<a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd>80+<a href="/files/tree/main" data-key="g f" aria-current="page">Files <kbd aria-hidden="true">g f</kbd>
81 </a>81 </a>
82 </li>82 </li>
83 <li>83 <li>
84-<a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd>84+<a href="/files/commits/main" data-key="g c">Commits <kbd aria-hidden="true">g c</kbd>
85 </a>85 </a>
86 </li>86 </li>
87 <li>87 <li>
88-<a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>88+<a href="/files/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
89 </a>89 </a>
90 </li>90 </li>
91 <li>91 <li>
92-<a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>92+<a href="/files/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
93 </a>93 </a>
94 </li>94 </li>
95 <li>95 <li>
96-<a href="/files/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>96+<a href="/files/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
97 </a>97 </a>
98 </li>98 </li>
99 <li>99 <li>
100-<a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>100+<a href="/files/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
101 </a>101 </a>
102 </li>102 </li>
103 </ul>103 </ul>
@@ -295,14 +295,16 @@
295 </div>295 </div>
296 <div>296 <div>
297 <dt>297 <dt>
298-<kbd>s</kbd>298+<kbd>g s</kbd>
299-<kbd>t</kbd>299+<kbd>g f</kbd>
300-<kbd>c</kbd>300+<kbd>g c</kbd>
301-<kbd>r</kbd>301+<kbd>g b</kbd>
302-<kbd>m</kbd>302+<kbd>g m</kbd>
303-<kbd>i</kbd>303+<kbd>g i</kbd>
304+<kbd>g r</kbd>
305+<kbd>g a</kbd>
304 </dt>306 </dt>
305-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>307+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
306 </div>308 </div>
307 <div>309 <div>
308 <dt>310 <dt>
@@ -401,45 +403,45 @@
401 <span class="k">go</span>403 <span class="k">go</span>
402 </a>404 </a>
403 </li>405 </li>
404-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">406+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
405 <a role="presentation" data-href="/files">407 <a role="presentation" data-href="/files">
406-<span class="lbl">summary</span>408+<span class="lbl">summary (g s)</span>
407 <span class="k">section</span>409 <span class="k">section</span>
408 </a>410 </a>
409 </li>411 </li>
410-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">412+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
411 <a role="presentation" data-href="/files/tree/main">413 <a role="presentation" data-href="/files/tree/main">
412-<span class="lbl">files</span>414+<span class="lbl">files (g f)</span>
413 <span class="k">section</span>415 <span class="k">section</span>
414 </a>416 </a>
415 </li>417 </li>
416-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">418+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
417 <a role="presentation" data-href="/files/commits/main">419 <a role="presentation" data-href="/files/commits/main">
418-<span class="lbl">commits</span>420+<span class="lbl">commits (g c)</span>
419 <span class="k">section</span>421 <span class="k">section</span>
420 </a>422 </a>
421 </li>423 </li>
422-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">424+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
423 <a role="presentation" data-href="/files/refs">425 <a role="presentation" data-href="/files/refs">
424-<span class="lbl">branches &amp; tags</span>426+<span class="lbl">branches &amp; tags (g b)</span>
425 <span class="k">section</span>427 <span class="k">section</span>
426 </a>428 </a>
427 </li>429 </li>
428-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">430+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
429 <a role="presentation" data-href="/files/merge-requests">431 <a role="presentation" data-href="/files/merge-requests">
430-<span class="lbl">merge requests</span>432+<span class="lbl">merge requests (g m)</span>
431 <span class="k">section</span>433 <span class="k">section</span>
432 </a>434 </a>
433 </li>435 </li>
434-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">436+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
435 <a role="presentation" data-href="/files/releases">437 <a role="presentation" data-href="/files/releases">
436-<span class="lbl">releases</span>438+<span class="lbl">releases (g r)</span>
437 <span class="k">section</span>439 <span class="k">section</span>
438 </a>440 </a>
439 </li>441 </li>
440-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">442+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
441 <a role="presentation" data-href="/files/actions">443 <a role="presentation" data-href="/files/actions">
442-<span class="lbl">actions</span>444+<span class="lbl">actions (g a)</span>
443 <span class="k">section</span>445 <span class="k">section</span>
444 </a>446 </a>
445 </li>447 </li>
@@ -492,7 +494,9 @@
492 k-filter from:body -> ermite.focusFilter();494 k-filter from:body -> ermite.focusFilter();
493 k-gh from:body -> ermite.go('.brand');495 k-gh from:body -> ermite.go('.brand');
494 k-s from:body -> ermite.tab('s');496 k-s from:body -> ermite.tab('s');
495- k-t from:body -> ermite.tab('t');497+ k-f from:body -> ermite.tab('f');
498+ k-b from:body -> ermite.tab('b');
499+ k-a from:body -> ermite.tab('a');
496 k-c from:body -> ermite.tab('c');500 k-c from:body -> ermite.tab('c');
497 k-r from:body -> ermite.tab('r');501 k-r from:body -> ermite.tab('r');
498 k-m from:body -> ermite.tab('m');502 k-m from:body -> ermite.tab('m');
@@ -508,7 +512,6 @@
508 k-permalink from:body -> ermite.permalink();512 k-permalink from:body -> ermite.permalink();
509 k-merge from:body -> window.ermiteMR?.openMerge();513 k-merge from:body -> window.ermiteMR?.openMerge();
510 k-read from:body -> window.ermiteMR?.markRead();514 k-read from:body -> window.ermiteMR?.markRead();
511- k-gf from:body -> window.ermiteMR?.focusFiles();
512 k-keys from:body -> ermite.toggleKeys()">515 k-keys from:body -> ermite.toggleKeys()">
513 </div>516 </div>
514 </body>517 </body>
M internal/app/testdata/web/commit-big.html +33 -30
@@ -72,31 +72,31 @@
7272 <nav class="tabs" aria-label="Sections">
7373 <ul>
7474 <li>
75-<a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>
75+<a href="/diffs" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
7676 </a>
7777 </li>
7878 <li>
79-<a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd>
79+<a href="/diffs/tree/main" data-key="g f">Files <kbd aria-hidden="true">g f</kbd>
8080 </a>
8181 </li>
8282 <li>
83-<a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd>
83+<a href="/diffs/commits/main" data-key="g c" aria-current="page">Commits <kbd aria-hidden="true">g c</kbd>
8484 </a>
8585 </li>
8686 <li>
87-<a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>
87+<a href="/diffs/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
8888 </a>
8989 </li>
9090 <li>
91-<a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>
91+<a href="/diffs/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
9292 </a>
9393 </li>
9494 <li>
95-<a href="/diffs/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>
95+<a href="/diffs/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
9696 </a>
9797 </li>
9898 <li>
99-<a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>
99+<a href="/diffs/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
100100 </a>
101101 </li>
102102 </ul>
@@ -280,14 +280,16 @@
280280 </div>
281281 <div>
282282 <dt>
283-<kbd>s</kbd>
284-<kbd>t</kbd>
285-<kbd>c</kbd>
286-<kbd>r</kbd>
287-<kbd>m</kbd>
288-<kbd>i</kbd>
283+<kbd>g s</kbd>
284+<kbd>g f</kbd>
285+<kbd>g c</kbd>
286+<kbd>g b</kbd>
287+<kbd>g m</kbd>
288+<kbd>g i</kbd>
289+<kbd>g r</kbd>
290+<kbd>g a</kbd>
289291 </dt>
290-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>
292+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
291293 </div>
292294 <div>
293295 <dt>
@@ -386,45 +388,45 @@
386388 <span class="k">go</span>
387389 </a>
388390 </li>
389-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">
391+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
390392 <a role="presentation" data-href="/diffs">
391-<span class="lbl">summary</span>
393+<span class="lbl">summary (g s)</span>
392394 <span class="k">section</span>
393395 </a>
394396 </li>
395-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">
397+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
396398 <a role="presentation" data-href="/diffs/tree/main">
397-<span class="lbl">files</span>
399+<span class="lbl">files (g f)</span>
398400 <span class="k">section</span>
399401 </a>
400402 </li>
401-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">
403+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
402404 <a role="presentation" data-href="/diffs/commits/main">
403-<span class="lbl">commits</span>
405+<span class="lbl">commits (g c)</span>
404406 <span class="k">section</span>
405407 </a>
406408 </li>
407-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">
409+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
408410 <a role="presentation" data-href="/diffs/refs">
409-<span class="lbl">branches &amp; tags</span>
411+<span class="lbl">branches &amp; tags (g b)</span>
410412 <span class="k">section</span>
411413 </a>
412414 </li>
413-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">
415+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
414416 <a role="presentation" data-href="/diffs/merge-requests">
415-<span class="lbl">merge requests</span>
417+<span class="lbl">merge requests (g m)</span>
416418 <span class="k">section</span>
417419 </a>
418420 </li>
419-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">
421+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
420422 <a role="presentation" data-href="/diffs/releases">
421-<span class="lbl">releases</span>
423+<span class="lbl">releases (g r)</span>
422424 <span class="k">section</span>
423425 </a>
424426 </li>
425-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">
427+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
426428 <a role="presentation" data-href="/diffs/actions">
427-<span class="lbl">actions</span>
429+<span class="lbl">actions (g a)</span>
428430 <span class="k">section</span>
429431 </a>
430432 </li>
@@ -477,7 +479,9 @@
477479 k-filter from:body -> ermite.focusFilter();
478480 k-gh from:body -> ermite.go('.brand');
479481 k-s from:body -> ermite.tab('s');
480- k-t from:body -> ermite.tab('t');
482+ k-f from:body -> ermite.tab('f');
483+ k-b from:body -> ermite.tab('b');
484+ k-a from:body -> ermite.tab('a');
481485 k-c from:body -> ermite.tab('c');
482486 k-r from:body -> ermite.tab('r');
483487 k-m from:body -> ermite.tab('m');
@@ -493,7 +497,6 @@
493497 k-permalink from:body -> ermite.permalink();
494498 k-merge from:body -> window.ermiteMR?.openMerge();
495499 k-read from:body -> window.ermiteMR?.markRead();
496- k-gf from:body -> window.ermiteMR?.focusFiles();
497500 k-keys from:body -> ermite.toggleKeys()">
498501 </div>
499502 </body>
@@ -72,31 +72,31 @@
72 <nav class="tabs" aria-label="Sections">72 <nav class="tabs" aria-label="Sections">
73 <ul>73 <ul>
74 <li>74 <li>
75-<a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>75+<a href="/diffs" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
76 </a>76 </a>
77 </li>77 </li>
78 <li>78 <li>
79-<a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd>79+<a href="/diffs/tree/main" data-key="g f">Files <kbd aria-hidden="true">g f</kbd>
80 </a>80 </a>
81 </li>81 </li>
82 <li>82 <li>
83-<a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd>83+<a href="/diffs/commits/main" data-key="g c" aria-current="page">Commits <kbd aria-hidden="true">g c</kbd>
84 </a>84 </a>
85 </li>85 </li>
86 <li>86 <li>
87-<a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>87+<a href="/diffs/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
88 </a>88 </a>
89 </li>89 </li>
90 <li>90 <li>
91-<a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>91+<a href="/diffs/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
92 </a>92 </a>
93 </li>93 </li>
94 <li>94 <li>
95-<a href="/diffs/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>95+<a href="/diffs/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
96 </a>96 </a>
97 </li>97 </li>
98 <li>98 <li>
99-<a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>99+<a href="/diffs/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
100 </a>100 </a>
101 </li>101 </li>
102 </ul>102 </ul>
@@ -280,14 +280,16 @@
280 </div>280 </div>
281 <div>281 <div>
282 <dt>282 <dt>
283-<kbd>s</kbd>283+<kbd>g s</kbd>
284-<kbd>t</kbd>284+<kbd>g f</kbd>
285-<kbd>c</kbd>285+<kbd>g c</kbd>
286-<kbd>r</kbd>286+<kbd>g b</kbd>
287-<kbd>m</kbd>287+<kbd>g m</kbd>
288-<kbd>i</kbd>288+<kbd>g i</kbd>
289+<kbd>g r</kbd>
290+<kbd>g a</kbd>
289 </dt>291 </dt>
290-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>292+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
291 </div>293 </div>
292 <div>294 <div>
293 <dt>295 <dt>
@@ -386,45 +388,45 @@
386 <span class="k">go</span>388 <span class="k">go</span>
387 </a>389 </a>
388 </li>390 </li>
389-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">391+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
390 <a role="presentation" data-href="/diffs">392 <a role="presentation" data-href="/diffs">
391-<span class="lbl">summary</span>393+<span class="lbl">summary (g s)</span>
392 <span class="k">section</span>394 <span class="k">section</span>
393 </a>395 </a>
394 </li>396 </li>
395-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">397+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
396 <a role="presentation" data-href="/diffs/tree/main">398 <a role="presentation" data-href="/diffs/tree/main">
397-<span class="lbl">files</span>399+<span class="lbl">files (g f)</span>
398 <span class="k">section</span>400 <span class="k">section</span>
399 </a>401 </a>
400 </li>402 </li>
401-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">403+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
402 <a role="presentation" data-href="/diffs/commits/main">404 <a role="presentation" data-href="/diffs/commits/main">
403-<span class="lbl">commits</span>405+<span class="lbl">commits (g c)</span>
404 <span class="k">section</span>406 <span class="k">section</span>
405 </a>407 </a>
406 </li>408 </li>
407-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">409+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
408 <a role="presentation" data-href="/diffs/refs">410 <a role="presentation" data-href="/diffs/refs">
409-<span class="lbl">branches &amp; tags</span>411+<span class="lbl">branches &amp; tags (g b)</span>
410 <span class="k">section</span>412 <span class="k">section</span>
411 </a>413 </a>
412 </li>414 </li>
413-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">415+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
414 <a role="presentation" data-href="/diffs/merge-requests">416 <a role="presentation" data-href="/diffs/merge-requests">
415-<span class="lbl">merge requests</span>417+<span class="lbl">merge requests (g m)</span>
416 <span class="k">section</span>418 <span class="k">section</span>
417 </a>419 </a>
418 </li>420 </li>
419-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">421+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
420 <a role="presentation" data-href="/diffs/releases">422 <a role="presentation" data-href="/diffs/releases">
421-<span class="lbl">releases</span>423+<span class="lbl">releases (g r)</span>
422 <span class="k">section</span>424 <span class="k">section</span>
423 </a>425 </a>
424 </li>426 </li>
425-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">427+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
426 <a role="presentation" data-href="/diffs/actions">428 <a role="presentation" data-href="/diffs/actions">
427-<span class="lbl">actions</span>429+<span class="lbl">actions (g a)</span>
428 <span class="k">section</span>430 <span class="k">section</span>
429 </a>431 </a>
430 </li>432 </li>
@@ -477,7 +479,9 @@
477 k-filter from:body -> ermite.focusFilter();479 k-filter from:body -> ermite.focusFilter();
478 k-gh from:body -> ermite.go('.brand');480 k-gh from:body -> ermite.go('.brand');
479 k-s from:body -> ermite.tab('s');481 k-s from:body -> ermite.tab('s');
480- k-t from:body -> ermite.tab('t');482+ k-f from:body -> ermite.tab('f');
483+ k-b from:body -> ermite.tab('b');
484+ k-a from:body -> ermite.tab('a');
481 k-c from:body -> ermite.tab('c');485 k-c from:body -> ermite.tab('c');
482 k-r from:body -> ermite.tab('r');486 k-r from:body -> ermite.tab('r');
483 k-m from:body -> ermite.tab('m');487 k-m from:body -> ermite.tab('m');
@@ -493,7 +497,6 @@
493 k-permalink from:body -> ermite.permalink();497 k-permalink from:body -> ermite.permalink();
494 k-merge from:body -> window.ermiteMR?.openMerge();498 k-merge from:body -> window.ermiteMR?.openMerge();
495 k-read from:body -> window.ermiteMR?.markRead();499 k-read from:body -> window.ermiteMR?.markRead();
496- k-gf from:body -> window.ermiteMR?.focusFiles();
497 k-keys from:body -> ermite.toggleKeys()">500 k-keys from:body -> ermite.toggleKeys()">
498 </div>501 </div>
499 </body>502 </body>
M internal/app/testdata/web/commit-binary.html +33 -30
@@ -72,31 +72,31 @@
7272 <nav class="tabs" aria-label="Sections">
7373 <ul>
7474 <li>
75-<a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>
75+<a href="/diffs" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
7676 </a>
7777 </li>
7878 <li>
79-<a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd>
79+<a href="/diffs/tree/main" data-key="g f">Files <kbd aria-hidden="true">g f</kbd>
8080 </a>
8181 </li>
8282 <li>
83-<a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd>
83+<a href="/diffs/commits/main" data-key="g c" aria-current="page">Commits <kbd aria-hidden="true">g c</kbd>
8484 </a>
8585 </li>
8686 <li>
87-<a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>
87+<a href="/diffs/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
8888 </a>
8989 </li>
9090 <li>
91-<a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>
91+<a href="/diffs/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
9292 </a>
9393 </li>
9494 <li>
95-<a href="/diffs/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>
95+<a href="/diffs/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
9696 </a>
9797 </li>
9898 <li>
99-<a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>
99+<a href="/diffs/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
100100 </a>
101101 </li>
102102 </ul>
@@ -271,14 +271,16 @@
271271 </div>
272272 <div>
273273 <dt>
274-<kbd>s</kbd>
275-<kbd>t</kbd>
276-<kbd>c</kbd>
277-<kbd>r</kbd>
278-<kbd>m</kbd>
279-<kbd>i</kbd>
274+<kbd>g s</kbd>
275+<kbd>g f</kbd>
276+<kbd>g c</kbd>
277+<kbd>g b</kbd>
278+<kbd>g m</kbd>
279+<kbd>g i</kbd>
280+<kbd>g r</kbd>
281+<kbd>g a</kbd>
280282 </dt>
281-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>
283+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
282284 </div>
283285 <div>
284286 <dt>
@@ -377,45 +379,45 @@
377379 <span class="k">go</span>
378380 </a>
379381 </li>
380-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">
382+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
381383 <a role="presentation" data-href="/diffs">
382-<span class="lbl">summary</span>
384+<span class="lbl">summary (g s)</span>
383385 <span class="k">section</span>
384386 </a>
385387 </li>
386-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">
388+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
387389 <a role="presentation" data-href="/diffs/tree/main">
388-<span class="lbl">files</span>
390+<span class="lbl">files (g f)</span>
389391 <span class="k">section</span>
390392 </a>
391393 </li>
392-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">
394+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
393395 <a role="presentation" data-href="/diffs/commits/main">
394-<span class="lbl">commits</span>
396+<span class="lbl">commits (g c)</span>
395397 <span class="k">section</span>
396398 </a>
397399 </li>
398-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">
400+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
399401 <a role="presentation" data-href="/diffs/refs">
400-<span class="lbl">branches &amp; tags</span>
402+<span class="lbl">branches &amp; tags (g b)</span>
401403 <span class="k">section</span>
402404 </a>
403405 </li>
404-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">
406+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
405407 <a role="presentation" data-href="/diffs/merge-requests">
406-<span class="lbl">merge requests</span>
408+<span class="lbl">merge requests (g m)</span>
407409 <span class="k">section</span>
408410 </a>
409411 </li>
410-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">
412+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
411413 <a role="presentation" data-href="/diffs/releases">
412-<span class="lbl">releases</span>
414+<span class="lbl">releases (g r)</span>
413415 <span class="k">section</span>
414416 </a>
415417 </li>
416-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">
418+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
417419 <a role="presentation" data-href="/diffs/actions">
418-<span class="lbl">actions</span>
420+<span class="lbl">actions (g a)</span>
419421 <span class="k">section</span>
420422 </a>
421423 </li>
@@ -468,7 +470,9 @@
468470 k-filter from:body -> ermite.focusFilter();
469471 k-gh from:body -> ermite.go('.brand');
470472 k-s from:body -> ermite.tab('s');
471- k-t from:body -> ermite.tab('t');
473+ k-f from:body -> ermite.tab('f');
474+ k-b from:body -> ermite.tab('b');
475+ k-a from:body -> ermite.tab('a');
472476 k-c from:body -> ermite.tab('c');
473477 k-r from:body -> ermite.tab('r');
474478 k-m from:body -> ermite.tab('m');
@@ -484,7 +488,6 @@
484488 k-permalink from:body -> ermite.permalink();
485489 k-merge from:body -> window.ermiteMR?.openMerge();
486490 k-read from:body -> window.ermiteMR?.markRead();
487- k-gf from:body -> window.ermiteMR?.focusFiles();
488491 k-keys from:body -> ermite.toggleKeys()">
489492 </div>
490493 </body>
@@ -72,31 +72,31 @@
72 <nav class="tabs" aria-label="Sections">72 <nav class="tabs" aria-label="Sections">
73 <ul>73 <ul>
74 <li>74 <li>
75-<a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>75+<a href="/diffs" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
76 </a>76 </a>
77 </li>77 </li>
78 <li>78 <li>
79-<a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd>79+<a href="/diffs/tree/main" data-key="g f">Files <kbd aria-hidden="true">g f</kbd>
80 </a>80 </a>
81 </li>81 </li>
82 <li>82 <li>
83-<a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd>83+<a href="/diffs/commits/main" data-key="g c" aria-current="page">Commits <kbd aria-hidden="true">g c</kbd>
84 </a>84 </a>
85 </li>85 </li>
86 <li>86 <li>
87-<a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>87+<a href="/diffs/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
88 </a>88 </a>
89 </li>89 </li>
90 <li>90 <li>
91-<a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>91+<a href="/diffs/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
92 </a>92 </a>
93 </li>93 </li>
94 <li>94 <li>
95-<a href="/diffs/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>95+<a href="/diffs/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
96 </a>96 </a>
97 </li>97 </li>
98 <li>98 <li>
99-<a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>99+<a href="/diffs/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
100 </a>100 </a>
101 </li>101 </li>
102 </ul>102 </ul>
@@ -271,14 +271,16 @@
271 </div>271 </div>
272 <div>272 <div>
273 <dt>273 <dt>
274-<kbd>s</kbd>274+<kbd>g s</kbd>
275-<kbd>t</kbd>275+<kbd>g f</kbd>
276-<kbd>c</kbd>276+<kbd>g c</kbd>
277-<kbd>r</kbd>277+<kbd>g b</kbd>
278-<kbd>m</kbd>278+<kbd>g m</kbd>
279-<kbd>i</kbd>279+<kbd>g i</kbd>
280+<kbd>g r</kbd>
281+<kbd>g a</kbd>
280 </dt>282 </dt>
281-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>283+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
282 </div>284 </div>
283 <div>285 <div>
284 <dt>286 <dt>
@@ -377,45 +379,45 @@
377 <span class="k">go</span>379 <span class="k">go</span>
378 </a>380 </a>
379 </li>381 </li>
380-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">382+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
381 <a role="presentation" data-href="/diffs">383 <a role="presentation" data-href="/diffs">
382-<span class="lbl">summary</span>384+<span class="lbl">summary (g s)</span>
383 <span class="k">section</span>385 <span class="k">section</span>
384 </a>386 </a>
385 </li>387 </li>
386-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">388+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
387 <a role="presentation" data-href="/diffs/tree/main">389 <a role="presentation" data-href="/diffs/tree/main">
388-<span class="lbl">files</span>390+<span class="lbl">files (g f)</span>
389 <span class="k">section</span>391 <span class="k">section</span>
390 </a>392 </a>
391 </li>393 </li>
392-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">394+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
393 <a role="presentation" data-href="/diffs/commits/main">395 <a role="presentation" data-href="/diffs/commits/main">
394-<span class="lbl">commits</span>396+<span class="lbl">commits (g c)</span>
395 <span class="k">section</span>397 <span class="k">section</span>
396 </a>398 </a>
397 </li>399 </li>
398-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">400+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
399 <a role="presentation" data-href="/diffs/refs">401 <a role="presentation" data-href="/diffs/refs">
400-<span class="lbl">branches &amp; tags</span>402+<span class="lbl">branches &amp; tags (g b)</span>
401 <span class="k">section</span>403 <span class="k">section</span>
402 </a>404 </a>
403 </li>405 </li>
404-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">406+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
405 <a role="presentation" data-href="/diffs/merge-requests">407 <a role="presentation" data-href="/diffs/merge-requests">
406-<span class="lbl">merge requests</span>408+<span class="lbl">merge requests (g m)</span>
407 <span class="k">section</span>409 <span class="k">section</span>
408 </a>410 </a>
409 </li>411 </li>
410-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">412+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
411 <a role="presentation" data-href="/diffs/releases">413 <a role="presentation" data-href="/diffs/releases">
412-<span class="lbl">releases</span>414+<span class="lbl">releases (g r)</span>
413 <span class="k">section</span>415 <span class="k">section</span>
414 </a>416 </a>
415 </li>417 </li>
416-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">418+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
417 <a role="presentation" data-href="/diffs/actions">419 <a role="presentation" data-href="/diffs/actions">
418-<span class="lbl">actions</span>420+<span class="lbl">actions (g a)</span>
419 <span class="k">section</span>421 <span class="k">section</span>
420 </a>422 </a>
421 </li>423 </li>
@@ -468,7 +470,9 @@
468 k-filter from:body -> ermite.focusFilter();470 k-filter from:body -> ermite.focusFilter();
469 k-gh from:body -> ermite.go('.brand');471 k-gh from:body -> ermite.go('.brand');
470 k-s from:body -> ermite.tab('s');472 k-s from:body -> ermite.tab('s');
471- k-t from:body -> ermite.tab('t');473+ k-f from:body -> ermite.tab('f');
474+ k-b from:body -> ermite.tab('b');
475+ k-a from:body -> ermite.tab('a');
472 k-c from:body -> ermite.tab('c');476 k-c from:body -> ermite.tab('c');
473 k-r from:body -> ermite.tab('r');477 k-r from:body -> ermite.tab('r');
474 k-m from:body -> ermite.tab('m');478 k-m from:body -> ermite.tab('m');
@@ -484,7 +488,6 @@
484 k-permalink from:body -> ermite.permalink();488 k-permalink from:body -> ermite.permalink();
485 k-merge from:body -> window.ermiteMR?.openMerge();489 k-merge from:body -> window.ermiteMR?.openMerge();
486 k-read from:body -> window.ermiteMR?.markRead();490 k-read from:body -> window.ermiteMR?.markRead();
487- k-gf from:body -> window.ermiteMR?.focusFiles();
488 k-keys from:body -> ermite.toggleKeys()">491 k-keys from:body -> ermite.toggleKeys()">
489 </div>492 </div>
490 </body>493 </body>
M internal/app/testdata/web/commit-merge.html +33 -30
@@ -72,31 +72,31 @@
7272 <nav class="tabs" aria-label="Sections">
7373 <ul>
7474 <li>
75-<a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>
75+<a href="/diffs" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
7676 </a>
7777 </li>
7878 <li>
79-<a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd>
79+<a href="/diffs/tree/main" data-key="g f">Files <kbd aria-hidden="true">g f</kbd>
8080 </a>
8181 </li>
8282 <li>
83-<a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd>
83+<a href="/diffs/commits/main" data-key="g c" aria-current="page">Commits <kbd aria-hidden="true">g c</kbd>
8484 </a>
8585 </li>
8686 <li>
87-<a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>
87+<a href="/diffs/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
8888 </a>
8989 </li>
9090 <li>
91-<a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>
91+<a href="/diffs/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
9292 </a>
9393 </li>
9494 <li>
95-<a href="/diffs/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>
95+<a href="/diffs/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
9696 </a>
9797 </li>
9898 <li>
99-<a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>
99+<a href="/diffs/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
100100 </a>
101101 </li>
102102 </ul>
@@ -313,14 +313,16 @@
313313 </div>
314314 <div>
315315 <dt>
316-<kbd>s</kbd>
317-<kbd>t</kbd>
318-<kbd>c</kbd>
319-<kbd>r</kbd>
320-<kbd>m</kbd>
321-<kbd>i</kbd>
316+<kbd>g s</kbd>
317+<kbd>g f</kbd>
318+<kbd>g c</kbd>
319+<kbd>g b</kbd>
320+<kbd>g m</kbd>
321+<kbd>g i</kbd>
322+<kbd>g r</kbd>
323+<kbd>g a</kbd>
322324 </dt>
323-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>
325+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
324326 </div>
325327 <div>
326328 <dt>
@@ -419,45 +421,45 @@
419421 <span class="k">go</span>
420422 </a>
421423 </li>
422-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">
424+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
423425 <a role="presentation" data-href="/diffs">
424-<span class="lbl">summary</span>
426+<span class="lbl">summary (g s)</span>
425427 <span class="k">section</span>
426428 </a>
427429 </li>
428-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">
430+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
429431 <a role="presentation" data-href="/diffs/tree/main">
430-<span class="lbl">files</span>
432+<span class="lbl">files (g f)</span>
431433 <span class="k">section</span>
432434 </a>
433435 </li>
434-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">
436+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
435437 <a role="presentation" data-href="/diffs/commits/main">
436-<span class="lbl">commits</span>
438+<span class="lbl">commits (g c)</span>
437439 <span class="k">section</span>
438440 </a>
439441 </li>
440-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">
442+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
441443 <a role="presentation" data-href="/diffs/refs">
442-<span class="lbl">branches &amp; tags</span>
444+<span class="lbl">branches &amp; tags (g b)</span>
443445 <span class="k">section</span>
444446 </a>
445447 </li>
446-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">
448+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
447449 <a role="presentation" data-href="/diffs/merge-requests">
448-<span class="lbl">merge requests</span>
450+<span class="lbl">merge requests (g m)</span>
449451 <span class="k">section</span>
450452 </a>
451453 </li>
452-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">
454+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
453455 <a role="presentation" data-href="/diffs/releases">
454-<span class="lbl">releases</span>
456+<span class="lbl">releases (g r)</span>
455457 <span class="k">section</span>
456458 </a>
457459 </li>
458-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">
460+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
459461 <a role="presentation" data-href="/diffs/actions">
460-<span class="lbl">actions</span>
462+<span class="lbl">actions (g a)</span>
461463 <span class="k">section</span>
462464 </a>
463465 </li>
@@ -510,7 +512,9 @@
510512 k-filter from:body -> ermite.focusFilter();
511513 k-gh from:body -> ermite.go('.brand');
512514 k-s from:body -> ermite.tab('s');
513- k-t from:body -> ermite.tab('t');
515+ k-f from:body -> ermite.tab('f');
516+ k-b from:body -> ermite.tab('b');
517+ k-a from:body -> ermite.tab('a');
514518 k-c from:body -> ermite.tab('c');
515519 k-r from:body -> ermite.tab('r');
516520 k-m from:body -> ermite.tab('m');
@@ -526,7 +530,6 @@
526530 k-permalink from:body -> ermite.permalink();
527531 k-merge from:body -> window.ermiteMR?.openMerge();
528532 k-read from:body -> window.ermiteMR?.markRead();
529- k-gf from:body -> window.ermiteMR?.focusFiles();
530533 k-keys from:body -> ermite.toggleKeys()">
531534 </div>
532535 </body>
@@ -72,31 +72,31 @@
72 <nav class="tabs" aria-label="Sections">72 <nav class="tabs" aria-label="Sections">
73 <ul>73 <ul>
74 <li>74 <li>
75-<a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>75+<a href="/diffs" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
76 </a>76 </a>
77 </li>77 </li>
78 <li>78 <li>
79-<a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd>79+<a href="/diffs/tree/main" data-key="g f">Files <kbd aria-hidden="true">g f</kbd>
80 </a>80 </a>
81 </li>81 </li>
82 <li>82 <li>
83-<a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd>83+<a href="/diffs/commits/main" data-key="g c" aria-current="page">Commits <kbd aria-hidden="true">g c</kbd>
84 </a>84 </a>
85 </li>85 </li>
86 <li>86 <li>
87-<a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>87+<a href="/diffs/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
88 </a>88 </a>
89 </li>89 </li>
90 <li>90 <li>
91-<a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>91+<a href="/diffs/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
92 </a>92 </a>
93 </li>93 </li>
94 <li>94 <li>
95-<a href="/diffs/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>95+<a href="/diffs/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
96 </a>96 </a>
97 </li>97 </li>
98 <li>98 <li>
99-<a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>99+<a href="/diffs/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
100 </a>100 </a>
101 </li>101 </li>
102 </ul>102 </ul>
@@ -313,14 +313,16 @@
313 </div>313 </div>
314 <div>314 <div>
315 <dt>315 <dt>
316-<kbd>s</kbd>316+<kbd>g s</kbd>
317-<kbd>t</kbd>317+<kbd>g f</kbd>
318-<kbd>c</kbd>318+<kbd>g c</kbd>
319-<kbd>r</kbd>319+<kbd>g b</kbd>
320-<kbd>m</kbd>320+<kbd>g m</kbd>
321-<kbd>i</kbd>321+<kbd>g i</kbd>
322+<kbd>g r</kbd>
323+<kbd>g a</kbd>
322 </dt>324 </dt>
323-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>325+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
324 </div>326 </div>
325 <div>327 <div>
326 <dt>328 <dt>
@@ -419,45 +421,45 @@
419 <span class="k">go</span>421 <span class="k">go</span>
420 </a>422 </a>
421 </li>423 </li>
422-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">424+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
423 <a role="presentation" data-href="/diffs">425 <a role="presentation" data-href="/diffs">
424-<span class="lbl">summary</span>426+<span class="lbl">summary (g s)</span>
425 <span class="k">section</span>427 <span class="k">section</span>
426 </a>428 </a>
427 </li>429 </li>
428-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">430+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
429 <a role="presentation" data-href="/diffs/tree/main">431 <a role="presentation" data-href="/diffs/tree/main">
430-<span class="lbl">files</span>432+<span class="lbl">files (g f)</span>
431 <span class="k">section</span>433 <span class="k">section</span>
432 </a>434 </a>
433 </li>435 </li>
434-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">436+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
435 <a role="presentation" data-href="/diffs/commits/main">437 <a role="presentation" data-href="/diffs/commits/main">
436-<span class="lbl">commits</span>438+<span class="lbl">commits (g c)</span>
437 <span class="k">section</span>439 <span class="k">section</span>
438 </a>440 </a>
439 </li>441 </li>
440-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">442+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
441 <a role="presentation" data-href="/diffs/refs">443 <a role="presentation" data-href="/diffs/refs">
442-<span class="lbl">branches &amp; tags</span>444+<span class="lbl">branches &amp; tags (g b)</span>
443 <span class="k">section</span>445 <span class="k">section</span>
444 </a>446 </a>
445 </li>447 </li>
446-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">448+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
447 <a role="presentation" data-href="/diffs/merge-requests">449 <a role="presentation" data-href="/diffs/merge-requests">
448-<span class="lbl">merge requests</span>450+<span class="lbl">merge requests (g m)</span>
449 <span class="k">section</span>451 <span class="k">section</span>
450 </a>452 </a>
451 </li>453 </li>
452-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">454+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
453 <a role="presentation" data-href="/diffs/releases">455 <a role="presentation" data-href="/diffs/releases">
454-<span class="lbl">releases</span>456+<span class="lbl">releases (g r)</span>
455 <span class="k">section</span>457 <span class="k">section</span>
456 </a>458 </a>
457 </li>459 </li>
458-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">460+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
459 <a role="presentation" data-href="/diffs/actions">461 <a role="presentation" data-href="/diffs/actions">
460-<span class="lbl">actions</span>462+<span class="lbl">actions (g a)</span>
461 <span class="k">section</span>463 <span class="k">section</span>
462 </a>464 </a>
463 </li>465 </li>
@@ -510,7 +512,9 @@
510 k-filter from:body -> ermite.focusFilter();512 k-filter from:body -> ermite.focusFilter();
511 k-gh from:body -> ermite.go('.brand');513 k-gh from:body -> ermite.go('.brand');
512 k-s from:body -> ermite.tab('s');514 k-s from:body -> ermite.tab('s');
513- k-t from:body -> ermite.tab('t');515+ k-f from:body -> ermite.tab('f');
516+ k-b from:body -> ermite.tab('b');
517+ k-a from:body -> ermite.tab('a');
514 k-c from:body -> ermite.tab('c');518 k-c from:body -> ermite.tab('c');
515 k-r from:body -> ermite.tab('r');519 k-r from:body -> ermite.tab('r');
516 k-m from:body -> ermite.tab('m');520 k-m from:body -> ermite.tab('m');
@@ -526,7 +530,6 @@
526 k-permalink from:body -> ermite.permalink();530 k-permalink from:body -> ermite.permalink();
527 k-merge from:body -> window.ermiteMR?.openMerge();531 k-merge from:body -> window.ermiteMR?.openMerge();
528 k-read from:body -> window.ermiteMR?.markRead();532 k-read from:body -> window.ermiteMR?.markRead();
529- k-gf from:body -> window.ermiteMR?.focusFiles();
530 k-keys from:body -> ermite.toggleKeys()">533 k-keys from:body -> ermite.toggleKeys()">
531 </div>534 </div>
532 </body>535 </body>
M internal/app/testdata/web/commit-modify-split.html +33 -30
@@ -72,31 +72,31 @@
7272 <nav class="tabs" aria-label="Sections">
7373 <ul>
7474 <li>
75-<a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>
75+<a href="/diffs" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
7676 </a>
7777 </li>
7878 <li>
79-<a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd>
79+<a href="/diffs/tree/main" data-key="g f">Files <kbd aria-hidden="true">g f</kbd>
8080 </a>
8181 </li>
8282 <li>
83-<a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd>
83+<a href="/diffs/commits/main" data-key="g c" aria-current="page">Commits <kbd aria-hidden="true">g c</kbd>
8484 </a>
8585 </li>
8686 <li>
87-<a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>
87+<a href="/diffs/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
8888 </a>
8989 </li>
9090 <li>
91-<a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>
91+<a href="/diffs/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
9292 </a>
9393 </li>
9494 <li>
95-<a href="/diffs/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>
95+<a href="/diffs/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
9696 </a>
9797 </li>
9898 <li>
99-<a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>
99+<a href="/diffs/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
100100 </a>
101101 </li>
102102 </ul>
@@ -436,14 +436,16 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
436436 </div>
437437 <div>
438438 <dt>
439-<kbd>s</kbd>
440-<kbd>t</kbd>
441-<kbd>c</kbd>
442-<kbd>r</kbd>
443-<kbd>m</kbd>
444-<kbd>i</kbd>
439+<kbd>g s</kbd>
440+<kbd>g f</kbd>
441+<kbd>g c</kbd>
442+<kbd>g b</kbd>
443+<kbd>g m</kbd>
444+<kbd>g i</kbd>
445+<kbd>g r</kbd>
446+<kbd>g a</kbd>
445447 </dt>
446-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>
448+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
447449 </div>
448450 <div>
449451 <dt>
@@ -542,45 +544,45 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
542544 <span class="k">go</span>
543545 </a>
544546 </li>
545-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">
547+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
546548 <a role="presentation" data-href="/diffs">
547-<span class="lbl">summary</span>
549+<span class="lbl">summary (g s)</span>
548550 <span class="k">section</span>
549551 </a>
550552 </li>
551-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">
553+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
552554 <a role="presentation" data-href="/diffs/tree/main">
553-<span class="lbl">files</span>
555+<span class="lbl">files (g f)</span>
554556 <span class="k">section</span>
555557 </a>
556558 </li>
557-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">
559+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
558560 <a role="presentation" data-href="/diffs/commits/main">
559-<span class="lbl">commits</span>
561+<span class="lbl">commits (g c)</span>
560562 <span class="k">section</span>
561563 </a>
562564 </li>
563-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">
565+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
564566 <a role="presentation" data-href="/diffs/refs">
565-<span class="lbl">branches &amp; tags</span>
567+<span class="lbl">branches &amp; tags (g b)</span>
566568 <span class="k">section</span>
567569 </a>
568570 </li>
569-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">
571+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
570572 <a role="presentation" data-href="/diffs/merge-requests">
571-<span class="lbl">merge requests</span>
573+<span class="lbl">merge requests (g m)</span>
572574 <span class="k">section</span>
573575 </a>
574576 </li>
575-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">
577+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
576578 <a role="presentation" data-href="/diffs/releases">
577-<span class="lbl">releases</span>
579+<span class="lbl">releases (g r)</span>
578580 <span class="k">section</span>
579581 </a>
580582 </li>
581-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">
583+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
582584 <a role="presentation" data-href="/diffs/actions">
583-<span class="lbl">actions</span>
585+<span class="lbl">actions (g a)</span>
584586 <span class="k">section</span>
585587 </a>
586588 </li>
@@ -633,7 +635,9 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
633635 k-filter from:body -> ermite.focusFilter();
634636 k-gh from:body -> ermite.go('.brand');
635637 k-s from:body -> ermite.tab('s');
636- k-t from:body -> ermite.tab('t');
638+ k-f from:body -> ermite.tab('f');
639+ k-b from:body -> ermite.tab('b');
640+ k-a from:body -> ermite.tab('a');
637641 k-c from:body -> ermite.tab('c');
638642 k-r from:body -> ermite.tab('r');
639643 k-m from:body -> ermite.tab('m');
@@ -649,7 +653,6 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
649653 k-permalink from:body -> ermite.permalink();
650654 k-merge from:body -> window.ermiteMR?.openMerge();
651655 k-read from:body -> window.ermiteMR?.markRead();
652- k-gf from:body -> window.ermiteMR?.focusFiles();
653656 k-keys from:body -> ermite.toggleKeys()">
654657 </div>
655658 </body>
@@ -72,31 +72,31 @@
72 <nav class="tabs" aria-label="Sections">72 <nav class="tabs" aria-label="Sections">
73 <ul>73 <ul>
74 <li>74 <li>
75-<a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>75+<a href="/diffs" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
76 </a>76 </a>
77 </li>77 </li>
78 <li>78 <li>
79-<a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd>79+<a href="/diffs/tree/main" data-key="g f">Files <kbd aria-hidden="true">g f</kbd>
80 </a>80 </a>
81 </li>81 </li>
82 <li>82 <li>
83-<a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd>83+<a href="/diffs/commits/main" data-key="g c" aria-current="page">Commits <kbd aria-hidden="true">g c</kbd>
84 </a>84 </a>
85 </li>85 </li>
86 <li>86 <li>
87-<a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>87+<a href="/diffs/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
88 </a>88 </a>
89 </li>89 </li>
90 <li>90 <li>
91-<a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>91+<a href="/diffs/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
92 </a>92 </a>
93 </li>93 </li>
94 <li>94 <li>
95-<a href="/diffs/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>95+<a href="/diffs/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
96 </a>96 </a>
97 </li>97 </li>
98 <li>98 <li>
99-<a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>99+<a href="/diffs/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
100 </a>100 </a>
101 </li>101 </li>
102 </ul>102 </ul>
@@ -436,14 +436,16 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
436 </div>436 </div>
437 <div>437 <div>
438 <dt>438 <dt>
439-<kbd>s</kbd>439+<kbd>g s</kbd>
440-<kbd>t</kbd>440+<kbd>g f</kbd>
441-<kbd>c</kbd>441+<kbd>g c</kbd>
442-<kbd>r</kbd>442+<kbd>g b</kbd>
443-<kbd>m</kbd>443+<kbd>g m</kbd>
444-<kbd>i</kbd>444+<kbd>g i</kbd>
445+<kbd>g r</kbd>
446+<kbd>g a</kbd>
445 </dt>447 </dt>
446-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>448+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
447 </div>449 </div>
448 <div>450 <div>
449 <dt>451 <dt>
@@ -542,45 +544,45 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
542 <span class="k">go</span>544 <span class="k">go</span>
543 </a>545 </a>
544 </li>546 </li>
545-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">547+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
546 <a role="presentation" data-href="/diffs">548 <a role="presentation" data-href="/diffs">
547-<span class="lbl">summary</span>549+<span class="lbl">summary (g s)</span>
548 <span class="k">section</span>550 <span class="k">section</span>
549 </a>551 </a>
550 </li>552 </li>
551-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">553+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
552 <a role="presentation" data-href="/diffs/tree/main">554 <a role="presentation" data-href="/diffs/tree/main">
553-<span class="lbl">files</span>555+<span class="lbl">files (g f)</span>
554 <span class="k">section</span>556 <span class="k">section</span>
555 </a>557 </a>
556 </li>558 </li>
557-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">559+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
558 <a role="presentation" data-href="/diffs/commits/main">560 <a role="presentation" data-href="/diffs/commits/main">
559-<span class="lbl">commits</span>561+<span class="lbl">commits (g c)</span>
560 <span class="k">section</span>562 <span class="k">section</span>
561 </a>563 </a>
562 </li>564 </li>
563-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">565+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
564 <a role="presentation" data-href="/diffs/refs">566 <a role="presentation" data-href="/diffs/refs">
565-<span class="lbl">branches &amp; tags</span>567+<span class="lbl">branches &amp; tags (g b)</span>
566 <span class="k">section</span>568 <span class="k">section</span>
567 </a>569 </a>
568 </li>570 </li>
569-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">571+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
570 <a role="presentation" data-href="/diffs/merge-requests">572 <a role="presentation" data-href="/diffs/merge-requests">
571-<span class="lbl">merge requests</span>573+<span class="lbl">merge requests (g m)</span>
572 <span class="k">section</span>574 <span class="k">section</span>
573 </a>575 </a>
574 </li>576 </li>
575-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">577+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
576 <a role="presentation" data-href="/diffs/releases">578 <a role="presentation" data-href="/diffs/releases">
577-<span class="lbl">releases</span>579+<span class="lbl">releases (g r)</span>
578 <span class="k">section</span>580 <span class="k">section</span>
579 </a>581 </a>
580 </li>582 </li>
581-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">583+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
582 <a role="presentation" data-href="/diffs/actions">584 <a role="presentation" data-href="/diffs/actions">
583-<span class="lbl">actions</span>585+<span class="lbl">actions (g a)</span>
584 <span class="k">section</span>586 <span class="k">section</span>
585 </a>587 </a>
586 </li>588 </li>
@@ -633,7 +635,9 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
633 k-filter from:body -> ermite.focusFilter();635 k-filter from:body -> ermite.focusFilter();
634 k-gh from:body -> ermite.go('.brand');636 k-gh from:body -> ermite.go('.brand');
635 k-s from:body -> ermite.tab('s');637 k-s from:body -> ermite.tab('s');
636- k-t from:body -> ermite.tab('t');638+ k-f from:body -> ermite.tab('f');
639+ k-b from:body -> ermite.tab('b');
640+ k-a from:body -> ermite.tab('a');
637 k-c from:body -> ermite.tab('c');641 k-c from:body -> ermite.tab('c');
638 k-r from:body -> ermite.tab('r');642 k-r from:body -> ermite.tab('r');
639 k-m from:body -> ermite.tab('m');643 k-m from:body -> ermite.tab('m');
@@ -649,7 +653,6 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
649 k-permalink from:body -> ermite.permalink();653 k-permalink from:body -> ermite.permalink();
650 k-merge from:body -> window.ermiteMR?.openMerge();654 k-merge from:body -> window.ermiteMR?.openMerge();
651 k-read from:body -> window.ermiteMR?.markRead();655 k-read from:body -> window.ermiteMR?.markRead();
652- k-gf from:body -> window.ermiteMR?.focusFiles();
653 k-keys from:body -> ermite.toggleKeys()">656 k-keys from:body -> ermite.toggleKeys()">
654 </div>657 </div>
655 </body>658 </body>
M internal/app/testdata/web/commit-modify.html +33 -30
@@ -72,31 +72,31 @@
7272 <nav class="tabs" aria-label="Sections">
7373 <ul>
7474 <li>
75-<a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>
75+<a href="/diffs" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
7676 </a>
7777 </li>
7878 <li>
79-<a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd>
79+<a href="/diffs/tree/main" data-key="g f">Files <kbd aria-hidden="true">g f</kbd>
8080 </a>
8181 </li>
8282 <li>
83-<a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd>
83+<a href="/diffs/commits/main" data-key="g c" aria-current="page">Commits <kbd aria-hidden="true">g c</kbd>
8484 </a>
8585 </li>
8686 <li>
87-<a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>
87+<a href="/diffs/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
8888 </a>
8989 </li>
9090 <li>
91-<a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>
91+<a href="/diffs/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
9292 </a>
9393 </li>
9494 <li>
95-<a href="/diffs/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>
95+<a href="/diffs/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
9696 </a>
9797 </li>
9898 <li>
99-<a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>
99+<a href="/diffs/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
100100 </a>
101101 </li>
102102 </ul>
@@ -436,14 +436,16 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
436436 </div>
437437 <div>
438438 <dt>
439-<kbd>s</kbd>
440-<kbd>t</kbd>
441-<kbd>c</kbd>
442-<kbd>r</kbd>
443-<kbd>m</kbd>
444-<kbd>i</kbd>
439+<kbd>g s</kbd>
440+<kbd>g f</kbd>
441+<kbd>g c</kbd>
442+<kbd>g b</kbd>
443+<kbd>g m</kbd>
444+<kbd>g i</kbd>
445+<kbd>g r</kbd>
446+<kbd>g a</kbd>
445447 </dt>
446-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>
448+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
447449 </div>
448450 <div>
449451 <dt>
@@ -542,45 +544,45 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
542544 <span class="k">go</span>
543545 </a>
544546 </li>
545-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">
547+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
546548 <a role="presentation" data-href="/diffs">
547-<span class="lbl">summary</span>
549+<span class="lbl">summary (g s)</span>
548550 <span class="k">section</span>
549551 </a>
550552 </li>
551-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">
553+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
552554 <a role="presentation" data-href="/diffs/tree/main">
553-<span class="lbl">files</span>
555+<span class="lbl">files (g f)</span>
554556 <span class="k">section</span>
555557 </a>
556558 </li>
557-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">
559+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
558560 <a role="presentation" data-href="/diffs/commits/main">
559-<span class="lbl">commits</span>
561+<span class="lbl">commits (g c)</span>
560562 <span class="k">section</span>
561563 </a>
562564 </li>
563-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">
565+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
564566 <a role="presentation" data-href="/diffs/refs">
565-<span class="lbl">branches &amp; tags</span>
567+<span class="lbl">branches &amp; tags (g b)</span>
566568 <span class="k">section</span>
567569 </a>
568570 </li>
569-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">
571+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
570572 <a role="presentation" data-href="/diffs/merge-requests">
571-<span class="lbl">merge requests</span>
573+<span class="lbl">merge requests (g m)</span>
572574 <span class="k">section</span>
573575 </a>
574576 </li>
575-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">
577+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
576578 <a role="presentation" data-href="/diffs/releases">
577-<span class="lbl">releases</span>
579+<span class="lbl">releases (g r)</span>
578580 <span class="k">section</span>
579581 </a>
580582 </li>
581-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">
583+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
582584 <a role="presentation" data-href="/diffs/actions">
583-<span class="lbl">actions</span>
585+<span class="lbl">actions (g a)</span>
584586 <span class="k">section</span>
585587 </a>
586588 </li>
@@ -633,7 +635,9 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
633635 k-filter from:body -> ermite.focusFilter();
634636 k-gh from:body -> ermite.go('.brand');
635637 k-s from:body -> ermite.tab('s');
636- k-t from:body -> ermite.tab('t');
638+ k-f from:body -> ermite.tab('f');
639+ k-b from:body -> ermite.tab('b');
640+ k-a from:body -> ermite.tab('a');
637641 k-c from:body -> ermite.tab('c');
638642 k-r from:body -> ermite.tab('r');
639643 k-m from:body -> ermite.tab('m');
@@ -649,7 +653,6 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
649653 k-permalink from:body -> ermite.permalink();
650654 k-merge from:body -> window.ermiteMR?.openMerge();
651655 k-read from:body -> window.ermiteMR?.markRead();
652- k-gf from:body -> window.ermiteMR?.focusFiles();
653656 k-keys from:body -> ermite.toggleKeys()">
654657 </div>
655658 </body>
@@ -72,31 +72,31 @@
72 <nav class="tabs" aria-label="Sections">72 <nav class="tabs" aria-label="Sections">
73 <ul>73 <ul>
74 <li>74 <li>
75-<a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>75+<a href="/diffs" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
76 </a>76 </a>
77 </li>77 </li>
78 <li>78 <li>
79-<a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd>79+<a href="/diffs/tree/main" data-key="g f">Files <kbd aria-hidden="true">g f</kbd>
80 </a>80 </a>
81 </li>81 </li>
82 <li>82 <li>
83-<a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd>83+<a href="/diffs/commits/main" data-key="g c" aria-current="page">Commits <kbd aria-hidden="true">g c</kbd>
84 </a>84 </a>
85 </li>85 </li>
86 <li>86 <li>
87-<a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>87+<a href="/diffs/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
88 </a>88 </a>
89 </li>89 </li>
90 <li>90 <li>
91-<a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>91+<a href="/diffs/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
92 </a>92 </a>
93 </li>93 </li>
94 <li>94 <li>
95-<a href="/diffs/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>95+<a href="/diffs/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
96 </a>96 </a>
97 </li>97 </li>
98 <li>98 <li>
99-<a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>99+<a href="/diffs/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
100 </a>100 </a>
101 </li>101 </li>
102 </ul>102 </ul>
@@ -436,14 +436,16 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
436 </div>436 </div>
437 <div>437 <div>
438 <dt>438 <dt>
439-<kbd>s</kbd>439+<kbd>g s</kbd>
440-<kbd>t</kbd>440+<kbd>g f</kbd>
441-<kbd>c</kbd>441+<kbd>g c</kbd>
442-<kbd>r</kbd>442+<kbd>g b</kbd>
443-<kbd>m</kbd>443+<kbd>g m</kbd>
444-<kbd>i</kbd>444+<kbd>g i</kbd>
445+<kbd>g r</kbd>
446+<kbd>g a</kbd>
445 </dt>447 </dt>
446-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>448+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
447 </div>449 </div>
448 <div>450 <div>
449 <dt>451 <dt>
@@ -542,45 +544,45 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
542 <span class="k">go</span>544 <span class="k">go</span>
543 </a>545 </a>
544 </li>546 </li>
545-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">547+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
546 <a role="presentation" data-href="/diffs">548 <a role="presentation" data-href="/diffs">
547-<span class="lbl">summary</span>549+<span class="lbl">summary (g s)</span>
548 <span class="k">section</span>550 <span class="k">section</span>
549 </a>551 </a>
550 </li>552 </li>
551-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">553+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
552 <a role="presentation" data-href="/diffs/tree/main">554 <a role="presentation" data-href="/diffs/tree/main">
553-<span class="lbl">files</span>555+<span class="lbl">files (g f)</span>
554 <span class="k">section</span>556 <span class="k">section</span>
555 </a>557 </a>
556 </li>558 </li>
557-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">559+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
558 <a role="presentation" data-href="/diffs/commits/main">560 <a role="presentation" data-href="/diffs/commits/main">
559-<span class="lbl">commits</span>561+<span class="lbl">commits (g c)</span>
560 <span class="k">section</span>562 <span class="k">section</span>
561 </a>563 </a>
562 </li>564 </li>
563-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">565+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
564 <a role="presentation" data-href="/diffs/refs">566 <a role="presentation" data-href="/diffs/refs">
565-<span class="lbl">branches &amp; tags</span>567+<span class="lbl">branches &amp; tags (g b)</span>
566 <span class="k">section</span>568 <span class="k">section</span>
567 </a>569 </a>
568 </li>570 </li>
569-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">571+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
570 <a role="presentation" data-href="/diffs/merge-requests">572 <a role="presentation" data-href="/diffs/merge-requests">
571-<span class="lbl">merge requests</span>573+<span class="lbl">merge requests (g m)</span>
572 <span class="k">section</span>574 <span class="k">section</span>
573 </a>575 </a>
574 </li>576 </li>
575-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">577+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
576 <a role="presentation" data-href="/diffs/releases">578 <a role="presentation" data-href="/diffs/releases">
577-<span class="lbl">releases</span>579+<span class="lbl">releases (g r)</span>
578 <span class="k">section</span>580 <span class="k">section</span>
579 </a>581 </a>
580 </li>582 </li>
581-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">583+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
582 <a role="presentation" data-href="/diffs/actions">584 <a role="presentation" data-href="/diffs/actions">
583-<span class="lbl">actions</span>585+<span class="lbl">actions (g a)</span>
584 <span class="k">section</span>586 <span class="k">section</span>
585 </a>587 </a>
586 </li>588 </li>
@@ -633,7 +635,9 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
633 k-filter from:body -> ermite.focusFilter();635 k-filter from:body -> ermite.focusFilter();
634 k-gh from:body -> ermite.go('.brand');636 k-gh from:body -> ermite.go('.brand');
635 k-s from:body -> ermite.tab('s');637 k-s from:body -> ermite.tab('s');
636- k-t from:body -> ermite.tab('t');638+ k-f from:body -> ermite.tab('f');
639+ k-b from:body -> ermite.tab('b');
640+ k-a from:body -> ermite.tab('a');
637 k-c from:body -> ermite.tab('c');641 k-c from:body -> ermite.tab('c');
638 k-r from:body -> ermite.tab('r');642 k-r from:body -> ermite.tab('r');
639 k-m from:body -> ermite.tab('m');643 k-m from:body -> ermite.tab('m');
@@ -649,7 +653,6 @@ And &lt;b&gt;html&lt;/b&gt; &amp; &#34;quotes&#34;.</pre>
649 k-permalink from:body -> ermite.permalink();653 k-permalink from:body -> ermite.permalink();
650 k-merge from:body -> window.ermiteMR?.openMerge();654 k-merge from:body -> window.ermiteMR?.openMerge();
651 k-read from:body -> window.ermiteMR?.markRead();655 k-read from:body -> window.ermiteMR?.markRead();
652- k-gf from:body -> window.ermiteMR?.focusFiles();
653 k-keys from:body -> ermite.toggleKeys()">656 k-keys from:body -> ermite.toggleKeys()">
654 </div>657 </div>
655 </body>658 </body>
M internal/app/testdata/web/commit-rename.html +33 -30
@@ -72,31 +72,31 @@
7272 <nav class="tabs" aria-label="Sections">
7373 <ul>
7474 <li>
75-<a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>
75+<a href="/diffs" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
7676 </a>
7777 </li>
7878 <li>
79-<a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd>
79+<a href="/diffs/tree/main" data-key="g f">Files <kbd aria-hidden="true">g f</kbd>
8080 </a>
8181 </li>
8282 <li>
83-<a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd>
83+<a href="/diffs/commits/main" data-key="g c" aria-current="page">Commits <kbd aria-hidden="true">g c</kbd>
8484 </a>
8585 </li>
8686 <li>
87-<a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>
87+<a href="/diffs/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
8888 </a>
8989 </li>
9090 <li>
91-<a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>
91+<a href="/diffs/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
9292 </a>
9393 </li>
9494 <li>
95-<a href="/diffs/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>
95+<a href="/diffs/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
9696 </a>
9797 </li>
9898 <li>
99-<a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>
99+<a href="/diffs/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
100100 </a>
101101 </li>
102102 </ul>
@@ -340,14 +340,16 @@
340340 </div>
341341 <div>
342342 <dt>
343-<kbd>s</kbd>
344-<kbd>t</kbd>
345-<kbd>c</kbd>
346-<kbd>r</kbd>
347-<kbd>m</kbd>
348-<kbd>i</kbd>
343+<kbd>g s</kbd>
344+<kbd>g f</kbd>
345+<kbd>g c</kbd>
346+<kbd>g b</kbd>
347+<kbd>g m</kbd>
348+<kbd>g i</kbd>
349+<kbd>g r</kbd>
350+<kbd>g a</kbd>
349351 </dt>
350-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>
352+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
351353 </div>
352354 <div>
353355 <dt>
@@ -446,45 +448,45 @@
446448 <span class="k">go</span>
447449 </a>
448450 </li>
449-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">
451+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
450452 <a role="presentation" data-href="/diffs">
451-<span class="lbl">summary</span>
453+<span class="lbl">summary (g s)</span>
452454 <span class="k">section</span>
453455 </a>
454456 </li>
455-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">
457+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
456458 <a role="presentation" data-href="/diffs/tree/main">
457-<span class="lbl">files</span>
459+<span class="lbl">files (g f)</span>
458460 <span class="k">section</span>
459461 </a>
460462 </li>
461-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">
463+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
462464 <a role="presentation" data-href="/diffs/commits/main">
463-<span class="lbl">commits</span>
465+<span class="lbl">commits (g c)</span>
464466 <span class="k">section</span>
465467 </a>
466468 </li>
467-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">
469+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
468470 <a role="presentation" data-href="/diffs/refs">
469-<span class="lbl">branches &amp; tags</span>
471+<span class="lbl">branches &amp; tags (g b)</span>
470472 <span class="k">section</span>
471473 </a>
472474 </li>
473-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">
475+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
474476 <a role="presentation" data-href="/diffs/merge-requests">
475-<span class="lbl">merge requests</span>
477+<span class="lbl">merge requests (g m)</span>
476478 <span class="k">section</span>
477479 </a>
478480 </li>
479-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">
481+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
480482 <a role="presentation" data-href="/diffs/releases">
481-<span class="lbl">releases</span>
483+<span class="lbl">releases (g r)</span>
482484 <span class="k">section</span>
483485 </a>
484486 </li>
485-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">
487+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
486488 <a role="presentation" data-href="/diffs/actions">
487-<span class="lbl">actions</span>
489+<span class="lbl">actions (g a)</span>
488490 <span class="k">section</span>
489491 </a>
490492 </li>
@@ -537,7 +539,9 @@
537539 k-filter from:body -> ermite.focusFilter();
538540 k-gh from:body -> ermite.go('.brand');
539541 k-s from:body -> ermite.tab('s');
540- k-t from:body -> ermite.tab('t');
542+ k-f from:body -> ermite.tab('f');
543+ k-b from:body -> ermite.tab('b');
544+ k-a from:body -> ermite.tab('a');
541545 k-c from:body -> ermite.tab('c');
542546 k-r from:body -> ermite.tab('r');
543547 k-m from:body -> ermite.tab('m');
@@ -553,7 +557,6 @@
553557 k-permalink from:body -> ermite.permalink();
554558 k-merge from:body -> window.ermiteMR?.openMerge();
555559 k-read from:body -> window.ermiteMR?.markRead();
556- k-gf from:body -> window.ermiteMR?.focusFiles();
557560 k-keys from:body -> ermite.toggleKeys()">
558561 </div>
559562 </body>
@@ -72,31 +72,31 @@
72 <nav class="tabs" aria-label="Sections">72 <nav class="tabs" aria-label="Sections">
73 <ul>73 <ul>
74 <li>74 <li>
75-<a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd>75+<a href="/diffs" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd>
76 </a>76 </a>
77 </li>77 </li>
78 <li>78 <li>
79-<a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd>79+<a href="/diffs/tree/main" data-key="g f">Files <kbd aria-hidden="true">g f</kbd>
80 </a>80 </a>
81 </li>81 </li>
82 <li>82 <li>
83-<a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd>83+<a href="/diffs/commits/main" data-key="g c" aria-current="page">Commits <kbd aria-hidden="true">g c</kbd>
84 </a>84 </a>
85 </li>85 </li>
86 <li>86 <li>
87-<a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches &amp; tags <kbd aria-hidden="true">r</kbd>87+<a href="/diffs/refs" data-key="g b">Branches &amp; tags <kbd aria-hidden="true">g b</kbd>
88 </a>88 </a>
89 </li>89 </li>
90 <li>90 <li>
91-<a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd>91+<a href="/diffs/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd>
92 </a>92 </a>
93 </li>93 </li>
94 <li>94 <li>
95-<a href="/diffs/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd>95+<a href="/diffs/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd>
96 </a>96 </a>
97 </li>97 </li>
98 <li>98 <li>
99-<a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd>99+<a href="/diffs/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd>
100 </a>100 </a>
101 </li>101 </li>
102 </ul>102 </ul>
@@ -340,14 +340,16 @@
340 </div>340 </div>
341 <div>341 <div>
342 <dt>342 <dt>
343-<kbd>s</kbd>343+<kbd>g s</kbd>
344-<kbd>t</kbd>344+<kbd>g f</kbd>
345-<kbd>c</kbd>345+<kbd>g c</kbd>
346-<kbd>r</kbd>346+<kbd>g b</kbd>
347-<kbd>m</kbd>347+<kbd>g m</kbd>
348-<kbd>i</kbd>348+<kbd>g i</kbd>
349+<kbd>g r</kbd>
350+<kbd>g a</kbd>
349 </dt>351 </dt>
350-<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues</dd>352+<dd>tabs: summary, files, commits, branches &amp; tags, merge requests, issues, releases, actions (when available)</dd>
351 </div>353 </div>
352 <div>354 <div>
353 <dt>355 <dt>
@@ -446,45 +448,45 @@
446 <span class="k">go</span>448 <span class="k">go</span>
447 </a>449 </a>
448 </li>450 </li>
449-<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary">451+<li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)">
450 <a role="presentation" data-href="/diffs">452 <a role="presentation" data-href="/diffs">
451-<span class="lbl">summary</span>453+<span class="lbl">summary (g s)</span>
452 <span class="k">section</span>454 <span class="k">section</span>
453 </a>455 </a>
454 </li>456 </li>
455-<li id="cmd-o2" role="option" aria-selected="false" data-k="section files">457+<li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)">
456 <a role="presentation" data-href="/diffs/tree/main">458 <a role="presentation" data-href="/diffs/tree/main">
457-<span class="lbl">files</span>459+<span class="lbl">files (g f)</span>
458 <span class="k">section</span>460 <span class="k">section</span>
459 </a>461 </a>
460 </li>462 </li>
461-<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits">463+<li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)">
462 <a role="presentation" data-href="/diffs/commits/main">464 <a role="presentation" data-href="/diffs/commits/main">
463-<span class="lbl">commits</span>465+<span class="lbl">commits (g c)</span>
464 <span class="k">section</span>466 <span class="k">section</span>
465 </a>467 </a>
466 </li>468 </li>
467-<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags">469+<li id="cmd-o4" role="option" aria-selected="false" data-k="section branches &amp; tags (g b)">
468 <a role="presentation" data-href="/diffs/refs">470 <a role="presentation" data-href="/diffs/refs">
469-<span class="lbl">branches &amp; tags</span>471+<span class="lbl">branches &amp; tags (g b)</span>
470 <span class="k">section</span>472 <span class="k">section</span>
471 </a>473 </a>
472 </li>474 </li>
473-<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests">475+<li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)">
474 <a role="presentation" data-href="/diffs/merge-requests">476 <a role="presentation" data-href="/diffs/merge-requests">
475-<span class="lbl">merge requests</span>477+<span class="lbl">merge requests (g m)</span>
476 <span class="k">section</span>478 <span class="k">section</span>
477 </a>479 </a>
478 </li>480 </li>
479-<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases">481+<li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)">
480 <a role="presentation" data-href="/diffs/releases">482 <a role="presentation" data-href="/diffs/releases">
481-<span class="lbl">releases</span>483+<span class="lbl">releases (g r)</span>
482 <span class="k">section</span>484 <span class="k">section</span>
483 </a>485 </a>
484 </li>486 </li>
485-<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions">487+<li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)">
486 <a role="presentation" data-href="/diffs/actions">488 <a role="presentation" data-href="/diffs/actions">
487-<span class="lbl">actions</span>489+<span class="lbl">actions (g a)</span>
488 <span class="k">section</span>490 <span class="k">section</span>
489 </a>491 </a>
490 </li>492 </li>
@@ -537,7 +539,9 @@
537 k-filter from:body -> ermite.focusFilter();539 k-filter from:body -> ermite.focusFilter();
538 k-gh from:body -> ermite.go('.brand');540 k-gh from:body -> ermite.go('.brand');
539 k-s from:body -> ermite.tab('s');541 k-s from:body -> ermite.tab('s');
540- k-t from:body -> ermite.tab('t');542+ k-f from:body -> ermite.tab('f');
543+ k-b from:body -> ermite.tab('b');
544+ k-a from:body -> ermite.tab('a');
541 k-c from:body -> ermite.tab('c');545 k-c from:body -> ermite.tab('c');
542 k-r from:body -> ermite.tab('r');546 k-r from:body -> ermite.tab('r');
543 k-m from:body -> ermite.tab('m');547 k-m from:body -> ermite.tab('m');
@@ -553,7 +557,6 @@
553 k-permalink from:body -> ermite.permalink();557 k-permalink from:body -> ermite.permalink();
554 k-merge from:body -> window.ermiteMR?.openMerge();558 k-merge from:body -> window.ermiteMR?.openMerge();
555 k-read from:body -> window.ermiteMR?.markRead();559 k-read from:body -> window.ermiteMR?.markRead();
556- k-gf from:body -> window.ermiteMR?.focusFiles();
557 k-keys from:body -> ermite.toggleKeys()">560 k-keys from:body -> ermite.toggleKeys()">
558 </div>561 </div>
559 </body>562 </body>
M internal/app/testdata/web/commit-root.html +33 -30
M internal/app/testdata/web/commit-trojan.html +33 -30
M internal/app/testdata/web/commits-page2.html +33 -30
M internal/app/testdata/web/commits-path.html +33 -30
M internal/app/testdata/web/commits-root.html +33 -30
M internal/app/testdata/web/commits-small.html +33 -30
M internal/app/testdata/web/empty.html +12 -9
M internal/app/testdata/web/home.html +12 -9
M internal/app/testdata/web/not-found-page.html +12 -9
M internal/app/testdata/web/not-found.html +12 -9
M internal/app/testdata/web/refs.html +33 -30
M internal/app/testdata/web/summary.html +33 -30
M internal/app/testdata/web/tree-nested.html +33 -30
M internal/app/testdata/web/tree-root.html +33 -30
M internal/app/testdata/web/tree-slash-ref.html +33 -30
M internal/app/testdata/web/tree-subdir.html +33 -30
M internal/app/web_issue_test.go +1 -1
M internal/app/web_mr_test.go +1 -1
M internal/ports/mocks/ReleaseStore.go +66 -0
M internal/ports/release.go +3 -0
M internal/usecase/releases.go +25 -9
M internal/usecase/releases_integration_test.go +59 -0
A internal/usecase/releases_write_test.go +27 -0

Comments0 open / 0

no comments yet.

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
l
merge request: the list of files (j k move, enter opens, esc back)
M
merge request: the merge menu; there 1–3 pick the mode, D deletes the branch, enter merges
R
merge request: mark the head read
esc
close a dialog, leave a field

Branches 2

Tags 41

↑↓ moveenter openesc close