Skip to content

Fix stale frames on Windows when output exactly fills the terminal#971

Merged
sindresorhus merged 2 commits into
vadimdemedes:masterfrom
costajohnt:fix-windows-fullscreen-clear
Jun 12, 2026
Merged

Fix stale frames on Windows when output exactly fills the terminal#971
sindresorhus merged 2 commits into
vadimdemedes:masterfrom
costajohnt:fix-windows-fullscreen-clear

Conversation

@costajohnt

Copy link
Copy Markdown
Contributor

Fixes #969

Problem

On Windows, an app whose output is exactly the height of the terminal leaves a stale copy of the previous frame behind on every rerender. The duplicates pile up above the UI as the app repaints. The reporter of #969 hit this through qwen-code, which upgraded ink 6 to 7 in its 0.16.0 release and whose UI sizes itself to fill the terminal (screenshots of the accumulation are in their report on the qwen-code tracker).

Root cause

Ink 6 wrote clearTerminal before every frame whenever outputHeight >= rows. 32d96a6 narrowed that to strict overflow (>) so that frames exactly at terminal height rerender through the incremental eraseLines path instead, which fixed the fullscreen flicker. That path moves the cursor up previousLineCount - 1 rows and assumes nothing scrolled in between.

That assumption does not hold on Windows. Windows consoles scroll the buffer when the bottom-right cell is written, while xterm-like terminals defer the wrap until the next character. A fullscreen frame with a full-width bottom line (separators, status bars) therefore scrolls one row per repaint, the cursor-up arithmetic lands one row off, and one stale line of the previous frame leaks out the top on every render.

I verified the mechanism by capturing the raw bytes ink 6.8.0 and 7.0.5 emit for the same fullscreen app in a fixed-size PTY and replaying both streams through a terminal model with each wrap semantic: under deferred wrap both are clean, under the Windows scroll behavior ink 6 stays clean (the per-frame clear wipes any drift) while ink 7 leaks one stale line per repaint.

Fix

On Windows only, treat fullscreen-height frames the way ink 6 did: full clear between frames. Non-Windows platforms keep the incremental path and the flicker fix from 32d96a6. Windows is also the one platform where the flicker tradeoff is moot, since the clear-vs-incremental flicker difference is dwarfed by the broken output.

I could not verify on physical Windows hardware, only against the documented console behavior and the byte-stream replay above. The fix restores the exact write pattern ink 6 used on Windows for years, so the risk is the known ink 6 behavior. Happy to adjust if you'd rather gate this differently.

Test

The new fixture reuses the #450 rerender harness with process.platform overridden to win32 before ink loads, so the Windows branch is exercised on the Linux CI matrix. The test asserts a clearTerminal per fullscreen rerender (the inverse of the #450 incremental assertion) and fails without the src change.

tsc --noEmit, xo, and the full ava suite pass (1019 tests, the 4 pre-existing test.failing cases unchanged).

costajohnt and others added 2 commits June 11, 2026 20:26
ansi-escapes resolves clearTerminal per process: the win32-spoofed
fixture on a Linux kernel emits the legacy variant while the test
process counts the modern one. Count the shared eraseScreen prefix
instead.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sindresorhus sindresorhus merged commit 2c08d55 into vadimdemedes:master Jun 12, 2026
2 checks passed
@costajohnt costajohnt deleted the fix-windows-fullscreen-clear branch June 13, 2026 06:08
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.

Ink 7 rendering broken on all Windows terminals

2 participants