feat(tui): inline rendering — conversation in native scrollback#131
Open
Fullstop000 wants to merge 9 commits into
Open
feat(tui): inline rendering — conversation in native scrollback#131Fullstop000 wants to merge 9 commits into
Fullstop000 wants to merge 9 commits into
Conversation
…lback Switch the TUI from alternate-screen Viewport::Fullscreen to Viewport::Inline: finalized blocks are pushed into the terminal's real scrollback via insert_before, so native copy, native scroll, and tmux detach/reattach all work. A fixed-height band stays pinned at the bottom; no alt-screen, no mouse capture (native selection restored). Working + dogfooded: welcome banner, user prompts, and assistant markdown responses all land in native scrollback; band pinned at bottom; /model and other pickers render in the grown viewport. WIP — remaining toward the full spec: streaming-token display (visual-row committer), strict fixed-band invariant for pickers/multiline (currently rebuilds the viewport on height change, the #21 mechanism), ask_user inline input-slot, and deletion of the now-inert in-app transcript/scroll model (leaves one dead-code warning on commit_transcript_lines).
Streaming committer (stream_commit.rs, TDD): the in-progress assistant/ reasoning block's STABLE visual rows flow into native scrollback via insert_before as they settle, instead of appearing whole on completion. The only retroactive markdown construct is a GFM table (render_md_block) — code fences are append-only, setext unsupported — so the rule is commit-per-line, holding a trailing run of outside-fence table rows. Proven by a byte-by-byte invariant test: every streamed commit is an exact prefix of the final render. Resize: ratatui 0.26's inline clear() only scrubs downward, stranding the old band above the new anchor on resize (#77; unchanged in 0.30). On any terminal size change we now full-clear + re-anchor a fresh viewport — the visible screen stays clean (conversation preserved, single band) across grow/shrink.
Inline rendering puts the conversation in the terminal's native scrollback, so the in-memory transcript Vec, scroll_offset/auto_follow/transcript_visible_rows state, the scroll_transcript_* methods, render_transcript, natural_max_offset, and the PgUp/PgDn/Ctrl+Home/End scroll-key handlers are all obsolete — removed along with their tests. PgUp/PgDn/etc. are now left to the terminal's own scrollback (mouse wheel, Shift+PgUp). A small committed_rows cursor on App replaces the runner-local row counter so /clear can't desync the incremental commit. Zero warnings, clippy clean, fmt clean.
Inline rendering only painted the band (ratatui-drawn) with the app bg; blocks pushed into scrollback via insert_before kept the terminal's default bg, leaving a two-tone seam (band dark, conversation not). render_block_into now fills the insert_before buffer with BG before blitting the lines, so the conversation scrollback matches the band. Verified with a contrasting-canvas pty screenshot: scrollback rows render at BG, not the terminal default.
In inline mode the conversation lives in the terminal's scrollback, so a session reset (/clear, /resume) that only cleared app.blocks left the old output lingering when scrolling up. Add a pending_screen_clear flag (set in the reset path); the runner wipes the visible screen + scrollback history (Clear All + Purge) and re-anchors a fresh viewport before committing the new session's blocks. Verified: a marker present before /clear is gone from the full scrollback after.
While a picker is open the band collapses to status + footer (no input box / slash / queued) — the picker is the interaction. For ask_user (inline_picker) the viewport is sized to just picker + band, so the conversation stays visible in native scrollback above it (CC-style 'replace the input bar'); slash pickers (/model etc.) still take the full screen. Removes the redundant empty input box that used to sit below an open picker.
#130 added a regression test asserting an over-wide ask_user trace stays visible via the old transcript auto-follow render path, which inline rendering removed. Re-target the same regression at the inline path: block_lines must hard-break over-wide words so every row fits the width (insert_before blits rows verbatim, so an over-width row would be truncated).
Viewport::Inline queries cursor position (ESC[6n) on startup and every rebuild; a real terminal replies. The pty harness didn't, so ratatui errored (cursor position unreadable) and the TUI exited before rendering — failing all six slash-flow e2e tests. The reader thread now answers each DSR query.
prep_history_strips_* asserted via HistoryPolicy::default(), which reads the
process-global IGNIS_HISTORY_TRIM env var; a sibling test that toggles it to
'off' raced them under parallel load (flaky). Pass an explicit
HistoryPolicy { strip_think: true } so the strip-behavior tests don't depend on
global env state. Production call site keeps default().
bcfee0b to
77795bc
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #131 +/- ##
==========================================
- Coverage 76.94% 76.83% -0.12%
==========================================
Files 66 67 +1
Lines 17671 17625 -46
==========================================
- Hits 13597 13542 -55
- Misses 4074 4083 +9 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
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.
Moves the TUI from alternate-screen
Viewport::Fullscreen(#89) to inline rendering (Viewport::Inline). Finalized conversation blocks are pushed into the terminal's real scrollback viainsert_before, so native copy, native scroll, and tmux detach/reattach persistence all work by construction. A fixed-height band stays pinned at the bottom; no alt-screen, no mouse capture.Supersedes #128 — the reattach
clear()band-aid becomes redundant once history lives in native scrollback. Close #128 in favor of this.What's in it
stream_commit.rs, TDD) — the in-progress assistant/reasoning block's stable visual rows flow into scrollback as they settle, rather than appearing whole on completion. The only retroactive markdown construct is a GFM table (code fences are append-only, setext unsupported), so the rule is commit-per-line holding a trailing run of outside-fence table rows. A byte-by-byte invariant test proves every streamed commit is an exact prefix of the final render (no row ever mutates).clear()only scrubs downward, stranding the old band above the new anchor on resize (TUI: terminal resize leaves stale input bands stacked in scrollback #77; confirmed unchanged in 0.30). We take over: full-clear + re-anchor on any size change. Visible screen stays clean (conversation preserved, single band) across grow/shrink./model,ask_user, …) render in the grown viewport with the band pinned.transcriptVec,scroll_offset/auto_follow,scroll_transcript_*,render_transcript, PgUp/PgDn handlers). Native terminal scroll replaces in-app scroll.Verification
cargo clippy --workspace --all-targets -- -D warningsclean;cargo fmt --checkclean.llm::protocols/hooksis pre-existing on master (passes in isolation), not introduced here./modelpicker,/clear, resize across 80×24 / 120×40 / 70×18.Known minor polish (follow-ups)
ask_userrenders in the grown viewport rather than strictly replacing the input slot./clearclears the logical session but not terminal scroll-up history.🤖 Generated with Claude Code