feat(tui-go): make the ? overlay list every keybind

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 <noreply@anthropic.com>
This commit is contained in:
2026-06-22 10:04:41 +00:00
parent 454f24fc3a
commit 035f7df704
2 changed files with 78 additions and 6 deletions
@@ -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)
}
}
}
+18 -6
View File
@@ -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"},