Skip to content

fix(tui): keep shrunken document tails at the buffer bottom - #873

Open
rlaope wants to merge 1 commit into
code-yeongyu:mainfrom
rlaope:fix/tui-shrink-trailing-blank-rows
Open

fix(tui): keep shrunken document tails at the buffer bottom#873
rlaope wants to merge 1 commit into
code-yeongyu:mainfrom
rlaope:fix/tui-shrink-trailing-blank-rows

Conversation

@rlaope

@rlaope rlaope commented Aug 14, 2026

Copy link
Copy Markdown

Problem

In the default main-screen renderer, any document shrink — closing the /model selector, 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 shows trailingBlankRows: 22 after the close (0 before). The existing mitigations both destroy state: PI_CLEAR_ON_SHRINK=1 emits 2J/3J (kills scrollback), and the scrollback-replay path also emitted 3J.

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.md documents the change per the fork contract.

Verification

  • New headless-xterm regression 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, no 2J/3J, bounded repaint, no duplicated history, cursor placement, growth absorption.
  • packages/tui suite: 1,177 tests pass; root npm run check and npm run build pass; coding-agent render tests pass.
  • Live end-to-end PTY repro (script at 120x40, /model open + Escape) re-measured through headless xterm: trailing blank rows 0 before, 0 with selector open, 0 after close (previously 22); close frame emits no 2J/3J and 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.

  • Applies to stable-size, non-multiplexer main-screen renders in packages/tui; resets on resize or when PI_CLEAR_ON_SHRINK=1.
  • Adds gap tracking (tailAnchorGap, tailAnchorGapIndex) and includes previousRawLines in state capture/restore; cursor offsets and line normalization account for the gap.
  • Default scrollback replay no longer emits ESC[3J; the legacy mux path keeps its previous byte shape.
  • Tests updated to assert zero trailing blank rows, no ESC[2J]/ESC[3J], bounded repaint, correct cursor placement, no duplicated history; packages/tui/src/changes.md documents the change.
  • Migration (only if you relied on scrollback clearing during shrink): set PI_CLEAR_ON_SHRINK=1 or use the legacy mux renderer. Otherwise, no action required.

Written for commit f9f7763. Summary will update on new commits.

Review in cubic

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant