fix: clamp cursor-up to viewport height, preventing terminal scroll-to-top - #917
Open
MagnetonIO wants to merge 2 commits into
Open
fix: clamp cursor-up to viewport height, preventing terminal scroll-to-top#917MagnetonIO wants to merge 2 commits into
MagnetonIO wants to merge 2 commits into
Conversation
…o-top When rendered content exceeds the terminal viewport, eraseLines() and cursorUp() emit ANSI cursor-up sequences that move the cursor above the visible area into scrollback. This causes terminals to snap the viewport to the top of scrollback history — a widespread bug affecting iTerm2, VS Code, tmux, Windows Terminal, kitty, and others. The fix reads stream.rows (available on TTY streams) and clamps all cursor-up operations to the viewport height. Lines beyond the viewport have already scrolled into terminal scrollback and cannot be erased, so the clamp is semantically correct. Non-TTY streams fall back to Infinity (no clamping). Fixes the root cause behind: - anthropics/claude-code#34845 - anthropics/claude-code#33814 - anthropics/claude-code#826 - anthropics/claude-code#36582 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Send it |
Collaborator
|
I think this is still incomplete.
Also, CI is failing. |
|
Need this asap |
Address review feedback: buildCursorSuffix() was still emitting unclamped cursorUp sequences when useCursor positioned the cursor far from the bottom of tall output. Add viewportHeight parameter to buildCursorSuffix and buildCursorOnlySequence, and pass stream.rows from all call sites. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.
Summary
eraseLines()andcursorUp()calls inlog-update.tstostream.rows(viewport height)Infinity(no clamping) for non-TTY streamsRoot Cause
When Ink re-renders content that exceeds the terminal viewport,
eraseLines(previousLineCount)andcursorUp(previousLines.length - 1)emit cursor-up escape sequences (\e[NA) where N can far exceed the viewport height. Terminals follow the cursor above the visible area and snap to the top of scrollback history.This affects all terminals — iTerm2, VS Code, tmux, Windows Terminal, kitty, GNOME Terminal, and xterm.js embeddings.
The Fix
Two small helpers read
stream.rowsfrom TTY streams and clamp line counts:Applied to all 6
eraseLines/cursorUpcall sites in bothcreateStandardandcreateIncremental. The clamp is semantically correct: lines above the viewport have already scrolled into scrollback and cannot be erased or navigated to.Tests
eraseLinesclamped when content exceeds viewportcursorUpclamped when content exceeds viewportDownstream Impact
This is the root cause behind a wave of scroll-to-top bugs in tools built on Ink, most notably Claude Code:
Also affects Gemini CLI and any Ink-based TUI that renders content taller than the viewport.
🤖 Generated with Claude Code