perf(terminal): fast-paint parked paired sessions

This commit is contained in:
Brennan Benson
2026-08-05 12:52:37 -07:00
parent 38ba22ecd1
commit ba21cc8087
9 changed files with 435 additions and 8 deletions
@@ -8527,6 +8527,37 @@ describe('connectPanePty', () => {
)
})
it('resets cached parked terminal modes before authoritative snapshot replay', async () => {
const { connectPanePty } = await import('./pty-connection')
enableActiveRuntimeEnvironment()
const remotePtyId = 'remote:env-1@@terminal-1'
const transport = createMockTransport(remotePtyId)
const replayCallback: { current: ((data: string) => void) | null } = { current: null }
transport.connect.mockImplementation(async ({ callbacks }: { callbacks: ConnectCallbacks }) => {
replayCallback.current = callbacks.onReplayData ?? null
return { id: remotePtyId, replay: '' }
})
transportFactoryQueue.push(transport)
const pane = createPane(1)
pane.container.dataset.parkedViewportFrame = 'true'
pane.terminal.write = vi.fn((_data: string, callback?: () => void) => {
callback?.()
})
const binding = connectPanePty(pane as never, createManager(1) as never, createDeps() as never)
await flushAsyncTicks(6)
replayCallback.current?.('authoritative snapshot')
await flushAsyncTicks(12)
const writes = pane.terminal.write.mock.calls.map(([data]) => data)
expect(writes.indexOf('\x1bc')).toBeGreaterThanOrEqual(0)
expect(writes.indexOf('\x1bc')).toBeLessThan(writes.indexOf('authoritative snapshot'))
expect(writes).not.toContain('\x1b[2J\x1b[3J\x1b[H')
expect(pane.container.dataset.parkedViewportFrame).toBeUndefined()
binding.dispose()
})
it('resizes the pane to the snapshot grid before replaying daemon snapshot bytes (bug #7279)', async () => {
// Why: the daemon serializes soft-wrapped lines flat, so reattach must resize xterm to the snapshot grid before writing, or rows rewrap wrong.
const { connectPanePty } = await import('./pty-connection')
@@ -272,6 +272,7 @@ import {
} from './command-code-done-settle'
import { canCommandCodeOutputOwnPane } from './command-code-output-ownership'
import { isTerminalTabParked } from './terminal-parked-watcher-registry'
import { consumeParkedTerminalViewportFrameMarker } from './terminal-parked-viewport-frame'
import {
getExecutionHostIdForWorktree,
getSettingsForWorktreeRuntimeOwner
@@ -5692,7 +5693,9 @@ export function connectPanePty(
// xterm. Local eager replay decides this earlier so metadata-only frames
// can keep restored scrollback while still using the replay guard.
if (clearBeforeReplay) {
await writeReplayDataAsync('\x1b[2J\x1b[3J\x1b[H')
await writeReplayDataAsync(
consumeParkedTerminalViewportFrameMarker(pane) ? '\x1bc' : '\x1b[2J\x1b[3J\x1b[H'
)
if (!isCurrentPayload()) {
continue
}
@@ -24,15 +24,20 @@ import {
import { collapseParkedExitedLeaf, startParkedPtyWatcher } from './terminal-parked-pty-watcher'
import {
capturedPanesByTabId,
consumeParkedTerminalViewportFrames,
disposeParkedTabWatchers,
parkedWatchersByTabId,
type ParkedTabWatcherEntry,
type ParkedTerminalPaneCapture
} from './terminal-parked-watcher-registry'
import type { PaneManager } from '@/lib/pane-manager/pane-manager'
import type { ReplayingPanesRef } from './replay-guard'
import { replayParkedTerminalViewportFrames } from './terminal-parked-viewport-frame'
// Why: re-export so callers keep one import surface; the registry split only breaks the store-slice import cycle.
export {
captureParkedTerminalPaneCandidates,
consumeParkedTerminalViewportFrames,
disposeAllParkedTerminalWatchers,
disposeRemovedWorktreeParkedTerminalWatchers,
disposeParkedTerminalWatchersForPtyIds,
@@ -49,6 +54,29 @@ export {
export type { ParkableTerminalTabModel } from './terminal-parked-watcher-reconciliation'
export type ParkedTerminalPtyEligibility = (ptyId: string) => boolean
export function replayParkedTerminalViewportForTab(args: {
manager: PaneManager
paneByLeafId: ReadonlyMap<string, number>
replayingPanesRef: ReplayingPanesRef
tabId: string
worktreeId: string
}): number {
const state = useAppStore.getState()
const tab = state.tabsByWorktree[args.worktreeId]?.find(
(candidate) => candidate.id === args.tabId
)
return replayParkedTerminalViewportFrames({
manager: args.manager,
frames: consumeParkedTerminalViewportFrames(
args.tabId,
state.terminalLayoutsByTabId[args.tabId]?.ptyIdsByLeafId ?? {},
tab?.ptyId ?? null
),
paneByLeafId: args.paneByLeafId,
replayingPanesRef: args.replayingPanesRef
})
}
const allowOrdinaryParkRestore = (ptyId: string): boolean =>
isRemoteRuntimePtyId(ptyId) ||
parseAppSshPtyId(ptyId) !== null ||
@@ -0,0 +1,105 @@
import { beforeEach, describe, expect, it, vi } from 'vitest'
import type { ManagedPane, PaneManager } from '@/lib/pane-manager/pane-manager'
import type { PtyTransport } from './pty-transport'
import {
captureParkedTerminalPanes,
captureParkedTerminalViewportFrame,
consumeParkedTerminalViewportFrameMarker,
replayParkedTerminalViewportFrames
} from './terminal-parked-viewport-frame'
const mocks = vi.hoisted(() => ({ replayIntoTerminal: vi.fn() }))
vi.mock('./replay-guard', () => ({ replayIntoTerminal: mocks.replayIntoTerminal }))
function makePane(
id = 1,
leafId = 'leaf-1'
): {
pane: ManagedPane
serialize: ReturnType<typeof vi.fn>
} {
const serialize = vi.fn(() => 'cached viewport')
return {
pane: {
id,
leafId,
terminal: {
cols: 120,
rows: 40,
buffer: { active: { cursorX: 4, cursorY: 7 } }
},
container: { dataset: {} },
serializeAddon: { serialize }
} as unknown as ManagedPane,
serialize
}
}
beforeEach(() => {
mocks.replayIntoTerminal.mockClear()
})
describe('parked terminal viewport frames', () => {
it('captures only the active viewport with an absolute cursor restore', () => {
const { pane, serialize } = makePane()
const frame = captureParkedTerminalViewportFrame(pane)
expect(serialize).toHaveBeenCalledWith({ scrollback: 0 })
expect(frame).toEqual({ data: 'cached viewport\x1b[8;5H', cols: 120, rows: 40 })
})
it('captures viewport frames only for paired runtime panes', () => {
const remote = makePane(1, 'remote-leaf')
const local = makePane(2, 'local-leaf')
const manager = {
getActivePane: () => remote.pane,
getPanes: () => [remote.pane, local.pane]
} as unknown as PaneManager
const transports = new Map<number, PtyTransport>([
[1, { getPtyId: () => 'remote:windows-low-spec@@terminal-1' } as PtyTransport],
[2, { getPtyId: () => 'local-pty' } as PtyTransport]
])
const captures = captureParkedTerminalPanes(manager, transports)
expect(captures[0].viewportFrame?.data).toContain('cached viewport')
expect(captures[1]).not.toHaveProperty('viewportFrame')
expect(local.serialize).not.toHaveBeenCalled()
})
it('replays a matching-grid frame and skips a stale geometry', () => {
const matching = makePane(1, 'matching')
const stale = makePane(2, 'stale')
const manager = {
getPanes: () => [matching.pane, stale.pane],
hasWebglRenderer: () => false
} as unknown as PaneManager
const replayingPanesRef = { current: new Map<number, number>() }
const replayed = replayParkedTerminalViewportFrames({
manager,
frames: [
{ leafId: 'matching', frame: { data: 'matching frame', cols: 120, rows: 40 } },
{ leafId: 'stale', frame: { data: 'stale frame', cols: 80, rows: 24 } }
],
paneByLeafId: new Map([
['matching', 1],
['stale', 2]
]),
replayingPanesRef
})
expect(replayed).toBe(1)
expect(mocks.replayIntoTerminal).toHaveBeenCalledOnce()
expect(mocks.replayIntoTerminal).toHaveBeenCalledWith(
matching.pane,
replayingPanesRef,
'matching frame',
expect.any(Object)
)
expect(consumeParkedTerminalViewportFrameMarker(matching.pane)).toBe(true)
expect(consumeParkedTerminalViewportFrameMarker(matching.pane)).toBe(false)
})
})
@@ -0,0 +1,95 @@
import type { ManagedPane, PaneManager } from '@/lib/pane-manager/pane-manager'
import type { PtyTransport } from './pty-transport'
import { isRemoteRuntimePtyId } from '@/runtime/runtime-terminal-inspection'
import { serializeWithAbsoluteCursor } from '../../../../shared/terminal-serialize-absolute-cursor'
import { replayIntoTerminal, type ReplayingPanesRef } from './replay-guard'
const MAX_PARKED_VIEWPORT_FRAME_CHARS = 256 * 1024
const PARKED_VIEWPORT_FRAME_DATASET_KEY = 'parkedViewportFrame'
export type ParkedTerminalViewportFrame = {
data: string
cols: number
rows: number
}
export type ParkedTerminalViewportFrameByLeaf = {
leafId: string
frame: ParkedTerminalViewportFrame
}
export function captureParkedTerminalPanes(
manager: PaneManager,
paneTransports: ReadonlyMap<number, PtyTransport>
): {
ptyId: string | null
paneId: number
leafId: string
drivesTabTitle: boolean
viewportFrame?: ParkedTerminalViewportFrame
}[] {
const activePaneId = manager.getActivePane()?.id
return manager.getPanes().map((pane) => {
const ptyId = paneTransports.get(pane.id)?.getPtyId() ?? null
const viewportFrame =
ptyId && isRemoteRuntimePtyId(ptyId) ? captureParkedTerminalViewportFrame(pane) : null
return {
ptyId,
paneId: pane.id,
leafId: pane.leafId,
drivesTabTitle: activePaneId === pane.id,
...(viewportFrame ? { viewportFrame } : {})
}
})
}
export function captureParkedTerminalViewportFrame(
pane: ManagedPane
): ParkedTerminalViewportFrame | null {
try {
if (pane.terminal.cols < 1 || pane.terminal.rows < 1) {
return null
}
const data = serializeWithAbsoluteCursor(pane.serializeAddon, pane.terminal, {
scrollback: 0
})
if (!data || data.length > MAX_PARKED_VIEWPORT_FRAME_CHARS) {
return null
}
return { data, cols: pane.terminal.cols, rows: pane.terminal.rows }
} catch {
return null
}
}
export function replayParkedTerminalViewportFrames(args: {
manager: PaneManager
frames: readonly ParkedTerminalViewportFrameByLeaf[]
paneByLeafId: ReadonlyMap<string, number>
replayingPanesRef: ReplayingPanesRef
}): number {
let replayed = 0
for (const { leafId, frame } of args.frames) {
const paneId = args.paneByLeafId.get(leafId)
const pane = args.manager.getPanes().find((candidate) => candidate.id === paneId)
if (!pane || pane.terminal.cols !== frame.cols || pane.terminal.rows !== frame.rows) {
continue
}
replayIntoTerminal(pane, args.replayingPanesRef, frame.data, {
shouldRefreshViewportSynchronously: () => !args.manager.hasWebglRenderer(pane.id)
})
pane.container.dataset[PARKED_VIEWPORT_FRAME_DATASET_KEY] = 'true'
replayed += 1
}
return replayed
}
export function consumeParkedTerminalViewportFrameMarker(
pane: Pick<ManagedPane, 'container'>
): boolean {
if (pane.container.dataset[PARKED_VIEWPORT_FRAME_DATASET_KEY] !== 'true') {
return false
}
delete pane.container.dataset[PARKED_VIEWPORT_FRAME_DATASET_KEY]
return true
}
@@ -9,6 +9,7 @@ import {
} from './pty-pre-handler-buffer'
import {
capturedPanesByTabId,
consumeParkedTerminalViewportFrames,
parkedWatchersByTabId,
pruneParkedTerminalWatchers,
terminalWatcherLiveWorkspaceIds
@@ -90,4 +91,58 @@ describe('terminal parked watcher registry removal', () => {
expect(capturedPanesByTabId.has(FLOATING_TAB_ID)).toBe(true)
expect(floatingDispose).not.toHaveBeenCalled()
})
it('consumes a matching parked viewport frame exactly once', () => {
const frame = { data: 'cached viewport', cols: 120, rows: 40 }
capturedPanesByTabId.set(TAB_ID, {
worktreeId: 'removed-worktree',
panes: [
{
ptyId: PTY_ID,
paneId: 1,
leafId: 'leaf-1',
drivesTabTitle: true,
viewportFrame: frame
}
]
})
parkedWatchersByTabId.set(TAB_ID, {
worktreeId: 'removed-worktree',
tabPtyId: PTY_ID,
paneIdByPtyId: new Map([[PTY_ID, 1]]),
disposersByPtyId: new Map([[PTY_ID, vi.fn()]])
})
expect(consumeParkedTerminalViewportFrames(TAB_ID, { 'leaf-1': PTY_ID }, null)).toEqual([
{ leafId: 'leaf-1', frame }
])
expect(consumeParkedTerminalViewportFrames(TAB_ID, { 'leaf-1': PTY_ID }, null)).toEqual([])
expect(capturedPanesByTabId.get(TAB_ID)?.panes[0]).not.toHaveProperty('viewportFrame')
})
it('drops a cached viewport when the parked PTY identity changed', () => {
capturedPanesByTabId.set(TAB_ID, {
worktreeId: 'removed-worktree',
panes: [
{
ptyId: PTY_ID,
paneId: 1,
leafId: 'leaf-1',
drivesTabTitle: true,
viewportFrame: { data: 'stale viewport', cols: 80, rows: 24 }
}
]
})
parkedWatchersByTabId.set(TAB_ID, {
worktreeId: 'removed-worktree',
tabPtyId: 'replacement-pty',
paneIdByPtyId: new Map([['replacement-pty', 1]]),
disposersByPtyId: new Map([['replacement-pty', vi.fn()]])
})
expect(
consumeParkedTerminalViewportFrames(TAB_ID, { 'leaf-1': 'replacement-pty' }, null)
).toEqual([])
expect(capturedPanesByTabId.get(TAB_ID)?.panes[0]).not.toHaveProperty('viewportFrame')
})
})
@@ -9,6 +9,10 @@
*/
import { discardPreHandlerPtyState } from './pty-pre-handler-buffer'
import { FLOATING_TERMINAL_WORKTREE_ID } from '../../../../shared/constants'
import type {
ParkedTerminalViewportFrame,
ParkedTerminalViewportFrameByLeaf
} from './terminal-parked-viewport-frame'
export type ParkedTerminalPaneCapture = {
ptyId: string | null
@@ -17,6 +21,7 @@ export type ParkedTerminalPaneCapture = {
/** Stable terminal-layout leaf UUID (paneKey attribution). */
leafId: string
drivesTabTitle: boolean
viewportFrame?: ParkedTerminalViewportFrame
}
export type CapturedTabPanes = { worktreeId: string; panes: ParkedTerminalPaneCapture[] }
@@ -67,6 +72,32 @@ export function isTerminalTabParked(tabId: string): boolean {
return (parkedWatchersByTabId.get(tabId)?.disposersByPtyId.size ?? 0) > 0
}
export function consumeParkedTerminalViewportFrames(
tabId: string,
expectedPtyIdsByLeafId: Readonly<Record<string, string>>,
fallbackPtyId: string | null
): ParkedTerminalViewportFrameByLeaf[] {
const capture = capturedPanesByTabId.get(tabId)
if (!capture) {
return []
}
const useFallback = capture.panes.length === 1
const frames = isTerminalTabParked(tabId)
? capture.panes.flatMap((pane) => {
const expectedPtyId =
expectedPtyIdsByLeafId[pane.leafId] ?? (useFallback ? fallbackPtyId : null)
return pane.viewportFrame && expectedPtyId && pane.ptyId === expectedPtyId
? [{ leafId: pane.leafId, frame: pane.viewportFrame }]
: []
})
: []
capturedPanesByTabId.set(tabId, {
...capture,
panes: capture.panes.map(({ viewportFrame: _viewportFrame, ...pane }) => pane)
})
return frames
}
export function disposeParkedTabWatchers(tabId: string): void {
const entry = parkedWatchersByTabId.get(tabId)
if (!entry) {
@@ -122,7 +122,11 @@ import { fitAndFocusPanes, fitPanes } from './pane-helpers'
import { markTerminalPinnedViewport } from '@/lib/pane-manager/terminal-scroll-intent'
import { syncTerminalScrollIntentSoon } from '@/lib/pane-manager/terminal-scroll-intent-settle'
import { registerRuntimeTerminalTab, scheduleRuntimeGraphSync } from '@/runtime/sync-runtime-graph'
import { captureParkedTerminalPaneCandidates } from './terminal-parked-tab-watchers'
import {
captureParkedTerminalPaneCandidates,
replayParkedTerminalViewportForTab
} from './terminal-parked-tab-watchers'
import { captureParkedTerminalPanes } from './terminal-parked-viewport-frame'
import { e2eConfig } from '@/lib/e2e-config'
import {
PRIMARY_SELECTION_MAX_LENGTH,
@@ -1500,6 +1504,13 @@ export function useTerminalPaneLifecycle({
replayingPanesRef,
restoredViewportBlankingPanesRef
)
replayParkedTerminalViewportForTab({
manager,
paneByLeafId: restoredPaneByLeafId,
replayingPanesRef,
tabId,
worktreeId
})
const hasScrollbackRefs = Boolean(initialLayoutRef.current.scrollbackRefsByLeafId)
if (
restoredBuffers &&
@@ -1745,12 +1756,7 @@ export function useTerminalPaneLifecycle({
captureParkedTerminalPaneCandidates(
tabId,
worktreeId,
manager.getPanes().map((capturedPane) => ({
ptyId: paneTransports.get(capturedPane.id)?.getPtyId() ?? null,
paneId: capturedPane.id,
leafId: capturedPane.leafId,
drivesTabTitle: manager.getActivePane()?.id === capturedPane.id
}))
captureParkedTerminalPanes(manager, paneTransports)
)
for (const transport of paneTransports.values()) {
const ptyId = transport.getPtyId()
@@ -482,3 +482,76 @@ test('paired client keeps revealed remote terminals interactive', async ({
await client.dispose()
}
})
test('paired client fast-paints a parked terminal before the runtime responds', async ({
orcaPage
}, testInfo) => {
test.setTimeout(180_000)
const offer = await createRuntimeDesktopPairingOffer(orcaPage)
const previousParkDelay = process.env.ORCA_E2E_TERMINAL_PARKING_DELAY_MS
process.env.ORCA_E2E_TERMINAL_PARKING_DELAY_MS = String(PARK_DELAY_MS)
const client = await launchPairedElectronClient(offer, testInfo, 'parked-fast-paint')
const createdTerminals: string[] = []
try {
const worktreeId = await orcaPage.evaluate(() => {
const id = window.__store?.getState().activeWorktreeId
if (!id) {
throw new Error('headed host has no active worktree')
}
return id
})
await expect
.poll(
() =>
client.page.evaluate(
(id) =>
window.__store
?.getState()
.allWorktrees()
.some((worktree) => worktree.id === id) ?? false,
worktreeId
),
{ timeout: 60_000, message: 'paired client never saw the host worktree' }
)
.toBe(true)
await client.page.evaluate((id) => {
const state = window.__store?.getState()
state?.setActiveView('terminal')
state?.setActiveWorktree(id)
}, worktreeId)
const { target, decoys } = await seedScenario(client, worktreeId)
createdTerminals.push(target.terminal, ...decoys.map((decoy) => decoy.terminal))
await openClientTab(client.page, worktreeId, decoys[0].webTabId)
await openClientTab(client.page, worktreeId, decoys[1].webTabId)
await waitForTabParked(client.page, target.webTabId, { parkDelayMs: PARK_DELAY_MS })
await client.page.evaluate(async (selector) => {
await window.api.runtimeEnvironments.disconnect({ selector })
}, client.environmentId)
const startedAt = Date.now()
await openClientTab(client.page, worktreeId, target.webTabId)
expect(
await waitForPaneMarker(client.page, target.webTabId, 'READY:', 2_000),
'cold-parked viewport stayed blank while the paired runtime was disconnected'
).toBe(true)
console.log(`[paired-reveal] cached viewport painted in ${Date.now() - startedAt}ms`)
} finally {
await client.page
.evaluate(async (selector) => {
await window.api.runtimeEnvironments.connect({ selector })
}, client.environmentId)
.catch(() => undefined)
for (const terminal of createdTerminals) {
await callEnvironment(client.page, client.environmentId, 'terminal.closeTab', {
terminal
}).catch(() => undefined)
}
if (previousParkDelay === undefined) {
delete process.env.ORCA_E2E_TERMINAL_PARKING_DELAY_MS
} else {
process.env.ORCA_E2E_TERMINAL_PARKING_DELAY_MS = previousParkDelay
}
await client.dispose()
}
})