Revert "fix(terminal): report the replay destination grid from measured hidden panes"

This reverts the behavior of 390ae5ce52, restoring the visibility gate on the
replay destination-grid report.

Neither position had a confirmed failure. Withholding the report leaves the
host on the snapshot grid only until the next layout change after reveal —
any window resize, split drag, or pane switch fires onResize and reports
through the normal guard chain — whereas an unguarded send from a hidden pane
is remembered before the claim branch and replayed with claim intent on the
next subscribe, reaching other clients. A stale grid that self-heals is the
more conservative failure than a durable wrong-grid claim, and gating matches
the sibling at the hidden-snapshot restore.

The condition and the test assertion are byte-identical to 6a032296b3; the
comments now record the accepted tradeoff so this line is not flipped again.
This commit is contained in:
Brennan Benson
2026-08-06 16:39:55 -07:00
parent 390ae5ce52
commit 7845cb8b46
2 changed files with 17 additions and 12 deletions
@@ -9094,10 +9094,7 @@ describe('connectPanePty', () => {
expect(transport.resize.mock.calls).toEqual([])
})
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.
it('refits but does not report the destination grid from a background measure-lease pane', async () => {
const { pane, transport, replayCallback } = await connectRemoteSnapshotGridPane(
'remote:web-env-1@@pty-replay-grid-hidden',
{ visible: false }
@@ -9106,9 +9103,12 @@ 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 reporting from a
// hidden pane would persist as a claim on the next subscribe. Its grid stays
// unreported until the next layout change after reveal; see the send condition.
expect(pane.fitAddon.fit).toHaveBeenCalled()
expect(pane.terminal.cols).toBe(120)
expect(transport.resize).toHaveBeenCalledWith(120, 40)
expect(transport.resize).not.toHaveBeenCalled()
})
it('arms no fit retry after replaying into an unmeasurable pane', async () => {
@@ -5813,16 +5813,21 @@ export function connectPanePty(
}
const destinationCols = pane.terminal.cols
const destinationRows = pane.terminal.rows
// 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.)
// Why the visibility gate, matching the sibling at the hidden-snapshot
// restore: the transport remembers every viewport handed to resize()
// before the claim branch and replays it with claim intent on the next
// subscribe, so a send from a hidden pane reaches other clients as a
// durable claim. Accepted tradeoff: a background measure-lease pane is
// fully sized, so its fit succeeds and its grid then goes unreported
// until the next layout change after reveal — fitRevealedPane will not
// re-fit a pane whose pixels never changed (opacity does not affect
// getBoundingClientRect) and remote PTYs have no ptySizeReassertion
// drift repair. A stale grid until the next resize is the more
// conservative failure than an unrevocable wrong-grid claim.
if (
destinationCols > 0 &&
destinationRows > 0 &&
isRendererPtyResizeAuthoritative() &&
!shouldSuppressDesktopPtyResize()
) {
transport.resize(destinationCols, destinationRows)