feat(tui-go): command-palette keybinds + configurable status bar
Palette: each command now shows its bare-key shortcut in a key column (and the filter matches on it), so the palette teaches the shortcuts instead of hiding them. Status bar: a new "status bar" palette command opens a toggle overlay to show/hide the non-essential segments (current stage, session status, workspace path, model, last-event clock, resource gauge); correx/connection/session-name/spinner stay. Choices persist TUI-local in tui-prefs.json (JSON in the config dir — display state, kept out of the shared/replayed server config) and reload on launch. Also trims the in-session footer (drops stats/model — both reachable via `p cmds`) so it no longer overflows + truncates `q quit` at ~100 cols after the transcript-nav additions. Tests cover toggle→render gating and prefs persistence; verified via cmd/preview renders. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -731,10 +731,43 @@ func (m Model) handleOverlayKey(k tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
m.fileFilter += string(k.Runes)
|
||||
m.fileIndex = 0
|
||||
}
|
||||
case OverlayStatusbar:
|
||||
switch {
|
||||
case k.Type == tea.KeyUp || runeIs(k, "k"):
|
||||
if m.sbIndex > 0 {
|
||||
m.sbIndex--
|
||||
}
|
||||
case k.Type == tea.KeyDown || runeIs(k, "j"):
|
||||
if m.sbIndex < len(statusSegments)-1 {
|
||||
m.sbIndex++
|
||||
}
|
||||
case k.Type == tea.KeySpace || k.Type == tea.KeyEnter || runeIs(k, "x"):
|
||||
if m.sbIndex >= 0 && m.sbIndex < len(statusSegments) {
|
||||
m.toggleStatusSegment(statusSegments[m.sbIndex].id)
|
||||
}
|
||||
case runeIs(k, "g"):
|
||||
m.overlay = OverlayNone
|
||||
}
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// toggleStatusSegment flips a status-bar segment's visibility and persists the change.
|
||||
func (m *Model) toggleStatusSegment(id string) {
|
||||
if m.sbHidden == nil {
|
||||
m.sbHidden = map[string]bool{}
|
||||
}
|
||||
if m.sbHidden[id] {
|
||||
delete(m.sbHidden, id)
|
||||
} else {
|
||||
m.sbHidden[id] = true
|
||||
}
|
||||
saveStatusbarHidden(m.sbHidden)
|
||||
}
|
||||
|
||||
// sbShow reports whether a status-bar segment is currently visible.
|
||||
func (m Model) sbShow(id string) bool { return !m.sbHidden[id] }
|
||||
|
||||
// openArtifacts opens the artifact viewer for the selected session and requests its
|
||||
// artifacts from the server. No-op when no session is selected.
|
||||
func (m *Model) openArtifacts() {
|
||||
@@ -821,22 +854,26 @@ func (m Model) handlePaletteKey(k tea.KeyMsg) (tea.Model, tea.Cmd) {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
type paletteCmd struct{ id, title, hint string }
|
||||
// paletteCmd is one command-palette row. key is the bare-key shortcut that runs the same
|
||||
// action directly (shown in the palette so the shortcuts are discoverable); empty when the
|
||||
// command has no direct key.
|
||||
type paletteCmd struct{ id, key, title, hint string }
|
||||
|
||||
func paletteCommands() []paletteCmd {
|
||||
return []paletteCmd{
|
||||
{"workflows", "start workflow", "open the workflow picker"},
|
||||
{"tools", "tool palette", "tools for the current stage"},
|
||||
{"models", "swap model", "pick / pin the local model"},
|
||||
{"events", "event inspector", "browse the event stream"},
|
||||
{"artifacts", "view artifacts", "browse this session's artifacts"},
|
||||
{"stats", "session stats", "metrics for the selected session"},
|
||||
{"sessions", "resume session", "browse / resume recent sessions"},
|
||||
{"config", "edit config", "view / change correx settings"},
|
||||
{"mode", "toggle mode", "switch chat / steering"},
|
||||
{"cancel", "cancel session", "stop the selected session"},
|
||||
{"back", "back to list", "leave the current session"},
|
||||
{"quit", "quit", "exit correx"},
|
||||
{"workflows", "w", "start workflow", "open the workflow picker"},
|
||||
{"tools", "t", "tool palette", "tools for the current stage"},
|
||||
{"models", "m", "swap model", "pick / pin the local model"},
|
||||
{"events", "e", "event inspector", "browse the event stream"},
|
||||
{"artifacts", "v", "view artifacts", "browse this session's artifacts"},
|
||||
{"stats", "S", "session stats", "metrics for the selected session"},
|
||||
{"sessions", "R", "resume session", "browse / resume recent sessions"},
|
||||
{"config", "g", "edit config", "view / change correx settings"},
|
||||
{"statusbar", "", "status bar", "show / hide status-bar segments"},
|
||||
{"mode", "s", "toggle mode", "switch chat / steering"},
|
||||
{"cancel", "c", "cancel session", "stop the selected session"},
|
||||
{"back", "l", "back to list", "leave the current session"},
|
||||
{"quit", "q", "quit", "exit correx"},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -847,7 +884,7 @@ func (m Model) filteredPalette() []paletteCmd {
|
||||
}
|
||||
var out []paletteCmd
|
||||
for _, c := range paletteCommands() {
|
||||
if strings.Contains(strings.ToLower(c.title+" "+c.hint), f) {
|
||||
if strings.Contains(strings.ToLower(c.key+" "+c.title+" "+c.hint), f) {
|
||||
out = append(out, c)
|
||||
}
|
||||
}
|
||||
@@ -875,6 +912,9 @@ func (m Model) execPalette(id string) (tea.Model, tea.Cmd) {
|
||||
return m, m.openSessions()
|
||||
case "config":
|
||||
m.openConfig()
|
||||
case "statusbar":
|
||||
m.overlay = OverlayStatusbar
|
||||
m.sbIndex = 0
|
||||
case "mode":
|
||||
m.cycleChatMode()
|
||||
case "cancel":
|
||||
|
||||
Reference in New Issue
Block a user