From 035f7df7044f750b1af2ed7df37a5a00ec5e1e27 Mon Sep 17 00:00:00 2001 From: kami Date: Mon, 22 Jun 2026 10:04:41 +0000 Subject: [PATCH] feat(tui-go): make the ? overlay list every keybind MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Audited handleNormalKey / handleApprovalKey / the OverlayDiff handler against the help cheat-sheet and found gaps: enter, ↑↓/jk, /, s, c, a, w, q and ? itself were bound but undocumented. Reorganise helpBody into navigate / session / overlays / approval band / diff-viewer groups covering all of them. help_complete_test.go asserts a row exists for every binding so the help can't silently drift from the handlers. The longer sheet scrolls fine via the modal-scroll support. Co-Authored-By: Claude Opus 4.8 --- .../tui-go/internal/app/help_complete_test.go | 60 +++++++++++++++++++ apps/tui-go/internal/app/overlays.go | 24 ++++++-- 2 files changed, 78 insertions(+), 6 deletions(-) create mode 100644 apps/tui-go/internal/app/help_complete_test.go diff --git a/apps/tui-go/internal/app/help_complete_test.go b/apps/tui-go/internal/app/help_complete_test.go new file mode 100644 index 00000000..38458693 --- /dev/null +++ b/apps/tui-go/internal/app/help_complete_test.go @@ -0,0 +1,60 @@ +package app + +import ( + "strings" + "testing" +) + +// Every key the TUI binds (normal mode, approval band, and the diff/preview viewer) must +// have a row in the ? overlay. Keyed by a distinctive description phrase so deleting a row +// fails loudly here — keep this list in step with handleNormalKey / handleApprovalKey and +// the OverlayDiff handler whenever a binding is added or changed. +func TestHelpCoversEveryKeybind(t *testing.T) { + m := inSessionModel(120, 80) + help := strings.Join(m.helpBody(), "\n") + + want := []string{ + // navigate + "move the session / list selection", // ↑↓ / j k + "open the selected session", // enter + "filter the session list", // / + "jump to your previous / next message", // ^↑ / ^↓ + "scroll output", // PgUp / PgDn (+ ^u / ^d) + "drop selection", // esc + "back to the session list", // l + // session + "compose a message", // i + "toggle chat / steering mode", // s + "show workflows", // w + "copy the selected message", // y + "open the latest diff / preview", // ^x + "cancel the running session", // c + "re-open a dismissed approval gate", // a + "quit", // q + // overlays + "command palette", // p + "this keybindings help", // ? + "event inspector", // e + "tools / artifacts", // t / v + "resume past sessions", // S / R + "swap model / edit config", // m / g + "grants / idea board", // G / I + // approval band + "approve once", // y / a / enter + "reject", // n / r + "steer (add a note)", // e / s + "approve-always", // A + "walk the pending queue", // ↑ / ↓ + "fullscreen the diff / command preview", // ^x + "dismiss for now", // esc + // diff / preview viewer + "scroll a line", // ↑ / ↓ + "scroll a page", // PgUp / PgDn + "jump to top / end", // g / G + } + for _, w := range want { + if !strings.Contains(help, w) { + t.Errorf("? help is missing a row for %q", w) + } + } +} diff --git a/apps/tui-go/internal/app/overlays.go b/apps/tui-go/internal/app/overlays.go index 503c616b..5d3787d4 100644 --- a/apps/tui-go/internal/app/overlays.go +++ b/apps/tui-go/internal/app/overlays.go @@ -818,20 +818,31 @@ func (m Model) helpBody() []string { } out = append(out, "") } - section("session", []kb{ - {"i", "compose a message"}, + section("navigate", []kb{ + {"↑↓ / j k", "move the session / list selection"}, + {"enter", "open the selected session"}, + {"/", "filter the session list"}, {"^↑ / ^↓", "jump to your previous / next message"}, - {"y", "copy the selected message (OSC52)"}, {"PgUp / PgDn", "scroll output (^u / ^d half-page)"}, {"esc", "drop selection / scroll → follow newest"}, - {"^x", "open the latest diff"}, {"l", "back to the session list"}, }) + section("session", []kb{ + {"i", "compose a message"}, + {"s", "toggle chat / steering mode"}, + {"w", "show workflows (idle screen)"}, + {"y", "copy the selected message (OSC52)"}, + {"^x", "open the latest diff / preview"}, + {"c", "cancel the running session"}, + {"a", "re-open a dismissed approval gate"}, + {"q", "quit"}, + }) section("overlays", []kb{ - {"p", "command palette (shows all shortcuts)"}, + {"p", "command palette (all shortcuts)"}, + {"?", "this keybindings help"}, {"e", "event inspector (/ to filter)"}, {"t / v", "tools / artifacts"}, - {"S / R", "session stats / resume sessions"}, + {"S / R", "session stats / resume past sessions"}, {"m / g", "swap model / edit config"}, {"G / I", "grants / idea board"}, }) @@ -842,6 +853,7 @@ func (m Model) helpBody() []string { {"A", "approve-always — choose scope"}, {"↑ / ↓", "walk the pending queue"}, {"^x", "fullscreen the diff / command preview"}, + {"esc", "dismiss for now (a re-opens)"}, }) section("diff / preview viewer (^x)", []kb{ {"↑ / ↓", "scroll a line"},