Skip to content

Commit 968ea5f

Browse files
committed
Use path-based metadata lookup for stable and legacy workspaces
Instead of extracting workspace ID from path (which fails for legacy workspaces), build a pathToMetadata map that maps both stableWorkspacePath and namedWorkspacePath to their metadata. This handles both new stable-ID workspaces and legacy name-based workspaces. Fixes E2E test failures where workspaces weren't appearing.
1 parent c7db9f0 commit 968ea5f

File tree

2 files changed

+35
-16
lines changed

2 files changed

+35
-16
lines changed

src/App.tsx

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import { useState, useEffect, useCallback, useRef } from "react";
1+
import { useState, useEffect, useCallback, useRef, useMemo } from "react";
22
import styled from "@emotion/styled";
33
import { Global, css } from "@emotion/react";
44
import { GlobalColors } from "./styles/colors";
55
import { GlobalFonts } from "./styles/fonts";
66
import { GlobalScrollbars } from "./styles/scrollbars";
77
import type { ProjectConfig } from "./config";
88
import type { WorkspaceSelection } from "./components/ProjectSidebar";
9+
import type { WorkspaceMetadataWithPaths } from "./types/workspace";
910
import { LeftSidebar } from "./components/LeftSidebar";
1011
import NewWorkspaceModal from "./components/NewWorkspaceModal";
1112
import { AIView } from "./components/AIView";
@@ -172,6 +173,17 @@ function AppInner() {
172173
onSelectedWorkspaceUpdate: setSelectedWorkspace,
173174
});
174175

176+
// Build path-to-metadata lookup map (handles both stable and legacy paths)
177+
const pathToMetadata = useMemo(() => {
178+
const map = new Map<string, WorkspaceMetadataWithPaths>();
179+
for (const metadata of workspaceMetadata.values()) {
180+
// Map both stable path (with ID) and named path (with name/legacy)
181+
map.set(metadata.stableWorkspacePath, metadata);
182+
map.set(metadata.namedWorkspacePath, metadata);
183+
}
184+
return map;
185+
}, [workspaceMetadata]);
186+
175187
// NEW: Sync workspace metadata with the stores
176188
const workspaceStore = useWorkspaceStoreRaw();
177189
const gitStatusStore = useGitStatusStoreRaw();
@@ -321,11 +333,9 @@ function AppInner() {
321333
result.set(
322334
projectPath,
323335
config.workspaces.slice().sort((a, b) => {
324-
// Extract workspace ID from path
325-
const aId = a.path.replace(/\\/g, "/").split("/").pop() ?? "";
326-
const bId = b.path.replace(/\\/g, "/").split("/").pop() ?? "";
327-
const aMeta = workspaceMetadata.get(aId);
328-
const bMeta = workspaceMetadata.get(bId);
336+
// Look up metadata by workspace path (handles both stable and legacy paths)
337+
const aMeta = pathToMetadata.get(a.path);
338+
const bMeta = pathToMetadata.get(b.path);
329339
if (!aMeta || !bMeta) return 0;
330340

331341
// Get timestamp of most recent user message (0 if never used)
@@ -349,7 +359,7 @@ function AppInner() {
349359
}
350360
return true;
351361
},
352-
[projects, workspaceMetadata, workspaceRecency]
362+
[projects, workspaceMetadata, workspaceRecency, pathToMetadata]
353363
);
354364

355365
const handleNavigateWorkspace = useCallback(
@@ -377,9 +387,8 @@ function AppInner() {
377387
const targetWorkspace = sortedWorkspaces[targetIndex];
378388
if (!targetWorkspace) return;
379389

380-
// Extract workspace ID from path
381-
const workspaceId = targetWorkspace.path.replace(/\\/g, "/").split("/").pop() ?? "";
382-
const metadata = workspaceMetadata.get(workspaceId);
390+
// Look up metadata by workspace path (handles both stable and legacy paths)
391+
const metadata = pathToMetadata.get(targetWorkspace.path);
383392
if (!metadata) return;
384393

385394
setSelectedWorkspace({
@@ -389,7 +398,7 @@ function AppInner() {
389398
workspaceId: metadata.id,
390399
});
391400
},
392-
[selectedWorkspace, sortedWorkspacesByProject, workspaceMetadata, setSelectedWorkspace]
401+
[selectedWorkspace, sortedWorkspacesByProject, pathToMetadata, setSelectedWorkspace]
393402
);
394403

395404
// Register command sources with registry

src/components/ProjectSidebar.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,17 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
512512
onUpdateSecrets,
513513
sortedWorkspacesByProject,
514514
}) => {
515+
// Build path-to-metadata lookup map (handles both stable and legacy paths)
516+
const pathToMetadata = React.useMemo(() => {
517+
const map = new Map<string, WorkspaceMetadataWithPaths>();
518+
for (const metadata of workspaceMetadata.values()) {
519+
// Map both stable path (with ID) and named path (with name/legacy)
520+
map.set(metadata.stableWorkspacePath, metadata);
521+
map.set(metadata.namedWorkspacePath, metadata);
522+
}
523+
return map;
524+
}, [workspaceMetadata]);
525+
515526
// Workspace-specific subscriptions moved to WorkspaceListItem component
516527

517528
// Store as array in localStorage, convert to Set for usage
@@ -822,12 +833,11 @@ const ProjectSidebarInner: React.FC<ProjectSidebarProps> = ({
822833
</WorkspaceHeader>
823834
{(sortedWorkspacesByProject.get(projectPath) ?? config.workspaces).map(
824835
(workspace) => {
825-
// Extract workspace ID from path (path format: ~/.cmux/src/projectName/workspaceId)
826-
// Handle both Unix (/) and Windows (\) path separators
827-
const pathParts = workspace.path.replace(/\\/g, "/").split("/");
828-
const workspaceId = pathParts[pathParts.length - 1] ?? "";
829-
const metadata = workspaceMetadata.get(workspaceId);
836+
// Look up metadata by workspace path (handles both stable ID paths and legacy name paths)
837+
const metadata = pathToMetadata.get(workspace.path);
830838
if (!metadata) return null;
839+
840+
const workspaceId = metadata.id;
831841
const isSelected =
832842
selectedWorkspace?.workspacePath === workspace.path;
833843

0 commit comments

Comments
 (0)