From 68fe0860b2d488e7a0a7bfd1ff5ccb4eb52b7fd4 Mon Sep 17 00:00:00 2001 From: Andy <119136210+AndyMik90@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:12:31 +0100 Subject: [PATCH] fix(frontend): sync worktree config to renderer on terminal restoration (#982) * fix(frontend): sync worktree config to renderer on terminal restoration When terminals are restored after app restart, the worktree config was not being synced to the renderer, causing the worktree label to not appear. This adds a new IPC channel to send worktree config during restoration and a listener in useTerminalEvents to update the terminal store. Co-Authored-By: Claude Opus 4.5 * fix(frontend): always sync worktreeConfig to handle deleted worktrees Addresses PR review feedback: send worktreeConfig IPC message unconditionally so the renderer can clear stale worktree labels when a worktree is deleted while the app is closed. Co-Authored-By: Claude Opus 4.5 --------- Co-authored-by: Claude Opus 4.5 --- .../src/main/terminal/terminal-lifecycle.ts | 3 +++ apps/frontend/src/preload/api/terminal-api.ts | 17 +++++++++++++++++ .../components/terminal/useTerminalEvents.ts | 12 ++++++++++++ .../src/renderer/lib/mocks/terminal-mock.ts | 1 + apps/frontend/src/shared/constants/ipc.ts | 1 + apps/frontend/src/shared/types/ipc.ts | 2 ++ 6 files changed, 36 insertions(+) diff --git a/apps/frontend/src/main/terminal/terminal-lifecycle.ts b/apps/frontend/src/main/terminal/terminal-lifecycle.ts index 22d7eaec..bb1f9141 100644 --- a/apps/frontend/src/main/terminal/terminal-lifecycle.ts +++ b/apps/frontend/src/main/terminal/terminal-lifecycle.ts @@ -189,6 +189,9 @@ export async function restoreTerminal( const win = getWindow(); if (win) { win.webContents.send(IPC_CHANNELS.TERMINAL_TITLE_CHANGE, session.id, session.title); + // Always sync worktreeConfig to renderer (even if undefined) to ensure correct state + // This handles both: showing labels after recovery AND clearing stale labels when worktrees are deleted + win.webContents.send(IPC_CHANNELS.TERMINAL_WORKTREE_CONFIG_CHANGE, session.id, terminal.worktreeConfig); } // Defer Claude resume until terminal becomes active (is viewed by user) diff --git a/apps/frontend/src/preload/api/terminal-api.ts b/apps/frontend/src/preload/api/terminal-api.ts index 7ea08f71..c66e0c2f 100644 --- a/apps/frontend/src/preload/api/terminal-api.ts +++ b/apps/frontend/src/preload/api/terminal-api.ts @@ -69,6 +69,7 @@ export interface TerminalAPI { onTerminalOutput: (callback: (id: string, data: string) => void) => () => void; onTerminalExit: (callback: (id: string, exitCode: number) => void) => () => void; onTerminalTitleChange: (callback: (id: string, title: string) => void) => () => void; + onTerminalWorktreeConfigChange: (callback: (id: string, config: TerminalWorktreeConfig | undefined) => void) => () => void; onTerminalClaudeSession: (callback: (id: string, sessionId: string) => void) => () => void; onTerminalRateLimit: (callback: (info: RateLimitInfo) => void) => () => void; onTerminalOAuthToken: ( @@ -227,6 +228,22 @@ export const createTerminalAPI = (): TerminalAPI => ({ }; }, + onTerminalWorktreeConfigChange: ( + callback: (id: string, config: TerminalWorktreeConfig | undefined) => void + ): (() => void) => { + const handler = ( + _event: Electron.IpcRendererEvent, + id: string, + config: TerminalWorktreeConfig | undefined + ): void => { + callback(id, config); + }; + ipcRenderer.on(IPC_CHANNELS.TERMINAL_WORKTREE_CONFIG_CHANGE, handler); + return () => { + ipcRenderer.removeListener(IPC_CHANNELS.TERMINAL_WORKTREE_CONFIG_CHANGE, handler); + }; + }, + onTerminalClaudeSession: ( callback: (id: string, sessionId: string) => void ): (() => void) => { diff --git a/apps/frontend/src/renderer/components/terminal/useTerminalEvents.ts b/apps/frontend/src/renderer/components/terminal/useTerminalEvents.ts index 60e60d9f..47dbe0c3 100644 --- a/apps/frontend/src/renderer/components/terminal/useTerminalEvents.ts +++ b/apps/frontend/src/renderer/components/terminal/useTerminalEvents.ts @@ -114,6 +114,18 @@ export function useTerminalEvents({ return cleanup; }, [terminalId]); + // Handle worktree config change (synced from main process during restoration) + // This ensures the worktree label appears after terminal recovery + useEffect(() => { + const cleanup = window.electronAPI.onTerminalWorktreeConfigChange((id, config) => { + if (id === terminalId) { + useTerminalStore.getState().setWorktreeConfig(terminalId, config); + } + }); + + return cleanup; + }, [terminalId]); + // Handle Claude session ID capture useEffect(() => { const cleanup = window.electronAPI.onTerminalClaudeSession((id, sessionId) => { diff --git a/apps/frontend/src/renderer/lib/mocks/terminal-mock.ts b/apps/frontend/src/renderer/lib/mocks/terminal-mock.ts index 4a806165..8888707c 100644 --- a/apps/frontend/src/renderer/lib/mocks/terminal-mock.ts +++ b/apps/frontend/src/renderer/lib/mocks/terminal-mock.ts @@ -92,6 +92,7 @@ export const terminalMock = { onTerminalOutput: () => () => {}, onTerminalExit: () => () => {}, onTerminalTitleChange: () => () => {}, + onTerminalWorktreeConfigChange: () => () => {}, onTerminalClaudeSession: () => () => {}, onTerminalRateLimit: () => () => {}, onTerminalOAuthToken: () => () => {}, diff --git a/apps/frontend/src/shared/constants/ipc.ts b/apps/frontend/src/shared/constants/ipc.ts index a95ef12c..0ea976ed 100644 --- a/apps/frontend/src/shared/constants/ipc.ts +++ b/apps/frontend/src/shared/constants/ipc.ts @@ -88,6 +88,7 @@ export const IPC_CHANNELS = { TERMINAL_OUTPUT: 'terminal:output', TERMINAL_EXIT: 'terminal:exit', TERMINAL_TITLE_CHANGE: 'terminal:titleChange', + TERMINAL_WORKTREE_CONFIG_CHANGE: 'terminal:worktreeConfigChange', // Worktree config restored/changed (for sync on recovery) TERMINAL_CLAUDE_SESSION: 'terminal:claudeSession', // Claude session ID captured TERMINAL_PENDING_RESUME: 'terminal:pendingResume', // Terminal has pending Claude resume (for deferred activation) TERMINAL_RATE_LIMIT: 'terminal:rateLimit', // Claude Code rate limit detected diff --git a/apps/frontend/src/shared/types/ipc.ts b/apps/frontend/src/shared/types/ipc.ts index d37617db..5afa310e 100644 --- a/apps/frontend/src/shared/types/ipc.ts +++ b/apps/frontend/src/shared/types/ipc.ts @@ -219,6 +219,8 @@ export interface ElectronAPI { onTerminalOutput: (callback: (id: string, data: string) => void) => () => void; onTerminalExit: (callback: (id: string, exitCode: number) => void) => () => void; onTerminalTitleChange: (callback: (id: string, title: string) => void) => () => void; + /** Listen for worktree config changes (synced from main process during restoration) */ + onTerminalWorktreeConfigChange: (callback: (id: string, config: TerminalWorktreeConfig | undefined) => void) => () => void; onTerminalClaudeSession: (callback: (id: string, sessionId: string) => void) => () => void; onTerminalRateLimit: (callback: (info: RateLimitInfo) => void) => () => void; /** Listen for OAuth authentication completion (token is auto-saved to profile, never exposed to frontend) */