"use client"; import * as React from "react"; import { Handle, Position } from "@xyflow/react"; import type { NodeProps } from "@xyflow/react"; import type { ReactNode } from "react"; // Custom node component with handles for connections export function CustomNode({ data, selected }: NodeProps) { const nodeType = data.nodeType; const isRunning = data.isRunning; const executionStatus = data.executionStatus; // Note node state - MUST be declared before any conditional returns // This ensures hooks are called in the same order every render const noteText = String((data as any).noteText || 'Double-click to edit note'); const [isEditing, setIsEditing] = React.useState(false); const [editText, setEditText] = React.useState(noteText); const textareaRef = React.useRef(null); // Determine border and background based on state const getBorderStyle = () => { // Note nodes have no border if (nodeType === 'note') return 'none'; if (isRunning) return '1px solid #22C55E'; if (executionStatus === 'completed') return '1px solid #9ca3af'; if (executionStatus === 'failed') return '1px solid #eb3424'; if (selected) return '1px solid #22C55E'; return '1px solid #e5e7eb'; }; const getBackgroundColor = () => { // Note nodes get yellow/gold background if (nodeType === 'note') return '#ca8a04'; // All nodes have white background return 'white'; }; const getOutlineStyle = () => { if (isRunning) return '2px solid rgba(34, 197, 94, 0.32)'; if (selected) return '2px solid rgba(24, 24, 27, 0.18)'; return '2px solid transparent'; }; // Note nodes have different styling const isNoteNode = nodeType === 'note'; // Determine text color based on background const getTextColor = () => { if (isNoteNode) return '#854d0e'; // Dark yellow text for note nodes if (nodeType === 'if-else' || nodeType === 'while') { return '#18181b'; // Dark text for orange background nodes } return '#18181b'; // Default dark text }; // Update editText when noteText changes (for different notes) React.useEffect(() => { if (isNoteNode) { setEditText(noteText); } }, [noteText, isNoteNode]); // Note nodes are visual-only sticky notes with inline editing if (isNoteNode) { React.useEffect(() => { if (isEditing && textareaRef.current) { textareaRef.current.focus(); textareaRef.current.select(); } }, [isEditing]); const handleSave = () => { setIsEditing(false); // Update the node data if ((data as any).onUpdate) { (data as any).onUpdate({ noteText: editText }); } }; return (
{ e.stopPropagation(); setIsEditing(true); }} style={{ padding: '10px', fontSize: '11px', backgroundColor: '#fef9c3', // Light yellow border: selected ? '2px solid #eab308' : 'none', outline: selected ? '2px solid rgba(234, 179, 8, 0.2)' : 'none', outlineOffset: 0, boxShadow: '0 2px 8px rgba(0, 0, 0, 0.08)', transition: 'all 0.2s ease-out', borderRadius: '6px', minWidth: '140px', maxWidth: '200px', width: 'fit-content', fontFamily: 'ui-sans-serif, system-ui, sans-serif', color: '#854d0e', lineHeight: '1.4', whiteSpace: 'pre-wrap', wordBreak: 'break-word', cursor: isEditing ? 'text' : 'move', }} > {/* Sticky note "tape" effect */}
{isEditing ? (