feat(sidebar): replace the external-worktrees inbox list with one card (#14341)

The Non-Orca worktrees modal already lists every hidden external worktree
with search, virtualization, and per-row Show. Its filter is a strict
superset of the sidebar inbox's, so the expanded sidebar list was a second,
worse copy that grew to ~900px at 24 worktrees.

The inbox is now a single clickable card stating the count, which opens that
modal. Drops the expand state, nested list, per-row Import, and the
Keep hidden / Import all footer.
This commit is contained in:
Brennan Benson
2026-08-13 14:04:09 -07:00
committed by GitHub
parent 2f0c33757d
commit 162001eef3
9 changed files with 160 additions and 299 deletions
@@ -16,7 +16,15 @@ vi.mock('@/components/ui/tooltip', () => ({
const roots: Root[] = []
async function renderLine(): Promise<HTMLDivElement> {
type RenderOverrides = {
inboxCount?: number
pending?: boolean
error?: string | null
onReview?: () => void
onSuppress?: () => void
}
async function renderLine(overrides: RenderOverrides = {}): Promise<HTMLDivElement> {
const container = document.createElement('div')
document.body.appendChild(container)
const root = createRoot(container)
@@ -26,20 +34,11 @@ async function renderLine(): Promise<HTMLDivElement> {
root.render(
<NewExternalWorktreesInboxLine
repoDisplayName="orca"
inboxWorktrees={[
{
id: 'external-1',
displayName: 'payments-refactor',
branch: 'refs/heads/payments-refactor',
path: '/worktrees/orca/payments-refactor'
}
]}
pending={false}
error={null}
onImportWorktree={vi.fn()}
onKeepHidden={vi.fn()}
onImportAll={vi.fn()}
onSuppress={vi.fn()}
inboxCount={overrides.inboxCount ?? 24}
pending={overrides.pending ?? false}
error={overrides.error ?? null}
onReview={overrides.onReview ?? vi.fn()}
onSuppress={overrides.onSuppress ?? vi.fn()}
/>
)
})
@@ -47,6 +46,10 @@ async function renderLine(): Promise<HTMLDivElement> {
return container
}
function getReviewButton(container: HTMLDivElement): HTMLButtonElement | null {
return container.querySelector<HTMLButtonElement>('button[aria-label^="Review "]')
}
describe('NewExternalWorktreesInboxLine', () => {
beforeEach(() => {
globalThis.IS_REACT_ACT_ENVIRONMENT = true
@@ -60,27 +63,83 @@ describe('NewExternalWorktreesInboxLine', () => {
vi.clearAllMocks()
})
it('keeps suppress as a hover-revealed header icon instead of expanded text action', async () => {
it('states the count without naming any worktree', async () => {
const container = await renderLine()
expect(container.textContent).toContain('24')
expect(container.textContent).toContain('hidden worktrees')
// The modal owns the list; the sidebar must not enumerate paths or names.
expect(container.querySelectorAll('li')).toHaveLength(0)
expect(container.textContent).not.toContain('Import')
})
it('opens review from a single card-wide button', async () => {
const onReview = vi.fn()
const container = await renderLine({ onReview })
const review = getReviewButton(container)
expect(review).not.toBeNull()
expect(review?.getAttribute('aria-label')).toBe('Review 24 hidden worktrees in orca')
await act(async () => {
review?.click()
})
expect(onReview).toHaveBeenCalledTimes(1)
})
it('uses the singular noun for one worktree', async () => {
const container = await renderLine({ inboxCount: 1 })
expect(container.textContent).toContain('hidden worktree')
expect(container.textContent).not.toContain('hidden worktrees')
expect(getReviewButton(container)?.getAttribute('aria-label')).toBe(
'Review 1 hidden worktree in orca'
)
})
it('keeps suppress as a hover-revealed control that does not trigger review', async () => {
const onReview = vi.fn()
const onSuppress = vi.fn()
const container = await renderLine({ onReview, onSuppress })
const suppressButton = container.querySelector<HTMLButtonElement>(
'button[aria-label="Hide external worktrees permanently for orca"]'
)
expect(suppressButton).not.toBeNull()
expect(suppressButton?.className).toContain('can-hover:group-hover:opacity-100')
expect(container.textContent).toContain("Don't show again")
// Nested buttons would make the suppress click ambiguous.
expect(getReviewButton(container)?.contains(suppressButton)).toBe(false)
const expandButton = container.querySelector<HTMLButtonElement>(
'button[aria-label="Expand new externally-created worktrees for orca"]'
)
await act(async () => {
expandButton?.click()
suppressButton?.click()
})
expect(onSuppress).toHaveBeenCalledTimes(1)
expect(onReview).not.toHaveBeenCalled()
})
expect(container.textContent).toContain('payments-refactor')
const textButtons = [...container.querySelectorAll('button')].filter(
(button) => button.textContent === "Don't show again"
)
expect(textButtons).toHaveLength(0)
it('disables both actions while a mutation is pending', async () => {
const container = await renderLine({ pending: true })
expect(getReviewButton(container)?.disabled).toBe(true)
expect(
container.querySelector<HTMLButtonElement>(
'button[aria-label="Hide external worktrees permanently for orca"]'
)?.disabled
).toBe(true)
expect(container.querySelector('section')?.getAttribute('aria-busy')).toBe('true')
})
it('renders nothing when the inbox is empty', async () => {
const container = await renderLine({ inboxCount: 0 })
expect(container.querySelector('section')).toBeNull()
})
it('surfaces the action error as an alert', async () => {
const container = await renderLine({ error: 'Could not import external worktrees. Try again.' })
const alert = container.querySelector('[role="alert"]')
expect(alert?.textContent).toBe('Could not import external worktrees. Try again.')
})
})
@@ -1,37 +1,30 @@
import React, { useState } from 'react'
import React from 'react'
import { ChevronRight, X } from 'lucide-react'
import { Button } from '@/components/ui/button'
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'
import { cn } from '@/lib/utils'
import { translate } from '@/i18n/i18n'
import type { NewExternalWorktreeInboxPreview } from './new-external-worktrees-inbox-candidates'
type NewExternalWorktreesInboxLineProps = {
repoDisplayName: string
inboxWorktrees: readonly NewExternalWorktreeInboxPreview[]
inboxCount: number
pending: boolean
error: string | null
onImportWorktree?: (worktreeId: string) => void
onKeepHidden?: () => void
onImportAll?: () => void
onReview?: () => void
onSuppress?: () => void
className?: string
}
export default function NewExternalWorktreesInboxLine({
repoDisplayName,
inboxWorktrees,
inboxCount,
pending,
error,
onImportWorktree,
onKeepHidden,
onImportAll,
onReview,
onSuppress,
className
}: NewExternalWorktreesInboxLineProps): React.JSX.Element | null {
const [isExpanded, setIsExpanded] = useState(false)
const inboxCount = inboxWorktrees.length
const suppressLabel = translate(
'auto.components.sidebar.NewExternalWorktreesInboxLine.c3e8a1f4b2',
"Don't show again"
@@ -41,6 +34,27 @@ export default function NewExternalWorktreesInboxLine({
'Hide external worktrees permanently for {{value0}}',
{ value0: repoDisplayName }
)
const isSingular = inboxCount === 1
const countLabel = isSingular
? translate(
'auto.components.sidebar.NewExternalWorktreesInboxLine.2a6f31d8c7',
'hidden worktree'
)
: translate(
'auto.components.sidebar.NewExternalWorktreesInboxLine.5b90e4a2f6',
'hidden worktrees'
)
const reviewAriaLabel = isSingular
? translate(
'auto.components.sidebar.NewExternalWorktreesInboxLine.7f18c5b0d3',
'Review {{value0}} hidden worktree in {{value1}}',
{ value0: inboxCount, value1: repoDisplayName }
)
: translate(
'auto.components.sidebar.NewExternalWorktreesInboxLine.4e2b7a9c05',
'Review {{value0}} hidden worktrees in {{value1}}',
{ value0: inboxCount, value1: repoDisplayName }
)
if (inboxCount === 0) {
return null
@@ -51,151 +65,55 @@ export default function NewExternalWorktreesInboxLine({
aria-busy={pending}
className={cn('mx-1 my-0.5 ml-3 text-worktree-sidebar-foreground', className)}
>
<div
className={cn(
'group flex min-h-7 min-w-0 items-center gap-1.5 rounded-md px-1.5 text-[11px] leading-none text-muted-foreground transition-colors',
'hover:bg-worktree-sidebar-accent hover:text-worktree-sidebar-accent-foreground'
)}
>
<Button
<div className="group relative">
<button
type="button"
variant="ghost"
size="icon-xs"
disabled={pending}
aria-expanded={isExpanded}
aria-label={
isExpanded
? translate(
'auto.components.sidebar.NewExternalWorktreesInboxLine.d9f7b2a14c',
'Collapse new externally-created worktrees for {{value0}}',
{ value0: repoDisplayName }
)
: translate(
'auto.components.sidebar.NewExternalWorktreesInboxLine.e2c4a8d91f',
'Expand new externally-created worktrees for {{value0}}',
{ value0: repoDisplayName }
)
}
onClick={() => setIsExpanded((value) => !value)}
className="shrink-0 rounded-[4px] text-muted-foreground hover:bg-worktree-sidebar-accent hover:text-worktree-sidebar-accent-foreground"
>
<ChevronRight
className={cn('size-3 transition-transform', isExpanded && 'rotate-90')}
aria-hidden="true"
/>
</Button>
<span className="min-w-0 flex-1 truncate">
{translate(
'auto.components.sidebar.NewExternalWorktreesInboxLine.7c4e9b2a81',
'New externally-created worktrees'
disabled={pending || !onReview}
aria-label={reviewAriaLabel}
onClick={onReview}
className={cn(
'flex min-h-8 w-full min-w-0 items-center gap-2 rounded-md border border-worktree-sidebar-border px-2 py-1.5',
'text-[11px] leading-none text-muted-foreground transition-colors',
'hover:bg-worktree-sidebar-accent hover:text-worktree-sidebar-accent-foreground',
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-worktree-sidebar-ring',
'disabled:pointer-events-none disabled:opacity-60'
)}
</span>
<span className="relative inline-grid size-6 shrink-0 place-items-center">
<span
>
<span className="inline-flex h-[18px] min-w-[18px] shrink-0 items-center justify-center rounded-full border border-border px-1.5 text-[10px] font-medium leading-none tabular-nums">
{inboxCount}
</span>
<span className="min-w-0 flex-1 truncate text-left">{countLabel}</span>
<ChevronRight
aria-hidden="true"
className={cn(
'inline-flex h-[18px] min-w-[18px] items-center justify-center rounded-full border border-border px-1.5 text-[10px] font-medium leading-none text-muted-foreground transition-opacity',
'size-3 shrink-0',
// Why: the suppress control takes over this slot on hover.
onSuppress &&
'can-hover:group-hover:opacity-0 can-hover:group-focus-within:opacity-0 [@media(hover:none)]:opacity-0'
)}
>
{inboxCount}
</span>
{onSuppress ? (
<Tooltip>
<TooltipTrigger asChild>
<Button
type="button"
variant="ghost"
size="icon-xs"
disabled={pending}
aria-label={suppressAriaLabel}
onClick={onSuppress}
className="absolute inset-0 text-muted-foreground hover:bg-worktree-sidebar-accent hover:text-worktree-sidebar-accent-foreground can-hover:pointer-events-none can-hover:opacity-0 can-hover:group-hover:pointer-events-auto can-hover:group-hover:opacity-100 can-hover:group-focus-within:pointer-events-auto can-hover:group-focus-within:opacity-100"
>
<X className="size-3" aria-hidden="true" />
</Button>
</TooltipTrigger>
<TooltipContent side="top" sideOffset={4}>
{suppressLabel}
</TooltipContent>
</Tooltip>
) : null}
</span>
</div>
{isExpanded ? (
<div className="ml-4 mt-0.5 border-l border-worktree-sidebar-border pb-1 pl-2">
<p className="px-1.5 py-1 text-[10px] leading-4 text-muted-foreground">
{translate(
'auto.components.sidebar.NewExternalWorktreesInboxLine.4d7a1c9e53',
'These worktrees were created outside of Orca.'
)}
</p>
<ul className="grid gap-0.5">
{inboxWorktrees.map((worktree) => (
<li
key={worktree.id ?? worktree.path ?? worktree.displayName}
className="flex min-h-7 min-w-0 items-center gap-2 rounded-md px-1.5 py-1 text-xs hover:bg-worktree-sidebar-accent"
/>
</button>
{onSuppress ? (
<Tooltip>
<TooltipTrigger asChild>
<Button
type="button"
variant="ghost"
size="icon-xs"
disabled={pending}
aria-label={suppressAriaLabel}
onClick={onSuppress}
className="absolute right-1 top-1/2 -translate-y-1/2 text-muted-foreground hover:bg-worktree-sidebar-accent hover:text-worktree-sidebar-accent-foreground can-hover:pointer-events-none can-hover:opacity-0 can-hover:group-hover:pointer-events-auto can-hover:group-hover:opacity-100 can-hover:group-focus-within:pointer-events-auto can-hover:group-focus-within:opacity-100"
>
<div className="min-w-0 flex-1">
<div className="truncate font-medium">{worktree.displayName}</div>
{worktree.path ? (
<div className="truncate font-mono text-[10px] text-muted-foreground">
{worktree.path}
</div>
) : null}
</div>
{onImportWorktree && worktree.id ? (
<Button
type="button"
variant="outline"
size="xs"
disabled={pending}
onClick={() => onImportWorktree(worktree.id!)}
>
{translate(
'auto.components.sidebar.NewExternalWorktreesInboxLine.8b3f2e1d74',
'Import'
)}
</Button>
) : null}
</li>
))}
</ul>
<div className="grid gap-1 px-1.5 pb-1 pt-1">
<div className="flex flex-wrap gap-1.5">
{onKeepHidden ? (
<Button
type="button"
variant="outline"
size="xs"
disabled={pending}
onClick={onKeepHidden}
>
{translate(
'auto.components.sidebar.NewExternalWorktreesInboxLine.1c9e7a4b28',
'Keep hidden'
)}
</Button>
) : null}
{onImportAll ? (
<Button
type="button"
variant="default"
size="xs"
disabled={pending}
onClick={onImportAll}
>
{translate(
'auto.components.sidebar.NewExternalWorktreesInboxLine.6f2d8c1e95',
'Import all'
)}
</Button>
) : null}
</div>
</div>
</div>
) : null}
<X className="size-3" aria-hidden="true" />
</Button>
</TooltipTrigger>
<TooltipContent side="top" sideOffset={4}>
{suppressLabel}
</TooltipContent>
</Tooltip>
) : null}
</div>
{error ? (
<p className="px-1.5 pb-1 pt-0.5 text-[11px] leading-4 text-destructive" role="alert">
@@ -270,8 +270,6 @@ import {
type ImportedWorktreeCardActionState
} from './imported-worktrees-card-actions'
import {
importNewExternalWorktreeInboxPaths,
keepNewExternalWorktreeInboxHidden,
suppressNewExternalWorktreeInbox,
type NewExternalWorktreesInboxActionState
} from './new-external-worktrees-inbox-actions'
@@ -280,10 +278,7 @@ import {
buildImportedWorktreesCardCandidates,
getHiddenImportedWorktrees
} from './imported-worktrees-card-candidates'
import {
buildNewExternalWorktreesInboxCandidates,
toNewExternalWorktreeInboxPreview
} from './new-external-worktrees-inbox-candidates'
import { buildNewExternalWorktreesInboxCandidates } from './new-external-worktrees-inbox-candidates'
import {
WORKTREE_SECTION_HEADER_PADDING_LEFT,
LINEAGE_CHILDREN_INLINE_OFFSET,
@@ -668,9 +663,6 @@ type VirtualizedWorktreeViewportProps = {
handleShowImportedWorktrees: (projectId: string) => void
handleKeepImportedWorktreesHidden: (projectId: string) => void
importedWorktreeCardActionState: ReadonlyMap<string, ImportedWorktreeCardActionState>
handleImportNewExternalWorktree: (projectId: string, worktreeId: string) => void
handleImportAllNewExternalWorktrees: (projectId: string) => void
handleKeepNewExternalWorktreeInboxHidden: (projectId: string) => void
handleOpenSuppressExternalWorktreeInbox: (projectId: string) => void
newExternalWorktreeInboxActionState: ReadonlyMap<string, NewExternalWorktreesInboxActionState>
handleRemoveProject: (repo: Repo) => void
@@ -1335,9 +1327,6 @@ const VirtualizedWorktreeViewport = React.memo(function VirtualizedWorktreeViewp
handleShowImportedWorktrees,
handleKeepImportedWorktreesHidden,
importedWorktreeCardActionState,
handleImportNewExternalWorktree,
handleImportAllNewExternalWorktrees,
handleKeepNewExternalWorktreeInboxHidden,
handleOpenSuppressExternalWorktreeInbox,
newExternalWorktreeInboxActionState,
handleRemoveProject,
@@ -5058,14 +5047,10 @@ const VirtualizedWorktreeViewport = React.memo(function VirtualizedWorktreeViewp
>
<NewExternalWorktreesInboxLine
repoDisplayName={row.repo.displayName}
inboxWorktrees={row.inboxWorktrees.map(toNewExternalWorktreeInboxPreview)}
inboxCount={row.inboxWorktrees.length}
pending={actionState?.pending ?? false}
error={actionState?.error ?? null}
onImportWorktree={(worktreeId) =>
handleImportNewExternalWorktree(row.repo.id, worktreeId)
}
onKeepHidden={() => handleKeepNewExternalWorktreeInboxHidden(row.repo.id)}
onImportAll={() => handleImportAllNewExternalWorktrees(row.repo.id)}
onReview={() => handleOpenWorktreeVisibility(row.repo)}
onSuppress={() => handleOpenSuppressExternalWorktreeInbox(row.repo.id)}
/>
</div>
@@ -6159,52 +6144,6 @@ const WorktreeList = React.memo(function WorktreeList({
[fetchWorktrees, repos, setNewExternalWorktreeInboxState, updateRepo]
)
const handleImportNewExternalWorktree = useCallback(
async (projectId: string, worktreeId: string) => {
const inboxWorktrees = newExternalWorktreesInboxByRepo.get(projectId)?.inboxWorktrees ?? []
const worktree = inboxWorktrees.find((candidate) => candidate.id === worktreeId)
if (!worktree) {
return
}
const args = getNewExternalWorktreeInboxActionArgs(projectId, [worktree.path])
if (!args) {
return
}
await importNewExternalWorktreeInboxPaths(args)
},
[getNewExternalWorktreeInboxActionArgs, newExternalWorktreesInboxByRepo]
)
const handleImportAllNewExternalWorktrees = useCallback(
async (projectId: string) => {
const inboxWorktrees = newExternalWorktreesInboxByRepo.get(projectId)?.inboxWorktrees ?? []
const args = getNewExternalWorktreeInboxActionArgs(
projectId,
inboxWorktrees.map((worktree) => worktree.path)
)
if (!args) {
return
}
await importNewExternalWorktreeInboxPaths(args)
},
[getNewExternalWorktreeInboxActionArgs, newExternalWorktreesInboxByRepo]
)
const handleKeepNewExternalWorktreeInboxHidden = useCallback(
async (projectId: string) => {
const inboxWorktrees = newExternalWorktreesInboxByRepo.get(projectId)?.inboxWorktrees ?? []
const args = getNewExternalWorktreeInboxActionArgs(
projectId,
inboxWorktrees.map((worktree) => worktree.path)
)
if (!args) {
return
}
await keepNewExternalWorktreeInboxHidden(args)
},
[getNewExternalWorktreeInboxActionArgs, newExternalWorktreesInboxByRepo]
)
const handleOpenSuppressExternalWorktreeInbox = useCallback((projectId: string) => {
setSuppressExternalWorktreeInboxRepoId(projectId)
}, [])
@@ -6866,9 +6805,6 @@ const WorktreeList = React.memo(function WorktreeList({
handleShowImportedWorktrees={handleShowImportedWorktrees}
handleKeepImportedWorktreesHidden={handleKeepImportedWorktreesHidden}
importedWorktreeCardActionState={importedWorktreeCardActionState}
handleImportNewExternalWorktree={handleImportNewExternalWorktree}
handleImportAllNewExternalWorktrees={handleImportAllNewExternalWorktrees}
handleKeepNewExternalWorktreeInboxHidden={handleKeepNewExternalWorktreeInboxHidden}
handleOpenSuppressExternalWorktreeInbox={handleOpenSuppressExternalWorktreeInbox}
newExternalWorktreeInboxActionState={newExternalWorktreeInboxActionState}
handleRemoveProject={handleRemoveProject}
@@ -1,9 +1,4 @@
import type {
DetectedWorktree,
DetectedWorktreeListResult,
Repo,
Worktree
} from '../../../../shared/types'
import type { DetectedWorktreeListResult, Repo, Worktree } from '../../../../shared/types'
import { getNewExternalWorktreeInboxWorktrees } from '../../../../shared/external-worktree-inbox'
import { isGitRepoKind } from '../../../../shared/repo-kind'
import type { NewExternalWorktreesInboxCandidate } from './worktree-list-groups'
@@ -39,19 +34,3 @@ export function buildNewExternalWorktreesInboxCandidates(args: {
}
return candidates
}
export type NewExternalWorktreeInboxPreview = Pick<
DetectedWorktree,
'id' | 'displayName' | 'path' | 'branch'
>
export function toNewExternalWorktreeInboxPreview(
worktree: DetectedWorktree
): NewExternalWorktreeInboxPreview {
return {
id: worktree.id,
displayName: worktree.displayName,
path: worktree.path,
branch: worktree.branch
}
}
+4 -7
View File
@@ -5219,14 +5219,11 @@
},
"NewExternalWorktreesInboxLine": {
"9f2d4c8b17": "Hide external worktrees permanently for {{value0}}",
"d9f7b2a14c": "Collapse new externally-created worktrees for {{value0}}",
"e2c4a8d91f": "Expand new externally-created worktrees for {{value0}}",
"7c4e9b2a81": "New externally-created worktrees",
"5e1b8d3f62": "Hide external worktrees permanently",
"4d7a1c9e53": "These worktrees were created outside of Orca.",
"8b3f2e1d74": "Import",
"1c9e7a4b28": "Keep hidden",
"6f2d8c1e95": "Import all",
"2a6f31d8c7": "hidden worktree",
"5b90e4a2f6": "hidden worktrees",
"7f18c5b0d3": "Review {{value0}} hidden worktree in {{value1}}",
"4e2b7a9c05": "Review {{value0}} hidden worktrees in {{value1}}",
"c3e8a1f4b2": "Don't show again"
},
"newExternalWorktreesInboxActions": {
-7
View File
@@ -5039,14 +5039,7 @@
},
"NewExternalWorktreesInboxLine": {
"9f2d4c8b17": "Ocultar worktrees externos permanentemente para {{value0}}",
"d9f7b2a14c": "Contraer worktrees nuevos creados externamente para {{value0}}",
"e2c4a8d91f": "Expandir worktrees nuevos creados externamente para {{value0}}",
"7c4e9b2a81": "Nuevos worktrees creados externamente",
"5e1b8d3f62": "Ocultar worktrees externos permanentemente",
"4d7a1c9e53": "Estos worktrees se crearon fuera de Orca.",
"8b3f2e1d74": "Importar",
"1c9e7a4b28": "Mantener ocultos",
"6f2d8c1e95": "Importar todo",
"c3e8a1f4b2": "No volver a mostrar"
},
"newExternalWorktreesInboxActions": {
-7
View File
@@ -5039,14 +5039,7 @@
},
"NewExternalWorktreesInboxLine": {
"9f2d4c8b17": "{{value0}} の外部ワークツリーを完全に非表示にする",
"d9f7b2a14c": "{{value0}} の外部で作成された新規ワークツリーを折りたたむ",
"e2c4a8d91f": "{{value0}} の外部で作成された新規ワークツリーを展開",
"7c4e9b2a81": "外部で作成された新規ワークツリー",
"5e1b8d3f62": "外部ワークツリーを完全に非表示にする",
"4d7a1c9e53": "これらのワークツリーは Orca の外部で作成されました。",
"8b3f2e1d74": "インポート",
"1c9e7a4b28": "非表示のままにする",
"6f2d8c1e95": "すべてインポート",
"c3e8a1f4b2": "今後表示しない"
},
"newExternalWorktreesInboxActions": {
-7
View File
@@ -5039,14 +5039,7 @@
},
"NewExternalWorktreesInboxLine": {
"9f2d4c8b17": "{{value0}}의 외부 워크트리를 영구적으로 숨기기",
"d9f7b2a14c": "{{value0}}의 외부에서 생성된 새 워크트리 접기",
"e2c4a8d91f": "{{value0}}의 외부에서 생성된 새 워크트리 펼치기",
"7c4e9b2a81": "외부에서 생성된 새 워크트리",
"5e1b8d3f62": "외부 워크트리를 영구적으로 숨기기",
"4d7a1c9e53": "이 워크트리는 Orca 외부에서 생성되었습니다.",
"8b3f2e1d74": "가져오기",
"1c9e7a4b28": "숨긴 상태 유지",
"6f2d8c1e95": "모두 가져오기",
"c3e8a1f4b2": "다시 표시하지 않기"
},
"newExternalWorktreesInboxActions": {
-7
View File
@@ -5051,14 +5051,7 @@
},
"NewExternalWorktreesInboxLine": {
"9f2d4c8b17": "永久隐藏 {{value0}} 的外部工作树",
"d9f7b2a14c": "折叠 {{value0}} 的外部创建的新工作树",
"e2c4a8d91f": "展开 {{value0}} 的外部创建的新工作树",
"7c4e9b2a81": "外部创建的新工作树",
"5e1b8d3f62": "永久隐藏外部工作树",
"4d7a1c9e53": "这些工作树是在 Orca 外部创建的。",
"8b3f2e1d74": "导入",
"1c9e7a4b28": "保持隐藏",
"6f2d8c1e95": "全部导入",
"c3e8a1f4b2": "不再显示"
},
"newExternalWorktreesInboxActions": {