diff --git a/packages/app/e2e/helpers/sidebar.ts b/packages/app/e2e/helpers/sidebar.ts index c6d0fb3ce..dc06f26cc 100644 --- a/packages/app/e2e/helpers/sidebar.ts +++ b/packages/app/e2e/helpers/sidebar.ts @@ -39,6 +39,13 @@ export async function clickArchiveWorkspaceMenuItem( await archiveItem.click(); } +export async function pinWorkspaceFromSidebar(page: Page, workspaceId: string): Promise { + const serverId = await openWorkspaceSidebarKebab(page, workspaceId); + const pinItem = page.getByTestId(`sidebar-workspace-menu-pin-${serverId}:${workspaceId}`); + await expect(pinItem).toBeVisible({ timeout: 10_000 }); + await pinItem.click(); +} + export async function archiveWorkspaceFromSidebar(page: Page, workspaceId: string): Promise { // A clean workspace archives with no prompt. Managed worktree backing may raise // a browser confirm for unsynced work, so accept it when present. diff --git a/packages/app/e2e/sidebar-workspace.spec.ts b/packages/app/e2e/sidebar-workspace.spec.ts index be68e73a7..51fee85cb 100644 --- a/packages/app/e2e/sidebar-workspace.spec.ts +++ b/packages/app/e2e/sidebar-workspace.spec.ts @@ -6,6 +6,7 @@ import { expectMobileAgentSidebarHidden, expectMobileAgentSidebarVisible, openMobileAgentSidebar, + pinWorkspaceFromSidebar, } from "./helpers/sidebar"; import { seedWorkspace } from "./helpers/seed-client"; import { expectWorkspaceHeader } from "./helpers/workspace-ui"; @@ -168,6 +169,30 @@ test.describe("Mobile sidebar panelState transition", () => { await closeMobileAgentSidebar(page); await expectMobileAgentSidebarHidden(page); }); + + test("keeps a pinned workspace rendered while the retained sidebar is closed", async ({ + page, + }) => { + const workspace = await seedWorkspace({ repoPrefix: "sidebar-retained-pin-" }); + + try { + await gotoAppShell(page); + await openMobileAgentSidebar(page); + await expectMobileAgentSidebarVisible(page); + + const row = page.getByTestId(getWorkspaceRowTestId(workspace.workspaceId)); + await expect(row).toBeVisible({ timeout: 30_000 }); + await pinWorkspaceFromSidebar(page, workspace.workspaceId); + await expect(page.getByTestId("sidebar-pinned-section")).toBeVisible(); + + await closeMobileAgentSidebar(page); + await expectMobileAgentSidebarHidden(page); + + await expect(row).toHaveCount(1); + } finally { + await workspace.cleanup(); + } + }); }); test.describe("Half-screen desktop layout", () => { diff --git a/packages/app/src/hooks/use-sidebar-workspace-entries.ts b/packages/app/src/hooks/use-sidebar-workspace-entries.ts index 5f0ccc407..c2eb65ac7 100644 --- a/packages/app/src/hooks/use-sidebar-workspace-entries.ts +++ b/packages/app/src/hooks/use-sidebar-workspace-entries.ts @@ -38,7 +38,10 @@ export function useSidebarWorkspaceEntries( // subscription to structurally shared indexes, never one session-store // subscription per mounted row. return useMemo(() => { - if (!enabled || placements.length === 0 || sessions.length === 0) { + if (!enabled) { + return previousEntriesRef.current; + } + if (placements.length === 0 || sessions.length === 0) { previousEntriesRef.current = EMPTY_ENTRIES; return EMPTY_ENTRIES; }