diff --git a/src/renderer/src/components/sidebar/worktree-list/rows/project-header-host-context.test.ts b/src/renderer/src/components/sidebar/worktree-list/rows/project-header-host-context.test.ts index 466452a7614..19d1d4dc835 100644 --- a/src/renderer/src/components/sidebar/worktree-list/rows/project-header-host-context.test.ts +++ b/src/renderer/src/components/sidebar/worktree-list/rows/project-header-host-context.test.ts @@ -96,6 +96,45 @@ describe('project header host context', () => { expect(getProjectHeaderHostContexts({ rows: [rows[0]!], hosts, repoMap }).size).toBe(0) }) + it('leaves distinct project names unlabelled across machines', () => { + const unique = { ...paired, displayName: 'website' } + expect( + getProjectHeaderHostContexts({ rows: [header(local), header(unique)], hosts, repoMap }).size + ).toBe(0) + }) + + it('labels only the colliding names in a mixed sidebar', () => { + const unique = { ...local, id: 'website', displayName: 'website' } + const contexts = getProjectHeaderHostContexts({ + rows: [...rows, header(unique)], + hosts, + repoMap: new Map([...repoMap, [unique.id, unique]]) + }) + expect([...contexts.keys()]).toEqual(rows.map((row) => row.key)) + }) + + it('keeps same-name projects quiet when they share a machine', () => { + const sibling = { ...local, id: 'sibling' } + const unique = { ...paired, displayName: 'website' } + expect( + getProjectHeaderHostContexts({ + rows: [header(local), header(sibling), header(unique)], + hosts, + repoMap: new Map([local, sibling, unique].map((value) => [value.id, value])) + }).size + ).toBe(0) + }) + + it('leaves unique folder-backed groups unlabelled across machines', () => { + expect( + getProjectHeaderHostContexts({ + rows: [header(local), { ...folderHeader, label: 'documents' }], + hosts, + repoMap + }).size + ).toBe(0) + }) + it('omits redundant labels under actual enclosing host sections', () => { expect(getProjectHeaderHostContexts({ rows: [hostHeader, ...rows], hosts, repoMap }).size).toBe( 0 @@ -214,7 +253,11 @@ describe('project header host context', () => { it('never labels a multi-host logical project with its representative repo owner', () => { const other = { ...paired, id: 'independent' } - const logical: GroupHeaderRow = { ...header(repo), key: `project:${project.id}` } + const logical: GroupHeaderRow = { + ...header(repo), + key: `project:${project.id}`, + label: project.displayName + } const contexts = getProjectHeaderHostContexts({ rows: [logical, header(other)], hosts, @@ -222,7 +265,7 @@ describe('project header host context', () => { projectGrouping: { projects: [project], projectHostSetups } }) expect(contexts.has(logical.key)).toBe(false) - expect(contexts.get(`repo:${other.id}`)?.hostId).toBe('runtime:home') + expect(contexts.size).toBe(0) }) describe.each([ @@ -249,7 +292,7 @@ describe('project header host context', () => { { name: 'empty local host scope', includeRemote: false, empty: true, hostFiltered: true } ])('$name', ({ includeRemote, empty, hostFiltered }) => { it.each([false, true])('counts only listed owners (collapsed: %s)', (collapsed) => { - const other = { ...local, id: 'independent-local' } + const other = { ...local, id: 'independent-local', displayName: project.displayName } const map = new Map([repo, remoteRepo, other].map((value) => [value.id, value])) const listedRepoIds = [repo.id, ...(includeRemote ? [remoteRepo.id] : []), other.id] const items = [ diff --git a/src/renderer/src/components/sidebar/worktree-list/rows/project-header-host-context.ts b/src/renderer/src/components/sidebar/worktree-list/rows/project-header-host-context.ts index 88f4c26d8ba..8054270dc90 100644 --- a/src/renderer/src/components/sidebar/worktree-list/rows/project-header-host-context.ts +++ b/src/renderer/src/components/sidebar/worktree-list/rows/project-header-host-context.ts @@ -61,7 +61,8 @@ export function getProjectHeaderHostContexts(args: { hostsByProjectKey.set(key, projectHosts) } const candidates = new Map() - const visibleHostIds = new Set() + const sectionLabels = new Map() + const hostsByLabel = new Map>() for (const row of args.rows) { // Host sections already identify every following project header. if (row.type === 'host-header') { @@ -70,6 +71,13 @@ export function getProjectHeaderHostContexts(args: { if (row.type !== 'header' || row.icon.kind !== 'project') { continue } + const label = row.label + if (!label) { + continue + } + sectionLabels.set(row.key, label) + const labelHosts = hostsByLabel.get(label) ?? new Set() + hostsByLabel.set(label, labelHosts) let ownerHostId: ExecutionHostId if (row.repo) { const projectHosts = hostsByProjectKey.get(row.key) @@ -83,7 +91,7 @@ export function getProjectHeaderHostContexts(args: { }) : projectHosts for (const hostId of visibleProjectHosts) { - visibleHostIds.add(hostId) + labelHosts.add(hostId) } continue } @@ -99,7 +107,7 @@ export function getProjectHeaderHostContexts(args: { continue } const hostId = canonicalHostId(ownerHostId) - visibleHostIds.add(hostId) + labelHosts.add(hostId) const host = hostsById.get(hostId) candidates.set(row.key, { hostId, @@ -107,6 +115,8 @@ export function getProjectHeaderHostContexts(args: { detail: host?.detail ?? getHostContextLabel(hostId) }) } - // Keep single-host sidebars quiet, independently of workspace-card preferences. - return visibleHostIds.size > 1 ? candidates : new Map() + // Only colliding names across machines need a host label. + return new Map( + [...candidates].filter(([key]) => (hostsByLabel.get(sectionLabels.get(key)!)?.size ?? 0) > 1) + ) }