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:
@@ -0,0 +1,80 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// prefs is the TUI's local, persisted preferences (display-only client state — kept out
|
||||
// of the server config, which is shared/replayed). Stored as JSON in the correx config dir.
|
||||
type prefs struct {
|
||||
StatusbarHidden []string `json:"statusbarHidden"`
|
||||
}
|
||||
|
||||
// statusSegment is one toggleable status-bar segment. The always-on segments (correx label,
|
||||
// connection, session name, spinner, background-updates) are not listed — they are load-bearing.
|
||||
type statusSegment struct{ id, label string }
|
||||
|
||||
var statusSegments = []statusSegment{
|
||||
{"stage", "current stage (⟐)"},
|
||||
{"status", "session status"},
|
||||
{"workspace", "workspace path (⌂)"},
|
||||
{"model", "model name"},
|
||||
{"clock", "last-event clock"},
|
||||
{"gauge", "resource gauge"},
|
||||
}
|
||||
|
||||
func prefsPath() string {
|
||||
dir := os.Getenv("CORREX_CONFIG_HOME")
|
||||
if dir == "" {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
dir = filepath.Join(home, ".config", "correx")
|
||||
}
|
||||
return filepath.Join(dir, "tui-prefs.json")
|
||||
}
|
||||
|
||||
// loadStatusbarHidden reads the persisted hidden-segment set (best-effort: a missing or
|
||||
// corrupt file yields an empty set, i.e. everything shown).
|
||||
func loadStatusbarHidden() map[string]bool {
|
||||
out := map[string]bool{}
|
||||
path := prefsPath()
|
||||
if path == "" {
|
||||
return out
|
||||
}
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return out
|
||||
}
|
||||
var p prefs
|
||||
if json.Unmarshal(data, &p) == nil {
|
||||
for _, id := range p.StatusbarHidden {
|
||||
out[id] = true
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// saveStatusbarHidden writes the hidden set back to disk (best-effort; ignores IO errors so
|
||||
// a read-only home never crashes the UI). Order follows statusSegments for a stable file.
|
||||
func saveStatusbarHidden(hidden map[string]bool) {
|
||||
path := prefsPath()
|
||||
if path == "" {
|
||||
return
|
||||
}
|
||||
var ids []string
|
||||
for _, seg := range statusSegments {
|
||||
if hidden[seg.id] {
|
||||
ids = append(ids, seg.id)
|
||||
}
|
||||
}
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
||||
return
|
||||
}
|
||||
if data, err := json.MarshalIndent(prefs{StatusbarHidden: ids}, "", " "); err == nil {
|
||||
_ = os.WriteFile(path, data, 0o644)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user