import { useState, useRef, useCallback, useMemo } from 'react'; interface BranchSelectorProps { projectPath: string; currentBranch: string; } export function BranchSelector({ projectPath, currentBranch }: BranchSelectorProps) { const [isOpen, setIsOpen] = useState(false); const [branches, setBranches] = useState([]); const [loading, setLoading] = useState(false); const [searchQuery, setSearchQuery] = useState(''); const [newBranchName, setNewBranchName] = useState(''); const [showNewBranchInput, setShowNewBranchInput] = useState(false); const [error, setError] = useState(null); const dropdownRef = useRef(null); const inputRef = useRef(null); const searchInputRef = useRef(null); const filteredBranches = useMemo(() => { if (!searchQuery.trim()) return branches; const query = searchQuery.toLowerCase(); return branches.filter((branch) => branch.toLowerCase().includes(query)); }, [branches, searchQuery]); const loadBranches = useCallback(async () => { setLoading(true); setError(null); try { const result = await window.git.listBranches(projectPath); setBranches(result.branches); } catch { setError('Failed to load branches'); } finally { setLoading(false); } }, [projectPath]); const handleOpen = useCallback(() => { setIsOpen(true); setShowNewBranchInput(false); setNewBranchName(''); setSearchQuery(''); setError(null); loadBranches(); setTimeout(() => searchInputRef.current?.focus(), 0); }, [loadBranches]); const handleClose = useCallback(() => { setIsOpen(false); setShowNewBranchInput(false); setNewBranchName(''); setSearchQuery(''); setError(null); }, []); const handleSwitchBranch = useCallback(async (branchName: string) => { if (branchName === currentBranch) { handleClose(); return; } setLoading(true); setError(null); const result = await window.git.switchBranch(projectPath, branchName); setLoading(false); if (result.success) { handleClose(); } else { setError(result.error || 'Failed to switch branch'); } }, [projectPath, currentBranch, handleClose]); const handleCreateBranch = useCallback(async () => { const name = newBranchName.trim(); if (!name) return; setLoading(true); setError(null); const result = await window.git.createBranch(projectPath, name); setLoading(false); if (result.success) { handleClose(); } else { setError(result.error || 'Failed to create branch'); } }, [projectPath, newBranchName, handleClose]); const handleKeyDown = useCallback((e: React.KeyboardEvent) => { if (e.key === 'Escape') { handleClose(); } else if (e.key === 'Enter' && showNewBranchInput) { handleCreateBranch(); } }, [handleClose, showNewBranchInput, handleCreateBranch]); return (
{isOpen && ( <>
setSearchQuery(e.target.value)} placeholder="Search branches..." className="w-full px-2 py-1.5 text-xs bg-gray-800 border border-gray-600 rounded text-gray-200 placeholder-gray-500 focus:outline-none focus:border-[#5a9bc7]" />
{error && (
{error}
)}
{loading && branches.length === 0 ? (
Loading...
) : filteredBranches.length === 0 ? (
No branches found
) : ( filteredBranches.map((branch) => ( )) )}
{showNewBranchInput ? (
setNewBranchName(e.target.value)} placeholder="Branch name" autoFocus className="flex-1 px-2 py-1 text-xs bg-gray-800 border border-gray-600 rounded text-gray-200 placeholder-gray-500 focus:outline-none focus:border-[#5a9bc7]" />
) : ( )}
)}
); }