diff --git a/src/components/nodes/ArrayNode.tsx b/src/components/nodes/ArrayNode.tsx index d509a8a1..00e61af4 100644 --- a/src/components/nodes/ArrayNode.tsx +++ b/src/components/nodes/ArrayNode.tsx @@ -1,6 +1,6 @@ "use client"; -import { useCallback, useEffect, useMemo, useRef } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { Handle, Node, NodeProps, Position, useReactFlow } from "@xyflow/react"; import { BaseNode } from "./BaseNode"; import { useCommentNavigation } from "@/hooks/useCommentNavigation"; @@ -24,25 +24,38 @@ export function ArrayNode({ id, data, selected }: NodeProps) { const updateNodeData = useWorkflowStore((state) => state.updateNodeData); const addNode = useWorkflowStore((state) => state.addNode); const onConnect = useWorkflowStore((state) => state.onConnect); - const nodes = useWorkflowStore((state) => state.nodes); - const getConnectedInputs = useWorkflowStore((state) => state.getConnectedInputs); - const edges = useWorkflowStore((state) => state.edges); - const { setNodes } = useReactFlow(); - const lastSyncedInputRef = useRef(null); - const lastDerivedWriteRef = useRef(null); - - const hasIncomingTextConnection = useMemo(() => { - return edges.some((edge) => { + const hasIncomingTextConnection = useWorkflowStore((state) => + state.edges.some((edge) => { + if (edge.target !== id) return false; + const handle = edge.targetHandle || "text"; + return handle === "text" || handle.startsWith("text-") || handle.includes("prompt"); + }) + ); + const connectedText = useWorkflowStore((state) => { + const hasIncoming = state.edges.some((edge) => { if (edge.target !== id) return false; const handle = edge.targetHandle || "text"; return handle === "text" || handle.startsWith("text-") || handle.includes("prompt"); }); - }, [edges, id]); + if (!hasIncoming) return null; + return state.getConnectedInputs(id).text; + }); + const { setNodes, getNodes } = useReactFlow(); + const lastSyncedInputRef = useRef(null); + const lastDerivedWriteRef = useRef(null); + const [showAdvanced, setShowAdvanced] = useState(false); // Pull upstream text into this node whenever the connected input changes. useEffect(() => { - if (!hasIncomingTextConnection) return; - const { text } = getConnectedInputs(id); + if (!hasIncomingTextConnection) { + // Array node has no manual input field; clear stale upstream text on disconnect. + if (nodeData.inputText !== null && nodeData.inputText !== "") { + lastSyncedInputRef.current = null; + updateNodeData(id, { inputText: null }); + } + return; + } + const text = connectedText; if ( text !== null && text !== nodeData.inputText && @@ -51,7 +64,7 @@ export function ArrayNode({ id, data, selected }: NodeProps) { lastSyncedInputRef.current = text; updateNodeData(id, { inputText: text }); } - }, [hasIncomingTextConnection, getConnectedInputs, id, nodeData.inputText, updateNodeData]); + }, [connectedText, hasIncomingTextConnection, id, nodeData.inputText, updateNodeData]); const parsed = useMemo(() => { return parseTextToArray(nodeData.inputText, { @@ -83,20 +96,14 @@ export function ArrayNode({ id, data, selected }: NodeProps) { if (lastDerivedWriteRef.current === writeSignature) return; lastDerivedWriteRef.current = writeSignature; - if ( - parsed.error !== nodeData.error || - nextOutputText !== (nodeData.outputText ?? "[]") || - !arraysEqual(parsed.items, nodeData.outputItems || []) - ) { - updateNodeData(id, { - outputItems: parsed.items, - outputText: nextOutputText, - error: parsed.error, - }); - } + updateNodeData(id, { + outputItems: parsed.items, + outputText: nextOutputText, + error: parsed.error, + }); }, [id, nodeData.error, nodeData.outputItems, nodeData.outputText, parsed.error, parsed.items, updateNodeData]); - const handleModeChange = useCallback( + const handleBasicModeChange = useCallback( (e: React.ChangeEvent) => { updateNodeData(id, { splitMode: e.target.value as ArrayNodeData["splitMode"] }); }, @@ -109,7 +116,7 @@ export function ArrayNode({ id, data, selected }: NodeProps) { const items = previewItems; if (items.length === 0) return; - const sourceNode = nodes.find((n) => n.id === id); + const sourceNode = getNodes().find((n) => n.id === id); if (!sourceNode) return; const sourceWidth = (sourceNode.style?.width as number) || 360; @@ -137,7 +144,7 @@ export function ArrayNode({ id, data, selected }: NodeProps) { }); updateNodeData(id, { selectedOutputIndex: previousSelected }); - }, [addNode, id, nodeData.selectedOutputIndex, nodes, onConnect, previewItems, updateNodeData]); + }, [addNode, getNodes, id, nodeData.selectedOutputIndex, onConnect, previewItems, updateNodeData]); // Reset selection if it no longer points to a valid parsed item. useEffect(() => { @@ -196,13 +203,12 @@ export function ArrayNode({ id, data, selected }: NodeProps) {
@@ -218,37 +224,66 @@ export function ArrayNode({ id, data, selected }: NodeProps) { )} - {nodeData.splitMode === "regex" && ( -
- - updateNodeData(id, { regexPattern: e.target.value })} - placeholder="/\\n+/" - className="nodrag nopan bg-neutral-900 border border-neutral-700 rounded px-2 py-1 text-[11px] text-neutral-100 focus:outline-none focus:ring-1 focus:ring-neutral-600" - /> -
- )} +
+ -
- - + {showAdvanced && ( +
+ + + {nodeData.splitMode === "regex" && ( +
+ + updateNodeData(id, { regexPattern: e.target.value })} + placeholder="/\\n+/" + className="nodrag nopan bg-neutral-900 border border-neutral-700 rounded px-2 py-1 text-[11px] text-neutral-100 focus:outline-none focus:ring-1 focus:ring-neutral-600" + /> +
+ )} + +
+ + +
+
+ )}