Repository navigation
feat(tui): fullscreen renderer — sticky composer + in-app scroll, now the default - #319
Merged
Merged
Conversation
viewport.ts + its test restored verbatim from 0a3a871^ (MP3 deletion). lines.ts restored and re-synced to today's MessageRow: markdownToLines now consumes the shared classifyMarkdownLines classifier and wraps with layout.ts wrapLineToWidth (the computeMsgHeight ruler), new branches for banner, guardKind harness/deny, and the todowrite gate-block header, and the stream cache carries fence state across flushes so mid-fence chunks classify as code. Drift alarm: tests pin renderMessageToLines(msg, cols).length === computeMsgHeight(msg, cols) on a marker-free corpus at 40/80/100 cols — computeMsgHeight is already lockstepped to MessageRow by the inline anchor ledger, so the identity chains lines.ts to the JSX renderer executably. No runtime wiring; nothing imports these yet. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ser, /fullscreen toggle (Stage 2) The mode (ADR decision-inline-renderer.md, 2026-07-31 amendment — CC v2.1.89 shipped the equivalent opt-in, clearing the §5 bar): - --fullscreen / --inline / --no-fullscreen (tri-state) + MINIMA_TUI_FULLSCREEN / MINIMA_TUI_INLINE env mirrors + per-project persisted /fullscreen pref (mode_prefs.ts suffix-key pattern). Inline stays the default. - Alt-screen lifecycle owned by the new altscreen.ts (idempotent enter/exit) — main.ts never carries the ?1049 literal (render-buffer guard retitled, kept). - Line viewport over staticItems via the restored viewport.ts/lines.ts: banner scrolls away with the transcript, the FULL streaming reply + reasoning peek live at the virtual tail, one permanent status row (pinned<->scrolled crossings never move the composer) with an N-new-messages counter latched on leaving follow. - Keys: PgUp/PgDn page the viewport (mid-run allowed); End jumps to newest via a consumable TextInput onEnd first-look (readline default preserved otherwise); submitting and every transcriptGen bump re-pin. - /fullscreen toggles mid-session both ways: enter paints the next commit into the alt screen; exit restores the main buffer and reuses the /clear-class reseat + gen bump so <Static> reprints the whole transcript into native scrollback (fullscreen-period messages included). - Anchor ledger bypassed while fullscreen (frozen refs; the exit gen bump takes the reset path). suspendToShell regains SuspendOptions and re-arms the alt screen on SIGCONT. D3b panels render in the same slot via a shared panelNode. Guards: cli.test.ts asserts the tri-state flags; kill-switches declares the env mirrors; reseat count 2 -> 3. Full unit suite + the whole inline PTY verify suite (tui_verify.sh) green — inline output unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…n hint (Stage 3) Wires the input-filter seams that have sat decoded-but-unconsumed since MP3: setMouseScrollCallback (33ms-coalesced notches -> scrollLinesBy, 3 lines/notch) and setClickCallback (a click under capture is a doomed drag-select — the status row shows the Option/Shift-drag + /mouse + Ctrl+Y escape hatches for 4s). Capture defaults ON in fullscreen (CC parity — the OLD selection-first OFF default is what earned "feels broken"); wheel-only, never ?1002/?1003. /mouse toggles it (inline gets an explanatory note); suspend re-arms capture on SIGCONT; /copy's selection advice branches on the capture state. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…inal (Stage 4) Five scenarios, byte + frame asserted, raw streams deliberately excluded from the inline no-mouse-capture sweep: - fs-basic: wheel storm mid-stream detaches the viewport (status row flips, composer stays bottom-anchored), the SLOW reply commits while scrolled and the N-new-messages counter appears without yanking the view, a click arms the selection hint, End re-pins to the newest content, exit leaves the alt screen. - fs-toggle: /fullscreen on mid-session enters the alt screen; off leaves it, replays the reseat clear, and one frame shows the OFF notice together with the pre-toggle transcript (the <Static> reprint proof, timing-jitter-proof). - fs-persist: a second session on the same prefs dir boots fullscreen from the persisted pref alone, no flag. - fs-suspend: Ctrl+Z drops capture + alt screen before SIGTSTP; SIGCONT re-arms both, ordered by raw byte offsets. - fs-perm: the bash permission overlay renders and answers inside the height=rows frame. Full tui_verify.sh green end to end — all inline scenarios byte-clean with the fs scenarios in the same run. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ndment) Resolution: --fullscreen/--inline flag > MINIMA_TUI_FULLSCREEN/INLINE env > the per-project persisted /fullscreen choice > fullscreen. The pref goes tri-state (on/off/unset) so an explicit inline choice persists and a future default flip never fights a stale stored value. Inline stays fully supported one command away; the banner tagline and help text now describe the fullscreen default. tui_verify.sh: BASE_ARGV (no renderer flag) vs INLINE_ARGV (pins --inline) so every inline scenario stays on the main-buffer path it asserts; fs-persist now proves the bare-boot fullscreen default AND that a persisted inline choice survives a restart. Validated: unit suite green; echo-budget stays inline under --inline; all five fs-* scenarios pass with the new default. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…c closers Ink's Text transform (wrap-ansi) re-encodes embedded ANSI and closes a combined ESC[37;48;2;...m open with 39m only — the truecolor background stayed active past end-of-line and background-color-erase flooded whole terminal rows with the user-bubble color (reported from a live session; reproduced in the PTY: every bubble row left the app with an unclosed bg). lines.ts paint()/emitStyledRow now emit ONE code per CSI with its specific closer (1/2->22, 3->23, 48;2->49, fg->39) and never rely on 0m, which the transform also rewrites. An empirical probe showed separate single-code sequences pass through the transform verbatim. tests/lines-bce.test.tsx pins both layers: the strings (no combined CSIs; every bg open paired with a 49m) and Ink's actual emitted output through the real truncate transform. PTY repro confirms 0 rows with active bg at EOL. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The branch reinstates what MP3 (MUB-146, 0a3a871) deleted, per the ADR's two 2026-07-31 amendments; main has since been through the merge wave, including #316's extraction of app.tsx's effects into hooks. Resolutions: - app.tsx COMMANDS -> allCommands(): main's memoized builder wins, with the `fullscreen` and `mouse` entries re-added inside it, and the `/fullscreen` line restored to /help (which now interpolates keyHelp()). - The resize effect and the beforeToolCall wiring are dropped from the branch's side: #316 owns them now (useTerminalSize, useToolCallHooks). Only the renderer-specific effects — live-reply cache reset, wheel/click seams, SGR mouse reporting — stay inline. - persistMode moved to use_session_boot.ts, so app.tsx imports only loadTaskPanelHidden/persistFullscreenPref/persistTaskPanelHidden. - footer.ts was removed by #341 in favour of context_meter.ts; every footerStatsFromMessages call site had already become ctxFor, so only the dead import needed dropping. - render-buffer.test.ts: the reseat now has FOUR sites, not three — the branch and main each added a different third one (/fullscreen exit and the $EDITOR return). Count and paired-count both go to 4. The branch's replacement for "no renderer selection remains" stands: main.ts may name the renderer, it just may not write ?1049 itself. - editor-wiring.test.ts pinned the whole Ctrl+Z statement; suspendToShell now takes renderer state, so the pin is the prefix. What it asserts is order. - lines-bce.test.tsx needed an `override` for #315's check:tests bar. 3244 pass, 0 fail.
shankha98
added a commit
that referenced
this pull request
Aug 10, 2026
Two import unions, plus one substantive follow-through: #319's fullscreen renderer landed after this branch's token pass, so its scroll/select hint was the one colour prop in the tree still hard-coding "gray". Themed it — otherwise picking a colorscheme would restyle every surface except the new default renderer. Zero `color="..."` literals remain in src/tui. 3250 pass, 0 fail.
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.
What
Brings back the fullscreen renderer as a first-class mode — alt-screen frame, line-viewport transcript, composer glued to the bottom row, wheel/PgUp/PgDn history scroll with a
↑ scrolled up · N new messagesstatus row, End re-pins to newest — and makes it the default (user decision, recorded as ADR amendments indocs/BigPlan/decision-inline-renderer.md). Inline remains fully supported one command away:--inline,/fullscreen(persisted per project, tri-state pref), orMINIMA_TUI_INLINE=1.Context: Claude Code CLI v2.1.89 shipped the equivalent opt-in fullscreen mode, clearing the ADR §5 re-introduction bar the 2026-07-16 amendment set.
How
viewport.tsverbatim (LineIndex prefix sums, binary-search windowing,null=follow scroll state) andlines.tsre-synced to today'sMessageRowvia the sharedclassifyMarkdownLinesclassifier. Drift alarm: tests pinrenderMessageToLines(msg, cols).length === computeMsgHeight(msg, cols)on a marker-free corpus — computeMsgHeight is already lockstepped to MessageRow by the inline anchor ledger, so the identity chains lines.ts to the JSX renderer executably. The stream cache carries fence state across flushes.--fullscreen/--inlineflags + env mirrors + per-project persisted/fullscreenchoice; alt-screen lifecycle owned by the newaltscreen.ts(main.tsstays free of the?1049literal — the render-buffer guard keeps that pin); viewport overstaticItemswith the full streaming reply in-scroll; constant-height status row (pinned↔scrolled crossings never move the composer); PgUp/PgDn + a consumable TextInputonEnd; mid-session toggle both ways (exit reuses the /clear-class reseat + gen bump so<Static>reprints the whole transcript into native scrollback); anchor ledger cleanly bypassed while fullscreen;suspendToShellre-arms on SIGCONT; D3b panels render in the same slot.input-filter.tsseams); click under capture → 4s selection-hint (Option/Shift-drag);/mousefrees the wheel for native selection.fs-*PTY scenarios intui_verify.sh(scroll mid-stream, counter, toggle round-trip with reprint proof, pref persistence across restart, suspend/resume re-arm, permission overlay in-frame), raw streams deliberately excluded from the inline no-mouse sweep; inline scenarios now pin--inline(BASE_ARGVvsINLINE_ARGV).ESC[37;48;2;…mwith39monly, leaving the truecolor background open past EOL → BCE flooded rows with the bubble color.paint()/emitStyledRownow emit one SGR code per sequence with its specific closer;tests/lines-bce.test.tsxpins both the strings and Ink's actual post-transform output.Verification
tsc, biome, terminology guard clean.fs-*PTY scenarios pass against bothbun runand the compiled binary; bare boot arms the alt screen (default proof) and an explicit inline choice survives restarts.echo-budgetbyte-clean under--inline; a 14-stream sweep shows inline never emits a capture/alt-screen byte.mainin a worktree): the fresh-bootbottom-anchorslack-0/1 checks,plan-exit-gate, andacceptanceare timing-broken on at least one machine — the suite's own header says "slack 2 on fresh"; follow-up candidate.🤖 Generated with Claude Code