fix(browser): latch overlay retention after commit

This commit is contained in:
Brennan Benson
2026-08-02 15:00:57 -07:00
parent a58f3cdc2a
commit b2dc29c6d6
2 changed files with 37 additions and 6 deletions
@@ -1,5 +1,6 @@
// @vitest-environment happy-dom
import { cleanup, render } from '@testing-library/react'
import { Suspense } from 'react'
import { renderToStaticMarkup } from 'react-dom/server'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import type { BrowserTab as BrowserTabState, Tab, TabGroup } from '../../../../shared/types'
@@ -94,6 +95,33 @@ describe('BrowserPaneOverlayLayer', () => {
expect(view.container.querySelectorAll('[data-browser-overlay-tab-id]')).toHaveLength(2)
})
it('does not retain browser slots from an eligible render that never commits', () => {
const pending = new Promise<never>(() => {})
const BlockCommit = ({ blocked }: { blocked: boolean }): null => {
if (blocked) {
throw pending
}
return null
}
const renderBoundary = (mountEligible: boolean, blocked: boolean) => (
<Suspense fallback={<span data-suspended />}>
<RetainedBrowserPaneOverlayLayer
worktreeId="wt-1"
isWorktreeActive={mountEligible}
mountEligible={mountEligible}
/>
<BlockCommit blocked={blocked} />
</Suspense>
)
const view = render(renderBoundary(false, false))
view.rerender(renderBoundary(true, true))
expect(view.container.querySelector('[data-suspended]')).not.toBeNull()
view.rerender(renderBoundary(false, false))
expect(view.container.querySelectorAll('[data-browser-overlay-tab-id]')).toHaveLength(0)
})
it('keeps inactive browser panes mounted for a visible worktree', () => {
const markup = renderOverlay({ isWorktreeActive: true })
@@ -1,4 +1,4 @@
import { memo, useCallback, useMemo, useRef } from 'react'
import { memo, useCallback, useLayoutEffect, useMemo, useState } from 'react'
import { registerBrowserOverlaySlotViewport } from './browser-page-viewport'
import { useShallow } from 'zustand/react/shallow'
import { useAppStore } from '../../store'
@@ -203,11 +203,14 @@ export const RetainedBrowserPaneOverlayLayer = memo(function RetainedBrowserPane
isWorktreeActive: boolean
mountEligible: boolean
}): React.JSX.Element | null {
const hasMountedRef = useRef(mountEligible)
if (mountEligible) {
hasMountedRef.current = true
}
if (!hasMountedRef.current) {
const [hasCommittedMount, setHasCommittedMount] = useState(false)
// Why: commit the latch with the persistent slot DOM so discarded renders cannot retain a guest host.
useLayoutEffect(() => {
if (mountEligible && !hasCommittedMount) {
setHasCommittedMount(true)
}
}, [hasCommittedMount, mountEligible])
if (!mountEligible && !hasCommittedMount) {
return null
}
return <BrowserPaneOverlayLayer worktreeId={worktreeId} isWorktreeActive={isWorktreeActive} />