diff --git a/apps/desktop/src/app/command-palette/index.tsx b/apps/desktop/src/app/command-palette/index.tsx index 42bcef4e820a..e86bd2415e39 100644 --- a/apps/desktop/src/app/command-palette/index.tsx +++ b/apps/desktop/src/app/command-palette/index.tsx @@ -71,6 +71,7 @@ import { COMMAND_CENTER_ROUTE, CRON_ROUTE, MESSAGING_ROUTE, + navigateToWorkspacePage, NEW_CHAT_ROUTE, PROFILES_ROUTE, sessionRoute, @@ -351,7 +352,7 @@ export function CommandPalette() { } }, [open, pendingPage]) - const go = useCallback((path: string) => () => navigate(path), [navigate]) + const go = useCallback((path: string) => () => navigateToWorkspacePage(navigate, path), [navigate]) // Step up one nested page (or back to the root list), clearing the filter so // the parent page doesn't reopen mid-search. diff --git a/apps/desktop/src/app/contrib/hooks/use-desktop-integrations.ts b/apps/desktop/src/app/contrib/hooks/use-desktop-integrations.ts index ed22e2cc7ebe..5e6c75dee23a 100644 --- a/apps/desktop/src/app/contrib/hooks/use-desktop-integrations.ts +++ b/apps/desktop/src/app/contrib/hooks/use-desktop-integrations.ts @@ -17,7 +17,7 @@ import { openUpdatesWindow, startUpdatePoller, stopUpdatePoller } from '@/store/ import { isSecondaryWindow } from '@/store/windows' import { requestComposerFocus, requestComposerInsert } from '../../chat/composer/focus' -import { appViewForPath, isOverlayView, NEW_CHAT_ROUTE, sessionRoute } from '../../routes' +import { appViewForPath, isOverlayView, navigateToWorkspacePage, NEW_CHAT_ROUTE, sessionRoute } from '../../routes' interface DesktopIntegrationsParams { chatOpen: boolean @@ -99,7 +99,7 @@ export function useDesktopIntegrations({ const route = getRememberedRoute() if (route && route !== NEW_CHAT_ROUTE && !isOverlayView(appViewForPath(route))) { - navigate(route, { replace: true }) + navigateToWorkspacePage(navigate, route, { replace: true }) return } diff --git a/apps/desktop/src/app/contrib/wiring.tsx b/apps/desktop/src/app/contrib/wiring.tsx index de2e0becffdb..8e02554b67f5 100644 --- a/apps/desktop/src/app/contrib/wiring.tsx +++ b/apps/desktop/src/app/contrib/wiring.tsx @@ -78,6 +78,7 @@ import { closeAllTerminals } from '../right-sidebar/terminal/terminals' import { $workspaceIsPage, CRON_ROUTE, + navigateToWorkspacePage, routeSessionId, sessionRoute, SETTINGS_ROUTE, @@ -1015,7 +1016,7 @@ export function ContribWiring({ children }: { children: ReactNode }) { initialSection={commandCenterInitialSection} onClose={closeOverlayToPreviousRoute} onDeleteSession={removeSession} - onNavigateRoute={path => navigate(path)} + onNavigateRoute={path => navigateToWorkspacePage(navigate, path)} onOpenSession={sessionId => navigate(sessionRoute(sessionId))} /> diff --git a/apps/desktop/src/app/hooks/use-keybinds.ts b/apps/desktop/src/app/hooks/use-keybinds.ts index 8a1fe275478e..7e49029d1cea 100644 --- a/apps/desktop/src/app/hooks/use-keybinds.ts +++ b/apps/desktop/src/app/hooks/use-keybinds.ts @@ -57,6 +57,7 @@ import { ARTIFACTS_ROUTE, CRON_ROUTE, MESSAGING_ROUTE, + navigateToWorkspacePage, PROFILES_ROUTE, sessionRoute, SETTINGS_ROUTE, @@ -138,9 +139,9 @@ export function useKeybinds(deps: KeybindRuntimeDeps): void { 'nav.commandCenter': deps.toggleCommandCenter, 'nav.settings': () => navigate(SETTINGS_ROUTE), 'nav.profiles': () => navigate(PROFILES_ROUTE), - 'nav.skills': () => navigate(SKILLS_ROUTE), - 'nav.messaging': () => navigate(MESSAGING_ROUTE), - 'nav.artifacts': () => navigate(ARTIFACTS_ROUTE), + 'nav.skills': () => navigateToWorkspacePage(navigate, SKILLS_ROUTE), + 'nav.messaging': () => navigateToWorkspacePage(navigate, MESSAGING_ROUTE), + 'nav.artifacts': () => navigateToWorkspacePage(navigate, ARTIFACTS_ROUTE), 'nav.cron': () => navigate(CRON_ROUTE), 'nav.agents': () => navigate(AGENTS_ROUTE), diff --git a/apps/desktop/src/app/routes.ts b/apps/desktop/src/app/routes.ts index b3c28a0ae017..e834d26b1907 100644 --- a/apps/desktop/src/app/routes.ts +++ b/apps/desktop/src/app/routes.ts @@ -1,8 +1,11 @@ import { atom } from 'nanostores' import type { ReactNode } from 'react' +import { noteActiveTreeGroup, revealTreePane } from '@/components/pane-shell/tree/store' import { registry } from '@/contrib/registry' +type NavigateLike = (to: string, options?: { replace?: boolean }) => void + export const SESSION_ROUTE_PREFIX = '/' export const NEW_CHAT_ROUTE = '/' export const SETTINGS_ROUTE = '/settings' @@ -195,3 +198,25 @@ export function syncWorkspaceIsPage(pathname: string): void { $workspaceIsPage.set(isPage) } } + +/** + * Navigate to `path`, and if it lands on a full page rendered inside the + * `workspace` pane (skills/messaging/artifacts/contributed routes) also + * front the `workspace` pane in the pane tree. + * + * Without this, `navigate()` alone updates the route and the content inside + * `workspace` correctly, but if a session tile is currently focused in the + * same tab group, `workspace` stays behind it (issue #72602). This mirrors + * what `$selectedStoredSessionId.listen` already does for session switches + * in `store/session-states.ts`. + */ +export function navigateToWorkspacePage(navigate: NavigateLike, path: string, options?: { replace?: boolean }): void { + navigate(path, options) + + const view = appViewForPath(path) + + if (view !== 'chat' && !isOverlayView(view)) { + noteActiveTreeGroup(null) + revealTreePane('workspace') + } +} diff --git a/apps/desktop/src/app/session/hooks/use-session-actions/index.ts b/apps/desktop/src/app/session/hooks/use-session-actions/index.ts index fd490666d126..5d293c012b21 100644 --- a/apps/desktop/src/app/session/hooks/use-session-actions/index.ts +++ b/apps/desktop/src/app/session/hooks/use-session-actions/index.ts @@ -68,7 +68,7 @@ import { broadcastSessionsChanged } from '@/store/session-sync' import { isWatchWindow } from '@/store/windows' import type { SessionCreateResponse, SessionMessage, SessionResumeResponse, UsageStats } from '@/types/hermes' -import { NEW_CHAT_ROUTE, sessionRoute, SETTINGS_ROUTE } from '../../../routes' +import { navigateToWorkspacePage, NEW_CHAT_ROUTE, sessionRoute, SETTINGS_ROUTE } from '../../../routes' import type { ClientSessionState, SidebarNavItem } from '../../../types' import { sessionContextDrift } from '../session-context-drift' @@ -458,7 +458,7 @@ export function useSessionActions({ } if (item.route) { - navigate(item.route) + navigateToWorkspacePage(navigate, item.route) } }, [navigate, startFreshSessionDraft]