From 2d755ba386ffef975eff6a2e452cedbe3e1ef8bd Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Fri, 17 Apr 2026 17:15:27 -0700 Subject: [PATCH] fix: free WebGL contexts for hidden worktrees to prevent terminal dying (#777) --- .../components/tab-group/TabGroupPanel.tsx | 22 +++++++++++++++---- .../tab-group/TabGroupSplitLayout.test.ts | 3 +++ .../tab-group/TabGroupSplitLayout.tsx | 1 + 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/renderer/src/components/tab-group/TabGroupPanel.tsx b/src/renderer/src/components/tab-group/TabGroupPanel.tsx index 55646fb38a3..b300e2592e4 100644 --- a/src/renderer/src/components/tab-group/TabGroupPanel.tsx +++ b/src/renderer/src/components/tab-group/TabGroupPanel.tsx @@ -21,6 +21,7 @@ const EditorPanel = lazy(() => import('../editor/EditorPanel')) export default function TabGroupPanel({ groupId, worktreeId, + isWorktreeActive, isFocused, hasSplitGroups, reserveClosedExplorerToggleSpace, @@ -30,6 +31,7 @@ export default function TabGroupPanel({ }: { groupId: string worktreeId: string + isWorktreeActive: boolean isFocused: boolean hasSplitGroups: boolean reserveClosedExplorerToggleSpace: boolean @@ -281,8 +283,15 @@ export default function TabGroupPanel({ // Why: in multi-group splits, the active terminal in each group // must remain visible (display:flex) so the user sees its output, // but only the focused group's terminal should receive keyboard - // input. isVisible controls rendering; isActive controls focus. - isVisible={activeTab?.id === item.id && activeTab.contentType === 'terminal'} + // input. Hidden worktrees stay mounted offscreen, so `isVisible` + // must also respect worktree visibility or those detached panes + // keep their WebGL renderers alive and exhaust Chromium's context + // budget across worktrees. + isVisible={ + isWorktreeActive && + activeTab?.id === item.id && + activeTab.contentType === 'terminal' + } onPtyExit={(ptyId) => { if (commands.consumeSuppressedPtyExit(ptyId)) { return @@ -319,9 +328,14 @@ export default function TabGroupPanel({
- +
))} diff --git a/src/renderer/src/components/tab-group/TabGroupSplitLayout.test.ts b/src/renderer/src/components/tab-group/TabGroupSplitLayout.test.ts index 2deee9fcc05..b03b2c300f2 100644 --- a/src/renderer/src/components/tab-group/TabGroupSplitLayout.test.ts +++ b/src/renderer/src/components/tab-group/TabGroupSplitLayout.test.ts @@ -44,6 +44,7 @@ describe('TabGroupSplitLayout', () => { return tabGroupPanelElement.props as { groupId: string worktreeId: string + isWorktreeActive: boolean isFocused: boolean hasSplitGroups: boolean reserveClosedExplorerToggleSpace: boolean @@ -56,6 +57,7 @@ describe('TabGroupSplitLayout', () => { expect.objectContaining({ groupId: 'group-1', worktreeId: 'wt-1', + isWorktreeActive: false, isFocused: false, hasSplitGroups: false, reserveClosedExplorerToggleSpace: true, @@ -69,6 +71,7 @@ describe('TabGroupSplitLayout', () => { expect.objectContaining({ groupId: 'group-1', worktreeId: 'wt-1', + isWorktreeActive: true, isFocused: true, hasSplitGroups: false, reserveClosedExplorerToggleSpace: true, diff --git a/src/renderer/src/components/tab-group/TabGroupSplitLayout.tsx b/src/renderer/src/components/tab-group/TabGroupSplitLayout.tsx index 76d0ea605b0..1c5105ed74a 100644 --- a/src/renderer/src/components/tab-group/TabGroupSplitLayout.tsx +++ b/src/renderer/src/components/tab-group/TabGroupSplitLayout.tsx @@ -115,6 +115,7 @@ function SplitNode({