mirror of
https://github.com/stablyai/orca.git
synced 2026-10-20 00:03:45 +00:00
fix(terminal): preserve floating park watchers
This commit is contained in:
@@ -113,7 +113,8 @@ import {
|
||||
disposeAllParkedTerminalWatchers,
|
||||
pruneParkedTerminalWatchers,
|
||||
shouldDeferParkedPtyExitTabClose,
|
||||
syncParkedTerminalTabWatchers
|
||||
syncParkedTerminalTabWatchers,
|
||||
terminalWatcherLiveWorkspaceIds
|
||||
} from './terminal-pane/terminal-parked-tab-watchers'
|
||||
import { isMainTerminalSideEffectAuthorityForPty } from './terminal-pane/terminal-side-effect-facts-handler'
|
||||
import { appendUniqueOpenFileIds } from './terminal/unsaved-close-queue'
|
||||
@@ -1294,7 +1295,9 @@ function Terminal(): React.JSX.Element | null {
|
||||
)
|
||||
// Why: legacy (non-split) host owns watcher reconciliation; split mode's overlay layers own theirs, so only dispose worktrees with no overlay layer.
|
||||
useEffect(() => {
|
||||
pruneParkedTerminalWatchers(new Set(workspaceSurfaces.map((workspace) => workspace.id)))
|
||||
pruneParkedTerminalWatchers(
|
||||
terminalWatcherLiveWorkspaceIds(workspaceSurfaces.map((workspace) => workspace.id))
|
||||
)
|
||||
for (const workspace of workspaceSurfaces) {
|
||||
if (
|
||||
anyMountedWorktreeHasLayout &&
|
||||
|
||||
@@ -35,7 +35,8 @@ export {
|
||||
disposeParkedTerminalWatchersForPtyIds,
|
||||
disposeParkedTerminalWatchersForWorktree,
|
||||
getParkedTerminalWatcherTabIds,
|
||||
pruneParkedTerminalWatchers
|
||||
pruneParkedTerminalWatchers,
|
||||
terminalWatcherLiveWorkspaceIds
|
||||
} from './terminal-parked-watcher-registry'
|
||||
export type { ParkedTerminalPaneCapture } from './terminal-parked-watcher-registry'
|
||||
export {
|
||||
|
||||
@@ -8,16 +8,24 @@ import {
|
||||
hasPreHandlerPtyExit
|
||||
} from './pty-pre-handler-buffer'
|
||||
import {
|
||||
capturedPanesByTabId,
|
||||
parkedWatchersByTabId,
|
||||
pruneParkedTerminalWatchers
|
||||
pruneParkedTerminalWatchers,
|
||||
terminalWatcherLiveWorkspaceIds
|
||||
} from './terminal-parked-watcher-registry'
|
||||
import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../../shared/constants'
|
||||
|
||||
const TAB_ID = 'removed-parked-tab'
|
||||
const PTY_ID = 'removed-worktree@@parked-pty'
|
||||
const FLOATING_TAB_ID = 'floating-parked-tab'
|
||||
const FLOATING_PTY_ID = `${FLOATING_TERMINAL_WORKTREE_ID}@@parked-pty`
|
||||
|
||||
describe('terminal parked watcher registry removal', () => {
|
||||
afterEach(() => {
|
||||
parkedWatchersByTabId.delete(TAB_ID)
|
||||
parkedWatchersByTabId.delete(FLOATING_TAB_ID)
|
||||
capturedPanesByTabId.delete(TAB_ID)
|
||||
capturedPanesByTabId.delete(FLOATING_TAB_ID)
|
||||
clearPreHandlerPtyState(PTY_ID)
|
||||
})
|
||||
|
||||
@@ -51,4 +59,35 @@ describe('terminal parked watcher registry removal', () => {
|
||||
expect(exit).not.toHaveBeenCalled()
|
||||
expect(delayedData).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it('keeps the floating workspace while pruning a removed workspace', () => {
|
||||
const removedDispose = vi.fn()
|
||||
const floatingDispose = vi.fn()
|
||||
parkedWatchersByTabId.set(TAB_ID, {
|
||||
worktreeId: 'removed-worktree',
|
||||
tabPtyId: PTY_ID,
|
||||
paneIdByPtyId: new Map([[PTY_ID, 1]]),
|
||||
disposersByPtyId: new Map([[PTY_ID, removedDispose]])
|
||||
})
|
||||
parkedWatchersByTabId.set(FLOATING_TAB_ID, {
|
||||
worktreeId: FLOATING_TERMINAL_WORKTREE_ID,
|
||||
tabPtyId: FLOATING_PTY_ID,
|
||||
paneIdByPtyId: new Map([[FLOATING_PTY_ID, 1]]),
|
||||
disposersByPtyId: new Map([[FLOATING_PTY_ID, floatingDispose]])
|
||||
})
|
||||
capturedPanesByTabId.set(TAB_ID, { worktreeId: 'removed-worktree', panes: [] })
|
||||
capturedPanesByTabId.set(FLOATING_TAB_ID, {
|
||||
worktreeId: FLOATING_TERMINAL_WORKTREE_ID,
|
||||
panes: []
|
||||
})
|
||||
|
||||
pruneParkedTerminalWatchers(terminalWatcherLiveWorkspaceIds([]))
|
||||
|
||||
expect(parkedWatchersByTabId.has(TAB_ID)).toBe(false)
|
||||
expect(capturedPanesByTabId.has(TAB_ID)).toBe(false)
|
||||
expect(removedDispose).toHaveBeenCalledOnce()
|
||||
expect(parkedWatchersByTabId.has(FLOATING_TAB_ID)).toBe(true)
|
||||
expect(capturedPanesByTabId.has(FLOATING_TAB_ID)).toBe(true)
|
||||
expect(floatingDispose).not.toHaveBeenCalled()
|
||||
})
|
||||
})
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
* import cycle-free, mirroring how pty-dispatcher exports its handler maps.
|
||||
*/
|
||||
import { discardPreHandlerPtyState } from './pty-pre-handler-buffer'
|
||||
import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../../shared/constants'
|
||||
|
||||
export type ParkedTerminalPaneCapture = {
|
||||
ptyId: string | null
|
||||
@@ -51,6 +52,11 @@ export function getParkedTerminalWatcherTabIds(): string[] {
|
||||
return Array.from(parkedWatchersByTabId.keys())
|
||||
}
|
||||
|
||||
// Why: the floating workspace is synthetic, so repo/folder surface lists never include it.
|
||||
export function terminalWatcherLiveWorkspaceIds(workspaceIds: Iterable<string>): Set<string> {
|
||||
return new Set([...workspaceIds, FLOATING_TERMINAL_WORKTREE_ID])
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this tab is parked right now — the reveal remount's own mount effect
|
||||
* runs before the host effect that disposes the watcher (child effects first),
|
||||
|
||||
Reference in New Issue
Block a user