From 369ddb6fe66837473e47f721c843c0a90f57fa5a Mon Sep 17 00:00:00 2001 From: Neil <4138956+nwparker@users.noreply.github.com> Date: Tue, 21 Apr 2026 15:55:18 -0700 Subject: [PATCH] Simplify new workspace quick-create and wire Use button to direct launch (#913) * Refine workspace composer controls * Working repo autofocus * after selects * Simplify workspace quick create dialog * Refine quick create form focus flow * autofocus fix * normal dialog * base dialogs * Wire Use button to launch workspace directly Skips the composer modal for the common case: creates the workspace, activates it, launches the default agent, and pastes the work-item URL into the agent's input as a reviewable draft. Falls back to the modal when setupRunPolicy is 'ask' or no compatible agent is detected. * Stretch combobox dropdowns to trigger width Repository and Agent rows in the quick-create dialog now span the full dialog width like the Workspace Name input, and their dropdown popovers inherit the trigger width so the entries align edge-to-edge with the trigger instead of clipping to a fixed 288/320px. --- .../components/NewWorkspaceComposerCard.tsx | 943 ++++++------------ .../components/NewWorkspaceComposerModal.tsx | 124 ++- .../src/components/NewWorkspacePage.tsx | 31 +- .../src/components/agent/AgentCombobox.tsx | 308 ++++++ .../components/agent/AgentSettingsDialog.tsx | 47 + .../src/components/repo/RepoCombobox.tsx | 276 +++-- .../src/components/settings/AgentsPane.tsx | 31 +- src/renderer/src/hooks/useComposerState.ts | 183 +++- src/renderer/src/lib/agent-ready-wait.ts | 117 +++ src/renderer/src/lib/detect-agents-cached.ts | 25 + .../src/lib/launch-work-item-direct.ts | 214 ++++ src/renderer/src/lib/tui-agent-startup.ts | 19 +- src/renderer/src/lib/worktree-activation.ts | 22 +- src/shared/types.ts | 7 +- 14 files changed, 1516 insertions(+), 831 deletions(-) create mode 100644 src/renderer/src/components/agent/AgentCombobox.tsx create mode 100644 src/renderer/src/components/agent/AgentSettingsDialog.tsx create mode 100644 src/renderer/src/lib/agent-ready-wait.ts create mode 100644 src/renderer/src/lib/detect-agents-cached.ts create mode 100644 src/renderer/src/lib/launch-work-item-direct.ts diff --git a/src/renderer/src/components/NewWorkspaceComposerCard.tsx b/src/renderer/src/components/NewWorkspaceComposerCard.tsx index 58ea22626..524e44163 100644 --- a/src/renderer/src/components/NewWorkspaceComposerCard.tsx +++ b/src/renderer/src/components/NewWorkspaceComposerCard.tsx @@ -5,90 +5,37 @@ import React from 'react' import { Check, ChevronDown, - CircleDot, CornerDownLeft, - GitPullRequest, - Github, + FolderPlus, LoaderCircle, - Paperclip, - Plus, - X + Settings2 } from 'lucide-react' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' -import { - Command, - CommandEmpty, - CommandGroup, - CommandInput, - CommandItem, - CommandList -} from '@/components/ui/command' -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue -} from '@/components/ui/select' -import { - DropdownMenu, - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuShortcut, - DropdownMenuTrigger -} from '@/components/ui/dropdown-menu' -import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' import RepoCombobox from '@/components/repo/RepoCombobox' -import { AGENT_CATALOG, AgentIcon } from '@/lib/agent-catalog' +import AgentCombobox from '@/components/agent/AgentCombobox' +import { AGENT_CATALOG } from '@/lib/agent-catalog' +import { useAppStore } from '@/store' import { cn } from '@/lib/utils' -import type { GitHubWorkItem, TuiAgent } from '../../../shared/types' +import type { TuiAgent } from '../../../shared/types' +import { isGitRepoKind } from '../../../shared/repo-kind' + +const isMac = typeof navigator !== 'undefined' && navigator.userAgent.includes('Mac') type RepoOption = React.ComponentProps['repos'][number] -type LinkedWorkItemSummary = { - type: 'issue' | 'pr' - number: number - title: string - url: string -} | null - type NewWorkspaceComposerCardProps = { containerClassName?: string composerRef?: React.RefObject nameInputRef?: React.RefObject - promptTextareaRef?: React.RefObject + quickAgent: TuiAgent | null + onQuickAgentChange: (agent: TuiAgent | null) => void eligibleRepos: RepoOption[] repoId: string onRepoChange: (value: string) => void name: string onNameChange: (event: React.ChangeEvent) => void - agentPrompt: string - onAgentPromptChange: (value: string) => void - onPromptKeyDown: (event: React.KeyboardEvent) => void - linkedOnlyTemplatePreview: string | null - attachmentPaths: string[] - getAttachmentLabel: (pathValue: string) => string - onAddAttachment: () => void - onRemoveAttachment: (pathValue: string) => void - addAttachmentShortcut: string - linkedWorkItem: LinkedWorkItemSummary - onRemoveLinkedWorkItem: () => void - linkPopoverOpen: boolean - onLinkPopoverOpenChange: (open: boolean) => void - linkQuery: string - onLinkQueryChange: (value: string) => void - filteredLinkItems: GitHubWorkItem[] - linkItemsLoading: boolean - linkDirectLoading: boolean - normalizedLinkQuery: { - query: string - repoMismatch: string | null - } - onSelectLinkedItem: (item: GitHubWorkItem) => void - tuiAgent: TuiAgent - onTuiAgentChange: (value: TuiAgent) => void detectedAgentIds: Set | null onOpenAgentSettings: () => void advancedOpen: boolean @@ -107,126 +54,6 @@ type NewWorkspaceComposerCardProps = { createError: string | null } -function PromptPrefixTextarea({ - textareaRef, - value, - onChange, - onKeyDown, - placeholder, - placeholderTone -}: { - textareaRef?: React.RefObject - value: string - onChange: (value: string) => void - onKeyDown: (event: React.KeyboardEvent) => void - placeholder: string - placeholderTone: 'muted' | 'ghost-prompt' -}): React.JSX.Element { - const internalRef = React.useRef(null) - - const setRefs = React.useCallback( - (node: HTMLTextAreaElement | null) => { - internalRef.current = node - if (textareaRef) { - textareaRef.current = node - } - }, - [textareaRef] - ) - - // Why: auto-size the textarea to its content and hoist scrolling onto the - // outer wrapper. Any JS-driven overlay sync (listening to `scroll`, updating - // `translateY`) always paints a frame behind the textarea's own scroll — on - // momentum scroll that shows up as visible wobble between the `>` and the - // typed text. Putting both elements inside the same native scroll container - // makes them move in lockstep with zero JS and no cross-layer paint delay. - React.useLayoutEffect(() => { - const el = internalRef.current - if (!el) { - return - } - el.style.height = 'auto' - el.style.height = `${el.scrollHeight}px` - }, [value]) - - return ( - // Why: allow the composer to grow with the user's prompt up to ~20 rows - // (560px at leading-7 ≈ 28px/row) before scrolling. The inner textarea's - // JS-driven auto-resize sets its own height to scrollHeight, so the wrapper - // simply follows until the max-height cap engages and hands off to scroll. -
-
- - {'>'} - -