This reverts commit 426f869fa3.
This commit is contained in:
parent
b8944852de
commit
c86539dcd5
|
|
@ -1845,19 +1845,6 @@ describe('Store', () => {
|
|||
expect(updated.comment).toBe('updated')
|
||||
})
|
||||
|
||||
it('setWorktreeMeta persists workspace status without deleting worktree metadata', async () => {
|
||||
const store = await createStore()
|
||||
store.setWorktreeMeta('wt1', { displayName: 'first', workspaceStatus: 'in-progress' })
|
||||
const updated = store.setWorktreeMeta('wt1', { workspaceStatus: 'completed' })
|
||||
store.flush()
|
||||
|
||||
const persisted = readDataFile() as PersistedState
|
||||
expect(updated.displayName).toBe('first')
|
||||
expect(store.getWorktreeMeta('wt1')?.workspaceStatus).toBe('completed')
|
||||
expect(persisted.worktreeMeta['wt1']?.workspaceStatus).toBe('completed')
|
||||
expect(persisted.worktreeMeta['wt1']?.displayName).toBe('first')
|
||||
})
|
||||
|
||||
// ── 9. Settings: get/update ────────────────────────────────────────
|
||||
|
||||
it('updateSettings merges partial updates', async () => {
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@ import type {
|
|||
GlobalSettings,
|
||||
Repo,
|
||||
Worktree,
|
||||
WorktreeCardProperty,
|
||||
WorkspaceStatusDefinition
|
||||
WorktreeCardProperty
|
||||
} from '../../../../shared/types'
|
||||
import type WorktreeCardComponent from './WorktreeCard'
|
||||
import type * as WorkspaceDeleteQuickAction from './workspace-delete-quick-action'
|
||||
|
|
@ -22,17 +21,6 @@ let tabsByWorktree: Record<string, { id: string }[]> = {}
|
|||
let ptyIdsByTabId: Record<string, string[]> = {}
|
||||
let browserTabsByWorktree: Record<string, { id: string }[]> = {}
|
||||
let settings: Partial<GlobalSettings> | null = null
|
||||
let workspaceStatuses: WorkspaceStatusDefinition[] = [
|
||||
{ id: 'completed', label: 'Done', color: 'conductor-done', icon: 'conductor-done' },
|
||||
{ id: 'in-review', label: 'In review', color: 'conductor-review', icon: 'conductor-review' },
|
||||
{
|
||||
id: 'in-progress',
|
||||
label: 'In progress',
|
||||
color: 'conductor-progress',
|
||||
icon: 'conductor-progress'
|
||||
},
|
||||
{ id: 'todo', label: 'Todo', color: 'neutral', icon: 'circle' }
|
||||
]
|
||||
let workspaceDeleteModifierPressed = false
|
||||
let gitConflictOperationByWorktree: Record<string, GitConflictOperation> = {}
|
||||
let WorktreeCard: typeof WorktreeCardComponent
|
||||
|
|
@ -55,7 +43,6 @@ vi.mock('@/store', () => ({
|
|||
ptyIdsByTabId,
|
||||
tabsByWorktree,
|
||||
updateWorktreeMeta,
|
||||
workspaceStatuses,
|
||||
worktreeCardProperties
|
||||
})
|
||||
}))
|
||||
|
|
@ -147,17 +134,6 @@ describe('WorktreeCard quick actions', () => {
|
|||
ptyIdsByTabId = {}
|
||||
browserTabsByWorktree = {}
|
||||
settings = null
|
||||
workspaceStatuses = [
|
||||
{ id: 'completed', label: 'Done', color: 'conductor-done', icon: 'conductor-done' },
|
||||
{ id: 'in-review', label: 'In review', color: 'conductor-review', icon: 'conductor-review' },
|
||||
{
|
||||
id: 'in-progress',
|
||||
label: 'In progress',
|
||||
color: 'conductor-progress',
|
||||
icon: 'conductor-progress'
|
||||
},
|
||||
{ id: 'todo', label: 'Todo', color: 'neutral', icon: 'circle' }
|
||||
]
|
||||
workspaceDeleteModifierPressed = false
|
||||
gitConflictOperationByWorktree = {}
|
||||
})
|
||||
|
|
@ -292,16 +268,15 @@ describe('WorktreeCard quick actions', () => {
|
|||
expect(markup).not.toContain('data-worktree-card-meta-row=""')
|
||||
})
|
||||
|
||||
it('shows mark done as the top-right quick action for an inactive workspace', () => {
|
||||
it('hides delete by default for an inactive workspace', () => {
|
||||
const markup = renderToStaticMarkup(
|
||||
<WorktreeCard worktree={makeWorktree()} repo={makeRepo()} isActive={false} />
|
||||
)
|
||||
|
||||
expect(markup).toContain('aria-label="Mark workspace done"')
|
||||
expect(markup).not.toContain('aria-label="Delete workspace"')
|
||||
})
|
||||
|
||||
it('replaces mark done with delete while Option/Alt is held', () => {
|
||||
it('shows delete as the top-right quick action while Option/Alt is held', () => {
|
||||
workspaceDeleteModifierPressed = true
|
||||
|
||||
const markup = renderToStaticMarkup(
|
||||
|
|
@ -309,27 +284,9 @@ describe('WorktreeCard quick actions', () => {
|
|||
)
|
||||
|
||||
expect(markup).toContain('aria-label="Delete workspace"')
|
||||
expect(markup).not.toContain('aria-label="Mark workspace done"')
|
||||
})
|
||||
|
||||
it('shows mark done as the quick action for inactive folder workspace instances', () => {
|
||||
const markup = renderToStaticMarkup(
|
||||
<WorktreeCard
|
||||
worktree={makeWorktree({
|
||||
id: 'repo-1::/repo::workspace:123e4567-e89b-12d3-a456-426614174000',
|
||||
path: '/repo',
|
||||
isMainWorktree: false
|
||||
})}
|
||||
repo={{ ...makeRepo(), kind: 'folder' }}
|
||||
isActive={false}
|
||||
/>
|
||||
)
|
||||
|
||||
expect(markup).toContain('aria-label="Mark workspace done"')
|
||||
expect(markup).not.toContain('aria-label="Delete workspace"')
|
||||
})
|
||||
|
||||
it('replaces mark done with delete for folder workspace instances while Option/Alt is held', () => {
|
||||
it('shows delete as the quick action for folder workspace instances while Option/Alt is held', () => {
|
||||
workspaceDeleteModifierPressed = true
|
||||
|
||||
const markup = renderToStaticMarkup(
|
||||
|
|
@ -345,7 +302,6 @@ describe('WorktreeCard quick actions', () => {
|
|||
)
|
||||
|
||||
expect(markup).toContain('aria-label="Delete workspace"')
|
||||
expect(markup).not.toContain('aria-label="Mark workspace done"')
|
||||
})
|
||||
|
||||
it('shows delete for a current workspace while Option/Alt is held', () => {
|
||||
|
|
@ -371,10 +327,9 @@ describe('WorktreeCard quick actions', () => {
|
|||
)
|
||||
|
||||
expect(markup).not.toContain('aria-label="Delete workspace"')
|
||||
expect(markup).toContain('aria-label="Mark workspace done"')
|
||||
})
|
||||
|
||||
it('keeps mark done available for a workspace with live activity', () => {
|
||||
it('does not replace sleep with delete for a workspace with live activity', () => {
|
||||
const worktree = makeWorktree()
|
||||
tabsByWorktree = { [worktree.id]: [{ id: 'tab-1' }] }
|
||||
ptyIdsByTabId = { 'tab-1': ['pty-1'] }
|
||||
|
|
@ -385,10 +340,9 @@ describe('WorktreeCard quick actions', () => {
|
|||
|
||||
expect(markup).not.toContain('aria-label="Sleep workspace"')
|
||||
expect(markup).not.toContain('aria-label="Delete workspace"')
|
||||
expect(markup).toContain('aria-label="Mark workspace done"')
|
||||
})
|
||||
|
||||
it('keeps mark done available for an active workspace', () => {
|
||||
it('does not show sleep as the top-right quick action for an active workspace', () => {
|
||||
const worktree = makeWorktree()
|
||||
tabsByWorktree = { [worktree.id]: [{ id: 'tab-1' }] }
|
||||
ptyIdsByTabId = { 'tab-1': ['pty-1'] }
|
||||
|
|
@ -399,21 +353,9 @@ describe('WorktreeCard quick actions', () => {
|
|||
|
||||
expect(markup).not.toContain('aria-label="Sleep workspace"')
|
||||
expect(markup).not.toContain('aria-label="Delete workspace"')
|
||||
expect(markup).toContain('aria-label="Mark workspace done"')
|
||||
})
|
||||
|
||||
it('reverses the quick action for a done workspace', () => {
|
||||
const worktree = makeWorktree({ workspaceStatus: 'completed' })
|
||||
|
||||
const markup = renderToStaticMarkup(
|
||||
<WorktreeCard worktree={worktree} repo={makeRepo()} isActive={false} />
|
||||
)
|
||||
|
||||
expect(markup).toContain('aria-label="Mark workspace in progress"')
|
||||
expect(markup).not.toContain('aria-label="Delete workspace"')
|
||||
})
|
||||
|
||||
it('keeps delete hidden when the workspace is current but not selected in the sidebar', () => {
|
||||
it('does not show delete when the workspace is current but not selected in the sidebar', () => {
|
||||
const worktree = makeWorktree()
|
||||
|
||||
const markup = renderToStaticMarkup(
|
||||
|
|
@ -421,18 +363,6 @@ describe('WorktreeCard quick actions', () => {
|
|||
)
|
||||
|
||||
expect(markup).not.toContain('aria-label="Delete workspace"')
|
||||
expect(markup).toContain('aria-label="Mark workspace done"')
|
||||
})
|
||||
|
||||
it('hides the completion quick action when the Done status is unavailable', () => {
|
||||
workspaceStatuses = [{ id: 'todo', label: 'Todo', color: 'neutral', icon: 'circle' }]
|
||||
|
||||
const markup = renderToStaticMarkup(
|
||||
<WorktreeCard worktree={makeWorktree()} repo={makeRepo()} isActive={false} />
|
||||
)
|
||||
|
||||
expect(markup).not.toContain('aria-label="Mark workspace done"')
|
||||
expect(markup).not.toContain('aria-label="Delete workspace"')
|
||||
})
|
||||
|
||||
it('does not show the rebase operation chip on the card', () => {
|
||||
|
|
|
|||
|
|
@ -8,8 +8,6 @@ import { Tooltip, TooltipTrigger, TooltipContent } from '@/components/ui/tooltip
|
|||
import {
|
||||
AlertTriangle,
|
||||
ChevronDown,
|
||||
CircleCheck,
|
||||
CircleDot,
|
||||
GitMerge,
|
||||
LoaderCircle,
|
||||
Server,
|
||||
|
|
@ -49,7 +47,6 @@ import { installWindowVisibilityInterval, isWindowVisible } from '@/lib/window-v
|
|||
import { isMacAppDataPath } from '@/lib/passive-macos-app-data-access'
|
||||
import { runWorktreeDelete } from './delete-worktree-flow'
|
||||
import { WorktreeTitleInlineRename } from './WorktreeTitleInlineRename'
|
||||
import { getWorktreeCompletionAction } from './worktree-completion-action'
|
||||
import {
|
||||
canShowWorkspaceDeleteQuickAction,
|
||||
useWorkspaceDeleteModifierPressed
|
||||
|
|
@ -121,7 +118,6 @@ const WorktreeCard = React.memo(function WorktreeCard({
|
|||
const openModal = useAppStore((s) => s.openModal)
|
||||
const openTaskPage = useAppStore((s) => s.openTaskPage)
|
||||
const updateWorktreeMeta = useAppStore((s) => s.updateWorktreeMeta)
|
||||
const workspaceStatuses = useAppStore((s) => s.workspaceStatuses)
|
||||
const fetchHostedReviewForBranch = useAppStore((s) => s.fetchHostedReviewForBranch)
|
||||
const settings = useAppStore((s) => s.settings)
|
||||
const fetchIssue = useAppStore((s) => s.fetchIssue)
|
||||
|
|
@ -436,10 +432,6 @@ const WorktreeCard = React.memo(function WorktreeCard({
|
|||
},
|
||||
[worktree.id, worktree.isUnread, updateWorktreeMeta]
|
||||
)
|
||||
// Why: completion is metadata-only, so it is safe on active, main, and SSH
|
||||
// workspaces where a delete hover action would be too risky.
|
||||
const completionAction = getWorktreeCompletionAction([worktree], workspaceStatuses)
|
||||
const completionTargetStatus = completionAction?.targetStatus
|
||||
// Why: delete is destructive, so it only appears while the user is holding
|
||||
// Option/Alt instead of being part of the ordinary hover chrome.
|
||||
const showDeleteQuickAction = canShowWorkspaceDeleteQuickAction({
|
||||
|
|
@ -447,22 +439,15 @@ const WorktreeCard = React.memo(function WorktreeCard({
|
|||
isDeleting,
|
||||
isMainWorktree: worktree.isMainWorktree
|
||||
})
|
||||
const showCompletionQuickAction =
|
||||
completionAction != null && !isDeleting && !showDeleteQuickAction
|
||||
const showWorkspaceQuickAction = showDeleteQuickAction || showCompletionQuickAction
|
||||
const handleWorkspaceQuickAction = useCallback(
|
||||
(event: React.MouseEvent<HTMLButtonElement>) => {
|
||||
event.preventDefault()
|
||||
event.stopPropagation()
|
||||
if (showDeleteQuickAction) {
|
||||
runWorktreeDelete(worktree.id)
|
||||
return
|
||||
}
|
||||
if (completionTargetStatus) {
|
||||
updateWorktreeMeta(worktree.id, { workspaceStatus: completionTargetStatus })
|
||||
}
|
||||
},
|
||||
[completionTargetStatus, showDeleteQuickAction, updateWorktreeMeta, worktree.id]
|
||||
[showDeleteQuickAction, worktree.id]
|
||||
)
|
||||
|
||||
const unreadTooltip = worktree.isUnread ? 'Mark read' : 'Mark unread'
|
||||
|
|
@ -586,7 +571,7 @@ const WorktreeCard = React.memo(function WorktreeCard({
|
|||
const showTitleRowUnread = compactCards && showUnreadQuickAction && !showStatus
|
||||
const showTitleRowPrimary = compactCards && worktree.isMainWorktree && !isFolder
|
||||
const hasMetaRow = hasMetadataBadge || cacheStartedAt != null
|
||||
const showHeaderActions = showTitleRowUnread || showTitleRowPrimary || showWorkspaceQuickAction
|
||||
const showHeaderActions = showTitleRowUnread || showTitleRowPrimary || showDeleteQuickAction
|
||||
const showBranchIdentityHover = !isFolder && branch !== worktree.displayName
|
||||
// Why: sidebar rows need a small surface inset, while their content remains
|
||||
// aligned with the pre-inset layout and the repo header hierarchy.
|
||||
|
|
@ -801,7 +786,7 @@ const WorktreeCard = React.memo(function WorktreeCard({
|
|||
</Tooltip>
|
||||
)}
|
||||
|
||||
{showWorkspaceQuickAction && (
|
||||
{showDeleteQuickAction && (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<button
|
||||
|
|
@ -812,27 +797,15 @@ const WorktreeCard = React.memo(function WorktreeCard({
|
|||
className={cn(
|
||||
'inline-flex size-4 items-center justify-center rounded bg-transparent opacity-0 transition-colors transition-opacity',
|
||||
'group-hover:opacity-100 group-focus-within:opacity-100 focus-visible:opacity-100',
|
||||
showDeleteQuickAction
|
||||
? 'text-muted-foreground hover:bg-destructive/10 hover:text-destructive focus-visible:bg-destructive/10 focus-visible:text-destructive'
|
||||
: 'text-muted-foreground hover:bg-transparent hover:text-foreground focus-visible:bg-transparent focus-visible:text-foreground'
|
||||
'text-muted-foreground hover:bg-destructive/10 hover:text-destructive focus-visible:bg-destructive/10 focus-visible:text-destructive'
|
||||
)}
|
||||
aria-label={
|
||||
showDeleteQuickAction
|
||||
? 'Delete workspace'
|
||||
: (completionAction?.ariaLabel ?? '')
|
||||
}
|
||||
aria-label="Delete workspace"
|
||||
>
|
||||
{showDeleteQuickAction ? (
|
||||
<Trash2 className="size-3.5" />
|
||||
) : completionAction?.kind === 'mark-done' ? (
|
||||
<CircleCheck className="size-3.5" />
|
||||
) : (
|
||||
<CircleDot className="size-3.5" />
|
||||
)}
|
||||
<Trash2 className="size-3.5" />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="right" sideOffset={8}>
|
||||
{showDeleteQuickAction ? 'Delete workspace' : completionAction?.tooltip}
|
||||
Delete workspace
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -17,8 +17,6 @@ import {
|
|||
Copy,
|
||||
Bell,
|
||||
BellOff,
|
||||
CircleCheck,
|
||||
CircleDot,
|
||||
CircleX,
|
||||
Moon,
|
||||
Pencil,
|
||||
|
|
@ -44,7 +42,6 @@ import { getLineageRenderInfo } from './worktree-list-groups'
|
|||
import { getWorkspaceStatus, getWorkspaceStatusVisualMeta } from './workspace-status'
|
||||
import { WorktreeOpenInSubMenu } from './WorktreeOpenInMenu'
|
||||
import { ProjectGroupNameDialog } from './ProjectGroupNameDialog'
|
||||
import { getWorktreeCompletionAction } from './worktree-completion-action'
|
||||
|
||||
type Props = {
|
||||
worktree: Worktree
|
||||
|
|
@ -256,10 +253,6 @@ const WorktreeContextMenu = React.memo(function WorktreeContextMenu({
|
|||
? status
|
||||
: ''
|
||||
}, [activeContextWorktrees, workspaceStatuses])
|
||||
const completionAction = useMemo(
|
||||
() => getWorktreeCompletionAction(activeContextWorktrees, workspaceStatuses),
|
||||
[activeContextWorktrees, workspaceStatuses]
|
||||
)
|
||||
const batchDeleteWorktrees = useMemo(
|
||||
() =>
|
||||
activeContextWorktrees.filter((item) => {
|
||||
|
|
@ -364,28 +357,6 @@ const WorktreeContextMenu = React.memo(function WorktreeContextMenu({
|
|||
[activeContextWorktrees, setMenuOpenState, updateWorktreeMeta, workspaceStatuses]
|
||||
)
|
||||
|
||||
const handleAssignCompletionStatus = useCallback(() => {
|
||||
if (!completionAction) {
|
||||
return
|
||||
}
|
||||
setMenuOpenState(false)
|
||||
// Why: marking done is only board metadata; SSH and local workspaces must
|
||||
// avoid archive hooks, filesystem deletion, or git worktree removal.
|
||||
void Promise.all(
|
||||
activeContextWorktrees.map((item) =>
|
||||
getWorkspaceStatus(item, workspaceStatuses) === completionAction.targetStatus
|
||||
? Promise.resolve()
|
||||
: updateWorktreeMeta(item.id, { workspaceStatus: completionAction.targetStatus })
|
||||
)
|
||||
)
|
||||
}, [
|
||||
activeContextWorktrees,
|
||||
completionAction,
|
||||
setMenuOpenState,
|
||||
updateWorktreeMeta,
|
||||
workspaceStatuses
|
||||
])
|
||||
|
||||
const handleRename = useCallback(() => {
|
||||
openModal('edit-meta', {
|
||||
worktreeId: worktree.id,
|
||||
|
|
@ -615,16 +586,6 @@ const WorktreeContextMenu = React.memo(function WorktreeContextMenu({
|
|||
<DropdownMenuSeparator />
|
||||
</>
|
||||
)}
|
||||
{completionAction && (
|
||||
<DropdownMenuItem onSelect={handleAssignCompletionStatus} disabled={deletingContext}>
|
||||
{completionAction.kind === 'mark-done' ? (
|
||||
<CircleCheck className="size-3.5" />
|
||||
) : (
|
||||
<CircleDot className="size-3.5" />
|
||||
)}
|
||||
{completionAction.label}
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuSub>
|
||||
<DropdownMenuSubTrigger disabled={deletingContext}>
|
||||
<Kanban className="size-3.5" />
|
||||
|
|
|
|||
|
|
@ -1,79 +0,0 @@
|
|||
import { describe, expect, it } from 'vitest'
|
||||
import { cloneDefaultWorkspaceStatuses } from '../../../../shared/workspace-statuses'
|
||||
import type { Worktree } from '../../../../shared/types'
|
||||
import { getWorktreeCompletionAction } from './worktree-completion-action'
|
||||
|
||||
function makeWorktree(workspaceStatus?: string): Pick<Worktree, 'workspaceStatus'> {
|
||||
return { workspaceStatus }
|
||||
}
|
||||
|
||||
describe('getWorktreeCompletionAction', () => {
|
||||
it('targets Done for an active workspace', () => {
|
||||
const action = getWorktreeCompletionAction(
|
||||
[makeWorktree('in-progress')],
|
||||
cloneDefaultWorkspaceStatuses()
|
||||
)
|
||||
|
||||
expect(action).toMatchObject({
|
||||
kind: 'mark-done',
|
||||
targetStatus: 'completed',
|
||||
label: 'Mark Done',
|
||||
ariaLabel: 'Mark workspace done'
|
||||
})
|
||||
})
|
||||
|
||||
it('reverses a done workspace back to In Progress', () => {
|
||||
const action = getWorktreeCompletionAction(
|
||||
[makeWorktree('completed')],
|
||||
cloneDefaultWorkspaceStatuses()
|
||||
)
|
||||
|
||||
expect(action).toMatchObject({
|
||||
kind: 'mark-in-progress',
|
||||
targetStatus: 'in-progress',
|
||||
label: 'Mark In Progress',
|
||||
ariaLabel: 'Mark workspace in progress'
|
||||
})
|
||||
})
|
||||
|
||||
it('marks a mixed multi-selection done and includes the selected count', () => {
|
||||
const action = getWorktreeCompletionAction(
|
||||
[makeWorktree('completed'), makeWorktree('todo')],
|
||||
cloneDefaultWorkspaceStatuses()
|
||||
)
|
||||
|
||||
expect(action).toMatchObject({
|
||||
kind: 'mark-done',
|
||||
targetStatus: 'completed',
|
||||
label: 'Mark 2 Done',
|
||||
ariaLabel: 'Mark selected workspaces done'
|
||||
})
|
||||
})
|
||||
|
||||
it('reverses an all-done multi-selection together', () => {
|
||||
const action = getWorktreeCompletionAction(
|
||||
[makeWorktree('completed'), makeWorktree('completed')],
|
||||
cloneDefaultWorkspaceStatuses()
|
||||
)
|
||||
|
||||
expect(action).toMatchObject({
|
||||
kind: 'mark-in-progress',
|
||||
targetStatus: 'in-progress',
|
||||
label: 'Mark 2 In Progress',
|
||||
ariaLabel: 'Mark selected workspaces in progress'
|
||||
})
|
||||
})
|
||||
|
||||
it('hides when the target status is not part of the current status board', () => {
|
||||
expect(
|
||||
getWorktreeCompletionAction(
|
||||
[makeWorktree('todo')],
|
||||
[{ id: 'todo', label: 'Todo', color: 'neutral', icon: 'circle' }]
|
||||
)
|
||||
).toBeNull()
|
||||
})
|
||||
|
||||
it('hides while workspace statuses are unavailable', () => {
|
||||
expect(getWorktreeCompletionAction([makeWorktree('todo')], undefined)).toBeNull()
|
||||
})
|
||||
})
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
import type { Worktree, WorkspaceStatus, WorkspaceStatusDefinition } from '../../../../shared/types'
|
||||
import {
|
||||
DEFAULT_WORKSPACE_STATUS_ID,
|
||||
DONE_WORKSPACE_STATUS_ID,
|
||||
getWorkspaceStatus,
|
||||
isWorkspaceStatusId
|
||||
} from '../../../../shared/workspace-statuses'
|
||||
|
||||
export type WorktreeCompletionActionKind = 'mark-done' | 'mark-in-progress'
|
||||
|
||||
export type WorktreeCompletionAction = {
|
||||
kind: WorktreeCompletionActionKind
|
||||
targetStatus: WorkspaceStatus
|
||||
label: string
|
||||
tooltip: string
|
||||
ariaLabel: string
|
||||
}
|
||||
|
||||
export function getWorktreeCompletionAction(
|
||||
worktrees: readonly Pick<Worktree, 'workspaceStatus'>[],
|
||||
workspaceStatuses: readonly WorkspaceStatusDefinition[] | undefined
|
||||
): WorktreeCompletionAction | null {
|
||||
if (worktrees.length === 0 || !Array.isArray(workspaceStatuses)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const allDone = worktrees.every(
|
||||
(worktree) => getWorkspaceStatus(worktree, workspaceStatuses) === DONE_WORKSPACE_STATUS_ID
|
||||
)
|
||||
const targetStatus = allDone ? DEFAULT_WORKSPACE_STATUS_ID : DONE_WORKSPACE_STATUS_ID
|
||||
|
||||
if (!isWorkspaceStatusId(targetStatus, workspaceStatuses)) {
|
||||
return null
|
||||
}
|
||||
|
||||
const isMulti = worktrees.length > 1
|
||||
if (allDone) {
|
||||
return {
|
||||
kind: 'mark-in-progress',
|
||||
targetStatus,
|
||||
label: isMulti ? `Mark ${worktrees.length} In Progress` : 'Mark In Progress',
|
||||
tooltip: 'Mark in progress',
|
||||
ariaLabel: isMulti ? 'Mark selected workspaces in progress' : 'Mark workspace in progress'
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
kind: 'mark-done',
|
||||
targetStatus,
|
||||
label: isMulti ? `Mark ${worktrees.length} Done` : 'Mark Done',
|
||||
tooltip: 'Mark done',
|
||||
ariaLabel: isMulti ? 'Mark selected workspaces done' : 'Mark workspace done'
|
||||
}
|
||||
}
|
||||
|
|
@ -1921,26 +1921,6 @@ describe('worktree remote runtime mutations', () => {
|
|||
expect(store.getState().worktreesByRepo.repo1[0]?.comment).toBe('remote note')
|
||||
})
|
||||
|
||||
it('marks a workspace done by persisting workspace status metadata only', async () => {
|
||||
const store = createTestStore()
|
||||
const wt = makeWorktree({
|
||||
id: 'repo1::/path/wt1',
|
||||
repoId: 'repo1',
|
||||
path: '/path/wt1',
|
||||
workspaceStatus: 'in-progress'
|
||||
})
|
||||
store.setState({ worktreesByRepo: { repo1: [wt] } } as Partial<AppState>)
|
||||
|
||||
await store.getState().updateWorktreeMeta(wt.id, { workspaceStatus: 'completed' })
|
||||
|
||||
expect(mockApi.worktrees.updateMeta).toHaveBeenCalledWith({
|
||||
worktreeId: wt.id,
|
||||
updates: { workspaceStatus: 'completed' }
|
||||
})
|
||||
expect(mockApi.worktrees.remove).not.toHaveBeenCalled()
|
||||
expect(store.getState().worktreesByRepo.repo1[0]?.workspaceStatus).toBe('completed')
|
||||
})
|
||||
|
||||
it('resolves and persists a push target when manually linking a GitHub PR', async () => {
|
||||
const store = createTestStore()
|
||||
const pushTarget = { remoteName: 'origin', branchName: 'bot/pr-bug-scan-2504' }
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ type WorkspaceStatusNormalizationOptions = {
|
|||
}
|
||||
|
||||
export const DEFAULT_WORKSPACE_STATUS_ID: WorkspaceStatus = 'in-progress'
|
||||
export const DONE_WORKSPACE_STATUS_ID: WorkspaceStatus = 'completed'
|
||||
export const DEFAULT_WORKSPACE_STATUS_COLOR_ID = 'neutral'
|
||||
export const DEFAULT_WORKSPACE_STATUS_ICON_ID = 'circle-dot'
|
||||
export const WORKSPACE_BOARD_COLUMN_WIDTH_DEFAULT = 308
|
||||
|
|
|
|||
Loading…
Reference in New Issue