/** * WorkflowEditor - OpenFang Workflow Editor Component * * Allows creating and editing multi-step workflows that chain * multiple Hands together for complex task automation. * * Design based on OpenFang Dashboard v0.4.0 */ import { useState, useEffect, useCallback } from 'react'; import { useHandStore, type Hand } from '../store/handStore'; import { useWorkflowStore, type Workflow } from '../store/workflowStore'; import { X, Plus, Trash2, GripVertical, ChevronDown, ChevronUp, ChevronLeft, ChevronRight, Save, Loader2, AlertCircle, GitBranch, } from 'lucide-react'; import { safeJsonParse } from '../lib/json-utils'; // === Types === interface WorkflowStep { id: string; handName: string; name?: string; params?: Record; condition?: string; } interface WorkflowEditorProps { workflow?: Workflow; // If provided, edit mode; otherwise create mode isOpen: boolean; onClose: () => void; onSave: (data: { name: string; description?: string; steps: Array<{ handName: string; name?: string; params?: Record; condition?: string; }>; }) => Promise; isSaving: boolean; } // === Step Editor Component === interface StepEditorProps { step: WorkflowStep; hands: Hand[]; index: number; onUpdate: (step: WorkflowStep) => void; onRemove: () => void; onMoveUp?: () => void; onMoveDown?: () => void; } function StepEditor({ step, hands, index, onUpdate, onRemove, onMoveUp, onMoveDown }: StepEditorProps) { const [isExpanded, setIsExpanded] = useState(true); const selectedHand = hands.find(h => h.name === step.handName); return (
{/* Header */}
{index + 1}
{onMoveUp && ( )} {onMoveDown && ( )}
{/* Expanded Content */} {isExpanded && (
{/* Step Name */}
onUpdate({ ...step, name: e.target.value || undefined })} placeholder={`${step.handName || '步骤'} ${index + 1}`} className="w-full px-2 py-1.5 text-sm border border-gray-200 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500" />
{/* Condition */}
onUpdate({ ...step, condition: e.target.value || undefined })} placeholder="例如: previous_result.success == true" className="w-full px-2 py-1.5 text-sm border border-gray-200 dark:border-gray-600 rounded-md bg-white dark:bg-gray-800 text-gray-900 dark:text-white focus:outline-none focus:ring-2 focus:ring-blue-500" />

使用表达式决定是否执行此步骤

{/* Parameters */}