From 64a1a6487c7609489badc024553f19aebef90887 Mon Sep 17 00:00:00 2001 From: brennanb2025 Date: Tue, 23 Jun 2026 17:36:32 -0700 Subject: [PATCH] Fix pinned worktree host metadata Co-authored-by: Orca --- .../src/components/sidebar/WorktreeList.tsx | 2 ++ .../components/sidebar/sleep-worktree-flow.ts | 2 ++ .../sidebar/worktree-list-groups.test.ts | 29 +++++++++++++++++++ .../sidebar/worktree-list-groups.ts | 9 +++--- 4 files changed, 37 insertions(+), 5 deletions(-) diff --git a/src/renderer/src/components/sidebar/WorktreeList.tsx b/src/renderer/src/components/sidebar/WorktreeList.tsx index dc0e7ef886f..9ff1700c2a3 100644 --- a/src/renderer/src/components/sidebar/WorktreeList.tsx +++ b/src/renderer/src/components/sidebar/WorktreeList.tsx @@ -922,6 +922,8 @@ function getPreferredWorktreeRows( rows: readonly WorktreeItemRow[], pinnedDisplayPolicy: PinnedWorktreeDisplayPolicy ): WorktreeItemRow[] { + // Why: when pinned rows are duplicated into natural groups, navigation and + // selection should prefer the natural-group copy and only fall back to pinned. if (pinnedDisplayPolicy === 'single-location') { const seen = new Set() return rows.filter((row) => { diff --git a/src/renderer/src/components/sidebar/sleep-worktree-flow.ts b/src/renderer/src/components/sidebar/sleep-worktree-flow.ts index 053fece73a2..9dbff46c695 100644 --- a/src/renderer/src/components/sidebar/sleep-worktree-flow.ts +++ b/src/renderer/src/components/sidebar/sleep-worktree-flow.ts @@ -29,6 +29,8 @@ function getSidebarWorktreeOptions(worktreeId: string): HTMLElement[] { } function isPinnedSidebarWorktreeOption(element: HTMLElement): boolean { + // Why: duplicated pinned rows share a worktree id; the row-key prefix is the + // stable row-scoped signal that distinguishes the pinned copy from natural rows. return element.dataset.worktreeRowKey?.startsWith('pinned:') === true } diff --git a/src/renderer/src/components/sidebar/worktree-list-groups.test.ts b/src/renderer/src/components/sidebar/worktree-list-groups.test.ts index 610716fc3c8..eb75c3107ed 100644 --- a/src/renderer/src/components/sidebar/worktree-list-groups.test.ts +++ b/src/renderer/src/components/sidebar/worktree-list-groups.test.ts @@ -220,6 +220,35 @@ describe('buildRows with pinned worktrees', () => { expect(rows[2]).toMatchObject({ type: 'header', icon: ALL_GROUP_META.icon }) }) + it('uses worktree host ownership for pinned header host counts', () => { + const runtimePinned = { + ...pinned, + hostId: 'runtime:03ef704c-b180-4b10-998d-e28fbd5de9a3' as const + } + + const rows = buildRows( + 'none', + [runtimePinned, unpinned1], + repoMap, + null, + new Set(), + undefined, + undefined, + undefined, + {}, + new Map([ + [runtimePinned.id, runtimePinned], + [unpinned1.id, unpinned1] + ]) + ) + const pinnedHeader = rows[0] + + expect(pinnedHeader).toMatchObject({ type: 'header', key: 'pinned' }) + expect(pinnedHeader.type === 'header' ? pinnedHeader.hostWorktreeCounts : undefined).toEqual( + new Map([['runtime:03ef704c-b180-4b10-998d-e28fbd5de9a3', 1]]) + ) + }) + it('groups all worktrees under All in groupBy none', () => { const rows = buildRows('none', [unpinned1, unpinned2], repoMap, null, new Set()) diff --git a/src/renderer/src/components/sidebar/worktree-list-groups.ts b/src/renderer/src/components/sidebar/worktree-list-groups.ts index 4f8c863ec8a..b5b548e6c44 100644 --- a/src/renderer/src/components/sidebar/worktree-list-groups.ts +++ b/src/renderer/src/components/sidebar/worktree-list-groups.ts @@ -412,11 +412,7 @@ function emitPinnedGroup( const pinnedRepoOrder: string[] = [] const seenPinnedRepoIds = new Set() for (const worktree of pinned) { - const repo = repoMap.get(worktree.repoId) - const hostId = - repo && (repo.connectionId || repo.executionHostId) - ? getRepoExecutionHostId(repo) - : defaultHostId + const hostId = getWorktreeHostId(worktree, repoMap, defaultHostId) hostWorktreeCounts.set(hostId, (hostWorktreeCounts.get(hostId) ?? 0) + 1) const hostIds = hostWorktreeIds.get(hostId) ?? [] hostIds.push(worktree.id) @@ -666,6 +662,9 @@ function getWorktreeHostId( repoMap: Map, defaultHostId: ExecutionHostId ): ExecutionHostId { + if (worktree.hostId) { + return worktree.hostId + } const repo = repoMap.get(worktree.repoId) return repo && (repo.connectionId || repo.executionHostId) ? getRepoExecutionHostId(repo)