feat(tui): move busy progress indicator above the input - #24
Merged
Conversation
The `⠸ 🧠 thinking` spinner sat in the header's top-right corner — the farthest spot from where the eyes are after submitting a prompt. It now renders on its own row between the transcript and the input box, right below the last user message. - statusBadge() keeps session-level header states (disconnected, reconnecting, approval required, ready); while busy the header segment is empty, so there is no duplicate spinner - new statusLine() carries the context-aware label (tool progress, thinking, composing) with the elapsed timer; hidden while an approval panel owns the input area or the socket is down - inputAreaHeight() accounts for the row via a shared statusLineVisible() predicate, and every busy transition (submit, done, error, disconnect, errMsg) relayouts so the viewport shrinks/reclaims the row exactly Deliberately not injected into the transcript: it would scroll away as the reply streams, and an in-transcript thinking placeholder is explicitly off-limits (AGENTS.md, TestEmptyStreamingTurnHidden).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The
⠸ 🧠 thinkingspinner lived in the header's top-right corner — the farthest spot from where your eyes are after hitting enter. It now renders on its own row between the transcript and the input box, right below the last user message.What changed
statusLine()(new) — the busy indicator (spinner + context-aware label + elapsed timer) on a dedicated row above the input; hidden when idle, while an approval panel owns the input area, or while disconnected (header badge carries those states). Zero height cost when hidden.statusBadge()— header keeps session-level states (disconnected/reconnecting…/⚠ approval required/● ready); the segment is empty while busy — no duplicate spinner, no trailing padding.inputAreaHeight()accounts for the row via a sharedstatusLineVisible()predicate; every busy transition (sendPrompt,done,error, disconnect,errMsg) callsrelayout()so the viewport shrinks/reclaims the row exactly.Design note
Deliberately not injected into the transcript below the user message: it would scroll away as the reply streams, and an in-transcript thinking placeholder is explicitly off-limits (AGENTS.md,
TestEmptyStreamingTurnHidden). The fixed line above the input sits directly below the last message at the moment it matters — the pattern other agent TUIs converged on.Tests
TestStatusBadgeStatesrewritten for the header/status-line split, incl. approval arriving mid-turn (badge announces, status line yields)TestStatusLinePlacement: renders below the last user message, absent from the header, exact +1-row layout math, hidden when idle / approval pendingChecklist
make fmtmake vetmake lint— 0 issuesmake test— full-racesuite green