Add specs for the siGit Code TUI tabs and thinking display

The tabbed TUI (sigit #25) and the thinking display with /thinking (sigit #26) were built from direct requests rather than the parity roadmaps, so their behavior had no source of truth here. This records both as after-the-fact specs alongside the parity docs: tab bar and navigation rules, History and Cloud tab contents and keys, the approval auto-switch, the live reasoning tail, the collapsed indicator, and the display-only boundaries. Ends with follow-ups, including an Issues and PRs tab once the client consumes the new MCP repo tools.

Seto Elkahfi committed Jul 5, 2026 at 08:44 UTC 6a879f65c26539457da2a711bff15f2c07ce49bd
1 file changed +108
docs/product/sigit-code-tui-features.md
+108
new file mode 100644 index 0000000..6e49f98 --- /dev/null +++ b/docs/product/sigit-code-tui-features.md @@ -0,0 +1,108 @@ +# siGit Code TUI features: tabs and thinking display + +Status: shipped, pending merge (sigit #25 and #26, 2026-07-05). Owner: product/eng. +Audience: internal (private repo). Scope: the **siGit Code** interactive terminal +UI (`src/chat.rs`, Unix-only). These two features were built from direct requests +rather than the parity roadmaps; this document records their specs after the +fact so the behavior has a source of truth alongside +[sigit-code-parity-roadmap.md](sigit-code-parity-roadmap.md) and +[sigit-code-copilot-cli-parity.md](sigit-code-copilot-cli-parity.md). + +--- + +## 1. Spec: tabbed TUI (sigit #25) + +Inspired by GitHub Copilot CLI's June 2026 terminal redesign (Session, Gists, +Issues, and PR tabs switched with the Tab key). + +### Behavior + +Three tabs, rendered as a bar at the top of the screen on every view: + +- **Session** (default): the chat exactly as before, messages, input, footer. +- **History**: the saved sessions from the session store + (`$SIGIT_CONFIG_DIR/sessions/*.jsonl`), listed newest first with id, coarse + age ("2m ago"), and message count. Up/Down selects. Enter restores the + selected session into the live backend, pushes a system message saying how + many messages were restored, and returns to the Session tab. `d` starts a + delete; a second `d` on the same selection confirms, any other key cancels. + The pending delete is keyed by session id, not list index, so a refresh can + never redirect the confirmation. `r` refreshes. An empty store shows "No + saved sessions yet." +- **Cloud**: siGit Code Cloud status and settings. Shows the signed-in account, + on-device vs remote inference, current model and engine state, the Local + Inference setting, the permission policy summary, and the config dir. `l` + toggles Local Inference, with a note that the toggle takes effect at the next + model selection because the running backend is not swapped. `r` refreshes. + +### Interaction rules + +- Tab cycles Session, History, Cloud. It only cycles when the input buffer is + empty, so pasted text containing tabs cannot hijack navigation; on non-Session + tabs the input is inactive so Tab always cycles. Esc on a non-Session tab + returns to Session. +- Inference keeps streaming while the user is on another tab. Restoring history + sits behind the busy gate so the conversation cannot be swapped mid-turn. +- A tool approval prompt arriving while on another tab auto-switches to the + Session tab: a y/a/n prompt must never be invisible. +- The Cloud status fetch runs as a spawned task feeding the render loop over a + oneshot, because `account::status_line()` can hit the network and must not + freeze rendering. A dead fetch degrades to "press r to retry". + +### Implementation notes + +Pure logic (the tab cycle, age and row formatting, `session_store::list`) lives +outside the Unix-only `mod tui` behind the same cfg-gate pattern as +`permissions::TUI_SESSION`, so all four CI targets compile and unit-test it. + +--- + +## 2. Spec: thinking display with `/thinking` (sigit #26) + +Reasoning models (Qwen 3, DeepSeek-R1 distills) emit their thought process in +`<think>` tags. Before this feature the TUI hid reasoning behind the spinner +while streaming and then rendered it as an unconditional box on every finished +message. Both extremes were wrong: watching the model reason is useful, reading +every past thought is noise. + +### Behavior + +- **While streaming**, if the turn is still inside its think block, the TUI + shows the last three wrapped lines of the reasoning live, dim italic, under + the thinking spinner. Once visible (non-think) text arrives, the reply + streams as before. +- **Finished replies** collapse the reasoning to a one-line dim indicator with + the line count, e.g. `· thought for a bit (12 lines), /thinking to show`. +- **`/thinking [on|off]`** (bare flips) expands the full reasoning block, dim + and visually separated, above each reply across the whole transcript, and + collapses it again when turned off. + +### Boundaries + +- Display only. Nothing changes in what is sent to the model, what is saved to + the session store, or what goes over ACP, where reasoning stays stripped and + editors render their own thought UI. `/thinking` is TUI-only and not + advertised over ACP, the same precedent as `/resume`. +- Restored sessions re-split think blocks on entry, so `/resume` and the + History tab keep the indicator behavior. +- The TUI streams only when no tools are offered (on-device tool turns buffer + to detect tool calls), so the live tail appears exactly on the turns that can + stream, which are also the turns where think deltas exist. + +### Implementation notes + +The live-tail extraction is a pure, width-aware function with its own greedy +word wrap (hard-splitting over-wide words), unit-tested without ratatui. + +--- + +## 3. Follow-ups worth considering + +- Copilot CLI's Issues and Pull requests tabs have a natural sigit equivalent + once the MCP repo workflow tools (sigit-si #3) are consumed by the client: a + fourth tab listing issues and PRs for the current repo when its origin points + at sigit.si. +- A persistent setting for the thinking toggle (settings.toml) if the + per-session default proves annoying. +- Coarse ages in the History tab could show absolute timestamps on a keypress + if precision is ever needed.