mirror of https://github.com/langgenius/dify.git
feat(elk-layout): add support for Human Input nodes with ELK ports and enhance edge sorting
This commit is contained in:
parent
7deaab116a
commit
23f5427349
|
|
@ -1,4 +1,5 @@
|
|||
import type { ElkNode, LayoutOptions } from 'elkjs/lib/elk-api'
|
||||
import type { HumanInputNodeType } from '@/app/components/workflow/nodes/human-input/types'
|
||||
import type { CaseItem, IfElseNodeType } from '@/app/components/workflow/nodes/if-else/types'
|
||||
import type {
|
||||
Edge,
|
||||
|
|
@ -345,6 +346,76 @@ const buildIfElseWithPorts = (
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Build Human Input node with ELK native Ports for multiple branches
|
||||
* Handles user actions as branches with __timeout as the last fixed branch
|
||||
*/
|
||||
const buildHumanInputWithPorts = (
|
||||
humanInputNode: Node,
|
||||
edges: Edge[],
|
||||
): { node: ElkNodeShape, portMap: Map<string, string> } | null => {
|
||||
const childEdges = edges.filter(edge => edge.source === humanInputNode.id)
|
||||
|
||||
if (childEdges.length <= 1)
|
||||
return null
|
||||
|
||||
// Sort child edges: user actions first (by order), then __timeout last
|
||||
const sortedChildEdges = [...childEdges].sort((edgeA, edgeB) => {
|
||||
const handleA = edgeA.sourceHandle
|
||||
const handleB = edgeB.sourceHandle
|
||||
|
||||
if (handleA && handleB) {
|
||||
const userActions = (humanInputNode.data as HumanInputNodeType).user_actions || []
|
||||
const isATimeout = handleA === '__timeout'
|
||||
const isBTimeout = handleB === '__timeout'
|
||||
|
||||
// __timeout should always be last
|
||||
if (isATimeout)
|
||||
return 1
|
||||
if (isBTimeout)
|
||||
return -1
|
||||
|
||||
// Sort by user_actions order
|
||||
const indexA = userActions.findIndex(action => action.id === handleA)
|
||||
const indexB = userActions.findIndex(action => action.id === handleB)
|
||||
|
||||
if (indexA !== -1 && indexB !== -1)
|
||||
return indexA - indexB
|
||||
}
|
||||
|
||||
return 0
|
||||
})
|
||||
|
||||
// Create ELK ports for each branch
|
||||
const ports: ElkPortShape[] = sortedChildEdges.map((edge, index) => ({
|
||||
id: `${humanInputNode.id}-port-${edge.sourceHandle || index}`,
|
||||
layoutOptions: {
|
||||
'port.side': 'EAST',
|
||||
'port.index': String(index),
|
||||
},
|
||||
}))
|
||||
|
||||
// Build port mapping: edge.id -> portId
|
||||
const portMap = new Map<string, string>()
|
||||
sortedChildEdges.forEach((edge, index) => {
|
||||
const portId = `${humanInputNode.id}-port-${edge.sourceHandle || index}`
|
||||
portMap.set(edge.id, portId)
|
||||
})
|
||||
|
||||
return {
|
||||
node: {
|
||||
id: humanInputNode.id,
|
||||
width: humanInputNode.width ?? DEFAULT_NODE_WIDTH,
|
||||
height: humanInputNode.height ?? DEFAULT_NODE_HEIGHT,
|
||||
ports,
|
||||
layoutOptions: {
|
||||
'elk.portConstraints': 'FIXED_ORDER',
|
||||
},
|
||||
},
|
||||
portMap,
|
||||
}
|
||||
}
|
||||
|
||||
const normaliseBounds = (layout: LayoutResult): LayoutResult => {
|
||||
const {
|
||||
nodes,
|
||||
|
|
@ -388,8 +459,7 @@ export const getLayoutByDagre = async (originNodes: Node[], originEdges: Edge[])
|
|||
// Track which edges have been processed for If/Else nodes with ports
|
||||
const edgeToPortMap = new Map<string, string>()
|
||||
|
||||
// Build nodes with ports for If/Else nodes
|
||||
// todo: add human-input node support
|
||||
// Build nodes with ports for If/Else and Human Input nodes
|
||||
nodes.forEach((node) => {
|
||||
if (node.data.type === BlockEnum.IfElse) {
|
||||
const portsResult = buildIfElseWithPorts(node, edges)
|
||||
|
|
@ -406,6 +476,21 @@ export const getLayoutByDagre = async (originNodes: Node[], originEdges: Edge[])
|
|||
elkNodes.push(toElkNode(node))
|
||||
}
|
||||
}
|
||||
else if (node.data.type === BlockEnum.HumanInput) {
|
||||
const portsResult = buildHumanInputWithPorts(node, edges)
|
||||
if (portsResult) {
|
||||
// Use node with ports
|
||||
elkNodes.push(portsResult.node)
|
||||
// Store port mappings for edges
|
||||
portsResult.portMap.forEach((portId, edgeId) => {
|
||||
edgeToPortMap.set(edgeId, portId)
|
||||
})
|
||||
}
|
||||
else {
|
||||
// No multiple branches, use normal node
|
||||
elkNodes.push(toElkNode(node))
|
||||
}
|
||||
}
|
||||
else {
|
||||
elkNodes.push(toElkNode(node))
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue