Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions src/main/text-generation/source-control-local-process.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,13 +251,14 @@ describe.runIf(process.platform !== 'win32')(
const slowStop = join(folder, 'slow-stop.cjs')
writeFileSync(
slowStop,
`require('node:fs').writeFileSync(${JSON.stringify(pidFile)}, String(process.pid))
process.stdin.resume()
`process.stdin.resume()
process.on('SIGTERM', () => setTimeout(() => {
require('node:fs').writeFileSync(${JSON.stringify(stoppedFile)}, 'stopped')
process.exit(0)
}, 300))
setInterval(() => {}, 60000)
// Publish readiness only after the signal handler is installed.
require('node:fs').writeFileSync(${JSON.stringify(pidFile)}, String(process.pid))
`
)
const answers = join(folder, 'answers.cjs')
Expand Down
2 changes: 1 addition & 1 deletion src/renderer/src/components/sidebar/WorktreeList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ const WorktreeList = React.memo(function WorktreeList({
agentSendTargetWorktreeId
})
const effectiveCollapsedGroups = useEffectiveCollapsedGroups({
hostScopedPinnedGroups:
hostScopedGroups:
filterState.workspaceHostScope !== 'all' || filterState.visibleWorkspaceHostIds !== null,
collapsedGroups,
agentSendTargetWorktreeId,
Expand Down
84 changes: 0 additions & 84 deletions src/renderer/src/components/sidebar/host-pinned-sections.test.ts

This file was deleted.

31 changes: 0 additions & 31 deletions src/renderer/src/components/sidebar/host-pinned-sections.ts

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { expect, it } from 'vitest'
import type { ProjectGroup } from '../../../../shared/project-group-types'
import { makeFolderWorkspace } from '../../store/slices/worktrees-slice-test-fixtures'
import { folderWorkspaceKey } from '../../../../shared/workspace-scope'
import { buildRows } from './worktree-list/grouping/build-rows'
import { addHostSectionRows } from './host-section-rows'
import { scopeHostSectionCollapse } from './host-section-collapse'
import { getFolderWorkspaceRevealGroupKeys } from './worktree-list/navigation/folder-reveal'

const localGroup: ProjectGroup = {
id: 'local-group',
name: 'Local',
parentPath: '/workspace/local',
parentGroupId: null,
createdFrom: 'manual',
tabOrder: 0,
isCollapsed: false,
color: null,
createdAt: 1,
updatedAt: 1
}
const remoteGroup = { ...localGroup, id: 'remote-group', connectionId: 'builder' }
const local = makeFolderWorkspace({ id: 'local-folder', projectGroupId: localGroup.id })
const remote = makeFolderWorkspace({
id: 'remote-folder',
projectGroupId: remoteGroup.id,
connectionId: 'builder'
})

for (const [groupBy, key] of [
['none', 'all'],
['workspace-status', 'workspace-status:in-progress'],
['pr-status', 'pr:in-progress']
] as const) {
const rows = buildRows(
groupBy,
[],
new Map(),
null,
new Set(),
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
undefined,
[localGroup, remoteGroup],
undefined,
undefined,
undefined,
undefined,
undefined,
[local, remote]
)
const sectioned = addHostSectionRows({
rows,
hostOptions: [
{ id: 'local', kind: 'local', label: 'Local', detail: '', health: 'local' },
{ id: 'ssh:builder', kind: 'ssh', label: 'Remote', detail: '', health: 'available' }
],
workspaceHostScope: 'all',
visibleWorkspaceHostIds: ['local', 'ssh:builder'],
defaultHostId: 'local'
})

it(`${groupBy}: collapses folder workspaces only on the owning host`, () => {
const collapsed = scopeHostSectionCollapse({ rows: sectioned, collapsedGroups: new Set([key]) })
expect(
collapsed
.filter((row) => row.type === 'folder-workspace')
.map((row) => row.folderWorkspace.id)
).toEqual([remote.id])
const remoteCollapsed = scopeHostSectionCollapse({
rows: sectioned,
collapsedGroups: new Set([`${key}:host:ssh:builder`])
})
expect(
remoteCollapsed
.filter((row) => row.type === 'folder-workspace')
.map((row) => row.folderWorkspace.id)
).toEqual([local.id])
})

it(`${groupBy}: reveals a remote folder using the rendered host lane key`, () => {
const keys = getFolderWorkspaceRevealGroupKeys(
folderWorkspaceKey(remote.id),
[local, remote],
[localGroup, remoteGroup],
{
groupBy,
defaultHostId: 'local',
hostScopedGroups: true
}
)
expect(keys).toContain(`${key}:host:ssh:builder`)
expect(keys).toContain('host:ssh:builder')
expect(keys).not.toContain(key)
expect(
getFolderWorkspaceRevealGroupKeys(folderWorkspaceKey(remote.id), [remote], [remoteGroup], {
groupBy,
defaultHostId: 'local'
})
).toContain(key)
})
}
102 changes: 102 additions & 0 deletions src/renderer/src/components/sidebar/host-section-collapse.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
import { describe, expect, it } from 'vitest'
import { makeRepo, makeWorktree } from './worktree-list-lineage-card-test-fixtures'
import { buildRows } from './worktree-list/grouping/build-rows'
import { addHostSectionRows } from './host-section-rows'
import { scopeHostSectionCollapse, deferHostSectionCollapse } from './host-section-collapse'

for (const [groupBy, sectionKey, pinned] of [
['none', 'pinned', true],
['none', 'all', false],
['workspace-status', 'workspace-status:in-progress', false],
['pr-status', 'pr:in-progress', false]
] as const) {
for (const hostId of ['ssh:builder', 'runtime:builder'] as const) {
describe(`${sectionKey} sections on ${hostId}`, () => {
const localRepo = makeRepo()
const remoteRepo = { ...localRepo, id: 'remote-repo', executionHostId: hostId }
const repoMap = new Map([
[localRepo.id, localRepo],
[remoteRepo.id, remoteRepo]
])
const local = {
...makeWorktree({
id: 'local',
instanceId: 'local',
displayName: 'Local pin',
branch: 'local',
sortOrder: 0
}),
hostId: 'local' as const,
isPinned: pinned
}
const remote = { ...local, id: 'remote', repoId: remoteRepo.id, hostId }
const rows = buildRows(groupBy, [local, remote], repoMap, null, new Set())
const sectioned = addHostSectionRows({
rows,
hostOptions: [
{ id: 'local', kind: 'local', label: 'Local', detail: '', health: 'local' },
{
id: hostId,
kind: hostId.startsWith('ssh:') ? 'ssh' : 'runtime',
label: 'Remote',
detail: '',
health: 'available'
}
],
workspaceHostScope: 'all',
visibleWorkspaceHostIds: ['local', hostId],
defaultHostId: 'local'
})

it('keeps remote pins visible while local pins are collapsed', () => {
const result = scopeHostSectionCollapse({
rows: sectioned,
collapsedGroups: new Set([sectionKey])
})
expect(result.filter((row) => row.type === 'item').map((row) => row.worktree.id)).toEqual([
'remote'
])
expect(result.filter((row) => row.type === 'header').map((row) => row.collapseKey)).toEqual(
[sectionKey, `${sectionKey}:host:${hostId}`]
)
})

it('keeps local pins visible while remote pins are collapsed', () => {
const result = scopeHostSectionCollapse({
rows: sectioned,
collapsedGroups: new Set([`${sectionKey}:host:${hostId}`])
})
expect(result.filter((row) => row.type === 'item').map((row) => row.worktree.id)).toEqual([
'local'
])
})

it('retains remote collapse ownership when it is the only visible host', () => {
const remoteRows = buildRows(groupBy, [remote], repoMap, null, new Set())
const result = scopeHostSectionCollapse({
rows: remoteRows,
collapsedGroups: new Set([`${sectionKey}:host:${hostId}`])
})
expect(result.filter((row) => row.type === 'item')).toEqual([])
expect(result.filter((row) => row.type === 'header').map((row) => row.collapseKey)).toEqual(
[`${sectionKey}:host:${hostId}`]
)
})
})
}
}

it('defers only lane collapse and retains unrelated project/lineage collapse', () => {
const groups = new Set([
'pinned',
'all',
'pr:in-progress',
'workspace-status:done',
'repo:1',
'lineage:local|parent'
])
expect([...deferHostSectionCollapse(groups)]).toEqual(['repo:1', 'lineage:local|parent'])
expect(groups.size).toBe(6)
const unchanged = new Set(['repo:1'])
expect(deferHostSectionCollapse(unchanged)).toBe(unchanged)
})
Loading
Loading