'use client'; import { memo, useState } from 'react'; import { Handle, Position, NodeProps } from 'reactflow'; import { Input } from '@/components/ui/input'; import { Switch } from '@/components/ui/switch'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '@/components/ui/select'; import { Trash2 } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { getNodeDefinition, CATEGORY_COLORS, PORT_COLORS, NodePort, } from '@/lib/visual-scripting/node-definitions'; import { NodeData } from '@/lib/visual-scripting/code-generator'; import { useVisualScriptStore } from '@/stores/visual-script-store'; export const CustomNode = memo(({ id, data, selected }: NodeProps) => { const definition = getNodeDefinition(data.type); const { updateNodeValue, removeNode } = useVisualScriptStore(); const [isHovered, setIsHovered] = useState(false); if (!definition) { return (
Unknown node: {data.type}
); } const handleValueChange = (key: string, value: any) => { updateNodeValue(id, key, value); }; // Filter out flow inputs for the input section const editableInputs = definition.inputs.filter( (input) => input.type !== 'flow' ); return (
setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} > {/* Header */}
{definition.category.charAt(0).toUpperCase()}
{definition.label}
{isHovered && ( )}
{/* Input Handles (Left Side) */}
{definition.inputs.map((input, index) => (
0 ? 45 : 20) + index * 28 + (input.type === 'flow' ? 0 : editableInputs.indexOf(input) * 36) }px`, }} isConnectable={true} /> {input.type === 'flow' && (
{input.name}
)}
))}
{/* Editable Inputs */} {editableInputs.length > 0 && (
{editableInputs.map((input) => ( handleValueChange(input.id, value)} /> ))}
)} {/* Output Handles (Right Side) */}
{definition.outputs.map((output, index) => (
{output.name}
))}
{/* Spacer for outputs */}
); }); CustomNode.displayName = 'CustomNode'; // Input field component for node properties function NodeInput({ input, value, onChange, }: { input: NodePort; value: any; onChange: (value: any) => void; }) { switch (input.type) { case 'number': return (
onChange(parseFloat(e.target.value) || 0)} className="h-7 text-xs" />
); case 'string': if (input.id === 'operation') { return (
); } if (input.id === 'comparison') { return (
); } if (input.id === 'key') { return (
); } if (input.id === 'service') { return (
); } return (
onChange(e.target.value)} className="h-7 text-xs" placeholder={input.name} />
); case 'boolean': return (
); default: return (
onChange(e.target.value)} className="h-7 text-xs" placeholder={`${input.name} (${input.type})`} />
); } }