Make New Workspace an app-wide screen (#1746)

* fix(app): make new workspace global

New Workspace now opens as an app-wide screen. Host and project context can still preselect the form through query params, but the route itself no longer lives under a host.

* fix(app): simplify new workspace selection
This commit is contained in:
Mohamed Boudra
2026-06-26 15:50:02 +08:00
committed by GitHub
parent 14a91d889c
commit 3fbd82664d
14 changed files with 140 additions and 89 deletions
+2 -2
View File
@@ -159,7 +159,7 @@ export async function openNewWorkspaceComposer(
await expect(button).toBeVisible({ timeout: 30_000 }); await expect(button).toBeVisible({ timeout: 30_000 });
await button.click(); await button.click();
await expect(page).toHaveURL(/\/h\/[^/]+\/new(?:\?.*)?$/, { await expect(page).toHaveURL(/\/new(?:\?.*)?$/, {
timeout: 30_000, timeout: 30_000,
}); });
} }
@@ -167,7 +167,7 @@ export async function openNewWorkspaceComposer(
export async function openGlobalNewWorkspaceComposer(page: Page): Promise<void> { export async function openGlobalNewWorkspaceComposer(page: Page): Promise<void> {
await page.getByTestId("sidebar-global-new-workspace").click(); await page.getByTestId("sidebar-global-new-workspace").click();
await expect(page).toHaveURL(/\/h\/[^/]+\/new(?:\?.*)?$/, { await expect(page).toHaveURL(/\/new(?:\?.*)?$/, {
timeout: 30_000, timeout: 30_000,
}); });
} }
+17 -1
View File
@@ -6,7 +6,9 @@ import {
openGlobalNewWorkspaceComposer, openGlobalNewWorkspaceComposer,
openNewWorkspaceComposer, openNewWorkspaceComposer,
} from "./helpers/new-workspace"; } from "./helpers/new-workspace";
import { getE2EDaemonPort } from "./helpers/daemon-port";
import { seedWorkspace, type SeededWorkspace } from "./helpers/seed-client"; import { seedWorkspace, type SeededWorkspace } from "./helpers/seed-client";
import { seedSavedSettingsHosts } from "./helpers/settings";
import { getServerId } from "./helpers/server-id"; import { getServerId } from "./helpers/server-id";
import { waitForSidebarHydration } from "./helpers/workspace-ui"; import { waitForSidebarHydration } from "./helpers/workspace-ui";
@@ -38,6 +40,19 @@ test.describe("New workspace entry points", () => {
const seeded: SeededWorkspace = await seedWorkspace({ repoPrefix: "entry-global-button-" }); const seeded: SeededWorkspace = await seedWorkspace({ repoPrefix: "entry-global-button-" });
try { try {
await seedSavedSettingsHosts(page, [
{
serverId: getServerId(),
label: "localhost",
endpoint: `127.0.0.1:${getE2EDaemonPort()}`,
},
{
serverId: "secondary-new-workspace-host",
label: "Secondary host",
endpoint: "127.0.0.1:9",
},
]);
await gotoAppShell(page); await gotoAppShell(page);
await waitForSidebarHydration(page); await waitForSidebarHydration(page);
await expect( await expect(
@@ -48,11 +63,12 @@ test.describe("New workspace entry points", () => {
await expect(globalButton).toBeVisible({ timeout: 30_000 }); await expect(globalButton).toBeVisible({ timeout: 30_000 });
await openGlobalNewWorkspaceComposer(page); await openGlobalNewWorkspaceComposer(page);
await expect(page.getByTestId("host-chooser")).toHaveCount(0);
// The screen is up: its project picker trigger is the canonical landmark.
await expect(page.getByTestId("new-workspace-project-picker-trigger")).toBeVisible({ await expect(page.getByTestId("new-workspace-project-picker-trigger")).toBeVisible({
timeout: 30_000, timeout: 30_000,
}); });
await expect(page.getByTestId("host-picker-trigger")).toBeVisible({ timeout: 30_000 });
} finally { } finally {
await seeded.cleanup(); await seeded.cleanup();
} }
+6 -1
View File
@@ -878,7 +878,11 @@ function AppWithSidebar({ children }: { children: ReactNode }) {
const routeHasKnownHost = const routeHasKnownHost =
routeServerId !== null && hosts.some((host) => host.serverId === routeServerId); routeServerId !== null && hosts.some((host) => host.serverId === routeServerId);
const shouldShowAppChrome = const shouldShowAppChrome =
storeReady && (pathname === "/open-project" || pathname === "/sessions" || routeHasKnownHost); storeReady &&
(pathname === "/open-project" ||
pathname === "/new" ||
pathname === "/sessions" ||
routeHasKnownHost);
// Parse selectedAgentKey directly from pathname // Parse selectedAgentKey directly from pathname
// useLocalSearchParams doesn't update when navigating between same-pattern routes // useLocalSearchParams doesn't update when navigating between same-pattern routes
@@ -933,6 +937,7 @@ function RootStack() {
<Stack.Screen name="settings/[section]" /> <Stack.Screen name="settings/[section]" />
<Stack.Screen name="settings/projects/index" /> <Stack.Screen name="settings/projects/index" />
<Stack.Screen name="settings/projects/[projectKey]" /> <Stack.Screen name="settings/projects/[projectKey]" />
<Stack.Screen name="new" />
<Stack.Screen name="open-project" /> <Stack.Screen name="open-project" />
<Stack.Screen name="sessions" /> <Stack.Screen name="sessions" />
<Stack.Screen name="pair-scan" /> <Stack.Screen name="pair-scan" />
@@ -39,7 +39,6 @@ function KnownHostRoute() {
<Stack.Screen name="agent/[agentId]" options={AGENT_SCREEN_OPTIONS} /> <Stack.Screen name="agent/[agentId]" options={AGENT_SCREEN_OPTIONS} />
<Stack.Screen name="sessions" /> <Stack.Screen name="sessions" />
<Stack.Screen name="open-project" /> <Stack.Screen name="open-project" />
<Stack.Screen name="new" />
<Stack.Screen name="settings" /> <Stack.Screen name="settings" />
</Stack> </Stack>
); );
@@ -2,7 +2,7 @@ import { useLocalSearchParams } from "expo-router";
import { HostRouteBootstrapBoundary } from "@/components/host-route-bootstrap-boundary"; import { HostRouteBootstrapBoundary } from "@/components/host-route-bootstrap-boundary";
import { NewWorkspaceScreen } from "@/screens/new-workspace-screen"; import { NewWorkspaceScreen } from "@/screens/new-workspace-screen";
export default function HostNewWorkspaceRoute() { export default function NewWorkspaceRoute() {
const params = useLocalSearchParams<{ const params = useLocalSearchParams<{
serverId?: string; serverId?: string;
dir?: string; dir?: string;
@@ -13,10 +13,17 @@ export default function HostNewWorkspaceRoute() {
const sourceDirectory = typeof params.dir === "string" ? params.dir : undefined; const sourceDirectory = typeof params.dir === "string" ? params.dir : undefined;
const displayName = typeof params.name === "string" ? params.name : undefined; const displayName = typeof params.name === "string" ? params.name : undefined;
const projectId = typeof params.projectId === "string" ? params.projectId : undefined; const projectId = typeof params.projectId === "string" ? params.projectId : undefined;
const screenKey = JSON.stringify([
serverId,
sourceDirectory ?? null,
displayName ?? null,
projectId ?? null,
]);
return ( return (
<HostRouteBootstrapBoundary> <HostRouteBootstrapBoundary>
<NewWorkspaceScreen <NewWorkspaceScreen
key={screenKey}
serverId={serverId} serverId={serverId}
sourceDirectory={sourceDirectory} sourceDirectory={sourceDirectory}
displayName={displayName} displayName={displayName}
+3 -10
View File
@@ -30,7 +30,6 @@ import { useIsCompactFormFactor } from "@/constants/layout";
import { isWeb } from "@/constants/platform"; import { isWeb } from "@/constants/platform";
import { useSidebarAnimation } from "@/contexts/sidebar-animation-context"; import { useSidebarAnimation } from "@/contexts/sidebar-animation-context";
import { useOpenProjectPicker } from "@/hooks/use-open-project-picker"; import { useOpenProjectPicker } from "@/hooks/use-open-project-picker";
import { useHostChooser } from "@/hosts/host-chooser";
import { useShortcutKeys } from "@/hooks/use-shortcut-keys"; import { useShortcutKeys } from "@/hooks/use-shortcut-keys";
import { useSidebarShortcutModel } from "@/hooks/use-sidebar-shortcut-model"; import { useSidebarShortcutModel } from "@/hooks/use-sidebar-shortcut-model";
import { import {
@@ -52,7 +51,7 @@ import { useWindowControlsPadding } from "@/utils/desktop-window";
import { canCloseLeftSidebarGesture } from "@/utils/sidebar-animation-state"; import { canCloseLeftSidebarGesture } from "@/utils/sidebar-animation-state";
import { import {
buildOpenProjectRoute, buildOpenProjectRoute,
buildHostNewWorkspaceRoute, buildNewWorkspaceRoute,
buildSessionsRoute, buildSessionsRoute,
buildSettingsAddHostRoute, buildSettingsAddHostRoute,
buildSettingsHostSectionRoute, buildSettingsHostSectionRoute,
@@ -166,7 +165,6 @@ export const LeftSidebar = memo(function LeftSidebar({
}, [isRevalidating, isManualRefresh]); }, [isRevalidating, isManualRefresh]);
const openProjectPicker = useOpenProjectPicker(); const openProjectPicker = useOpenProjectPicker();
const chooseHost = useHostChooser();
const handleOpenProjectMobile = useCallback(() => { const handleOpenProjectMobile = useCallback(() => {
showMobileAgent(); showMobileAgent();
@@ -178,13 +176,8 @@ export const LeftSidebar = memo(function LeftSidebar({
}, [openProjectPicker]); }, [openProjectPicker]);
const handleNewWorkspaceNavigate = useCallback(() => { const handleNewWorkspaceNavigate = useCallback(() => {
chooseHost({ router.push(buildNewWorkspaceRoute());
title: "Choose host", }, []);
onChooseHost: (serverId) => {
router.push(buildHostNewWorkspaceRoute(serverId));
},
});
}, [chooseHost]);
const handleSettingsMobile = useCallback(() => { const handleSettingsMobile = useCallback(() => {
showMobileAgent(); showMobileAgent();
@@ -59,7 +59,7 @@ import { getHostRuntimeStore, useHosts } from "@/runtime/host-runtime";
import { useIsCompactFormFactor } from "@/constants/layout"; import { useIsCompactFormFactor } from "@/constants/layout";
import { useProjectIconDataByProjectKey } from "@/projects/project-icons"; import { useProjectIconDataByProjectKey } from "@/projects/project-icons";
import { import {
buildHostNewWorkspaceRoute, buildNewWorkspaceRoute,
buildProjectSettingsRoute, buildProjectSettingsRoute,
parseHostWorkspaceRouteFromPathname, parseHostWorkspaceRouteFromPathname,
} from "@/utils/host-routes"; } from "@/utils/host-routes";
@@ -992,7 +992,9 @@ function NewWorkspaceGhostRow({
const handlePress = useCallback(() => { const handlePress = useCallback(() => {
onWorkspacePress?.(); onWorkspacePress?.();
router.navigate( router.navigate(
buildHostNewWorkspaceRoute(worktreeTarget.serverId, worktreeTarget.iconWorkingDir, { buildNewWorkspaceRoute({
serverId: worktreeTarget.serverId,
sourceDirectory: worktreeTarget.iconWorkingDir,
displayName, displayName,
projectId: project.projectKey, projectId: project.projectKey,
}) as Href, }) as Href,
@@ -1287,7 +1289,9 @@ function ProjectHeaderRow({
} }
onWorkspacePress?.(); onWorkspacePress?.();
router.navigate( router.navigate(
buildHostNewWorkspaceRoute(worktreeTarget.serverId, worktreeTarget.iconWorkingDir, { buildNewWorkspaceRoute({
serverId: worktreeTarget.serverId,
sourceDirectory: worktreeTarget.iconWorkingDir,
displayName, displayName,
projectId: project.projectKey, projectId: project.projectKey,
}) as Href, }) as Href,
@@ -4,7 +4,7 @@ import { useKeyboardActionHandler } from "@/hooks/use-keyboard-action-handler";
import type { KeyboardActionId } from "@/keyboard/keyboard-action-dispatcher"; import type { KeyboardActionId } from "@/keyboard/keyboard-action-dispatcher";
import { useActiveWorkspaceSelection } from "@/stores/navigation-active-workspace-store"; import { useActiveWorkspaceSelection } from "@/stores/navigation-active-workspace-store";
import { useSessionStore } from "@/stores/session-store"; import { useSessionStore } from "@/stores/session-store";
import { buildHostNewWorkspaceRoute } from "@/utils/host-routes"; import { buildNewWorkspaceRoute } from "@/utils/host-routes";
import { projectDisplayNameFromProjectId } from "@/utils/project-display-name"; import { projectDisplayNameFromProjectId } from "@/utils/project-display-name";
const WORKTREE_NEW_ACTIONS: readonly KeyboardActionId[] = ["worktree.new"]; const WORKTREE_NEW_ACTIONS: readonly KeyboardActionId[] = ["worktree.new"];
@@ -14,7 +14,7 @@ export function useActiveWorktreeNewAction() {
const serverId = selection?.serverId ?? null; const serverId = selection?.serverId ?? null;
const workspaceId = selection?.workspaceId ?? null; const workspaceId = selection?.workspaceId ?? null;
const workingDir = useSessionStore((state) => { const activeGitWorkspace = useSessionStore((state) => {
if (!serverId || !workspaceId) { if (!serverId || !workspaceId) {
return null; return null;
} }
@@ -22,31 +22,30 @@ export function useActiveWorktreeNewAction() {
if (!workspace || workspace.projectKind !== "git") { if (!workspace || workspace.projectKind !== "git") {
return null; return null;
} }
return workspace.projectRootPath; return workspace;
}); });
const displayName = useSessionStore((state) => { const workingDir = activeGitWorkspace?.projectRootPath ?? null;
if (!serverId || !workspaceId) { const projectId = activeGitWorkspace?.projectId ?? null;
return null; const displayName = activeGitWorkspace
} ? activeGitWorkspace.projectDisplayName ||
const workspace = state.sessions[serverId]?.workspaces?.get(workspaceId); projectDisplayNameFromProjectId(activeGitWorkspace.projectId)
if (!workspace || workspace.projectKind !== "git") { : null;
return null;
}
return workspace.projectDisplayName || projectDisplayNameFromProjectId(workspace.projectId);
});
const handle = useCallback(() => { const handle = useCallback(() => {
if (!serverId || !workingDir) { if (!serverId || !workingDir) {
return false; return false;
} }
router.navigate( router.navigate(
buildHostNewWorkspaceRoute(serverId, workingDir, { buildNewWorkspaceRoute({
serverId,
sourceDirectory: workingDir,
displayName: displayName ?? undefined, displayName: displayName ?? undefined,
projectId: projectId ?? undefined,
}) as never, }) as never,
); );
return true; return true;
}, [serverId, workingDir, displayName]); }, [serverId, workingDir, displayName, projectId]);
useKeyboardActionHandler({ useKeyboardActionHandler({
handlerId: "worktree-new-active", handlerId: "worktree-new-active",
@@ -2,27 +2,29 @@ import { useCallback } from "react";
import { router } from "expo-router"; import { router } from "expo-router";
import { useKeyboardActionHandler } from "@/hooks/use-keyboard-action-handler"; import { useKeyboardActionHandler } from "@/hooks/use-keyboard-action-handler";
import type { KeyboardActionId } from "@/keyboard/keyboard-action-dispatcher"; import type { KeyboardActionId } from "@/keyboard/keyboard-action-dispatcher";
import { useHosts } from "@/runtime/host-runtime";
import { useActiveWorkspaceSelection } from "@/stores/navigation-active-workspace-store"; import { useActiveWorkspaceSelection } from "@/stores/navigation-active-workspace-store";
import { buildHostNewWorkspaceRoute } from "@/utils/host-routes"; import { buildNewWorkspaceRoute } from "@/utils/host-routes";
const WORKSPACE_NEW_ACTIONS: readonly KeyboardActionId[] = ["workspace.new"]; const WORKSPACE_NEW_ACTIONS: readonly KeyboardActionId[] = ["workspace.new"];
export function useGlobalNewWorkspaceAction() { export function useGlobalNewWorkspaceAction() {
const selection = useActiveWorkspaceSelection(); const selection = useActiveWorkspaceSelection();
const serverId = selection?.serverId ?? null; const serverId = selection?.serverId ?? null;
const hosts = useHosts();
const handle = useCallback(() => { const handle = useCallback(() => {
if (!serverId) { if (hosts.length === 0) {
return false; return false;
} }
router.navigate(buildHostNewWorkspaceRoute(serverId) as never); router.navigate(buildNewWorkspaceRoute(serverId ? { serverId } : undefined) as never);
return true; return true;
}, [serverId]); }, [hosts.length, serverId]);
useKeyboardActionHandler({ useKeyboardActionHandler({
handlerId: "workspace-new-global", handlerId: "workspace-new-global",
actions: WORKSPACE_NEW_ACTIONS, actions: WORKSPACE_NEW_ACTIONS,
enabled: serverId !== null, enabled: hosts.length > 0,
priority: 0, priority: 0,
handle, handle,
}); });
@@ -976,11 +976,24 @@ function submitWorkspaceDraft(input: SubmitDraftInput): void {
function useNewWorkspaceHostSelector(initialServerId: string) { function useNewWorkspaceHostSelector(initialServerId: string) {
const allHosts = useHosts(); const allHosts = useHosts();
const allServerIds = useMemo(() => allHosts.map((h) => h.serverId), [allHosts]); const allServerIds = useMemo(() => allHosts.map((h) => h.serverId), [allHosts]);
const [selectedServerId, setSelectedServerId] = useState(initialServerId); const lastWorkspaceSelection = useLastWorkspaceSelection();
const normalizedInitialServerId = initialServerId.trim();
const routeInitialServerId = allServerIds.includes(normalizedInitialServerId)
? normalizedInitialServerId
: null;
const fallbackServerId =
lastWorkspaceSelection && allServerIds.includes(lastWorkspaceSelection.serverId)
? lastWorkspaceSelection.serverId
: (allServerIds[0] ?? "");
const [manualServerId, setManualServerId] = useState<string | null>(null);
const [hostPickerOpen, setHostPickerOpen] = useState(false); const [hostPickerOpen, setHostPickerOpen] = useState(false);
const selectedServerId =
manualServerId && allServerIds.includes(manualServerId)
? manualServerId
: (routeInitialServerId ?? fallbackServerId);
const handleSelectHost = useCallback((id: string) => { const handleSelectHost = useCallback((id: string) => {
setSelectedServerId(id); setManualServerId(id);
setHostPickerOpen(false); setHostPickerOpen(false);
}, []); }, []);
@@ -996,9 +1009,7 @@ function useNewWorkspaceHostSelector(initialServerId: string) {
allHosts, allHosts,
allServerIds, allServerIds,
selectedServerId, selectedServerId,
setSelectedServerId,
hostPickerOpen, hostPickerOpen,
setHostPickerOpen,
handleSelectHost, handleSelectHost,
handleHostPickerOpenChange, handleHostPickerOpenChange,
openHostPicker, openHostPicker,
+20 -14
View File
@@ -1,10 +1,10 @@
import { describe, expect, it } from "vitest"; import { describe, expect, it } from "vitest";
import { import {
buildHostAgentDetailRoute, buildHostAgentDetailRoute,
buildHostNewWorkspaceRoute,
buildHostRootRoute, buildHostRootRoute,
buildHostWorkspaceOpenRoute, buildHostWorkspaceOpenRoute,
buildHostWorkspaceRoute, buildHostWorkspaceRoute,
buildNewWorkspaceRoute,
buildOpenProjectRoute, buildOpenProjectRoute,
resolveKnownHostRoute, resolveKnownHostRoute,
buildSessionsRoute, buildSessionsRoute,
@@ -149,19 +149,6 @@ describe("workspace route parsing", () => {
).toBe("/h/local/workspace/b64_L3RtcC9yZXBv"); ).toBe("/h/local/workspace/b64_L3RtcC9yZXBv");
}); });
it("builds a global new workspace route without a source directory", () => {
expect(buildHostNewWorkspaceRoute("local")).toBe("/h/local/new");
});
it("builds a project shortcut new workspace route with initial project context", () => {
expect(
buildHostNewWorkspaceRoute("local", "/repo/project", {
displayName: "Project",
projectId: "project-1",
}),
).toBe("/h/local/new?dir=%2Frepo%2Fproject&name=Project&projectId=project-1");
});
it("round-trips URL-safe IDs through encode/decode", () => { it("round-trips URL-safe IDs through encode/decode", () => {
const ids = ["1", "40", "164", "9999", "workspace-1", "opaque_id.v2~test"]; const ids = ["1", "40", "164", "9999", "workspace-1", "opaque_id.v2~test"];
for (const id of ids) { for (const id of ids) {
@@ -216,6 +203,25 @@ describe("global routes", () => {
it("buildSessionsRoute returns the all-host Sessions route", () => { it("buildSessionsRoute returns the all-host Sessions route", () => {
expect(buildSessionsRoute()).toBe("/sessions"); expect(buildSessionsRoute()).toBe("/sessions");
}); });
it("buildNewWorkspaceRoute returns the all-host New Workspace route", () => {
expect(buildNewWorkspaceRoute()).toBe("/new");
});
it("buildNewWorkspaceRoute accepts an initial host", () => {
expect(buildNewWorkspaceRoute({ serverId: "local" })).toBe("/new?serverId=local");
});
it("buildNewWorkspaceRoute accepts initial project context", () => {
expect(
buildNewWorkspaceRoute({
serverId: "local",
sourceDirectory: "/repo/project",
displayName: "Project",
projectId: "project-1",
}),
).toBe("/new?serverId=local&dir=%2Frepo%2Fproject&name=Project&projectId=project-1");
});
}); });
describe("host settings section slugs", () => { describe("host settings section slugs", () => {
+33 -26
View File
@@ -414,6 +414,39 @@ export function buildOpenProjectRoute() {
return "/open-project" as const; return "/open-project" as const;
} }
interface NewWorkspaceRouteOptions {
serverId?: string;
sourceDirectory?: string;
displayName?: string;
projectId?: string;
}
function buildNewWorkspaceSearch(options: NewWorkspaceRouteOptions): string {
const params = new URLSearchParams();
const serverId = trimNonEmpty(options.serverId);
if (serverId) {
params.set("serverId", serverId);
}
if (options.sourceDirectory) {
params.set("dir", options.sourceDirectory);
}
if (options.displayName) {
params.set("name", options.displayName);
}
if (options.projectId) {
params.set("projectId", options.projectId);
}
return params.toString();
}
export function buildNewWorkspaceRoute(options: NewWorkspaceRouteOptions = {}) {
const query = buildNewWorkspaceSearch(options);
if (!query) {
return "/new" as const;
}
return `/new?${query}` as const;
}
export type KnownHostRouteResolution = export type KnownHostRouteResolution =
| { kind: "render" } | { kind: "render" }
| { kind: "redirect"; href: ReturnType<typeof buildOpenProjectRoute> | "/welcome" }; | { kind: "redirect"; href: ReturnType<typeof buildOpenProjectRoute> | "/welcome" };
@@ -434,32 +467,6 @@ export function resolveKnownHostRoute(input: {
return { kind: "redirect", href: "/welcome" }; return { kind: "redirect", href: "/welcome" };
} }
export function buildHostNewWorkspaceRoute(
serverId: string,
sourceDirectory?: string,
options?: { displayName?: string; projectId?: string },
) {
const base = buildHostRootRoute(serverId);
if (base === "/") {
return "/" as const;
}
const params = new URLSearchParams();
if (sourceDirectory) {
params.set("dir", sourceDirectory);
}
if (options?.displayName) {
params.set("name", options.displayName);
}
if (options?.projectId) {
params.set("projectId", options.projectId);
}
const query = params.toString();
if (!query) {
return `${base}/new` as const;
}
return `${base}/new?${query}` as const;
}
export const SETTINGS_SECTION_SLUGS = [ export const SETTINGS_SECTION_SLUGS = [
"general", "general",
"daemon", "daemon",
@@ -40,7 +40,7 @@ describe("buildWorkspaceArchiveRedirectRoute", () => {
archivedWorkspaceId: "/repo/.paseo/worktrees/feature", archivedWorkspaceId: "/repo/.paseo/worktrees/feature",
workspaces, workspaces,
}), }),
).toBe("/h/server-1/new?dir=%2Frepo&name=Project&projectId=project-1"); ).toBe("/new?serverId=server-1&dir=%2Frepo&name=Project&projectId=project-1");
}); });
it("redirects to the new workspace route when no sibling workspace target exists", () => { it("redirects to the new workspace route when no sibling workspace target exists", () => {
@@ -58,7 +58,7 @@ describe("buildWorkspaceArchiveRedirectRoute", () => {
archivedWorkspaceId: "/repo/.paseo/worktrees/feature", archivedWorkspaceId: "/repo/.paseo/worktrees/feature",
workspaces, workspaces,
}), }),
).toBe("/h/server-1/new?dir=%2Frepo&name=Project&projectId=project-1"); ).toBe("/new?serverId=server-1&dir=%2Frepo&name=Project&projectId=project-1");
}); });
it("redirects to the new workspace route instead of another workspace", () => { it("redirects to the new workspace route instead of another workspace", () => {
@@ -78,7 +78,7 @@ describe("buildWorkspaceArchiveRedirectRoute", () => {
archivedWorkspaceId: "/notes", archivedWorkspaceId: "/notes",
workspaces, workspaces,
}), }),
).toBe("/h/server-1/new?dir=%2Fnotes&name=Project&projectId=notes"); ).toBe("/new?serverId=server-1&dir=%2Fnotes&name=Project&projectId=notes");
}); });
}); });
@@ -136,6 +136,6 @@ describe("redirectIfArchivingActiveWorkspace", () => {
), ),
).toBe(true); ).toBe(true);
expect(routes).toEqual(["/h/server-1/new?dir=%2Frepo&name=Project&projectId=project-1"]); expect(routes).toEqual(["/new?serverId=server-1&dir=%2Frepo&name=Project&projectId=project-1"]);
}); });
}); });
@@ -1,6 +1,6 @@
import type { Href } from "expo-router"; import type { Href } from "expo-router";
import type { WorkspaceDescriptor } from "@/stores/session-store"; import type { WorkspaceDescriptor } from "@/stores/session-store";
import { buildHostNewWorkspaceRoute, buildHostRootRoute } from "@/utils/host-routes"; import { buildHostRootRoute, buildNewWorkspaceRoute } from "@/utils/host-routes";
import { resolveWorkspaceRouteId } from "@/utils/workspace-identity"; import { resolveWorkspaceRouteId } from "@/utils/workspace-identity";
export function buildWorkspaceArchiveRedirectRoute(input: { export function buildWorkspaceArchiveRedirectRoute(input: {
@@ -23,7 +23,9 @@ export function buildWorkspaceArchiveRedirectRoute(input: {
return buildHostRootRoute(input.serverId); return buildHostRootRoute(input.serverId);
} }
return buildHostNewWorkspaceRoute(input.serverId, sourceDirectory, { return buildNewWorkspaceRoute({
serverId: input.serverId,
sourceDirectory,
displayName: archivedWorkspace.projectDisplayName, displayName: archivedWorkspace.projectDisplayName,
projectId: archivedWorkspace.projectId, projectId: archivedWorkspace.projectId,
}); });