diff --git a/apps/web/components/task-create-dialog-form-body.tsx b/apps/web/components/task-create-dialog-form-body.tsx index 98b596da0f..8555fa7495 100644 --- a/apps/web/components/task-create-dialog-form-body.tsx +++ b/apps/web/components/task-create-dialog-form-body.tsx @@ -313,6 +313,12 @@ export type DialogPromptSectionProps = { descriptionPlaceholder?: string; /** Optional slot rendered above the description textarea (e.g. a tab toggle). */ aboveDescriptionSlot?: React.ReactNode; + /** + * Whether the description textarea should grab focus on mount. Defaults to + * `!isTaskStarted`. Callers that render a task-name input above the + * description should pass `false` so the name field wins focus. + */ + autoFocusDescription?: boolean; }; // importBindings collapses the optional Jira/Linear import callbacks into the @@ -344,19 +350,21 @@ export function DialogPromptSection({ extraFormSlot, descriptionPlaceholder, aboveDescriptionSlot, + autoFocusDescription, }: DialogPromptSectionProps) { const importsEnabled = !isSessionMode && !isTaskStarted; const ws = workspaceId ?? null; const placeholder = isPassthroughProfile ? "Passthrough mode — prompt not supported" : descriptionPlaceholder; + const shouldAutoFocus = autoFocusDescription ?? !isTaskStarted; return ( <> {aboveDescriptionSlot} (null); + const hasFocusedRef = useRef(false); useEffect(() => { - if (autoFocus && inputRef.current) { + if (autoFocus && !hasFocusedRef.current && inputRef.current) { + hasFocusedRef.current = true; inputRef.current.focus(); inputRef.current.select(); } diff --git a/apps/web/components/task-create-dialog.tsx b/apps/web/components/task-create-dialog.tsx index 459ca42135..6d65cc1e4a 100644 --- a/apps/web/components/task-create-dialog.tsx +++ b/apps/web/components/task-create-dialog.tsx @@ -177,6 +177,7 @@ function CreateModeBody(props: DialogFormBodyProps) { isLocalExecutor, } = props; const showTaskName = (isCreateMode || isEditMode) && !isTaskStarted; + const taskNameAutoFocus = !isEditMode && !fs.useGitHubUrl; return ( <> )}