From d7b7be1909d614a4022b345bdbeef0c1ec32e159 Mon Sep 17 00:00:00 2001 From: Luke Parker <10430890+Hona@users.noreply.github.com> Date: Thu, 30 Apr 2026 08:39:19 +1000 Subject: fix(desktop): Path mismatches cause sessions missing + strong ID + existing data fix (#25013) --- packages/app/src/pages/layout/helpers.test.ts | 16 ++++++++-------- packages/app/src/pages/layout/helpers.ts | 17 +++++------------ packages/app/src/pages/layout/sidebar-workspace.tsx | 5 +++-- 3 files changed, 16 insertions(+), 22 deletions(-) (limited to 'packages/app/src/pages/layout') diff --git a/packages/app/src/pages/layout/helpers.test.ts b/packages/app/src/pages/layout/helpers.test.ts index 988332ab7..9cf302482 100644 --- a/packages/app/src/pages/layout/helpers.test.ts +++ b/packages/app/src/pages/layout/helpers.test.ts @@ -14,8 +14,8 @@ import { errorMessage, hasProjectPermissions, latestRootSession, - workspaceKey, } from "./helpers" +import { pathKey } from "@/utils/path-key" const session = (input: Partial & Pick) => ({ @@ -104,16 +104,16 @@ describe("layout deep links", () => { describe("layout workspace helpers", () => { test("normalizes trailing slash in workspace key", () => { - expect(workspaceKey("/tmp/demo///")).toBe("/tmp/demo") - expect(workspaceKey("C:\\tmp\\demo\\\\")).toBe("C:/tmp/demo") + expect(String(pathKey("/tmp/demo///"))).toBe("/tmp/demo") + expect(String(pathKey("C:\\tmp\\demo\\\\"))).toBe("C:/tmp/demo") }) test("preserves posix and drive roots in workspace key", () => { - expect(workspaceKey("/")).toBe("/") - expect(workspaceKey("///")).toBe("/") - expect(workspaceKey("C:\\")).toBe("C:/") - expect(workspaceKey("C://")).toBe("C:/") - expect(workspaceKey("C:///")).toBe("C:/") + expect(String(pathKey("/"))).toBe("/") + expect(String(pathKey("///"))).toBe("/") + expect(String(pathKey("C:\\"))).toBe("C:/") + expect(String(pathKey("C://"))).toBe("C:/") + expect(String(pathKey("C:///"))).toBe("C:/") }) test("keeps local first while preserving known order", () => { diff --git a/packages/app/src/pages/layout/helpers.ts b/packages/app/src/pages/layout/helpers.ts index 4bc5254d9..d53381e40 100644 --- a/packages/app/src/pages/layout/helpers.ts +++ b/packages/app/src/pages/layout/helpers.ts @@ -1,19 +1,12 @@ import { getFilename } from "@opencode-ai/core/util/path" import { type Session } from "@opencode-ai/sdk/v2/client" +import { pathKey } from "@/utils/path-key" type SessionStore = { session?: Session[] path: { directory: string } } -export const workspaceKey = (directory: string) => { - const value = directory.replaceAll("\\", "/") - const drive = value.match(/^([A-Za-z]:)\/+$/) - if (drive) return `${drive[1]}/` - if (/^\/+$/i.test(value)) return "/" - return value.replace(/\/+$/, "") -} - function sortSessions(now: number) { const oneMinuteAgo = now - 60 * 1000 return (a: Session, b: Session) => { @@ -29,7 +22,7 @@ function sortSessions(now: number) { } const isRootVisibleSession = (session: Session, directory: string) => - workspaceKey(session.directory) === workspaceKey(directory) && !session.parentID && !session.time?.archived + pathKey(session.directory) === pathKey(directory) && !session.parentID && !session.time?.archived export const roots = (store: SessionStore) => (store.session ?? []).filter((session) => isRootVisibleSession(session, store.path.directory)) @@ -72,11 +65,11 @@ export const errorMessage = (err: unknown, fallback: string) => { } export const effectiveWorkspaceOrder = (local: string, dirs: string[], persisted?: string[]) => { - const root = workspaceKey(local) + const root = pathKey(local) const live = new Map() for (const dir of dirs) { - const key = workspaceKey(dir) + const key = pathKey(dir) if (key === root) continue if (!live.has(key)) live.set(key, dir) } @@ -85,7 +78,7 @@ export const effectiveWorkspaceOrder = (local: string, dirs: string[], persisted const result = [local] for (const dir of persisted) { - const key = workspaceKey(dir) + const key = pathKey(dir) if (key === root) continue const match = live.get(key) if (!match) continue diff --git a/packages/app/src/pages/layout/sidebar-workspace.tsx b/packages/app/src/pages/layout/sidebar-workspace.tsx index 0a3fc7f41..d2e887b44 100644 --- a/packages/app/src/pages/layout/sidebar-workspace.tsx +++ b/packages/app/src/pages/layout/sidebar-workspace.tsx @@ -16,8 +16,9 @@ import { type Session } from "@opencode-ai/sdk/v2/client" import { type LocalProject } from "@/context/layout" import { loadSessionsQuery, useGlobalSync } from "@/context/global-sync" import { useLanguage } from "@/context/language" +import { pathKey } from "@/utils/path-key" import { NewSessionItem, SessionItem, SessionSkeleton } from "./sidebar-items" -import { sortedRootSessions, workspaceKey } from "./helpers" +import { sortedRootSessions } from "./helpers" import { useQuery } from "@tanstack/solid-query" type InlineEditorComponent = (props: { @@ -309,7 +310,7 @@ export const SortableWorkspace = (props: { const slug = createMemo(() => base64Encode(props.directory)) const sessions = createMemo(() => sortedRootSessions(workspaceStore, props.sortNow())) const local = createMemo(() => props.directory === props.project.worktree) - const active = createMemo(() => workspaceKey(props.ctx.currentDir()) === workspaceKey(props.directory)) + const active = createMemo(() => pathKey(props.ctx.currentDir()) === pathKey(props.directory)) const workspaceValue = createMemo(() => { const branch = workspaceStore.vcs?.branch const name = branch ?? getFilename(props.directory) -- cgit v1.2.3