Keep selected projects when switching hosts (#2700)

* fix(app): preserve project selection across hosts

Rendered project identity and persisted equivalence were conflated after duplicate-project handling. Keep them separate so host changes resolve selectable equivalents deterministically without resetting legacy sidebar identity.

* fix(app): restore selected clone after host round trip

Keep manual origin and current destination placements separately so switching back restores the chosen checkout while archive gaps preserve the active host selection.

* fix(app): key status icons by project view

* fix(app): preserve origin during archive gaps
This commit is contained in:
Mohamed Boudra
2026-07-31 15:42:08 +02:00
committed by GitHub
parent 87ef631ac4
commit 99eca19a76
56 changed files with 1234 additions and 428 deletions
+11 -6
View File
@@ -3,6 +3,7 @@ import type { DaemonClient as InternalDaemonClient } from "@getpaseo/client/inte
import { decodeWorkspaceIdFromPathSegment } from "@/utils/host-routes";
import { connectDaemonClient } from "./daemon-client-loader";
import { daemonWsRoutePattern } from "./daemon-port";
import { projectEquivalenceViewKey } from "./project-view-key";
import { expectWorkspaceHeader } from "./workspace-ui";
type NewWorkspaceDaemonClient = Pick<
@@ -167,11 +168,12 @@ export async function openNewWorkspaceComposer(
page: Page,
input: { projectKey: string; projectDisplayName: string },
): Promise<void> {
const projectRow = page.getByTestId(`sidebar-project-row-${input.projectKey}`).first();
const projectViewKey = projectEquivalenceViewKey(input.projectKey);
const projectRow = page.getByTestId(`sidebar-project-row-${projectViewKey}`).first();
await expect(projectRow).toBeVisible({ timeout: 30_000 });
await projectRow.hover();
const button = page.getByTestId(`sidebar-project-new-worktree-${input.projectKey}`).first();
const button = page.getByTestId(`sidebar-project-new-worktree-${projectViewKey}`).first();
await expect(button).toBeVisible({ timeout: 30_000 });
await button.click();
@@ -216,8 +218,10 @@ export async function expectNewWorkspaceDraft(page: Page, draft: string): Promis
}
export async function selectNewWorkspaceHost(page: Page, hostLabel: string): Promise<void> {
await page.getByTestId("host-picker-trigger").click();
await page.getByText(hostLabel, { exact: true }).click();
const trigger = page.getByTestId("host-picker-trigger");
await trigger.click();
await page.getByRole("button", { name: hostLabel, exact: true }).click();
await expect(trigger).toContainText(hostLabel);
}
export async function submitNewWorkspacePrompt(
@@ -244,13 +248,14 @@ export async function clickNewWorkspaceButton(
export async function selectNewWorkspaceProject(
page: Page,
input: { projectKey: string; projectDisplayName: string },
input: { projectKey: string; projectDisplayName: string; projectViewKey?: string },
): Promise<void> {
const trigger = page.getByTestId("new-workspace-project-picker-trigger");
await expect(trigger).toBeVisible({ timeout: 30_000 });
await trigger.click();
const option = page.getByTestId(`new-workspace-project-picker-option-${input.projectKey}`);
const projectViewKey = input.projectViewKey ?? projectEquivalenceViewKey(input.projectKey);
const option = page.getByTestId(`new-workspace-project-picker-option-${projectViewKey}`);
await expect(option).toBeVisible({ timeout: 30_000 });
await option.click();
@@ -0,0 +1,7 @@
export function projectEquivalenceViewKey(projectKey: string): string {
return projectKey;
}
export function projectPlacementViewKey(serverId: string, projectId: string): string {
return JSON.stringify([serverId, projectId]);
}