diff --git a/src/renderer/src/components/worktree-creation/VmBuildSheet.tsx b/src/renderer/src/components/worktree-creation/VmBuildSheet.tsx index 0748c02cdac..0ef94ca7d64 100644 --- a/src/renderer/src/components/worktree-creation/VmBuildSheet.tsx +++ b/src/renderer/src/components/worktree-creation/VmBuildSheet.tsx @@ -293,11 +293,7 @@ export function VmBuildSheet({ : null } timing={timings[step.id] ?? null} - stopped={failure !== null} t={t} - waitedMs={ - steps.find((candidate) => candidate.id === 'provision')?.elapsedMs ?? 0 - } /> ))} {/* Orca Cloud: agent setup happens after create (the setup page and the workspace's diff --git a/src/renderer/src/components/worktree-creation/VmBuildStepRow.test.tsx b/src/renderer/src/components/worktree-creation/VmBuildStepRow.test.tsx index 1d1cf2fd03f..97d5cf80dfa 100644 --- a/src/renderer/src/components/worktree-creation/VmBuildStepRow.test.tsx +++ b/src/renderer/src/components/worktree-creation/VmBuildStepRow.test.tsx @@ -1,15 +1,12 @@ // @vitest-environment happy-dom -import { act, cleanup, render } from '@testing-library/react' -import { afterEach, expect, it, vi } from 'vitest' +import { cleanup, render } from '@testing-library/react' +import { afterEach, expect, it } from 'vitest' import type { Translate } from '@/i18n/use-translate' import { VmBuildStepRow } from './VmBuildStepRow' import type { VmBuildStep } from './vm-build-steps' -afterEach(() => { - cleanup() - vi.useRealTimers() -}) +afterEach(cleanup) const t: Translate = (_key, fallback) => fallback const pendingClone = (cloneMode?: 'clone' | 'update'): VmBuildStep => ({ @@ -22,50 +19,19 @@ const pendingClone = (cloneMode?: 'clone' | 'update'): VmBuildStep => ({ ...(cloneMode ? { cloneMode } : {}) }) -it('names the repository row once: no label guessed, a placeholder only after 150 ms (PC-4)', () => { - vi.useFakeTimers() - const row = (step: VmBuildStep) => ( -
    - -
- ) - const { container, rerender } = render(row(pendingClone())) - // Accessible name only; no visible label, no placeholder yet (the plan usually answers by now). - expect(container.textContent).toContain('Repository') - expect(container.textContent).not.toContain('Clone repository') - expect(container.querySelector('[data-slot="skeleton"]')).toBeNull() - act(() => { - vi.advanceTimersByTime(160) - }) - expect(container.querySelector('[data-slot="skeleton"]')).not.toBeNull() - // The plan answers: the one label, never changed afterwards. - rerender(row(pendingClone('update'))) - expect(container.querySelector('[data-slot="skeleton"]')).toBeNull() - expect(container.textContent).toContain('Update repository') -}) +const row = (step: VmBuildStep) => ( +
    + +
+) -it('names an undecided repository step neutrally once the start has failed (N-12)', () => { - const { container } = render( -
    - -
- ) +it('reads "Get repository" until the boot plan answers, then switches in place, no placeholder bar', () => { + const { container, rerender } = render(row(pendingClone())) expect(container.textContent).toContain('Get repository') expect(container.querySelector('[data-slot="skeleton"]')).toBeNull() -}) - -it('shows the placeholder at once when the create has already waited past 150 ms (no blank row)', () => { - const { container } = render( -
    - -
- ) - expect(container.querySelector('[data-slot="skeleton"]')).not.toBeNull() + rerender(row(pendingClone('update'))) + expect(container.textContent).toContain('Update repository') + expect(container.textContent).not.toContain('Get repository') + rerender(row(pendingClone('clone'))) + expect(container.textContent).toContain('Clone repository') }) diff --git a/src/renderer/src/components/worktree-creation/VmBuildStepRow.tsx b/src/renderer/src/components/worktree-creation/VmBuildStepRow.tsx index 5379ee88272..f11eba5fa55 100644 --- a/src/renderer/src/components/worktree-creation/VmBuildStepRow.tsx +++ b/src/renderer/src/components/worktree-creation/VmBuildStepRow.tsx @@ -3,7 +3,6 @@ import { CircleSlash } from 'lucide-react' import { cn } from '@/lib/utils' import type { Translate } from '@/i18n/use-translate' import { Progress } from '@/components/ui/progress' -import { Skeleton } from '@/components/ui/skeleton' import { StepIcon } from './WorktreeSetupSteps' import type { VmBuildStep } from './vm-build-steps' import { @@ -14,47 +13,21 @@ import { import { formatElapsedTime } from './worktree-creation-clock' import { stepLabel, stepStateLabel } from './vm-build-sheet-copy' -/** The boot plan usually answers within this; only a slower answer shows the placeholder. */ -const PLACEHOLDER_DELAY_MS = 150 - -/** True once `active` has held for `delayMs` (the row's mode is unknown only once per create). */ -function useDelayedFlag(active: boolean, delayMs: number): boolean { - const [due, setDue] = React.useState(false) - React.useEffect(() => { - if (!active) { - return - } - const timer = window.setTimeout(() => setDue(true), delayMs) - return () => window.clearTimeout(timer) - }, [active, delayMs]) - return active && due -} - /** One timed row of the build checklist; the failure card, not the row, carries the red. */ export function VmBuildStepRow({ step, cloud, detail, timing, - stopped = false, - t, - waitedMs = 0 + t }: { step: VmBuildStep cloud: boolean - /** The build stopped (a failure card shows): an undecided clone/update never will be decided. */ - stopped?: boolean /** The step's measured duration, when there is one. */ timing: VmBuildStepTiming | null /** What the running step is doing right now, in plain words. */ detail: string | null t: Translate - /** - * How long the create has been running (the sandbox step's time). Past PLACEHOLDER_DELAY_MS the - * boot plan had its chance to answer, so a still-unknown row shows its placeholder at once: no - * blank row, also not in a fixture or a remount. - */ - waitedMs?: number }): React.JSX.Element { const labelId = React.useId() // A live percent already says how far along the step is. @@ -65,11 +38,9 @@ export function VmBuildStepRow({ timing?.p95Ms ?? (cloud && step.id === 'provision' ? SLOW_WITHOUT_MEASURE_MS : null) const slow = measuring && slowAfterMs !== null && step.elapsedMs !== null && step.elapsedMs > slowAfterMs + // Clone or update is known once the boot plan answers; until then (or for good, if the start + // failed first) the row reads "Get repository" with its empty circle, then switches in place. const undecided = cloud && step.id === 'clone' && step.state === 'pending' && !step.cloneMode - // N-12: once the start has failed, the mode stays unknown for good: name the step neutrally. - const modeUnknown = undecided && !stopped - const placeholderDelayed = useDelayedFlag(modeUnknown, PLACEHOLDER_DELAY_MS) - const placeholderDue = modeUnknown && (placeholderDelayed || waitedMs >= PLACEHOLDER_DELAY_MS) return (
  • - {modeUnknown ? ( - // Clone or update is decided in a moment (boot plan): a placeholder, never a guessed label. - // Usually decided within ~150 ms, so the placeholder shows only after that (no blink). - <> - - {t( - 'auto.components.worktree.creation.VmBuildSheet.repositoryPending', - 'Repository' - )} - - {placeholderDue ? ( - - ) : ( - - {'\u00a0'} - - )} - - ) : undecided ? ( - t('auto.components.worktree.creation.VmBuildSheet.getRepository', 'Get repository') - ) : ( - stepLabel(step, cloud, t) - )} + {undecided + ? t('auto.components.worktree.creation.VmBuildSheet.getRepository', 'Get repository') + : stepLabel(step, cloud, t)} {step.skipped diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json index a2f3a926a1d..77ef1aa3a4f 100644 --- a/src/renderer/src/i18n/locales/en.json +++ b/src/renderer/src/i18n/locales/en.json @@ -4258,7 +4258,6 @@ "cloneCounting": "Counting objects…", "updateCloud": "Update repository", "updateFetching": "Fetching changes…", - "repositoryPending": "Repository", "getRepository": "Get repository" }, "VmBuildFailureCard": {