Skip to content

fix(core): keep cursor on screen when shrinking the terminal vertically#89

Open
Ramalama2 wants to merge 1 commit into
vercel-labs:mainfrom
Ramalama2:fix/resize-shrink-cursor-aware
Open

fix(core): keep cursor on screen when shrinking the terminal vertically#89
Ramalama2 wants to merge 1 commit into
vercel-labs:mainfrom
Ramalama2:fix/resize-shrink-cursor-aware

Conversation

@Ramalama2

Copy link
Copy Markdown

Problem

Terminal.resize() always keeps the top rows rows on a vertical shrink and pushes the bottom rows into scrollback, no matter where the cursor is:

// Push excess bottom rows into scrollback when shrinking vertically
if (rows < old_rows) {
    if (!self.using_alt_screen and self.scrollback != null) {
        var r: u16 = rows;
        while (r < old_rows) : (r += 1) {
            self.scrollback.?.push(&self.grid.cells[r], ...);
        }
    }
}

At a fresh or cleared shell prompt the cursor sits on row 0 and the rest of the screen is blank. Shrinking the viewport then pushes all of those trailing blank rows into scrollback, so you end up with phantom blank lines stuck above the prompt. The shell never learns that the cursor moved. The only way out is clear, and the next resize brings the blank lines right back.

To reproduce, sit at a fresh $ prompt on a tall terminal and drag the window shorter. Scroll up afterwards and there are several blank lines above the prompt that were not there before.

Fix

The shrink is now cursor-aware, the same way xterm, VTE and ghostty handle it.

  • Content scrolls off the top only, and only as far as needed to keep the cursor inside the smaller viewport (scroll = cursor_row - rows + 1, otherwise 0).
  • Rows that scroll off the top become real scrollback history.
  • Trailing rows below the cursor are discarded instead of preserved.
  • The alt screen is untouched. It has no scrollback and its app repaints on SIGWINCH, so its top rows stay in place and only the cursor is clamped.

Shrinking at a top-of-screen prompt now adds no scrollback at all. Shrinking a full screen scrolls the top into history while the cursor stays visible.

Tests

Two regression tests in src/terminal.zig:

  • resize shrink at top-of-screen prompt adds no phantom scrollback
  • resize shrink at bottom scrolls top rows into scrollback

zig build test passes and zig fmt --check src/terminal.zig is clean. packages/@wterm/core/wasm/wterm.wasm was rebuilt with zig build -Doptimize=ReleaseSmall (Zig 0.16.0) so the WASM drift check stays green.

Terminal.resize() always kept the top `rows` rows on a vertical shrink and
pushed the bottom rows into scrollback, no matter where the cursor was. At a
fresh or cleared shell prompt the cursor sits on the first row and the rest
of the screen is blank, so shrinking the viewport pushed all of those
trailing blank rows into scrollback. That left phantom blank lines stuck
above the prompt, and the shell never learns that the cursor moved. The only
way out was `clear`, and the next resize brought the blank lines right back.

This makes the shrink cursor-aware, the same way xterm, VTE and ghostty
handle it:

- Content scrolls off the top only, and only as far as needed to keep the
  cursor inside the smaller viewport (scroll = cursor_row - rows + 1, else 0).
- Rows that scroll off the top become real scrollback history.
- Trailing rows below the cursor are discarded instead of preserved.
- The alt screen keeps its current behaviour. It has no scrollback and its
  app repaints on SIGWINCH, so its top rows stay put and only the cursor is
  clamped.

Shrinking at a top-of-screen prompt now adds no scrollback at all. Shrinking
a full screen scrolls the top into history while the cursor stays visible.

Adds two regression tests.
@vercel

vercel Bot commented Jun 17, 2026

Copy link
Copy Markdown

@Ramalama2 is attempting to deploy a commit to the Vercel Labs Team on Vercel.

A member of the Team first needs to authorize it.

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