// ======================================== // Command Create Dialog Component // ======================================== // Modal dialog for creating/importing commands with two modes: // - Import: import existing command file // - CLI Generate: AI-generated command from description import { useState, useCallback } from 'react'; import { useIntl } from 'react-intl'; import { Folder, User, FileCode, Sparkles, CheckCircle, XCircle, Loader2, Info, } from 'lucide-react'; import { Dialog, DialogContent, DialogHeader, DialogFooter, DialogTitle, DialogDescription, } from '@/components/ui/Dialog'; import { Button } from '@/components/ui/Button'; import { Input } from '@/components/ui/Input'; import { Textarea } from '@/components/ui/Textarea'; import { Label } from '@/components/ui/Label'; import { validateCommandImport, createCommand } from '@/lib/api'; import { useWorkflowStore, selectProjectPath } from '@/stores/workflowStore'; import { cn } from '@/lib/utils'; export interface CommandCreateDialogProps { open: boolean; onOpenChange: (open: boolean) => void; onCreated: () => void; cliType?: 'claude' | 'codex'; } type CreateMode = 'import' | 'cli-generate'; type CommandLocation = 'project' | 'user'; interface ValidationResult { valid: boolean; errors?: string[]; commandInfo?: { name: string; description: string; usage?: string }; } export function CommandCreateDialog({ open, onOpenChange, onCreated, cliType = 'claude' }: CommandCreateDialogProps) { const { formatMessage } = useIntl(); const projectPath = useWorkflowStore(selectProjectPath); const [mode, setMode] = useState('import'); const [location, setLocation] = useState('project'); // Import mode state const [sourcePath, setSourcePath] = useState(''); const [customName, setCustomName] = useState(''); const [validationResult, setValidationResult] = useState(null); const [isValidating, setIsValidating] = useState(false); // CLI Generate mode state const [commandName, setCommandName] = useState(''); const [description, setDescription] = useState(''); const [isCreating, setIsCreating] = useState(false); const resetState = useCallback(() => { setMode('import'); setLocation('project'); setSourcePath(''); setCustomName(''); setValidationResult(null); setIsValidating(false); setCommandName(''); setDescription(''); setIsCreating(false); }, []); const handleOpenChange = useCallback((open: boolean) => { if (!open) { resetState(); } onOpenChange(open); }, [onOpenChange, resetState]); const handleValidate = useCallback(async () => { if (!sourcePath.trim()) return; setIsValidating(true); setValidationResult(null); try { const result = await validateCommandImport(sourcePath.trim()); setValidationResult(result); } catch (err) { setValidationResult({ valid: false, errors: [err instanceof Error ? err.message : String(err)], }); } finally { setIsValidating(false); } }, [sourcePath]); const handleCreate = useCallback(async () => { if (mode === 'import') { if (!sourcePath.trim()) return; if (!validationResult?.valid) return; } else { if (!commandName.trim()) return; if (!description.trim()) return; } setIsCreating(true); try { await createCommand({ mode, location, sourcePath: mode === 'import' ? sourcePath.trim() : undefined, commandName: mode === 'import' ? (customName.trim() || undefined) : commandName.trim(), description: mode === 'cli-generate' ? description.trim() : undefined, generationType: mode === 'cli-generate' ? 'description' : undefined, projectPath, cliType, }); handleOpenChange(false); onCreated(); } catch (err) { console.error('Failed to create command:', err); if (mode === 'import') { setValidationResult({ valid: false, errors: [err instanceof Error ? err.message : formatMessage({ id: 'commands.create.createError' })], }); } } finally { setIsCreating(false); } }, [mode, location, sourcePath, customName, commandName, description, validationResult, projectPath, handleOpenChange, onCreated, formatMessage]); const canCreate = mode === 'import' ? sourcePath.trim() && validationResult?.valid && !isCreating : commandName.trim() && description.trim() && !isCreating; return ( {formatMessage({ id: 'commands.create.title' })} {formatMessage({ id: 'commands.description' })}
{/* Location Selection */}
{/* Mode Selection */}
{/* Import Mode Content */} {mode === 'import' && (
{ setSourcePath(e.target.value); setValidationResult(null); }} placeholder={formatMessage({ id: 'commands.create.sourcePathPlaceholder' })} className="font-mono text-sm" />

{formatMessage({ id: 'commands.create.sourcePathHint' })}

setCustomName(e.target.value)} placeholder={formatMessage({ id: 'commands.create.customNamePlaceholder' })} />
{/* Validation Result */} {isValidating && (
{formatMessage({ id: 'commands.create.validating' })}
)} {validationResult && !isValidating && ( validationResult.valid ? (
{formatMessage({ id: 'commands.create.validCommand' })}
{validationResult.commandInfo && (
{formatMessage({ id: 'commands.card.name' })}: {validationResult.commandInfo.name}
{validationResult.commandInfo.description && (
{formatMessage({ id: 'commands.card.description' })}: {validationResult.commandInfo.description}
)} {validationResult.commandInfo.usage && (
{formatMessage({ id: 'commands.card.usage' })}: {validationResult.commandInfo.usage}
)}
)}
) : (
{formatMessage({ id: 'commands.create.invalidCommand' })}
{validationResult.errors && (
    {validationResult.errors.map((error, i) => (
  • {error}
  • ))}
)}
) )}
)} {/* CLI Generate Mode Content */} {mode === 'cli-generate' && (
setCommandName(e.target.value)} placeholder={formatMessage({ id: 'commands.create.commandNamePlaceholder' })} />

{formatMessage({ id: 'commands.create.commandNameHint' })}