From 1e445b2547c5f83a4632358f44ba4e51497eb050 Mon Sep 17 00:00:00 2001 From: Brooklyn Nicholson Date: Thu, 23 Apr 2026 13:10:52 -0500 Subject: [PATCH] fix(ui-tui): heal post-resize alt-screen drift Broaden the settle repaint from xterm.js-only to all alt-screen terminals. Ink upstream and ConPTY/xterm reports point to resize/reflow desync as a general stale-cell class, not a host-specific quirk. --- ui-tui/packages/hermes-ink/src/ink/ink.tsx | 31 +++++++++++----------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/ui-tui/packages/hermes-ink/src/ink/ink.tsx b/ui-tui/packages/hermes-ink/src/ink/ink.tsx index 5a32ceeffd..4cfb435c7c 100644 --- a/ui-tui/packages/hermes-ink/src/ink/ink.tsx +++ b/ui-tui/packages/hermes-ink/src/ink/ink.tsx @@ -70,7 +70,7 @@ import { startSelection, updateSelection } from './selection.js' -import { isXtermJs, supportsExtendedKeys, SYNC_OUTPUT_SUPPORTED, type Terminal, writeDiffToTerminal } from './terminal.js' +import { supportsExtendedKeys, SYNC_OUTPUT_SUPPORTED, type Terminal, writeDiffToTerminal } from './terminal.js' import { CURSOR_HOME, cursorMove, @@ -463,23 +463,22 @@ export default class Ink { this.resetFramesForAltScreen() this.needsEraseBeforePaint = true - // xterm.js burst-drift healer: 160ms after the last resize, force one - // full reconcile so Yoga/React catch up to the final viewport. No flag - // dance — setTimeout already drained the burst coalescer; a concurrent - // render would be idempotent. - if (isXtermJs()) { - this.resizeSettleTimer = setTimeout(() => { - this.resizeSettleTimer = null + // Post-resize drift healer: 160ms after the last resize, force one full + // reconcile so Yoga/React catch up to the final viewport and any stale + // terminal cells from host-side reflow get repainted away. Ink upstream + // and ConPTY/xterm reports point to this as a general resize/reflow + // desync class, not an xterm.js-only quirk. + this.resizeSettleTimer = setTimeout(() => { + this.resizeSettleTimer = null - if (!this.canAltScreenRepaint()) { - return - } + if (!this.canAltScreenRepaint()) { + return + } - this.resetFramesForAltScreen() - this.needsEraseBeforePaint = true - this.render(this.currentNode!) - }, 160) - } + this.resetFramesForAltScreen() + this.needsEraseBeforePaint = true + this.render(this.currentNode!) + }, 160) } // Already queued: later events in this burst updated dims/alt-screen