fix(tui): keep shrunken document tails at the buffer bottom - #873
Open
rlaope wants to merge 1 commit into
Open
Conversation
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.
Problem
In the default main-screen renderer, any document shrink — closing the
/modelselector, a collapsing slash-command autocomplete, Ctrl+O collapse — rewrites the tail in place: the rendered content moves up within the screen while the terminal buffer bottom stays fixed. This leaves N trailing blank buffer rows (N = shrink amount), so the terminal becomes scrollable below the UI: at the fully-scrolled-down position the editor/status/footer sit rows above the screen bottom with blank space underneath. In xterm.js-based hosts with a scrollbar (IDE-embedded terminals) users can visibly scroll into the blank region.Repro (any recent release, verified on 2026.8.13 at 120x40): start senpi with enough startup output to have scrollback, run
/model, press Escape. Headless-xterm replay of the raw PTY capture showstrailingBlankRows: 22after the close (0 before). The existing mitigations both destroy state:PI_CLEAR_ON_SHRINK=1emits2J/3J(kills scrollback), and the scrollback-replay path also emitted3J.Fix
Stable-size, non-mux main-screen renders now retain a tracked blank gap (
tailAnchorGap) at the first changed document row when a mounted tail shrinks. The physical frame keeps its length, so the tail stays flush with the buffer bottom — no screen clear, no scrollback clear, no document replay, no duplicated history. Later growth consumes the gap before extending the buffer. Cursor coordinates, memoized raw lines, Kitty image row boundaries, lifecycle resets, and main-screen/fullscreen state transfer all account for the gap. The explicit legacy-mux renderer keeps its previous byte shape.packages/tui/src/changes.mddocuments the change per the fork contract.Verification
keeps a shrunken document tail flush with the buffer bottom(fails on HEAD with 5 trailing blank rows) plus updated shrink/render/SGR assertions: zero trailing rows, no2J/3J, bounded repaint, no duplicated history, cursor placement, growth absorption.packages/tuisuite: 1,177 tests pass; rootnpm run checkandnpm run buildpass; coding-agent render tests pass.scriptat 120x40,/modelopen + Escape) re-measured through headless xterm: trailing blank rows 0 before, 0 with selector open, 0 after close (previously 22); close frame emits no2J/3Jand repaint work stays below the 40-row viewport.Summary by cubic
Keep the editor/status/footer anchored to the terminal buffer bottom when main-screen content shrinks, eliminating scrollable blank space below the UI. Previously, shrink moved the tail up and left N trailing blank buffer rows; now the renderer retains an internal blank gap above the tail and consumes it on regrowth, avoiding screen/scrollback clears and history replay.
packages/tui; resets on resize or whenPI_CLEAR_ON_SHRINK=1.tailAnchorGap,tailAnchorGapIndex) and includespreviousRawLinesin state capture/restore; cursor offsets and line normalization account for the gap.packages/tui/src/changes.mddocuments the change.PI_CLEAR_ON_SHRINK=1or use the legacy mux renderer. Otherwise, no action required.Written for commit f9f7763. Summary will update on new commits.