From aca1238fef71c4836a04ea21e678510174ffdef2 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Fri, 28 Aug 2026 11:51:35 -0700 Subject: [PATCH] refactor(worktree): split background pane exit-output recording out The sequenced-command fix pushed launch-worktree-background-terminals.ts to 303 effective lines, over the 300 cap. Moved persistExitedPaneOutput and registerBackgroundPaneBuffer, which are one concern, into background-pane-exit-output.ts. No behavior change: dropping the registerEagerPtyBuffer call still reddens the background-terminals suite. --- .../src/lib/background-pane-exit-output.ts | 49 +++++++++++++++++++ .../launch-worktree-background-terminals.ts | 45 +---------------- 2 files changed, 50 insertions(+), 44 deletions(-) create mode 100644 src/renderer/src/lib/background-pane-exit-output.ts diff --git a/src/renderer/src/lib/background-pane-exit-output.ts b/src/renderer/src/lib/background-pane-exit-output.ts new file mode 100644 index 00000000000..2b295e30989 --- /dev/null +++ b/src/renderer/src/lib/background-pane-exit-output.ts @@ -0,0 +1,49 @@ +import { + registerEagerPtyBuffer, + type EagerPtyHandle +} from '@/components/terminal-pane/pty-dispatcher' +import { useAppStore } from '@/store' + +/** The id a background pane got, plus which lifetime of it this spawn owns. */ +export type SpawnedPane = { ptyId: string; incarnationId?: string } + +function persistExitedPaneOutput(tabId: string, leafId: string, output: string): void { + const store = useAppStore.getState() + const layout = store.terminalLayoutsByTabId[tabId] + if (!layout) { + return + } + const { ptyIdsByLeafId: existingPtyIds, buffersByLeafId: existingBuffers, ...rest } = layout + const nextPtyIds = { ...existingPtyIds } + delete nextPtyIds[leafId] + const trimmedOutput = output.trim() ? output : '' + store.setTabLayout(tabId, { + ...rest, + ...(Object.keys(nextPtyIds).length > 0 ? { ptyIdsByLeafId: nextPtyIds } : {}), + ...(trimmedOutput + ? { + buffersByLeafId: { + ...existingBuffers, + [leafId]: output + } + } + : existingBuffers + ? { buffersByLeafId: existingBuffers } + : {}) + }) +} + +// Why the incarnation: a relay-recycled id can hold the previous owner's exit, and draining that +// into this handler tears the pane down seconds after it launched. +export function registerBackgroundPaneBuffer( + tabId: string, + leafId: string, + pane: SpawnedPane +): void { + let eagerBuffer: EagerPtyHandle | null = null + const onExit = (exitPtyId: string): void => { + persistExitedPaneOutput(tabId, leafId, eagerBuffer?.flush() ?? '') + useAppStore.getState().clearTabPtyId(tabId, exitPtyId) + } + eagerBuffer = registerEagerPtyBuffer(pane.ptyId, onExit, pane.incarnationId) +} diff --git a/src/renderer/src/lib/launch-worktree-background-terminals.ts b/src/renderer/src/lib/launch-worktree-background-terminals.ts index 7f2a8b0ea91..e6bf8325306 100644 --- a/src/renderer/src/lib/launch-worktree-background-terminals.ts +++ b/src/renderer/src/lib/launch-worktree-background-terminals.ts @@ -1,7 +1,4 @@ -import { - registerEagerPtyBuffer, - type EagerPtyHandle -} from '@/components/terminal-pane/pty-dispatcher' +import { registerBackgroundPaneBuffer, type SpawnedPane } from '@/lib/background-pane-exit-output' import { createBrowserUuid } from '@/lib/browser-uuid' import { getSettingsForWorktreeRuntimeOwner } from '@/lib/worktree-runtime-owner' import { getActiveRuntimeTarget } from '@/runtime/runtime-rpc-client' @@ -87,43 +84,6 @@ function buildSplitLayout( } } -function persistExitedPaneOutput(tabId: string, leafId: string, output: string): void { - const store = useAppStore.getState() - const layout = store.terminalLayoutsByTabId[tabId] - if (!layout) { - return - } - const { ptyIdsByLeafId: existingPtyIds, buffersByLeafId: existingBuffers, ...rest } = layout - const nextPtyIds = { ...existingPtyIds } - delete nextPtyIds[leafId] - const trimmedOutput = output.trim() ? output : '' - store.setTabLayout(tabId, { - ...rest, - ...(Object.keys(nextPtyIds).length > 0 ? { ptyIdsByLeafId: nextPtyIds } : {}), - ...(trimmedOutput - ? { - buffersByLeafId: { - ...existingBuffers, - [leafId]: output - } - } - : existingBuffers - ? { buffersByLeafId: existingBuffers } - : {}) - }) -} - -// Why the incarnation: a relay-recycled id can hold the previous owner's exit, and draining that -// into this handler tears the pane down seconds after it launched. -function registerBackgroundPaneBuffer(tabId: string, leafId: string, pane: SpawnedPane): void { - let eagerBuffer: EagerPtyHandle | null = null - const onExit = (exitPtyId: string): void => { - persistExitedPaneOutput(tabId, leafId, eagerBuffer?.flush() ?? '') - useAppStore.getState().clearTabPtyId(tabId, exitPtyId) - } - eagerBuffer = registerEagerPtyBuffer(pane.ptyId, onExit, pane.incarnationId) -} - function buildSetupCommand(setup: WorktreeSetupLaunch): string { // Why: a sequenced launch carries the gated command that records setup's outcome for a waiting // agent terminal; rebuilding the bare runner here ran setup but recorded nothing, so the agent @@ -139,9 +99,6 @@ function buildSetupCommand(setup: WorktreeSetupLaunch): string { ) } -/** The id a background pane got, plus which lifetime of it this spawn owns. */ -type SpawnedPane = { ptyId: string; incarnationId?: string } - async function spawnPane(args: { worktree: Worktree connectionId: string | null