From c2b95d057f05559056cfa2630845ffa5a14c6aac Mon Sep 17 00:00:00 2001 From: reaper Date: Sat, 8 Aug 2026 05:53:10 -0500 Subject: [PATCH] Close create wizard modal as soon as ddev config succeeds MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit handleSubmit awaited the entire post-create chain (start, then for WordPress: download core + install) before ever calling onClose(), so the modal overlay sat there for the whole multi-step process with no way to see what was actually happening — the terminal panel that already tracks this operation's live output was hidden behind it. Close right after the quick config step succeeds and let post-create continue as a tracked background operation; failures still surface via the existing toast/terminal-panel error handling regardless of modal state. Also extracts the project-name validation helpers out of CreateProjectModal.tsx into their own module — mixing component and non-component exports in one file breaks React Fast Refresh. Co-Authored-By: Claude Sonnet 5 --- .../components/create/CreateProjectModal.tsx | 32 ++++++++----------- .../src/components/create/projectName.test.ts | 2 +- .../src/components/create/projectName.ts | 14 ++++++++ 3 files changed, 28 insertions(+), 20 deletions(-) create mode 100644 src/renderer/src/components/create/projectName.ts diff --git a/src/renderer/src/components/create/CreateProjectModal.tsx b/src/renderer/src/components/create/CreateProjectModal.tsx index 59e58cf..1bae70d 100644 --- a/src/renderer/src/components/create/CreateProjectModal.tsx +++ b/src/renderer/src/components/create/CreateProjectModal.tsx @@ -19,25 +19,11 @@ import { getTypeLabel, PROJECT_TYPES } from './types/registry' import { GenericSetup } from './types/GenericSetup' import { WordpressSetup } from './types/WordpressSetup' import type { TypeSetupHandle } from './types/shared' +import { isValidProjectName, slugifyProjectName } from './projectName' import docksideIcon from '../../assets/dockside-icon.png' type Step = 'site' | 'setup' -// DDEV project names must be valid hostname labels: alphanumeric and -// hyphens only, can't start/end with a hyphen. -const PROJECT_NAME_PATTERN = /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/ - -export function isValidProjectName(name: string): boolean { - return PROJECT_NAME_PATTERN.test(name) -} - -export function slugifyProjectName(raw: string): string { - return raw - .toLowerCase() - .replace(/[^a-z0-9]+/g, '-') - .replace(/^-+|-+$/g, '') -} - const fieldClass = 'w-full rounded-lg border border-neutral-300 bg-white/80 px-3 py-2 text-sm shadow-sm transition placeholder:text-neutral-400 focus:border-cyan-400 dark:border-white/10 dark:bg-neutral-950/70 dark:placeholder:text-neutral-600' @@ -92,12 +78,20 @@ export function CreateProjectModal({ onClose }: { onClose: () => void }): React. setIsSubmitting(true) try { await createProject.mutateAsync({ directory, projectName: name, projectType, docroot }) - await setupRef.current?.runPostCreate({ directory, projectName: name }) - selectProject(name) - onClose() - } finally { + } catch { setIsSubmitting(false) + return } + + // Post-create (starting the project, downloading/installing WordPress, + // etc.) can run long. Close the modal as soon as the quick `ddev + // config` step succeeds instead of blocking the whole wizard on it — + // the terminal panel already tracks and surfaces this operation's + // progress and any failure independently of the modal. + const runPostCreate = setupRef.current?.runPostCreate + selectProject(name) + onClose() + runPostCreate?.({ directory, projectName: name }).catch(() => {}) } return ( diff --git a/src/renderer/src/components/create/projectName.test.ts b/src/renderer/src/components/create/projectName.test.ts index 63168fb..5e0b3d0 100644 --- a/src/renderer/src/components/create/projectName.test.ts +++ b/src/renderer/src/components/create/projectName.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from 'vitest' -import { isValidProjectName, slugifyProjectName } from './CreateProjectModal' +import { isValidProjectName, slugifyProjectName } from './projectName' describe('isValidProjectName', () => { it('accepts hostname-safe names', () => { diff --git a/src/renderer/src/components/create/projectName.ts b/src/renderer/src/components/create/projectName.ts new file mode 100644 index 0000000..1f28590 --- /dev/null +++ b/src/renderer/src/components/create/projectName.ts @@ -0,0 +1,14 @@ +// DDEV project names must be valid hostname labels: alphanumeric and +// hyphens only, can't start/end with a hyphen. +const PROJECT_NAME_PATTERN = /^[a-zA-Z0-9]([a-zA-Z0-9-]*[a-zA-Z0-9])?$/ + +export function isValidProjectName(name: string): boolean { + return PROJECT_NAME_PATTERN.test(name) +} + +export function slugifyProjectName(raw: string): string { + return raw + .toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, '') +}