From 390ae5ce52fcdf6696f4ae96140e16a9ec21e477 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 6 Aug 2026 16:35:57 -0700 Subject: [PATCH] fix(terminal): report the replay destination grid from measured hidden panes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverts the visibility gate added in the previous commit; the remaining changes there stand. Gating the destination-grid report on pane visibility strands the host. A hidden pane only reaches this report when its fit measured a real box, which means the background measure lease deliberately laid it out full size for ~3s — an authoritative grid, not hidden-layout churn. Withholding the report there has no recovery: opacity does not change getBoundingClientRect, so the pane's pixels are identical at reveal, fitRevealedPane therefore neither re-fits nor re-reports, and remote PTYs are excluded from the size reassertion that would otherwise repair the drift. The host would stay pinned at the snapshot grid permanently. The measurability precheck already covers the case the gate was reaching for: a display:none pane now skips the fit entirely rather than reporting from it. Skipping an impossible fit leaves xterm on the source grid, so reveal sees a real dimension change and reports through the normal guard chain; withholding a report from a fit that succeeded leaves nothing for reveal to notice. The presence lock still applies, and this path never signals SIGWINCH. The measure-lease test now asserts the report goes out, so re-adding the gate fails it. --- .../components/terminal-pane/pty-connection.test.ts | 9 +++++---- .../src/components/terminal-pane/pty-connection.ts | 12 +++++++----- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/src/renderer/src/components/terminal-pane/pty-connection.test.ts b/src/renderer/src/components/terminal-pane/pty-connection.test.ts index 97f565a9e63..bfde8f99a5e 100644 --- a/src/renderer/src/components/terminal-pane/pty-connection.test.ts +++ b/src/renderer/src/components/terminal-pane/pty-connection.test.ts @@ -9094,7 +9094,10 @@ describe('connectPanePty', () => { expect(transport.resize.mock.calls).toEqual([]) }) - it('refits but does not report the destination grid from a background measure-lease pane', async () => { + it('reports the destination grid from a background measure-lease pane', async () => { + // Why this test: the report is deliberately NOT gated on visibility here. A hidden + // pane that still measures is under the background measure lease, so its grid is + // authoritative — and reveal never re-sends it, because the pixels never changed. const { pane, transport, replayCallback } = await connectRemoteSnapshotGridPane( 'remote:web-env-1@@pty-replay-grid-hidden', { visible: false } @@ -9103,11 +9106,9 @@ describe('connectPanePty', () => { replayCallback.current?.('remote snapshot bytes', { snapshotCols: 80, snapshotRows: 24 }) await flushAsyncTicks(20) - // A measure-lease pane is fully sized, so the fit lands — but a resize from a - // non-visible pane would SIGWINCH the host TUI and persist as the next claim. expect(pane.fitAddon.fit).toHaveBeenCalled() expect(pane.terminal.cols).toBe(120) - expect(transport.resize).not.toHaveBeenCalled() + expect(transport.resize).toHaveBeenCalledWith(120, 40) }) it('arms no fit retry after replaying into an unmeasurable pane', async () => { diff --git a/src/renderer/src/components/terminal-pane/pty-connection.ts b/src/renderer/src/components/terminal-pane/pty-connection.ts index 5dc5bf2b421..5d0ddb7c8fd 100644 --- a/src/renderer/src/components/terminal-pane/pty-connection.ts +++ b/src/renderer/src/components/terminal-pane/pty-connection.ts @@ -5813,14 +5813,16 @@ export function connectPanePty( } const destinationCols = pane.terminal.cols const destinationRows = pane.terminal.rows - // Why guarded but still fitted: xterm must leave the source grid, yet - // a background measure-lease pane is fully sized (so the fit lands) - // while not visible, and even a non-claiming resize is remembered and - // replayed as a claim on the next subscribe. + // Why NOT gated on visibility, unlike the sibling fits: reaching here + // means the fit measured a real box, which for a hidden pane means the + // background measure lease deliberately laid it out full size — an + // authoritative grid, not hidden-layout churn. Reveal will not re-send + // it either: the pixels never changed, so fitRevealedPane skips, and + // remote PTYs are excluded from ptySizeReassertion. This is the only + // chance to report. (This path never signals SIGWINCH.) if ( destinationCols > 0 && destinationRows > 0 && - isRendererPtyResizeAuthoritative() && !shouldSuppressDesktopPtyResize() ) { transport.resize(destinationCols, destinationRows)