From 18ca6919770a100ee38e81f8652bb1419fb3be76 Mon Sep 17 00:00:00 2001 From: Brennan Benson <79079362+brennanb2025@users.noreply.github.com> Date: Thu, 11 Jun 2026 19:45:01 -0700 Subject: [PATCH] Update create project dialog design (#5231) Co-authored-by: Orca --- .../sidebar/AddRepoCreateStep.test.tsx | 37 +- .../components/sidebar/AddRepoCreateStep.tsx | 349 ++++++------------ .../src/components/sidebar/AddRepoDialog.tsx | 14 +- .../sidebar/AddRepoDialogStepContent.test.tsx | 7 +- .../sidebar/AddRepoDialogStepContent.tsx | 6 - .../sidebar/AddRepoStartSteps.test.tsx | 10 +- .../sidebar/add-repo-local-start-actions.ts | 8 +- .../sidebar/useCreateProjectDefaults.test.ts | 25 +- .../sidebar/useCreateProjectDefaults.ts | 25 +- .../useCreateRepo.default-checkout.test.ts | 8 +- .../src/components/sidebar/useCreateRepo.ts | 11 +- src/renderer/src/i18n/locales/en.json | 14 +- src/renderer/src/i18n/locales/es.json | 14 +- src/renderer/src/i18n/locales/ja.json | 14 +- src/renderer/src/i18n/locales/ko.json | 14 +- src/renderer/src/i18n/locales/zh.json | 14 +- 16 files changed, 219 insertions(+), 351 deletions(-) diff --git a/src/renderer/src/components/sidebar/AddRepoCreateStep.test.tsx b/src/renderer/src/components/sidebar/AddRepoCreateStep.test.tsx index 5aa81139153..4b592ad1a46 100644 --- a/src/renderer/src/components/sidebar/AddRepoCreateStep.test.tsx +++ b/src/renderer/src/components/sidebar/AddRepoCreateStep.test.tsx @@ -3,17 +3,15 @@ import { describe, expect, it, vi } from 'vitest' import { Dialog } from '@/components/ui/dialog' import { TooltipProvider } from '@/components/ui/tooltip' import { CreateStep } from './AddRepoCreateStep' -import type { GitAvailability, RepoKind } from './create-project-defaults' +import type { GitAvailability } from './create-project-defaults' function renderCreateStep({ createName = '', - createKind = 'git', gitAvailability = 'available', createParent = '/Users/alice/orca/projects', parentDefaultPending = false }: { createName?: string - createKind?: RepoKind gitAvailability?: GitAvailability createParent?: string parentDefaultPending?: boolean @@ -24,7 +22,6 @@ function renderCreateStep({ @@ -43,24 +39,31 @@ function renderCreateStep({ } describe('CreateStep', () => { - it('renders the name-first create UI with advanced controls collapsed', () => { + it('renders the conductor-style Git project form without templates or kind selection', () => { const html = renderCreateStep() - expect(html).toContain('Create a new project') - expect(html).toContain('Name') - expect(html).toContain('Git repository in ~/orca/projects') - // The summary card itself is the collapsed disclosure for the uncommon settings. - expect(html).toContain('aria-expanded="false"') + expect(html).toContain('Create project') + expect(html).toContain('Project name') + expect(html).not.toContain('Git repo:') + expect(html).not.toContain('>project-name') + expect(html).toContain('Parent folder') + expect(html).toContain('Browse') + expect(html).not.toContain('Template') expect(html).not.toContain('Project kind') - expect(html).not.toContain('Location') - expect(html).not.toContain('aria-label="Browse server filesystem"') }) - it('shows the Git fallback explanation in the collapsed summary', () => { - const html = renderCreateStep({ createKind: 'folder', gitAvailability: 'unavailable' }) + it('shows the repo name in the helper only after a project name is entered', () => { + const html = renderCreateStep({ createName: 'demo-project' }) - expect(html).toContain('Folder in ~/orca/projects') - expect(html).toContain('Git isn't installed, so a plain folder is the default.') + expect(html).toContain('Git repo:') + expect(html).toContain('demo-project') + }) + + it('requires Git instead of falling back to folder creation', () => { + const html = renderCreateStep({ gitAvailability: 'unavailable' }) + + expect(html).toContain('Git is required to create a project.') + expect(html).toContain('disabled=""') }) it('disables create while an auto-filled parent belongs to a previous target', () => { diff --git a/src/renderer/src/components/sidebar/AddRepoCreateStep.tsx b/src/renderer/src/components/sidebar/AddRepoCreateStep.tsx index 863b1121238..8fd3e75d196 100644 --- a/src/renderer/src/components/sidebar/AddRepoCreateStep.tsx +++ b/src/renderer/src/components/sidebar/AddRepoCreateStep.tsx @@ -1,30 +1,17 @@ // Step for AddRepoDialog (orca#763), split out so create-project state stays scoped. -import React, { useCallback, useMemo, useRef, useState } from 'react' -import { ChevronDown, Folder, GitBranch, Loader2 } from 'lucide-react' +import React, { useMemo, useState } from 'react' +import { CornerDownLeft, FolderOpen, Loader2 } from 'lucide-react' import { DialogHeader, DialogTitle, DialogDescription } from '@/components/ui/dialog' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' -import { cn } from '@/lib/utils' -import { - CreateProjectLocationField, - CreateProjectParentBrowser -} from './CreateProjectLocationField' +import { CreateProjectParentBrowser } from './CreateProjectLocationField' import { translate } from '@/i18n/i18n' -import { - formatCreateProjectParentSummary, - joinCreateProjectPath, - type GitAvailability, - type RepoKind -} from './create-project-defaults' - -// ── UI helpers ─────────────────────────────────────────────────────── - -const CREATE_PROJECT_NAME_PLACEHOLDER = 'project-name' +import { getScreenSubmitModifierLabel } from '@/lib/screen-submit-shortcut' +import { formatCreateProjectParentSummary, type GitAvailability } from './create-project-defaults' type CreateStepProps = { createName: string createParent: string - createKind: RepoKind createError: string | null isCreating: boolean defaultParent?: string @@ -35,7 +22,6 @@ type CreateStepProps = { runtimeEnvironmentId?: string | null onNameChange: (value: string) => void onParentChange: (value: string) => void - onKindChange: (kind: RepoKind) => void onPickParent: () => void onCreate: () => void } @@ -43,7 +29,6 @@ type CreateStepProps = { export function CreateStep({ createName, createParent, - createKind, createError, isCreating, defaultParent = '', @@ -54,52 +39,16 @@ export function CreateStep({ runtimeEnvironmentId, onNameChange, onParentChange, - onKindChange, onPickParent, onCreate }: CreateStepProps): React.JSX.Element { - const radioGroupRef = useRef(null) - const radioFocusFrameRef = useRef(null) const [browsingParent, setBrowsingParent] = useState(false) - const [advancedOpen, setAdvancedOpen] = useState(false) - - const cancelRadioFocusFrame = useCallback((): void => { - if (radioFocusFrameRef.current === null) { - return - } - cancelAnimationFrame(radioFocusFrameRef.current) - radioFocusFrameRef.current = null - }, []) - - const setRadioGroupNode = useCallback( - (node: HTMLDivElement | null): void => { - // Why: the queued arrow-key focus is only valid while this radiogroup is mounted. - if (!node) { - cancelRadioFocusFrame() - } - radioGroupRef.current = node - }, - [cancelRadioFocusFrame] - ) - - // Arrow keys cycle selection within the radiogroup (WAI-ARIA radio pattern). - const cycleKind = useCallback(() => { - const next = createKind === 'git' ? 'folder' : 'git' - onKindChange(next) - cancelRadioFocusFrame() - radioFocusFrameRef.current = requestAnimationFrame(() => { - radioFocusFrameRef.current = null - const nextEl = radioGroupRef.current?.querySelector( - `[data-kind="${next}"]` - ) - nextEl?.focus() - }) - }, [cancelRadioFocusFrame, createKind, onKindChange]) const canSubmit = createName.trim().length > 0 && createParent.trim().length > 0 && gitAvailability !== 'checking' && + gitAvailability !== 'unavailable' && !parentDefaultPending && !isCreating const missingLocationLabel = translate( @@ -111,7 +60,7 @@ export function CreateStep({ 'server folder not selected' ) - const summaryParent = useMemo( + const parentSummary = useMemo( () => formatCreateProjectParentSummary({ parent: createParent, @@ -128,14 +77,8 @@ export function CreateStep({ runtimeEnvironmentId ] ) - const targetPathPreview = useMemo(() => { - const name = createName.trim() || CREATE_PROJECT_NAME_PLACEHOLDER - return createParent.trim() ? joinCreateProjectPath(createParent, name) : '' - }, [createName, createParent]) - const kindLabel = - createKind === 'git' - ? translate('auto.components.sidebar.AddRepoCreateStep.11fd2a7db8', 'Git repository') - : translate('auto.components.sidebar.AddRepoCreateStep.038729c107', 'Folder') + const repoNamePreview = createName.trim() + const submitShortcutModifierLabel = getScreenSubmitModifierLabel() const showGitFallback = gitAvailability === 'unavailable' const showGitChecking = gitAvailability === 'checking' const showRuntimeMissingParent = @@ -157,14 +100,14 @@ export function CreateStep({ {translate( - 'auto.components.sidebar.AddRepoCreateStep.c7b9f94456', - 'Create a new project' + 'auto.components.sidebar.AddRepoCreateStep.createProjectTitle', + 'Create project' )} {translate( - 'auto.components.sidebar.AddRepoCreateStep.b100311784', - 'Name it and Orca will create a real project with sensible defaults.' + 'auto.components.sidebar.AddRepoCreateStep.createProjectDescription', + 'Create a local Git repo and first workspace.' )} @@ -173,14 +116,16 @@ export function CreateStep({ (= content size), so a long path inside the Location row would blow out the dialog width even with flex + truncate on the row itself. min-w-0 here caps the grid track at the dialog's max-width. */} -
- {/* Name. Monospaced because it ends up as a directory name. */} -
+
+
+ {repoNamePreview ? ( +

+ {translate( + 'auto.components.sidebar.AddRepoCreateStep.createsGitRepoHelp', + 'Git repo:' + )}{' '} + {repoNamePreview} +

+ ) : null}
- {/* Summary card doubles as the disclosure for the uncommon settings, so the - defaults and the controls to change them live in one place. */} -
- - - {advancedOpen && ( -
- {/* Real radiogroup so screen readers announce the segmented choice. */} -
- - {translate( - 'auto.components.sidebar.AddRepoCreateStep.180e9b5e48', - 'Project kind' - )} - -
- {(['git', 'folder'] as const).map((kind) => { - const selected = createKind === kind - const label = - kind === 'git' - ? translate( - 'auto.components.sidebar.AddRepoCreateStep.11fd2a7db8', - 'Git repository' - ) - : translate( - 'auto.components.sidebar.AddRepoCreateStep.038729c107', - 'Folder' - ) - const Icon = kind === 'git' ? GitBranch : Folder - return ( - - ) - })} -
- {showGitFallback && ( -

- {translate( - 'auto.components.sidebar.AddRepoCreateStep.fe1e616c5b', - "Git isn't installed, so a plain folder is the default." - )} -

- )} -
- - {/* The local picker returns client paths; runtime servers browse host paths via RPC. */} - setBrowsingParent(true)} - /> - - {targetPathPreview && ( -

- {targetPathPreview} -

+ +
+ {showGitChecking ? ( +

+ + {translate( + 'auto.components.sidebar.AddRepoCreateStep.2a762f3b19', + 'Checking Git on this host...' )} -

+

+ ) : showGitFallback ? ( +

+ {translate( + 'auto.components.sidebar.AddRepoCreateStep.gitRequiredError', + 'Git is required to create a project.' + )} +

+ ) : showRuntimeMissingParent ? ( +

+ {translate( + 'auto.components.sidebar.AddRepoCreateStep.c234df77f7', + 'Choose or enter a server parent folder before creating.' + )} +

+ ) : ( +

+ {parentSummary} +

)}
{createError && ( -

+

{createError}

)} - +
+ +
) diff --git a/src/renderer/src/components/sidebar/AddRepoDialog.tsx b/src/renderer/src/components/sidebar/AddRepoDialog.tsx index 5bf06950be3..9d2c17a12b7 100644 --- a/src/renderer/src/components/sidebar/AddRepoDialog.tsx +++ b/src/renderer/src/components/sidebar/AddRepoDialog.tsx @@ -133,12 +133,10 @@ const AddRepoDialog = React.memo(function AddRepoDialog() { const { createName, createParent, - createKind, createError, isCreating, setCreateName, setCreateParent, - setCreateKind, setCreateError, resetCreateState, handlePickParent, @@ -153,14 +151,12 @@ const AddRepoDialog = React.memo(function AddRepoDialog() { createRuntimeParentStatus, createParentDefaultPending, resetCreateDefaultState, - markCreateParentTouched, - markCreateKindTouched + markCreateParentTouched } = useCreateProjectDefaults({ step, activeRuntimeEnvironmentId: settings?.activeRuntimeEnvironmentId, createParent, - setCreateParent, - setCreateKind + setCreateParent }) const { @@ -332,7 +328,6 @@ const AddRepoDialog = React.memo(function AddRepoDialog() { nestedGroupName={nestedGroupName} createName={createName} createParent={createParent} - createKind={createKind} createError={createError} isCreating={isCreating} createDefaultParent={createDefaultParent} @@ -390,11 +385,6 @@ const AddRepoDialog = React.memo(function AddRepoDialog() { setCreateParent(value) setCreateError(null) }} - onCreateKindChange={(kind) => { - markCreateKindTouched() - setCreateKind(kind) - setCreateError(null) - }} onPickCreateParent={() => { void handlePickParent().then((dir) => { if (dir) { diff --git a/src/renderer/src/components/sidebar/AddRepoDialogStepContent.test.tsx b/src/renderer/src/components/sidebar/AddRepoDialogStepContent.test.tsx index f422bb391f1..b295293c100 100644 --- a/src/renderer/src/components/sidebar/AddRepoDialogStepContent.test.tsx +++ b/src/renderer/src/components/sidebar/AddRepoDialogStepContent.test.tsx @@ -53,7 +53,6 @@ function renderStepContent(overrides: Partial): string { nestedGroupName: 'platform', createName: '', createParent: '', - createKind: 'git', createError: null, isCreating: false, createDefaultParent: '', @@ -82,7 +81,6 @@ function renderStepContent(overrides: Partial): string { onImportNestedRepos: vi.fn(), onCreateNameChange: vi.fn(), onCreateParentChange: vi.fn(), - onCreateKindChange: vi.fn(), onPickCreateParent: vi.fn(), onCreate: vi.fn(), ...overrides @@ -129,8 +127,9 @@ describe('AddRepoDialogStepContent nested imports', () => { activeRuntimeEnvironmentId: 'env-1' }) - expect(html).toContain('Create a new project') - expect(html).toContain('server folder not selected') + expect(html).toContain('Create project') + expect(html).toContain('Choose or enter a server parent folder before creating.') + expect(html).toContain('Browse') }) it('offers server browsing for remote clone destinations', () => { diff --git a/src/renderer/src/components/sidebar/AddRepoDialogStepContent.tsx b/src/renderer/src/components/sidebar/AddRepoDialogStepContent.tsx index dc09458f42b..baa39686ba7 100644 --- a/src/renderer/src/components/sidebar/AddRepoDialogStepContent.tsx +++ b/src/renderer/src/components/sidebar/AddRepoDialogStepContent.tsx @@ -38,7 +38,6 @@ type AddRepoDialogStepContentProps = { nestedGroupName: string createName: string createParent: string - createKind: 'git' | 'folder' createError: string | null isCreating: boolean createDefaultParent: string @@ -67,7 +66,6 @@ type AddRepoDialogStepContentProps = { onImportNestedRepos: (mode: 'group' | 'separate') => void onCreateNameChange: (name: string) => void onCreateParentChange: (parent: string) => void - onCreateKindChange: (kind: 'git' | 'folder') => void onPickCreateParent: () => void onCreate: () => void } @@ -100,7 +98,6 @@ export function AddRepoDialogStepContent({ nestedGroupName, createName, createParent, - createKind, createError, isCreating, createDefaultParent, @@ -129,7 +126,6 @@ export function AddRepoDialogStepContent({ onImportNestedRepos, onCreateNameChange, onCreateParentChange, - onCreateKindChange, onPickCreateParent, onCreate }: AddRepoDialogStepContentProps): React.JSX.Element | null { @@ -224,7 +220,6 @@ export function AddRepoDialogStepContent({ diff --git a/src/renderer/src/components/sidebar/AddRepoStartSteps.test.tsx b/src/renderer/src/components/sidebar/AddRepoStartSteps.test.tsx index ace7b22bc85..62cfae41daa 100644 --- a/src/renderer/src/components/sidebar/AddRepoStartSteps.test.tsx +++ b/src/renderer/src/components/sidebar/AddRepoStartSteps.test.tsx @@ -131,7 +131,7 @@ describe('AddRepoLocalStartStep', () => { expect(markup).toContain('Browse folder') expect(markup).toContain('Clone from URL') expect(markup).toContain('Remote project') - expect(markup).toContain('Create new project') + expect(markup).toContain('Create project') expect(markup).toContain('Other ways to add') expect(markup).not.toContain('More options') }) @@ -140,7 +140,7 @@ describe('AddRepoLocalStartStep', () => { const titles = getActionTitles(false) expect(titles.primary).toBe('Browse folder') - expect(titles.secondary).toEqual(['Clone from URL', 'Remote project', 'Create new project']) + expect(titles.secondary).toEqual(['Clone from URL', 'Remote project', 'Create project']) }) it('keeps Browse folder primary for SSH-likely users', () => { @@ -149,14 +149,14 @@ describe('AddRepoLocalStartStep', () => { expect(markup).toContain('Browse folder') expect(markup).toContain('Remote project') expect(markup).toContain('Clone from URL') - expect(markup).toContain('Create new project') + expect(markup).toContain('Create project') }) it('orders secondary actions remote-first for SSH-likely users', () => { const titles = getActionTitles(true) expect(titles.primary).toBe('Browse folder') - expect(titles.secondary).toEqual(['Remote project', 'Clone from URL', 'Create new project']) + expect(titles.secondary).toEqual(['Remote project', 'Clone from URL', 'Create project']) }) it('focuses Browse folder when the default Add Project step opens', async () => { @@ -188,7 +188,7 @@ describe('AddRepoLocalStartStep', () => { expect(findButton(container, 'Clone from URL').disabled).toBe(false) expect(findButton(container, 'Remote project').disabled).toBe(false) - expect(findButton(container, 'Create new project').disabled).toBe(false) + expect(findButton(container, 'Create project').disabled).toBe(false) await act(async () => { root.unmount() diff --git a/src/renderer/src/components/sidebar/add-repo-local-start-actions.ts b/src/renderer/src/components/sidebar/add-repo-local-start-actions.ts index 259b6b8f3b7..0572b1dc236 100644 --- a/src/renderer/src/components/sidebar/add-repo-local-start-actions.ts +++ b/src/renderer/src/components/sidebar/add-repo-local-start-actions.ts @@ -71,12 +71,12 @@ export function getAddRepoLocalStartActions({ kind: 'create' as const, icon: Plus, title: translate( - 'auto.components.sidebar.add.repo.local.start.actions.c709860596', - 'Create new project' + 'auto.components.sidebar.add.repo.local.start.actions.createProjectTitle', + 'Create project' ), description: translate( - 'auto.components.sidebar.add.repo.local.start.actions.d72789705e', - 'Start from an empty folder' + 'auto.components.sidebar.add.repo.local.start.actions.createGitProjectDescription', + 'Create a local Git repository' ), onClick: onOpenCreateStep } diff --git a/src/renderer/src/components/sidebar/useCreateProjectDefaults.test.ts b/src/renderer/src/components/sidebar/useCreateProjectDefaults.test.ts index 3508458daa4..941362ca5e8 100644 --- a/src/renderer/src/components/sidebar/useCreateProjectDefaults.test.ts +++ b/src/renderer/src/components/sidebar/useCreateProjectDefaults.test.ts @@ -63,16 +63,14 @@ function useHarness(overrides: Partial { @@ -93,16 +91,15 @@ describe('useCreateProjectDefaults', () => { mocks.getDefaultCreateProjectParent.mockResolvedValue('/Users/alice/orca/projects') }) - it('auto-fills the local default parent and defaults to git when available', async () => { + it('auto-fills the local default parent and records Git availability', async () => { mocks.isGitAvailable.mockResolvedValue(true) - const { setCreateParent, setCreateKind } = useHarness() + const { setCreateParent } = useHarness() await flushAsync() expect(setCreateParent).toHaveBeenCalledWith('/Users/alice/orca/projects') expect(mocks.stateValues[DEFAULT_PARENT_STATE]).toBe('/Users/alice/orca/projects') expect(mocks.stateValues[GIT_AVAILABILITY_STATE]).toBe('available') - expect(setCreateKind).toHaveBeenCalledWith('git') expect(mocks.getDefaultCreateProjectParent).toHaveBeenCalled() expect(mocks.callRuntimeRpc).not.toHaveBeenCalled() }) @@ -128,24 +125,22 @@ describe('useCreateProjectDefaults', () => { expect(mocks.stateValues[DEFAULT_PARENT_STATE]).toBe('/Users/alice/orca/projects') }) - it('defaults to folder with a visible fallback when Git is unavailable', async () => { + it('records unavailable Git without changing project kind', async () => { mocks.isGitAvailable.mockResolvedValue(false) - const { setCreateKind } = useHarness() + useHarness() await flushAsync() expect(mocks.stateValues[GIT_AVAILABILITY_STATE]).toBe('unavailable') - expect(setCreateKind).toHaveBeenCalledWith('folder') }) - it('reports unknown availability and keeps the kind when the Git probe fails', async () => { + it('reports unknown availability when the Git probe fails', async () => { mocks.isGitAvailable.mockRejectedValue(new Error('probe failed')) - const { setCreateKind } = useHarness() + useHarness() await flushAsync() expect(mocks.stateValues[GIT_AVAILABILITY_STATE]).toBe('unknown') - expect(setCreateKind).not.toHaveBeenCalled() }) it('does not overwrite a parent the user already chose', async () => { @@ -161,7 +156,7 @@ describe('useCreateProjectDefaults', () => { mocks.browseRuntimeServerDirectory.mockResolvedValue({ resolvedPath: '/home/alice' }) mocks.callRuntimeRpc.mockResolvedValue({ available: true }) - const { setCreateParent, setCreateKind } = useHarness({ activeRuntimeEnvironmentId: 'env-1' }) + const { setCreateParent } = useHarness({ activeRuntimeEnvironmentId: 'env-1' }) await flushAsync() expect(mocks.browseRuntimeServerDirectory).toHaveBeenCalledWith('env-1', '~') @@ -176,7 +171,6 @@ describe('useCreateProjectDefaults', () => { { timeoutMs: 3000 } ) expect(mocks.isGitAvailable).not.toHaveBeenCalled() - expect(setCreateKind).toHaveBeenCalledWith('git') }) it('replaces an untouched local default when switching to a runtime target', async () => { @@ -249,11 +243,10 @@ describe('useCreateProjectDefaults', () => { }) it('does nothing outside the create step', async () => { - const { setCreateParent, setCreateKind } = useHarness({ step: 'add' }) + const { setCreateParent } = useHarness({ step: 'add' }) await flushAsync() expect(setCreateParent).not.toHaveBeenCalled() - expect(setCreateKind).not.toHaveBeenCalled() expect(mocks.isGitAvailable).not.toHaveBeenCalled() expect(mocks.browseRuntimeServerDirectory).not.toHaveBeenCalled() }) diff --git a/src/renderer/src/components/sidebar/useCreateProjectDefaults.ts b/src/renderer/src/components/sidebar/useCreateProjectDefaults.ts index 9c94cba1629..f93e61ee3d1 100644 --- a/src/renderer/src/components/sidebar/useCreateProjectDefaults.ts +++ b/src/renderer/src/components/sidebar/useCreateProjectDefaults.ts @@ -5,11 +5,7 @@ import { useCallback, useEffect, useRef, useState } from 'react' import { browseRuntimeServerDirectory } from '@/runtime/runtime-server-directory-browser' import { callRuntimeRpc } from '@/runtime/runtime-rpc-client' import type { AddRepoDialogStep } from './add-repo-dialog-types' -import { - getDefaultCreateProjectParent, - type GitAvailability, - type RepoKind -} from './create-project-defaults' +import { getDefaultCreateProjectParent, type GitAvailability } from './create-project-defaults' const LOCAL_GIT_AVAILABILITY_TIMEOUT_MS = 1500 const RUNTIME_GIT_AVAILABILITY_TIMEOUT_MS = 3000 @@ -51,14 +47,12 @@ export function useCreateProjectDefaults({ step, activeRuntimeEnvironmentId, createParent, - setCreateParent, - setCreateKind + setCreateParent }: { step: AddRepoDialogStep activeRuntimeEnvironmentId: string | null | undefined createParent: string setCreateParent: (value: string) => void - setCreateKind: (kind: RepoKind) => void }): { createDefaultParent: string createGitAvailability: GitAvailability @@ -66,7 +60,6 @@ export function useCreateProjectDefaults({ createParentDefaultPending: boolean resetCreateDefaultState: () => void markCreateParentTouched: (value?: string) => void - markCreateKindTouched: () => void } { const [createDefaultParent, setCreateDefaultParent] = useState('') const [createGitAvailability, setCreateGitAvailability] = useState('unknown') @@ -76,7 +69,6 @@ export function useCreateProjectDefaults({ const autoFilledCreateParentRef = useRef(null) const createParentProvenanceRef = useRef(null) const createParentTouchedRef = useRef(false) - const createKindTouchedRef = useRef(false) const createParentDefaultGenRef = useRef(0) const createGitProbeGenRef = useRef(0) const activeCreateParentRuntimeEnvironmentId = activeRuntimeEnvironmentId?.trim() || null @@ -96,7 +88,6 @@ export function useCreateProjectDefaults({ autoFilledCreateParentRef.current = null createParentProvenanceRef.current = null createParentTouchedRef.current = false - createKindTouchedRef.current = false setCreateDefaultParent('') setCreateGitAvailability('unknown') setCreateRuntimeParentStatus('idle') @@ -114,9 +105,6 @@ export function useCreateProjectDefaults({ }, [activeCreateParentRuntimeEnvironmentId, createParent] ) - const markCreateKindTouched = useCallback(() => { - createKindTouchedRef.current = true - }, []) const createParentDefaultPending = step === 'create' && @@ -282,10 +270,6 @@ export function useCreateProjectDefaults({ return } setCreateGitAvailability(available ? 'available' : 'unavailable') - if (createKindTouchedRef.current) { - return - } - setCreateKind(available ? 'git' : 'folder') }) .catch(() => { if (gen !== createGitProbeGenRef.current) { @@ -293,7 +277,7 @@ export function useCreateProjectDefaults({ } setCreateGitAvailability('unknown') }) - }, [activeRuntimeEnvironmentId, setCreateKind, step]) + }, [activeRuntimeEnvironmentId, step]) return { createDefaultParent, @@ -301,7 +285,6 @@ export function useCreateProjectDefaults({ createRuntimeParentStatus, createParentDefaultPending: createParentPending, resetCreateDefaultState, - markCreateParentTouched, - markCreateKindTouched + markCreateParentTouched } } diff --git a/src/renderer/src/components/sidebar/useCreateRepo.default-checkout.test.ts b/src/renderer/src/components/sidebar/useCreateRepo.default-checkout.test.ts index 4afc7041c91..78a26a3848c 100644 --- a/src/renderer/src/components/sidebar/useCreateRepo.default-checkout.test.ts +++ b/src/renderer/src/components/sidebar/useCreateRepo.default-checkout.test.ts @@ -89,7 +89,7 @@ describe('useCreateRepo default-checkout handoff', () => { vi.clearAllMocks() mocks.stateIndex = 0 mocks.stateSetters = [] - mocks.stateValues = ['created', '/projects', 'git', null, false] + mocks.stateValues = ['created', '/projects', null, false] mocks.storeState.repos = [] mocks.storeState.worktreesByRepo = {} mocks.storeState.settings.activeRuntimeEnvironmentId = null @@ -163,11 +163,11 @@ describe('useCreateRepo default-checkout handoff', () => { ) }) - it('marks onboarding folder progress when a created folder project opens', async () => { + it('uses the folder completion path if IPC returns a folder project', async () => { const repo = makeRepo({ kind: 'folder' }) const worktree = { id: `${repo.id}::/projects/created` } const closeModal = vi.fn() - mocks.stateValues = ['created', '/projects', 'folder', null, false] + mocks.stateValues = ['created', '/projects', null, false] mocks.createRepo.mockResolvedValue({ repo }) mocks.fetchWorktrees.mockImplementation(async (repoId: string) => { mocks.storeState.worktreesByRepo = { [repoId]: [worktree] } @@ -181,7 +181,7 @@ describe('useCreateRepo default-checkout handoff', () => { expect(mocks.createRepo).toHaveBeenCalledWith({ parentPath: '/projects', name: 'created', - kind: 'folder' + kind: 'git' }) expect(mocks.fetchWorktrees).toHaveBeenCalledWith(repo.id) expect(mocks.activateAndRevealWorktree).toHaveBeenCalledWith(worktree.id, { diff --git a/src/renderer/src/components/sidebar/useCreateRepo.ts b/src/renderer/src/components/sidebar/useCreateRepo.ts index bb12da03eb7..9d9aa95e418 100644 --- a/src/renderer/src/components/sidebar/useCreateRepo.ts +++ b/src/renderer/src/components/sidebar/useCreateRepo.ts @@ -10,7 +10,6 @@ import { callRuntimeRpc, getActiveRuntimeTarget } from '@/runtime/runtime-rpc-cl import { isGitRepoKind } from '../../../../shared/repo-kind' import type { Repo } from '../../../../shared/types' import { translate } from '@/i18n/i18n' -import type { RepoKind } from './create-project-defaults' export function useCreateRepo( fetchWorktrees: ( @@ -22,7 +21,6 @@ export function useCreateRepo( ) { const [createName, setCreateName] = useState('') const [createParent, setCreateParent] = useState('') - const [createKind, setCreateKind] = useState('git') const [createError, setCreateError] = useState(null) const [isCreating, setIsCreating] = useState(false) const mountedRef = useMountedRef() @@ -36,7 +34,6 @@ export function useCreateRepo( createGenRef.current++ setCreateName('') setCreateParent('') - setCreateKind('git') setCreateError(null) setIsCreating(false) }, []) @@ -82,14 +79,14 @@ export function useCreateRepo( { parentPath, name, - kind: createKind + kind: 'git' }, { timeoutMs: 60_000 } ) : await window.api.repos.create({ parentPath, name, - kind: createKind + kind: 'git' }) // Why: if the user closed the dialog or clicked Back mid-create, // createGenRef was bumped by resetCreateState. Ignore stale results. @@ -170,17 +167,15 @@ export function useCreateRepo( setIsCreating(false) } } - }, [createName, createParent, createKind, fetchWorktrees, mountedRef, closeModal, onGitRepoReady]) + }, [createName, createParent, fetchWorktrees, mountedRef, closeModal, onGitRepoReady]) return { createName, createParent, - createKind, createError, isCreating, setCreateName, setCreateParent, - setCreateKind, setCreateError, resetCreateState, handlePickParent, diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json index 8a7d2103a28..59d81ba2579 100644 --- a/src/renderer/src/i18n/locales/en.json +++ b/src/renderer/src/i18n/locales/en.json @@ -3062,7 +3062,15 @@ "2c12db1511": "Project already added", "875dda0995": "Enter a server parent path.", "45b7c26034": "Create project", - "85085d74d2": "Creating…" + "85085d74d2": "Creating…", + "createProjectTitle": "Create project", + "createProjectDescription": "Create a local Git repo and first workspace.", + "projectNameLabel": "Project name", + "createsGitRepoHelp": "Git repo:", + "parentFolderLabel": "Parent folder", + "browseParentFolder": "Browse", + "gitRequiredError": "Git is required to create a project.", + "createAction": "Create" }, "AddRepoNestedImportStep": { "496f68cf8c": "Scanning repositories. Click to stop.", @@ -3663,7 +3671,9 @@ "a6c20dca96": "Open a project from an SSH target", "3d162cc76f": "Remote project", "fb4fc5380e": "Local project, Git repo, or folder with many repos", - "2281fdc8c7": "Browse folder" + "2281fdc8c7": "Browse folder", + "createProjectTitle": "Create project", + "createGitProjectDescription": "Create a local Git repository" } } } diff --git a/src/renderer/src/i18n/locales/es.json b/src/renderer/src/i18n/locales/es.json index b12bdc671e9..65cbbcee527 100644 --- a/src/renderer/src/i18n/locales/es.json +++ b/src/renderer/src/i18n/locales/es.json @@ -3062,7 +3062,15 @@ "fe1e616c5b": "Git no está instalado, así que una carpeta simple será el valor predeterminado.", "c234df77f7": "Elige o introduce una carpeta principal del servidor antes de crear.", "3a13f6e88b": "ubicación no seleccionada", - "6ed14c0281": "carpeta del servidor no seleccionada" + "6ed14c0281": "carpeta del servidor no seleccionada", + "createProjectTitle": "Crear proyecto", + "createProjectDescription": "Crea un repositorio Git local y el primer espacio de trabajo.", + "projectNameLabel": "Nombre del proyecto", + "createsGitRepoHelp": "Repositorio Git:", + "parentFolderLabel": "Carpeta principal", + "browseParentFolder": "Explorar", + "gitRequiredError": "Git es necesario para crear un proyecto.", + "createAction": "Crear" }, "AddRepoNestedImportStep": { "496f68cf8c": "Escaneo de repositorios. Haga clic para detener.", @@ -3663,7 +3671,9 @@ "a6c20dca96": "Abrir un proyecto desde un destino SSH", "3d162cc76f": "Proyecto remoto", "fb4fc5380e": "Proyecto local, repo de Git o carpeta con muchos repos", - "2281fdc8c7": "Explorar carpeta" + "2281fdc8c7": "Explorar carpeta", + "createProjectTitle": "Crear proyecto", + "createGitProjectDescription": "Crear un repositorio Git local" } } } diff --git a/src/renderer/src/i18n/locales/ja.json b/src/renderer/src/i18n/locales/ja.json index fc3ac2b0837..888ce576bb0 100644 --- a/src/renderer/src/i18n/locales/ja.json +++ b/src/renderer/src/i18n/locales/ja.json @@ -3043,7 +3043,15 @@ "fe1e616c5b": "Git がインストールされていないため、通常のフォルダが既定になります。", "c234df77f7": "作成する前に、サーバーの親フォルダを選択または入力してください。", "3a13f6e88b": "場所が選択されていません", - "6ed14c0281": "サーバーフォルダが選択されていません" + "6ed14c0281": "サーバーフォルダが選択されていません", + "createProjectTitle": "プロジェクトの作成", + "createProjectDescription": "ローカルの Git リポジトリと最初のワークスペースを作成します。", + "projectNameLabel": "プロジェクト名", + "createsGitRepoHelp": "Git リポジトリ:", + "parentFolderLabel": "親フォルダ", + "browseParentFolder": "参照", + "gitRequiredError": "プロジェクトを作成するには Git が必要です。", + "createAction": "作成" }, "AddRepoNestedImportStep": { "496f68cf8c": "リポジトリをスキャンしています。クリックして停止します。", @@ -3644,7 +3652,9 @@ "a6c20dca96": "SSH ターゲットからプロジェクトを開く", "3d162cc76f": "リモートプロジェクト", "fb4fc5380e": "ローカルプロジェクト、Git repo、または多数の repos を含むフォルダー", - "2281fdc8c7": "フォルダを参照する" + "2281fdc8c7": "フォルダを参照する", + "createProjectTitle": "プロジェクトの作成", + "createGitProjectDescription": "ローカルの Git リポジトリを作成" } } } diff --git a/src/renderer/src/i18n/locales/ko.json b/src/renderer/src/i18n/locales/ko.json index a70b8cd7374..8b34556d89c 100644 --- a/src/renderer/src/i18n/locales/ko.json +++ b/src/renderer/src/i18n/locales/ko.json @@ -3043,7 +3043,15 @@ "fe1e616c5b": "Git이 설치되어 있지 않아 일반 폴더가 기본값입니다.", "c234df77f7": "만들기 전에 서버 상위 폴더를 선택하거나 입력하세요.", "3a13f6e88b": "위치가 선택되지 않음", - "6ed14c0281": "서버 폴더가 선택되지 않음" + "6ed14c0281": "서버 폴더가 선택되지 않음", + "createProjectTitle": "프로젝트 생성", + "createProjectDescription": "로컬 Git 저장소와 첫 번째 작업 공간을 만듭니다.", + "projectNameLabel": "프로젝트 이름", + "createsGitRepoHelp": "Git 저장소:", + "parentFolderLabel": "상위 폴더", + "browseParentFolder": "찾아보기", + "gitRequiredError": "프로젝트를 만들려면 Git이 필요합니다.", + "createAction": "생성" }, "AddRepoNestedImportStep": { "496f68cf8c": "저장소를 스캔하는 중입니다. 중지하려면 클릭하세요.", @@ -3644,7 +3652,9 @@ "a6c20dca96": "SSH 대상에서 프로젝트 열기", "3d162cc76f": "원격 프로젝트", "fb4fc5380e": "로컬 프로젝트, Git repo 또는 repos가 많은 폴더", - "2281fdc8c7": "폴더 찾아보기" + "2281fdc8c7": "폴더 찾아보기", + "createProjectTitle": "프로젝트 생성", + "createGitProjectDescription": "로컬 Git 저장소 생성" } } } diff --git a/src/renderer/src/i18n/locales/zh.json b/src/renderer/src/i18n/locales/zh.json index 7fba13602df..50187293a2e 100644 --- a/src/renderer/src/i18n/locales/zh.json +++ b/src/renderer/src/i18n/locales/zh.json @@ -3043,7 +3043,15 @@ "fe1e616c5b": "未安装 Git,因此默认创建普通文件夹。", "c234df77f7": "创建前请选择或输入服务器父文件夹。", "3a13f6e88b": "未选择位置", - "6ed14c0281": "未选择服务器文件夹" + "6ed14c0281": "未选择服务器文件夹", + "createProjectTitle": "创建项目", + "createProjectDescription": "创建本地 Git 存储库和第一个工作区。", + "projectNameLabel": "项目名称", + "createsGitRepoHelp": "Git 存储库:", + "parentFolderLabel": "父文件夹", + "browseParentFolder": "浏览", + "gitRequiredError": "创建项目需要 Git。", + "createAction": "创建" }, "AddRepoNestedImportStep": { "496f68cf8c": "扫描存储库。单击停止。", @@ -3644,7 +3652,9 @@ "a6c20dca96": "从 SSH 目标打开项目", "3d162cc76f": "远程项目", "fb4fc5380e": "本地项目、Git repo 或包含多个 repos 的文件夹", - "2281fdc8c7": "浏览文件夹" + "2281fdc8c7": "浏览文件夹", + "createProjectTitle": "创建项目", + "createGitProjectDescription": "创建本地 Git 存储库" } } }