From cb99c4b4e869ecd65546837c7ae2fd9fdb83d0cf Mon Sep 17 00:00:00 2001 From: gsxdsm Date: Tue, 17 Feb 2026 22:08:22 -0800 Subject: [PATCH] feat: Replace Select with Popover+Command for branch selection UI --- .../dialogs/create-branch-dialog.tsx | 177 +++++++++++++----- 1 file changed, 126 insertions(+), 51 deletions(-) diff --git a/apps/ui/src/components/views/board-view/dialogs/create-branch-dialog.tsx b/apps/ui/src/components/views/board-view/dialogs/create-branch-dialog.tsx index ca7a398a..51dc1345 100644 --- a/apps/ui/src/components/views/board-view/dialogs/create-branch-dialog.tsx +++ b/apps/ui/src/components/views/board-view/dialogs/create-branch-dialog.tsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useCallback } from 'react'; +import { useState, useEffect, useCallback, useMemo, useRef } from 'react'; import { createLogger } from '@automaker/utils/logger'; import { Dialog, @@ -11,21 +11,22 @@ import { import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; +import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'; import { - Select, - SelectContent, - SelectGroup, - SelectItem, - SelectLabel, - SelectSeparator, - SelectTrigger, - SelectValue, -} from '@/components/ui/select'; + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, + CommandSeparator, +} from '@/components/ui/command'; import { getElectronAPI } from '@/lib/electron'; import { getHttpApiClient } from '@/lib/http-api-client'; import { toast } from 'sonner'; -import { GitBranchPlus, RefreshCw } from 'lucide-react'; +import { Check, ChevronsUpDown, GitBranchPlus, Globe, RefreshCw } from 'lucide-react'; import { Spinner } from '@/components/ui/spinner'; +import { cn } from '@/lib/utils'; interface WorktreeInfo { path: string; @@ -62,6 +63,9 @@ export function CreateBranchDialog({ const [isLoadingBranches, setIsLoadingBranches] = useState(false); const [isCreating, setIsCreating] = useState(false); const [error, setError] = useState(null); + const [baseBranchPopoverOpen, setBaseBranchPopoverOpen] = useState(false); + const baseBranchTriggerRef = useRef(null); + const [baseBranchTriggerWidth, setBaseBranchTriggerWidth] = useState(0); const fetchBranches = useCallback(async () => { if (!worktree) return; @@ -93,10 +97,23 @@ export function CreateBranchDialog({ setBaseBranch(''); setError(null); setBranches([]); + setBaseBranchPopoverOpen(false); fetchBranches(); } }, [open, fetchBranches]); + // Track trigger width for popover sizing + useEffect(() => { + const el = baseBranchTriggerRef.current; + if (!el) return; + const observer = new ResizeObserver(() => { + setBaseBranchTriggerWidth(el.offsetWidth); + }); + observer.observe(el); + setBaseBranchTriggerWidth(el.offsetWidth); + return () => observer.disconnect(); + }, [baseBranchPopoverOpen]); + const handleCreate = async () => { if (!worktree || !branchName.trim()) return; @@ -141,8 +158,16 @@ export function CreateBranchDialog({ }; // Separate local and remote branches - const localBranches = branches.filter((b) => !b.isRemote); - const remoteBranches = branches.filter((b) => b.isRemote); + const localBranches = useMemo(() => branches.filter((b) => !b.isRemote), [branches]); + const remoteBranches = useMemo(() => branches.filter((b) => b.isRemote), [branches]); + + // Display label for the selected base branch + const baseBranchDisplayLabel = useMemo(() => { + if (!baseBranch) return null; + const found = branches.find((b) => b.name === baseBranch); + if (!found) return baseBranch; + return found.isCurrent ? `${found.name} (current)` : found.name; + }, [baseBranch, branches]); return ( @@ -200,44 +225,94 @@ export function CreateBranchDialog({ Loading branches... ) : ( - + + + + + e.stopPropagation()} + onTouchMove={(e) => e.stopPropagation()} + > + + + + No matching branches + {localBranches.length > 0 && ( + + {localBranches.map((branch) => ( + { + setBaseBranch(value); + setBaseBranchPopoverOpen(false); + }} + > + + + {branch.name} + + {branch.isCurrent && ( + + (current) + + )} + + ))} + + )} + {remoteBranches.length > 0 && ( + <> + {localBranches.length > 0 && } + + {remoteBranches.map((branch) => ( + { + setBaseBranch(value); + setBaseBranchPopoverOpen(false); + }} + > + + + {branch.name} + + ))} + + + )} + + + + )}