Merge 5336d8b77ba1fc3916f4dc9c563dddd80bfe37ea into cf2636510abd18ed4875895a18b54026dbca22e1
73 files · +2058 -1284
M README.md +1 -0
| @@ -139,6 +139,7 @@ Give the agent the [`skills/ermite`](skills/ermite/SKILL.md) skill so it knows t | |||
| 139 | 139 | - [Issues](docs/issues.md) | |
| 140 | 140 | - [Commit signatures](docs/commit-signatures.md): SSH and OpenPGP badges, trusted keys | |
| 141 | 141 | - [Web sign-in](docs/web-sign-in.md): passkeys and one-time links | |
| 142 | + | - [Keyboard shortcuts](docs/keyboard-shortcuts.md): web section navigation and page actions | |
| 142 | 143 | - [CI](docs/ci.md): runners, trust, concurrency, reusable workflows | |
| 143 | 144 | - [CI events](docs/ci-events.md) | |
| 144 | 145 | - [OIDC](docs/ci-oidc.md) | |
M docs/issues.md +1 -1
| @@ -50,7 +50,7 @@ Limits: the 100 most recent commits of the merge request, 16 KiB of each text, 1 | |||
| 50 | 50 | ||
| 51 | 51 | The signed-in owner has an Inbox at `/-/inbox`, reached from the **Inbox** button at the top left of the home page, which shows the number of open items. It lists the open merge requests and the open issues of every repository, most recently updated first. The counters at the top (`N merge requests · M issues`) always count everything; `?repo=<name>` restricts the two lists to one repository, and the repository links under the counters set it. Repositories with issues off contribute no issues. | |
| 52 | 52 | ||
| 53 | - | The **Running** section, after **Awaiting moderation**, lists queued and in-progress CI runs newest first, with links to each run, its repository, ref, and start time. The `N running` counter counts every repository; the repository filter also applies to this section. **Shift+I** (`I`) opens the inbox from any page for the signed-in owner, unless single-key shortcuts are off; lowercase `i` still opens the issues tab. | |
| 53 | + | The **Running** section, after **Awaiting moderation**, lists queued and in-progress CI runs newest first, with links to each run, its repository, ref, and start time. The `N running` counter counts every repository; the repository filter also applies to this section. **Shift+I** (`I`) opens the inbox from any page for the signed-in owner, unless single-key shortcuts are off; `g i` opens the issues tab. | |
| 54 | 54 | ||
| 55 | 55 | The page updates live: run status changes, merge request and issue events of any repository refresh the counters and the lists. Anyone who is not the signed-in owner, including a token, gets the same 404 as for an unknown page, and the same answer for the `owner` event stream it follows. | |
| 56 | 56 | ||
A docs/keyboard-shortcuts.md +30 -0
| @@ -0,0 +1,30 @@ | |||
| 1 | + | # Web keyboard shortcuts | |
| 2 | + | ||
| 3 | + | Press `?` for the complete, page-specific list. Shortcuts do not run while typing | |
| 4 | + | in a field. Section navigation uses two successive keys, not a chord: | |
| 5 | + | ||
| 6 | + | | Keys | Destination | | |
| 7 | + | | --- | --- | | |
| 8 | + | | `g s` | Summary | | |
| 9 | + | | `g f` | Files | | |
| 10 | + | | `g c` | Commits | | |
| 11 | + | | `g b` | Branches & tags | | |
| 12 | + | | `g m` | Merge requests | | |
| 13 | + | | `g i` | Issues | | |
| 14 | + | | `g r` | Releases | | |
| 15 | + | | `g a` | Actions (when CI is enabled) | | |
| 16 | + | | `g h` | Home | | |
| 17 | + | | `g g` | Top / first row | | |
| 18 | + | | `G` (Shift+g) | Bottom / last row | | |
| 19 | + | ||
| 20 | + | There is no `g t` alias, and `s`, `t`, `c`, `r`, `m`, and `i` alone do | |
| 21 | + | not navigate sections. `b` still opens the branch/tag switcher; `f`, `:`, | |
| 22 | + | and Ctrl+K (Cmd+K on macOS) open the command palette. | |
| 23 | + | ||
| 24 | + | On merge requests, `l` focuses the files side panel; `g f` always navigates | |
| 25 | + | to the repository Files section. `h` returns to the content. Uppercase `M` | |
| 26 | + | opens the merge menu, `R` marks the head read, `E` expands every diff file, | |
| 27 | + | and `I` opens the signed-in owner's inbox. | |
| 28 | + | ||
| 29 | + | The help dialog can turn off character shortcuts, including `g` sequences | |
| 30 | + | and shifted letters. Ctrl+K / Cmd+K and Escape remain available. | |
M docs/merge-requests.md +1 -1
| @@ -142,7 +142,7 @@ the diff opens the Checks panel and continues to update live. Commit totals use | |||
| 142 | 142 | `N+` when the branch list is capped: the reader's existing count operation counts all reachable | |
| 143 | 143 | ancestors, not the source-minus-target range. | |
| 144 | 144 | ||
| 145 | - | The rail keeps Files (including its `g f` shortcut) and Verdicts. Merge settings | |
| 145 | + | The rail keeps Files (including its `l` shortcut) and Verdicts. Merge settings | |
| 146 | 146 | are read-only information in the More (⋯) menu, available to every viewer. | |
| 147 | 147 | Signing-key information is also there when a merge box offers signed modes; | |
| 148 | 148 | merge actions remain restricted to their existing permissions. | |
M internal/adapters/kvstore/issues_test.go +1 -1
| @@ -364,7 +364,7 @@ func TestSchemaNewerThanThisBuildStopsIssuesData(t *testing.T) { | |||
| 364 | 364 | putRaw(t, dir, "meta/schema", mustProto(t, &storagepb.SchemaVersion{Version: kvstore.SchemaVersion + 1})) | |
| 365 | 365 | _, err := kvstore.Open(ctx, dir) | |
| 366 | 366 | require.ErrorIs(t, err, kvstore.ErrSchemaTooNew) | |
| 367 | - | assert.Equal(t, uint32(6), uint32(kvstore.SchemaVersion)) | |
| 367 | + | assert.Equal(t, uint32(7), uint32(kvstore.SchemaVersion)) | |
| 368 | 368 | } | |
| 369 | 369 | ||
| 370 | 370 | // A reopen racing the delete of the repository either lands before the delete | |
M internal/adapters/kvstore/releases.go +28 -2
| @@ -36,7 +36,26 @@ func relNamePrefix(repo domain.RepoName, id int64) string { | |||
| 36 | 36 | func relNameKey(a domain.ReleaseAsset) []byte { | |
| 37 | 37 | return []byte(relNamePrefix(a.Repo, a.ReleaseID) + a.Name) | |
| 38 | 38 | } | |
| 39 | - | func relUseKey(repo domain.RepoName) []byte { return []byte("reluse/" + repo.String()) } | |
| 39 | + | func relLatestKey(repo domain.RepoName) []byte { return []byte("rellatest/" + repo.String()) } | |
| 40 | + | func relUseKey(repo domain.RepoName) []byte { return []byte("reluse/" + repo.String()) } | |
| 41 | + | ||
| 42 | + | // GetLatestRelease resolves the index and release from the same snapshot. | |
| 43 | + | func (c *Releases) GetLatestRelease(ctx context.Context, repo domain.RepoName) (domain.Release, error) { | |
| 44 | + | snap := c.db.Snapshot(ctx) | |
| 45 | + | item, ok := snap.Get(ctx, relLatestKey(repo)) | |
| 46 | + | if !ok { | |
| 47 | + | return domain.Release{}, domain.ErrNotFound | |
| 48 | + | } | |
| 49 | + | id, err := strconv.ParseInt(string(item.Value), 10, 64) | |
| 50 | + | if err != nil { | |
| 51 | + | return domain.Release{}, err | |
| 52 | + | } | |
| 53 | + | item, ok = snap.Get(ctx, relKey(repo, id)) | |
| 54 | + | if !ok { | |
| 55 | + | return domain.Release{}, domain.ErrNotFound | |
| 56 | + | } | |
| 57 | + | return decodeRelease(item) | |
| 58 | + | } | |
| 40 | 59 | func encodeRelease(r domain.Release) []byte { | |
| 41 | 60 | return mustMarshal(&storagepb.Release{ | |
| 42 | 61 | Id: r.ID, | |
| @@ -285,6 +304,13 @@ func (c *Releases) ApplyReleases(ctx context.Context, w ports.ReleaseWrite) erro | |||
| 285 | 304 | b.RequireVersion(relUseKey(w.Repo), w.UsageVersion) | |
| 286 | 305 | } | |
| 287 | 306 | b.Put(relUseKey(w.Repo), mustMarshal(&storagepb.ReleaseUsage{Used: w.Used})) | |
| 307 | + | if w.SetLatest { | |
| 308 | + | if w.LatestID > 0 { | |
| 309 | + | b.Put(relLatestKey(w.Repo), []byte(strconv.FormatInt(w.LatestID, 10))) | |
| 310 | + | } else { | |
| 311 | + | b.Delete(relLatestKey(w.Repo)) | |
| 312 | + | } | |
| 313 | + | } | |
| 288 | 314 | snap := c.db.Snapshot(ctx) | |
| 289 | 315 | for _, r := range w.DeleteReleases { | |
| 290 | 316 | if r.Repo != w.Repo || r.Version == 0 { | |
| @@ -388,7 +414,7 @@ func (c *Releases) ApplyReleases(ctx context.Context, w ports.ReleaseWrite) erro | |||
| 388 | 414 | ||
| 389 | 415 | // releaseKeys collects all repository-owned release keys, including global indexes. | |
| 390 | 416 | func releaseKeys(ctx context.Context, snap *kv.Snapshot, repo domain.RepoName) [][]byte { | |
| 391 | - | keys := [][]byte{relUseKey(repo)} | |
| 417 | + | keys := [][]byte{relUseKey(repo), relLatestKey(repo)} | |
| 392 | 418 | for _, prefix := range []string{"rel/", "reltag/", "relassets/"} { | |
| 393 | 419 | for key, item := range snap.Prefix(ctx, []byte(prefix+repo.String()+"/")) { | |
| 394 | 420 | keys = append(keys, slices.Clone(key)) | |
A internal/adapters/kvstore/releases_index.go +67 -0
| @@ -0,0 +1,67 @@ | |||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | package kvstore | |
| 3 | + | ||
| 4 | + | import ( | |
| 5 | + | "context" | |
| 6 | + | "fmt" | |
| 7 | + | "strconv" | |
| 8 | + | ||
| 9 | + | kv "bnema.dev/ermite-kv" | |
| 10 | + | "bnema.dev/ermite/internal/domain" | |
| 11 | + | ) | |
| 12 | + | ||
| 13 | + | const releaseLatestIndexMarker = "meta/release-latest-index" | |
| 14 | + | ||
| 15 | + | // buildReleaseLatestIndex runs before the store is exposed to writers. A crash | |
| 16 | + | // before the marker is written safely repeats the pass on the next open. | |
| 17 | + | // Selection remains owned by domain.LatestRelease. | |
| 18 | + | func (s *Store) buildReleaseLatestIndex(ctx context.Context) error { | |
| 19 | + | if _, ok := s.db.Get(ctx, []byte(releaseLatestIndexMarker)); ok { | |
| 20 | + | return nil | |
| 21 | + | } | |
| 22 | + | var repo domain.RepoName | |
| 23 | + | var best domain.Release | |
| 24 | + | var eligible bool | |
| 25 | + | flush := func() error { | |
| 26 | + | var b kv.Batch | |
| 27 | + | if eligible { | |
| 28 | + | b.Put(relLatestKey(repo), []byte(strconv.FormatInt(best.ID, 10))) | |
| 29 | + | } else { | |
| 30 | + | b.Delete(relLatestKey(repo)) | |
| 31 | + | } | |
| 32 | + | if _, err := s.db.Apply(ctx, &b); err != nil { | |
| 33 | + | return fmt.Errorf("build latest release index: %w", err) | |
| 34 | + | } | |
| 35 | + | return nil | |
| 36 | + | } | |
| 37 | + | haveRepo := false | |
| 38 | + | for _, item := range s.db.Snapshot(ctx).Prefix(ctx, []byte("rel/")) { | |
| 39 | + | rel, err := decodeRelease(item) | |
| 40 | + | if err != nil { | |
| 41 | + | return fmt.Errorf("build latest release index: %w", err) | |
| 42 | + | } | |
| 43 | + | if haveRepo && rel.Repo != repo { | |
| 44 | + | if err := flush(); err != nil { | |
| 45 | + | return err | |
| 46 | + | } | |
| 47 | + | best, eligible = domain.Release{}, false | |
| 48 | + | } | |
| 49 | + | repo, haveRepo = rel.Repo, true | |
| 50 | + | if eligible { | |
| 51 | + | best, eligible = domain.LatestRelease([]domain.Release{best, rel}) | |
| 52 | + | } else { | |
| 53 | + | best, eligible = domain.LatestRelease([]domain.Release{rel}) | |
| 54 | + | } | |
| 55 | + | } | |
| 56 | + | if haveRepo { | |
| 57 | + | if err := flush(); err != nil { | |
| 58 | + | return err | |
| 59 | + | } | |
| 60 | + | } | |
| 61 | + | var b kv.Batch | |
| 62 | + | b.Put([]byte(releaseLatestIndexMarker), []byte{1}) | |
| 63 | + | if _, err := s.db.Apply(ctx, &b); err != nil { | |
| 64 | + | return fmt.Errorf("mark latest release index: %w", err) | |
| 65 | + | } | |
| 66 | + | return nil | |
| 67 | + | } | |
A internal/adapters/kvstore/releases_index_test.go +97 -0
| @@ -0,0 +1,97 @@ | |||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | package kvstore | |
| 3 | + | ||
| 4 | + | import ( | |
| 5 | + | "context" | |
| 6 | + | "testing" | |
| 7 | + | "time" | |
| 8 | + | ||
| 9 | + | kv "bnema.dev/ermite-kv" | |
| 10 | + | "bnema.dev/ermite/internal/domain" | |
| 11 | + | "bnema.dev/ermite/internal/ports" | |
| 12 | + | "github.com/stretchr/testify/require" | |
| 13 | + | ) | |
| 14 | + | ||
| 15 | + | func TestReleaseLatestIndexBackfill(t *testing.T) { | |
| 16 | + | ctx := t.Context() | |
| 17 | + | dir := t.TempDir() | |
| 18 | + | s, err := Open(ctx, dir) | |
| 19 | + | require.NoError(t, err) | |
| 20 | + | var repos []domain.RepoName | |
| 21 | + | for _, raw := range []string{"backfill", "empty", "newest"} { | |
| 22 | + | repo, err := domain.ParseRepoName(raw) | |
| 23 | + | require.NoError(t, err) | |
| 24 | + | repos = append(repos, repo) | |
| 25 | + | } | |
| 26 | + | for _, repo := range repos { | |
| 27 | + | require.NoError(t, s.Create(ctx, domain.Repo{Name: repo, Visibility: domain.VisibilityPublic})) | |
| 28 | + | } | |
| 29 | + | var b kv.Batch | |
| 30 | + | for i, repo := range repos { | |
| 31 | + | for j := 1; j <= 5; j++ { | |
| 32 | + | rel := domain.Release{ID: int64(i*10 + j), Repo: repo, Tag: string(rune('a' + j)), CreatedAt: time.Unix(int64(j), 0)} | |
| 33 | + | rel.Draft = j == 5 || i == 1 | |
| 34 | + | rel.Prerelease = j == 4 | |
| 35 | + | rel.MakeLatest = j == 1 && i == 0 | |
| 36 | + | b.Put(relKey(repo, rel.ID), encodeRelease(rel)) | |
| 37 | + | } | |
| 38 | + | // A stale key must also be removed for a repo with no eligible release. | |
| 39 | + | b.Put(relLatestKey(repo), []byte("999")) | |
| 40 | + | } | |
| 41 | + | b.Delete([]byte(releaseLatestIndexMarker)) | |
| 42 | + | _, err = s.db.Apply(ctx, &b) | |
| 43 | + | require.NoError(t, err) | |
| 44 | + | require.NoError(t, s.Close(context.Background())) | |
| 45 | + | s, err = Open(ctx, dir) | |
| 46 | + | require.NoError(t, err) | |
| 47 | + | defer func() { require.NoError(t, s.Close(context.Background())) }() | |
| 48 | + | for i, want := range []int64{1, 0, 23} { | |
| 49 | + | got, err := s.Releases().GetLatestRelease(ctx, repos[i]) | |
| 50 | + | if want == 0 { | |
| 51 | + | require.ErrorIs(t, err, domain.ErrNotFound) | |
| 52 | + | } else { | |
| 53 | + | require.NoError(t, err) | |
| 54 | + | require.Equal(t, want, got.ID) | |
| 55 | + | } | |
| 56 | + | } | |
| 57 | + | require.NoError(t, s.buildReleaseLatestIndex(ctx)) | |
| 58 | + | got, err := s.Releases().GetLatestRelease(ctx, repos[0]) | |
| 59 | + | require.NoError(t, err) | |
| 60 | + | require.EqualValues(t, 1, got.ID) | |
| 61 | + | require.NoError(t, s.Delete(ctx, repos[0])) | |
| 62 | + | _, err = s.Releases().GetLatestRelease(ctx, repos[0]) | |
| 63 | + | require.ErrorIs(t, err, domain.ErrNotFound) | |
| 64 | + | } | |
| 65 | + | ||
| 66 | + | func TestGetLatestReleaseAtomicUpdate(t *testing.T) { | |
| 67 | + | ctx := t.Context() | |
| 68 | + | s, err := Open(ctx, t.TempDir()) | |
| 69 | + | require.NoError(t, err) | |
| 70 | + | defer func() { require.NoError(t, s.Close(context.Background())) }() | |
| 71 | + | repo, err := domain.ParseRepoName("atomic") | |
| 72 | + | require.NoError(t, err) | |
| 73 | + | require.NoError(t, s.Create(ctx, domain.Repo{Name: repo, Visibility: domain.VisibilityPublic})) | |
| 74 | + | a := domain.Release{ID: 1, Repo: repo, Tag: "a"} | |
| 75 | + | b := domain.Release{ID: 2, Repo: repo, Tag: "b"} | |
| 76 | + | store := s.Releases() | |
| 77 | + | require.NoError(t, store.ApplyReleases(ctx, ports.ReleaseWrite{Repo: repo, PutReleases: []domain.Release{a, b}, SetLatest: true, LatestID: a.ID})) | |
| 78 | + | a, err = store.GetLatestRelease(ctx, repo) | |
| 79 | + | require.NoError(t, err) | |
| 80 | + | require.EqualValues(t, 1, a.ID) | |
| 81 | + | _, version, err := store.ReleaseUsage(ctx, repo) | |
| 82 | + | require.NoError(t, err) | |
| 83 | + | a.Prerelease = true | |
| 84 | + | require.NoError(t, store.ApplyReleases(ctx, ports.ReleaseWrite{Repo: repo, UsageVersion: version, PutReleases: []domain.Release{a}, SetLatest: true, LatestID: b.ID})) | |
| 85 | + | got, err := store.GetLatestRelease(ctx, repo) | |
| 86 | + | require.NoError(t, err) | |
| 87 | + | require.EqualValues(t, 2, got.ID) | |
| 88 | + | // An asset-only/usage write leaves the index untouched, including its version. | |
| 89 | + | before, ok := s.db.Get(ctx, relLatestKey(repo)) | |
| 90 | + | require.True(t, ok) | |
| 91 | + | _, version, err = store.ReleaseUsage(ctx, repo) | |
| 92 | + | require.NoError(t, err) | |
| 93 | + | require.NoError(t, store.ApplyReleases(ctx, ports.ReleaseWrite{Repo: repo, UsageVersion: version, Used: 10})) | |
| 94 | + | after, ok := s.db.Get(ctx, relLatestKey(repo)) | |
| 95 | + | require.True(t, ok) | |
| 96 | + | require.Equal(t, before, after) | |
| 97 | + | } | |
M internal/adapters/kvstore/store.go +6 -1
| @@ -43,7 +43,8 @@ import ( | |||
| 43 | 43 | // holding all eleven previous scopes, preserving their owner rights. Version 6 | |
| 44 | 44 | // adds releases and release:write; older builds cannot clean up their metadata | |
| 45 | 45 | // or preserve the owner rights of upgraded tokens (see upgradeReleaseTokens). | |
| 46 | - | const SchemaVersion = 6 | |
| 46 | + | // Version 7 adds the latest-release index; older writers would leave it stale. | |
| 47 | + | const SchemaVersion = 7 | |
| 47 | 48 | ||
| 48 | 49 | const schemaKey = "meta/schema" | |
| 49 | 50 | ||
| @@ -76,6 +77,10 @@ func Open(ctx context.Context, dir string) (*Store, error) { | |||
| 76 | 77 | _ = db.Close(context.WithoutCancel(ctx)) | |
| 77 | 78 | return nil, err | |
| 78 | 79 | } | |
| 80 | + | if err := s.buildReleaseLatestIndex(ctx); err != nil { | |
| 81 | + | _ = db.Close(context.WithoutCancel(ctx)) | |
| 82 | + | return nil, err | |
| 83 | + | } | |
| 79 | 84 | return s, nil | |
| 80 | 85 | } | |
| 81 | 86 | ||
M internal/adapters/web/fragments.go +1 -1
| @@ -167,7 +167,7 @@ func (l *liveStream) gone(ctx context.Context, name string) ([]fragment, error) | |||
| 167 | 167 | func (l *liveStream) repoPage(ctx context.Context, we ports.WatchEvent) ([]fragment, error) { | |
| 168 | 168 | switch ev := we.Event.(type) { | |
| 169 | 169 | case domain.ReleasePublished, domain.ReleaseUpdated, domain.ReleaseDeleted, domain.ReleaseUnpublished: | |
| 170 | - | if l.view == "releases" || l.view == "release" { | |
| 170 | + | if l.view == "releases" || l.view == "release" || l.view == "summary" { | |
| 171 | 171 | return gap, nil // notes, assets, latest or visibility changed: reload | |
| 172 | 172 | } | |
| 173 | 173 | return nil, nil | |
M internal/adapters/web/fragments_test.go +3 -3
| @@ -101,8 +101,8 @@ func TestMRPushedHeadSignal(t *testing.T) { | |||
| 101 | 101 | } | |
| 102 | 102 | } | |
| 103 | 103 | ||
| 104 | - | // Release lifecycle events invalidate the release pages without putting notes | |
| 105 | - | // or asset metadata in SSE payloads. Other views need no release fragments. | |
| 104 | + | // Release lifecycle events invalidate release pages and the summary's release | |
| 105 | + | // fact without putting notes or asset metadata in SSE payloads. | |
| 106 | 106 | func TestReleaseLiveUpdates(t *testing.T) { | |
| 107 | 107 | for _, ev := range []domain.Event{domain.ReleasePublished{}, domain.ReleaseUpdated{}, domain.ReleaseDeleted{}, domain.ReleaseUnpublished{}} { | |
| 108 | 108 | we := ports.WatchEvent{Event: ev} | |
| @@ -111,7 +111,7 @@ func TestReleaseLiveUpdates(t *testing.T) { | |||
| 111 | 111 | st := &liveStream{repo: "proj", view: view} | |
| 112 | 112 | got, err := st.render(t.Context(), we) | |
| 113 | 113 | require.NoError(t, err) | |
| 114 | - | if view == "release" || view == "releases" { | |
| 114 | + | if view == "release" || view == "releases" || view == "summary" { | |
| 115 | 115 | assert.Equal(t, gap, got) | |
| 116 | 116 | } else { | |
| 117 | 117 | assert.Empty(t, got) | |
M internal/adapters/web/issues_test.go +3 -3
| @@ -100,7 +100,7 @@ func TestIssueList(t *testing.T) { | |||
| 100 | 100 | assert.Contains(t, body, "crash <b>on</b> start") | |
| 101 | 101 | assert.Contains(t, body, `href="/proj/issues?page=tok2"`, "older page") | |
| 102 | 102 | assert.Contains(t, body, `href="/proj/issues?state=closed"`, "closed tab") | |
| 103 | - | assert.Contains(t, body, `href="/proj/issues" data-key="i"`, "the Issues tab") | |
| 103 | + | assert.Contains(t, body, `href="/proj/issues" data-key="g i"`, "the Issues tab") | |
| 104 | 104 | assert.NotContains(t, body, "New issue", "no form for an anonymous visitor") | |
| 105 | 105 | assert.Equal(t, "no-cache", rec.Header().Get("Cache-Control")) | |
| 106 | 106 | ||
| @@ -169,7 +169,7 @@ func TestIssueNotFound(t *testing.T) { | |||
| 169 | 169 | } | |
| 170 | 170 | summary := off.do("GET", "/proj", false, nil) | |
| 171 | 171 | require.Equal(t, 200, summary.Code) | |
| 172 | - | assert.NotContains(t, summary.Body.String(), `data-key="i"`) | |
| 172 | + | assert.NotContains(t, summary.Body.String(), `data-key="g i"`) | |
| 173 | 173 | ||
| 174 | 174 | // The mode is unset in the settings: off as well. | |
| 175 | 175 | unset := newIssueRig(t, "", false) | |
| @@ -257,7 +257,7 @@ func TestIssueRoutesAbsentWithoutService(t *testing.T) { | |||
| 257 | 257 | } | |
| 258 | 258 | rec := httptest.NewRecorder() | |
| 259 | 259 | srv.ServeHTTP(rec, httptest.NewRequest("GET", "/proj", nil)) | |
| 260 | - | assert.NotContains(t, rec.Body.String(), `data-key="i"`) | |
| 260 | + | assert.NotContains(t, rec.Body.String(), `data-key="g i"`) | |
| 261 | 261 | } | |
| 262 | 262 | ||
| 263 | 263 | func TestIssueNumberRedirects(t *testing.T) { | |
A internal/adapters/web/jstest/keyboard.test.mjs +76 -0
| @@ -0,0 +1,76 @@ | |||
| 1 | + | // Exercise the production key handler without a browser or dependencies. | |
| 2 | + | import assert from 'node:assert/strict'; | |
| 3 | + | import { readFileSync } from 'node:fs'; | |
| 4 | + | import { test } from 'node:test'; | |
| 5 | + | import { runInNewContext } from 'node:vm'; | |
| 6 | + | const source = readFileSync(new URL('../static/js/ermite.js', import.meta.url), 'utf8'); | |
| 7 | + | function keyboard({ owner = true, off = false, typing = false, dialog = false } = {}) { | |
| 8 | + | let handler; | |
| 9 | + | const fired = []; | |
| 10 | + | const timers = new Map(); | |
| 11 | + | let timerID = 0; | |
| 12 | + | runInNewContext(source.slice(source.indexOf(' const single ='), source.indexOf(' // 2. A [data-fire]')), { | |
| 13 | + | addEventListener: (_, fn) => { handler = fn; }, | |
| 14 | + | window: { ermiteKeys: { blocks: () => off } }, | |
| 15 | + | ermite: { fire: name => fired.push(name) }, | |
| 16 | + | $: selector => (owner && selector === '[data-inbox]') || (dialog && selector === 'dialog[open]'), | |
| 17 | + | closeMenus: () => false, | |
| 18 | + | clearTimeout: id => timers.delete(id), | |
| 19 | + | setTimeout: fn => { timers.set(++timerID, fn); return timerID; }, | |
| 20 | + | }); | |
| 21 | + | return { fired, expireTimers() { | |
| 22 | + | const callbacks = [...timers.values()]; | |
| 23 | + | timers.clear(); | |
| 24 | + | for (const callback of callbacks) callback(); | |
| 25 | + | }, press(key, shiftKey = false) { | |
| 26 | + | handler({ key, shiftKey, target: { closest: selector => typing && selector.startsWith('input'), blur() {} }, preventDefault() {} }); | |
| 27 | + | } }; | |
| 28 | + | } | |
| 29 | + | test('sections require g and have no old t alias', () => { | |
| 30 | + | const k = keyboard(); | |
| 31 | + | for (const key of ['s', 't', 'c', 'r', 'm', 'i']) k.press(key); | |
| 32 | + | assert.deepEqual(k.fired, []); | |
| 33 | + | for (const key of ['s', 'f', 'c', 'b', 'm', 'i', 'r', 'a', 'h', 'g', 't']) { k.press('g'); k.press(key); } | |
| 34 | + | assert.deepEqual(k.fired, ['s', 'f', 'c', 'b', 'm', 'i', 'r', 'a', 'gh', 'top']); | |
| 35 | + | }); | |
| 36 | + | test('uppercase actions work with either reported casing and interrupt prefixes', () => { | |
| 37 | + | for (const [key, action] of Object.entries({ G: 'bottom', M: 'merge', R: 'read', E: 'expandAll', I: 'inbox' })) { | |
| 38 | + | for (const reported of [key, key.toLowerCase()]) { | |
| 39 | + | const k = keyboard(); | |
| 40 | + | k.press('g'); k.press(reported, true); k.press(reported, true); | |
| 41 | + | assert.deepEqual(k.fired, [action, action]); | |
| 42 | + | } | |
| 43 | + | } | |
| 44 | + | }); | |
| 45 | + | test('uppercase actions respect preference, fields and dialogs', () => { | |
| 46 | + | for (const options of [{ off: true }, { typing: true }, { dialog: true }]) { | |
| 47 | + | const k = keyboard(options); k.press('g', true); | |
| 48 | + | assert.deepEqual(k.fired, []); | |
| 49 | + | } | |
| 50 | + | }); | |
| 51 | + | ||
| 52 | + | test('Shift+I opens inbox while issues need the g i sequence', () => { | |
| 53 | + | const k = keyboard(); | |
| 54 | + | k.press('I', true); k.press('i'); | |
| 55 | + | assert.deepEqual(k.fired, ['inbox']); | |
| 56 | + | k.press('g'); k.press('i'); | |
| 57 | + | assert.deepEqual(k.fired, ['inbox', 'i']); | |
| 58 | + | }); | |
| 59 | + | test('inbox shortcut respects preference, fields, dialogs, and owner target', () => { | |
| 60 | + | for (const options of [{ off: true }, { typing: true }, { dialog: true }, { owner: false }]) { | |
| 61 | + | const k = keyboard(options); k.press('I', true); | |
| 62 | + | assert.deepEqual(k.fired, [], JSON.stringify(options)); | |
| 63 | + | } | |
| 64 | + | }); | |
| 65 | + | test('shifted punctuation still opens help and the command palette', () => { | |
| 66 | + | const k = keyboard(); | |
| 67 | + | k.press('?', true); k.press(':', true); | |
| 68 | + | assert.deepEqual(k.fired, ['help', 'cmd']); | |
| 69 | + | }); | |
| 70 | + | test('g prefix expires without scheduling real timers', () => { | |
| 71 | + | const k = keyboard(); | |
| 72 | + | k.press('g'); k.expireTimers(); k.press('s'); | |
| 73 | + | assert.deepEqual(k.fired, []); | |
| 74 | + | k.press('g'); k.press('s'); | |
| 75 | + | assert.deepEqual(k.fired, ['s']); | |
| 76 | + | }); | |
D internal/adapters/web/jstest/keys.test.mjs +0 -29
| @@ -1,29 +0,0 @@ | |||
| 1 | - | // Exercise the production keydown handler without loading the rest of the page. | |
| 2 | - | import assert from 'node:assert/strict'; | |
| 3 | - | import { readFileSync } from 'node:fs'; | |
| 4 | - | import { test } from 'node:test'; | |
| 5 | - | const src = readFileSync(new URL('../static/js/ermite.js', import.meta.url), 'utf8'); | |
| 6 | - | const block = src.slice(src.indexOf(' // 1. Keys.'), src.indexOf(' // 2. A [data-fire]')); | |
| 7 | - | ||
| 8 | - | function page({ owner = true, off = false, typing = false, dialog = false } = {}) { | |
| 9 | - | let handler; | |
| 10 | - | const fired = []; | |
| 11 | - | const window = { ermiteKeys: { blocks: () => off } }; | |
| 12 | - | const $ = (s) => s === 'dialog[open]' ? dialog : s === '[data-inbox]' ? owner : null; | |
| 13 | - | new Function('window', '$', 'ermite', 'addEventListener', block)(window, $, { fire: (n) => fired.push(n) }, (_, f) => { handler = f; }); | |
| 14 | - | return (key) => { | |
| 15 | - | const e = { key, shiftKey: key === 'I', preventDefault() {}, target: { closest: (s) => s.startsWith('input') && typing } }; | |
| 16 | - | handler(e); | |
| 17 | - | return fired; | |
| 18 | - | }; | |
| 19 | - | } | |
| 20 | - | test('Shift+I opens inbox while lowercase i retains the issues tab', () => { | |
| 21 | - | const press = page(); | |
| 22 | - | assert.deepEqual(press('I'), ['inbox']); | |
| 23 | - | assert.deepEqual(press('i'), ['inbox', 'i']); | |
| 24 | - | }); | |
| 25 | - | test('inbox shortcut respects preference, fields, dialogs, and owner target', () => { | |
| 26 | - | for (const options of [{ off: true }, { typing: true }, { dialog: true }, { owner: false }]) { | |
| 27 | - | assert.deepEqual(page(options)('I'), [], JSON.stringify(options)); | |
| 28 | - | } | |
| 29 | - | }); | |
M internal/adapters/web/keys.templ +3 -2
| @@ -23,7 +23,9 @@ templ Keys() { | |||
| 23 | 23 | k-filter from:body -> ermite.focusFilter(); | |
| 24 | 24 | k-gh from:body -> ermite.go('.brand'); | |
| 25 | 25 | k-s from:body -> ermite.tab('s'); | |
| 26 | - | k-t from:body -> ermite.tab('t'); | |
| 26 | + | k-f from:body -> ermite.tab('f'); | |
| 27 | + | k-b from:body -> ermite.tab('b'); | |
| 28 | + | k-a from:body -> ermite.tab('a'); | |
| 27 | 29 | k-c from:body -> ermite.tab('c'); | |
| 28 | 30 | k-r from:body -> ermite.tab('r'); | |
| 29 | 31 | k-m from:body -> ermite.tab('m'); | |
| @@ -39,7 +41,6 @@ templ Keys() { | |||
| 39 | 41 | k-permalink from:body -> ermite.permalink(); | |
| 40 | 42 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 41 | 43 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 42 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 43 | 44 | k-keys from:body -> ermite.toggleKeys()" | |
| 44 | 45 | ></div> | |
| 45 | 46 | } | |
M internal/adapters/web/keys_templ.go +1 -1
| @@ -34,7 +34,7 @@ func Keys() templ.Component { | |||
| 34 | 34 | templ_7745c5c3_Var1 = templ.NopComponent | |
| 35 | 35 | } | |
| 36 | 36 | ctx = templ.ClearChildren(ctx) | |
| 37 | - | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div id=\"keys\" hidden hx-on=\"k-j from:body -> ermite.move(1);\n k-k from:body -> ermite.move(-1);\n k-top from:body -> ermite.edge(false);\n k-bottom from:body -> ermite.edge(true);\n k-open from:body -> ermite.open();\n k-right from:body -> ermite.rail(true);\n k-left from:body -> ermite.rail(false);\n k-esc from:body -> ermite.closeDialogs();\n k-help from:body -> ermite.dialog('panel-help');\n k-refs from:body -> ermite.dialog('panel-refs', 'refs-filter');\n k-cmd from:body -> ermite.dialog('panel-cmd', 'cmd-q'); ermiteFinder.load();\n k-filter from:body -> ermite.focusFilter();\n k-gh from:body -> ermite.go('.brand');\n k-s from:body -> ermite.tab('s');\n k-t from:body -> ermite.tab('t');\n k-c from:body -> ermite.tab('c');\n k-r from:body -> ermite.tab('r');\n k-m from:body -> ermite.tab('m');\n k-i from:body -> ermite.tab('i');\n k-inbox from:body -> ermite.go('[data-inbox]');\n k-next from:body -> ermite.nextNote();\n k-split from:body -> ermite.layout('split');\n k-unified from:body -> ermite.layout('unified');\n k-collapse from:body -> ermite.collapse();\n k-wide from:body -> window.ermiteWide.toggle();\n k-expand from:body -> ermite.expandFile();\n k-expandAll from:body -> ermite.expandAll();\n k-permalink from:body -> ermite.permalink();\n k-merge from:body -> window.ermiteMR?.openMerge();\n k-read from:body -> window.ermiteMR?.markRead();\n k-gf from:body -> window.ermiteMR?.focusFiles();\n k-keys from:body -> ermite.toggleKeys()\"></div>") | |
| 37 | + | templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 1, "<div id=\"keys\" hidden hx-on=\"k-j from:body -> ermite.move(1);\n k-k from:body -> ermite.move(-1);\n k-top from:body -> ermite.edge(false);\n k-bottom from:body -> ermite.edge(true);\n k-open from:body -> ermite.open();\n k-right from:body -> ermite.rail(true);\n k-left from:body -> ermite.rail(false);\n k-esc from:body -> ermite.closeDialogs();\n k-help from:body -> ermite.dialog('panel-help');\n k-refs from:body -> ermite.dialog('panel-refs', 'refs-filter');\n k-cmd from:body -> ermite.dialog('panel-cmd', 'cmd-q'); ermiteFinder.load();\n k-filter from:body -> ermite.focusFilter();\n k-gh from:body -> ermite.go('.brand');\n k-s from:body -> ermite.tab('s');\n k-f from:body -> ermite.tab('f');\n k-b from:body -> ermite.tab('b');\n k-a from:body -> ermite.tab('a');\n k-c from:body -> ermite.tab('c');\n k-r from:body -> ermite.tab('r');\n k-m from:body -> ermite.tab('m');\n k-i from:body -> ermite.tab('i');\n k-inbox from:body -> ermite.go('[data-inbox]');\n k-next from:body -> ermite.nextNote();\n k-split from:body -> ermite.layout('split');\n k-unified from:body -> ermite.layout('unified');\n k-collapse from:body -> ermite.collapse();\n k-wide from:body -> window.ermiteWide.toggle();\n k-expand from:body -> ermite.expandFile();\n k-expandAll from:body -> ermite.expandAll();\n k-permalink from:body -> ermite.permalink();\n k-merge from:body -> window.ermiteMR?.openMerge();\n k-read from:body -> window.ermiteMR?.markRead();\n k-keys from:body -> ermite.toggleKeys()\"></div>") | |
| 38 | 38 | if templ_7745c5c3_Err != nil { | |
| 39 | 39 | return templ_7745c5c3_Err | |
| 40 | 40 | } | |
A internal/adapters/web/keys_test.go +26 -0
| @@ -0,0 +1,26 @@ | |||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | ||
| 3 | + | package web | |
| 4 | + | ||
| 5 | + | import ( | |
| 6 | + | "testing" | |
| 7 | + | ||
| 8 | + | "github.com/stretchr/testify/assert" | |
| 9 | + | ) | |
| 10 | + | ||
| 11 | + | func TestSectionShortcutSequences(t *testing.T) { | |
| 12 | + | sections := []Section{} | |
| 13 | + | for _, key := range []string{"s", "f", "c", "b", "m", "i", "r", "a"} { | |
| 14 | + | sections = append(sections, Section{Key: key, Label: key, Href: "/" + key}) | |
| 15 | + | } | |
| 16 | + | header := renderToString(t, Header(Layout{Sections: sections})) | |
| 17 | + | bindings := renderToString(t, Keys()) | |
| 18 | + | for _, section := range sections { | |
| 19 | + | assert.Contains(t, header, `data-key="g `+section.Key+`"`) | |
| 20 | + | assert.NotContains(t, header, `aria-keyshortcuts="g `+section.Key+`"`) | |
| 21 | + | assert.Contains(t, header, `<kbd aria-hidden="true">g `+section.Key+`</kbd>`) | |
| 22 | + | assert.Contains(t, bindings, "ermite.tab('"+section.Key+"')") | |
| 23 | + | } | |
| 24 | + | assert.NotContains(t, bindings, "k-gf") | |
| 25 | + | assert.NotContains(t, bindings, "k-t from:body") | |
| 26 | + | } | |
M internal/adapters/web/mr.templ +1 -1
| @@ -430,7 +430,7 @@ templ mrFooter(p MRPage) { | |||
| 430 | 430 | templ mrRail(p MRPage) { | |
| 431 | 431 | <section aria-labelledby="r-files" class="r-files"> | |
| 432 | 432 | <h2 id="r-files"> | |
| 433 | - | Files <span class="count">{ itoa(len(p.Files)) }</span><kbd aria-hidden="true">g f</kbd> | |
| 433 | + | Files <span class="count">{ itoa(len(p.Files)) }</span><kbd aria-hidden="true">l</kbd> | |
| 434 | 434 | <button type="button" class="btn icon fold" data-js aria-expanded="true" title="fold the list of files" hx-on:click="ermiteMR.foldFiles()"> | |
| 435 | 435 | <span class="ico i-chevron" aria-hidden="true"></span><span class="sr">fold the list of files</span> | |
| 436 | 436 | </button> | |
M internal/adapters/web/mr_templ.go +1 -1 generated file: collapsed
M internal/adapters/web/mr_test.go +1 -1
| @@ -156,7 +156,7 @@ func TestMRPanelsAndMenus(t *testing.T) { | |||
| 156 | 156 | assert.Contains(t, footer, "more commits") | |
| 157 | 157 | assert.Contains(t, footer, "1/2 failed") | |
| 158 | 158 | rail := renderToString(t, mrRail(p)) | |
| 159 | - | assert.Contains(t, rail, "g f") | |
| 159 | + | assert.Contains(t, rail, `<kbd aria-hidden="true">l</kbd>`) | |
| 160 | 160 | for _, text := range []string{"Checks", "Commits", "Settings", "mr-checks"} { | |
| 161 | 161 | assert.NotContains(t, rail, text) | |
| 162 | 162 | } | |
M internal/adapters/web/overlays.templ +2 -2
| @@ -26,7 +26,7 @@ templ OverlayHelp(mr bool) { | |||
| 26 | 26 | <div><dt><kbd>g g</kbd> <kbd>G</kbd></dt><dd>first / last row</dd></div> | |
| 27 | 27 | <div><dt><kbd>/</kbd></dt><dd>filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)</dd></div> | |
| 28 | 28 | <div><dt><kbd>b</kbd></dt><dd>{ "switch" } branch or tag</dd></div> | |
| 29 | - | <div><dt><kbd>s</kbd> <kbd>t</kbd> <kbd>c</kbd> <kbd>r</kbd> <kbd>m</kbd> <kbd>i</kbd></dt><dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd></div> | |
| 29 | + | <div><dt><kbd>g s</kbd> <kbd>g f</kbd> <kbd>g c</kbd> <kbd>g b</kbd> <kbd>g m</kbd> <kbd>g i</kbd> <kbd>g r</kbd> <kbd>g a</kbd></dt><dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd></div> | |
| 30 | 30 | if SignedIn(ctx) { | |
| 31 | 31 | <div><dt><kbd>I</kbd></dt><dd>inbox</dd></div> | |
| 32 | 32 | } | |
| @@ -37,7 +37,7 @@ templ OverlayHelp(mr bool) { | |||
| 37 | 37 | <div><dt><kbd>e</kbd> <kbd>E</kbd></dt><dd>diff: the whole file in view / every file, with the lines the hunks hide</dd></div> | |
| 38 | 38 | <div><dt><kbd>n</kbd></dt><dd>merge request: next note in the diff</dd></div> | |
| 39 | 39 | if mr { | |
| 40 | - | <div><dt><kbd>g f</kbd></dt><dd>merge request: the list of files (j k move, enter opens, esc back)</dd></div> | |
| 40 | + | <div><dt><kbd>l</kbd></dt><dd>merge request: the list of files (j k move, enter opens, esc back)</dd></div> | |
| 41 | 41 | <div><dt><kbd>M</kbd></dt><dd>merge request: the merge menu; there 1–3 pick the mode, D deletes the branch, enter merges</dd></div> | |
| 42 | 42 | <div><dt><kbd>R</kbd></dt><dd>merge request: mark the head read</dd></div> | |
| 43 | 43 | } | |
M internal/adapters/web/overlays_templ.go +2 -2 generated file: collapsed
M internal/adapters/web/pages.templ +8 -0
| @@ -115,6 +115,14 @@ templ summaryRail(p SummaryPage) { | |||
| 115 | 115 | if p.Latest != nil { | |
| 116 | 116 | @latestTagFact(*p.Latest) | |
| 117 | 117 | } | |
| 118 | + | if r := p.Release; r != nil { | |
| 119 | + | <dt>release</dt> | |
| 120 | + | <dd> | |
| 121 | + | <a href={ templ.SafeURL(r.Href) }>{ r.Tag }</a>{ " · " } | |
| 122 | + | <time datetime={ r.WhenAt } title={ r.WhenAt }>{ r.When }</time>{ " · " } | |
| 123 | + | <a href={ templ.SafeURL(r.AllHref) }>all releases</a> | |
| 124 | + | </dd> | |
| 125 | + | } | |
| 118 | 126 | </dl> | |
| 119 | 127 | </section> | |
| 120 | 128 | if len(p.Recent) > 0 { | |
M internal/adapters/web/pages_templ.go +371 -261 generated file: collapsed
M internal/adapters/web/releases.templ +16 -9
| @@ -40,15 +40,22 @@ templ Releases(l Layout, p ReleaseListPage) { | |||
| 40 | 40 | ||
| 41 | 41 | templ ReleaseDetail(l Layout, p ReleaseDetailPage) { | |
| 42 | 42 | @Shell(l, nil) { | |
| 43 | - | <h1>{ p.Title }</h1> | |
| 44 | - | <p> | |
| 45 | - | <span class="pill">{ p.Tag }</span> @ReleaseBadges(p.ReleaseRow) | |
| 46 | - | if p.SHA != "" { | |
| 47 | - | <a href={ templ.SafeURL(p.CommitHref) }><code>{ p.SHA }</code></a> | |
| 48 | - | } | |
| 49 | - | <span class="dim">{ p.Published }</span> | |
| 50 | - | </p> | |
| 51 | - | <div class="md">@templ.Raw(string(p.Notes))</div> | |
| 43 | + | <section class="mr-card"> | |
| 44 | + | <header class="show mr-h"> | |
| 45 | + | <h1>{ p.Title }</h1> | |
| 46 | + | <dl class="kv show-kv"> | |
| 47 | + | <dt>tag</dt> | |
| 48 | + | <dd><span class="pill">{ p.Tag }</span> @ReleaseBadges(p.ReleaseRow)</dd> | |
| 49 | + | if p.SHA != "" { | |
| 50 | + | <dt>commit</dt> | |
| 51 | + | <dd><a href={ templ.SafeURL(p.CommitHref) }><code>{ p.SHA }</code></a></dd> | |
| 52 | + | } | |
| 53 | + | <dt>published</dt> | |
| 54 | + | <dd>{ p.Published }</dd> | |
| 55 | + | </dl> | |
| 56 | + | </header> | |
| 57 | + | <article class="mr-desc prose">@templ.Raw(string(p.Notes))</article> | |
| 58 | + | </section> | |
| 52 | 59 | <h2>Assets</h2> | |
| 53 | 60 | if len(p.Assets) == 0 { | |
| 54 | 61 | <p class="dim">no assets.</p> | |
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 { | |||
| 38 | 38 | t.Helper() | |
| 39 | 39 | repo := repoName(t, "proj") | |
| 40 | 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 | 42 | f.draft = domain.Release{ID: 2, Repo: repo, Tag: "unpublished", Title: "Secret draft", Draft: true, MakeLatest: true} | |
| 43 | 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 | 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 | 120 | assert.Contains(t, body, "2026-10-12") | |
| 121 | 121 | assert.Contains(t, body, `<td>1</td>`) | |
| 122 | 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 | 125 | assert.Contains(t, body, "view=releases") | |
| 125 | 126 | } | |
| 126 | 127 | owner := f.request("GET", "/proj/releases?page=cursor%2B%2F%3D", "tok", "") | |
| @@ -137,6 +138,15 @@ func TestReleasePages(t *testing.T) { | |||
| 137 | 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 | 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 | 150 | assert.NotContains(t, body, "<script>alert") | |
| 141 | 151 | assert.NotContains(t, body, `href="javascript:`) | |
| 142 | 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 | |||
| 144 | 144 | } | |
| 145 | 145 | l.Sections = []Section{ | |
| 146 | 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 | 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 | 151 | if s.cfg.MergeRequests != nil { | |
| 152 | 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 | 157 | l.Sections = append(l.Sections, mk("i", "Issues", issuesBase(d.name()), "issues")) | |
| 158 | 158 | } | |
| 159 | 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 | 162 | l.Sections = s.withActions(l.Sections, d.name(), section) | |
| 163 | 163 | l.Crumbs = []Crumb{{Label: d.name(), Href: repoHref(d.name()), Current: section == "summary"}} | |
| 164 | 164 | ||
| 165 | 165 | l.Cmd = append(l.Cmd, CmdItem{Kind: "go", Label: "home", Href: "/"}) | |
| 166 | 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 | 169 | if l.Ref != "" { | |
| 170 | 170 | for _, b := range d.branches { | |
M internal/adapters/web/shell.templ +2 -3
| @@ -172,8 +172,7 @@ templ Header(l Layout) { | |||
| 172 | 172 | <li> | |
| 173 | 173 | <a | |
| 174 | 174 | href={ templ.SafeURL(s.Href) } | |
| 175 | - | data-key={ s.Key } | |
| 176 | - | aria-keyshortcuts={ s.Key } | |
| 175 | + | data-key={ "g " + s.Key } | |
| 177 | 176 | if s.Current { | |
| 178 | 177 | aria-current="page" | |
| 179 | 178 | } | |
| @@ -182,7 +181,7 @@ templ Header(l Layout) { | |||
| 182 | 181 | if s.Count != "" { | |
| 183 | 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 | 185 | </a> | |
| 187 | 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 @@ | |||
| 112 | 112 | $(sel)?.click(); | |
| 113 | 113 | }, | |
| 114 | 114 | tab(key) { | |
| 115 | - | $(`.tabs [data-key=${key}]`)?.click(); | |
| 115 | + | $(`.tabs [data-key="g ${key}"]`)?.click(); | |
| 116 | 116 | }, | |
| 117 | 117 | ||
| 118 | 118 | // The preference "single-key shortcuts" (ermiteKeys): the checkbox of the | |
| @@ -612,11 +612,11 @@ | |||
| 612 | 612 | ||
| 613 | 613 | // 1. Keys. | |
| 614 | 614 | const single = { j: 'j', k: 'k', G: 'bottom', '/': 'filter', '?': 'help', b: 'refs', f: 'cmd', ':': 'cmd', y: 'permalink', | |
| 615 | - | 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', | |
| 615 | + | I: 'inbox', n: 'next', h: 'left', l: 'right', d: 'split', u: 'unified', x: 'collapse', w: 'wide', e: 'expand', E: 'expandAll', Enter: 'open', Escape: 'esc', | |
| 616 | 616 | // the merge request page (mr.js): the merge menu, mark read | |
| 617 | 617 | M: 'merge', R: 'read' }; | |
| 618 | - | // "g t" is the key "t": the tabs have one event each. | |
| 619 | - | const goto = { h: 'gh', t: 't', c: 'c', r: 'r', m: 'm', i: 'i', g: 'top', f: 'gf' }; | |
| 618 | + | // Section keys are the second letter of the g sequence. | |
| 619 | + | const goto = { h: 'gh', s: 's', f: 'f', c: 'c', b: 'b', m: 'm', i: 'i', r: 'r', a: 'a', g: 'top' }; | |
| 620 | 620 | let pending = 0; | |
| 621 | 621 | let pendingTimer = 0; | |
| 622 | 622 | addEventListener('keydown', (e) => { | |
| @@ -642,6 +642,15 @@ | |||
| 642 | 642 | // behind it does not react (scripted clicks would go through inert). | |
| 643 | 643 | if ($('dialog[open]') && !['?', 'b', ':', 'f', 'Enter'].includes(e.key)) { pending = 0; return; } | |
| 644 | 644 | if (e.key === 'Enter' && e.target.closest('a, button, summary')) return; | |
| 645 | + | // Some layouts report lowercase e.key even with Shift. Uppercase actions | |
| 646 | + | // also interrupt a pending g rather than being swallowed by it. | |
| 647 | + | const actionKey = e.shiftKey ? e.key.toUpperCase() : e.key; | |
| 648 | + | if (['G', 'M', 'R', 'E', 'I'].includes(actionKey)) { | |
| 649 | + | pending = 0; | |
| 650 | + | if (actionKey === 'I' && !$('[data-inbox]')) return; | |
| 651 | + | e.preventDefault(); | |
| 652 | + | return ermite.fire(single[actionKey]); | |
| 653 | + | } | |
| 645 | 654 | if (pending) { | |
| 646 | 655 | const name = goto[e.key]; | |
| 647 | 656 | pending = 0; | |
M internal/adapters/web/static/js/mr.js +2 -2
| @@ -8,7 +8,7 @@ | |||
| 8 | 8 | // on its button, 1-9 pick a mode, D toggles the branch deletion, Enter | |
| 9 | 9 | // submits (the browser does it: the focus is on the button), Esc closes. | |
| 10 | 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 / k | |
| 11 | + | // 3. l puts the focus in the list of files (opening it when folded); j / k | |
| 12 | 12 | // move in it, Enter opens the file, Esc goes back to the diff. The button | |
| 13 | 13 | // of its header folds the list, and the choice is remembered. | |
| 14 | 14 | // | |
| @@ -186,7 +186,7 @@ window.ermiteMR = (() => { | |||
| 186 | 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 | 190 | const links = () => $$('.r-files .files > li:not([hidden]) > a'); | |
| 191 | 191 | function focusFiles() { | |
| 192 | 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, | |||
| 68 | 68 | ||
| 69 | 69 | p.Facts = s.facts(d, now) | |
| 70 | 70 | p.Latest = s.latest(ctx, d) | |
| 71 | + | p.Release = s.summaryRelease(ctx, d.repo.Name, now) | |
| 71 | 72 | p.Actions = s.recentActions(ctx, d.repo.Name, now) | |
| 72 | 73 | ||
| 73 | 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 | 274 | 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 | + | } | |
M internal/adapters/web/view.go +7 -1
| @@ -211,12 +211,18 @@ type SummaryPage struct { | |||
| 211 | 211 | Outline []Heading | |
| 212 | 212 | AllHref string | |
| 213 | 213 | // Latest is the newest tag, nil when the repository has none. | |
| 214 | - | Latest *LatestTag | |
| 214 | + | Latest *LatestTag | |
| 215 | + | Release *LatestRelease | |
| 215 | 216 | // Actions is the recent runs of the rail, nil when the viewer cannot read | |
| 216 | 217 | // CI (or the server runs none). | |
| 217 | 218 | 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 | 226 | // RecentActions is the "Recent actions" block of the summary rail: the newest | |
| 221 | 227 | // runs. With no run the block is in the page but hidden, so that the first | |
| 222 | 228 | // run reaches it live. | |
M internal/app/testdata/web/blob-binary.html +33 -30
| @@ -73,31 +73,31 @@ | |||
| 73 | 73 | <nav class="tabs" aria-label="Sections"> | |
| 74 | 74 | <ul> | |
| 75 | 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 | 77 | </a> | |
| 78 | 78 | </li> | |
| 79 | 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 | 81 | </a> | |
| 82 | 82 | </li> | |
| 83 | 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 | 85 | </a> | |
| 86 | 86 | </li> | |
| 87 | 87 | <li> | |
| 88 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 88 | + | <a href="/files/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 89 | 89 | </a> | |
| 90 | 90 | </li> | |
| 91 | 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 | 93 | </a> | |
| 94 | 94 | </li> | |
| 95 | 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 | 97 | </a> | |
| 98 | 98 | </li> | |
| 99 | 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 | 101 | </a> | |
| 102 | 102 | </li> | |
| 103 | 103 | </ul> | |
| @@ -226,14 +226,16 @@ | |||
| 226 | 226 | </div> | |
| 227 | 227 | <div> | |
| 228 | 228 | <dt> | |
| 229 | - | <kbd>s</kbd> | |
| 230 | - | <kbd>t</kbd> | |
| 231 | - | <kbd>c</kbd> | |
| 232 | - | <kbd>r</kbd> | |
| 233 | - | <kbd>m</kbd> | |
| 234 | - | <kbd>i</kbd> | |
| 229 | + | <kbd>g s</kbd> | |
| 230 | + | <kbd>g f</kbd> | |
| 231 | + | <kbd>g c</kbd> | |
| 232 | + | <kbd>g b</kbd> | |
| 233 | + | <kbd>g m</kbd> | |
| 234 | + | <kbd>g i</kbd> | |
| 235 | + | <kbd>g r</kbd> | |
| 236 | + | <kbd>g a</kbd> | |
| 235 | 237 | </dt> | |
| 236 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 238 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 237 | 239 | </div> | |
| 238 | 240 | <div> | |
| 239 | 241 | <dt> | |
| @@ -332,45 +334,45 @@ | |||
| 332 | 334 | <span class="k">go</span> | |
| 333 | 335 | </a> | |
| 334 | 336 | </li> | |
| 335 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 337 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 336 | 338 | <a href="/files" tabindex="-1"> | |
| 337 | - | <span class="lbl">summary</span> | |
| 339 | + | <span class="lbl">summary (g s)</span> | |
| 338 | 340 | <span class="k">section</span> | |
| 339 | 341 | </a> | |
| 340 | 342 | </li> | |
| 341 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 343 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 342 | 344 | <a href="/files/tree/main" tabindex="-1"> | |
| 343 | - | <span class="lbl">files</span> | |
| 345 | + | <span class="lbl">files (g f)</span> | |
| 344 | 346 | <span class="k">section</span> | |
| 345 | 347 | </a> | |
| 346 | 348 | </li> | |
| 347 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 349 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 348 | 350 | <a href="/files/commits/main" tabindex="-1"> | |
| 349 | - | <span class="lbl">commits</span> | |
| 351 | + | <span class="lbl">commits (g c)</span> | |
| 350 | 352 | <span class="k">section</span> | |
| 351 | 353 | </a> | |
| 352 | 354 | </li> | |
| 353 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 355 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 354 | 356 | <a href="/files/refs" tabindex="-1"> | |
| 355 | - | <span class="lbl">branches & tags</span> | |
| 357 | + | <span class="lbl">branches & tags (g b)</span> | |
| 356 | 358 | <span class="k">section</span> | |
| 357 | 359 | </a> | |
| 358 | 360 | </li> | |
| 359 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 361 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 360 | 362 | <a href="/files/merge-requests" tabindex="-1"> | |
| 361 | - | <span class="lbl">merge requests</span> | |
| 363 | + | <span class="lbl">merge requests (g m)</span> | |
| 362 | 364 | <span class="k">section</span> | |
| 363 | 365 | </a> | |
| 364 | 366 | </li> | |
| 365 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 367 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 366 | 368 | <a href="/files/releases" tabindex="-1"> | |
| 367 | - | <span class="lbl">releases</span> | |
| 369 | + | <span class="lbl">releases (g r)</span> | |
| 368 | 370 | <span class="k">section</span> | |
| 369 | 371 | </a> | |
| 370 | 372 | </li> | |
| 371 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 373 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 372 | 374 | <a href="/files/actions" tabindex="-1"> | |
| 373 | - | <span class="lbl">actions</span> | |
| 375 | + | <span class="lbl">actions (g a)</span> | |
| 374 | 376 | <span class="k">section</span> | |
| 375 | 377 | </a> | |
| 376 | 378 | </li> | |
| @@ -423,7 +425,9 @@ | |||
| 423 | 425 | k-filter from:body -> ermite.focusFilter(); | |
| 424 | 426 | k-gh from:body -> ermite.go('.brand'); | |
| 425 | 427 | k-s from:body -> ermite.tab('s'); | |
| 426 | - | k-t from:body -> ermite.tab('t'); | |
| 428 | + | k-f from:body -> ermite.tab('f'); | |
| 429 | + | k-b from:body -> ermite.tab('b'); | |
| 430 | + | k-a from:body -> ermite.tab('a'); | |
| 427 | 431 | k-c from:body -> ermite.tab('c'); | |
| 428 | 432 | k-r from:body -> ermite.tab('r'); | |
| 429 | 433 | k-m from:body -> ermite.tab('m'); | |
| @@ -439,7 +443,6 @@ | |||
| 439 | 443 | k-permalink from:body -> ermite.permalink(); | |
| 440 | 444 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 441 | 445 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 442 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 443 | 446 | k-keys from:body -> ermite.toggleKeys()"> | |
| 444 | 447 | </div> | |
| 445 | 448 | </body> | |
M internal/app/testdata/web/blob-code.html +33 -30
| @@ -73,31 +73,31 @@ | |||
| 73 | 73 | <nav class="tabs" aria-label="Sections"> | |
| 74 | 74 | <ul> | |
| 75 | 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 | 77 | </a> | |
| 78 | 78 | </li> | |
| 79 | 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 | 81 | </a> | |
| 82 | 82 | </li> | |
| 83 | 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 | 85 | </a> | |
| 86 | 86 | </li> | |
| 87 | 87 | <li> | |
| 88 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 88 | + | <a href="/files/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 89 | 89 | </a> | |
| 90 | 90 | </li> | |
| 91 | 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 | 93 | </a> | |
| 94 | 94 | </li> | |
| 95 | 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 | 97 | </a> | |
| 98 | 98 | </li> | |
| 99 | 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 | 101 | </a> | |
| 102 | 102 | </li> | |
| 103 | 103 | </ul> | |
| @@ -296,14 +296,16 @@ | |||
| 296 | 296 | </div> | |
| 297 | 297 | <div> | |
| 298 | 298 | <dt> | |
| 299 | - | <kbd>s</kbd> | |
| 300 | - | <kbd>t</kbd> | |
| 301 | - | <kbd>c</kbd> | |
| 302 | - | <kbd>r</kbd> | |
| 303 | - | <kbd>m</kbd> | |
| 304 | - | <kbd>i</kbd> | |
| 299 | + | <kbd>g s</kbd> | |
| 300 | + | <kbd>g f</kbd> | |
| 301 | + | <kbd>g c</kbd> | |
| 302 | + | <kbd>g b</kbd> | |
| 303 | + | <kbd>g m</kbd> | |
| 304 | + | <kbd>g i</kbd> | |
| 305 | + | <kbd>g r</kbd> | |
| 306 | + | <kbd>g a</kbd> | |
| 305 | 307 | </dt> | |
| 306 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 308 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 307 | 309 | </div> | |
| 308 | 310 | <div> | |
| 309 | 311 | <dt> | |
| @@ -402,45 +404,45 @@ | |||
| 402 | 404 | <span class="k">go</span> | |
| 403 | 405 | </a> | |
| 404 | 406 | </li> | |
| 405 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 407 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 406 | 408 | <a href="/files" tabindex="-1"> | |
| 407 | - | <span class="lbl">summary</span> | |
| 409 | + | <span class="lbl">summary (g s)</span> | |
| 408 | 410 | <span class="k">section</span> | |
| 409 | 411 | </a> | |
| 410 | 412 | </li> | |
| 411 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 413 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 412 | 414 | <a href="/files/tree/main" tabindex="-1"> | |
| 413 | - | <span class="lbl">files</span> | |
| 415 | + | <span class="lbl">files (g f)</span> | |
| 414 | 416 | <span class="k">section</span> | |
| 415 | 417 | </a> | |
| 416 | 418 | </li> | |
| 417 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 419 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 418 | 420 | <a href="/files/commits/main" tabindex="-1"> | |
| 419 | - | <span class="lbl">commits</span> | |
| 421 | + | <span class="lbl">commits (g c)</span> | |
| 420 | 422 | <span class="k">section</span> | |
| 421 | 423 | </a> | |
| 422 | 424 | </li> | |
| 423 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 425 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 424 | 426 | <a href="/files/refs" tabindex="-1"> | |
| 425 | - | <span class="lbl">branches & tags</span> | |
| 427 | + | <span class="lbl">branches & tags (g b)</span> | |
| 426 | 428 | <span class="k">section</span> | |
| 427 | 429 | </a> | |
| 428 | 430 | </li> | |
| 429 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 431 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 430 | 432 | <a href="/files/merge-requests" tabindex="-1"> | |
| 431 | - | <span class="lbl">merge requests</span> | |
| 433 | + | <span class="lbl">merge requests (g m)</span> | |
| 432 | 434 | <span class="k">section</span> | |
| 433 | 435 | </a> | |
| 434 | 436 | </li> | |
| 435 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 437 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 436 | 438 | <a href="/files/releases" tabindex="-1"> | |
| 437 | - | <span class="lbl">releases</span> | |
| 439 | + | <span class="lbl">releases (g r)</span> | |
| 438 | 440 | <span class="k">section</span> | |
| 439 | 441 | </a> | |
| 440 | 442 | </li> | |
| 441 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 443 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 442 | 444 | <a href="/files/actions" tabindex="-1"> | |
| 443 | - | <span class="lbl">actions</span> | |
| 445 | + | <span class="lbl">actions (g a)</span> | |
| 444 | 446 | <span class="k">section</span> | |
| 445 | 447 | </a> | |
| 446 | 448 | </li> | |
| @@ -493,7 +495,9 @@ | |||
| 493 | 495 | k-filter from:body -> ermite.focusFilter(); | |
| 494 | 496 | k-gh from:body -> ermite.go('.brand'); | |
| 495 | 497 | k-s from:body -> ermite.tab('s'); | |
| 496 | - | k-t from:body -> ermite.tab('t'); | |
| 498 | + | k-f from:body -> ermite.tab('f'); | |
| 499 | + | k-b from:body -> ermite.tab('b'); | |
| 500 | + | k-a from:body -> ermite.tab('a'); | |
| 497 | 501 | k-c from:body -> ermite.tab('c'); | |
| 498 | 502 | k-r from:body -> ermite.tab('r'); | |
| 499 | 503 | k-m from:body -> ermite.tab('m'); | |
| @@ -509,7 +513,6 @@ | |||
| 509 | 513 | k-permalink from:body -> ermite.permalink(); | |
| 510 | 514 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 511 | 515 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 512 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 513 | 516 | k-keys from:body -> ermite.toggleKeys()"> | |
| 514 | 517 | </div> | |
| 515 | 518 | </body> | |
M internal/app/testdata/web/blob-empty.html +33 -30
| @@ -73,31 +73,31 @@ | |||
| 73 | 73 | <nav class="tabs" aria-label="Sections"> | |
| 74 | 74 | <ul> | |
| 75 | 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 | 77 | </a> | |
| 78 | 78 | </li> | |
| 79 | 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 | 81 | </a> | |
| 82 | 82 | </li> | |
| 83 | 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 | 85 | </a> | |
| 86 | 86 | </li> | |
| 87 | 87 | <li> | |
| 88 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 88 | + | <a href="/files/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 89 | 89 | </a> | |
| 90 | 90 | </li> | |
| 91 | 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 | 93 | </a> | |
| 94 | 94 | </li> | |
| 95 | 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 | 97 | </a> | |
| 98 | 98 | </li> | |
| 99 | 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 | 101 | </a> | |
| 102 | 102 | </li> | |
| 103 | 103 | </ul> | |
| @@ -226,14 +226,16 @@ | |||
| 226 | 226 | </div> | |
| 227 | 227 | <div> | |
| 228 | 228 | <dt> | |
| 229 | - | <kbd>s</kbd> | |
| 230 | - | <kbd>t</kbd> | |
| 231 | - | <kbd>c</kbd> | |
| 232 | - | <kbd>r</kbd> | |
| 233 | - | <kbd>m</kbd> | |
| 234 | - | <kbd>i</kbd> | |
| 229 | + | <kbd>g s</kbd> | |
| 230 | + | <kbd>g f</kbd> | |
| 231 | + | <kbd>g c</kbd> | |
| 232 | + | <kbd>g b</kbd> | |
| 233 | + | <kbd>g m</kbd> | |
| 234 | + | <kbd>g i</kbd> | |
| 235 | + | <kbd>g r</kbd> | |
| 236 | + | <kbd>g a</kbd> | |
| 235 | 237 | </dt> | |
| 236 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 238 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 237 | 239 | </div> | |
| 238 | 240 | <div> | |
| 239 | 241 | <dt> | |
| @@ -332,45 +334,45 @@ | |||
| 332 | 334 | <span class="k">go</span> | |
| 333 | 335 | </a> | |
| 334 | 336 | </li> | |
| 335 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 337 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 336 | 338 | <a href="/files" tabindex="-1"> | |
| 337 | - | <span class="lbl">summary</span> | |
| 339 | + | <span class="lbl">summary (g s)</span> | |
| 338 | 340 | <span class="k">section</span> | |
| 339 | 341 | </a> | |
| 340 | 342 | </li> | |
| 341 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 343 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 342 | 344 | <a href="/files/tree/main" tabindex="-1"> | |
| 343 | - | <span class="lbl">files</span> | |
| 345 | + | <span class="lbl">files (g f)</span> | |
| 344 | 346 | <span class="k">section</span> | |
| 345 | 347 | </a> | |
| 346 | 348 | </li> | |
| 347 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 349 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 348 | 350 | <a href="/files/commits/main" tabindex="-1"> | |
| 349 | - | <span class="lbl">commits</span> | |
| 351 | + | <span class="lbl">commits (g c)</span> | |
| 350 | 352 | <span class="k">section</span> | |
| 351 | 353 | </a> | |
| 352 | 354 | </li> | |
| 353 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 355 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 354 | 356 | <a href="/files/refs" tabindex="-1"> | |
| 355 | - | <span class="lbl">branches & tags</span> | |
| 357 | + | <span class="lbl">branches & tags (g b)</span> | |
| 356 | 358 | <span class="k">section</span> | |
| 357 | 359 | </a> | |
| 358 | 360 | </li> | |
| 359 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 361 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 360 | 362 | <a href="/files/merge-requests" tabindex="-1"> | |
| 361 | - | <span class="lbl">merge requests</span> | |
| 363 | + | <span class="lbl">merge requests (g m)</span> | |
| 362 | 364 | <span class="k">section</span> | |
| 363 | 365 | </a> | |
| 364 | 366 | </li> | |
| 365 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 367 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 366 | 368 | <a href="/files/releases" tabindex="-1"> | |
| 367 | - | <span class="lbl">releases</span> | |
| 369 | + | <span class="lbl">releases (g r)</span> | |
| 368 | 370 | <span class="k">section</span> | |
| 369 | 371 | </a> | |
| 370 | 372 | </li> | |
| 371 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 373 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 372 | 374 | <a href="/files/actions" tabindex="-1"> | |
| 373 | - | <span class="lbl">actions</span> | |
| 375 | + | <span class="lbl">actions (g a)</span> | |
| 374 | 376 | <span class="k">section</span> | |
| 375 | 377 | </a> | |
| 376 | 378 | </li> | |
| @@ -423,7 +425,9 @@ | |||
| 423 | 425 | k-filter from:body -> ermite.focusFilter(); | |
| 424 | 426 | k-gh from:body -> ermite.go('.brand'); | |
| 425 | 427 | k-s from:body -> ermite.tab('s'); | |
| 426 | - | k-t from:body -> ermite.tab('t'); | |
| 428 | + | k-f from:body -> ermite.tab('f'); | |
| 429 | + | k-b from:body -> ermite.tab('b'); | |
| 430 | + | k-a from:body -> ermite.tab('a'); | |
| 427 | 431 | k-c from:body -> ermite.tab('c'); | |
| 428 | 432 | k-r from:body -> ermite.tab('r'); | |
| 429 | 433 | k-m from:body -> ermite.tab('m'); | |
| @@ -439,7 +443,6 @@ | |||
| 439 | 443 | k-permalink from:body -> ermite.permalink(); | |
| 440 | 444 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 441 | 445 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 442 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 443 | 446 | k-keys from:body -> ermite.toggleKeys()"> | |
| 444 | 447 | </div> | |
| 445 | 448 | </body> | |
M internal/app/testdata/web/blob-image.html +33 -30
| @@ -73,31 +73,31 @@ | |||
| 73 | 73 | <nav class="tabs" aria-label="Sections"> | |
| 74 | 74 | <ul> | |
| 75 | 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 | 77 | </a> | |
| 78 | 78 | </li> | |
| 79 | 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 | 81 | </a> | |
| 82 | 82 | </li> | |
| 83 | 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 | 85 | </a> | |
| 86 | 86 | </li> | |
| 87 | 87 | <li> | |
| 88 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 88 | + | <a href="/files/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 89 | 89 | </a> | |
| 90 | 90 | </li> | |
| 91 | 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 | 93 | </a> | |
| 94 | 94 | </li> | |
| 95 | 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 | 97 | </a> | |
| 98 | 98 | </li> | |
| 99 | 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 | 101 | </a> | |
| 102 | 102 | </li> | |
| 103 | 103 | </ul> | |
| @@ -228,14 +228,16 @@ | |||
| 228 | 228 | </div> | |
| 229 | 229 | <div> | |
| 230 | 230 | <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> | |
| 237 | 239 | </dt> | |
| 238 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 240 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 239 | 241 | </div> | |
| 240 | 242 | <div> | |
| 241 | 243 | <dt> | |
| @@ -334,45 +336,45 @@ | |||
| 334 | 336 | <span class="k">go</span> | |
| 335 | 337 | </a> | |
| 336 | 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 | 340 | <a href="/files" tabindex="-1"> | |
| 339 | - | <span class="lbl">summary</span> | |
| 341 | + | <span class="lbl">summary (g s)</span> | |
| 340 | 342 | <span class="k">section</span> | |
| 341 | 343 | </a> | |
| 342 | 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 | 346 | <a href="/files/tree/main" tabindex="-1"> | |
| 345 | - | <span class="lbl">files</span> | |
| 347 | + | <span class="lbl">files (g f)</span> | |
| 346 | 348 | <span class="k">section</span> | |
| 347 | 349 | </a> | |
| 348 | 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 | 352 | <a href="/files/commits/main" tabindex="-1"> | |
| 351 | - | <span class="lbl">commits</span> | |
| 353 | + | <span class="lbl">commits (g c)</span> | |
| 352 | 354 | <span class="k">section</span> | |
| 353 | 355 | </a> | |
| 354 | 356 | </li> | |
| 355 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 357 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 356 | 358 | <a href="/files/refs" tabindex="-1"> | |
| 357 | - | <span class="lbl">branches & tags</span> | |
| 359 | + | <span class="lbl">branches & tags (g b)</span> | |
| 358 | 360 | <span class="k">section</span> | |
| 359 | 361 | </a> | |
| 360 | 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 | 364 | <a href="/files/merge-requests" tabindex="-1"> | |
| 363 | - | <span class="lbl">merge requests</span> | |
| 365 | + | <span class="lbl">merge requests (g m)</span> | |
| 364 | 366 | <span class="k">section</span> | |
| 365 | 367 | </a> | |
| 366 | 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 | 370 | <a href="/files/releases" tabindex="-1"> | |
| 369 | - | <span class="lbl">releases</span> | |
| 371 | + | <span class="lbl">releases (g r)</span> | |
| 370 | 372 | <span class="k">section</span> | |
| 371 | 373 | </a> | |
| 372 | 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 | 376 | <a href="/files/actions" tabindex="-1"> | |
| 375 | - | <span class="lbl">actions</span> | |
| 377 | + | <span class="lbl">actions (g a)</span> | |
| 376 | 378 | <span class="k">section</span> | |
| 377 | 379 | </a> | |
| 378 | 380 | </li> | |
| @@ -425,7 +427,9 @@ | |||
| 425 | 427 | k-filter from:body -> ermite.focusFilter(); | |
| 426 | 428 | k-gh from:body -> ermite.go('.brand'); | |
| 427 | 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 | 433 | k-c from:body -> ermite.tab('c'); | |
| 430 | 434 | k-r from:body -> ermite.tab('r'); | |
| 431 | 435 | k-m from:body -> ermite.tab('m'); | |
| @@ -441,7 +445,6 @@ | |||
| 441 | 445 | k-permalink from:body -> ermite.permalink(); | |
| 442 | 446 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 443 | 447 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 444 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 445 | 448 | k-keys from:body -> ermite.toggleKeys()"> | |
| 446 | 449 | </div> | |
| 447 | 450 | </body> | |
M internal/app/testdata/web/blob-markdown.html +33 -30
| @@ -76,31 +76,31 @@ | |||
| 76 | 76 | <nav class="tabs" aria-label="Sections"> | |
| 77 | 77 | <ul> | |
| 78 | 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 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 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 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 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 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 90 | <li> | |
| 91 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 91 | + | <a href="/files/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 92 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 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 | 96 | </a> | |
| 97 | 97 | </li> | |
| 98 | 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 | 100 | </a> | |
| 101 | 101 | </li> | |
| 102 | 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 | 104 | </a> | |
| 105 | 105 | </li> | |
| 106 | 106 | </ul> | |
| @@ -293,14 +293,16 @@ | |||
| 293 | 293 | </div> | |
| 294 | 294 | <div> | |
| 295 | 295 | <dt> | |
| 296 | - | <kbd>s</kbd> | |
| 297 | - | <kbd>t</kbd> | |
| 298 | - | <kbd>c</kbd> | |
| 299 | - | <kbd>r</kbd> | |
| 300 | - | <kbd>m</kbd> | |
| 301 | - | <kbd>i</kbd> | |
| 296 | + | <kbd>g s</kbd> | |
| 297 | + | <kbd>g f</kbd> | |
| 298 | + | <kbd>g c</kbd> | |
| 299 | + | <kbd>g b</kbd> | |
| 300 | + | <kbd>g m</kbd> | |
| 301 | + | <kbd>g i</kbd> | |
| 302 | + | <kbd>g r</kbd> | |
| 303 | + | <kbd>g a</kbd> | |
| 302 | 304 | </dt> | |
| 303 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 305 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 304 | 306 | </div> | |
| 305 | 307 | <div> | |
| 306 | 308 | <dt> | |
| @@ -399,45 +401,45 @@ | |||
| 399 | 401 | <span class="k">go</span> | |
| 400 | 402 | </a> | |
| 401 | 403 | </li> | |
| 402 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 404 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 403 | 405 | <a href="/files" tabindex="-1"> | |
| 404 | - | <span class="lbl">summary</span> | |
| 406 | + | <span class="lbl">summary (g s)</span> | |
| 405 | 407 | <span class="k">section</span> | |
| 406 | 408 | </a> | |
| 407 | 409 | </li> | |
| 408 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 410 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 409 | 411 | <a href="/files/tree/main" tabindex="-1"> | |
| 410 | - | <span class="lbl">files</span> | |
| 412 | + | <span class="lbl">files (g f)</span> | |
| 411 | 413 | <span class="k">section</span> | |
| 412 | 414 | </a> | |
| 413 | 415 | </li> | |
| 414 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 416 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 415 | 417 | <a href="/files/commits/main" tabindex="-1"> | |
| 416 | - | <span class="lbl">commits</span> | |
| 418 | + | <span class="lbl">commits (g c)</span> | |
| 417 | 419 | <span class="k">section</span> | |
| 418 | 420 | </a> | |
| 419 | 421 | </li> | |
| 420 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 422 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 421 | 423 | <a href="/files/refs" tabindex="-1"> | |
| 422 | - | <span class="lbl">branches & tags</span> | |
| 424 | + | <span class="lbl">branches & tags (g b)</span> | |
| 423 | 425 | <span class="k">section</span> | |
| 424 | 426 | </a> | |
| 425 | 427 | </li> | |
| 426 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 428 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 427 | 429 | <a href="/files/merge-requests" tabindex="-1"> | |
| 428 | - | <span class="lbl">merge requests</span> | |
| 430 | + | <span class="lbl">merge requests (g m)</span> | |
| 429 | 431 | <span class="k">section</span> | |
| 430 | 432 | </a> | |
| 431 | 433 | </li> | |
| 432 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 434 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 433 | 435 | <a href="/files/releases" tabindex="-1"> | |
| 434 | - | <span class="lbl">releases</span> | |
| 436 | + | <span class="lbl">releases (g r)</span> | |
| 435 | 437 | <span class="k">section</span> | |
| 436 | 438 | </a> | |
| 437 | 439 | </li> | |
| 438 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 440 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 439 | 441 | <a href="/files/actions" tabindex="-1"> | |
| 440 | - | <span class="lbl">actions</span> | |
| 442 | + | <span class="lbl">actions (g a)</span> | |
| 441 | 443 | <span class="k">section</span> | |
| 442 | 444 | </a> | |
| 443 | 445 | </li> | |
| @@ -490,7 +492,9 @@ | |||
| 490 | 492 | k-filter from:body -> ermite.focusFilter(); | |
| 491 | 493 | k-gh from:body -> ermite.go('.brand'); | |
| 492 | 494 | k-s from:body -> ermite.tab('s'); | |
| 493 | - | k-t from:body -> ermite.tab('t'); | |
| 495 | + | k-f from:body -> ermite.tab('f'); | |
| 496 | + | k-b from:body -> ermite.tab('b'); | |
| 497 | + | k-a from:body -> ermite.tab('a'); | |
| 494 | 498 | k-c from:body -> ermite.tab('c'); | |
| 495 | 499 | k-r from:body -> ermite.tab('r'); | |
| 496 | 500 | k-m from:body -> ermite.tab('m'); | |
| @@ -506,7 +510,6 @@ | |||
| 506 | 510 | k-permalink from:body -> ermite.permalink(); | |
| 507 | 511 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 508 | 512 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 509 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 510 | 513 | k-keys from:body -> ermite.toggleKeys()"> | |
| 511 | 514 | </div> | |
| 512 | 515 | </body> | |
M internal/app/testdata/web/blob-svg.html +33 -30
| @@ -73,31 +73,31 @@ | |||
| 73 | 73 | <nav class="tabs" aria-label="Sections"> | |
| 74 | 74 | <ul> | |
| 75 | 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 | 77 | </a> | |
| 78 | 78 | </li> | |
| 79 | 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 | 81 | </a> | |
| 82 | 82 | </li> | |
| 83 | 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 | 85 | </a> | |
| 86 | 86 | </li> | |
| 87 | 87 | <li> | |
| 88 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 88 | + | <a href="/files/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 89 | 89 | </a> | |
| 90 | 90 | </li> | |
| 91 | 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 | 93 | </a> | |
| 94 | 94 | </li> | |
| 95 | 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 | 97 | </a> | |
| 98 | 98 | </li> | |
| 99 | 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 | 101 | </a> | |
| 102 | 102 | </li> | |
| 103 | 103 | </ul> | |
| @@ -251,14 +251,16 @@ | |||
| 251 | 251 | </div> | |
| 252 | 252 | <div> | |
| 253 | 253 | <dt> | |
| 254 | - | <kbd>s</kbd> | |
| 255 | - | <kbd>t</kbd> | |
| 256 | - | <kbd>c</kbd> | |
| 257 | - | <kbd>r</kbd> | |
| 258 | - | <kbd>m</kbd> | |
| 259 | - | <kbd>i</kbd> | |
| 254 | + | <kbd>g s</kbd> | |
| 255 | + | <kbd>g f</kbd> | |
| 256 | + | <kbd>g c</kbd> | |
| 257 | + | <kbd>g b</kbd> | |
| 258 | + | <kbd>g m</kbd> | |
| 259 | + | <kbd>g i</kbd> | |
| 260 | + | <kbd>g r</kbd> | |
| 261 | + | <kbd>g a</kbd> | |
| 260 | 262 | </dt> | |
| 261 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 263 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 262 | 264 | </div> | |
| 263 | 265 | <div> | |
| 264 | 266 | <dt> | |
| @@ -357,45 +359,45 @@ | |||
| 357 | 359 | <span class="k">go</span> | |
| 358 | 360 | </a> | |
| 359 | 361 | </li> | |
| 360 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 362 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 361 | 363 | <a href="/files" tabindex="-1"> | |
| 362 | - | <span class="lbl">summary</span> | |
| 364 | + | <span class="lbl">summary (g s)</span> | |
| 363 | 365 | <span class="k">section</span> | |
| 364 | 366 | </a> | |
| 365 | 367 | </li> | |
| 366 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 368 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 367 | 369 | <a href="/files/tree/main" tabindex="-1"> | |
| 368 | - | <span class="lbl">files</span> | |
| 370 | + | <span class="lbl">files (g f)</span> | |
| 369 | 371 | <span class="k">section</span> | |
| 370 | 372 | </a> | |
| 371 | 373 | </li> | |
| 372 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 374 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 373 | 375 | <a href="/files/commits/main" tabindex="-1"> | |
| 374 | - | <span class="lbl">commits</span> | |
| 376 | + | <span class="lbl">commits (g c)</span> | |
| 375 | 377 | <span class="k">section</span> | |
| 376 | 378 | </a> | |
| 377 | 379 | </li> | |
| 378 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 380 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 379 | 381 | <a href="/files/refs" tabindex="-1"> | |
| 380 | - | <span class="lbl">branches & tags</span> | |
| 382 | + | <span class="lbl">branches & tags (g b)</span> | |
| 381 | 383 | <span class="k">section</span> | |
| 382 | 384 | </a> | |
| 383 | 385 | </li> | |
| 384 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 386 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 385 | 387 | <a href="/files/merge-requests" tabindex="-1"> | |
| 386 | - | <span class="lbl">merge requests</span> | |
| 388 | + | <span class="lbl">merge requests (g m)</span> | |
| 387 | 389 | <span class="k">section</span> | |
| 388 | 390 | </a> | |
| 389 | 391 | </li> | |
| 390 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 392 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 391 | 393 | <a href="/files/releases" tabindex="-1"> | |
| 392 | - | <span class="lbl">releases</span> | |
| 394 | + | <span class="lbl">releases (g r)</span> | |
| 393 | 395 | <span class="k">section</span> | |
| 394 | 396 | </a> | |
| 395 | 397 | </li> | |
| 396 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 398 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 397 | 399 | <a href="/files/actions" tabindex="-1"> | |
| 398 | - | <span class="lbl">actions</span> | |
| 400 | + | <span class="lbl">actions (g a)</span> | |
| 399 | 401 | <span class="k">section</span> | |
| 400 | 402 | </a> | |
| 401 | 403 | </li> | |
| @@ -448,7 +450,9 @@ | |||
| 448 | 450 | k-filter from:body -> ermite.focusFilter(); | |
| 449 | 451 | k-gh from:body -> ermite.go('.brand'); | |
| 450 | 452 | k-s from:body -> ermite.tab('s'); | |
| 451 | - | k-t from:body -> ermite.tab('t'); | |
| 453 | + | k-f from:body -> ermite.tab('f'); | |
| 454 | + | k-b from:body -> ermite.tab('b'); | |
| 455 | + | k-a from:body -> ermite.tab('a'); | |
| 452 | 456 | k-c from:body -> ermite.tab('c'); | |
| 453 | 457 | k-r from:body -> ermite.tab('r'); | |
| 454 | 458 | k-m from:body -> ermite.tab('m'); | |
| @@ -464,7 +468,6 @@ | |||
| 464 | 468 | k-permalink from:body -> ermite.permalink(); | |
| 465 | 469 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 466 | 470 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 467 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 468 | 471 | k-keys from:body -> ermite.toggleKeys()"> | |
| 469 | 472 | </div> | |
| 470 | 473 | </body> | |
M internal/app/testdata/web/blob-text.html +33 -30
| @@ -73,31 +73,31 @@ | |||
| 73 | 73 | <nav class="tabs" aria-label="Sections"> | |
| 74 | 74 | <ul> | |
| 75 | 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 | 77 | </a> | |
| 78 | 78 | </li> | |
| 79 | 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 | 81 | </a> | |
| 82 | 82 | </li> | |
| 83 | 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 | 85 | </a> | |
| 86 | 86 | </li> | |
| 87 | 87 | <li> | |
| 88 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 88 | + | <a href="/files/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 89 | 89 | </a> | |
| 90 | 90 | </li> | |
| 91 | 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 | 93 | </a> | |
| 94 | 94 | </li> | |
| 95 | 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 | 97 | </a> | |
| 98 | 98 | </li> | |
| 99 | 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 | 101 | </a> | |
| 102 | 102 | </li> | |
| 103 | 103 | </ul> | |
| @@ -246,14 +246,16 @@ | |||
| 246 | 246 | </div> | |
| 247 | 247 | <div> | |
| 248 | 248 | <dt> | |
| 249 | - | <kbd>s</kbd> | |
| 250 | - | <kbd>t</kbd> | |
| 251 | - | <kbd>c</kbd> | |
| 252 | - | <kbd>r</kbd> | |
| 253 | - | <kbd>m</kbd> | |
| 254 | - | <kbd>i</kbd> | |
| 249 | + | <kbd>g s</kbd> | |
| 250 | + | <kbd>g f</kbd> | |
| 251 | + | <kbd>g c</kbd> | |
| 252 | + | <kbd>g b</kbd> | |
| 253 | + | <kbd>g m</kbd> | |
| 254 | + | <kbd>g i</kbd> | |
| 255 | + | <kbd>g r</kbd> | |
| 256 | + | <kbd>g a</kbd> | |
| 255 | 257 | </dt> | |
| 256 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 258 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 257 | 259 | </div> | |
| 258 | 260 | <div> | |
| 259 | 261 | <dt> | |
| @@ -352,45 +354,45 @@ | |||
| 352 | 354 | <span class="k">go</span> | |
| 353 | 355 | </a> | |
| 354 | 356 | </li> | |
| 355 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 357 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 356 | 358 | <a href="/files" tabindex="-1"> | |
| 357 | - | <span class="lbl">summary</span> | |
| 359 | + | <span class="lbl">summary (g s)</span> | |
| 358 | 360 | <span class="k">section</span> | |
| 359 | 361 | </a> | |
| 360 | 362 | </li> | |
| 361 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 363 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 362 | 364 | <a href="/files/tree/main" tabindex="-1"> | |
| 363 | - | <span class="lbl">files</span> | |
| 365 | + | <span class="lbl">files (g f)</span> | |
| 364 | 366 | <span class="k">section</span> | |
| 365 | 367 | </a> | |
| 366 | 368 | </li> | |
| 367 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 369 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 368 | 370 | <a href="/files/commits/main" tabindex="-1"> | |
| 369 | - | <span class="lbl">commits</span> | |
| 371 | + | <span class="lbl">commits (g c)</span> | |
| 370 | 372 | <span class="k">section</span> | |
| 371 | 373 | </a> | |
| 372 | 374 | </li> | |
| 373 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 375 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 374 | 376 | <a href="/files/refs" tabindex="-1"> | |
| 375 | - | <span class="lbl">branches & tags</span> | |
| 377 | + | <span class="lbl">branches & tags (g b)</span> | |
| 376 | 378 | <span class="k">section</span> | |
| 377 | 379 | </a> | |
| 378 | 380 | </li> | |
| 379 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 381 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 380 | 382 | <a href="/files/merge-requests" tabindex="-1"> | |
| 381 | - | <span class="lbl">merge requests</span> | |
| 383 | + | <span class="lbl">merge requests (g m)</span> | |
| 382 | 384 | <span class="k">section</span> | |
| 383 | 385 | </a> | |
| 384 | 386 | </li> | |
| 385 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 387 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 386 | 388 | <a href="/files/releases" tabindex="-1"> | |
| 387 | - | <span class="lbl">releases</span> | |
| 389 | + | <span class="lbl">releases (g r)</span> | |
| 388 | 390 | <span class="k">section</span> | |
| 389 | 391 | </a> | |
| 390 | 392 | </li> | |
| 391 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 393 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 392 | 394 | <a href="/files/actions" tabindex="-1"> | |
| 393 | - | <span class="lbl">actions</span> | |
| 395 | + | <span class="lbl">actions (g a)</span> | |
| 394 | 396 | <span class="k">section</span> | |
| 395 | 397 | </a> | |
| 396 | 398 | </li> | |
| @@ -443,7 +445,9 @@ | |||
| 443 | 445 | k-filter from:body -> ermite.focusFilter(); | |
| 444 | 446 | k-gh from:body -> ermite.go('.brand'); | |
| 445 | 447 | k-s from:body -> ermite.tab('s'); | |
| 446 | - | k-t from:body -> ermite.tab('t'); | |
| 448 | + | k-f from:body -> ermite.tab('f'); | |
| 449 | + | k-b from:body -> ermite.tab('b'); | |
| 450 | + | k-a from:body -> ermite.tab('a'); | |
| 447 | 451 | k-c from:body -> ermite.tab('c'); | |
| 448 | 452 | k-r from:body -> ermite.tab('r'); | |
| 449 | 453 | k-m from:body -> ermite.tab('m'); | |
| @@ -459,7 +463,6 @@ | |||
| 459 | 463 | k-permalink from:body -> ermite.permalink(); | |
| 460 | 464 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 461 | 465 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 462 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 463 | 466 | k-keys from:body -> ermite.toggleKeys()"> | |
| 464 | 467 | </div> | |
| 465 | 468 | </body> | |
M internal/app/testdata/web/blob-too-large.html +33 -30
| @@ -73,31 +73,31 @@ | |||
| 73 | 73 | <nav class="tabs" aria-label="Sections"> | |
| 74 | 74 | <ul> | |
| 75 | 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 | 77 | </a> | |
| 78 | 78 | </li> | |
| 79 | 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 | 81 | </a> | |
| 82 | 82 | </li> | |
| 83 | 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 | 85 | </a> | |
| 86 | 86 | </li> | |
| 87 | 87 | <li> | |
| 88 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 88 | + | <a href="/files/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 89 | 89 | </a> | |
| 90 | 90 | </li> | |
| 91 | 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 | 93 | </a> | |
| 94 | 94 | </li> | |
| 95 | 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 | 97 | </a> | |
| 98 | 98 | </li> | |
| 99 | 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 | 101 | </a> | |
| 102 | 102 | </li> | |
| 103 | 103 | </ul> | |
| @@ -226,14 +226,16 @@ | |||
| 226 | 226 | </div> | |
| 227 | 227 | <div> | |
| 228 | 228 | <dt> | |
| 229 | - | <kbd>s</kbd> | |
| 230 | - | <kbd>t</kbd> | |
| 231 | - | <kbd>c</kbd> | |
| 232 | - | <kbd>r</kbd> | |
| 233 | - | <kbd>m</kbd> | |
| 234 | - | <kbd>i</kbd> | |
| 229 | + | <kbd>g s</kbd> | |
| 230 | + | <kbd>g f</kbd> | |
| 231 | + | <kbd>g c</kbd> | |
| 232 | + | <kbd>g b</kbd> | |
| 233 | + | <kbd>g m</kbd> | |
| 234 | + | <kbd>g i</kbd> | |
| 235 | + | <kbd>g r</kbd> | |
| 236 | + | <kbd>g a</kbd> | |
| 235 | 237 | </dt> | |
| 236 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 238 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 237 | 239 | </div> | |
| 238 | 240 | <div> | |
| 239 | 241 | <dt> | |
| @@ -332,45 +334,45 @@ | |||
| 332 | 334 | <span class="k">go</span> | |
| 333 | 335 | </a> | |
| 334 | 336 | </li> | |
| 335 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 337 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 336 | 338 | <a href="/files" tabindex="-1"> | |
| 337 | - | <span class="lbl">summary</span> | |
| 339 | + | <span class="lbl">summary (g s)</span> | |
| 338 | 340 | <span class="k">section</span> | |
| 339 | 341 | </a> | |
| 340 | 342 | </li> | |
| 341 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 343 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 342 | 344 | <a href="/files/tree/main" tabindex="-1"> | |
| 343 | - | <span class="lbl">files</span> | |
| 345 | + | <span class="lbl">files (g f)</span> | |
| 344 | 346 | <span class="k">section</span> | |
| 345 | 347 | </a> | |
| 346 | 348 | </li> | |
| 347 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 349 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 348 | 350 | <a href="/files/commits/main" tabindex="-1"> | |
| 349 | - | <span class="lbl">commits</span> | |
| 351 | + | <span class="lbl">commits (g c)</span> | |
| 350 | 352 | <span class="k">section</span> | |
| 351 | 353 | </a> | |
| 352 | 354 | </li> | |
| 353 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 355 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 354 | 356 | <a href="/files/refs" tabindex="-1"> | |
| 355 | - | <span class="lbl">branches & tags</span> | |
| 357 | + | <span class="lbl">branches & tags (g b)</span> | |
| 356 | 358 | <span class="k">section</span> | |
| 357 | 359 | </a> | |
| 358 | 360 | </li> | |
| 359 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 361 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 360 | 362 | <a href="/files/merge-requests" tabindex="-1"> | |
| 361 | - | <span class="lbl">merge requests</span> | |
| 363 | + | <span class="lbl">merge requests (g m)</span> | |
| 362 | 364 | <span class="k">section</span> | |
| 363 | 365 | </a> | |
| 364 | 366 | </li> | |
| 365 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 367 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 366 | 368 | <a href="/files/releases" tabindex="-1"> | |
| 367 | - | <span class="lbl">releases</span> | |
| 369 | + | <span class="lbl">releases (g r)</span> | |
| 368 | 370 | <span class="k">section</span> | |
| 369 | 371 | </a> | |
| 370 | 372 | </li> | |
| 371 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 373 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 372 | 374 | <a href="/files/actions" tabindex="-1"> | |
| 373 | - | <span class="lbl">actions</span> | |
| 375 | + | <span class="lbl">actions (g a)</span> | |
| 374 | 376 | <span class="k">section</span> | |
| 375 | 377 | </a> | |
| 376 | 378 | </li> | |
| @@ -423,7 +425,9 @@ | |||
| 423 | 425 | k-filter from:body -> ermite.focusFilter(); | |
| 424 | 426 | k-gh from:body -> ermite.go('.brand'); | |
| 425 | 427 | k-s from:body -> ermite.tab('s'); | |
| 426 | - | k-t from:body -> ermite.tab('t'); | |
| 428 | + | k-f from:body -> ermite.tab('f'); | |
| 429 | + | k-b from:body -> ermite.tab('b'); | |
| 430 | + | k-a from:body -> ermite.tab('a'); | |
| 427 | 431 | k-c from:body -> ermite.tab('c'); | |
| 428 | 432 | k-r from:body -> ermite.tab('r'); | |
| 429 | 433 | k-m from:body -> ermite.tab('m'); | |
| @@ -439,7 +443,6 @@ | |||
| 439 | 443 | k-permalink from:body -> ermite.permalink(); | |
| 440 | 444 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 441 | 445 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 442 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 443 | 446 | k-keys from:body -> ermite.toggleKeys()"> | |
| 444 | 447 | </div> | |
| 445 | 448 | </body> | |
M internal/app/testdata/web/blob-trojan.html +33 -30
| @@ -73,31 +73,31 @@ | |||
| 73 | 73 | <nav class="tabs" aria-label="Sections"> | |
| 74 | 74 | <ul> | |
| 75 | 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 | 77 | </a> | |
| 78 | 78 | </li> | |
| 79 | 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 | 81 | </a> | |
| 82 | 82 | </li> | |
| 83 | 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 | 85 | </a> | |
| 86 | 86 | </li> | |
| 87 | 87 | <li> | |
| 88 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 88 | + | <a href="/files/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 89 | 89 | </a> | |
| 90 | 90 | </li> | |
| 91 | 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 | 93 | </a> | |
| 94 | 94 | </li> | |
| 95 | 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 | 97 | </a> | |
| 98 | 98 | </li> | |
| 99 | 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 | 101 | </a> | |
| 102 | 102 | </li> | |
| 103 | 103 | </ul> | |
| @@ -293,14 +293,16 @@ | |||
| 293 | 293 | </div> | |
| 294 | 294 | <div> | |
| 295 | 295 | <dt> | |
| 296 | - | <kbd>s</kbd> | |
| 297 | - | <kbd>t</kbd> | |
| 298 | - | <kbd>c</kbd> | |
| 299 | - | <kbd>r</kbd> | |
| 300 | - | <kbd>m</kbd> | |
| 301 | - | <kbd>i</kbd> | |
| 296 | + | <kbd>g s</kbd> | |
| 297 | + | <kbd>g f</kbd> | |
| 298 | + | <kbd>g c</kbd> | |
| 299 | + | <kbd>g b</kbd> | |
| 300 | + | <kbd>g m</kbd> | |
| 301 | + | <kbd>g i</kbd> | |
| 302 | + | <kbd>g r</kbd> | |
| 303 | + | <kbd>g a</kbd> | |
| 302 | 304 | </dt> | |
| 303 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 305 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 304 | 306 | </div> | |
| 305 | 307 | <div> | |
| 306 | 308 | <dt> | |
| @@ -399,45 +401,45 @@ | |||
| 399 | 401 | <span class="k">go</span> | |
| 400 | 402 | </a> | |
| 401 | 403 | </li> | |
| 402 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 404 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 403 | 405 | <a href="/files" tabindex="-1"> | |
| 404 | - | <span class="lbl">summary</span> | |
| 406 | + | <span class="lbl">summary (g s)</span> | |
| 405 | 407 | <span class="k">section</span> | |
| 406 | 408 | </a> | |
| 407 | 409 | </li> | |
| 408 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 410 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 409 | 411 | <a href="/files/tree/main" tabindex="-1"> | |
| 410 | - | <span class="lbl">files</span> | |
| 412 | + | <span class="lbl">files (g f)</span> | |
| 411 | 413 | <span class="k">section</span> | |
| 412 | 414 | </a> | |
| 413 | 415 | </li> | |
| 414 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 416 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 415 | 417 | <a href="/files/commits/main" tabindex="-1"> | |
| 416 | - | <span class="lbl">commits</span> | |
| 418 | + | <span class="lbl">commits (g c)</span> | |
| 417 | 419 | <span class="k">section</span> | |
| 418 | 420 | </a> | |
| 419 | 421 | </li> | |
| 420 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 422 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 421 | 423 | <a href="/files/refs" tabindex="-1"> | |
| 422 | - | <span class="lbl">branches & tags</span> | |
| 424 | + | <span class="lbl">branches & tags (g b)</span> | |
| 423 | 425 | <span class="k">section</span> | |
| 424 | 426 | </a> | |
| 425 | 427 | </li> | |
| 426 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 428 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 427 | 429 | <a href="/files/merge-requests" tabindex="-1"> | |
| 428 | - | <span class="lbl">merge requests</span> | |
| 430 | + | <span class="lbl">merge requests (g m)</span> | |
| 429 | 431 | <span class="k">section</span> | |
| 430 | 432 | </a> | |
| 431 | 433 | </li> | |
| 432 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 434 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 433 | 435 | <a href="/files/releases" tabindex="-1"> | |
| 434 | - | <span class="lbl">releases</span> | |
| 436 | + | <span class="lbl">releases (g r)</span> | |
| 435 | 437 | <span class="k">section</span> | |
| 436 | 438 | </a> | |
| 437 | 439 | </li> | |
| 438 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 440 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 439 | 441 | <a href="/files/actions" tabindex="-1"> | |
| 440 | - | <span class="lbl">actions</span> | |
| 442 | + | <span class="lbl">actions (g a)</span> | |
| 441 | 443 | <span class="k">section</span> | |
| 442 | 444 | </a> | |
| 443 | 445 | </li> | |
| @@ -490,7 +492,9 @@ | |||
| 490 | 492 | k-filter from:body -> ermite.focusFilter(); | |
| 491 | 493 | k-gh from:body -> ermite.go('.brand'); | |
| 492 | 494 | k-s from:body -> ermite.tab('s'); | |
| 493 | - | k-t from:body -> ermite.tab('t'); | |
| 495 | + | k-f from:body -> ermite.tab('f'); | |
| 496 | + | k-b from:body -> ermite.tab('b'); | |
| 497 | + | k-a from:body -> ermite.tab('a'); | |
| 494 | 498 | k-c from:body -> ermite.tab('c'); | |
| 495 | 499 | k-r from:body -> ermite.tab('r'); | |
| 496 | 500 | k-m from:body -> ermite.tab('m'); | |
| @@ -506,7 +510,6 @@ | |||
| 506 | 510 | k-permalink from:body -> ermite.permalink(); | |
| 507 | 511 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 508 | 512 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 509 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 510 | 513 | k-keys from:body -> ermite.toggleKeys()"> | |
| 511 | 514 | </div> | |
| 512 | 515 | </body> | |
M internal/app/testdata/web/commit-big.html +33 -30
| @@ -72,31 +72,31 @@ | |||
| 72 | 72 | <nav class="tabs" aria-label="Sections"> | |
| 73 | 73 | <ul> | |
| 74 | 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 | 76 | </a> | |
| 77 | 77 | </li> | |
| 78 | 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 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 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 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 86 | <li> | |
| 87 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 87 | + | <a href="/diffs/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 88 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 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 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 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 | 96 | </a> | |
| 97 | 97 | </li> | |
| 98 | 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 | 100 | </a> | |
| 101 | 101 | </li> | |
| 102 | 102 | </ul> | |
| @@ -278,14 +278,16 @@ | |||
| 278 | 278 | </div> | |
| 279 | 279 | <div> | |
| 280 | 280 | <dt> | |
| 281 | - | <kbd>s</kbd> | |
| 282 | - | <kbd>t</kbd> | |
| 283 | - | <kbd>c</kbd> | |
| 284 | - | <kbd>r</kbd> | |
| 285 | - | <kbd>m</kbd> | |
| 286 | - | <kbd>i</kbd> | |
| 281 | + | <kbd>g s</kbd> | |
| 282 | + | <kbd>g f</kbd> | |
| 283 | + | <kbd>g c</kbd> | |
| 284 | + | <kbd>g b</kbd> | |
| 285 | + | <kbd>g m</kbd> | |
| 286 | + | <kbd>g i</kbd> | |
| 287 | + | <kbd>g r</kbd> | |
| 288 | + | <kbd>g a</kbd> | |
| 287 | 289 | </dt> | |
| 288 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 290 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 289 | 291 | </div> | |
| 290 | 292 | <div> | |
| 291 | 293 | <dt> | |
| @@ -384,45 +386,45 @@ | |||
| 384 | 386 | <span class="k">go</span> | |
| 385 | 387 | </a> | |
| 386 | 388 | </li> | |
| 387 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 389 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 388 | 390 | <a href="/diffs" tabindex="-1"> | |
| 389 | - | <span class="lbl">summary</span> | |
| 391 | + | <span class="lbl">summary (g s)</span> | |
| 390 | 392 | <span class="k">section</span> | |
| 391 | 393 | </a> | |
| 392 | 394 | </li> | |
| 393 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 395 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 394 | 396 | <a href="/diffs/tree/main" tabindex="-1"> | |
| 395 | - | <span class="lbl">files</span> | |
| 397 | + | <span class="lbl">files (g f)</span> | |
| 396 | 398 | <span class="k">section</span> | |
| 397 | 399 | </a> | |
| 398 | 400 | </li> | |
| 399 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 401 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 400 | 402 | <a href="/diffs/commits/main" tabindex="-1"> | |
| 401 | - | <span class="lbl">commits</span> | |
| 403 | + | <span class="lbl">commits (g c)</span> | |
| 402 | 404 | <span class="k">section</span> | |
| 403 | 405 | </a> | |
| 404 | 406 | </li> | |
| 405 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 407 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 406 | 408 | <a href="/diffs/refs" tabindex="-1"> | |
| 407 | - | <span class="lbl">branches & tags</span> | |
| 409 | + | <span class="lbl">branches & tags (g b)</span> | |
| 408 | 410 | <span class="k">section</span> | |
| 409 | 411 | </a> | |
| 410 | 412 | </li> | |
| 411 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 413 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 412 | 414 | <a href="/diffs/merge-requests" tabindex="-1"> | |
| 413 | - | <span class="lbl">merge requests</span> | |
| 415 | + | <span class="lbl">merge requests (g m)</span> | |
| 414 | 416 | <span class="k">section</span> | |
| 415 | 417 | </a> | |
| 416 | 418 | </li> | |
| 417 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 419 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 418 | 420 | <a href="/diffs/releases" tabindex="-1"> | |
| 419 | - | <span class="lbl">releases</span> | |
| 421 | + | <span class="lbl">releases (g r)</span> | |
| 420 | 422 | <span class="k">section</span> | |
| 421 | 423 | </a> | |
| 422 | 424 | </li> | |
| 423 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 425 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 424 | 426 | <a href="/diffs/actions" tabindex="-1"> | |
| 425 | - | <span class="lbl">actions</span> | |
| 427 | + | <span class="lbl">actions (g a)</span> | |
| 426 | 428 | <span class="k">section</span> | |
| 427 | 429 | </a> | |
| 428 | 430 | </li> | |
| @@ -475,7 +477,9 @@ | |||
| 475 | 477 | k-filter from:body -> ermite.focusFilter(); | |
| 476 | 478 | k-gh from:body -> ermite.go('.brand'); | |
| 477 | 479 | k-s from:body -> ermite.tab('s'); | |
| 478 | - | k-t from:body -> ermite.tab('t'); | |
| 480 | + | k-f from:body -> ermite.tab('f'); | |
| 481 | + | k-b from:body -> ermite.tab('b'); | |
| 482 | + | k-a from:body -> ermite.tab('a'); | |
| 479 | 483 | k-c from:body -> ermite.tab('c'); | |
| 480 | 484 | k-r from:body -> ermite.tab('r'); | |
| 481 | 485 | k-m from:body -> ermite.tab('m'); | |
| @@ -491,7 +495,6 @@ | |||
| 491 | 495 | k-permalink from:body -> ermite.permalink(); | |
| 492 | 496 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 493 | 497 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 494 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 495 | 498 | k-keys from:body -> ermite.toggleKeys()"> | |
| 496 | 499 | </div> | |
| 497 | 500 | </body> | |
M internal/app/testdata/web/commit-binary.html +33 -30
| @@ -72,31 +72,31 @@ | |||
| 72 | 72 | <nav class="tabs" aria-label="Sections"> | |
| 73 | 73 | <ul> | |
| 74 | 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 | 76 | </a> | |
| 77 | 77 | </li> | |
| 78 | 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 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 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 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 86 | <li> | |
| 87 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 87 | + | <a href="/diffs/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 88 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 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 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 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 | 96 | </a> | |
| 97 | 97 | </li> | |
| 98 | 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 | 100 | </a> | |
| 101 | 101 | </li> | |
| 102 | 102 | </ul> | |
| @@ -269,14 +269,16 @@ | |||
| 269 | 269 | </div> | |
| 270 | 270 | <div> | |
| 271 | 271 | <dt> | |
| 272 | - | <kbd>s</kbd> | |
| 273 | - | <kbd>t</kbd> | |
| 274 | - | <kbd>c</kbd> | |
| 275 | - | <kbd>r</kbd> | |
| 276 | - | <kbd>m</kbd> | |
| 277 | - | <kbd>i</kbd> | |
| 272 | + | <kbd>g s</kbd> | |
| 273 | + | <kbd>g f</kbd> | |
| 274 | + | <kbd>g c</kbd> | |
| 275 | + | <kbd>g b</kbd> | |
| 276 | + | <kbd>g m</kbd> | |
| 277 | + | <kbd>g i</kbd> | |
| 278 | + | <kbd>g r</kbd> | |
| 279 | + | <kbd>g a</kbd> | |
| 278 | 280 | </dt> | |
| 279 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 281 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 280 | 282 | </div> | |
| 281 | 283 | <div> | |
| 282 | 284 | <dt> | |
| @@ -375,45 +377,45 @@ | |||
| 375 | 377 | <span class="k">go</span> | |
| 376 | 378 | </a> | |
| 377 | 379 | </li> | |
| 378 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 380 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 379 | 381 | <a href="/diffs" tabindex="-1"> | |
| 380 | - | <span class="lbl">summary</span> | |
| 382 | + | <span class="lbl">summary (g s)</span> | |
| 381 | 383 | <span class="k">section</span> | |
| 382 | 384 | </a> | |
| 383 | 385 | </li> | |
| 384 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 386 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 385 | 387 | <a href="/diffs/tree/main" tabindex="-1"> | |
| 386 | - | <span class="lbl">files</span> | |
| 388 | + | <span class="lbl">files (g f)</span> | |
| 387 | 389 | <span class="k">section</span> | |
| 388 | 390 | </a> | |
| 389 | 391 | </li> | |
| 390 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 392 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 391 | 393 | <a href="/diffs/commits/main" tabindex="-1"> | |
| 392 | - | <span class="lbl">commits</span> | |
| 394 | + | <span class="lbl">commits (g c)</span> | |
| 393 | 395 | <span class="k">section</span> | |
| 394 | 396 | </a> | |
| 395 | 397 | </li> | |
| 396 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 398 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 397 | 399 | <a href="/diffs/refs" tabindex="-1"> | |
| 398 | - | <span class="lbl">branches & tags</span> | |
| 400 | + | <span class="lbl">branches & tags (g b)</span> | |
| 399 | 401 | <span class="k">section</span> | |
| 400 | 402 | </a> | |
| 401 | 403 | </li> | |
| 402 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 404 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 403 | 405 | <a href="/diffs/merge-requests" tabindex="-1"> | |
| 404 | - | <span class="lbl">merge requests</span> | |
| 406 | + | <span class="lbl">merge requests (g m)</span> | |
| 405 | 407 | <span class="k">section</span> | |
| 406 | 408 | </a> | |
| 407 | 409 | </li> | |
| 408 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 410 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 409 | 411 | <a href="/diffs/releases" tabindex="-1"> | |
| 410 | - | <span class="lbl">releases</span> | |
| 412 | + | <span class="lbl">releases (g r)</span> | |
| 411 | 413 | <span class="k">section</span> | |
| 412 | 414 | </a> | |
| 413 | 415 | </li> | |
| 414 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 416 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 415 | 417 | <a href="/diffs/actions" tabindex="-1"> | |
| 416 | - | <span class="lbl">actions</span> | |
| 418 | + | <span class="lbl">actions (g a)</span> | |
| 417 | 419 | <span class="k">section</span> | |
| 418 | 420 | </a> | |
| 419 | 421 | </li> | |
| @@ -466,7 +468,9 @@ | |||
| 466 | 468 | k-filter from:body -> ermite.focusFilter(); | |
| 467 | 469 | k-gh from:body -> ermite.go('.brand'); | |
| 468 | 470 | k-s from:body -> ermite.tab('s'); | |
| 469 | - | k-t from:body -> ermite.tab('t'); | |
| 471 | + | k-f from:body -> ermite.tab('f'); | |
| 472 | + | k-b from:body -> ermite.tab('b'); | |
| 473 | + | k-a from:body -> ermite.tab('a'); | |
| 470 | 474 | k-c from:body -> ermite.tab('c'); | |
| 471 | 475 | k-r from:body -> ermite.tab('r'); | |
| 472 | 476 | k-m from:body -> ermite.tab('m'); | |
| @@ -482,7 +486,6 @@ | |||
| 482 | 486 | k-permalink from:body -> ermite.permalink(); | |
| 483 | 487 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 484 | 488 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 485 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 486 | 489 | k-keys from:body -> ermite.toggleKeys()"> | |
| 487 | 490 | </div> | |
| 488 | 491 | </body> | |
M internal/app/testdata/web/commit-merge.html +33 -30
| @@ -72,31 +72,31 @@ | |||
| 72 | 72 | <nav class="tabs" aria-label="Sections"> | |
| 73 | 73 | <ul> | |
| 74 | 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 | 76 | </a> | |
| 77 | 77 | </li> | |
| 78 | 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 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 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 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 86 | <li> | |
| 87 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 87 | + | <a href="/diffs/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 88 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 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 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 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 | 96 | </a> | |
| 97 | 97 | </li> | |
| 98 | 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 | 100 | </a> | |
| 101 | 101 | </li> | |
| 102 | 102 | </ul> | |
| @@ -311,14 +311,16 @@ | |||
| 311 | 311 | </div> | |
| 312 | 312 | <div> | |
| 313 | 313 | <dt> | |
| 314 | - | <kbd>s</kbd> | |
| 315 | - | <kbd>t</kbd> | |
| 316 | - | <kbd>c</kbd> | |
| 317 | - | <kbd>r</kbd> | |
| 318 | - | <kbd>m</kbd> | |
| 319 | - | <kbd>i</kbd> | |
| 314 | + | <kbd>g s</kbd> | |
| 315 | + | <kbd>g f</kbd> | |
| 316 | + | <kbd>g c</kbd> | |
| 317 | + | <kbd>g b</kbd> | |
| 318 | + | <kbd>g m</kbd> | |
| 319 | + | <kbd>g i</kbd> | |
| 320 | + | <kbd>g r</kbd> | |
| 321 | + | <kbd>g a</kbd> | |
| 320 | 322 | </dt> | |
| 321 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 323 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 322 | 324 | </div> | |
| 323 | 325 | <div> | |
| 324 | 326 | <dt> | |
| @@ -417,45 +419,45 @@ | |||
| 417 | 419 | <span class="k">go</span> | |
| 418 | 420 | </a> | |
| 419 | 421 | </li> | |
| 420 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 422 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 421 | 423 | <a href="/diffs" tabindex="-1"> | |
| 422 | - | <span class="lbl">summary</span> | |
| 424 | + | <span class="lbl">summary (g s)</span> | |
| 423 | 425 | <span class="k">section</span> | |
| 424 | 426 | </a> | |
| 425 | 427 | </li> | |
| 426 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 428 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 427 | 429 | <a href="/diffs/tree/main" tabindex="-1"> | |
| 428 | - | <span class="lbl">files</span> | |
| 430 | + | <span class="lbl">files (g f)</span> | |
| 429 | 431 | <span class="k">section</span> | |
| 430 | 432 | </a> | |
| 431 | 433 | </li> | |
| 432 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 434 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 433 | 435 | <a href="/diffs/commits/main" tabindex="-1"> | |
| 434 | - | <span class="lbl">commits</span> | |
| 436 | + | <span class="lbl">commits (g c)</span> | |
| 435 | 437 | <span class="k">section</span> | |
| 436 | 438 | </a> | |
| 437 | 439 | </li> | |
| 438 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 440 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 439 | 441 | <a href="/diffs/refs" tabindex="-1"> | |
| 440 | - | <span class="lbl">branches & tags</span> | |
| 442 | + | <span class="lbl">branches & tags (g b)</span> | |
| 441 | 443 | <span class="k">section</span> | |
| 442 | 444 | </a> | |
| 443 | 445 | </li> | |
| 444 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 446 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 445 | 447 | <a href="/diffs/merge-requests" tabindex="-1"> | |
| 446 | - | <span class="lbl">merge requests</span> | |
| 448 | + | <span class="lbl">merge requests (g m)</span> | |
| 447 | 449 | <span class="k">section</span> | |
| 448 | 450 | </a> | |
| 449 | 451 | </li> | |
| 450 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 452 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 451 | 453 | <a href="/diffs/releases" tabindex="-1"> | |
| 452 | - | <span class="lbl">releases</span> | |
| 454 | + | <span class="lbl">releases (g r)</span> | |
| 453 | 455 | <span class="k">section</span> | |
| 454 | 456 | </a> | |
| 455 | 457 | </li> | |
| 456 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 458 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 457 | 459 | <a href="/diffs/actions" tabindex="-1"> | |
| 458 | - | <span class="lbl">actions</span> | |
| 460 | + | <span class="lbl">actions (g a)</span> | |
| 459 | 461 | <span class="k">section</span> | |
| 460 | 462 | </a> | |
| 461 | 463 | </li> | |
| @@ -508,7 +510,9 @@ | |||
| 508 | 510 | k-filter from:body -> ermite.focusFilter(); | |
| 509 | 511 | k-gh from:body -> ermite.go('.brand'); | |
| 510 | 512 | k-s from:body -> ermite.tab('s'); | |
| 511 | - | k-t from:body -> ermite.tab('t'); | |
| 513 | + | k-f from:body -> ermite.tab('f'); | |
| 514 | + | k-b from:body -> ermite.tab('b'); | |
| 515 | + | k-a from:body -> ermite.tab('a'); | |
| 512 | 516 | k-c from:body -> ermite.tab('c'); | |
| 513 | 517 | k-r from:body -> ermite.tab('r'); | |
| 514 | 518 | k-m from:body -> ermite.tab('m'); | |
| @@ -524,7 +528,6 @@ | |||
| 524 | 528 | k-permalink from:body -> ermite.permalink(); | |
| 525 | 529 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 526 | 530 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 527 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 528 | 531 | k-keys from:body -> ermite.toggleKeys()"> | |
| 529 | 532 | </div> | |
| 530 | 533 | </body> | |
M internal/app/testdata/web/commit-modify-split.html +33 -30
| @@ -72,31 +72,31 @@ | |||
| 72 | 72 | <nav class="tabs" aria-label="Sections"> | |
| 73 | 73 | <ul> | |
| 74 | 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 | 76 | </a> | |
| 77 | 77 | </li> | |
| 78 | 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 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 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 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 86 | <li> | |
| 87 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 87 | + | <a href="/diffs/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 88 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 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 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 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 | 96 | </a> | |
| 97 | 97 | </li> | |
| 98 | 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 | 100 | </a> | |
| 101 | 101 | </li> | |
| 102 | 102 | </ul> | |
| @@ -434,14 +434,16 @@ And <b>html</b> & "quotes".</pre> | |||
| 434 | 434 | </div> | |
| 435 | 435 | <div> | |
| 436 | 436 | <dt> | |
| 437 | - | <kbd>s</kbd> | |
| 438 | - | <kbd>t</kbd> | |
| 439 | - | <kbd>c</kbd> | |
| 440 | - | <kbd>r</kbd> | |
| 441 | - | <kbd>m</kbd> | |
| 442 | - | <kbd>i</kbd> | |
| 437 | + | <kbd>g s</kbd> | |
| 438 | + | <kbd>g f</kbd> | |
| 439 | + | <kbd>g c</kbd> | |
| 440 | + | <kbd>g b</kbd> | |
| 441 | + | <kbd>g m</kbd> | |
| 442 | + | <kbd>g i</kbd> | |
| 443 | + | <kbd>g r</kbd> | |
| 444 | + | <kbd>g a</kbd> | |
| 443 | 445 | </dt> | |
| 444 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 446 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 445 | 447 | </div> | |
| 446 | 448 | <div> | |
| 447 | 449 | <dt> | |
| @@ -540,45 +542,45 @@ And <b>html</b> & "quotes".</pre> | |||
| 540 | 542 | <span class="k">go</span> | |
| 541 | 543 | </a> | |
| 542 | 544 | </li> | |
| 543 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 545 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 544 | 546 | <a href="/diffs" tabindex="-1"> | |
| 545 | - | <span class="lbl">summary</span> | |
| 547 | + | <span class="lbl">summary (g s)</span> | |
| 546 | 548 | <span class="k">section</span> | |
| 547 | 549 | </a> | |
| 548 | 550 | </li> | |
| 549 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 551 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 550 | 552 | <a href="/diffs/tree/main" tabindex="-1"> | |
| 551 | - | <span class="lbl">files</span> | |
| 553 | + | <span class="lbl">files (g f)</span> | |
| 552 | 554 | <span class="k">section</span> | |
| 553 | 555 | </a> | |
| 554 | 556 | </li> | |
| 555 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 557 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 556 | 558 | <a href="/diffs/commits/main" tabindex="-1"> | |
| 557 | - | <span class="lbl">commits</span> | |
| 559 | + | <span class="lbl">commits (g c)</span> | |
| 558 | 560 | <span class="k">section</span> | |
| 559 | 561 | </a> | |
| 560 | 562 | </li> | |
| 561 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 563 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 562 | 564 | <a href="/diffs/refs" tabindex="-1"> | |
| 563 | - | <span class="lbl">branches & tags</span> | |
| 565 | + | <span class="lbl">branches & tags (g b)</span> | |
| 564 | 566 | <span class="k">section</span> | |
| 565 | 567 | </a> | |
| 566 | 568 | </li> | |
| 567 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 569 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 568 | 570 | <a href="/diffs/merge-requests" tabindex="-1"> | |
| 569 | - | <span class="lbl">merge requests</span> | |
| 571 | + | <span class="lbl">merge requests (g m)</span> | |
| 570 | 572 | <span class="k">section</span> | |
| 571 | 573 | </a> | |
| 572 | 574 | </li> | |
| 573 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 575 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 574 | 576 | <a href="/diffs/releases" tabindex="-1"> | |
| 575 | - | <span class="lbl">releases</span> | |
| 577 | + | <span class="lbl">releases (g r)</span> | |
| 576 | 578 | <span class="k">section</span> | |
| 577 | 579 | </a> | |
| 578 | 580 | </li> | |
| 579 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 581 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 580 | 582 | <a href="/diffs/actions" tabindex="-1"> | |
| 581 | - | <span class="lbl">actions</span> | |
| 583 | + | <span class="lbl">actions (g a)</span> | |
| 582 | 584 | <span class="k">section</span> | |
| 583 | 585 | </a> | |
| 584 | 586 | </li> | |
| @@ -631,7 +633,9 @@ And <b>html</b> & "quotes".</pre> | |||
| 631 | 633 | k-filter from:body -> ermite.focusFilter(); | |
| 632 | 634 | k-gh from:body -> ermite.go('.brand'); | |
| 633 | 635 | k-s from:body -> ermite.tab('s'); | |
| 634 | - | k-t from:body -> ermite.tab('t'); | |
| 636 | + | k-f from:body -> ermite.tab('f'); | |
| 637 | + | k-b from:body -> ermite.tab('b'); | |
| 638 | + | k-a from:body -> ermite.tab('a'); | |
| 635 | 639 | k-c from:body -> ermite.tab('c'); | |
| 636 | 640 | k-r from:body -> ermite.tab('r'); | |
| 637 | 641 | k-m from:body -> ermite.tab('m'); | |
| @@ -647,7 +651,6 @@ And <b>html</b> & "quotes".</pre> | |||
| 647 | 651 | k-permalink from:body -> ermite.permalink(); | |
| 648 | 652 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 649 | 653 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 650 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 651 | 654 | k-keys from:body -> ermite.toggleKeys()"> | |
| 652 | 655 | </div> | |
| 653 | 656 | </body> | |
M internal/app/testdata/web/commit-modify.html +33 -30
| @@ -72,31 +72,31 @@ | |||
| 72 | 72 | <nav class="tabs" aria-label="Sections"> | |
| 73 | 73 | <ul> | |
| 74 | 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 | 76 | </a> | |
| 77 | 77 | </li> | |
| 78 | 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 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 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 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 86 | <li> | |
| 87 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 87 | + | <a href="/diffs/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 88 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 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 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 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 | 96 | </a> | |
| 97 | 97 | </li> | |
| 98 | 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 | 100 | </a> | |
| 101 | 101 | </li> | |
| 102 | 102 | </ul> | |
| @@ -434,14 +434,16 @@ And <b>html</b> & "quotes".</pre> | |||
| 434 | 434 | </div> | |
| 435 | 435 | <div> | |
| 436 | 436 | <dt> | |
| 437 | - | <kbd>s</kbd> | |
| 438 | - | <kbd>t</kbd> | |
| 439 | - | <kbd>c</kbd> | |
| 440 | - | <kbd>r</kbd> | |
| 441 | - | <kbd>m</kbd> | |
| 442 | - | <kbd>i</kbd> | |
| 437 | + | <kbd>g s</kbd> | |
| 438 | + | <kbd>g f</kbd> | |
| 439 | + | <kbd>g c</kbd> | |
| 440 | + | <kbd>g b</kbd> | |
| 441 | + | <kbd>g m</kbd> | |
| 442 | + | <kbd>g i</kbd> | |
| 443 | + | <kbd>g r</kbd> | |
| 444 | + | <kbd>g a</kbd> | |
| 443 | 445 | </dt> | |
| 444 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 446 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 445 | 447 | </div> | |
| 446 | 448 | <div> | |
| 447 | 449 | <dt> | |
| @@ -540,45 +542,45 @@ And <b>html</b> & "quotes".</pre> | |||
| 540 | 542 | <span class="k">go</span> | |
| 541 | 543 | </a> | |
| 542 | 544 | </li> | |
| 543 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 545 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 544 | 546 | <a href="/diffs" tabindex="-1"> | |
| 545 | - | <span class="lbl">summary</span> | |
| 547 | + | <span class="lbl">summary (g s)</span> | |
| 546 | 548 | <span class="k">section</span> | |
| 547 | 549 | </a> | |
| 548 | 550 | </li> | |
| 549 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 551 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 550 | 552 | <a href="/diffs/tree/main" tabindex="-1"> | |
| 551 | - | <span class="lbl">files</span> | |
| 553 | + | <span class="lbl">files (g f)</span> | |
| 552 | 554 | <span class="k">section</span> | |
| 553 | 555 | </a> | |
| 554 | 556 | </li> | |
| 555 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 557 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 556 | 558 | <a href="/diffs/commits/main" tabindex="-1"> | |
| 557 | - | <span class="lbl">commits</span> | |
| 559 | + | <span class="lbl">commits (g c)</span> | |
| 558 | 560 | <span class="k">section</span> | |
| 559 | 561 | </a> | |
| 560 | 562 | </li> | |
| 561 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 563 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 562 | 564 | <a href="/diffs/refs" tabindex="-1"> | |
| 563 | - | <span class="lbl">branches & tags</span> | |
| 565 | + | <span class="lbl">branches & tags (g b)</span> | |
| 564 | 566 | <span class="k">section</span> | |
| 565 | 567 | </a> | |
| 566 | 568 | </li> | |
| 567 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 569 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 568 | 570 | <a href="/diffs/merge-requests" tabindex="-1"> | |
| 569 | - | <span class="lbl">merge requests</span> | |
| 571 | + | <span class="lbl">merge requests (g m)</span> | |
| 570 | 572 | <span class="k">section</span> | |
| 571 | 573 | </a> | |
| 572 | 574 | </li> | |
| 573 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 575 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 574 | 576 | <a href="/diffs/releases" tabindex="-1"> | |
| 575 | - | <span class="lbl">releases</span> | |
| 577 | + | <span class="lbl">releases (g r)</span> | |
| 576 | 578 | <span class="k">section</span> | |
| 577 | 579 | </a> | |
| 578 | 580 | </li> | |
| 579 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 581 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 580 | 582 | <a href="/diffs/actions" tabindex="-1"> | |
| 581 | - | <span class="lbl">actions</span> | |
| 583 | + | <span class="lbl">actions (g a)</span> | |
| 582 | 584 | <span class="k">section</span> | |
| 583 | 585 | </a> | |
| 584 | 586 | </li> | |
| @@ -631,7 +633,9 @@ And <b>html</b> & "quotes".</pre> | |||
| 631 | 633 | k-filter from:body -> ermite.focusFilter(); | |
| 632 | 634 | k-gh from:body -> ermite.go('.brand'); | |
| 633 | 635 | k-s from:body -> ermite.tab('s'); | |
| 634 | - | k-t from:body -> ermite.tab('t'); | |
| 636 | + | k-f from:body -> ermite.tab('f'); | |
| 637 | + | k-b from:body -> ermite.tab('b'); | |
| 638 | + | k-a from:body -> ermite.tab('a'); | |
| 635 | 639 | k-c from:body -> ermite.tab('c'); | |
| 636 | 640 | k-r from:body -> ermite.tab('r'); | |
| 637 | 641 | k-m from:body -> ermite.tab('m'); | |
| @@ -647,7 +651,6 @@ And <b>html</b> & "quotes".</pre> | |||
| 647 | 651 | k-permalink from:body -> ermite.permalink(); | |
| 648 | 652 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 649 | 653 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 650 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 651 | 654 | k-keys from:body -> ermite.toggleKeys()"> | |
| 652 | 655 | </div> | |
| 653 | 656 | </body> | |
M internal/app/testdata/web/commit-rename.html +33 -30
| @@ -72,31 +72,31 @@ | |||
| 72 | 72 | <nav class="tabs" aria-label="Sections"> | |
| 73 | 73 | <ul> | |
| 74 | 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 | 76 | </a> | |
| 77 | 77 | </li> | |
| 78 | 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 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 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 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 86 | <li> | |
| 87 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 87 | + | <a href="/diffs/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 88 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 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 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 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 | 96 | </a> | |
| 97 | 97 | </li> | |
| 98 | 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 | 100 | </a> | |
| 101 | 101 | </li> | |
| 102 | 102 | </ul> | |
| @@ -338,14 +338,16 @@ | |||
| 338 | 338 | </div> | |
| 339 | 339 | <div> | |
| 340 | 340 | <dt> | |
| 341 | - | <kbd>s</kbd> | |
| 342 | - | <kbd>t</kbd> | |
| 343 | - | <kbd>c</kbd> | |
| 344 | - | <kbd>r</kbd> | |
| 345 | - | <kbd>m</kbd> | |
| 346 | - | <kbd>i</kbd> | |
| 341 | + | <kbd>g s</kbd> | |
| 342 | + | <kbd>g f</kbd> | |
| 343 | + | <kbd>g c</kbd> | |
| 344 | + | <kbd>g b</kbd> | |
| 345 | + | <kbd>g m</kbd> | |
| 346 | + | <kbd>g i</kbd> | |
| 347 | + | <kbd>g r</kbd> | |
| 348 | + | <kbd>g a</kbd> | |
| 347 | 349 | </dt> | |
| 348 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 350 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 349 | 351 | </div> | |
| 350 | 352 | <div> | |
| 351 | 353 | <dt> | |
| @@ -444,45 +446,45 @@ | |||
| 444 | 446 | <span class="k">go</span> | |
| 445 | 447 | </a> | |
| 446 | 448 | </li> | |
| 447 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 449 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 448 | 450 | <a href="/diffs" tabindex="-1"> | |
| 449 | - | <span class="lbl">summary</span> | |
| 451 | + | <span class="lbl">summary (g s)</span> | |
| 450 | 452 | <span class="k">section</span> | |
| 451 | 453 | </a> | |
| 452 | 454 | </li> | |
| 453 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 455 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 454 | 456 | <a href="/diffs/tree/main" tabindex="-1"> | |
| 455 | - | <span class="lbl">files</span> | |
| 457 | + | <span class="lbl">files (g f)</span> | |
| 456 | 458 | <span class="k">section</span> | |
| 457 | 459 | </a> | |
| 458 | 460 | </li> | |
| 459 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 461 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 460 | 462 | <a href="/diffs/commits/main" tabindex="-1"> | |
| 461 | - | <span class="lbl">commits</span> | |
| 463 | + | <span class="lbl">commits (g c)</span> | |
| 462 | 464 | <span class="k">section</span> | |
| 463 | 465 | </a> | |
| 464 | 466 | </li> | |
| 465 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 467 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 466 | 468 | <a href="/diffs/refs" tabindex="-1"> | |
| 467 | - | <span class="lbl">branches & tags</span> | |
| 469 | + | <span class="lbl">branches & tags (g b)</span> | |
| 468 | 470 | <span class="k">section</span> | |
| 469 | 471 | </a> | |
| 470 | 472 | </li> | |
| 471 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 473 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 472 | 474 | <a href="/diffs/merge-requests" tabindex="-1"> | |
| 473 | - | <span class="lbl">merge requests</span> | |
| 475 | + | <span class="lbl">merge requests (g m)</span> | |
| 474 | 476 | <span class="k">section</span> | |
| 475 | 477 | </a> | |
| 476 | 478 | </li> | |
| 477 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 479 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 478 | 480 | <a href="/diffs/releases" tabindex="-1"> | |
| 479 | - | <span class="lbl">releases</span> | |
| 481 | + | <span class="lbl">releases (g r)</span> | |
| 480 | 482 | <span class="k">section</span> | |
| 481 | 483 | </a> | |
| 482 | 484 | </li> | |
| 483 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 485 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 484 | 486 | <a href="/diffs/actions" tabindex="-1"> | |
| 485 | - | <span class="lbl">actions</span> | |
| 487 | + | <span class="lbl">actions (g a)</span> | |
| 486 | 488 | <span class="k">section</span> | |
| 487 | 489 | </a> | |
| 488 | 490 | </li> | |
| @@ -535,7 +537,9 @@ | |||
| 535 | 537 | k-filter from:body -> ermite.focusFilter(); | |
| 536 | 538 | k-gh from:body -> ermite.go('.brand'); | |
| 537 | 539 | k-s from:body -> ermite.tab('s'); | |
| 538 | - | k-t from:body -> ermite.tab('t'); | |
| 540 | + | k-f from:body -> ermite.tab('f'); | |
| 541 | + | k-b from:body -> ermite.tab('b'); | |
| 542 | + | k-a from:body -> ermite.tab('a'); | |
| 539 | 543 | k-c from:body -> ermite.tab('c'); | |
| 540 | 544 | k-r from:body -> ermite.tab('r'); | |
| 541 | 545 | k-m from:body -> ermite.tab('m'); | |
| @@ -551,7 +555,6 @@ | |||
| 551 | 555 | k-permalink from:body -> ermite.permalink(); | |
| 552 | 556 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 553 | 557 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 554 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 555 | 558 | k-keys from:body -> ermite.toggleKeys()"> | |
| 556 | 559 | </div> | |
| 557 | 560 | </body> | |
M internal/app/testdata/web/commit-root.html +33 -30
| @@ -72,31 +72,31 @@ | |||
| 72 | 72 | <nav class="tabs" aria-label="Sections"> | |
| 73 | 73 | <ul> | |
| 74 | 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 | 76 | </a> | |
| 77 | 77 | </li> | |
| 78 | 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 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 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 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 86 | <li> | |
| 87 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 87 | + | <a href="/diffs/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 88 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 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 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 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 | 96 | </a> | |
| 97 | 97 | </li> | |
| 98 | 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 | 100 | </a> | |
| 101 | 101 | </li> | |
| 102 | 102 | </ul> | |
| @@ -657,14 +657,16 @@ | |||
| 657 | 657 | </div> | |
| 658 | 658 | <div> | |
| 659 | 659 | <dt> | |
| 660 | - | <kbd>s</kbd> | |
| 661 | - | <kbd>t</kbd> | |
| 662 | - | <kbd>c</kbd> | |
| 663 | - | <kbd>r</kbd> | |
| 664 | - | <kbd>m</kbd> | |
| 665 | - | <kbd>i</kbd> | |
| 660 | + | <kbd>g s</kbd> | |
| 661 | + | <kbd>g f</kbd> | |
| 662 | + | <kbd>g c</kbd> | |
| 663 | + | <kbd>g b</kbd> | |
| 664 | + | <kbd>g m</kbd> | |
| 665 | + | <kbd>g i</kbd> | |
| 666 | + | <kbd>g r</kbd> | |
| 667 | + | <kbd>g a</kbd> | |
| 666 | 668 | </dt> | |
| 667 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 669 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 668 | 670 | </div> | |
| 669 | 671 | <div> | |
| 670 | 672 | <dt> | |
| @@ -763,45 +765,45 @@ | |||
| 763 | 765 | <span class="k">go</span> | |
| 764 | 766 | </a> | |
| 765 | 767 | </li> | |
| 766 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 768 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 767 | 769 | <a href="/diffs" tabindex="-1"> | |
| 768 | - | <span class="lbl">summary</span> | |
| 770 | + | <span class="lbl">summary (g s)</span> | |
| 769 | 771 | <span class="k">section</span> | |
| 770 | 772 | </a> | |
| 771 | 773 | </li> | |
| 772 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 774 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 773 | 775 | <a href="/diffs/tree/main" tabindex="-1"> | |
| 774 | - | <span class="lbl">files</span> | |
| 776 | + | <span class="lbl">files (g f)</span> | |
| 775 | 777 | <span class="k">section</span> | |
| 776 | 778 | </a> | |
| 777 | 779 | </li> | |
| 778 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 780 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 779 | 781 | <a href="/diffs/commits/main" tabindex="-1"> | |
| 780 | - | <span class="lbl">commits</span> | |
| 782 | + | <span class="lbl">commits (g c)</span> | |
| 781 | 783 | <span class="k">section</span> | |
| 782 | 784 | </a> | |
| 783 | 785 | </li> | |
| 784 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 786 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 785 | 787 | <a href="/diffs/refs" tabindex="-1"> | |
| 786 | - | <span class="lbl">branches & tags</span> | |
| 788 | + | <span class="lbl">branches & tags (g b)</span> | |
| 787 | 789 | <span class="k">section</span> | |
| 788 | 790 | </a> | |
| 789 | 791 | </li> | |
| 790 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 792 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 791 | 793 | <a href="/diffs/merge-requests" tabindex="-1"> | |
| 792 | - | <span class="lbl">merge requests</span> | |
| 794 | + | <span class="lbl">merge requests (g m)</span> | |
| 793 | 795 | <span class="k">section</span> | |
| 794 | 796 | </a> | |
| 795 | 797 | </li> | |
| 796 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 798 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 797 | 799 | <a href="/diffs/releases" tabindex="-1"> | |
| 798 | - | <span class="lbl">releases</span> | |
| 800 | + | <span class="lbl">releases (g r)</span> | |
| 799 | 801 | <span class="k">section</span> | |
| 800 | 802 | </a> | |
| 801 | 803 | </li> | |
| 802 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 804 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 803 | 805 | <a href="/diffs/actions" tabindex="-1"> | |
| 804 | - | <span class="lbl">actions</span> | |
| 806 | + | <span class="lbl">actions (g a)</span> | |
| 805 | 807 | <span class="k">section</span> | |
| 806 | 808 | </a> | |
| 807 | 809 | </li> | |
| @@ -854,7 +856,9 @@ | |||
| 854 | 856 | k-filter from:body -> ermite.focusFilter(); | |
| 855 | 857 | k-gh from:body -> ermite.go('.brand'); | |
| 856 | 858 | k-s from:body -> ermite.tab('s'); | |
| 857 | - | k-t from:body -> ermite.tab('t'); | |
| 859 | + | k-f from:body -> ermite.tab('f'); | |
| 860 | + | k-b from:body -> ermite.tab('b'); | |
| 861 | + | k-a from:body -> ermite.tab('a'); | |
| 858 | 862 | k-c from:body -> ermite.tab('c'); | |
| 859 | 863 | k-r from:body -> ermite.tab('r'); | |
| 860 | 864 | k-m from:body -> ermite.tab('m'); | |
| @@ -870,7 +874,6 @@ | |||
| 870 | 874 | k-permalink from:body -> ermite.permalink(); | |
| 871 | 875 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 872 | 876 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 873 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 874 | 877 | k-keys from:body -> ermite.toggleKeys()"> | |
| 875 | 878 | </div> | |
| 876 | 879 | </body> | |
M internal/app/testdata/web/commit-trojan.html +33 -30
| @@ -72,31 +72,31 @@ | |||
| 72 | 72 | <nav class="tabs" aria-label="Sections"> | |
| 73 | 73 | <ul> | |
| 74 | 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 | 76 | </a> | |
| 77 | 77 | </li> | |
| 78 | 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 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 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 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 86 | <li> | |
| 87 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 87 | + | <a href="/diffs/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 88 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 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 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 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 | 96 | </a> | |
| 97 | 97 | </li> | |
| 98 | 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 | 100 | </a> | |
| 101 | 101 | </li> | |
| 102 | 102 | </ul> | |
| @@ -486,14 +486,16 @@ | |||
| 486 | 486 | </div> | |
| 487 | 487 | <div> | |
| 488 | 488 | <dt> | |
| 489 | - | <kbd>s</kbd> | |
| 490 | - | <kbd>t</kbd> | |
| 491 | - | <kbd>c</kbd> | |
| 492 | - | <kbd>r</kbd> | |
| 493 | - | <kbd>m</kbd> | |
| 494 | - | <kbd>i</kbd> | |
| 489 | + | <kbd>g s</kbd> | |
| 490 | + | <kbd>g f</kbd> | |
| 491 | + | <kbd>g c</kbd> | |
| 492 | + | <kbd>g b</kbd> | |
| 493 | + | <kbd>g m</kbd> | |
| 494 | + | <kbd>g i</kbd> | |
| 495 | + | <kbd>g r</kbd> | |
| 496 | + | <kbd>g a</kbd> | |
| 495 | 497 | </dt> | |
| 496 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 498 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 497 | 499 | </div> | |
| 498 | 500 | <div> | |
| 499 | 501 | <dt> | |
| @@ -592,45 +594,45 @@ | |||
| 592 | 594 | <span class="k">go</span> | |
| 593 | 595 | </a> | |
| 594 | 596 | </li> | |
| 595 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 597 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 596 | 598 | <a href="/diffs" tabindex="-1"> | |
| 597 | - | <span class="lbl">summary</span> | |
| 599 | + | <span class="lbl">summary (g s)</span> | |
| 598 | 600 | <span class="k">section</span> | |
| 599 | 601 | </a> | |
| 600 | 602 | </li> | |
| 601 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 603 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 602 | 604 | <a href="/diffs/tree/main" tabindex="-1"> | |
| 603 | - | <span class="lbl">files</span> | |
| 605 | + | <span class="lbl">files (g f)</span> | |
| 604 | 606 | <span class="k">section</span> | |
| 605 | 607 | </a> | |
| 606 | 608 | </li> | |
| 607 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 609 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 608 | 610 | <a href="/diffs/commits/main" tabindex="-1"> | |
| 609 | - | <span class="lbl">commits</span> | |
| 611 | + | <span class="lbl">commits (g c)</span> | |
| 610 | 612 | <span class="k">section</span> | |
| 611 | 613 | </a> | |
| 612 | 614 | </li> | |
| 613 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 615 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 614 | 616 | <a href="/diffs/refs" tabindex="-1"> | |
| 615 | - | <span class="lbl">branches & tags</span> | |
| 617 | + | <span class="lbl">branches & tags (g b)</span> | |
| 616 | 618 | <span class="k">section</span> | |
| 617 | 619 | </a> | |
| 618 | 620 | </li> | |
| 619 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 621 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 620 | 622 | <a href="/diffs/merge-requests" tabindex="-1"> | |
| 621 | - | <span class="lbl">merge requests</span> | |
| 623 | + | <span class="lbl">merge requests (g m)</span> | |
| 622 | 624 | <span class="k">section</span> | |
| 623 | 625 | </a> | |
| 624 | 626 | </li> | |
| 625 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 627 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 626 | 628 | <a href="/diffs/releases" tabindex="-1"> | |
| 627 | - | <span class="lbl">releases</span> | |
| 629 | + | <span class="lbl">releases (g r)</span> | |
| 628 | 630 | <span class="k">section</span> | |
| 629 | 631 | </a> | |
| 630 | 632 | </li> | |
| 631 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 633 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 632 | 634 | <a href="/diffs/actions" tabindex="-1"> | |
| 633 | - | <span class="lbl">actions</span> | |
| 635 | + | <span class="lbl">actions (g a)</span> | |
| 634 | 636 | <span class="k">section</span> | |
| 635 | 637 | </a> | |
| 636 | 638 | </li> | |
| @@ -683,7 +685,9 @@ | |||
| 683 | 685 | k-filter from:body -> ermite.focusFilter(); | |
| 684 | 686 | k-gh from:body -> ermite.go('.brand'); | |
| 685 | 687 | k-s from:body -> ermite.tab('s'); | |
| 686 | - | k-t from:body -> ermite.tab('t'); | |
| 688 | + | k-f from:body -> ermite.tab('f'); | |
| 689 | + | k-b from:body -> ermite.tab('b'); | |
| 690 | + | k-a from:body -> ermite.tab('a'); | |
| 687 | 691 | k-c from:body -> ermite.tab('c'); | |
| 688 | 692 | k-r from:body -> ermite.tab('r'); | |
| 689 | 693 | k-m from:body -> ermite.tab('m'); | |
| @@ -699,7 +703,6 @@ | |||
| 699 | 703 | k-permalink from:body -> ermite.permalink(); | |
| 700 | 704 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 701 | 705 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 702 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 703 | 706 | k-keys from:body -> ermite.toggleKeys()"> | |
| 704 | 707 | </div> | |
| 705 | 708 | </body> | |
M internal/app/testdata/web/commits-page2.html +33 -30
| @@ -70,31 +70,31 @@ | |||
| 70 | 70 | <nav class="tabs" aria-label="Sections"> | |
| 71 | 71 | <ul> | |
| 72 | 72 | <li> | |
| 73 | - | <a href="/long" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 73 | + | <a href="/long" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd> | |
| 74 | 74 | </a> | |
| 75 | 75 | </li> | |
| 76 | 76 | <li> | |
| 77 | - | <a href="/long/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> | |
| 77 | + | <a href="/long/tree/main" data-key="g f">Files <kbd aria-hidden="true">g f</kbd> | |
| 78 | 78 | </a> | |
| 79 | 79 | </li> | |
| 80 | 80 | <li> | |
| 81 | - | <a href="/long/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> | |
| 81 | + | <a href="/long/commits/main" data-key="g c" aria-current="page">Commits <kbd aria-hidden="true">g c</kbd> | |
| 82 | 82 | </a> | |
| 83 | 83 | </li> | |
| 84 | 84 | <li> | |
| 85 | - | <a href="/long/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 85 | + | <a href="/long/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 86 | 86 | </a> | |
| 87 | 87 | </li> | |
| 88 | 88 | <li> | |
| 89 | - | <a href="/long/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 89 | + | <a href="/long/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd> | |
| 90 | 90 | </a> | |
| 91 | 91 | </li> | |
| 92 | 92 | <li> | |
| 93 | - | <a href="/long/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd> | |
| 93 | + | <a href="/long/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd> | |
| 94 | 94 | </a> | |
| 95 | 95 | </li> | |
| 96 | 96 | <li> | |
| 97 | - | <a href="/long/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 97 | + | <a href="/long/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd> | |
| 98 | 98 | </a> | |
| 99 | 99 | </li> | |
| 100 | 100 | </ul> | |
| @@ -253,14 +253,16 @@ | |||
| 253 | 253 | </div> | |
| 254 | 254 | <div> | |
| 255 | 255 | <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> | |
| 262 | 264 | </dt> | |
| 263 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 265 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 264 | 266 | </div> | |
| 265 | 267 | <div> | |
| 266 | 268 | <dt> | |
| @@ -351,45 +353,45 @@ | |||
| 351 | 353 | <span class="k">go</span> | |
| 352 | 354 | </a> | |
| 353 | 355 | </li> | |
| 354 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 356 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 355 | 357 | <a href="/long" tabindex="-1"> | |
| 356 | - | <span class="lbl">summary</span> | |
| 358 | + | <span class="lbl">summary (g s)</span> | |
| 357 | 359 | <span class="k">section</span> | |
| 358 | 360 | </a> | |
| 359 | 361 | </li> | |
| 360 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 362 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 361 | 363 | <a href="/long/tree/main" tabindex="-1"> | |
| 362 | - | <span class="lbl">files</span> | |
| 364 | + | <span class="lbl">files (g f)</span> | |
| 363 | 365 | <span class="k">section</span> | |
| 364 | 366 | </a> | |
| 365 | 367 | </li> | |
| 366 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 368 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 367 | 369 | <a href="/long/commits/main" tabindex="-1"> | |
| 368 | - | <span class="lbl">commits</span> | |
| 370 | + | <span class="lbl">commits (g c)</span> | |
| 369 | 371 | <span class="k">section</span> | |
| 370 | 372 | </a> | |
| 371 | 373 | </li> | |
| 372 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 374 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 373 | 375 | <a href="/long/refs" tabindex="-1"> | |
| 374 | - | <span class="lbl">branches & tags</span> | |
| 376 | + | <span class="lbl">branches & tags (g b)</span> | |
| 375 | 377 | <span class="k">section</span> | |
| 376 | 378 | </a> | |
| 377 | 379 | </li> | |
| 378 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 380 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 379 | 381 | <a href="/long/merge-requests" tabindex="-1"> | |
| 380 | - | <span class="lbl">merge requests</span> | |
| 382 | + | <span class="lbl">merge requests (g m)</span> | |
| 381 | 383 | <span class="k">section</span> | |
| 382 | 384 | </a> | |
| 383 | 385 | </li> | |
| 384 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 386 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 385 | 387 | <a href="/long/releases" tabindex="-1"> | |
| 386 | - | <span class="lbl">releases</span> | |
| 388 | + | <span class="lbl">releases (g r)</span> | |
| 387 | 389 | <span class="k">section</span> | |
| 388 | 390 | </a> | |
| 389 | 391 | </li> | |
| 390 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 392 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 391 | 393 | <a href="/long/actions" tabindex="-1"> | |
| 392 | - | <span class="lbl">actions</span> | |
| 394 | + | <span class="lbl">actions (g a)</span> | |
| 393 | 395 | <span class="k">section</span> | |
| 394 | 396 | </a> | |
| 395 | 397 | </li> | |
| @@ -436,7 +438,9 @@ | |||
| 436 | 438 | k-filter from:body -> ermite.focusFilter(); | |
| 437 | 439 | k-gh from:body -> ermite.go('.brand'); | |
| 438 | 440 | k-s from:body -> ermite.tab('s'); | |
| 439 | - | k-t from:body -> ermite.tab('t'); | |
| 441 | + | k-f from:body -> ermite.tab('f'); | |
| 442 | + | k-b from:body -> ermite.tab('b'); | |
| 443 | + | k-a from:body -> ermite.tab('a'); | |
| 440 | 444 | k-c from:body -> ermite.tab('c'); | |
| 441 | 445 | k-r from:body -> ermite.tab('r'); | |
| 442 | 446 | k-m from:body -> ermite.tab('m'); | |
| @@ -452,7 +456,6 @@ | |||
| 452 | 456 | k-permalink from:body -> ermite.permalink(); | |
| 453 | 457 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 454 | 458 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 455 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 456 | 459 | k-keys from:body -> ermite.toggleKeys()"> | |
| 457 | 460 | </div> | |
| 458 | 461 | </body> | |
M internal/app/testdata/web/commits-path.html +33 -30
| @@ -73,31 +73,31 @@ | |||
| 73 | 73 | <nav class="tabs" aria-label="Sections"> | |
| 74 | 74 | <ul> | |
| 75 | 75 | <li> | |
| 76 | - | <a href="/long" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 76 | + | <a href="/long" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd> | |
| 77 | 77 | </a> | |
| 78 | 78 | </li> | |
| 79 | 79 | <li> | |
| 80 | - | <a href="/long/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> | |
| 80 | + | <a href="/long/tree/main" data-key="g f">Files <kbd aria-hidden="true">g f</kbd> | |
| 81 | 81 | </a> | |
| 82 | 82 | </li> | |
| 83 | 83 | <li> | |
| 84 | - | <a href="/long/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> | |
| 84 | + | <a href="/long/commits/main" data-key="g c" aria-current="page">Commits <kbd aria-hidden="true">g c</kbd> | |
| 85 | 85 | </a> | |
| 86 | 86 | </li> | |
| 87 | 87 | <li> | |
| 88 | - | <a href="/long/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 88 | + | <a href="/long/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 89 | 89 | </a> | |
| 90 | 90 | </li> | |
| 91 | 91 | <li> | |
| 92 | - | <a href="/long/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 92 | + | <a href="/long/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd> | |
| 93 | 93 | </a> | |
| 94 | 94 | </li> | |
| 95 | 95 | <li> | |
| 96 | - | <a href="/long/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd> | |
| 96 | + | <a href="/long/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd> | |
| 97 | 97 | </a> | |
| 98 | 98 | </li> | |
| 99 | 99 | <li> | |
| 100 | - | <a href="/long/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 100 | + | <a href="/long/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd> | |
| 101 | 101 | </a> | |
| 102 | 102 | </li> | |
| 103 | 103 | </ul> | |
| @@ -268,14 +268,16 @@ | |||
| 268 | 268 | </div> | |
| 269 | 269 | <div> | |
| 270 | 270 | <dt> | |
| 271 | - | <kbd>s</kbd> | |
| 272 | - | <kbd>t</kbd> | |
| 273 | - | <kbd>c</kbd> | |
| 274 | - | <kbd>r</kbd> | |
| 275 | - | <kbd>m</kbd> | |
| 276 | - | <kbd>i</kbd> | |
| 271 | + | <kbd>g s</kbd> | |
| 272 | + | <kbd>g f</kbd> | |
| 273 | + | <kbd>g c</kbd> | |
| 274 | + | <kbd>g b</kbd> | |
| 275 | + | <kbd>g m</kbd> | |
| 276 | + | <kbd>g i</kbd> | |
| 277 | + | <kbd>g r</kbd> | |
| 278 | + | <kbd>g a</kbd> | |
| 277 | 279 | </dt> | |
| 278 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 280 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 279 | 281 | </div> | |
| 280 | 282 | <div> | |
| 281 | 283 | <dt> | |
| @@ -366,45 +368,45 @@ | |||
| 366 | 368 | <span class="k">go</span> | |
| 367 | 369 | </a> | |
| 368 | 370 | </li> | |
| 369 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 371 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 370 | 372 | <a href="/long" tabindex="-1"> | |
| 371 | - | <span class="lbl">summary</span> | |
| 373 | + | <span class="lbl">summary (g s)</span> | |
| 372 | 374 | <span class="k">section</span> | |
| 373 | 375 | </a> | |
| 374 | 376 | </li> | |
| 375 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 377 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 376 | 378 | <a href="/long/tree/main" tabindex="-1"> | |
| 377 | - | <span class="lbl">files</span> | |
| 379 | + | <span class="lbl">files (g f)</span> | |
| 378 | 380 | <span class="k">section</span> | |
| 379 | 381 | </a> | |
| 380 | 382 | </li> | |
| 381 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 383 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 382 | 384 | <a href="/long/commits/main" tabindex="-1"> | |
| 383 | - | <span class="lbl">commits</span> | |
| 385 | + | <span class="lbl">commits (g c)</span> | |
| 384 | 386 | <span class="k">section</span> | |
| 385 | 387 | </a> | |
| 386 | 388 | </li> | |
| 387 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 389 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 388 | 390 | <a href="/long/refs" tabindex="-1"> | |
| 389 | - | <span class="lbl">branches & tags</span> | |
| 391 | + | <span class="lbl">branches & tags (g b)</span> | |
| 390 | 392 | <span class="k">section</span> | |
| 391 | 393 | </a> | |
| 392 | 394 | </li> | |
| 393 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 395 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 394 | 396 | <a href="/long/merge-requests" tabindex="-1"> | |
| 395 | - | <span class="lbl">merge requests</span> | |
| 397 | + | <span class="lbl">merge requests (g m)</span> | |
| 396 | 398 | <span class="k">section</span> | |
| 397 | 399 | </a> | |
| 398 | 400 | </li> | |
| 399 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 401 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 400 | 402 | <a href="/long/releases" tabindex="-1"> | |
| 401 | - | <span class="lbl">releases</span> | |
| 403 | + | <span class="lbl">releases (g r)</span> | |
| 402 | 404 | <span class="k">section</span> | |
| 403 | 405 | </a> | |
| 404 | 406 | </li> | |
| 405 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 407 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 406 | 408 | <a href="/long/actions" tabindex="-1"> | |
| 407 | - | <span class="lbl">actions</span> | |
| 409 | + | <span class="lbl">actions (g a)</span> | |
| 408 | 410 | <span class="k">section</span> | |
| 409 | 411 | </a> | |
| 410 | 412 | </li> | |
| @@ -451,7 +453,9 @@ | |||
| 451 | 453 | k-filter from:body -> ermite.focusFilter(); | |
| 452 | 454 | k-gh from:body -> ermite.go('.brand'); | |
| 453 | 455 | k-s from:body -> ermite.tab('s'); | |
| 454 | - | k-t from:body -> ermite.tab('t'); | |
| 456 | + | k-f from:body -> ermite.tab('f'); | |
| 457 | + | k-b from:body -> ermite.tab('b'); | |
| 458 | + | k-a from:body -> ermite.tab('a'); | |
| 455 | 459 | k-c from:body -> ermite.tab('c'); | |
| 456 | 460 | k-r from:body -> ermite.tab('r'); | |
| 457 | 461 | k-m from:body -> ermite.tab('m'); | |
| @@ -467,7 +471,6 @@ | |||
| 467 | 471 | k-permalink from:body -> ermite.permalink(); | |
| 468 | 472 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 469 | 473 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 470 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 471 | 474 | k-keys from:body -> ermite.toggleKeys()"> | |
| 472 | 475 | </div> | |
| 473 | 476 | </body> | |
M internal/app/testdata/web/commits-root.html +33 -30
| @@ -70,31 +70,31 @@ | |||
| 70 | 70 | <nav class="tabs" aria-label="Sections"> | |
| 71 | 71 | <ul> | |
| 72 | 72 | <li> | |
| 73 | - | <a href="/long" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 73 | + | <a href="/long" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd> | |
| 74 | 74 | </a> | |
| 75 | 75 | </li> | |
| 76 | 76 | <li> | |
| 77 | - | <a href="/long/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> | |
| 77 | + | <a href="/long/tree/main" data-key="g f">Files <kbd aria-hidden="true">g f</kbd> | |
| 78 | 78 | </a> | |
| 79 | 79 | </li> | |
| 80 | 80 | <li> | |
| 81 | - | <a href="/long/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> | |
| 81 | + | <a href="/long/commits/main" data-key="g c" aria-current="page">Commits <kbd aria-hidden="true">g c</kbd> | |
| 82 | 82 | </a> | |
| 83 | 83 | </li> | |
| 84 | 84 | <li> | |
| 85 | - | <a href="/long/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 85 | + | <a href="/long/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 86 | 86 | </a> | |
| 87 | 87 | </li> | |
| 88 | 88 | <li> | |
| 89 | - | <a href="/long/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 89 | + | <a href="/long/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd> | |
| 90 | 90 | </a> | |
| 91 | 91 | </li> | |
| 92 | 92 | <li> | |
| 93 | - | <a href="/long/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd> | |
| 93 | + | <a href="/long/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd> | |
| 94 | 94 | </a> | |
| 95 | 95 | </li> | |
| 96 | 96 | <li> | |
| 97 | - | <a href="/long/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 97 | + | <a href="/long/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd> | |
| 98 | 98 | </a> | |
| 99 | 99 | </li> | |
| 100 | 100 | </ul> | |
| @@ -1058,14 +1058,16 @@ | |||
| 1058 | 1058 | </div> | |
| 1059 | 1059 | <div> | |
| 1060 | 1060 | <dt> | |
| 1061 | - | <kbd>s</kbd> | |
| 1062 | - | <kbd>t</kbd> | |
| 1063 | - | <kbd>c</kbd> | |
| 1064 | - | <kbd>r</kbd> | |
| 1065 | - | <kbd>m</kbd> | |
| 1066 | - | <kbd>i</kbd> | |
| 1061 | + | <kbd>g s</kbd> | |
| 1062 | + | <kbd>g f</kbd> | |
| 1063 | + | <kbd>g c</kbd> | |
| 1064 | + | <kbd>g b</kbd> | |
| 1065 | + | <kbd>g m</kbd> | |
| 1066 | + | <kbd>g i</kbd> | |
| 1067 | + | <kbd>g r</kbd> | |
| 1068 | + | <kbd>g a</kbd> | |
| 1067 | 1069 | </dt> | |
| 1068 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 1070 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 1069 | 1071 | </div> | |
| 1070 | 1072 | <div> | |
| 1071 | 1073 | <dt> | |
| @@ -1156,45 +1158,45 @@ | |||
| 1156 | 1158 | <span class="k">go</span> | |
| 1157 | 1159 | </a> | |
| 1158 | 1160 | </li> | |
| 1159 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 1161 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 1160 | 1162 | <a href="/long" tabindex="-1"> | |
| 1161 | - | <span class="lbl">summary</span> | |
| 1163 | + | <span class="lbl">summary (g s)</span> | |
| 1162 | 1164 | <span class="k">section</span> | |
| 1163 | 1165 | </a> | |
| 1164 | 1166 | </li> | |
| 1165 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 1167 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 1166 | 1168 | <a href="/long/tree/main" tabindex="-1"> | |
| 1167 | - | <span class="lbl">files</span> | |
| 1169 | + | <span class="lbl">files (g f)</span> | |
| 1168 | 1170 | <span class="k">section</span> | |
| 1169 | 1171 | </a> | |
| 1170 | 1172 | </li> | |
| 1171 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 1173 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 1172 | 1174 | <a href="/long/commits/main" tabindex="-1"> | |
| 1173 | - | <span class="lbl">commits</span> | |
| 1175 | + | <span class="lbl">commits (g c)</span> | |
| 1174 | 1176 | <span class="k">section</span> | |
| 1175 | 1177 | </a> | |
| 1176 | 1178 | </li> | |
| 1177 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 1179 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 1178 | 1180 | <a href="/long/refs" tabindex="-1"> | |
| 1179 | - | <span class="lbl">branches & tags</span> | |
| 1181 | + | <span class="lbl">branches & tags (g b)</span> | |
| 1180 | 1182 | <span class="k">section</span> | |
| 1181 | 1183 | </a> | |
| 1182 | 1184 | </li> | |
| 1183 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 1185 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 1184 | 1186 | <a href="/long/merge-requests" tabindex="-1"> | |
| 1185 | - | <span class="lbl">merge requests</span> | |
| 1187 | + | <span class="lbl">merge requests (g m)</span> | |
| 1186 | 1188 | <span class="k">section</span> | |
| 1187 | 1189 | </a> | |
| 1188 | 1190 | </li> | |
| 1189 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 1191 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 1190 | 1192 | <a href="/long/releases" tabindex="-1"> | |
| 1191 | - | <span class="lbl">releases</span> | |
| 1193 | + | <span class="lbl">releases (g r)</span> | |
| 1192 | 1194 | <span class="k">section</span> | |
| 1193 | 1195 | </a> | |
| 1194 | 1196 | </li> | |
| 1195 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 1197 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 1196 | 1198 | <a href="/long/actions" tabindex="-1"> | |
| 1197 | - | <span class="lbl">actions</span> | |
| 1199 | + | <span class="lbl">actions (g a)</span> | |
| 1198 | 1200 | <span class="k">section</span> | |
| 1199 | 1201 | </a> | |
| 1200 | 1202 | </li> | |
| @@ -1241,7 +1243,9 @@ | |||
| 1241 | 1243 | k-filter from:body -> ermite.focusFilter(); | |
| 1242 | 1244 | k-gh from:body -> ermite.go('.brand'); | |
| 1243 | 1245 | k-s from:body -> ermite.tab('s'); | |
| 1244 | - | k-t from:body -> ermite.tab('t'); | |
| 1246 | + | k-f from:body -> ermite.tab('f'); | |
| 1247 | + | k-b from:body -> ermite.tab('b'); | |
| 1248 | + | k-a from:body -> ermite.tab('a'); | |
| 1245 | 1249 | k-c from:body -> ermite.tab('c'); | |
| 1246 | 1250 | k-r from:body -> ermite.tab('r'); | |
| 1247 | 1251 | k-m from:body -> ermite.tab('m'); | |
| @@ -1257,7 +1261,6 @@ | |||
| 1257 | 1261 | k-permalink from:body -> ermite.permalink(); | |
| 1258 | 1262 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 1259 | 1263 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 1260 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 1261 | 1264 | k-keys from:body -> ermite.toggleKeys()"> | |
| 1262 | 1265 | </div> | |
| 1263 | 1266 | </body> | |
M internal/app/testdata/web/commits-small.html +33 -30
| @@ -70,31 +70,31 @@ | |||
| 70 | 70 | <nav class="tabs" aria-label="Sections"> | |
| 71 | 71 | <ul> | |
| 72 | 72 | <li> | |
| 73 | - | <a href="/diffs" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 73 | + | <a href="/diffs" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd> | |
| 74 | 74 | </a> | |
| 75 | 75 | </li> | |
| 76 | 76 | <li> | |
| 77 | - | <a href="/diffs/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> | |
| 77 | + | <a href="/diffs/tree/main" data-key="g f">Files <kbd aria-hidden="true">g f</kbd> | |
| 78 | 78 | </a> | |
| 79 | 79 | </li> | |
| 80 | 80 | <li> | |
| 81 | - | <a href="/diffs/commits/main" data-key="c" aria-keyshortcuts="c" aria-current="page">Commits <kbd aria-hidden="true">c</kbd> | |
| 81 | + | <a href="/diffs/commits/main" data-key="g c" aria-current="page">Commits <kbd aria-hidden="true">g c</kbd> | |
| 82 | 82 | </a> | |
| 83 | 83 | </li> | |
| 84 | 84 | <li> | |
| 85 | - | <a href="/diffs/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 85 | + | <a href="/diffs/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 86 | 86 | </a> | |
| 87 | 87 | </li> | |
| 88 | 88 | <li> | |
| 89 | - | <a href="/diffs/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 89 | + | <a href="/diffs/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd> | |
| 90 | 90 | </a> | |
| 91 | 91 | </li> | |
| 92 | 92 | <li> | |
| 93 | - | <a href="/diffs/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd> | |
| 93 | + | <a href="/diffs/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd> | |
| 94 | 94 | </a> | |
| 95 | 95 | </li> | |
| 96 | 96 | <li> | |
| 97 | - | <a href="/diffs/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 97 | + | <a href="/diffs/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd> | |
| 98 | 98 | </a> | |
| 99 | 99 | </li> | |
| 100 | 100 | </ul> | |
| @@ -403,14 +403,16 @@ | |||
| 403 | 403 | </div> | |
| 404 | 404 | <div> | |
| 405 | 405 | <dt> | |
| 406 | - | <kbd>s</kbd> | |
| 407 | - | <kbd>t</kbd> | |
| 408 | - | <kbd>c</kbd> | |
| 409 | - | <kbd>r</kbd> | |
| 410 | - | <kbd>m</kbd> | |
| 411 | - | <kbd>i</kbd> | |
| 406 | + | <kbd>g s</kbd> | |
| 407 | + | <kbd>g f</kbd> | |
| 408 | + | <kbd>g c</kbd> | |
| 409 | + | <kbd>g b</kbd> | |
| 410 | + | <kbd>g m</kbd> | |
| 411 | + | <kbd>g i</kbd> | |
| 412 | + | <kbd>g r</kbd> | |
| 413 | + | <kbd>g a</kbd> | |
| 412 | 414 | </dt> | |
| 413 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 415 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 414 | 416 | </div> | |
| 415 | 417 | <div> | |
| 416 | 418 | <dt> | |
| @@ -509,45 +511,45 @@ | |||
| 509 | 511 | <span class="k">go</span> | |
| 510 | 512 | </a> | |
| 511 | 513 | </li> | |
| 512 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 514 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 513 | 515 | <a href="/diffs" tabindex="-1"> | |
| 514 | - | <span class="lbl">summary</span> | |
| 516 | + | <span class="lbl">summary (g s)</span> | |
| 515 | 517 | <span class="k">section</span> | |
| 516 | 518 | </a> | |
| 517 | 519 | </li> | |
| 518 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 520 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 519 | 521 | <a href="/diffs/tree/main" tabindex="-1"> | |
| 520 | - | <span class="lbl">files</span> | |
| 522 | + | <span class="lbl">files (g f)</span> | |
| 521 | 523 | <span class="k">section</span> | |
| 522 | 524 | </a> | |
| 523 | 525 | </li> | |
| 524 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 526 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 525 | 527 | <a href="/diffs/commits/main" tabindex="-1"> | |
| 526 | - | <span class="lbl">commits</span> | |
| 528 | + | <span class="lbl">commits (g c)</span> | |
| 527 | 529 | <span class="k">section</span> | |
| 528 | 530 | </a> | |
| 529 | 531 | </li> | |
| 530 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 532 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 531 | 533 | <a href="/diffs/refs" tabindex="-1"> | |
| 532 | - | <span class="lbl">branches & tags</span> | |
| 534 | + | <span class="lbl">branches & tags (g b)</span> | |
| 533 | 535 | <span class="k">section</span> | |
| 534 | 536 | </a> | |
| 535 | 537 | </li> | |
| 536 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 538 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 537 | 539 | <a href="/diffs/merge-requests" tabindex="-1"> | |
| 538 | - | <span class="lbl">merge requests</span> | |
| 540 | + | <span class="lbl">merge requests (g m)</span> | |
| 539 | 541 | <span class="k">section</span> | |
| 540 | 542 | </a> | |
| 541 | 543 | </li> | |
| 542 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 544 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 543 | 545 | <a href="/diffs/releases" tabindex="-1"> | |
| 544 | - | <span class="lbl">releases</span> | |
| 546 | + | <span class="lbl">releases (g r)</span> | |
| 545 | 547 | <span class="k">section</span> | |
| 546 | 548 | </a> | |
| 547 | 549 | </li> | |
| 548 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 550 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 549 | 551 | <a href="/diffs/actions" tabindex="-1"> | |
| 550 | - | <span class="lbl">actions</span> | |
| 552 | + | <span class="lbl">actions (g a)</span> | |
| 551 | 553 | <span class="k">section</span> | |
| 552 | 554 | </a> | |
| 553 | 555 | </li> | |
| @@ -606,7 +608,9 @@ | |||
| 606 | 608 | k-filter from:body -> ermite.focusFilter(); | |
| 607 | 609 | k-gh from:body -> ermite.go('.brand'); | |
| 608 | 610 | k-s from:body -> ermite.tab('s'); | |
| 609 | - | k-t from:body -> ermite.tab('t'); | |
| 611 | + | k-f from:body -> ermite.tab('f'); | |
| 612 | + | k-b from:body -> ermite.tab('b'); | |
| 613 | + | k-a from:body -> ermite.tab('a'); | |
| 610 | 614 | k-c from:body -> ermite.tab('c'); | |
| 611 | 615 | k-r from:body -> ermite.tab('r'); | |
| 612 | 616 | k-m from:body -> ermite.tab('m'); | |
| @@ -622,7 +626,6 @@ | |||
| 622 | 626 | k-permalink from:body -> ermite.permalink(); | |
| 623 | 627 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 624 | 628 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 625 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 626 | 629 | k-keys from:body -> ermite.toggleKeys()"> | |
| 627 | 630 | </div> | |
| 628 | 631 | </body> | |
M internal/app/testdata/web/empty.html +12 -9
| @@ -171,14 +171,16 @@ | |||
| 171 | 171 | </div> | |
| 172 | 172 | <div> | |
| 173 | 173 | <dt> | |
| 174 | - | <kbd>s</kbd> | |
| 175 | - | <kbd>t</kbd> | |
| 176 | - | <kbd>c</kbd> | |
| 177 | - | <kbd>r</kbd> | |
| 178 | - | <kbd>m</kbd> | |
| 179 | - | <kbd>i</kbd> | |
| 174 | + | <kbd>g s</kbd> | |
| 175 | + | <kbd>g f</kbd> | |
| 176 | + | <kbd>g c</kbd> | |
| 177 | + | <kbd>g b</kbd> | |
| 178 | + | <kbd>g m</kbd> | |
| 179 | + | <kbd>g i</kbd> | |
| 180 | + | <kbd>g r</kbd> | |
| 181 | + | <kbd>g a</kbd> | |
| 180 | 182 | </dt> | |
| 181 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 183 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 182 | 184 | </div> | |
| 183 | 185 | <div> | |
| 184 | 186 | <dt> | |
| @@ -279,7 +281,9 @@ | |||
| 279 | 281 | k-filter from:body -> ermite.focusFilter(); | |
| 280 | 282 | k-gh from:body -> ermite.go('.brand'); | |
| 281 | 283 | k-s from:body -> ermite.tab('s'); | |
| 282 | - | k-t from:body -> ermite.tab('t'); | |
| 284 | + | k-f from:body -> ermite.tab('f'); | |
| 285 | + | k-b from:body -> ermite.tab('b'); | |
| 286 | + | k-a from:body -> ermite.tab('a'); | |
| 283 | 287 | k-c from:body -> ermite.tab('c'); | |
| 284 | 288 | k-r from:body -> ermite.tab('r'); | |
| 285 | 289 | k-m from:body -> ermite.tab('m'); | |
| @@ -295,7 +299,6 @@ | |||
| 295 | 299 | k-permalink from:body -> ermite.permalink(); | |
| 296 | 300 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 297 | 301 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 298 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 299 | 302 | k-keys from:body -> ermite.toggleKeys()"> | |
| 300 | 303 | </div> | |
| 301 | 304 | </body> | |
M internal/app/testdata/web/home.html +12 -9
| @@ -275,14 +275,16 @@ | |||
| 275 | 275 | </div> | |
| 276 | 276 | <div> | |
| 277 | 277 | <dt> | |
| 278 | - | <kbd>s</kbd> | |
| 279 | - | <kbd>t</kbd> | |
| 280 | - | <kbd>c</kbd> | |
| 281 | - | <kbd>r</kbd> | |
| 282 | - | <kbd>m</kbd> | |
| 283 | - | <kbd>i</kbd> | |
| 278 | + | <kbd>g s</kbd> | |
| 279 | + | <kbd>g f</kbd> | |
| 280 | + | <kbd>g c</kbd> | |
| 281 | + | <kbd>g b</kbd> | |
| 282 | + | <kbd>g m</kbd> | |
| 283 | + | <kbd>g i</kbd> | |
| 284 | + | <kbd>g r</kbd> | |
| 285 | + | <kbd>g a</kbd> | |
| 284 | 286 | </dt> | |
| 285 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 287 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 286 | 288 | </div> | |
| 287 | 289 | <div> | |
| 288 | 290 | <dt> | |
| @@ -389,7 +391,9 @@ | |||
| 389 | 391 | k-filter from:body -> ermite.focusFilter(); | |
| 390 | 392 | k-gh from:body -> ermite.go('.brand'); | |
| 391 | 393 | k-s from:body -> ermite.tab('s'); | |
| 392 | - | k-t from:body -> ermite.tab('t'); | |
| 394 | + | k-f from:body -> ermite.tab('f'); | |
| 395 | + | k-b from:body -> ermite.tab('b'); | |
| 396 | + | k-a from:body -> ermite.tab('a'); | |
| 393 | 397 | k-c from:body -> ermite.tab('c'); | |
| 394 | 398 | k-r from:body -> ermite.tab('r'); | |
| 395 | 399 | k-m from:body -> ermite.tab('m'); | |
| @@ -405,7 +409,6 @@ | |||
| 405 | 409 | k-permalink from:body -> ermite.permalink(); | |
| 406 | 410 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 407 | 411 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 408 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 409 | 412 | k-keys from:body -> ermite.toggleKeys()"> | |
| 410 | 413 | </div> | |
| 411 | 414 | </body> | |
M internal/app/testdata/web/not-found-page.html +12 -9
| @@ -160,14 +160,16 @@ | |||
| 160 | 160 | </div> | |
| 161 | 161 | <div> | |
| 162 | 162 | <dt> | |
| 163 | - | <kbd>s</kbd> | |
| 164 | - | <kbd>t</kbd> | |
| 165 | - | <kbd>c</kbd> | |
| 166 | - | <kbd>r</kbd> | |
| 167 | - | <kbd>m</kbd> | |
| 168 | - | <kbd>i</kbd> | |
| 163 | + | <kbd>g s</kbd> | |
| 164 | + | <kbd>g f</kbd> | |
| 165 | + | <kbd>g c</kbd> | |
| 166 | + | <kbd>g b</kbd> | |
| 167 | + | <kbd>g m</kbd> | |
| 168 | + | <kbd>g i</kbd> | |
| 169 | + | <kbd>g r</kbd> | |
| 170 | + | <kbd>g a</kbd> | |
| 169 | 171 | </dt> | |
| 170 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 172 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 171 | 173 | </div> | |
| 172 | 174 | <div> | |
| 173 | 175 | <dt> | |
| @@ -262,7 +264,9 @@ | |||
| 262 | 264 | k-filter from:body -> ermite.focusFilter(); | |
| 263 | 265 | k-gh from:body -> ermite.go('.brand'); | |
| 264 | 266 | k-s from:body -> ermite.tab('s'); | |
| 265 | - | k-t from:body -> ermite.tab('t'); | |
| 267 | + | k-f from:body -> ermite.tab('f'); | |
| 268 | + | k-b from:body -> ermite.tab('b'); | |
| 269 | + | k-a from:body -> ermite.tab('a'); | |
| 266 | 270 | k-c from:body -> ermite.tab('c'); | |
| 267 | 271 | k-r from:body -> ermite.tab('r'); | |
| 268 | 272 | k-m from:body -> ermite.tab('m'); | |
| @@ -278,7 +282,6 @@ | |||
| 278 | 282 | k-permalink from:body -> ermite.permalink(); | |
| 279 | 283 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 280 | 284 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 281 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 282 | 285 | k-keys from:body -> ermite.toggleKeys()"> | |
| 283 | 286 | </div> | |
| 284 | 287 | </body> | |
M internal/app/testdata/web/not-found.html +12 -9
| @@ -160,14 +160,16 @@ | |||
| 160 | 160 | </div> | |
| 161 | 161 | <div> | |
| 162 | 162 | <dt> | |
| 163 | - | <kbd>s</kbd> | |
| 164 | - | <kbd>t</kbd> | |
| 165 | - | <kbd>c</kbd> | |
| 166 | - | <kbd>r</kbd> | |
| 167 | - | <kbd>m</kbd> | |
| 168 | - | <kbd>i</kbd> | |
| 163 | + | <kbd>g s</kbd> | |
| 164 | + | <kbd>g f</kbd> | |
| 165 | + | <kbd>g c</kbd> | |
| 166 | + | <kbd>g b</kbd> | |
| 167 | + | <kbd>g m</kbd> | |
| 168 | + | <kbd>g i</kbd> | |
| 169 | + | <kbd>g r</kbd> | |
| 170 | + | <kbd>g a</kbd> | |
| 169 | 171 | </dt> | |
| 170 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 172 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 171 | 173 | </div> | |
| 172 | 174 | <div> | |
| 173 | 175 | <dt> | |
| @@ -262,7 +264,9 @@ | |||
| 262 | 264 | k-filter from:body -> ermite.focusFilter(); | |
| 263 | 265 | k-gh from:body -> ermite.go('.brand'); | |
| 264 | 266 | k-s from:body -> ermite.tab('s'); | |
| 265 | - | k-t from:body -> ermite.tab('t'); | |
| 267 | + | k-f from:body -> ermite.tab('f'); | |
| 268 | + | k-b from:body -> ermite.tab('b'); | |
| 269 | + | k-a from:body -> ermite.tab('a'); | |
| 266 | 270 | k-c from:body -> ermite.tab('c'); | |
| 267 | 271 | k-r from:body -> ermite.tab('r'); | |
| 268 | 272 | k-m from:body -> ermite.tab('m'); | |
| @@ -278,7 +282,6 @@ | |||
| 278 | 282 | k-permalink from:body -> ermite.permalink(); | |
| 279 | 283 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 280 | 284 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 281 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 282 | 285 | k-keys from:body -> ermite.toggleKeys()"> | |
| 283 | 286 | </div> | |
| 284 | 287 | </body> | |
M internal/app/testdata/web/refs.html +33 -30
| @@ -67,31 +67,31 @@ | |||
| 67 | 67 | <nav class="tabs" aria-label="Sections"> | |
| 68 | 68 | <ul> | |
| 69 | 69 | <li> | |
| 70 | - | <a href="/demo" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 70 | + | <a href="/demo" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd> | |
| 71 | 71 | </a> | |
| 72 | 72 | </li> | |
| 73 | 73 | <li> | |
| 74 | - | <a href="/demo/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> | |
| 74 | + | <a href="/demo/tree/main" data-key="g f">Files <kbd aria-hidden="true">g f</kbd> | |
| 75 | 75 | </a> | |
| 76 | 76 | </li> | |
| 77 | 77 | <li> | |
| 78 | - | <a href="/demo/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> | |
| 78 | + | <a href="/demo/commits/main" data-key="g c">Commits <kbd aria-hidden="true">g c</kbd> | |
| 79 | 79 | </a> | |
| 80 | 80 | </li> | |
| 81 | 81 | <li> | |
| 82 | - | <a href="/demo/refs" data-key="r" aria-keyshortcuts="r" aria-current="page">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 82 | + | <a href="/demo/refs" data-key="g b" aria-current="page">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 83 | 83 | </a> | |
| 84 | 84 | </li> | |
| 85 | 85 | <li> | |
| 86 | - | <a href="/demo/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 86 | + | <a href="/demo/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd> | |
| 87 | 87 | </a> | |
| 88 | 88 | </li> | |
| 89 | 89 | <li> | |
| 90 | - | <a href="/demo/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd> | |
| 90 | + | <a href="/demo/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd> | |
| 91 | 91 | </a> | |
| 92 | 92 | </li> | |
| 93 | 93 | <li> | |
| 94 | - | <a href="/demo/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 94 | + | <a href="/demo/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd> | |
| 95 | 95 | </a> | |
| 96 | 96 | </li> | |
| 97 | 97 | </ul> | |
| @@ -285,14 +285,16 @@ | |||
| 285 | 285 | </div> | |
| 286 | 286 | <div> | |
| 287 | 287 | <dt> | |
| 288 | - | <kbd>s</kbd> | |
| 289 | - | <kbd>t</kbd> | |
| 290 | - | <kbd>c</kbd> | |
| 291 | - | <kbd>r</kbd> | |
| 292 | - | <kbd>m</kbd> | |
| 293 | - | <kbd>i</kbd> | |
| 288 | + | <kbd>g s</kbd> | |
| 289 | + | <kbd>g f</kbd> | |
| 290 | + | <kbd>g c</kbd> | |
| 291 | + | <kbd>g b</kbd> | |
| 292 | + | <kbd>g m</kbd> | |
| 293 | + | <kbd>g i</kbd> | |
| 294 | + | <kbd>g r</kbd> | |
| 295 | + | <kbd>g a</kbd> | |
| 294 | 296 | </dt> | |
| 295 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 297 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 296 | 298 | </div> | |
| 297 | 299 | <div> | |
| 298 | 300 | <dt> | |
| @@ -395,45 +397,45 @@ | |||
| 395 | 397 | <span class="k">go</span> | |
| 396 | 398 | </a> | |
| 397 | 399 | </li> | |
| 398 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 400 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 399 | 401 | <a href="/demo" tabindex="-1"> | |
| 400 | - | <span class="lbl">summary</span> | |
| 402 | + | <span class="lbl">summary (g s)</span> | |
| 401 | 403 | <span class="k">section</span> | |
| 402 | 404 | </a> | |
| 403 | 405 | </li> | |
| 404 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 406 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 405 | 407 | <a href="/demo/tree/main" tabindex="-1"> | |
| 406 | - | <span class="lbl">files</span> | |
| 408 | + | <span class="lbl">files (g f)</span> | |
| 407 | 409 | <span class="k">section</span> | |
| 408 | 410 | </a> | |
| 409 | 411 | </li> | |
| 410 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 412 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 411 | 413 | <a href="/demo/commits/main" tabindex="-1"> | |
| 412 | - | <span class="lbl">commits</span> | |
| 414 | + | <span class="lbl">commits (g c)</span> | |
| 413 | 415 | <span class="k">section</span> | |
| 414 | 416 | </a> | |
| 415 | 417 | </li> | |
| 416 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 418 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 417 | 419 | <a href="/demo/refs" tabindex="-1"> | |
| 418 | - | <span class="lbl">branches & tags</span> | |
| 420 | + | <span class="lbl">branches & tags (g b)</span> | |
| 419 | 421 | <span class="k">section</span> | |
| 420 | 422 | </a> | |
| 421 | 423 | </li> | |
| 422 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 424 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 423 | 425 | <a href="/demo/merge-requests" tabindex="-1"> | |
| 424 | - | <span class="lbl">merge requests</span> | |
| 426 | + | <span class="lbl">merge requests (g m)</span> | |
| 425 | 427 | <span class="k">section</span> | |
| 426 | 428 | </a> | |
| 427 | 429 | </li> | |
| 428 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 430 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 429 | 431 | <a href="/demo/releases" tabindex="-1"> | |
| 430 | - | <span class="lbl">releases</span> | |
| 432 | + | <span class="lbl">releases (g r)</span> | |
| 431 | 433 | <span class="k">section</span> | |
| 432 | 434 | </a> | |
| 433 | 435 | </li> | |
| 434 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 436 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 435 | 437 | <a href="/demo/actions" tabindex="-1"> | |
| 436 | - | <span class="lbl">actions</span> | |
| 438 | + | <span class="lbl">actions (g a)</span> | |
| 437 | 439 | <span class="k">section</span> | |
| 438 | 440 | </a> | |
| 439 | 441 | </li> | |
| @@ -498,7 +500,9 @@ | |||
| 498 | 500 | k-filter from:body -> ermite.focusFilter(); | |
| 499 | 501 | k-gh from:body -> ermite.go('.brand'); | |
| 500 | 502 | k-s from:body -> ermite.tab('s'); | |
| 501 | - | k-t from:body -> ermite.tab('t'); | |
| 503 | + | k-f from:body -> ermite.tab('f'); | |
| 504 | + | k-b from:body -> ermite.tab('b'); | |
| 505 | + | k-a from:body -> ermite.tab('a'); | |
| 502 | 506 | k-c from:body -> ermite.tab('c'); | |
| 503 | 507 | k-r from:body -> ermite.tab('r'); | |
| 504 | 508 | k-m from:body -> ermite.tab('m'); | |
| @@ -514,7 +518,6 @@ | |||
| 514 | 518 | k-permalink from:body -> ermite.permalink(); | |
| 515 | 519 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 516 | 520 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 517 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 518 | 521 | k-keys from:body -> ermite.toggleKeys()"> | |
| 519 | 522 | </div> | |
| 520 | 523 | </body> | |
M internal/app/testdata/web/summary.html +33 -30
| @@ -64,31 +64,31 @@ | |||
| 64 | 64 | <nav class="tabs" aria-label="Sections"> | |
| 65 | 65 | <ul> | |
| 66 | 66 | <li> | |
| 67 | - | <a href="/demo" data-key="s" aria-keyshortcuts="s" aria-current="page">Summary <kbd aria-hidden="true">s</kbd> | |
| 67 | + | <a href="/demo" data-key="g s" aria-current="page">Summary <kbd aria-hidden="true">g s</kbd> | |
| 68 | 68 | </a> | |
| 69 | 69 | </li> | |
| 70 | 70 | <li> | |
| 71 | - | <a href="/demo/tree/main" data-key="t" aria-keyshortcuts="t">Files <kbd aria-hidden="true">t</kbd> | |
| 71 | + | <a href="/demo/tree/main" data-key="g f">Files <kbd aria-hidden="true">g f</kbd> | |
| 72 | 72 | </a> | |
| 73 | 73 | </li> | |
| 74 | 74 | <li> | |
| 75 | - | <a href="/demo/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> | |
| 75 | + | <a href="/demo/commits/main" data-key="g c">Commits <kbd aria-hidden="true">g c</kbd> | |
| 76 | 76 | </a> | |
| 77 | 77 | </li> | |
| 78 | 78 | <li> | |
| 79 | - | <a href="/demo/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 79 | + | <a href="/demo/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 80 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 82 | <li> | |
| 83 | - | <a href="/demo/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 83 | + | <a href="/demo/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd> | |
| 84 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 86 | <li> | |
| 87 | - | <a href="/demo/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd> | |
| 87 | + | <a href="/demo/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd> | |
| 88 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 90 | <li> | |
| 91 | - | <a href="/demo/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 91 | + | <a href="/demo/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd> | |
| 92 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 94 | </ul> | |
| @@ -311,14 +311,16 @@ | |||
| 311 | 311 | </div> | |
| 312 | 312 | <div> | |
| 313 | 313 | <dt> | |
| 314 | - | <kbd>s</kbd> | |
| 315 | - | <kbd>t</kbd> | |
| 316 | - | <kbd>c</kbd> | |
| 317 | - | <kbd>r</kbd> | |
| 318 | - | <kbd>m</kbd> | |
| 319 | - | <kbd>i</kbd> | |
| 314 | + | <kbd>g s</kbd> | |
| 315 | + | <kbd>g f</kbd> | |
| 316 | + | <kbd>g c</kbd> | |
| 317 | + | <kbd>g b</kbd> | |
| 318 | + | <kbd>g m</kbd> | |
| 319 | + | <kbd>g i</kbd> | |
| 320 | + | <kbd>g r</kbd> | |
| 321 | + | <kbd>g a</kbd> | |
| 320 | 322 | </dt> | |
| 321 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 323 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 322 | 324 | </div> | |
| 323 | 325 | <div> | |
| 324 | 326 | <dt> | |
| @@ -421,45 +423,45 @@ | |||
| 421 | 423 | <span class="k">go</span> | |
| 422 | 424 | </a> | |
| 423 | 425 | </li> | |
| 424 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 426 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 425 | 427 | <a href="/demo" tabindex="-1"> | |
| 426 | - | <span class="lbl">summary</span> | |
| 428 | + | <span class="lbl">summary (g s)</span> | |
| 427 | 429 | <span class="k">section</span> | |
| 428 | 430 | </a> | |
| 429 | 431 | </li> | |
| 430 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 432 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 431 | 433 | <a href="/demo/tree/main" tabindex="-1"> | |
| 432 | - | <span class="lbl">files</span> | |
| 434 | + | <span class="lbl">files (g f)</span> | |
| 433 | 435 | <span class="k">section</span> | |
| 434 | 436 | </a> | |
| 435 | 437 | </li> | |
| 436 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 438 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 437 | 439 | <a href="/demo/commits/main" tabindex="-1"> | |
| 438 | - | <span class="lbl">commits</span> | |
| 440 | + | <span class="lbl">commits (g c)</span> | |
| 439 | 441 | <span class="k">section</span> | |
| 440 | 442 | </a> | |
| 441 | 443 | </li> | |
| 442 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 444 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 443 | 445 | <a href="/demo/refs" tabindex="-1"> | |
| 444 | - | <span class="lbl">branches & tags</span> | |
| 446 | + | <span class="lbl">branches & tags (g b)</span> | |
| 445 | 447 | <span class="k">section</span> | |
| 446 | 448 | </a> | |
| 447 | 449 | </li> | |
| 448 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 450 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 449 | 451 | <a href="/demo/merge-requests" tabindex="-1"> | |
| 450 | - | <span class="lbl">merge requests</span> | |
| 452 | + | <span class="lbl">merge requests (g m)</span> | |
| 451 | 453 | <span class="k">section</span> | |
| 452 | 454 | </a> | |
| 453 | 455 | </li> | |
| 454 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 456 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 455 | 457 | <a href="/demo/releases" tabindex="-1"> | |
| 456 | - | <span class="lbl">releases</span> | |
| 458 | + | <span class="lbl">releases (g r)</span> | |
| 457 | 459 | <span class="k">section</span> | |
| 458 | 460 | </a> | |
| 459 | 461 | </li> | |
| 460 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 462 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 461 | 463 | <a href="/demo/actions" tabindex="-1"> | |
| 462 | - | <span class="lbl">actions</span> | |
| 464 | + | <span class="lbl">actions (g a)</span> | |
| 463 | 465 | <span class="k">section</span> | |
| 464 | 466 | </a> | |
| 465 | 467 | </li> | |
| @@ -524,7 +526,9 @@ | |||
| 524 | 526 | k-filter from:body -> ermite.focusFilter(); | |
| 525 | 527 | k-gh from:body -> ermite.go('.brand'); | |
| 526 | 528 | k-s from:body -> ermite.tab('s'); | |
| 527 | - | k-t from:body -> ermite.tab('t'); | |
| 529 | + | k-f from:body -> ermite.tab('f'); | |
| 530 | + | k-b from:body -> ermite.tab('b'); | |
| 531 | + | k-a from:body -> ermite.tab('a'); | |
| 528 | 532 | k-c from:body -> ermite.tab('c'); | |
| 529 | 533 | k-r from:body -> ermite.tab('r'); | |
| 530 | 534 | k-m from:body -> ermite.tab('m'); | |
| @@ -540,7 +544,6 @@ | |||
| 540 | 544 | k-permalink from:body -> ermite.permalink(); | |
| 541 | 545 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 542 | 546 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 543 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 544 | 547 | k-keys from:body -> ermite.toggleKeys()"> | |
| 545 | 548 | </div> | |
| 546 | 549 | </body> | |
M internal/app/testdata/web/tree-nested.html +33 -30
| @@ -76,31 +76,31 @@ | |||
| 76 | 76 | <nav class="tabs" aria-label="Sections"> | |
| 77 | 77 | <ul> | |
| 78 | 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 | 80 | </a> | |
| 81 | 81 | </li> | |
| 82 | 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 | 84 | </a> | |
| 85 | 85 | </li> | |
| 86 | 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 | 88 | </a> | |
| 89 | 89 | </li> | |
| 90 | 90 | <li> | |
| 91 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 91 | + | <a href="/files/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 92 | 92 | </a> | |
| 93 | 93 | </li> | |
| 94 | 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 | 96 | </a> | |
| 97 | 97 | </li> | |
| 98 | 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 | 100 | </a> | |
| 101 | 101 | </li> | |
| 102 | 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 | 104 | </a> | |
| 105 | 105 | </li> | |
| 106 | 106 | </ul> | |
| @@ -241,14 +241,16 @@ | |||
| 241 | 241 | </div> | |
| 242 | 242 | <div> | |
| 243 | 243 | <dt> | |
| 244 | - | <kbd>s</kbd> | |
| 245 | - | <kbd>t</kbd> | |
| 246 | - | <kbd>c</kbd> | |
| 247 | - | <kbd>r</kbd> | |
| 248 | - | <kbd>m</kbd> | |
| 249 | - | <kbd>i</kbd> | |
| 244 | + | <kbd>g s</kbd> | |
| 245 | + | <kbd>g f</kbd> | |
| 246 | + | <kbd>g c</kbd> | |
| 247 | + | <kbd>g b</kbd> | |
| 248 | + | <kbd>g m</kbd> | |
| 249 | + | <kbd>g i</kbd> | |
| 250 | + | <kbd>g r</kbd> | |
| 251 | + | <kbd>g a</kbd> | |
| 250 | 252 | </dt> | |
| 251 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 253 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 252 | 254 | </div> | |
| 253 | 255 | <div> | |
| 254 | 256 | <dt> | |
| @@ -347,45 +349,45 @@ | |||
| 347 | 349 | <span class="k">go</span> | |
| 348 | 350 | </a> | |
| 349 | 351 | </li> | |
| 350 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 352 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 351 | 353 | <a href="/files" tabindex="-1"> | |
| 352 | - | <span class="lbl">summary</span> | |
| 354 | + | <span class="lbl">summary (g s)</span> | |
| 353 | 355 | <span class="k">section</span> | |
| 354 | 356 | </a> | |
| 355 | 357 | </li> | |
| 356 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 358 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 357 | 359 | <a href="/files/tree/main" tabindex="-1"> | |
| 358 | - | <span class="lbl">files</span> | |
| 360 | + | <span class="lbl">files (g f)</span> | |
| 359 | 361 | <span class="k">section</span> | |
| 360 | 362 | </a> | |
| 361 | 363 | </li> | |
| 362 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 364 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 363 | 365 | <a href="/files/commits/main" tabindex="-1"> | |
| 364 | - | <span class="lbl">commits</span> | |
| 366 | + | <span class="lbl">commits (g c)</span> | |
| 365 | 367 | <span class="k">section</span> | |
| 366 | 368 | </a> | |
| 367 | 369 | </li> | |
| 368 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 370 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 369 | 371 | <a href="/files/refs" tabindex="-1"> | |
| 370 | - | <span class="lbl">branches & tags</span> | |
| 372 | + | <span class="lbl">branches & tags (g b)</span> | |
| 371 | 373 | <span class="k">section</span> | |
| 372 | 374 | </a> | |
| 373 | 375 | </li> | |
| 374 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 376 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 375 | 377 | <a href="/files/merge-requests" tabindex="-1"> | |
| 376 | - | <span class="lbl">merge requests</span> | |
| 378 | + | <span class="lbl">merge requests (g m)</span> | |
| 377 | 379 | <span class="k">section</span> | |
| 378 | 380 | </a> | |
| 379 | 381 | </li> | |
| 380 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 382 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 381 | 383 | <a href="/files/releases" tabindex="-1"> | |
| 382 | - | <span class="lbl">releases</span> | |
| 384 | + | <span class="lbl">releases (g r)</span> | |
| 383 | 385 | <span class="k">section</span> | |
| 384 | 386 | </a> | |
| 385 | 387 | </li> | |
| 386 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 388 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 387 | 389 | <a href="/files/actions" tabindex="-1"> | |
| 388 | - | <span class="lbl">actions</span> | |
| 390 | + | <span class="lbl">actions (g a)</span> | |
| 389 | 391 | <span class="k">section</span> | |
| 390 | 392 | </a> | |
| 391 | 393 | </li> | |
| @@ -438,7 +440,9 @@ | |||
| 438 | 440 | k-filter from:body -> ermite.focusFilter(); | |
| 439 | 441 | k-gh from:body -> ermite.go('.brand'); | |
| 440 | 442 | k-s from:body -> ermite.tab('s'); | |
| 441 | - | k-t from:body -> ermite.tab('t'); | |
| 443 | + | k-f from:body -> ermite.tab('f'); | |
| 444 | + | k-b from:body -> ermite.tab('b'); | |
| 445 | + | k-a from:body -> ermite.tab('a'); | |
| 442 | 446 | k-c from:body -> ermite.tab('c'); | |
| 443 | 447 | k-r from:body -> ermite.tab('r'); | |
| 444 | 448 | k-m from:body -> ermite.tab('m'); | |
| @@ -454,7 +458,6 @@ | |||
| 454 | 458 | k-permalink from:body -> ermite.permalink(); | |
| 455 | 459 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 456 | 460 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 457 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 458 | 461 | k-keys from:body -> ermite.toggleKeys()"> | |
| 459 | 462 | </div> | |
| 460 | 463 | </body> | |
M internal/app/testdata/web/tree-root.html +33 -30
| @@ -70,31 +70,31 @@ | |||
| 70 | 70 | <nav class="tabs" aria-label="Sections"> | |
| 71 | 71 | <ul> | |
| 72 | 72 | <li> | |
| 73 | - | <a href="/files" data-key="s" aria-keyshortcuts="s">Summary <kbd aria-hidden="true">s</kbd> | |
| 73 | + | <a href="/files" data-key="g s">Summary <kbd aria-hidden="true">g s</kbd> | |
| 74 | 74 | </a> | |
| 75 | 75 | </li> | |
| 76 | 76 | <li> | |
| 77 | - | <a href="/files/tree/main" data-key="t" aria-keyshortcuts="t" aria-current="page">Files <kbd aria-hidden="true">t</kbd> | |
| 77 | + | <a href="/files/tree/main" data-key="g f" aria-current="page">Files <kbd aria-hidden="true">g f</kbd> | |
| 78 | 78 | </a> | |
| 79 | 79 | </li> | |
| 80 | 80 | <li> | |
| 81 | - | <a href="/files/commits/main" data-key="c" aria-keyshortcuts="c">Commits <kbd aria-hidden="true">c</kbd> | |
| 81 | + | <a href="/files/commits/main" data-key="g c">Commits <kbd aria-hidden="true">g c</kbd> | |
| 82 | 82 | </a> | |
| 83 | 83 | </li> | |
| 84 | 84 | <li> | |
| 85 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 85 | + | <a href="/files/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 86 | 86 | </a> | |
| 87 | 87 | </li> | |
| 88 | 88 | <li> | |
| 89 | - | <a href="/files/merge-requests" data-key="m" aria-keyshortcuts="m">Merge requests <kbd aria-hidden="true">m</kbd> | |
| 89 | + | <a href="/files/merge-requests" data-key="g m">Merge requests <kbd aria-hidden="true">g m</kbd> | |
| 90 | 90 | </a> | |
| 91 | 91 | </li> | |
| 92 | 92 | <li> | |
| 93 | - | <a href="/files/releases" data-key="l" aria-keyshortcuts="l">Releases <kbd aria-hidden="true">l</kbd> | |
| 93 | + | <a href="/files/releases" data-key="g r">Releases <kbd aria-hidden="true">g r</kbd> | |
| 94 | 94 | </a> | |
| 95 | 95 | </li> | |
| 96 | 96 | <li> | |
| 97 | - | <a href="/files/actions" data-key="a" aria-keyshortcuts="a">Actions <kbd aria-hidden="true">a</kbd> | |
| 97 | + | <a href="/files/actions" data-key="g a">Actions <kbd aria-hidden="true">g a</kbd> | |
| 98 | 98 | </a> | |
| 99 | 99 | </li> | |
| 100 | 100 | </ul> | |
| @@ -462,14 +462,16 @@ | |||
| 462 | 462 | </div> | |
| 463 | 463 | <div> | |
| 464 | 464 | <dt> | |
| 465 | - | <kbd>s</kbd> | |
| 466 | - | <kbd>t</kbd> | |
| 467 | - | <kbd>c</kbd> | |
| 468 | - | <kbd>r</kbd> | |
| 469 | - | <kbd>m</kbd> | |
| 470 | - | <kbd>i</kbd> | |
| 465 | + | <kbd>g s</kbd> | |
| 466 | + | <kbd>g f</kbd> | |
| 467 | + | <kbd>g c</kbd> | |
| 468 | + | <kbd>g b</kbd> | |
| 469 | + | <kbd>g m</kbd> | |
| 470 | + | <kbd>g i</kbd> | |
| 471 | + | <kbd>g r</kbd> | |
| 472 | + | <kbd>g a</kbd> | |
| 471 | 473 | </dt> | |
| 472 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 474 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 473 | 475 | </div> | |
| 474 | 476 | <div> | |
| 475 | 477 | <dt> | |
| @@ -568,45 +570,45 @@ | |||
| 568 | 570 | <span class="k">go</span> | |
| 569 | 571 | </a> | |
| 570 | 572 | </li> | |
| 571 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 573 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 572 | 574 | <a href="/files" tabindex="-1"> | |
| 573 | - | <span class="lbl">summary</span> | |
| 575 | + | <span class="lbl">summary (g s)</span> | |
| 574 | 576 | <span class="k">section</span> | |
| 575 | 577 | </a> | |
| 576 | 578 | </li> | |
| 577 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 579 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 578 | 580 | <a href="/files/tree/main" tabindex="-1"> | |
| 579 | - | <span class="lbl">files</span> | |
| 581 | + | <span class="lbl">files (g f)</span> | |
| 580 | 582 | <span class="k">section</span> | |
| 581 | 583 | </a> | |
| 582 | 584 | </li> | |
| 583 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 585 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 584 | 586 | <a href="/files/commits/main" tabindex="-1"> | |
| 585 | - | <span class="lbl">commits</span> | |
| 587 | + | <span class="lbl">commits (g c)</span> | |
| 586 | 588 | <span class="k">section</span> | |
| 587 | 589 | </a> | |
| 588 | 590 | </li> | |
| 589 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 591 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 590 | 592 | <a href="/files/refs" tabindex="-1"> | |
| 591 | - | <span class="lbl">branches & tags</span> | |
| 593 | + | <span class="lbl">branches & tags (g b)</span> | |
| 592 | 594 | <span class="k">section</span> | |
| 593 | 595 | </a> | |
| 594 | 596 | </li> | |
| 595 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 597 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 596 | 598 | <a href="/files/merge-requests" tabindex="-1"> | |
| 597 | - | <span class="lbl">merge requests</span> | |
| 599 | + | <span class="lbl">merge requests (g m)</span> | |
| 598 | 600 | <span class="k">section</span> | |
| 599 | 601 | </a> | |
| 600 | 602 | </li> | |
| 601 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 603 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 602 | 604 | <a href="/files/releases" tabindex="-1"> | |
| 603 | - | <span class="lbl">releases</span> | |
| 605 | + | <span class="lbl">releases (g r)</span> | |
| 604 | 606 | <span class="k">section</span> | |
| 605 | 607 | </a> | |
| 606 | 608 | </li> | |
| 607 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 609 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 608 | 610 | <a href="/files/actions" tabindex="-1"> | |
| 609 | - | <span class="lbl">actions</span> | |
| 611 | + | <span class="lbl">actions (g a)</span> | |
| 610 | 612 | <span class="k">section</span> | |
| 611 | 613 | </a> | |
| 612 | 614 | </li> | |
| @@ -659,7 +661,9 @@ | |||
| 659 | 661 | k-filter from:body -> ermite.focusFilter(); | |
| 660 | 662 | k-gh from:body -> ermite.go('.brand'); | |
| 661 | 663 | k-s from:body -> ermite.tab('s'); | |
| 662 | - | k-t from:body -> ermite.tab('t'); | |
| 664 | + | k-f from:body -> ermite.tab('f'); | |
| 665 | + | k-b from:body -> ermite.tab('b'); | |
| 666 | + | k-a from:body -> ermite.tab('a'); | |
| 663 | 667 | k-c from:body -> ermite.tab('c'); | |
| 664 | 668 | k-r from:body -> ermite.tab('r'); | |
| 665 | 669 | k-m from:body -> ermite.tab('m'); | |
| @@ -675,7 +679,6 @@ | |||
| 675 | 679 | k-permalink from:body -> ermite.permalink(); | |
| 676 | 680 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 677 | 681 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 678 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 679 | 682 | k-keys from:body -> ermite.toggleKeys()"> | |
| 680 | 683 | </div> | |
| 681 | 684 | </body> | |
M internal/app/testdata/web/tree-slash-ref.html +33 -30
| @@ -73,31 +73,31 @@ | |||
| 73 | 73 | <nav class="tabs" aria-label="Sections"> | |
| 74 | 74 | <ul> | |
| 75 | 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 | 77 | </a> | |
| 78 | 78 | </li> | |
| 79 | 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 | 81 | </a> | |
| 82 | 82 | </li> | |
| 83 | 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 | 85 | </a> | |
| 86 | 86 | </li> | |
| 87 | 87 | <li> | |
| 88 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 88 | + | <a href="/files/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 89 | 89 | </a> | |
| 90 | 90 | </li> | |
| 91 | 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 | 93 | </a> | |
| 94 | 94 | </li> | |
| 95 | 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 | 97 | </a> | |
| 98 | 98 | </li> | |
| 99 | 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 | 101 | </a> | |
| 102 | 102 | </li> | |
| 103 | 103 | </ul> | |
| @@ -238,14 +238,16 @@ | |||
| 238 | 238 | </div> | |
| 239 | 239 | <div> | |
| 240 | 240 | <dt> | |
| 241 | - | <kbd>s</kbd> | |
| 242 | - | <kbd>t</kbd> | |
| 243 | - | <kbd>c</kbd> | |
| 244 | - | <kbd>r</kbd> | |
| 245 | - | <kbd>m</kbd> | |
| 246 | - | <kbd>i</kbd> | |
| 241 | + | <kbd>g s</kbd> | |
| 242 | + | <kbd>g f</kbd> | |
| 243 | + | <kbd>g c</kbd> | |
| 244 | + | <kbd>g b</kbd> | |
| 245 | + | <kbd>g m</kbd> | |
| 246 | + | <kbd>g i</kbd> | |
| 247 | + | <kbd>g r</kbd> | |
| 248 | + | <kbd>g a</kbd> | |
| 247 | 249 | </dt> | |
| 248 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 250 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 249 | 251 | </div> | |
| 250 | 252 | <div> | |
| 251 | 253 | <dt> | |
| @@ -344,45 +346,45 @@ | |||
| 344 | 346 | <span class="k">go</span> | |
| 345 | 347 | </a> | |
| 346 | 348 | </li> | |
| 347 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 349 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 348 | 350 | <a href="/files" tabindex="-1"> | |
| 349 | - | <span class="lbl">summary</span> | |
| 351 | + | <span class="lbl">summary (g s)</span> | |
| 350 | 352 | <span class="k">section</span> | |
| 351 | 353 | </a> | |
| 352 | 354 | </li> | |
| 353 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 355 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 354 | 356 | <a href="/files/tree/main" tabindex="-1"> | |
| 355 | - | <span class="lbl">files</span> | |
| 357 | + | <span class="lbl">files (g f)</span> | |
| 356 | 358 | <span class="k">section</span> | |
| 357 | 359 | </a> | |
| 358 | 360 | </li> | |
| 359 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 361 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 360 | 362 | <a href="/files/commits/main" tabindex="-1"> | |
| 361 | - | <span class="lbl">commits</span> | |
| 363 | + | <span class="lbl">commits (g c)</span> | |
| 362 | 364 | <span class="k">section</span> | |
| 363 | 365 | </a> | |
| 364 | 366 | </li> | |
| 365 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 367 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 366 | 368 | <a href="/files/refs" tabindex="-1"> | |
| 367 | - | <span class="lbl">branches & tags</span> | |
| 369 | + | <span class="lbl">branches & tags (g b)</span> | |
| 368 | 370 | <span class="k">section</span> | |
| 369 | 371 | </a> | |
| 370 | 372 | </li> | |
| 371 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 373 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 372 | 374 | <a href="/files/merge-requests" tabindex="-1"> | |
| 373 | - | <span class="lbl">merge requests</span> | |
| 375 | + | <span class="lbl">merge requests (g m)</span> | |
| 374 | 376 | <span class="k">section</span> | |
| 375 | 377 | </a> | |
| 376 | 378 | </li> | |
| 377 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 379 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 378 | 380 | <a href="/files/releases" tabindex="-1"> | |
| 379 | - | <span class="lbl">releases</span> | |
| 381 | + | <span class="lbl">releases (g r)</span> | |
| 380 | 382 | <span class="k">section</span> | |
| 381 | 383 | </a> | |
| 382 | 384 | </li> | |
| 383 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 385 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 384 | 386 | <a href="/files/actions" tabindex="-1"> | |
| 385 | - | <span class="lbl">actions</span> | |
| 387 | + | <span class="lbl">actions (g a)</span> | |
| 386 | 388 | <span class="k">section</span> | |
| 387 | 389 | </a> | |
| 388 | 390 | </li> | |
| @@ -435,7 +437,9 @@ | |||
| 435 | 437 | k-filter from:body -> ermite.focusFilter(); | |
| 436 | 438 | k-gh from:body -> ermite.go('.brand'); | |
| 437 | 439 | k-s from:body -> ermite.tab('s'); | |
| 438 | - | k-t from:body -> ermite.tab('t'); | |
| 440 | + | k-f from:body -> ermite.tab('f'); | |
| 441 | + | k-b from:body -> ermite.tab('b'); | |
| 442 | + | k-a from:body -> ermite.tab('a'); | |
| 439 | 443 | k-c from:body -> ermite.tab('c'); | |
| 440 | 444 | k-r from:body -> ermite.tab('r'); | |
| 441 | 445 | k-m from:body -> ermite.tab('m'); | |
| @@ -451,7 +455,6 @@ | |||
| 451 | 455 | k-permalink from:body -> ermite.permalink(); | |
| 452 | 456 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 453 | 457 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 454 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 455 | 458 | k-keys from:body -> ermite.toggleKeys()"> | |
| 456 | 459 | </div> | |
| 457 | 460 | </body> | |
M internal/app/testdata/web/tree-subdir.html +33 -30
| @@ -73,31 +73,31 @@ | |||
| 73 | 73 | <nav class="tabs" aria-label="Sections"> | |
| 74 | 74 | <ul> | |
| 75 | 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 | 77 | </a> | |
| 78 | 78 | </li> | |
| 79 | 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 | 81 | </a> | |
| 82 | 82 | </li> | |
| 83 | 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 | 85 | </a> | |
| 86 | 86 | </li> | |
| 87 | 87 | <li> | |
| 88 | - | <a href="/files/refs" data-key="r" aria-keyshortcuts="r">Branches & tags <kbd aria-hidden="true">r</kbd> | |
| 88 | + | <a href="/files/refs" data-key="g b">Branches & tags <kbd aria-hidden="true">g b</kbd> | |
| 89 | 89 | </a> | |
| 90 | 90 | </li> | |
| 91 | 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 | 93 | </a> | |
| 94 | 94 | </li> | |
| 95 | 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 | 97 | </a> | |
| 98 | 98 | </li> | |
| 99 | 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 | 101 | </a> | |
| 102 | 102 | </li> | |
| 103 | 103 | </ul> | |
| @@ -254,14 +254,16 @@ | |||
| 254 | 254 | </div> | |
| 255 | 255 | <div> | |
| 256 | 256 | <dt> | |
| 257 | - | <kbd>s</kbd> | |
| 258 | - | <kbd>t</kbd> | |
| 259 | - | <kbd>c</kbd> | |
| 260 | - | <kbd>r</kbd> | |
| 261 | - | <kbd>m</kbd> | |
| 262 | - | <kbd>i</kbd> | |
| 257 | + | <kbd>g s</kbd> | |
| 258 | + | <kbd>g f</kbd> | |
| 259 | + | <kbd>g c</kbd> | |
| 260 | + | <kbd>g b</kbd> | |
| 261 | + | <kbd>g m</kbd> | |
| 262 | + | <kbd>g i</kbd> | |
| 263 | + | <kbd>g r</kbd> | |
| 264 | + | <kbd>g a</kbd> | |
| 263 | 265 | </dt> | |
| 264 | - | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues</dd> | |
| 266 | + | <dd>tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)</dd> | |
| 265 | 267 | </div> | |
| 266 | 268 | <div> | |
| 267 | 269 | <dt> | |
| @@ -360,45 +362,45 @@ | |||
| 360 | 362 | <span class="k">go</span> | |
| 361 | 363 | </a> | |
| 362 | 364 | </li> | |
| 363 | - | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary"> | |
| 365 | + | <li id="cmd-o1" role="option" aria-selected="false" data-k="section summary (g s)"> | |
| 364 | 366 | <a href="/files" tabindex="-1"> | |
| 365 | - | <span class="lbl">summary</span> | |
| 367 | + | <span class="lbl">summary (g s)</span> | |
| 366 | 368 | <span class="k">section</span> | |
| 367 | 369 | </a> | |
| 368 | 370 | </li> | |
| 369 | - | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files"> | |
| 371 | + | <li id="cmd-o2" role="option" aria-selected="false" data-k="section files (g f)"> | |
| 370 | 372 | <a href="/files/tree/main" tabindex="-1"> | |
| 371 | - | <span class="lbl">files</span> | |
| 373 | + | <span class="lbl">files (g f)</span> | |
| 372 | 374 | <span class="k">section</span> | |
| 373 | 375 | </a> | |
| 374 | 376 | </li> | |
| 375 | - | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits"> | |
| 377 | + | <li id="cmd-o3" role="option" aria-selected="false" data-k="section commits (g c)"> | |
| 376 | 378 | <a href="/files/commits/main" tabindex="-1"> | |
| 377 | - | <span class="lbl">commits</span> | |
| 379 | + | <span class="lbl">commits (g c)</span> | |
| 378 | 380 | <span class="k">section</span> | |
| 379 | 381 | </a> | |
| 380 | 382 | </li> | |
| 381 | - | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags"> | |
| 383 | + | <li id="cmd-o4" role="option" aria-selected="false" data-k="section branches & tags (g b)"> | |
| 382 | 384 | <a href="/files/refs" tabindex="-1"> | |
| 383 | - | <span class="lbl">branches & tags</span> | |
| 385 | + | <span class="lbl">branches & tags (g b)</span> | |
| 384 | 386 | <span class="k">section</span> | |
| 385 | 387 | </a> | |
| 386 | 388 | </li> | |
| 387 | - | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests"> | |
| 389 | + | <li id="cmd-o5" role="option" aria-selected="false" data-k="section merge requests (g m)"> | |
| 388 | 390 | <a href="/files/merge-requests" tabindex="-1"> | |
| 389 | - | <span class="lbl">merge requests</span> | |
| 391 | + | <span class="lbl">merge requests (g m)</span> | |
| 390 | 392 | <span class="k">section</span> | |
| 391 | 393 | </a> | |
| 392 | 394 | </li> | |
| 393 | - | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases"> | |
| 395 | + | <li id="cmd-o6" role="option" aria-selected="false" data-k="section releases (g r)"> | |
| 394 | 396 | <a href="/files/releases" tabindex="-1"> | |
| 395 | - | <span class="lbl">releases</span> | |
| 397 | + | <span class="lbl">releases (g r)</span> | |
| 396 | 398 | <span class="k">section</span> | |
| 397 | 399 | </a> | |
| 398 | 400 | </li> | |
| 399 | - | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions"> | |
| 401 | + | <li id="cmd-o7" role="option" aria-selected="false" data-k="section actions (g a)"> | |
| 400 | 402 | <a href="/files/actions" tabindex="-1"> | |
| 401 | - | <span class="lbl">actions</span> | |
| 403 | + | <span class="lbl">actions (g a)</span> | |
| 402 | 404 | <span class="k">section</span> | |
| 403 | 405 | </a> | |
| 404 | 406 | </li> | |
| @@ -451,7 +453,9 @@ | |||
| 451 | 453 | k-filter from:body -> ermite.focusFilter(); | |
| 452 | 454 | k-gh from:body -> ermite.go('.brand'); | |
| 453 | 455 | k-s from:body -> ermite.tab('s'); | |
| 454 | - | k-t from:body -> ermite.tab('t'); | |
| 456 | + | k-f from:body -> ermite.tab('f'); | |
| 457 | + | k-b from:body -> ermite.tab('b'); | |
| 458 | + | k-a from:body -> ermite.tab('a'); | |
| 455 | 459 | k-c from:body -> ermite.tab('c'); | |
| 456 | 460 | k-r from:body -> ermite.tab('r'); | |
| 457 | 461 | k-m from:body -> ermite.tab('m'); | |
| @@ -467,7 +471,6 @@ | |||
| 467 | 471 | k-permalink from:body -> ermite.permalink(); | |
| 468 | 472 | k-merge from:body -> window.ermiteMR?.openMerge(); | |
| 469 | 473 | k-read from:body -> window.ermiteMR?.markRead(); | |
| 470 | - | k-gf from:body -> window.ermiteMR?.focusFiles(); | |
| 471 | 474 | k-keys from:body -> ermite.toggleKeys()"> | |
| 472 | 475 | </div> | |
| 473 | 476 | </body> | |
M internal/app/web_issue_test.go +1 -1
| @@ -28,7 +28,7 @@ func TestWebIssuePagesEndToEnd(t *testing.T) { | |||
| 28 | 28 | // Off by default: no page, no tab, for the owner as well. | |
| 29 | 29 | assert.Equal(t, 404, b.get("/proj/issues", as(token)).status) | |
| 30 | 30 | assert.Equal(t, 404, b.get("/proj/issues/new", as(token)).status) | |
| 31 | - | assert.NotContains(t, b.get("/proj", as(token)).body, `data-key="i"`) | |
| 31 | + | assert.NotContains(t, b.get("/proj", as(token)).body, `data-key="g i"`) | |
| 32 | 32 | ||
| 33 | 33 | mode := domain.IssuesOwner | |
| 34 | 34 | _, err := r.forge.Repos.Update(r.ctx, domain.Root(), r.name("proj"), ports.UpdateRepoParams{Issues: &mode}) | |
M internal/app/web_mr_test.go +1 -1
| @@ -93,7 +93,7 @@ func TestWebMergeRequestPagesRender(t *testing.T) { | |||
| 93 | 93 | require.Equal(t, 200, list.status) | |
| 94 | 94 | assert.Contains(t, list.body, "no open merge requests") | |
| 95 | 95 | assert.Contains(t, list.body, `href="/proj/compare/feat/search"`) | |
| 96 | - | assert.Contains(t, list.body, `data-key="m"`, "the section of the nav") | |
| 96 | + | assert.Contains(t, list.body, `data-key="g m"`, "the section of the nav") | |
| 97 | 97 | assert.NotContains(t, strings.ToLower(list.body), "proposal") | |
| 98 | 98 | ||
| 99 | 99 | // Open one from the compare page. | |
M internal/ports/mocks/ReleaseStore.go +66 -0
| @@ -311,6 +311,72 @@ func (_c *ReleaseStore_GetAssetByName_Call) RunAndReturn(run func(ctx context.Co | |||
| 311 | 311 | return _c | |
| 312 | 312 | } | |
| 313 | 313 | ||
| 314 | + | // GetLatestRelease provides a mock function for the type ReleaseStore | |
| 315 | + | func (_mock *ReleaseStore) GetLatestRelease(ctx context.Context, repo domain.RepoName) (domain.Release, error) { | |
| 316 | + | ret := _mock.Called(ctx, repo) | |
| 317 | + | ||
| 318 | + | if len(ret) == 0 { | |
| 319 | + | panic("no return value specified for GetLatestRelease") | |
| 320 | + | } | |
| 321 | + | ||
| 322 | + | var r0 domain.Release | |
| 323 | + | var r1 error | |
| 324 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) (domain.Release, error)); ok { | |
| 325 | + | return returnFunc(ctx, repo) | |
| 326 | + | } | |
| 327 | + | if returnFunc, ok := ret.Get(0).(func(context.Context, domain.RepoName) domain.Release); ok { | |
| 328 | + | r0 = returnFunc(ctx, repo) | |
| 329 | + | } else { | |
| 330 | + | r0 = ret.Get(0).(domain.Release) | |
| 331 | + | } | |
| 332 | + | if returnFunc, ok := ret.Get(1).(func(context.Context, domain.RepoName) error); ok { | |
| 333 | + | r1 = returnFunc(ctx, repo) | |
| 334 | + | } else { | |
| 335 | + | r1 = ret.Error(1) | |
| 336 | + | } | |
| 337 | + | return r0, r1 | |
| 338 | + | } | |
| 339 | + | ||
| 340 | + | // ReleaseStore_GetLatestRelease_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'GetLatestRelease' | |
| 341 | + | type ReleaseStore_GetLatestRelease_Call struct { | |
| 342 | + | *mock.Call | |
| 343 | + | } | |
| 344 | + | ||
| 345 | + | // GetLatestRelease is a helper method to define mock.On call | |
| 346 | + | // - ctx context.Context | |
| 347 | + | // - repo domain.RepoName | |
| 348 | + | func (_e *ReleaseStore_Expecter) GetLatestRelease(ctx any, repo any) *ReleaseStore_GetLatestRelease_Call { | |
| 349 | + | return &ReleaseStore_GetLatestRelease_Call{Call: _e.mock.On("GetLatestRelease", ctx, repo)} | |
| 350 | + | } | |
| 351 | + | ||
| 352 | + | func (_c *ReleaseStore_GetLatestRelease_Call) Run(run func(ctx context.Context, repo domain.RepoName)) *ReleaseStore_GetLatestRelease_Call { | |
| 353 | + | _c.Call.Run(func(args mock.Arguments) { | |
| 354 | + | var arg0 context.Context | |
| 355 | + | if args[0] != nil { | |
| 356 | + | arg0 = args[0].(context.Context) | |
| 357 | + | } | |
| 358 | + | var arg1 domain.RepoName | |
| 359 | + | if args[1] != nil { | |
| 360 | + | arg1 = args[1].(domain.RepoName) | |
| 361 | + | } | |
| 362 | + | run( | |
| 363 | + | arg0, | |
| 364 | + | arg1, | |
| 365 | + | ) | |
| 366 | + | }) | |
| 367 | + | return _c | |
| 368 | + | } | |
| 369 | + | ||
| 370 | + | func (_c *ReleaseStore_GetLatestRelease_Call) Return(release domain.Release, err error) *ReleaseStore_GetLatestRelease_Call { | |
| 371 | + | _c.Call.Return(release, err) | |
| 372 | + | return _c | |
| 373 | + | } | |
| 374 | + | ||
| 375 | + | func (_c *ReleaseStore_GetLatestRelease_Call) RunAndReturn(run func(ctx context.Context, repo domain.RepoName) (domain.Release, error)) *ReleaseStore_GetLatestRelease_Call { | |
| 376 | + | _c.Call.Return(run) | |
| 377 | + | return _c | |
| 378 | + | } | |
| 379 | + | ||
| 314 | 380 | // GetRelease provides a mock function for the type ReleaseStore | |
| 315 | 381 | func (_mock *ReleaseStore) GetRelease(ctx context.Context, repo domain.RepoName, id int64) (domain.Release, error) { | |
| 316 | 382 | ret := _mock.Called(ctx, repo, id) | |
M internal/ports/release.go +3 -0
| @@ -13,6 +13,8 @@ import ( | |||
| 13 | 13 | // Used includes both uploading and uploaded assets. | |
| 14 | 14 | type ReleaseWrite struct { | |
| 15 | 15 | Repo domain.RepoName | |
| 16 | + | SetLatest bool // false leaves the latest index unchanged | |
| 17 | + | LatestID int64 // zero means no eligible release when SetLatest | |
| 16 | 18 | Used int64 | |
| 17 | 19 | UsageVersion uint64 | |
| 18 | 20 | PutReleases, DeleteReleases []domain.Release | |
| @@ -21,6 +23,7 @@ type ReleaseWrite struct { | |||
| 21 | 23 | ||
| 22 | 24 | // ReleaseStore keeps metadata, never contents. *kvstore.Releases implements it. | |
| 23 | 25 | type ReleaseStore interface { | |
| 26 | + | GetLatestRelease(ctx context.Context, repo domain.RepoName) (domain.Release, error) | |
| 24 | 27 | NextReleaseID(ctx context.Context) (int64, error) | |
| 25 | 28 | ReleaseUsage(ctx context.Context, repo domain.RepoName) (used int64, version uint64, err error) | |
| 26 | 29 | GetRelease(ctx context.Context, repo domain.RepoName, id int64) (domain.Release, error) | |
M internal/usecase/releases.go +25 -9
| @@ -92,6 +92,30 @@ func (r *Releases) updateReleases(ctx context.Context, repo domain.RepoName, fn | |||
| 92 | 92 | if err = fn(&w); err != nil { | |
| 93 | 93 | return err | |
| 94 | 94 | } | |
| 95 | + | if len(w.PutReleases) > 0 || len(w.DeleteReleases) > 0 { | |
| 96 | + | w.SetLatest = true | |
| 97 | + | all, err := r.store.ListReleases(ctx, repo) | |
| 98 | + | if err != nil { | |
| 99 | + | return err | |
| 100 | + | } | |
| 101 | + | byID := make(map[int64]domain.Release, len(all)+len(w.PutReleases)) | |
| 102 | + | for _, rel := range all { | |
| 103 | + | byID[rel.ID] = rel | |
| 104 | + | } | |
| 105 | + | for _, rel := range w.DeleteReleases { | |
| 106 | + | delete(byID, rel.ID) | |
| 107 | + | } | |
| 108 | + | for _, rel := range w.PutReleases { | |
| 109 | + | byID[rel.ID] = rel | |
| 110 | + | } | |
| 111 | + | all = all[:0] | |
| 112 | + | for _, rel := range byID { | |
| 113 | + | all = append(all, rel) | |
| 114 | + | } | |
| 115 | + | if latest, ok := domain.LatestRelease(all); ok { | |
| 116 | + | w.LatestID = latest.ID | |
| 117 | + | } | |
| 118 | + | } | |
| 95 | 119 | err = r.store.ApplyReleases(ctx, w) | |
| 96 | 120 | if errors.Is(err, domain.ErrConflict) { | |
| 97 | 121 | continue | |
| @@ -245,15 +269,7 @@ func (r *Releases) GetRelease(ctx context.Context, p domain.Principal, name doma | |||
| 245 | 269 | case ref.Tag != "": | |
| 246 | 270 | rel, err = r.store.GetReleaseByTag(ctx, name, ref.Tag) | |
| 247 | 271 | default: | |
| 248 | - | var all []domain.Release | |
| 249 | - | all, err = r.store.ListReleases(ctx, name) | |
| 250 | - | if err == nil { | |
| 251 | - | var ok bool | |
| 252 | - | rel, ok = domain.LatestRelease(all) | |
| 253 | - | if !ok { | |
| 254 | - | err = domain.ErrNotFound | |
| 255 | - | } | |
| 256 | - | } | |
| 272 | + | rel, err = r.store.GetLatestRelease(ctx, name) | |
| 257 | 273 | } | |
| 258 | 274 | if err == nil && rel.Draft && !releaseWriter(p, name) { | |
| 259 | 275 | err = domain.ErrNotFound | |
M internal/usecase/releases_integration_test.go +59 -0
| @@ -263,6 +263,7 @@ func TestReleaseWriteRetriesUsageConflict(t *testing.T) { | |||
| 263 | 263 | reader.EXPECT().ResolveRevision(ctx, repo, rev).Return(domain.ResolvedRevision{}, domain.ErrNotFound) | |
| 264 | 264 | store.EXPECT().GetReleaseByTag(ctx, repo, "v1").Return(domain.Release{}, domain.ErrNotFound) | |
| 265 | 265 | store.EXPECT().NextReleaseID(ctx).Return(int64(1), nil) | |
| 266 | + | store.EXPECT().ListReleases(ctx, repo).Return(nil, nil).Twice() | |
| 266 | 267 | store.EXPECT().ReleaseUsage(ctx, repo).Return(int64(0), uint64(1), nil).Once() | |
| 267 | 268 | store.EXPECT().ApplyReleases(ctx, mock.MatchedBy(func(w ports.ReleaseWrite) bool { return w.UsageVersion == 1 && len(w.PutReleases) == 1 })).Return(domain.ErrConflict).Once() | |
| 268 | 269 | store.EXPECT().ReleaseUsage(ctx, repo).Return(int64(0), uint64(2), nil).Once() | |
| @@ -334,3 +335,61 @@ func TestReleaseUpdateTargetNeverMovesExistingTag(t *testing.T) { | |||
| 334 | 335 | require.NoError(t, err) | |
| 335 | 336 | require.Equal(t, oidA, got.TargetCommitish) | |
| 336 | 337 | } | |
| 338 | + | ||
| 339 | + | func TestReleaseLatestIndexTransitions(t *testing.T) { | |
| 340 | + | r := newReleaseRig(t, ports.ReleaseLimits{}) | |
| 341 | + | latest := func(id int64) { | |
| 342 | + | t.Helper() | |
| 343 | + | got, err := r.svc.GetRelease(r.ctx, domain.Anonymous(), r.repo, ports.ReleaseRef{Latest: true}) | |
| 344 | + | if id == 0 { | |
| 345 | + | require.ErrorIs(t, err, domain.ErrNotFound) | |
| 346 | + | return | |
| 347 | + | } | |
| 348 | + | require.NoError(t, err) | |
| 349 | + | require.Equal(t, id, got.ID) | |
| 350 | + | } | |
| 351 | + | update := func(id int64, p ports.UpdateReleaseParams) { | |
| 352 | + | t.Helper() | |
| 353 | + | _, err := r.svc.UpdateRelease(r.ctx, domain.Root(), r.repo, id, p) | |
| 354 | + | require.NoError(t, err) | |
| 355 | + | } | |
| 356 | + | yes, no := true, false | |
| 357 | + | draft := r.create(t, "draft", true) | |
| 358 | + | latest(0) | |
| 359 | + | first := r.create(t, "v1", false) | |
| 360 | + | latest(first.ID) | |
| 361 | + | update(draft.ID, ports.UpdateReleaseParams{Draft: &no}) | |
| 362 | + | latest(first.ID) // equal creation times: higher ID wins | |
| 363 | + | second := r.create(t, "v2", false) | |
| 364 | + | latest(second.ID) | |
| 365 | + | update(second.ID, ports.UpdateReleaseParams{Prerelease: &yes}) | |
| 366 | + | latest(first.ID) | |
| 367 | + | update(second.ID, ports.UpdateReleaseParams{Prerelease: &no}) | |
| 368 | + | latest(second.ID) | |
| 369 | + | update(first.ID, ports.UpdateReleaseParams{MakeLatest: &yes}) | |
| 370 | + | latest(first.ID) | |
| 371 | + | tag := "renamed" | |
| 372 | + | r.resolve(t, tag, nil) | |
| 373 | + | update(first.ID, ports.UpdateReleaseParams{Tag: &tag}) | |
| 374 | + | latest(first.ID) | |
| 375 | + | require.NoError(t, r.svc.DeleteRelease(r.ctx, domain.Root(), r.repo, first.ID)) | |
| 376 | + | latest(second.ID) | |
| 377 | + | update(second.ID, ports.UpdateReleaseParams{Draft: &yes}) | |
| 378 | + | latest(draft.ID) | |
| 379 | + | update(draft.ID, ports.UpdateReleaseParams{Draft: &yes}) | |
| 380 | + | latest(0) | |
| 381 | + | } | |
| 382 | + | ||
| 383 | + | // No ListReleases expectation: latest resolution must use only point reads. | |
| 384 | + | func TestReleaseLatestUsesIndex(t *testing.T) { | |
| 385 | + | ctx := t.Context() | |
| 386 | + | repo := name(t, "indexed") | |
| 387 | + | store := mocks.NewReleaseStore(t) | |
| 388 | + | meta := mocks.NewRepoMetaStore(t) | |
| 389 | + | meta.EXPECT().Get(ctx, repo).Return(domain.Repo{Name: repo, Visibility: domain.VisibilityPublic}, nil) | |
| 390 | + | store.EXPECT().GetLatestRelease(ctx, repo).Return(domain.Release{ID: 42, Repo: repo}, nil).Once() | |
| 391 | + | svc := usecase.NewReleases(store, nil, meta, nil, nil, nil, nil, nil, ports.ReleaseLimits{}) | |
| 392 | + | got, err := svc.GetRelease(ctx, domain.Anonymous(), repo, ports.ReleaseRef{Latest: true}) | |
| 393 | + | require.NoError(t, err) | |
| 394 | + | require.EqualValues(t, 42, got.ID) | |
| 395 | + | } | |
A internal/usecase/releases_write_test.go +27 -0
| @@ -0,0 +1,27 @@ | |||
| 1 | + | // SPDX-License-Identifier: AGPL-3.0-or-later | |
| 2 | + | package usecase | |
| 3 | + | ||
| 4 | + | import ( | |
| 5 | + | "testing" | |
| 6 | + | ||
| 7 | + | "bnema.dev/ermite/internal/domain" | |
| 8 | + | "bnema.dev/ermite/internal/ports" | |
| 9 | + | "bnema.dev/ermite/internal/ports/mocks" | |
| 10 | + | "github.com/stretchr/testify/require" | |
| 11 | + | ) | |
| 12 | + | ||
| 13 | + | // No ListReleases expectation: asset-only changes must not scan releases. | |
| 14 | + | func TestAssetOnlyWriteDoesNotListReleases(t *testing.T) { | |
| 15 | + | ctx := t.Context() | |
| 16 | + | repo, err := domain.ParseRepoName("assets") | |
| 17 | + | require.NoError(t, err) | |
| 18 | + | store := mocks.NewReleaseStore(t) | |
| 19 | + | asset := domain.ReleaseAsset{ID: 1, Repo: repo} | |
| 20 | + | store.EXPECT().ReleaseUsage(ctx, repo).Return(int64(0), uint64(2), nil).Once() | |
| 21 | + | store.EXPECT().ApplyReleases(ctx, ports.ReleaseWrite{Repo: repo, UsageVersion: 2, PutAssets: []domain.ReleaseAsset{asset}}).Return(nil).Once() | |
| 22 | + | svc := &Releases{store: store} | |
| 23 | + | require.NoError(t, svc.updateReleases(ctx, repo, func(w *ports.ReleaseWrite) error { | |
| 24 | + | w.PutAssets = append(w.PutAssets, asset) | |
| 25 | + | return nil | |
| 26 | + | })) | |
| 27 | + | } | |