wwf
3 天以前 a430284aa21e3ae1f0d5654e55b2ad2852519cc2
app/components/workflow/hooks/use-nodes-interactions.ts
@@ -29,8 +29,6 @@
  CUSTOM_EDGE,
  ITERATION_CHILDREN_Z_INDEX,
  ITERATION_PADDING,
  LOOP_CHILDREN_Z_INDEX,
  LOOP_PADDING,
  NODES_INITIAL_DATA,
  NODE_WIDTH_X_OFFSET,
  X_OFFSET,
@@ -39,18 +37,14 @@
import {
  genNewNodeTitleFromOld,
  generateNewNode,
  getNodeCustomTypeByNodeDataType,
  getNodesConnectedSourceOrTargetHandleIdsMap,
  getTopLeftNodePosition,
} from '../utils'
import { CUSTOM_NOTE_NODE } from '../note-node/constants'
import type { IterationNodeType } from '../nodes/iteration/types'
import type { LoopNodeType } from '../nodes/loop/types'
import { CUSTOM_ITERATION_START_NODE } from '../nodes/iteration-start/constants'
import { CUSTOM_LOOP_START_NODE } from '../nodes/loop-start/constants'
import type { VariableAssignerNodeType } from '../nodes/variable-assigner/types'
import { useNodeIterationInteractions } from '../nodes/iteration/use-interactions'
import { useNodeLoopInteractions } from '../nodes/loop/use-interactions'
import { useWorkflowHistoryStore } from '../workflow-history-store'
import { useNodesSyncDraft } from './use-nodes-sync-draft'
import { useHelpline } from './use-helpline'
@@ -79,10 +73,6 @@
    handleNodeIterationChildDrag,
    handleNodeIterationChildrenCopy,
  } = useNodeIterationInteractions()
  const {
    handleNodeLoopChildDrag,
    handleNodeLoopChildrenCopy,
  } = useNodeLoopInteractions()
  const dragNodeStartPosition = useRef({ x: 0, y: 0 } as { x: number; y: number })
  const { saveStateToHistory, undo, redo } = useWorkflowHistory()
@@ -96,9 +86,6 @@
    if (node.type === CUSTOM_ITERATION_START_NODE || node.type === CUSTOM_NOTE_NODE)
      return
    if (node.type === CUSTOM_LOOP_START_NODE || node.type === CUSTOM_NOTE_NODE)
      return
    dragNodeStartPosition.current = { x: node.position.x, y: node.position.y }
  }, [workflowStore, getNodesReadOnly])
