fix(desktop): front workspace pane when navigating sidebar routes

Capabilities/Messaging/Artifacts (and other full-page workspace routes) rendered their content correctly on navigate(), but the workspace pane itself stayed behind an active session tile in the pane tree if one was focused. Clicking the same sidebar item again after switching to a session tile appeared to do nothing.

Session switches already call revealTreePane('workspace') + noteActiveTreeGroup(null) to front the pane (store/session-states.ts), but sidebar/keybind/command-palette navigation to full-page routes did not have an equivalent.

Add navigateToWorkspacePage() helper in routes.ts that wraps navigate() and fronts the workspace pane for non-overlay, non-chat views. Apply it at all 5 call sites that navigate to full-page workspace routes: selectSidebarItem, use-keybinds (nav.skills/messaging/artifacts), command palette 'go', Command Center's onNavigateRoute, and cold-start route restore.

Fixes #72602
This commit is contained in:
alelpoan 2026-07-27 16:02:45 +03:00 committed by Brooklyn Nicholson
parent c2e45b555f
commit 9cef5496eb
6 changed files with 37 additions and 9 deletions

View file

@ -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.

View file

@ -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
}

View file

@ -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))}
/>
</Suspense>

View file

@ -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),

View file

@ -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')
}
}

View file

@ -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]