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
@@ -1,6 +1,6 @@
import { describe, expect, test } from "vitest";
import type { ProjectDescriptor, WorkspaceDescriptor } from "@/stores/session-store";
import { buildWorkspaceStructureProjects } from "./workspace-structure";
import { buildWorkspaceStructureProjects, createProjectViewKey } from "./workspace-structure";
function project(input: {
id: string;
@@ -57,6 +57,7 @@ describe("buildWorkspaceStructureProjects", () => {
expect(result).toHaveLength(1);
expect(result[0]).toMatchObject({
viewKey: key,
projectKey: key,
hosts: [
{ serverId: "host-a", projectId: "prj_a" },
@@ -85,6 +86,7 @@ describe("buildWorkspaceStructureProjects", () => {
});
expect(result).toHaveLength(2);
expect(result.map((item) => item.projectKey)).toEqual([key, key]);
expect(result.map((item) => item.hosts[0]?.projectId).sort()).toEqual(["prj_one", "prj_two"]);
expect(result.map((item) => item.workspaceKeys[0]).sort()).toEqual([
"host-a:ws-one",
@@ -112,7 +114,7 @@ describe("buildWorkspaceStructureProjects", () => {
const identitiesByProjectId = (projects: ReturnType<typeof build>) => {
const identities: Record<string, string> = {};
for (const group of projects) {
for (const host of group.hosts) identities[host.projectId] = group.projectKey;
for (const host of group.hosts) identities[host.projectId] = group.viewKey;
}
return identities;
};
@@ -142,6 +144,33 @@ describe("buildWorkspaceStructureProjects", () => {
});
expect(result).toHaveLength(2);
expect(new Set(result.map((item) => item.projectKey)).size).toBe(2);
expect(result.map((item) => item.projectKey)).toEqual([null, null]);
expect(new Set(result.map((item) => item.viewKey)).size).toBe(2);
});
test("keeps opaque project keys separate from placement view keys", () => {
const placementShapedKey = createProjectViewKey({
kind: "placement",
serverId: "host-a",
projectId: "prj_b",
});
const result = buildWorkspaceStructureProjects({
sessions: [
{
serverId: "host-a",
projects: [
project({ id: "prj_a", key: placementShapedKey, root: "/repos/a" }),
project({ id: "prj_b", key: null, root: "/repos/b" }),
],
workspaces: [],
},
],
});
expect(result).toHaveLength(2);
expect(new Set(result.map((item) => item.viewKey)).size).toBe(2);
expect(result.find((item) => item.projectKey === placementShapedKey)?.viewKey).toBe(
placementShapedKey,
);
});
});