@@ -107,9 +94,6 @@
      return
    if (node.type === CUSTOM_ITERATION_START_NODE)
      return
    if (node.type === CUSTOM_LOOP_START_NODE)
      return
    const {
@@ -121,7 +105,6 @@
    const nodes = getNodes()
    const { restrictPosition } = handleNodeIterationChildDrag(node)
    const { restrictPosition: restrictLoopPosition } = handleNodeLoopChildDrag(node)
    const {
      showHorizontalHelpLineNodes,
@@ -137,8 +120,6 @@
        currentNode.position.x = showVerticalHelpLineNodes[0].position.x
      else if (restrictPosition.x !== undefined)
        currentNode.position.x = restrictPosition.x
      else if (restrictLoopPosition.x !== undefined)
        currentNode.position.x = restrictLoopPosition.x
      else
        currentNode.position.x = node.position.x
@@ -146,13 +127,12 @@
        currentNode.position.y = showHorizontalHelpLineNodes[0].position.y
      else if (restrictPosition.y !== undefined)
        currentNode.position.y = restrictPosition.y
      else if (restrictLoopPosition.y !== undefined)
        currentNode.position.y = restrictLoopPosition.y
      else
        currentNode.position.y = node.position.y
    })
    setNodes(newNodes)
  }, [getNodesReadOnly, store, handleNodeIterationChildDrag, handleNodeLoopChildDrag, handleSetHelpline])
  }, [store, getNodesReadOnly, handleSetHelpline, handleNodeIterationChildDrag])
  const handleNodeDragStop = useCallback<NodeDragHandler>((_, node) => {
    const {
@@ -181,9 +161,6 @@
      return
    if (node.type === CUSTOM_NOTE_NODE || node.type === CUSTOM_ITERATION_START_NODE)
      return
    if (node.type === CUSTOM_LOOP_START_NODE || node.type === CUSTOM_NOTE_NODE)
      return
    const {
@@ -260,9 +237,6 @@
    if (node.type === CUSTOM_NOTE_NODE || node.type === CUSTOM_ITERATION_START_NODE)
      return
    if (node.type === CUSTOM_NOTE_NODE || node.type === CUSTOM_LOOP_START_NODE)
      return
    const {
      setEnteringNodePayload,
    } = workflowStore.getState()
@@ -337,8 +311,6 @@
  const handleNodeClick = useCallback<NodeMouseHandler>((_, node) => {
    if (node.type === CUSTOM_ITERATION_START_NODE)
      return
    if (node.type === CUSTOM_LOOP_START_NODE)
      return
    handleNodeSelect(node.id)
  }, [handleNodeSelect])
@@ -372,10 +344,6 @@
    if (edges.find(edge => edge.source === source && edge.sourceHandle === sourceHandle && edge.target === target && edge.targetHandle === targetHandle))
      return
    const parendNode = nodes.find(node => node.id === targetNode?.parentId)
    const isInIteration = parendNode && parendNode.data.type === BlockEnum.Iteration
    const isInLoop = !!parendNode && parendNode.data.type === BlockEnum.Loop
    const newEdge = {
      id: `${source}-${sourceHandle}-${target}-${targetHandle}`,
      type: CUSTOM_EDGE,
@@ -386,12 +354,10 @@
      data: {
        sourceType: nodes.find(node => node.id === source)!.data.type,
        targetType: nodes.find(node => node.id === target)!.data.type,
        isInIteration,
        iteration_id: isInIteration ? targetNode?.parentId : undefined,
        isInLoop,
        loop_id: isInLoop ? targetNode?.parentId : undefined,
        isInIteration: !!targetNode?.parentId,
        iteration_id: targetNode?.parentId,
      },
      zIndex: targetNode?.parentId ? (isInIteration ? ITERATION_CHILDREN_Z_INDEX : LOOP_CHILDREN_Z_INDEX) : 0,
      zIndex: targetNode?.parentId ? ITERATION_CHILDREN_Z_INDEX : 0,
    }
    const nodesConnectedSourceOrTargetHandleIdsMap = getNodesConnectedSourceOrTargetHandleIdsMap(
      [
@@ -588,45 +554,6 @@
        }
      }
    }
    if (currentNode.data.type === BlockEnum.Loop) {
      const loopChildren = nodes.filter(node => node.parentId === currentNode.id)
      if (loopChildren.length) {
        if (currentNode.data._isBundled) {
          loopChildren.forEach((child) => {
            handleNodeDelete(child.id)
          })
          return handleNodeDelete(nodeId)
        }
        else {
          if (loopChildren.length === 1) {
            handleNodeDelete(loopChildren[0].id)
            handleNodeDelete(nodeId)
            return
          }
          const { setShowConfirm, showConfirm } = workflowStore.getState()
          if (!showConfirm) {
            setShowConfirm({
              title: t('workflow.nodes.loop.deleteTitle'),
              desc: t('workflow.nodes.loop.deleteDesc') || '',
              onConfirm: () => {
                loopChildren.forEach((child) => {
                  handleNodeDelete(child.id)
                })
                handleNodeDelete(nodeId)
                handleSyncWorkflowDraft()
                setShowConfirm(undefined)
              },
            })
            return
          }
        }
      }
    }
    const connectedEdges = getConnectedEdges([{ id: nodeId } as Node], edges)
    const nodesConnectedSourceOrTargetHandleIdsMap = getNodesConnectedSourceOrTargetHandleIdsMap(connectedEdges.map(edge => ({ type: 'remove', edge })), nodes)
    const newNodes = produce(nodes, (draft: Node[]) => {
@@ -639,7 +566,7 @@
        }
        if (node.id === currentNode.parentId)
          node.data._children = node.data._children?.filter(child => child.nodeId !== nodeId)
          node.data._children = node.data._children?.filter(child => child !== nodeId)
      })
      draft.splice(currentNodeIndex, 1)
    })
