diff --git a/src/renderer/src/components/tab-group/TabGroupPanel.tsx b/src/renderer/src/components/tab-group/TabGroupPanel.tsx index 55646fb3..b300e259 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 2deee9fc..b03b2c30 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 76d0ea60..1c5105ed 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({