import { useCallback, useState } from 'react'; import { AlertCircle, FunctionSquare, Plus, Trash2, X } from 'lucide-react'; import type { InvokeFunctionConfig, WorkflowNode, WorkflowNodeConfig, WorkflowValidationIssue, } from '@shared/domain/workflow'; interface InvokeFunctionInspectorProps { node: WorkflowNode; validationIssues?: WorkflowValidationIssue[]; onNodeChange: (nodeId: string, patch: Partial) => void; onNodeConfigChange: (nodeId: string, config: WorkflowNodeConfig) => void; onNodeRemove: (nodeId: string) => void; } function InputField({ label, value, onChange, placeholder, }: { label: string; value: string; onChange: (value: string) => void; placeholder?: string; }) { const base = 'w-full rounded-lg border border-[var(--color-border)] bg-[var(--color-surface-1)] px-3 py-2 text-[13px] text-[var(--color-text-primary)] placeholder-[var(--color-text-muted)] outline-none transition focus:border-[var(--color-accent)]/50'; return ( ); } function stringifyValue(v: unknown): string { if (typeof v === 'string') return v; return JSON.stringify(v) ?? ''; } export function InvokeFunctionInspector({ node, validationIssues, onNodeChange, onNodeConfigChange, onNodeRemove, }: InvokeFunctionInspectorProps) { const config = node.config as InvokeFunctionConfig; const nodeIssues = validationIssues?.filter((i) => i.nodeId === node.id) ?? []; const args = config.arguments ?? {}; const argEntries = Object.entries(args); const [newKey, setNewKey] = useState(''); const patchConfig = useCallback( (patch: Partial) => { onNodeConfigChange(node.id, { ...config, ...patch }); }, [node.id, config, onNodeConfigChange], ); const handleArgChange = useCallback( (oldKey: string, newArgKey: string, value: string) => { const next = { ...args }; if (newArgKey !== oldKey) { delete next[oldKey]; } next[newArgKey] = value; patchConfig({ arguments: next }); }, [args, patchConfig], ); const handleArgRemove = useCallback( (key: string) => { const next = { ...args }; delete next[key]; patchConfig({ arguments: next }); }, [args, patchConfig], ); const handleArgAdd = useCallback(() => { const key = newKey.trim() || `arg${argEntries.length + 1}`; patchConfig({ arguments: { ...args, [key]: '' } }); setNewKey(''); }, [newKey, argEntries.length, args, patchConfig]); return (
{/* Header */}
{node.label || 'Function Tool'}
{/* Label */} onNodeChange(node.id, { label: v })} placeholder="Display label" value={node.label} /> {/* Function name */} patchConfig({ functionName: v })} placeholder="e.g. GetUserData" value={config.functionName} /> {/* Result variable */}
patchConfig({ resultVariable: v || undefined })} placeholder="e.g. Local.result" value={config.resultVariable ?? ''} />

State path where the return value is stored for use in subsequent steps.

{/* Require approval */}

Pause and require human confirmation before invoking this function.

{/* Arguments editor */}
Arguments
{argEntries.length === 0 && (

No arguments defined.

)} {argEntries.map(([key, value]) => (
handleArgChange(key, e.target.value, stringifyValue(value))} placeholder="key" value={key} /> handleArgChange(key, key, e.target.value)} placeholder="value" value={stringifyValue(value)} />
))}
{/* Validation issues */} {nodeIssues.length > 0 && (
{nodeIssues.map((issue, i) => (
{issue.message}
))}
)}
); }