diff --git a/src/components/Editor/IOEditor/InputValueEditor/InputValueEditor.tsx b/src/components/Editor/IOEditor/InputValueEditor/InputValueEditor.tsx index 476b27e24..60771b649 100644 --- a/src/components/Editor/IOEditor/InputValueEditor/InputValueEditor.tsx +++ b/src/components/Editor/IOEditor/InputValueEditor/InputValueEditor.tsx @@ -9,13 +9,14 @@ import { Icon } from "@/components/ui/icon"; import { BlockStack } from "@/components/ui/layout"; import { Heading, Paragraph } from "@/components/ui/typography"; import useConfirmationDialog from "@/hooks/useConfirmationDialog"; +import { useNodeManager } from "@/hooks/useNodeManager"; import { useNodeSelectionTransfer } from "@/hooks/useNodeSelectionTransfer"; import useToastNotification from "@/hooks/useToastNotification"; import { useComponentSpec } from "@/providers/ComponentSpecProvider"; import { useContextPanel } from "@/providers/ContextPanelProvider"; import { type InputSpec } from "@/utils/componentSpec"; import { checkInputConnectionToRequiredFields } from "@/utils/inputConnectionUtils"; -import { inputNameToNodeId } from "@/utils/nodes/nodeIdUtils"; +import { inputNameToInputId } from "@/utils/nodes/nodeIdUtils"; import { NameField, TextField, TypeField } from "./FormFields/FormFields"; import { checkNameCollision } from "./FormFields/utils"; @@ -30,6 +31,16 @@ export const InputValueEditor = ({ input, disabled = false, }: InputValueEditorProps) => { + const { getInputNodeId } = useNodeManager(); + + const inputNameToNodeId = useCallback( + (inputName: string): string => { + const inputId = inputNameToInputId(inputName); + return getInputNodeId(inputId); + }, + [getInputNodeId], + ); + const notify = useToastNotification(); const { transferSelection } = useNodeSelectionTransfer(inputNameToNodeId); const { componentSpec, setComponentSpec } = useComponentSpec(); diff --git a/src/components/Editor/IOEditor/OutputNameEditor/OutputNameEditor.tsx b/src/components/Editor/IOEditor/OutputNameEditor/OutputNameEditor.tsx index 8ce7e957b..afbae71dd 100644 --- a/src/components/Editor/IOEditor/OutputNameEditor/OutputNameEditor.tsx +++ b/src/components/Editor/IOEditor/OutputNameEditor/OutputNameEditor.tsx @@ -7,11 +7,11 @@ import { Icon } from "@/components/ui/icon"; import { BlockStack, InlineStack } from "@/components/ui/layout"; import { Heading, Paragraph } from "@/components/ui/typography"; import useConfirmationDialog from "@/hooks/useConfirmationDialog"; +import { useNodeManager } from "@/hooks/useNodeManager"; import { useNodeSelectionTransfer } from "@/hooks/useNodeSelectionTransfer"; import { useComponentSpec } from "@/providers/ComponentSpecProvider"; import { useContextPanel } from "@/providers/ContextPanelProvider"; import { type OutputSpec } from "@/utils/componentSpec"; -import { outputNameToNodeId } from "@/utils/nodes/nodeIdUtils"; import { type OutputConnectedDetails } from "../../utils/getOutputConnectedDetails"; import { updateOutputNameOnComponentSpec } from "../../utils/updateOutputNameOnComponentSpec"; @@ -29,6 +29,16 @@ export const OutputNameEditor = ({ disabled, connectedDetails, }: OutputNameEditorProps) => { + const { getOutputNodeId } = useNodeManager(); + + const outputNameToNodeId = useCallback( + (outputName: string): string => { + const outputId = outputNameToNodeId(outputName); + return getOutputNodeId(outputId); + }, + [getOutputNodeId], + ); + const { transferSelection } = useNodeSelectionTransfer(outputNameToNodeId); const { setComponentSpec, componentSpec } = useComponentSpec(); const { clearContent } = useContextPanel(); diff --git a/src/components/shared/ReactFlow/FlowCanvas/FlowCanvas.tsx b/src/components/shared/ReactFlow/FlowCanvas/FlowCanvas.tsx index 9cb281687..88fe62fbf 100644 --- a/src/components/shared/ReactFlow/FlowCanvas/FlowCanvas.tsx +++ b/src/components/shared/ReactFlow/FlowCanvas/FlowCanvas.tsx @@ -303,15 +303,23 @@ const FlowCanvas = ({ let updatedComponentSpec = { ...componentSpec }; for (const edge of params.edges) { - updatedComponentSpec = removeEdge(edge, updatedComponentSpec); + updatedComponentSpec = removeEdge( + edge, + updatedComponentSpec, + nodeManager, + ); } for (const node of params.nodes) { - updatedComponentSpec = removeNode(node, updatedComponentSpec); + updatedComponentSpec = removeNode( + node, + updatedComponentSpec, + nodeManager, + ); } setComponentSpec(updatedComponentSpec); }, - [componentSpec, setComponentSpec], + [componentSpec, nodeManager, setComponentSpec], ); const nodeCallbacks = useNodeCallbacks({ @@ -334,7 +342,11 @@ const FlowCanvas = ({ (connection: Connection) => { if (connection.source === connection.target) return; - const updatedGraphSpec = handleConnection(graphSpec, connection); + const updatedGraphSpec = handleConnection( + graphSpec, + connection, + nodeManager, + ); updateGraphSpec(updatedGraphSpec); }, [graphSpec, handleConnection, updateGraphSpec], @@ -374,7 +386,11 @@ const FlowCanvas = ({ ); if (existingInputEdge) { - newComponentSpec = removeEdge(existingInputEdge, newComponentSpec); + newComponentSpec = removeEdge( + existingInputEdge, + newComponentSpec, + nodeManager, + ); } const updatedComponentSpec = addAndConnectNode({ @@ -382,11 +398,18 @@ const FlowCanvas = ({ fromHandle, position, componentSpec: newComponentSpec, + nodeManager, }); setComponentSpec(updatedComponentSpec); }, - [reactFlowInstance, componentSpec, setComponentSpec, updateOrAddNodes], + [ + reactFlowInstance, + componentSpec, + nodeManager, + setComponentSpec, + updateOrAddNodes, + ], ); useEffect(() => { @@ -633,6 +656,7 @@ const FlowCanvas = ({ const updatedComponentSpec = updateNodePositions( updatedNodes, componentSpec, + nodeManager, ); setComponentSpec(updatedComponentSpec); } @@ -640,7 +664,7 @@ const FlowCanvas = ({ onNodesChange(changes); }, - [nodes, componentSpec, setComponentSpec, onNodesChange], + [nodes, componentSpec, nodeManager, setComponentSpec, onNodesChange], ); const handleBeforeDelete = async (params: NodesAndEdges) => { diff --git a/src/components/shared/ReactFlow/FlowCanvas/TaskNode/TaskNodeCard/Handles.tsx b/src/components/shared/ReactFlow/FlowCanvas/TaskNode/TaskNodeCard/Handles.tsx index 2d12ad9f7..17607355f 100644 --- a/src/components/shared/ReactFlow/FlowCanvas/TaskNode/TaskNodeCard/Handles.tsx +++ b/src/components/shared/ReactFlow/FlowCanvas/TaskNode/TaskNodeCard/Handles.tsx @@ -11,9 +11,14 @@ import { TooltipContent, TooltipTrigger, } from "@/components/ui/tooltip"; +import { useNodeManager } from "@/hooks/useNodeManager"; import { cn } from "@/lib/utils"; import { useTaskNode } from "@/providers/TaskNodeProvider"; import type { InputSpec, OutputSpec } from "@/utils/componentSpec"; +import { + inputNameToInputId, + outputNameToOutputId, +} from "@/utils/nodes/nodeIdUtils"; type InputHandleProps = { input: InputSpec; @@ -32,6 +37,7 @@ export const InputHandle = ({ onLabelClick, onHandleSelectionChange, }: InputHandleProps) => { + const { getInputNodeId } = useNodeManager(); const { nodeId, state } = useTaskNode(); const fromHandle = useConnection((connection) => connection.fromHandle?.id); @@ -44,7 +50,7 @@ export const InputHandle = ({ const [selected, setSelected] = useState(false); const [active, setActive] = useState(false); - const handleId = getInputHandleId(input.name); + const handleId = getInputNodeId(inputNameToInputId(input.name)); const missing = invalid ? "bg-red-700!" : "bg-gray-500!"; const hasValue = value !== undefined && value !== null; @@ -218,6 +224,7 @@ export const OutputHandle = ({ onLabelClick, onHandleSelectionChange, }: OutputHandleProps) => { + const { getOutputNodeId } = useNodeManager(); const { nodeId, state } = useTaskNode(); const fromHandle = useConnection((connection) => connection.fromHandle?.id); @@ -230,7 +237,7 @@ export const OutputHandle = ({ const [selected, setSelected] = useState(false); const [active, setActive] = useState(false); - const handleId = getOutputHandleId(output.name); + const handleId = getOutputNodeId(outputNameToOutputId(output.name)); const hasValue = value !== undefined && value !== "" && value !== null; const handleHandleClick = useCallback( @@ -355,14 +362,6 @@ export const OutputHandle = ({ ); }; -const getOutputHandleId = (outputName: string) => { - return `output_${outputName}`; -}; - -const getInputHandleId = (inputName: string) => { - return `input_${inputName}`; -}; - const skipHandleDeselect = (e: MouseEvent) => { let el = e.target as HTMLElement | null; while (el) { diff --git a/src/components/shared/ReactFlow/FlowCanvas/TaskNode/TaskNodeCard/TaskNodeInputs.tsx b/src/components/shared/ReactFlow/FlowCanvas/TaskNode/TaskNodeCard/TaskNodeInputs.tsx index 58eb9a3a6..aa5461944 100644 --- a/src/components/shared/ReactFlow/FlowCanvas/TaskNode/TaskNodeCard/TaskNodeInputs.tsx +++ b/src/components/shared/ReactFlow/FlowCanvas/TaskNode/TaskNodeCard/TaskNodeInputs.tsx @@ -2,6 +2,7 @@ import { useConnection } from "@xyflow/react"; import { AlertCircle } from "lucide-react"; import { type MouseEvent, useCallback, useEffect, useState } from "react"; +import { useNodeManager } from "@/hooks/useNodeManager"; import { cn } from "@/lib/utils"; import { useForcedSearchContext } from "@/providers/ComponentLibraryProvider/ForcedSearchProvider"; import { isValidFilterRequest } from "@/providers/ComponentLibraryProvider/types"; @@ -10,7 +11,7 @@ import { useTaskNode } from "@/providers/TaskNodeProvider"; import { inputsWithInvalidArguments } from "@/services/componentService"; import type { InputSpec } from "@/utils/componentSpec"; import { ComponentSearchFilter } from "@/utils/constants"; -import { inputNameToNodeId } from "@/utils/nodes/nodeIdUtils"; +import { inputNameToInputId } from "@/utils/nodes/nodeIdUtils"; import { checkArtifactMatchesSearchFilters } from "@/utils/searchUtils"; import { InputHandle } from "./Handles"; @@ -27,6 +28,7 @@ export function TaskNodeInputs({ expanded, onBackgroundClick, }: TaskNodeInputsProps) { + const { getInputNodeId } = useNodeManager(); const { inputs, taskSpec, state, select } = useTaskNode(); const { graphSpec } = useComponentSpec(); const { @@ -145,7 +147,7 @@ export function TaskNodeInputs({ } const input = inputs.find( - (i) => inputNameToNodeId(i.name) === fromHandle?.id, + (i) => getInputNodeId(inputNameToInputId(i.name)) === fromHandle?.id, ); if (!input) return; diff --git a/src/components/shared/ReactFlow/FlowCanvas/TaskNode/TaskNodeCard/TaskNodeOutputs.tsx b/src/components/shared/ReactFlow/FlowCanvas/TaskNode/TaskNodeCard/TaskNodeOutputs.tsx index f20907c0e..87d6ecf4c 100644 --- a/src/components/shared/ReactFlow/FlowCanvas/TaskNode/TaskNodeCard/TaskNodeOutputs.tsx +++ b/src/components/shared/ReactFlow/FlowCanvas/TaskNode/TaskNodeCard/TaskNodeOutputs.tsx @@ -1,13 +1,14 @@ import { useConnection, useEdges } from "@xyflow/react"; import { type MouseEvent, useCallback, useEffect, useState } from "react"; +import { useNodeManager } from "@/hooks/useNodeManager"; import { cn } from "@/lib/utils"; import { useForcedSearchContext } from "@/providers/ComponentLibraryProvider/ForcedSearchProvider"; import { isValidFilterRequest } from "@/providers/ComponentLibraryProvider/types"; import { useTaskNode } from "@/providers/TaskNodeProvider"; import type { OutputSpec } from "@/utils/componentSpec"; import { ComponentSearchFilter } from "@/utils/constants"; -import { outputNameToNodeId } from "@/utils/nodes/nodeIdUtils"; +import { outputNameToOutputId } from "@/utils/nodes/nodeIdUtils"; import { checkArtifactMatchesSearchFilters } from "@/utils/searchUtils"; import { OutputHandle } from "./Handles"; @@ -23,6 +24,7 @@ export function TaskNodeOutputs({ expanded, onBackgroundClick, }: TaskNodeOutputsProps) { + const { getOutputNodeId } = useNodeManager(); const { nodeId, outputs, state, select } = useTaskNode(); const { highlightSearchFilter, @@ -40,7 +42,8 @@ export function TaskNodeOutputs({ edges.some( (edge) => edge.source === nodeId && - edge.sourceHandle === outputNameToNodeId(output.name), + edge.sourceHandle === + getOutputNodeId(outputNameToOutputId(output.name)), ), ); @@ -138,7 +141,7 @@ export function TaskNodeOutputs({ } const output = outputs.find( - (o) => outputNameToNodeId(o.name) === fromHandle?.id, + (o) => getOutputNodeId(outputNameToOutputId(o.name)) === fromHandle?.id, ); if (!output) return; diff --git a/src/components/shared/ReactFlow/FlowCanvas/utils/addAndConnectNode.ts b/src/components/shared/ReactFlow/FlowCanvas/utils/addAndConnectNode.ts index 7d76f002b..dd2620758 100644 --- a/src/components/shared/ReactFlow/FlowCanvas/utils/addAndConnectNode.ts +++ b/src/components/shared/ReactFlow/FlowCanvas/utils/addAndConnectNode.ts @@ -1,17 +1,17 @@ import type { Connection, Handle } from "@xyflow/react"; -import type { - ComponentReference, - ComponentSpec, - TaskSpec, - TypeSpecType, +import type { NodeManager } from "@/nodeManager"; +import { + type ComponentReference, + type ComponentSpec, + isGraphImplementation, + type TaskSpec, + type TypeSpecType, } from "@/utils/componentSpec"; import { DEFAULT_NODE_DIMENSIONS } from "@/utils/constants"; import { - inputNameToNodeId, - nodeIdToTaskId, - outputNameToNodeId, - taskIdToNodeId, + inputNameToInputId, + outputNameToOutputId, } from "@/utils/nodes/nodeIdUtils"; import addTask from "./addTask"; @@ -22,6 +22,7 @@ type AddAndConnectNodeParams = { fromHandle: Handle | null; position: { x: number; y: number }; componentSpec: ComponentSpec; + nodeManager: NodeManager; }; export function addAndConnectNode({ @@ -29,6 +30,7 @@ export function addAndConnectNode({ fromHandle, position, componentSpec, + nodeManager, }: AddAndConnectNodeParams): ComponentSpec { // 1. Add the new node const taskSpec: TaskSpec = { @@ -36,7 +38,7 @@ export function addAndConnectNode({ componentRef: { ...componentRef }, }; - if (!("graph" in componentSpec.implementation)) { + if (!isGraphImplementation(componentSpec.implementation)) { return componentSpec; } @@ -58,7 +60,7 @@ export function addAndConnectNode({ ); // 2. Find the new node - if (!("graph" in newComponentSpec.implementation)) { + if (!isGraphImplementation(newComponentSpec.implementation)) { return newComponentSpec; } @@ -72,14 +74,17 @@ export function addAndConnectNode({ return newComponentSpec; } - const newNodeId = taskIdToNodeId(newTaskId); + const newNodeId = nodeManager.getNodeId(newTaskId, "task"); // 3. Determine the connection data type and find the first matching handle on the new node if (!fromHandle) { return newComponentSpec; } - const fromTaskId = nodeIdToTaskId(fromHandle.nodeId); + const fromTaskId = nodeManager.getTaskId(fromHandle.nodeId); + if (!fromTaskId) { + return newComponentSpec; + } const fromTaskSpec = graphSpec.tasks[fromTaskId]; const fromComponentSpec = fromTaskSpec?.componentRef.spec; @@ -112,7 +117,8 @@ export function addAndConnectNode({ return newComponentSpec; } - targetHandleId = inputNameToNodeId(handleName); + const inputId = inputNameToInputId(handleName); + targetHandleId = nodeManager.getNodeId(inputId, "input"); } else if (toHandleType === "output") { const handleName = componentRef.spec?.outputs?.find( (io) => io.type === connectionType, @@ -121,7 +127,8 @@ export function addAndConnectNode({ return newComponentSpec; } - targetHandleId = outputNameToNodeId(handleName); + const outputId = outputNameToOutputId(handleName); + targetHandleId = nodeManager.getNodeId(outputId, "output"); } // 4. Build a Connection object and use handleConnection to add the edge @@ -144,7 +151,11 @@ export function addAndConnectNode({ targetHandle: targetHandleId, }; - const updatedGraphSpec = handleConnection(graphSpec, connection); + const updatedGraphSpec = handleConnection( + graphSpec, + connection, + nodeManager, + ); return { ...newComponentSpec, diff --git a/src/components/shared/ReactFlow/FlowCanvas/utils/addTask.ts b/src/components/shared/ReactFlow/FlowCanvas/utils/addTask.ts index 180469b58..5a64729e0 100644 --- a/src/components/shared/ReactFlow/FlowCanvas/utils/addTask.ts +++ b/src/components/shared/ReactFlow/FlowCanvas/utils/addTask.ts @@ -9,6 +9,7 @@ import { type OutputSpec, type TaskSpec, } from "@/utils/componentSpec"; +import { taskNameToTaskId } from "@/utils/nodes/nodeIdUtils"; import { getUniqueInputName, getUniqueOutputName, @@ -61,11 +62,13 @@ const addTask = ( arguments: taskArguments ?? {}, }; - const taskId = getUniqueTaskName( + const uniqueTaskName = getUniqueTaskName( graphSpec, taskSpec.componentRef.spec?.name ?? "Task", ); + const taskId = taskNameToTaskId(uniqueTaskName); + const newGraphSpec: GraphSpec = { ...graphSpec, tasks: { @@ -78,9 +81,9 @@ const addTask = ( } if (taskType === "input") { - const inputId = getUniqueInputName(componentSpec); + const uniqueInputName = getUniqueInputName(componentSpec); const inputSpec: InputSpec = { - name: inputId, + name: uniqueInputName, annotations: positionAnnotations, }; const inputs = (componentSpec.inputs ?? []).concat([inputSpec]); @@ -89,9 +92,9 @@ const addTask = ( } if (taskType === "output") { - const outputId = getUniqueOutputName(componentSpec); + const uniqueOutputName = getUniqueOutputName(componentSpec); const outputSpec: OutputSpec = { - name: outputId, + name: uniqueOutputName, annotations: positionAnnotations, }; diff --git a/src/components/shared/ReactFlow/FlowCanvas/utils/duplicateNodes.ts b/src/components/shared/ReactFlow/FlowCanvas/utils/duplicateNodes.ts index 9ea20c512..d4240058e 100644 --- a/src/components/shared/ReactFlow/FlowCanvas/utils/duplicateNodes.ts +++ b/src/components/shared/ReactFlow/FlowCanvas/utils/duplicateNodes.ts @@ -16,12 +16,10 @@ import { createOutputNode } from "@/utils/nodes/createOutputNode"; import { createTaskNode } from "@/utils/nodes/createTaskNode"; import { getNodesBounds } from "@/utils/nodes/getNodesBounds"; import { - inputNameToNodeId, - nodeIdToInputName, - nodeIdToOutputName, - nodeIdToTaskId, - outputNameToNodeId, - taskIdToNodeId, + inputNameToInputId, + outputNameToOutputId, + taskIdToTaskName, + taskNameToTaskId, } from "@/utils/nodes/nodeIdUtils"; import { setPositionInAnnotations } from "@/utils/nodes/setPositionInAnnotations"; import { convertTaskCallbacksToNodeCallbacks } from "@/utils/nodes/taskCallbackUtils"; @@ -74,9 +72,16 @@ export const duplicateNodes = ( const oldNodeId = node.id; if (node.type === "task") { - const oldTaskId = nodeIdToTaskId(oldNodeId); - const newTaskId = getUniqueTaskName(graphSpec, oldTaskId); - const newNodeId = taskIdToNodeId(newTaskId); + const oldTaskId = nodeManager.getTaskId(oldNodeId); + if (!oldTaskId) { + console.warn("Could not find taskId for node:", node); + return; + } + + const oldTaskName = taskIdToTaskName(oldTaskId); + const newTaskName = getUniqueTaskName(graphSpec, oldTaskName); + const newTaskId = taskNameToTaskId(newTaskName); + const newNodeId = nodeManager.getNodeId(newTaskId, "task"); nodeIdMap[oldNodeId] = newNodeId; @@ -103,9 +108,12 @@ export const duplicateNodes = ( (input) => input.name === node.data.label, ); - const newInputId = getUniqueInputName(componentSpec, inputSpec?.name); - - const newNodeId = inputNameToNodeId(newInputId); + const uniqueInputName = getUniqueInputName( + componentSpec, + inputSpec?.name, + ); + const newInputId = inputNameToInputId(uniqueInputName); + const newNodeId = nodeManager.getNodeId(newInputId, "input"); nodeIdMap[oldNodeId] = newNodeId; @@ -118,19 +126,22 @@ export const duplicateNodes = ( const newInputSpec = { ...inputSpec, - name: newInputId, + name: uniqueInputName, annotations: updatedAnnotations, }; - newInputs[newInputId] = newInputSpec; + newInputs[uniqueInputName] = newInputSpec; // tbc if this should be uniqueInputName or newInputId } else if (node.type === "output") { const outputSpec = componentSpec.outputs?.find( (output) => output.name === node.data.label, ); - const newOutputId = getUniqueOutputName(componentSpec, outputSpec?.name); - - const newNodeId = outputNameToNodeId(newOutputId); + const uniqueOutputName = getUniqueOutputName( + componentSpec, + outputSpec?.name, + ); + const newOutputId = outputNameToOutputId(uniqueOutputName); + const newNodeId = nodeManager.getNodeId(newOutputId, "output"); nodeIdMap[oldNodeId] = newNodeId; @@ -143,11 +154,11 @@ export const duplicateNodes = ( const newOutputSpec = { ...outputSpec, - name: newOutputId, + name: uniqueOutputName, annotations: updatedAnnotations, }; - newOutputs[newOutputId] = newOutputSpec; + newOutputs[uniqueOutputName] = newOutputSpec; // tbc if this should be uniqueOutputName or newOutputId } }); @@ -173,6 +184,7 @@ export const duplicateNodes = ( nodesToDuplicate, componentSpec, connection, + nodeManager, ); } else { // If the Argument is not a TaskOutput or GraphInput, copy it over @@ -194,7 +206,7 @@ export const duplicateNodes = ( /* Reconfigure Outputs */ Object.entries(newOutputs).forEach((output) => { const [outputId] = output; - const newNodeId = outputNameToNodeId(outputId); + const newNodeId = nodeManager.getNodeId(outputId, "output"); const oldNodeId = Object.keys(nodeIdMap).find( (key) => nodeIdMap[key] === newNodeId, ); @@ -203,9 +215,9 @@ export const duplicateNodes = ( return; } - const oldOutputId = nodeIdToOutputName(oldNodeId); + const oldOutputId = nodeManager.getTaskId(oldNodeId); - if (!graphSpec.outputValues) { + if (!graphSpec.outputValues || !oldOutputId) { return; } @@ -226,9 +238,13 @@ export const duplicateNodes = ( ) { if ("taskOutput" in updatedOutputValue) { const oldTaskId = updatedOutputValue.taskOutput.taskId; - const oldTaskNodeId = taskIdToNodeId(oldTaskId); + const oldTaskNodeId = nodeManager.getNodeId(oldTaskId, "task"); if (oldTaskNodeId in nodeIdMap) { - const newTaskId = nodeIdToTaskId(nodeIdMap[oldTaskNodeId]); + const newTaskId = nodeManager.getTaskId(nodeIdMap[oldTaskNodeId]); + if (!newTaskId) { + return; + } + updatedOutputValue.taskOutput = { ...updatedOutputValue.taskOutput, taskId: newTaskId, @@ -279,10 +295,14 @@ export const duplicateNodes = ( return null; } - if (originalNode.type === "task") { - const newTaskId = nodeIdToTaskId(newNodeId); + const newId = nodeManager.getTaskId(newNodeId); - const newTaskSpec = updatedGraphSpec.tasks[newTaskId]; + if (!newId) { + return null; + } + + if (originalNode.type === "task") { + const newTaskSpec = updatedGraphSpec.tasks[newId]; const taskData = originalNode.data as TaskNodeData; const nodeData: NodeData = { @@ -292,7 +312,7 @@ export const duplicateNodes = ( nodeManager, }; - const newNode = createTaskNode([newTaskId, newTaskSpec], nodeData); + const newNode = createTaskNode([newId, newTaskSpec], nodeData); newNode.id = newNodeId; newNode.selected = false; @@ -309,9 +329,8 @@ export const duplicateNodes = ( return newNode; } else if (originalNode.type === "input") { - const newInputId = nodeIdToInputName(newNodeId); const newInputSpec = updatedInputs.find( - (input) => input.name === newInputId, + (input) => input.name === newId, ); if (!newInputSpec) { @@ -341,9 +360,8 @@ export const duplicateNodes = ( return newNode; } else if (originalNode.type === "output") { - const newOutputId = nodeIdToOutputName(newNodeId); const newOutputSpec = updatedOutputs.find( - (output) => output.name === newOutputId, + (output) => output.name === newId, ); if (!newOutputSpec) { @@ -394,9 +412,13 @@ export const duplicateNodes = ( y: node.position.y + offset.y, }; - if (node.type === "task") { - const taskId = nodeIdToTaskId(node.id); + const newId = nodeManager.getTaskId(node.id); + + if (!newId) { + return null; + } + if (node.type === "task") { const taskSpec = node.data.taskSpec as TaskSpec; const annotations = taskSpec.annotations || {}; @@ -410,11 +432,9 @@ export const duplicateNodes = ( annotations: updatedAnnotations, }; - updatedGraphSpec.tasks[taskId] = newTaskSpec; + updatedGraphSpec.tasks[newId] = newTaskSpec; } else if (node.type === "input") { - const inputId = nodeIdToInputName(node.id); - - const inputSpec = updatedInputs.find((input) => input.name === inputId); + const inputSpec = updatedInputs.find((input) => input.name === newId); if (!inputSpec) { return; @@ -433,17 +453,15 @@ export const duplicateNodes = ( }; const updatedInputIndex = updatedInputs.findIndex( - (input) => input.name === inputId, + (input) => input.name === newId, ); if (updatedInputIndex !== -1) { updatedInputs[updatedInputIndex] = newInputSpec; } } else if (node.type === "output") { - const outputId = nodeIdToOutputName(node.id); - const outputSpec = updatedOutputs.find( - (output) => output.name === outputId, + (output) => output.name === newId, ); if (!outputSpec) { @@ -463,7 +481,7 @@ export const duplicateNodes = ( }; const updatedOutputIndex = updatedOutputs.findIndex( - (output) => output.name === outputId, + (output) => output.name === newId, ); if (updatedOutputIndex !== -1) { @@ -496,14 +514,15 @@ function reconfigureConnections( nodes: Node[], componentSpec: ComponentSpec, mode: ConnectionMode, + nodeManager: NodeManager, ) { - let oldNodeId = undefined; + const oldNodeId = undefined; let newArgId = undefined; let isExternal = false; if ("taskOutput" in argument) { const oldTaskId = argument.taskOutput.taskId; - oldNodeId = taskIdToNodeId(oldTaskId); + const oldNodeId = nodeManager.getNodeId(oldTaskId, "task"); if (!isGraphImplementation(componentSpec.implementation)) { throw new Error("ComponentSpec does not contain a graph implementation."); @@ -518,12 +537,12 @@ function reconfigureConnections( return reconfigureExternalConnection(taskSpec, argKey, mode); } - const newTaskId = nodeIdToTaskId(newNodeId); + const newTaskId = nodeManager.getTaskId(newNodeId); newArgId = newTaskId; } else if ("graphInput" in argument) { const oldInputId = argument.graphInput.inputName; - oldNodeId = inputNameToNodeId(oldInputId); + const oldNodeId = nodeManager.getNodeId(oldInputId, "input"); if (!("inputs" in componentSpec)) { throw new Error("ComponentSpec does not contain inputs."); @@ -538,7 +557,7 @@ function reconfigureConnections( return reconfigureExternalConnection(taskSpec, argKey, mode); } - const newInputId = nodeIdToInputName(newNodeId); + const newInputId = nodeManager.getTaskId(newNodeId); newArgId = newInputId; } diff --git a/src/components/shared/ReactFlow/FlowCanvas/utils/handleConnection.ts b/src/components/shared/ReactFlow/FlowCanvas/utils/handleConnection.ts index fad0e0c97..b8f07b1a3 100644 --- a/src/components/shared/ReactFlow/FlowCanvas/utils/handleConnection.ts +++ b/src/components/shared/ReactFlow/FlowCanvas/utils/handleConnection.ts @@ -1,15 +1,12 @@ import type { Connection } from "@xyflow/react"; +import type { NodeManager } from "@/nodeManager"; import type { GraphInputArgument, GraphSpec, TaskOutputArgument, } from "@/utils/componentSpec"; -import { - nodeIdToInputName, - nodeIdToOutputName, - nodeIdToTaskId, -} from "@/utils/nodes/nodeIdUtils"; +import { inputIdToInputName } from "@/utils/nodes/nodeIdUtils"; import { setGraphOutputValue } from "./setGraphOutputValue"; import { setTaskArgument } from "./setTaskArgument"; @@ -17,44 +14,83 @@ import { setTaskArgument } from "./setTaskArgument"; export const handleConnection = ( graphSpec: GraphSpec, connection: Connection, + nodeManager: NodeManager, ) => { const targetTaskInputName = connection.targetHandle?.replace(/^input_/, ""); const sourceTaskOutputName = connection.sourceHandle?.replace(/^output_/, ""); + const targetId = nodeManager.getTaskId(connection.target); + const sourceId = nodeManager.getTaskId(connection.source); + if (sourceTaskOutputName !== undefined) { + if (!sourceId) { + console.error( + "addConnection: Could not find task ID for source node: ", + connection.source, + ); + return graphSpec; + } + const taskOutputArgument: TaskOutputArgument = { taskOutput: { - taskId: nodeIdToTaskId(connection.source), + taskId: sourceId, outputName: sourceTaskOutputName, }, }; if (targetTaskInputName !== undefined) { + if (!targetId) { + console.error( + "addConnection: Could not find Input ID for target node: ", + connection.target, + ); + return graphSpec; + } + return setTaskArgument( graphSpec, - nodeIdToTaskId(connection.target), + targetId, targetTaskInputName, taskOutputArgument, ); } else { - return setGraphOutputValue( - graphSpec, - nodeIdToOutputName(connection.target), - taskOutputArgument, - ); + if (!targetId) { + console.error( + "addConnection: Could not find Output ID for target node: ", + connection.target, + ); + return graphSpec; + } + + return setGraphOutputValue(graphSpec, targetId, taskOutputArgument); // TODO: Perhaps propagate type information } } else { - const graphInputName = nodeIdToInputName(connection.source); + if (!sourceId) { + console.error( + "addConnection: Could not find task ID for source node: ", + connection.source, + ); + return graphSpec; + } + const inputName = inputIdToInputName(sourceId); const graphInputArgument: GraphInputArgument = { graphInput: { - inputName: graphInputName, + inputName: inputName, }, }; if (targetTaskInputName !== undefined) { + if (!targetId) { + console.error( + "addConnection: Could not find Output ID for target node: ", + connection.target, + ); + return graphSpec; + } + return setTaskArgument( graphSpec, - nodeIdToTaskId(connection.target), + targetId, targetTaskInputName, graphInputArgument, ); diff --git a/src/components/shared/ReactFlow/FlowCanvas/utils/removeEdge.ts b/src/components/shared/ReactFlow/FlowCanvas/utils/removeEdge.ts index 5654807c8..8e18eab49 100644 --- a/src/components/shared/ReactFlow/FlowCanvas/utils/removeEdge.ts +++ b/src/components/shared/ReactFlow/FlowCanvas/utils/removeEdge.ts @@ -1,12 +1,17 @@ import type { Edge } from "@xyflow/react"; +import type { NodeManager } from "@/nodeManager"; import type { ComponentSpec, GraphImplementation } from "@/utils/componentSpec"; -import { nodeIdToOutputName, nodeIdToTaskId } from "@/utils/nodes/nodeIdUtils"; +import { outputIdToOutputName } from "@/utils/nodes/nodeIdUtils"; import { setGraphOutputValue } from "./setGraphOutputValue"; import { setTaskArgument } from "./setTaskArgument"; -export const removeEdge = (edge: Edge, componentSpec: ComponentSpec) => { +export const removeEdge = ( + edge: Edge, + componentSpec: ComponentSpec, + nodeManager: NodeManager, +) => { const graphSpec = (componentSpec.implementation as GraphImplementation) ?.graph; @@ -16,8 +21,10 @@ export const removeEdge = (edge: Edge, componentSpec: ComponentSpec) => { ...componentSpec, }; + const taskId = nodeManager.getTaskId(edge.target); + if (!taskId) return componentSpec; + if (inputName !== undefined && graphSpec) { - const taskId = nodeIdToTaskId(edge.target); const newGraphSpec = setTaskArgument(graphSpec, taskId, inputName); updatedComponentSpec.implementation = { ...updatedComponentSpec.implementation, @@ -26,7 +33,7 @@ export const removeEdge = (edge: Edge, componentSpec: ComponentSpec) => { return updatedComponentSpec; } else { - const outputName = nodeIdToOutputName(edge.target); + const outputName = outputIdToOutputName(taskId); const newGraphSpec = setGraphOutputValue(graphSpec, outputName); updatedComponentSpec.implementation = { ...updatedComponentSpec.implementation, diff --git a/src/components/shared/ReactFlow/FlowCanvas/utils/removeNode.ts b/src/components/shared/ReactFlow/FlowCanvas/utils/removeNode.ts index 923ea8744..327b9101c 100644 --- a/src/components/shared/ReactFlow/FlowCanvas/utils/removeNode.ts +++ b/src/components/shared/ReactFlow/FlowCanvas/utils/removeNode.ts @@ -1,31 +1,38 @@ import { type Node } from "@xyflow/react"; +import type { NodeManager } from "@/nodeManager"; import { type ComponentSpec, isGraphImplementation, } from "@/utils/componentSpec"; import { - nodeIdToInputName, - nodeIdToOutputName, - nodeIdToTaskId, + inputIdToInputName, + outputIdToOutputName, } from "@/utils/nodes/nodeIdUtils"; import { setGraphOutputValue } from "./setGraphOutputValue"; import { setTaskArgument } from "./setTaskArgument"; -export const removeNode = (node: Node, componentSpec: ComponentSpec) => { +export const removeNode = ( + node: Node, + componentSpec: ComponentSpec, + nodeManager: NodeManager, +) => { + const id = nodeManager.getTaskId(node.id); + + if (!id) return componentSpec; + if (node.type === "task") { - const taskId = nodeIdToTaskId(node.id); - return removeTask(taskId, componentSpec); + return removeTask(id, componentSpec); } if (node.type === "input") { - const inputName = nodeIdToInputName(node.id); + const inputName = inputIdToInputName(id); return removeGraphInput(inputName, componentSpec); } if (node.type === "output") { - const outputName = nodeIdToOutputName(node.id); + const outputName = outputIdToOutputName(id); return removeGraphOutput(outputName, componentSpec); } diff --git a/src/components/shared/ReactFlow/FlowCanvas/utils/replaceTaskNode.ts b/src/components/shared/ReactFlow/FlowCanvas/utils/replaceTaskNode.ts index e0b9cf89f..000089c97 100644 --- a/src/components/shared/ReactFlow/FlowCanvas/utils/replaceTaskNode.ts +++ b/src/components/shared/ReactFlow/FlowCanvas/utils/replaceTaskNode.ts @@ -6,6 +6,7 @@ import type { TaskSpec, } from "@/utils/componentSpec"; import { deepClone } from "@/utils/deepClone"; +import { taskNameToTaskId } from "@/utils/nodes/nodeIdUtils"; import { getUniqueTaskName } from "@/utils/unique"; export const replaceTaskNode = ( @@ -18,7 +19,8 @@ export const replaceTaskNode = ( const taskName = newComponentRef.spec?.name; const newTaskInputs = newComponentRef.spec?.inputs; const newTaskOutputs = newComponentRef.spec?.outputs; - const newTaskId = getUniqueTaskName(graphSpec, taskName); + const uniqueTaskName = getUniqueTaskName(graphSpec, taskName); + const newTaskId = taskNameToTaskId(uniqueTaskName); const oldTaskId = taskId; const oldTask = deepClone(updatedGraphSpec.tasks[oldTaskId]) as TaskSpec; diff --git a/src/components/shared/ReactFlow/FlowCanvas/utils/updateNodePosition.ts b/src/components/shared/ReactFlow/FlowCanvas/utils/updateNodePosition.ts index 9d1829176..12196b1f7 100644 --- a/src/components/shared/ReactFlow/FlowCanvas/utils/updateNodePosition.ts +++ b/src/components/shared/ReactFlow/FlowCanvas/utils/updateNodePosition.ts @@ -1,19 +1,20 @@ import type { Node } from "@xyflow/react"; +import type { NodeManager } from "@/nodeManager"; import { type ComponentSpec, isGraphImplementation, } from "@/utils/componentSpec"; import { - nodeIdToInputName, - nodeIdToOutputName, - nodeIdToTaskId, + inputIdToInputName, + outputIdToOutputName, } from "@/utils/nodes/nodeIdUtils"; import { setPositionInAnnotations } from "@/utils/nodes/setPositionInAnnotations"; export const updateNodePositions = ( updatedNodes: Node[], componentSpec: ComponentSpec, + nodeManager: NodeManager, ) => { const newComponentSpec = { ...componentSpec }; @@ -31,10 +32,13 @@ export const updateNodePositions = ( y: node.position.y, }; + const id = nodeManager.getTaskId(node.id); + + if (!id) continue; + if (node.type === "task") { - const taskId = nodeIdToTaskId(node.id); - if (updatedGraphSpec.tasks[taskId]) { - const taskSpec = { ...updatedGraphSpec.tasks[taskId] }; + if (updatedGraphSpec.tasks[id]) { + const taskSpec = { ...updatedGraphSpec.tasks[id] }; const annotations = taskSpec.annotations || {}; @@ -48,12 +52,12 @@ export const updateNodePositions = ( annotations: updatedAnnotations, }; - updatedGraphSpec.tasks[taskId] = newTaskSpec; + updatedGraphSpec.tasks[id] = newTaskSpec; newComponentSpec.implementation.graph = updatedGraphSpec; } } else if (node.type === "input") { - const inputName = nodeIdToInputName(node.id); + const inputName = inputIdToInputName(id); const inputs = [...(newComponentSpec.inputs || [])]; const inputIndex = inputs.findIndex((input) => input.name === inputName); @@ -73,7 +77,7 @@ export const updateNodePositions = ( newComponentSpec.inputs = inputs; } } else if (node.type === "output") { - const outputName = nodeIdToOutputName(node.id); + const outputName = outputIdToOutputName(id); const outputs = [...(newComponentSpec.outputs || [])]; const outputIndex = outputs.findIndex( (output) => output.name === outputName, diff --git a/src/hooks/useComponentSpecToEdges.ts b/src/hooks/useComponentSpecToEdges.ts index 48c9c3190..6510b06e7 100644 --- a/src/hooks/useComponentSpecToEdges.ts +++ b/src/hooks/useComponentSpecToEdges.ts @@ -6,34 +6,38 @@ import { } from "@xyflow/react"; import { useEffect } from "react"; -import type { - ArgumentType, - ComponentSpec, - GraphInputArgument, - GraphSpec, - TaskOutputArgument, - TaskSpec, +import { NodeManager } from "@/nodeManager"; +import { + type ArgumentType, + type ComponentSpec, + type GraphInputArgument, + type GraphSpec, + isGraphImplementation, + type TaskOutputArgument, + type TaskSpec, } from "@/utils/componentSpec"; import { - inputNameToNodeId, - outputNameToNodeId, - taskIdToNodeId, + inputNameToInputId, + outputNameToOutputId, } from "@/utils/nodes/nodeIdUtils"; +import { useNodeManager } from "./useNodeManager"; + const useComponentSpecToEdges = ( componentSpec: ComponentSpec, ): { edges: Edge[]; onEdgesChange: (changes: EdgeChange[]) => void; } => { + const { nodeManager } = useNodeManager(); const [flowEdges, setFlowEdges, onFlowEdgesChange] = useEdgesState( - getEdges(componentSpec), + getEdges(componentSpec, nodeManager), ); useEffect(() => { - const newEdges = getEdges(componentSpec); + const newEdges = getEdges(componentSpec, nodeManager); setFlowEdges(newEdges); - }, [componentSpec]); + }, [componentSpec, nodeManager]); return { edges: flowEdges, @@ -41,28 +45,35 @@ const useComponentSpecToEdges = ( }; }; -const getEdges = (componentSpec: ComponentSpec) => { - if (!("graph" in componentSpec.implementation)) { +const getEdges = (componentSpec: ComponentSpec, nodeManager: NodeManager) => { + if (!isGraphImplementation(componentSpec.implementation)) { return []; } const graphSpec = componentSpec.implementation.graph; - const taskEdges = createEdgesFromTaskSpec(graphSpec); - const outputEdges = createOutputEdgesFromGraphSpec(graphSpec); + const taskEdges = createEdgesFromTaskSpec(graphSpec, nodeManager); + const outputEdges = createOutputEdgesFromGraphSpec(graphSpec, nodeManager); return [...taskEdges, ...outputEdges]; }; -const createEdgesFromTaskSpec = (graphSpec: GraphSpec) => { +const createEdgesFromTaskSpec = ( + graphSpec: GraphSpec, + nodeManager: NodeManager, +) => { const edges: Edge[] = Object.entries(graphSpec.tasks).flatMap( - ([taskId, taskSpec]) => createEdgesForTask(taskId, taskSpec), + ([taskId, taskSpec]) => createEdgesForTask(taskId, taskSpec, nodeManager), ); return edges; }; -const createEdgesForTask = (taskId: string, taskSpec: TaskSpec): Edge[] => { +const createEdgesForTask = ( + taskId: string, + taskSpec: TaskSpec, + nodeManager: NodeManager, +): Edge[] => { return Object.entries(taskSpec.arguments ?? {}).flatMap( ([inputName, argument]) => - createEdgeForArgument(taskId, inputName, argument), + createEdgeForArgument(taskId, inputName, argument, nodeManager), ); }; @@ -70,17 +81,22 @@ const createEdgeForArgument = ( taskId: string, inputName: string, argument: ArgumentType, + nodeManager: NodeManager, ): Edge[] => { if (typeof argument === "string") { return []; } if ("taskOutput" in argument) { - return [createTaskOutputEdge(taskId, inputName, argument.taskOutput)]; + return [ + createTaskOutputEdge(taskId, inputName, argument.taskOutput, nodeManager), + ]; } if ("graphInput" in argument) { - return [createGraphInputEdge(taskId, inputName, argument.graphInput)]; + return [ + createGraphInputEdge(taskId, inputName, argument.graphInput, nodeManager), + ]; } console.error("Impossible task input argument kind: ", argument); @@ -91,13 +107,21 @@ const createTaskOutputEdge = ( taskId: string, inputName: string, taskOutput: TaskOutputArgument["taskOutput"], + nodeManager: NodeManager, ): Edge => { + const sourceNodeId = nodeManager.getNodeId(taskOutput.taskId, "task"); + const sourceOutputId = outputNameToOutputId(taskOutput.outputName); + const sourceHandleNodeId = nodeManager.getNodeId(sourceOutputId, "output"); + const targetNodeId = nodeManager.getNodeId(taskId, "task"); + const targetInputId = inputNameToInputId(inputName); + const targetHandleNodeId = nodeManager.getNodeId(targetInputId, "input"); + return { - id: `${taskOutput.taskId}_${taskOutput.outputName}-${taskId}_${inputName}`, - source: taskIdToNodeId(taskOutput.taskId), - sourceHandle: outputNameToNodeId(taskOutput.outputName), - target: taskIdToNodeId(taskId), - targetHandle: inputNameToNodeId(inputName), + id: `${taskOutput.taskId}_${sourceOutputId}-${taskId}_${targetInputId}`, + source: sourceNodeId, + sourceHandle: sourceHandleNodeId, + target: targetNodeId, + targetHandle: targetHandleNodeId, markerEnd: { type: MarkerType.Arrow }, type: "customEdge", }; @@ -107,27 +131,47 @@ const createGraphInputEdge = ( taskId: string, inputName: string, graphInput: GraphInputArgument["graphInput"], + nodeManager: NodeManager, ): Edge => { + const inputId = inputNameToInputId(graphInput.inputName); + const sourceNodeId = nodeManager.getNodeId(inputId, "input"); + const targetNodeId = nodeManager.getNodeId(taskId, "task"); + const targetInputId = inputNameToInputId(inputName); + const targetHandleNodeId = nodeManager.getNodeId(targetInputId, "input"); + return { - id: `Input_${graphInput.inputName}-${taskId}_${inputName}`, - source: inputNameToNodeId(graphInput.inputName), + id: `Input_${inputId}-${taskId}_${targetInputId}`, + source: sourceNodeId, sourceHandle: null, - target: taskIdToNodeId(taskId), - targetHandle: inputNameToNodeId(inputName), + target: targetNodeId, + targetHandle: targetHandleNodeId, type: "customEdge", markerEnd: { type: MarkerType.Arrow }, }; }; -const createOutputEdgesFromGraphSpec = (graphSpec: GraphSpec) => { +const createOutputEdgesFromGraphSpec = ( + graphSpec: GraphSpec, + nodeManager: NodeManager, +) => { const outputEdges: Edge[] = Object.entries(graphSpec.outputValues ?? {}).map( ([outputName, argument]) => { const taskOutput = argument.taskOutput; + + const sourceNodeId = nodeManager.getNodeId(taskOutput.taskId, "task"); + const sourceOutputId = outputNameToOutputId(taskOutput.outputName); + const sourceHandleNodeId = nodeManager.getNodeId( + sourceOutputId, + "output", + ); + const targetOutputId = outputNameToOutputId(outputName); + const targetNodeId = nodeManager.getNodeId(targetOutputId, "output"); + const edge: Edge = { - id: `${taskOutput.taskId}_${taskOutput.outputName}-Output_${outputName}`, - source: taskIdToNodeId(taskOutput.taskId), - sourceHandle: outputNameToNodeId(taskOutput.outputName), - target: outputNameToNodeId(outputName), + id: `${taskOutput.taskId}_${sourceOutputId}-Output_${targetOutputId}`, + source: sourceNodeId, + sourceHandle: sourceHandleNodeId, + target: targetNodeId, targetHandle: null, type: "customEdge", markerEnd: { type: MarkerType.Arrow }, diff --git a/src/providers/TaskNodeProvider.tsx b/src/providers/TaskNodeProvider.tsx index 4e7eed2db..b6426c8a5 100644 --- a/src/providers/TaskNodeProvider.tsx +++ b/src/providers/TaskNodeProvider.tsx @@ -3,6 +3,7 @@ import { type ReactNode, useCallback, useMemo } from "react"; import type { ContainerExecutionStatus } from "@/api/types.gen"; import useComponentFromUrl from "@/hooks/useComponentFromUrl"; +import { useNodeManager } from "@/hooks/useNodeManager"; import { useTaskNodeDimensions } from "@/hooks/useTaskNodeDimensions"; import useToastNotification from "@/hooks/useToastNotification"; import type { Annotations } from "@/types/annotations"; @@ -15,7 +16,6 @@ import type { TaskSpec, } from "@/utils/componentSpec"; import { getComponentName } from "@/utils/getComponentName"; -import { taskIdToNodeId } from "@/utils/nodes/nodeIdUtils"; import { createRequiredContext, @@ -71,10 +71,11 @@ export const TaskNodeProvider = ({ }: TaskNodeProviderProps) => { const notify = useToastNotification(); const reactFlowInstance = useReactFlow(); + const { getStableNodeId } = useNodeManager(); const taskSpec = data.taskSpec ?? ({} as TaskSpec); const taskId = data.taskId as string; - const nodeId = taskIdToNodeId(taskId); + const nodeId = getStableNodeId(taskId, "task"); const inputs = taskSpec.componentRef.spec?.inputs || []; const outputs = taskSpec.componentRef.spec?.outputs || []; diff --git a/src/utils/nodes/createInputNode.ts b/src/utils/nodes/createInputNode.ts index 0023266dc..baa2f4a77 100644 --- a/src/utils/nodes/createInputNode.ts +++ b/src/utils/nodes/createInputNode.ts @@ -4,12 +4,14 @@ import type { IONodeData, NodeData } from "@/types/nodes"; import type { InputSpec } from "../componentSpec"; import { extractPositionFromAnnotations } from "./extractPositionFromAnnotations"; +import { inputNameToInputId } from "./nodeIdUtils"; export const createInputNode = (input: InputSpec, nodeData: NodeData) => { const { name, annotations } = input; const { nodeManager, readOnly } = nodeData; - const nodeId = nodeManager?.getNodeId(name, "input"); + const inputId = inputNameToInputId(name); + const nodeId = nodeManager.getNodeId(inputId, "input"); console.log("Creating input node:", { name, nodeId }); const position = extractPositionFromAnnotations(annotations); diff --git a/src/utils/nodes/createOutputNode.ts b/src/utils/nodes/createOutputNode.ts index 3a32a60eb..81f7e575c 100644 --- a/src/utils/nodes/createOutputNode.ts +++ b/src/utils/nodes/createOutputNode.ts @@ -4,12 +4,14 @@ import type { IONodeData, NodeData } from "@/types/nodes"; import type { OutputSpec } from "../componentSpec"; import { extractPositionFromAnnotations } from "./extractPositionFromAnnotations"; +import { outputNameToOutputId } from "./nodeIdUtils"; export const createOutputNode = (output: OutputSpec, nodeData: NodeData) => { const { name, annotations } = output; const { nodeManager, readOnly } = nodeData; - const nodeId = nodeManager?.getNodeId(name, "output"); + const outputId = outputNameToOutputId(name); + const nodeId = nodeManager.getNodeId(outputId, "output"); console.log("Creating output node:", { name, nodeId }); const position = extractPositionFromAnnotations(annotations); diff --git a/src/utils/nodes/nodeIdUtils.ts b/src/utils/nodes/nodeIdUtils.ts index 676764383..5462ce0ba 100644 --- a/src/utils/nodes/nodeIdUtils.ts +++ b/src/utils/nodes/nodeIdUtils.ts @@ -1,43 +1,9 @@ -import type { NodeManager, NodeType } from "@/nodeManager"; - -// DEPRECATED: Legacy functions - use NodeManager instead -export const taskIdToNodeId = (taskId: string): string => `task_${taskId}`; // Legacy -export const inputNameToNodeId = (inputName: string): string => - `input_${inputName}`; // Legacy -export const outputNameToNodeId = (outputName: string): string => - `output_${outputName}`; // Legacy - -// RENAMED: For backwards compatibility and clarity -export const inputNameToInputId = (inputName: string): string => inputName; // 1:1 mapping -export const outputNameToOutputId = (outputName: string): string => outputName; // 1:1 mapping -export const inputIdToInputName = (inputId: string): string => inputId; // 1:1 mapping -export const outputIdToOutputName = (outputId: string): string => outputId; // 1:1 mapping - -// LEGACY: Keep for backwards compatibility -export const nodeIdToTaskId = (nodeId: string): string => { - return nodeId.replace(/^task_/, ""); -}; - -export const nodeIdToInputName = (nodeId: string): string => { - return nodeId.replace(/^input_/, ""); -}; - -export const nodeIdToOutputName = (nodeId: string): string => { - return nodeId.replace(/^output_/, ""); -}; - -// NEW: NodeManager-aware functions -export const getTaskIdFromNodeId = ( - nodeId: string, - nodeManager: NodeManager, -): string | undefined => { - return nodeManager.getTaskId(nodeId); -}; - -export const getStableNodeId = ( - taskId: string, - nodeType: NodeType, - nodeManager: NodeManager, -): string => { - return nodeManager.getNodeId(taskId, nodeType); -}; +/* Conversions between names and IDs for tasks, inputs, and outputs. */ +/* Conversion between IDs and ReactFlow NodeId is done via the Node Manager. */ + +export const taskNameToTaskId = (taskName: string): string => taskName; +export const inputNameToInputId = (inputName: string): string => inputName; +export const outputNameToOutputId = (outputName: string): string => outputName; +export const taskIdToTaskName = (taskId: string): string => taskId; +export const inputIdToInputName = (inputId: string): string => inputId; +export const outputIdToOutputName = (outputId: string): string => outputId;