diff --git a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx
index bb57d526c9..467d5be3c0 100644
--- a/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx
+++ b/web/app/(commonLayout)/app/(appDetailLayout)/[appId]/workflow/page.tsx
@@ -3,10 +3,51 @@
import { memo } from 'react'
import Workflow from '@/app/components/workflow'
+// export function createNodesAndEdges(xNodes = 10, yNodes = 10) {
+// const nodes = []
+// const edges = []
+// let nodeId = 1
+// let recentNodeId = null
+
+// for (let y = 0; y < yNodes; y++) {
+// for (let x = 0; x < xNodes; x++) {
+// const position = { x: x * 200, y: y * 50 }
+// const node = {
+// id: `stress-${nodeId.toString()}`,
+// type: 'custom',
+// data: { type: 'start', title: '开始', variables: [] },
+// position,
+// }
+// nodes.push(node)
+
+// if (recentNodeId && nodeId <= xNodes * yNodes) {
+// edges.push({
+// id: `${x}-${y}`,
+// type: 'custom',
+// source: `stress-${recentNodeId.toString()}`,
+// target: `stress-${nodeId.toString()}`,
+// })
+// }
+
+// recentNodeId = nodeId
+// nodeId++
+// }
+// }
+
+// return { nodes, edges }
+// }
+
const Page = () => {
+ // const {
+ // nodes,
+ // edges,
+ // } = createNodesAndEdges()
return (
-
+
)
}
diff --git a/web/app/components/workflow/hooks/use-edges-interactions.ts b/web/app/components/workflow/hooks/use-edges-interactions.ts
index 160d4367f4..20d5019707 100644
--- a/web/app/components/workflow/hooks/use-edges-interactions.ts
+++ b/web/app/components/workflow/hooks/use-edges-interactions.ts
@@ -6,6 +6,7 @@ import type {
} from 'reactflow'
import { useStoreApi } from 'reactflow'
import { useStore } from '../store'
+import type { Node } from '../types'
import { useNodesSyncDraft } from './use-nodes-sync-draft'
export const useEdgesInteractions = () => {
@@ -55,14 +56,26 @@ export const useEdgesInteractions = () => {
return
const {
+ getNodes,
+ setNodes,
edges,
setEdges,
} = store.getState()
- const newEdges = produce(edges, (draft) => {
- const index = draft.findIndex(edge => edge.source === nodeId && edge.sourceHandle === branchId)
+ const currentEdgeIndex = edges.findIndex(edge => edge.source === nodeId && edge.sourceHandle === branchId)
+ const currentEdge = edges[currentEdgeIndex]
+ const newNodes = produce(getNodes(), (draft: Node[]) => {
+ const sourceNode = draft.find(node => node.id === currentEdge.source)
+ const targetNode = draft.find(node => node.id === currentEdge.target)
- if (index > -1)
- draft.splice(index, 1)
+ if (sourceNode)
+ sourceNode.data._connectedSourceHandleIds = sourceNode.data._connectedSourceHandleIds?.filter(handleId => handleId !== currentEdge.sourceHandle)
+
+ if (targetNode)
+ targetNode.data._connectedTargetHandleIds = targetNode.data._connectedTargetHandleIds?.filter(handleId => handleId !== currentEdge.targetHandle)
+ })
+ setNodes(newNodes)
+ const newEdges = produce(edges, (draft) => {
+ draft.splice(currentEdgeIndex, 1)
})
setEdges(newEdges)
handleSyncWorkflowDraft()
@@ -75,14 +88,26 @@ export const useEdgesInteractions = () => {
return
const {
+ getNodes,
+ setNodes,
edges,
setEdges,
} = store.getState()
- const newEdges = produce(edges, (draft) => {
- const index = draft.findIndex(edge => edge.selected)
+ const currentEdgeIndex = edges.findIndex(edge => edge.selected)
+ const currentEdge = edges[currentEdgeIndex]
+ const newNodes = produce(getNodes(), (draft: Node[]) => {
+ const sourceNode = draft.find(node => node.id === currentEdge.source)
+ const targetNode = draft.find(node => node.id === currentEdge.target)
- if (index > -1)
- draft.splice(index, 1)
+ if (sourceNode)
+ sourceNode.data._connectedSourceHandleIds = sourceNode.data._connectedSourceHandleIds?.filter(handleId => handleId !== currentEdge.sourceHandle)
+
+ if (targetNode)
+ targetNode.data._connectedTargetHandleIds = targetNode.data._connectedTargetHandleIds?.filter(handleId => handleId !== currentEdge.targetHandle)
+ })
+ setNodes(newNodes)
+ const newEdges = produce(edges, (draft) => {
+ draft.splice(currentEdgeIndex, 1)
})
setEdges(newEdges)
handleSyncWorkflowDraft()
diff --git a/web/app/components/workflow/hooks/use-nodes-interactions.ts b/web/app/components/workflow/hooks/use-nodes-interactions.ts
index 02dd1c3d56..1fb90fa74b 100644
--- a/web/app/components/workflow/hooks/use-nodes-interactions.ts
+++ b/web/app/components/workflow/hooks/use-nodes-interactions.ts
@@ -260,9 +260,8 @@ export const useNodesInteractions = () => {
edges,
setEdges,
} = store.getState()
-
const newEdges = produce(edges, (draft) => {
- const filtered = draft.filter(edge => edge.source !== source && edge.target !== target)
+ const filtered = draft.filter(edge => (edge.source !== source && edge.sourceHandle !== sourceHandle) || (edge.target !== target && edge.targetHandle !== targetHandle))
filtered.push({
id: `${source}-${target}`,
@@ -292,14 +291,25 @@ export const useNodesInteractions = () => {
setEdges,
} = store.getState()
- const newNodes = produce(getNodes(), (draft) => {
- const index = draft.findIndex(node => node.id === nodeId)
+ const nodes = getNodes()
+ const currentNodeIndex = nodes.findIndex(node => node.id === nodeId)
+ const currentNode = nodes[currentNodeIndex]
+ const incomersIds = getIncomers(currentNode, nodes, edges).map(incomer => incomer.id)
+ const outgoersIds = getOutgoers(currentNode, nodes, edges).map(outgoer => outgoer.id)
+ const connectedEdges = getConnectedEdges([{ id: nodeId } as Node], edges)
+ const sourceEdgesHandleIds = connectedEdges.filter(edge => edge.target === nodeId).map(edge => edge.sourceHandle)
+ const targetEdgesHandleIds = connectedEdges.filter(edge => edge.source === nodeId).map(edge => edge.targetHandle)
+ const newNodes = produce(nodes, (draft: Node[]) => {
+ draft.forEach((node) => {
+ if (incomersIds.includes(node.id))
+ node.data._connectedSourceHandleIds = node.data._connectedSourceHandleIds?.filter(handleId => !sourceEdgesHandleIds.includes(handleId))
- if (index > -1)
- draft.splice(index, 1)
+ if (outgoersIds.includes(node.id))
+ node.data._connectedTargetHandleIds = node.data._connectedTargetHandleIds?.filter(handleId => !targetEdgesHandleIds.includes(handleId))
+ })
+ draft.splice(currentNodeIndex, 1)
})
setNodes(newNodes)
- const connectedEdges = getConnectedEdges([{ id: nodeId } as Node], edges)
const newEdges = produce(edges, (draft) => {
return draft.filter(edge => !connectedEdges.find(connectedEdge => connectedEdge.id === edge.id))
})
@@ -325,7 +335,8 @@ export const useNodesInteractions = () => {
setEdges,
} = store.getState()
const nodes = getNodes()
- const currentNode = nodes.find(node => node.id === currentNodeId)!
+ const currentNodeIndex = nodes.findIndex(node => node.id === currentNodeId)
+ const currentNode = nodes[currentNodeIndex]
const outgoers = getOutgoers(currentNode, nodes, edges).sort((a, b) => a.position.y - b.position.y)
const lastOutgoer = outgoers[outgoers.length - 1]
const nextNode: Node = {
@@ -334,6 +345,7 @@ export const useNodesInteractions = () => {
data: {
...nodesInitialData[nodeType],
...(toolDefaultValue || {}),
+ _connectedTargetHandleIds: ['target'],
selected: true,
},
position: {
@@ -350,9 +362,12 @@ export const useNodesInteractions = () => {
target: nextNode.id,
targetHandle: 'target',
}
- const newNodes = produce(nodes, (draft) => {
- draft.forEach((node) => {
+ const newNodes = produce(nodes, (draft: Node[]) => {
+ draft.forEach((node, index) => {
node.data.selected = false
+
+ if (index === currentNodeIndex)
+ node.data._connectedSourceHandleIds?.push(sourceHandle)
})
draft.push(nextNode)
})
diff --git a/web/app/components/workflow/index.tsx b/web/app/components/workflow/index.tsx
index 513c7c4391..c43b2b2474 100644
--- a/web/app/components/workflow/index.tsx
+++ b/web/app/components/workflow/index.tsx
@@ -182,7 +182,7 @@ const WorkflowWrap: FC = ({
return nodes
if (data)
- return initialNodes(data.graph.nodes)
+ return initialNodes(data.graph.nodes, data.graph.edges)
return [startNode]
}, [data, nodes, startNode])
diff --git a/web/app/components/workflow/nodes/_base/components/node-handle.tsx b/web/app/components/workflow/nodes/_base/components/node-handle.tsx
index 5803ae8ec4..f2955c8932 100644
--- a/web/app/components/workflow/nodes/_base/components/node-handle.tsx
+++ b/web/app/components/workflow/nodes/_base/components/node-handle.tsx
@@ -1,15 +1,13 @@
import type { MouseEvent } from 'react'
import {
+ memo,
useCallback,
useEffect,
useState,
} from 'react'
-import type { NodeProps } from 'reactflow'
import {
Handle,
Position,
- getConnectedEdges,
- useEdges,
} from 'reactflow'
import { BlockEnum } from '../../../types'
import type { Node } from '../../../types'
@@ -22,9 +20,9 @@ type NodeHandleProps = {
handleId: string
handleClassName?: string
nodeSelectorClassName?: string
-} & Pick
+} & Pick
-export const NodeTargetHandle = ({
+export const NodeTargetHandle = memo(({
id,
data,
handleId,
@@ -33,9 +31,7 @@ export const NodeTargetHandle = ({
}: NodeHandleProps) => {
const [open, setOpen] = useState(false)
const { handleNodeAddPrev } = useNodesInteractions()
- const edges = useEdges()
- const connectedEdges = getConnectedEdges([{ id } as Node], edges)
- const connected = connectedEdges.find(edge => edge.targetHandle === handleId && edge.target === id)
+ const connected = data._connectedTargetHandleIds?.includes(handleId)
const handleOpenChange = useCallback((v: boolean) => {
setOpen(v)
@@ -85,9 +81,10 @@ export const NodeTargetHandle = ({
>
)
-}
+})
+NodeTargetHandle.displayName = 'NodeTargetHandle'
-export const NodeSourceHandle = ({
+export const NodeSourceHandle = memo(({
id,
data,
handleId,
@@ -97,9 +94,7 @@ export const NodeSourceHandle = ({
const notInitialWorkflow = useStore(s => s.notInitialWorkflow)
const [open, setOpen] = useState(false)
const { handleNodeAddNext } = useNodesInteractions()
- const edges = useEdges()
- const connectedEdges = getConnectedEdges([{ id } as Node], edges)
- const connected = connectedEdges.find(edge => edge.sourceHandle === handleId && edge.source === id)
+ const connected = data._connectedSourceHandleIds?.includes(handleId)
const handleOpenChange = useCallback((v: boolean) => {
setOpen(v)
}, [])
@@ -150,4 +145,5 @@ export const NodeSourceHandle = ({
>
)
-}
+})
+NodeSourceHandle.displayName = 'NodeSourceHandle'
diff --git a/web/app/components/workflow/nodes/_base/node.tsx b/web/app/components/workflow/nodes/_base/node.tsx
index fd781bc64e..6a8079e560 100644
--- a/web/app/components/workflow/nodes/_base/node.tsx
+++ b/web/app/components/workflow/nodes/_base/node.tsx
@@ -47,6 +47,7 @@ const BaseNode: FC = ({
${data._runningStatus === NodeRunningStatus.Running && '!border-primary-500'}
${data._runningStatus === NodeRunningStatus.Succeeded && '!border-[#12B76A]'}
${data._runningStatus === NodeRunningStatus.Failed && '!border-[#F04438]'}
+ ${data._runningStatus === NodeRunningStatus.Waiting && 'opacity-70'}
`}
>
{
diff --git a/web/app/components/workflow/operator/zoom-in-out.tsx b/web/app/components/workflow/operator/zoom-in-out.tsx
index 8d732df726..0a4f09ae39 100644
--- a/web/app/components/workflow/operator/zoom-in-out.tsx
+++ b/web/app/components/workflow/operator/zoom-in-out.tsx
@@ -94,7 +94,10 @@ const ZoomInOut: FC = () => {
placement='top-start'
open={open}
onOpenChange={setOpen}
- offset={4}
+ offset={{
+ mainAxis: 4,
+ crossAxis: -2,
+ }}
>
= {
+ _connectedSourceHandleIds?: string[]
+ _connectedTargetHandleIds?: string[]
_targetBranches?: Branch[]
_isSingleRun?: boolean
_runningStatus?: NodeRunningStatus
diff --git a/web/app/components/workflow/utils.ts b/web/app/components/workflow/utils.ts
index 7939a5687d..0e0161ea19 100644
--- a/web/app/components/workflow/utils.ts
+++ b/web/app/components/workflow/utils.ts
@@ -80,10 +80,14 @@ export const nodesLevelOrderTraverse = (
}
}
-export const initialNodes = (nodes: Node[]) => {
+export const initialNodes = (nodes: Node[], edges: Edge[]) => {
return nodes.map((node) => {
node.type = 'custom'
+ const connectedEdges = getConnectedEdges([node], edges)
+ node.data._connectedSourceHandleIds = connectedEdges.filter(edge => edge.source === node.id).map(edge => edge.sourceHandle || 'source')
+ node.data._connectedTargetHandleIds = connectedEdges.filter(edge => edge.target === node.id).map(edge => edge.targetHandle || 'target')
+
if (node.data.type === BlockEnum.IfElse) {
node.data._targetBranches = [
{
@@ -170,3 +174,10 @@ export const canRunBySingle = (nodeType: BlockEnum) => {
|| nodeType === BlockEnum.HttpRequest
|| nodeType === BlockEnum.Tool
}
+
+type ConnectedSourceOrTargetNodesChange = {
+ type: 'add' | 'remove'
+ edge: Edge
+}[]
+export const getConnectedSourceOrTargetNodesChangeList = (changes: ConnectedSourceOrTargetNodesChange, nodes: Node[], edges: Edge[]) => {
+}