Skip PTY resize when cols/rows didn't actually change

Most ResizeObserver firings in XtermPane don't change xterm's cell
grid — just shuffle a few pixels around. We were still calling
resizePane() (which SIGWINCHs bash, which redraws its prompt, which
emits data, which resets the idle timer). With many panes, this
created a self-reinforcing flap loop where the idle indicator would
toggle every few seconds.

Now: track the last cols/rows we actually sent to the backend in the
mount effect's closure. If a debounced fit() ends up at the same
grid dimensions, skip the resizePane call entirely. No SIGWINCH, no
prompt redraw, no data event, no idle flap.

Zero functional change for actual resizes; only suppresses redundant
PTY syscalls.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
megaproxy 2026-05-22 22:12:06 +01:00
parent daf0d4e88a
commit 9827b01688

View file

@ -175,6 +175,8 @@ export default function XtermPane({
// final size — bash gets a single clean redraw.
let resizeRaf: number | null = null;
let resizePtyTimer: number | null = null;
let lastSentCols = -1;
let lastSentRows = -1;
ro = new ResizeObserver(() => {
if (resizeRaf != null) return;
resizeRaf = requestAnimationFrame(() => {
@ -186,9 +188,17 @@ export default function XtermPane({
if (resizePtyTimer != null) clearTimeout(resizePtyTimer);
resizePtyTimer = window.setTimeout(() => {
resizePtyTimer = null;
if (paneId != null && term) {
void resizePane(paneId, term.cols, term.rows);
if (paneId == null || !term) return;
// Skip if the cell grid didn't actually change — saves a
// pointless SIGWINCH that would make bash redraw its prompt
// (which feeds back into onDataReceived and causes the idle
// indicator to flap; see the analysis around v0.2.2).
if (term.cols === lastSentCols && term.rows === lastSentRows) {
return;
}
lastSentCols = term.cols;
lastSentRows = term.rows;
void resizePane(paneId, term.cols, term.rows);
}, 150);
} catch (e) {
console.warn("resize failed", e);