fix(terminal): flush heavy reveal metrics after WebGL resume

This commit is contained in:
Brennan Benson
2026-08-07 01:08:51 -07:00
parent 5fd8d338d4
commit 25bf0f8fc7
2 changed files with 21 additions and 11 deletions
@@ -136,6 +136,17 @@ describe('resumeTerminalVisibility reveal repaint', () => {
expect(manager.fitAllPanes).not.toHaveBeenCalled()
})
it('leaves heavy metric flushing to the reveal fit after rendering resumes', () => {
const manager = createManager()
manager.getPanes.mockReturnValue([{ terminal: {} }])
resumeTerminalVisibility(resumeArgs(manager, false))
expect(manager.resumeRendering).toHaveBeenCalledTimes(1)
expect(manager.fitAllRevealedPanes).toHaveBeenCalledTimes(1)
expect(flushDeferredPaneMetricOptionsIfMeasurable).not.toHaveBeenCalled()
})
it('does not fit on a light tab reveal', () => {
const manager = createManager()
resumeTerminalVisibility(resumeArgs(manager, true))
@@ -145,8 +156,8 @@ describe('resumeTerminalVisibility reveal repaint', () => {
})
it('flushes hidden-era metric options on reveal and refits the light path', () => {
// P0 bold/blurry-font shape: a font change while hidden defers; the reveal
// must land it and refit, or cols/rows stay pinned to the old metrics.
// A font change while hidden must land and refit on reveal, or cols/rows
// stay pinned to the old metrics.
const manager = createManager()
manager.getPanes.mockReturnValue([{ terminal: {} }])
flushDeferredPaneMetricOptionsIfMeasurable.mockReturnValueOnce(true)
@@ -72,16 +72,13 @@ export function resumeTerminalVisibility({
// not jump to the wrong history entry.
captureViewportPositions(!wasVisible)
withSuppressedScrollTracking(() => {
// Why before the branch: metric options deferred while hidden must land ahead
// of any fit/repaint below so the first visible frame uses correct metrics.
// Still-unmeasurable panes keep their deferral; the fit path flushes those.
let flushedDeferredMetrics = false
for (const pane of manager.getPanes()) {
if (flushDeferredPaneMetricOptionsIfMeasurable(pane)) {
flushedDeferredMetrics = true
}
}
if (shouldUseLightTabResume) {
let flushedDeferredMetrics = false
for (const pane of manager.getPanes()) {
if (flushDeferredPaneMetricOptionsIfMeasurable(pane)) {
flushedDeferredMetrics = true
}
}
// Why: intra-worktree tab switches only toggle the overlay. Keeping
// synchronous drain and atlas rebuilds off this path avoids racing the
// overlay's delayed geometry fit. Still request hidden-output recovery:
@@ -100,6 +97,8 @@ export function resumeTerminalVisibility({
focusActivePane(manager)
}
} else {
// fitAllRevealedPanes flushes after WebGL reattaches, avoiding a redundant
// full refresh in the suspended DOM renderer while preserving first paint.
resumeTerminalVisibilityHeavy(manager, isActive)
}
enforceTerminalViewportIntents(manager)