mirror of
https://github.com/stablyai/orca.git
synced 2026-10-20 16:03:20 +00:00
fix(renderer): retry failed remote pane layout pushes
This commit is contained in:
@@ -1069,14 +1069,6 @@ function TerminalPane(
|
||||
}
|
||||
}, [tabId, setTabLayout, worktreeId])
|
||||
|
||||
// Why: the dedupe cache mirrors what one host holds for one tab, so drop it when either changes or the pane unmounts.
|
||||
useEffect(() => {
|
||||
const pusher = remotePaneLayoutPusherRef.current
|
||||
return () => {
|
||||
pusher?.reset()
|
||||
}
|
||||
}, [tabId, worktreeId])
|
||||
|
||||
const clearPaneScrollback = useCallback(
|
||||
(pane: ManagedPane): void => {
|
||||
clearedScrollbackLeafIdsRef.current.add(pane.leafId)
|
||||
|
||||
@@ -36,7 +36,7 @@ function makeLayout(overrides: Partial<TerminalLayoutSnapshot> = {}): TerminalLa
|
||||
|
||||
describe('createRemotePaneLayoutPusher', () => {
|
||||
beforeEach(() => {
|
||||
updateWebRuntimePaneLayout.mockClear()
|
||||
updateWebRuntimePaneLayout.mockReset().mockResolvedValue(true)
|
||||
})
|
||||
|
||||
it('pushes once across 100 persists of an unchanged layout', () => {
|
||||
@@ -125,11 +125,33 @@ describe('createRemotePaneLayoutPusher', () => {
|
||||
expect(updateWebRuntimePaneLayout).toHaveBeenCalledTimes(3)
|
||||
})
|
||||
|
||||
it('re-pushes after reset so a remount re-establishes host geometry', () => {
|
||||
it('does not carry a cached layout across worktrees', () => {
|
||||
const pusher = createRemotePaneLayoutPusher()
|
||||
pusher.push({ worktreeId: 'wt-1', tabId: 'tab-1', layout: makeLayout() })
|
||||
pusher.reset()
|
||||
pusher.push({ worktreeId: 'wt-2', tabId: 'tab-1', layout: makeLayout() })
|
||||
expect(updateWebRuntimePaneLayout).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('retries an unchanged layout after a failed push', async () => {
|
||||
updateWebRuntimePaneLayout.mockResolvedValueOnce(false)
|
||||
const pusher = createRemotePaneLayoutPusher()
|
||||
const input = { worktreeId: 'wt-1', tabId: 'tab-1', layout: makeLayout() }
|
||||
|
||||
pusher.push(input)
|
||||
await Promise.resolve()
|
||||
pusher.push(input)
|
||||
|
||||
expect(updateWebRuntimePaneLayout).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
|
||||
it('re-pushes after a remount re-establishes host geometry', () => {
|
||||
const pusher = createRemotePaneLayoutPusher()
|
||||
pusher.push({ worktreeId: 'wt-1', tabId: 'tab-1', layout: makeLayout() })
|
||||
createRemotePaneLayoutPusher().push({
|
||||
worktreeId: 'wt-1',
|
||||
tabId: 'tab-1',
|
||||
layout: makeLayout()
|
||||
})
|
||||
expect(updateWebRuntimePaneLayout).toHaveBeenCalledTimes(2)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -4,7 +4,6 @@ import { updateWebRuntimePaneLayout } from '@/runtime/web-runtime-session'
|
||||
|
||||
export type RemotePaneLayoutPusher = {
|
||||
push: (input: { worktreeId: string; tabId: string; layout: TerminalLayoutSnapshot }) => void
|
||||
reset: () => void
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -13,23 +12,36 @@ export type RemotePaneLayoutPusher = {
|
||||
* Dedupe against the last push so unchanged layouts cost no remote round trip.
|
||||
*/
|
||||
export function createRemotePaneLayoutPusher(): RemotePaneLayoutPusher {
|
||||
let lastPushed: { tabId: string; snapshot: TerminalLayoutSnapshot } | null = null
|
||||
let lastAttempt: {
|
||||
id: number
|
||||
worktreeId: string
|
||||
tabId: string
|
||||
snapshot: TerminalLayoutSnapshot
|
||||
} | null = null
|
||||
let nextAttemptId = 0
|
||||
return {
|
||||
push: ({ worktreeId, tabId, layout }) => {
|
||||
if (lastPushed?.tabId === tabId && terminalLayoutEqual(lastPushed.snapshot, layout)) {
|
||||
if (
|
||||
lastAttempt?.worktreeId === worktreeId &&
|
||||
lastAttempt.tabId === tabId &&
|
||||
terminalLayoutEqual(lastAttempt.snapshot, layout)
|
||||
) {
|
||||
return
|
||||
}
|
||||
lastPushed = { tabId, snapshot: layout }
|
||||
const attempt = { id: ++nextAttemptId, worktreeId, tabId, snapshot: layout }
|
||||
lastAttempt = attempt
|
||||
void updateWebRuntimePaneLayout({
|
||||
worktreeId,
|
||||
tabId,
|
||||
root: layout.root,
|
||||
expandedLeafId: layout.expandedLeafId,
|
||||
...(layout.titlesByLeafId ? { titlesByLeafId: layout.titlesByLeafId } : {})
|
||||
}).then((updated) => {
|
||||
// Why: a disconnected or timed-out push carried no information, so the next persist must retry it.
|
||||
if (!updated && lastAttempt?.id === attempt.id) {
|
||||
lastAttempt = null
|
||||
}
|
||||
})
|
||||
},
|
||||
reset: () => {
|
||||
lastPushed = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user