fix: free WebGL contexts for hidden worktrees to prevent terminal dying (#777)

This commit is contained in:
Brennan Benson 2026-04-17 17:15:27 -07:00 committed by GitHub
parent 9962655492
commit 2d755ba386
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 22 additions and 4 deletions

View file

@ -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({
<div
key={bt.id}
className="absolute inset-0 flex min-h-0 min-w-0"
style={{ display: activeBrowserTab?.id === bt.id ? undefined : 'none' }}
style={{
display: isWorktreeActive && activeBrowserTab?.id === bt.id ? undefined : 'none'
}}
>
<BrowserPane browserTab={bt} isActive={activeBrowserTab?.id === bt.id} />
<BrowserPane
browserTab={bt}
isActive={isWorktreeActive && activeBrowserTab?.id === bt.id}
/>
</div>
))}
</div>

View file

@ -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,

View file

@ -115,6 +115,7 @@ function SplitNode({
<TabGroupPanel
groupId={node.groupId}
worktreeId={worktreeId}
isWorktreeActive={isWorktreeActive}
// Why: hidden worktrees stay mounted so their PTYs and split layouts
// survive worktree switches, but only the visible worktree may own the
// global terminal shortcuts. If an offscreen group's pane stays