diff --git a/apps/desktop/src/renderer/src/components/desktop-layout.tsx b/apps/desktop/src/renderer/src/components/desktop-layout.tsx index 02459a09da..faafd8f1c0 100644 --- a/apps/desktop/src/renderer/src/components/desktop-layout.tsx +++ b/apps/desktop/src/renderer/src/components/desktop-layout.tsx @@ -15,7 +15,6 @@ import { ChatFab, ChatWindow } from "@multica/views/chat"; import { StepWorkspace } from "@multica/views/onboarding"; import { WorkspaceSlugProvider } from "@multica/core/paths"; import { getCurrentSlug, subscribeToCurrentSlug } from "@multica/core/platform"; -import { MulticaIcon } from "@multica/ui/components/common/multica-icon"; import { DesktopNavigationProvider } from "@/platform/navigation"; import { OnboardingGate } from "./onboarding-gate"; import { TabBar } from "./tab-bar"; @@ -105,31 +104,31 @@ export function DesktopShell() { )} > - {slug ? ( - -
- - } searchSlot={} /> - {/* Right side: header + content container */} -
- - {/* Content area with inset styling — relative so ChatWindow/ChatFab are constrained here */} -
- - - -
+ {/* WorkspaceSlugProvider accepts null — components that need slug + use useWorkspaceSlug() (nullable) or useRequiredWorkspaceSlug() + (throws). TabContent MUST always render so the tab router can + mount WorkspaceRouteLayout, which calls setCurrentWorkspace() + to populate the slug. The sidebar gates on slug being present + to avoid the useRequiredWorkspaceSlug throw. */} + +
+ + {slug && } searchSlot={} />} + {/* Right side: header + content container */} +
+ + {/* Content area with inset styling — relative so ChatWindow/ChatFab are constrained here */} +
+ + {slug && } + {slug && }
- -
- - - - ) : ( -
- +
+
- )} + {slug && } + {slug && } +
); diff --git a/apps/desktop/src/renderer/src/stores/tab-store.ts b/apps/desktop/src/renderer/src/stores/tab-store.ts index 3886857e56..6842b44d7b 100644 --- a/apps/desktop/src/renderer/src/stores/tab-store.ts +++ b/apps/desktop/src/renderer/src/stores/tab-store.ts @@ -199,12 +199,28 @@ export const useTabStore = create()( | undefined; if (!persisted?.tabs?.length) return currentState; - const tabs: Tab[] = persisted.tabs.map((tab) => ({ - ...tab, - router: createTabRouter(tab.path), - historyIndex: 0, - historyLength: 1, - })); + const tabs: Tab[] = persisted.tabs.map((tab) => { + // Migration: pre-refactor tab paths like "/issues/abc" lack a + // workspace slug prefix. These would 404 in the new router. + // Reset to "/" so IndexRedirect picks the right workspace. + let path = tab.path; + if (path !== "/" && !isGlobalPath(path)) { + const segments = path.split("/").filter(Boolean); + const firstSegment = segments[0] ?? ""; + // If the first segment IS a known route name (e.g. "issues", + // "projects"), it's an old-format path missing the slug prefix. + if (ROUTE_ICONS[firstSegment]) { + path = "/"; + } + } + return { + ...tab, + path, + router: createTabRouter(path), + historyIndex: 0, + historyLength: 1, + }; + }); // Validate activeTabId — fall back to first tab if stale const activeTabId = tabs.some((t) => t.id === persisted.activeTabId)