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"` InlineActionsHidden bool `json:"inlineActionsHidden"` ThinkingShown bool `json:"thinkingShown"` } // 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") } // loadPrefs reads the whole prefs file (best-effort: a missing or corrupt file yields the // zero value, i.e. everything shown / actions visible). func loadPrefs() prefs { var p prefs path := prefsPath() if path == "" { return p } if data, err := os.ReadFile(path); err == nil { _ = json.Unmarshal(data, &p) } return p } // savePrefs writes the whole prefs file (best-effort; ignores IO errors so a read-only home // never crashes the UI). Callers load, mutate one field, then save so nothing is clobbered. func savePrefs(p prefs) { path := prefsPath() if path == "" { return } if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { return } if data, err := json.MarshalIndent(p, "", " "); err == nil { _ = os.WriteFile(path, data, 0o644) } } // 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{} for _, id := range loadPrefs().StatusbarHidden { out[id] = true } return out } // saveStatusbarHidden writes the hidden set back to disk, preserving the other prefs fields. // Order follows statusSegments for a stable file. func saveStatusbarHidden(hidden map[string]bool) { var ids []string for _, seg := range statusSegments { if hidden[seg.id] { ids = append(ids, seg.id) } } p := loadPrefs() p.StatusbarHidden = ids savePrefs(p) } // saveInlineActionsHidden persists the inline-action-rows toggle, preserving other prefs fields. func saveInlineActionsHidden(hidden bool) { p := loadPrefs() p.InlineActionsHidden = hidden savePrefs(p) } // saveThinkingShown persists the reasoning-blocks toggle, preserving other prefs fields. func saveThinkingShown(shown bool) { p := loadPrefs() p.ThinkingShown = shown savePrefs(p) }