Skip to content

fix(render): force per-frame clear in alt-screen to kill log-update drift#640

Merged
esengine merged 1 commit into
mainfrom
fix/issue-639-altscreen-clear-frame
May 11, 2026
Merged

fix(render): force per-frame clear in alt-screen to kill log-update drift#640
esengine merged 1 commit into
mainfrom
fix/issue-639-altscreen-clear-frame

Conversation

@esengine

Copy link
Copy Markdown
Owner

Summary

  • Patches ink@7.0.2's renderInteractiveFrame so that, in alt-screen mode, every frame is preceded by clearTerminal instead of eraseLines(previousLineCount). Alt-screen has no scrollback, so the per-frame clear is invisible; incrementalRendering is already disabled (src/cli/commands/chat.tsx:571) so we were already paying the full-frame stdout cost.
  • Adds patch-package + "postinstall": "patch-package" so the diff survives reinstalls.
  • ink@7.0.2 is the current latest on npm — no upstream release to bump to.

Why the existing fallback didn't catch it

shouldClearTerminalForFrame in node_modules/ink/build/ink.js:83-102 already clears when outputHeight > viewportRows. But outputHeight comes from Yoga, which measures via string-width's default ambiguous-width = 1. On East Asian terminals that render those characters as width 2, Yoga undercounts and the fallback never fires — exactly the case that triggers the user-visible drift.

The underlying log-update bookkeeping (node_modules/ink/build/log-update.js:52) tracks previousLineCount = lines.length (logical newlines), so when actual visual rows > logical rows, eraseLines(N) undershoots and the top of the previous frame survives. That's what the reporter's screenshot shows: a duplicated status bar with two horizontal separators stacked at the bottom of the viewport.

Closes #639

Test plan

Upstream

Worth filing the same change against vadimdemedes/ink once we have the reporter's confirmation — but we ship the local patch in the meantime so users don't wait on upstream review.

…rift

ink@7.0.2's log-update tracks previousLineCount as logical newlines, not
visual rows. When any rendered line wraps because its visual width
exceeds terminal columns — which happens on CJK / ambiguous-width
content under East Asian terminals — the clear region on the next frame
undershoots the actual on-screen footprint and the bottom of the
previous frame survives. The reported symptom on Windows Terminal at
120×30 with zh-CN content is a duplicated status bar (two horizontal
separators stacked) and stale residue creeping up from below.

shouldClearTerminalForFrame already handles the overflow case, but it
gates on Yoga's outputHeight — which can itself undercount visual rows
for the same width-misjudgment reason, so the fallback misses exactly
the cases that trigger the bug.

Fix: in alt-screen mode, take the clearTerminal path unconditionally.
Alt-screen has no scrollback, so per-frame clear costs nothing visible;
incrementalRendering is already off (chat.tsx:571) so we're paying the
full-frame stdout cost regardless. Patched via patch-package so the fix
survives reinstalls until the same shape lands upstream.

Closes #639
@esengine esengine merged commit 940d8aa into main May 11, 2026
3 checks passed
@esengine esengine deleted the fix/issue-639-altscreen-clear-frame branch May 11, 2026 05:42
ChasLui pushed a commit to ChasLui/DeepSeek-Reasonix that referenced this pull request May 23, 2026
…rift (esengine#640)

ink@7.0.2's log-update tracks previousLineCount as logical newlines, not
visual rows. When any rendered line wraps because its visual width
exceeds terminal columns — which happens on CJK / ambiguous-width
content under East Asian terminals — the clear region on the next frame
undershoots the actual on-screen footprint and the bottom of the
previous frame survives. The reported symptom on Windows Terminal at
120×30 with zh-CN content is a duplicated status bar (two horizontal
separators stacked) and stale residue creeping up from below.

shouldClearTerminalForFrame already handles the overflow case, but it
gates on Yoga's outputHeight — which can itself undercount visual rows
for the same width-misjudgment reason, so the fallback misses exactly
the cases that trigger the bug.

Fix: in alt-screen mode, take the clearTerminal path unconditionally.
Alt-screen has no scrollback, so per-frame clear costs nothing visible;
incrementalRendering is already off (chat.tsx:571) so we're paying the
full-frame stdout cost regardless. Patched via patch-package so the fix
survives reinstalls until the same shape lands upstream.

Closes esengine#639
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.

Scroll/flicker during streaming output on standalone Windows Terminal (Win11 22H2)

1 participant