@@ -685,9 +612,7 @@
    const {
      newNode,
      newIterationStartNode,
      newLoopStartNode,
    } = generateNewNode({
      type: getNodeCustomTypeByNodeDataType(nodeType),
      data: {
        ...NODES_INITIAL_DATA[nodeType],
        title: nodesWithSameType.length > 0 ? `${t(`workflow.blocks.${nodeType}`)} ${nodesWithSameType.length + 1}` : t(`workflow.blocks.${nodeType}`),
@@ -715,28 +640,13 @@
      }
      newNode.parentId = prevNode.parentId
      newNode.extent = prevNode.extent
      const parentNode = nodes.find(node => node.id === prevNode.parentId) || null
      const isInIteration = !!parentNode && parentNode.data.type === BlockEnum.Iteration
      const isInLoop = !!parentNode && parentNode.data.type === BlockEnum.Loop
      if (prevNode.parentId) {
        newNode.data.isInIteration = isInIteration
        newNode.data.isInLoop = isInLoop
        if (isInIteration) {
          newNode.data.iteration_id = parentNode.id
          newNode.zIndex = ITERATION_CHILDREN_Z_INDEX
        }
        if (isInLoop) {
          newNode.data.loop_id = parentNode.id
          newNode.zIndex = LOOP_CHILDREN_Z_INDEX
        }
        if (isInIteration && (newNode.data.type === BlockEnum.Answer || newNode.data.type === BlockEnum.Tool || newNode.data.type === BlockEnum.Assigner)) {
          const iterNodeData: IterationNodeType = parentNode.data
          iterNodeData._isShowTips = true
        }
        if (isInLoop && (newNode.data.type === BlockEnum.Answer || newNode.data.type === BlockEnum.Tool || newNode.data.type === BlockEnum.Assigner)) {
          const iterNodeData: IterationNodeType = parentNode.data
        newNode.data.isInIteration = true
        newNode.data.iteration_id = prevNode.parentId
        newNode.zIndex = ITERATION_CHILDREN_Z_INDEX
        if (newNode.data.type === BlockEnum.Answer || newNode.data.type === BlockEnum.Tool || newNode.data.type === BlockEnum.Assigner) {
          const parentIterNodeIndex = nodes.findIndex(node => node.id === prevNode.parentId)
          const iterNodeData: IterationNodeType = nodes[parentIterNodeIndex].data
          iterNodeData._isShowTips = true
        }
      }
@@ -751,13 +661,11 @@
        data: {
          sourceType: prevNode.data.type,
          targetType: newNode.data.type,
          isInIteration,
          isInLoop,
          iteration_id: isInIteration ? prevNode.parentId : undefined,
          loop_id: isInLoop ? prevNode.parentId : undefined,
          isInIteration: !!prevNode.parentId,
          iteration_id: prevNode.parentId,
          _connectedNodeIsSelected: true,
        },
        zIndex: prevNode.parentId ? (isInIteration ? ITERATION_CHILDREN_Z_INDEX : LOOP_CHILDREN_Z_INDEX) : 0,
        zIndex: prevNode.parentId ? ITERATION_CHILDREN_Z_INDEX : 0,
      }
      const nodesConnectedSourceOrTargetHandleIdsMap = getNodesConnectedSourceOrTargetHandleIdsMap(
        [
@@ -777,18 +685,11 @@
          }
          if (node.data.type === BlockEnum.Iteration && prevNode.parentId === node.id)
            node.data._children?.push({ nodeId: newNode.id, nodeType: newNode.data.type })
          if (node.data.type === BlockEnum.Loop && prevNode.parentId === node.id)
            node.data._children?.push({ nodeId: newNode.id, nodeType: newNode.data.type })
            node.data._children?.push(newNode.id)
        })
        draft.push(newNode)
        if (newIterationStartNode)
          draft.push(newIterationStartNode)
        if (newLoopStartNode)
          draft.push(newLoopStartNode)
      })
      if (newNode.data.type === BlockEnum.VariableAssigner || newNode.data.type === BlockEnum.VariableAggregator) {
@@ -835,27 +736,15 @@
      }
      newNode.parentId = nextNode.parentId
      newNode.extent = nextNode.extent
      const parentNode = nodes.find(node => node.id === nextNode.parentId) || null
      const isInIteration = !!parentNode && parentNode.data.type === BlockEnum.Iteration
      const isInLoop = !!parentNode && parentNode.data.type === BlockEnum.Loop
      if (parentNode && nextNode.parentId) {
        newNode.data.isInIteration = isInIteration
        newNode.data.isInLoop = isInLoop
        if (isInIteration) {
          newNode.data.iteration_id = parentNode.id
          newNode.zIndex = ITERATION_CHILDREN_Z_INDEX
        }
        if (isInLoop) {
          newNode.data.loop_id = parentNode.id
          newNode.zIndex = LOOP_CHILDREN_Z_INDEX
        }
      if (nextNode.parentId) {
        newNode.data.isInIteration = true
        newNode.data.iteration_id = nextNode.parentId
        newNode.zIndex = ITERATION_CHILDREN_Z_INDEX
      }
      let newEdge
      if ((nodeType !== BlockEnum.IfElse) && (nodeType !== BlockEnum.QuestionClassifier) && (nodeType !== BlockEnum.LoopEnd)) {
      if ((nodeType !== BlockEnum.IfElse) && (nodeType !== BlockEnum.QuestionClassifier)) {
        newEdge = {
          id: `${newNode.id}-${sourceHandle}-${nextNodeId}-${nextNodeTargetHandle}`,
          type: CUSTOM_EDGE,
@@ -866,13 +755,11 @@
          data: {
            sourceType: newNode.data.type,
            targetType: nextNode.data.type,
            isInIteration,
            isInLoop,
            iteration_id: isInIteration ? nextNode.parentId : undefined,
            loop_id: isInLoop ? nextNode.parentId : undefined,
            isInIteration: !!nextNode.parentId,
            iteration_id: nextNode.parentId,
            _connectedNodeIsSelected: true,
          },
          zIndex: nextNode.parentId ? (isInIteration ? ITERATION_CHILDREN_Z_INDEX : LOOP_CHILDREN_Z_INDEX) : 0,
          zIndex: nextNode.parentId ? ITERATION_CHILDREN_Z_INDEX : 0,
        }
      }
@@ -903,17 +790,9 @@
          }
          if (node.data.type === BlockEnum.Iteration && nextNode.parentId === node.id)
            node.data._children?.push({ nodeId: newNode.id, nodeType: newNode.data.type })
            node.data._children?.push(newNode.id)
          if (node.data.type === BlockEnum.Iteration && node.data.start_node_id === nextNodeId) {
            node.data.start_node_id = newNode.id
            node.data.startNodeType = newNode.data.type
          }
          if (node.data.type === BlockEnum.Loop && nextNode.parentId === node.id)
            node.data._children?.push({ nodeId: newNode.id, nodeType: newNode.data.type })
          if (node.data.type === BlockEnum.Loop && node.data.start_node_id === nextNodeId) {
            node.data.start_node_id = newNode.id
            node.data.startNodeType = newNode.data.type
          }
@@ -921,8 +800,6 @@
        draft.push(newNode)
        if (newIterationStartNode)
          draft.push(newIterationStartNode)
        if (newLoopStartNode)
          draft.push(newLoopStartNode)
      })
      if (newEdge) {
        const newEdges = produce(edges, (draft) => {
@@ -963,22 +840,10 @@
      }
      newNode.parentId = prevNode.parentId
      newNode.extent = prevNode.extent
      const parentNode = nodes.find(node => node.id === prevNode.parentId) || null
      const isInIteration = !!parentNode && parentNode.data.type === BlockEnum.Iteration
      const isInLoop = !!parentNode && parentNode.data.type === BlockEnum.Loop
      if (parentNode && prevNode.parentId) {
        newNode.data.isInIteration = isInIteration
        newNode.data.isInLoop = isInLoop
        if (isInIteration) {
          newNode.data.iteration_id = parentNode.id
          newNode.zIndex = ITERATION_CHILDREN_Z_INDEX
        }
        if (isInLoop) {
          newNode.data.loop_id = parentNode.id
          newNode.zIndex = LOOP_CHILDREN_Z_INDEX
        }
      if (prevNode.parentId) {
        newNode.data.isInIteration = true
        newNode.data.iteration_id = prevNode.parentId
        newNode.zIndex = ITERATION_CHILDREN_Z_INDEX
      }
      const currentEdgeIndex = edges.findIndex(edge => edge.source === prevNodeId && edge.target === nextNodeId)
@@ -992,21 +857,14 @@
        data: {
          sourceType: prevNode.data.type,
          targetType: newNode.data.type,
          isInIteration,
          isInLoop,
          iteration_id: isInIteration ? prevNode.parentId : undefined,
          loop_id: isInLoop ? prevNode.parentId : undefined,
          isInIteration: !!prevNode.parentId,
          iteration_id: prevNode.parentId,
          _connectedNodeIsSelected: true,
        },
        zIndex: prevNode.parentId ? (isInIteration ? ITERATION_CHILDREN_Z_INDEX : LOOP_CHILDREN_Z_INDEX) : 0,
        zIndex: prevNode.parentId ? ITERATION_CHILDREN_Z_INDEX : 0,
      }
      let newNextEdge: Edge | null = null
      const nextNodeParentNode = nodes.find(node => node.id === nextNode.parentId) || null
      const isNextNodeInIteration = !!nextNodeParentNode && nextNodeParentNode.data.type === BlockEnum.Iteration
      const isNextNodeInLoop = !!nextNodeParentNode && nextNodeParentNode.data.type === BlockEnum.Loop
      if (nodeType !== BlockEnum.IfElse && nodeType !== BlockEnum.QuestionClassifier && nodeType !== BlockEnum.LoopEnd) {
      if (nodeType !== BlockEnum.IfElse && nodeType !== BlockEnum.QuestionClassifier) {
        newNextEdge = {
          id: `${newNode.id}-${sourceHandle}-${nextNodeId}-${nextNodeTargetHandle}`,
          type: CUSTOM_EDGE,
@@ -1017,13 +875,11 @@
          data: {
            sourceType: newNode.data.type,
            targetType: nextNode.data.type,
            isInIteration: isNextNodeInIteration,
            isInLoop: isNextNodeInLoop,
            iteration_id: isNextNodeInIteration ? nextNode.parentId : undefined,
            loop_id: isNextNodeInLoop ? nextNode.parentId : undefined,
            isInIteration: !!nextNode.parentId,
            iteration_id: nextNode.parentId,
            _connectedNodeIsSelected: true,
          },
          zIndex: nextNode.parentId ? (isNextNodeInIteration ? ITERATION_CHILDREN_Z_INDEX : LOOP_CHILDREN_Z_INDEX) : 0,
          zIndex: nextNode.parentId ? ITERATION_CHILDREN_Z_INDEX : 0,
        }
      }
      const nodesConnectedSourceOrTargetHandleIdsMap = getNodesConnectedSourceOrTargetHandleIdsMap(
@@ -1051,15 +907,11 @@
            node.position.x += NODE_WIDTH_X_OFFSET
          if (node.data.type === BlockEnum.Iteration && prevNode.parentId === node.id)
            node.data._children?.push({ nodeId: newNode.id, nodeType: newNode.data.type })
          if (node.data.type === BlockEnum.Loop && prevNode.parentId === node.id)
            node.data._children?.push({ nodeId: newNode.id, nodeType: newNode.data.type })
            node.data._children?.push(newNode.id)
        })
        draft.push(newNode)
        if (newIterationStartNode)
          draft.push(newIterationStartNode)
        if (newLoopStartNode)
          draft.push(newLoopStartNode)
      })
      setNodes(newNodes)
      if (newNode.data.type === BlockEnum.VariableAssigner || newNode.data.type === BlockEnum.VariableAggregator) {
@@ -1117,9 +969,7 @@
    const {
      newNode: newCurrentNode,
      newIterationStartNode,
      newLoopStartNode,
    } = generateNewNode({
      type: getNodeCustomTypeByNodeDataType(nodeType),
      data: {
        ...NODES_INITIAL_DATA[nodeType],
        title: nodesWithSameType.length > 0 ? `${t(`workflow.blocks.${nodeType}`)} ${nodesWithSameType.length + 1}` : t(`workflow.blocks.${nodeType}`),
@@ -1128,9 +978,7 @@
        _connectedTargetHandleIds: [],
        selected: currentNode.data.selected,
        isInIteration: currentNode.data.isInIteration,
        isInLoop: currentNode.data.isInLoop,
        iteration_id: currentNode.data.iteration_id,
        loop_id: currentNode.data.loop_id,
      },
      position: {
        x: currentNode.position.x,
@@ -1162,8 +1010,6 @@
      draft.splice(index, 1, newCurrentNode)
      if (newIterationStartNode)
        draft.push(newIterationStartNode)
      if (newLoopStartNode)
        draft.push(newLoopStartNode)
    })
    setNodes(newNodes)
    const newEdges = produce(edges, (draft) => {
@@ -1176,6 +1022,22 @@
    saveStateToHistory(WorkflowHistoryEvent.NodeChange)
  }, [getNodesReadOnly, store, t, handleSyncWorkflowDraft, saveStateToHistory])
  const handleNodeCancelRunningStatus = useCallback(() => {
    const {
      getNodes,
      setNodes,
    } = store.getState()
    const nodes = getNodes()
    const newNodes = produce(nodes, (draft) => {
      draft.forEach((node) => {
        node.data._runningStatus = undefined
        node.data._waitingRun = false
      })
    })
    setNodes(newNodes)
  }, [store])
  const handleNodesCancelSelected = useCallback(() => {
    const {
@@ -1194,9 +1056,6 @@
  const handleNodeContextMenu = useCallback((e: MouseEvent, node: Node) => {
    if (node.type === CUSTOM_NOTE_NODE || node.type === CUSTOM_ITERATION_START_NODE)
      return
    if (node.type === CUSTOM_NOTE_NODE || node.type === CUSTOM_LOOP_START_NODE)
      return
    e.preventDefault()
@@ -1226,22 +1085,20 @@
    if (nodeId) {
      // If nodeId is provided, copy that specific node
      const nodeToCopy = nodes.find(node => node.id === nodeId && node.data.type !== BlockEnum.Start
        && node.type !== CUSTOM_ITERATION_START_NODE && node.type !== CUSTOM_LOOP_START_NODE && node.data.type !== BlockEnum.LoopEnd)
      const nodeToCopy = nodes.find(node => node.id === nodeId && node.data.type !== BlockEnum.Start && node.type !== CUSTOM_ITERATION_START_NODE)
      if (nodeToCopy)
        setClipboardElements([nodeToCopy])
    }
    else {
      // If no nodeId is provided, fall back to the current behavior
      const bundledNodes = nodes.filter(node => node.data._isBundled && node.data.type !== BlockEnum.Start
        && !node.data.isInIteration && !node.data.isInLoop)
      const bundledNodes = nodes.filter(node => node.data._isBundled && node.data.type !== BlockEnum.Start && !node.data.isInIteration)
      if (bundledNodes.length) {
        setClipboardElements(bundledNodes)
        return
      }
      const selectedNode = nodes.find(node => node.data.selected && node.data.type !== BlockEnum.Start && node.data.type !== BlockEnum.LoopEnd)
      const selectedNode = nodes.find(node => node.data.selected && node.data.type !== BlockEnum.Start)
      if (selectedNode)
        setClipboardElements([selectedNode])
@@ -1260,12 +1117,9 @@
    const {
      getNodes,
      setNodes,
      edges,
      setEdges,
    } = store.getState()
    const nodesToPaste: Node[] = []
    const edgesToPaste: Edge[] = []
    const nodes = getNodes()
    if (clipboardElements.length) {
@@ -1274,14 +1128,12 @@
      const currentPosition = screenToFlowPosition({ x: mousePosition.pageX, y: mousePosition.pageY })
      const offsetX = currentPosition.x - x
      const offsetY = currentPosition.y - y
      let idMapping: Record<string, string> = {}
      clipboardElements.forEach((nodeToPaste, index) => {
        const nodeType = nodeToPaste.data.type
        const {
          newNode,
          newIterationStartNode,
          newLoopStartNode,
        } = generateNewNode({
          type: nodeToPaste.type,
          data: {
@@ -1307,28 +1159,11 @@
          newIterationStartNode!.parentId = newNode.id;
          (newNode.data as IterationNodeType).start_node_id = newIterationStartNode!.id
          const oldIterationStartNode = nodes
            .find(n => n.parentId === nodeToPaste.id && n.type === CUSTOM_ITERATION_START_NODE)
          idMapping[oldIterationStartNode!.id] = newIterationStartNode!.id
          const { copyChildren, newIdMapping } = handleNodeIterationChildrenCopy(nodeToPaste.id, newNode.id, idMapping)
          newChildren = copyChildren
          idMapping = newIdMapping
          newChildren = handleNodeIterationChildrenCopy(nodeToPaste.id, newNode.id)
          newChildren.forEach((child) => {
            newNode.data._children?.push({ nodeId: child.id, nodeType: child.data.type })
            newNode.data._children?.push(child.id)
          })
          newChildren.push(newIterationStartNode!)
        }
        if (nodeToPaste.data.type === BlockEnum.Loop) {
          newLoopStartNode!.parentId = newNode.id;
          (newNode.data as LoopNodeType).start_node_id = newLoopStartNode!.id
          newChildren = handleNodeLoopChildrenCopy(nodeToPaste.id, newNode.id)
          newChildren.forEach((child) => {
            newNode.data._children?.push({ nodeId: child.id, nodeType: child.data.type })
          })
          newChildren.push(newLoopStartNode!)
        }
        nodesToPaste.push(newNode)
@@ -1337,31 +1172,11 @@
          nodesToPaste.push(...newChildren)
      })
      edges.forEach((edge) => {
        const sourceId = idMapping[edge.source]
        const targetId = idMapping[edge.target]
        if (sourceId && targetId) {
          const newEdge: Edge = {
            ...edge,
            id: `${sourceId}-${edge.sourceHandle}-${targetId}-${edge.targetHandle}`,
            source: sourceId,
            target: targetId,
            data: {
              ...edge.data,
              _connectedNodeIsSelected: false,
            },
          }
          edgesToPaste.push(newEdge)
        }
      })
      setNodes([...nodes, ...nodesToPaste])
      setEdges([...edges, ...edgesToPaste])
      saveStateToHistory(WorkflowHistoryEvent.NodePaste)
      handleSyncWorkflowDraft()
    }
  }, [getNodesReadOnly, workflowStore, store, reactflow, saveStateToHistory, handleSyncWorkflowDraft, handleNodeIterationChildrenCopy, handleNodeLoopChildrenCopy])
  }, [getNodesReadOnly, workflowStore, store, reactflow, saveStateToHistory, handleSyncWorkflowDraft, handleNodeIterationChildrenCopy])
  const handleNodesDuplicate = useCallback((nodeId?: string) => {
    if (getNodesReadOnly())
@@ -1411,7 +1226,7 @@
    const nodes = getNodes()
    const currentNode = nodes.find(n => n.id === nodeId)!
    const childrenNodes = nodes.filter(n => currentNode.data._children?.find((c: any) => c.nodeId === n.id))
    const childrenNodes = nodes.filter(n => currentNode.data._children?.includes(n.id))
    let rightNode: Node
    let bottomNode: Node
@@ -1433,12 +1248,9 @@
    })
    if (rightNode! && bottomNode!) {
      const parentNode = nodes.find(n => n.id === rightNode.parentId)
      const paddingMap = parentNode?.data.type === BlockEnum.Iteration ? ITERATION_PADDING : LOOP_PADDING
      if (width < rightNode!.position.x + rightNode.width! + paddingMap.right)
      if (width < rightNode!.position.x + rightNode.width! + ITERATION_PADDING.right)
        return
      if (height < bottomNode.position.y + bottomNode.height! + paddingMap.bottom)
      if (height < bottomNode.position.y + bottomNode.height! + ITERATION_PADDING.bottom)
        return
    }
    const newNodes = produce(nodes, (draft) => {
@@ -1538,6 +1350,7 @@
    handleNodeDelete,
    handleNodeChange,
    handleNodeAdd,
    handleNodeCancelRunningStatus,
    handleNodesCancelSelected,
    handleNodeContextMenu,
    handleNodesCopy,