import { useState, useEffect, useCallback, useMemo } from "react" import type { WorktreeDefaultsResponse, BranchInfo, WorktreeIncludeStatus } from "@roo-code/types" import { vscode } from "@/utils/vscode" import { useAppTranslation } from "@/i18n/TranslationContext" import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, Button, Input, } from "@/components/ui" import { SearchableSelect, type SearchableSelectOption } from "@/components/ui/searchable-select" interface CreateWorktreeModalProps { open: boolean onClose: () => void openAfterCreate?: boolean onSuccess?: () => void } export const CreateWorktreeModal = ({ open, onClose, openAfterCreate = false, onSuccess, }: CreateWorktreeModalProps) => { const { t } = useAppTranslation() // Form state const [branchName, setBranchName] = useState("") const [worktreePath, setWorktreePath] = useState("") const [baseBranch, setBaseBranch] = useState("") // Data state const [defaults, setDefaults] = useState(null) const [branches, setBranches] = useState(null) const [includeStatus, setIncludeStatus] = useState(null) // UI state const [isCreating, setIsCreating] = useState(false) const [error, setError] = useState(null) // Fetch defaults and branches on open useEffect(() => { if (open) { vscode.postMessage({ type: "getWorktreeDefaults" }) vscode.postMessage({ type: "getAvailableBranches" }) vscode.postMessage({ type: "getWorktreeIncludeStatus" }) } }, [open]) // Handle messages from extension useEffect(() => { const handleMessage = (event: MessageEvent) => { const message = event.data switch (message.type) { case "worktreeDefaults": { const data = message as WorktreeDefaultsResponse setDefaults(data) setBranchName(data.suggestedBranch) setWorktreePath(data.suggestedPath) break } case "branchList": { const data = message as BranchInfo setBranches(data) setBaseBranch(data.currentBranch || "main") break } case "worktreeIncludeStatus": { setIncludeStatus(message.worktreeIncludeStatus) break } case "worktreeResult": { setIsCreating(false) if (message.success) { if (openAfterCreate) { vscode.postMessage({ type: "switchWorktree", worktreePath: worktreePath, worktreeNewWindow: true, }) } onSuccess?.() onClose() } else { setError(message.text || "Unknown error") } break } } } window.addEventListener("message", handleMessage) return () => window.removeEventListener("message", handleMessage) }, [openAfterCreate, worktreePath, onSuccess, onClose]) const handleCreate = useCallback(() => { setError(null) setIsCreating(true) vscode.postMessage({ type: "createWorktree", worktreePath: worktreePath, worktreeBranch: branchName, worktreeBaseBranch: baseBranch, worktreeCreateNewBranch: true, }) }, [worktreePath, branchName, baseBranch]) const isValid = branchName.trim() && worktreePath.trim() && baseBranch.trim() // Convert branches to SearchableSelect options format const branchOptions = useMemo((): SearchableSelectOption[] => { if (!branches) return [] const localOptions: SearchableSelectOption[] = branches.localBranches.map((branch) => ({ value: branch, label: branch, icon: , })) const remoteOptions: SearchableSelectOption[] = branches.remoteBranches.map((branch) => ({ value: branch, label: branch, icon: , })) return [...localOptions, ...remoteOptions] }, [branches]) return ( !isOpen && onClose()}> {t("worktrees:createWorktree")} {t("worktrees:createWorktreeDescription")}
{/* No .worktreeinclude warning - shows when the current worktree doesn't have .worktreeinclude */} {includeStatus?.exists === false && (
{t("worktrees:noIncludeFileWarning")} {" — "} {t("worktrees:noIncludeFileHint")}
)} {/* Branch name */}
setBranchName(e.target.value)} placeholder={defaults?.suggestedBranch || "worktree/feature-name"} className="rounded-full" />
{/* Base branch selector */}
{!branches ? (
{t("worktrees:loadingBranches")}
) : ( )}
{/* Worktree path */}
setWorktreePath(e.target.value)} placeholder={defaults?.suggestedPath || "/path/to/worktree"} className="rounded-full" />

{t("worktrees:pathHint")}

{/* Error message */} {error && (

{error}

)}
) }