From 2908367186c807e866be13da0da828fcebaf39d1 Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Thu, 14 May 2026 01:18:53 -0700 Subject: [PATCH] fix(terminal): repaint after WebGL attach (#1817) Centralize the xterm refresh after successful WebGL attachment so resumed, reparented, or setting-toggled panes do not show a stale empty WebGL canvas until the next output or resize. --- .../src/lib/pane-manager/pane-lifecycle.test.ts | 9 +++++++++ .../src/lib/pane-manager/pane-rendering-control.ts | 8 -------- .../src/lib/pane-manager/pane-webgl-renderer.ts | 11 +++++++++++ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/src/renderer/src/lib/pane-manager/pane-lifecycle.test.ts b/src/renderer/src/lib/pane-manager/pane-lifecycle.test.ts index b17788f01a3..ec9fcf177e9 100644 --- a/src/renderer/src/lib/pane-manager/pane-lifecycle.test.ts +++ b/src/renderer/src/lib/pane-manager/pane-lifecycle.test.ts @@ -96,6 +96,7 @@ describe('attachWebgl', () => { attachWebgl(pane) expect(pane.terminal.loadAddon).toHaveBeenCalledTimes(1) expect(webglMock.contextLossHandler).not.toBeNull() + vi.mocked(pane.terminal.refresh).mockClear() webglMock.contextLossHandler?.() @@ -109,6 +110,14 @@ describe('attachWebgl', () => { expect(pane.terminal.loadAddon).toHaveBeenCalledTimes(1) }) + it('repaints the current buffer after WebGL attaches', () => { + const pane = createPane() + + attachWebgl(pane) + + expect(pane.terminal.refresh).toHaveBeenCalledWith(0, 23) + }) + it('does not attach WebGL while initial rendering is deferred', () => { const pane = createPane() pane.webglAttachmentDeferred = true diff --git a/src/renderer/src/lib/pane-manager/pane-rendering-control.ts b/src/renderer/src/lib/pane-manager/pane-rendering-control.ts index af71be62902..31794923f9e 100644 --- a/src/renderer/src/lib/pane-manager/pane-rendering-control.ts +++ b/src/renderer/src/lib/pane-manager/pane-rendering-control.ts @@ -47,13 +47,5 @@ export function resumePaneRendering(panes: Iterable): void for (const pane of panes) { pane.webglAttachmentDeferred = false reattachWebglIfNeeded(pane) - // Why: fresh WebGL canvas has no content — refresh prevents frozen terminal. - if (pane.webglAddon) { - try { - pane.terminal.refresh(0, pane.terminal.rows - 1) - } catch { - /* ignore */ - } - } } } diff --git a/src/renderer/src/lib/pane-manager/pane-webgl-renderer.ts b/src/renderer/src/lib/pane-manager/pane-webgl-renderer.ts index 4fd29457595..4f0d813fa06 100644 --- a/src/renderer/src/lib/pane-manager/pane-webgl-renderer.ts +++ b/src/renderer/src/lib/pane-manager/pane-webgl-renderer.ts @@ -21,6 +21,16 @@ function shouldUseWebgl(pane: ManagedPaneInternal): boolean { ) } +function refreshTerminalAfterWebglAttach(pane: ManagedPaneInternal): void { + try { + // Why: a newly attached WebGL canvas starts empty; repaint immediately so + // resume/reparent/settings toggles do not look frozen until new output. + pane.terminal.refresh(0, pane.terminal.rows - 1) + } catch { + /* ignore — pane may have been disposed in the meantime */ + } +} + export function disposeWebgl( pane: ManagedPaneInternal, options?: { refreshDimensions?: boolean } @@ -84,6 +94,7 @@ export function attachWebgl(pane: ManagedPaneInternal): void { }) pane.terminal.loadAddon(webglAddon) pane.webglAddon = webglAddon + refreshTerminalAfterWebglAttach(pane) } catch (err) { if (pane.terminalGpuAcceleration === 'auto') { // Why: mirrors VS Code's `terminal.integrated.gpuAcceleration=auto`