feat(tui-go): global input history + stop idle redraws (copy-safe)

Input history is now a single global ring (recordInput) shared by every input surface —
free-chat, the workflow-intent line, and in-session chat — so ↑/↓ recalls anything you've
sent, including outside a session (previously per-session, so idle/intent had none).

Copy fix: the 120ms animation tick now only runs while something actually animates
(animating(): active session spinner, blinking caret, reconnect). Init no longer starts it
unconditionally; tickKick (re)starts it on demand and tickMsg stops it when idle. An idle
screen no longer auto-redraws, so a native terminal text selection survives instead of being
wiped every frame. Tests cover the ring (dedup/cap/cross-context walk) and the idle gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-21 09:33:16 +00:00
parent 7d4e203429
commit 9143d554f1
3 changed files with 173 additions and 19 deletions
+7 -5
View File
@@ -228,9 +228,9 @@ type Model struct {
inputMode InputMode
inputBuffer string
inputCursor int
history map[string][]string
historyIndex int
savedBuffer string
inputHistory []string // submitted lines across all contexts (chat, intent, in-session), newest last
historyIndex int // -1 = editing the live buffer; else an index into inputHistory
savedBuffer string // live buffer stashed while walking history
// flow flags
sessionEntered bool
@@ -302,7 +302,10 @@ type Model struct {
paletteIndex int
// animation
frame int // tick counter; drives spinner + caret blink
frame int // tick counter; drives spinner + caret blink
ticking bool // true while a tick loop is scheduled. The loop is gated on animating()
// so an idle screen stops redrawing — which lets native terminal selection survive
// (the 120ms redraw used to wipe a mouse drag every frame).
// snapshot phase
snapshotPhase bool
@@ -338,7 +341,6 @@ func NewModel(client *ws.Client) Model {
theme: NewTheme(SoftBlue),
wfIndex: -1,
inputMode: ModeRouter,
history: map[string][]string{},
historyIndex: -1,
routerMessages: map[string][]RouterEntry{},
configStaged: map[string]string{},