From e53c3da2231e45ed6622faf505191ab98496f5a2 Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Wed, 10 Jun 2026 02:24:47 +0300 Subject: [PATCH] fix: keep tray session switches in sync Open tray sessions from the correct directory Allow remote instances to update the tray Update the active project when switching sessions --- packages/electron/main.mjs | 1 + packages/electron/tray.mjs | 6 +++--- packages/ui/src/sync/session-ui-store.ts | 11 +++++++++++ 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/packages/electron/main.mjs b/packages/electron/main.mjs index 3067a1a0..4358e39f 100644 --- a/packages/electron/main.mjs +++ b/packages/electron/main.mjs @@ -4004,6 +4004,7 @@ const COMMANDS_SAFE_FOR_REMOTE = new Set([ 'desktop_get_app_version', 'desktop_get_lan_address', 'desktop_capture_page_rect', + 'desktop_tray_update', ]); ipcMain.handle('openchamber:invoke', async (event, command, args) => { diff --git a/packages/electron/tray.mjs b/packages/electron/tray.mjs index 5a8bb339..721c891c 100644 --- a/packages/electron/tray.mjs +++ b/packages/electron/tray.mjs @@ -180,13 +180,13 @@ export const createTrayController = ({ idleIconPath, unseenIconPath, breathIconP { type: 'separator' }, { label: 'Deny', click: () => onAction({ type: 'respond-permission', sessionId: approval.sessionId, id: approval.id, response: 'reject' }) }, { type: 'separator' }, - { label: 'Open in app', click: () => onAction({ type: 'focus-session', sessionId: approval.sessionId }) }, + { label: 'Open in app', click: () => onAction({ type: 'focus-session', sessionId: approval.sessionId, directory: approval.directory || '' }) }, ], }; } return { label: approvalLabel(approval), - click: () => onAction({ type: 'focus-session', sessionId: approval.sessionId }), + click: () => onAction({ type: 'focus-session', sessionId: approval.sessionId, directory: approval.directory || '' }), }; }; for (const approval of approvals.slice(0, MAX_APPROVALS)) { @@ -209,7 +209,7 @@ export const createTrayController = ({ idleIconPath, unseenIconPath, breathIconP icon: statusIcons[statusIconKey(session)] || statusIcons.blank, // Secondary smaller line (macOS): project ยท branch. ...(session.subtitle ? { sublabel: truncate(session.subtitle, 48) } : {}), - click: () => onAction({ type: 'focus-session', sessionId: session.id }), + click: () => onAction({ type: 'focus-session', sessionId: session.id, directory: session.directory || '' }), }); if (sessions.length > 0) { diff --git a/packages/ui/src/sync/session-ui-store.ts b/packages/ui/src/sync/session-ui-store.ts index 2e8311ae..fcf0a4b0 100644 --- a/packages/ui/src/sync/session-ui-store.ts +++ b/packages/ui/src/sync/session-ui-store.ts @@ -442,6 +442,14 @@ export const useSessionUIStore = create()((set, get) => ({ ) const fallbackDir = opencodeClient.getDirectory() ?? directoryState.currentDirectory ?? null const resolvedDir = (directoryHint ? normalizePath(directoryHint) : null) ?? sessionDir ?? fallbackDir + const projectsState = useProjectsStore.getState() + const sessionProject = resolvedDir + ? resolveProjectForSessionDirectory( + projectsState.projects, + get().availableWorktreesByProject, + resolvedDir, + ) + : null // Set the directory together with the session id so chat hooks read the // same child store that send/SSE events will update during startup races. @@ -452,6 +460,9 @@ export const useSessionUIStore = create()((set, get) => ({ if (resolvedDir && directoryState.currentDirectory !== resolvedDir) { directoryState.setDirectory(resolvedDir, { showOverlay: false }) } + if (sessionProject && projectsState.activeProjectId !== sessionProject.id) { + projectsState.setActiveProjectIdOnly(sessionProject.id) + } opencodeClient.setDirectory(resolvedDir ?? undefined) } catch (e) { console.warn("Failed to set OpenCode directory for session switch:", e)