mirror of
				https://github.com/langgenius/dify.git
				synced 2025-11-04 12:53:38 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			179 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			179 lines
		
	
	
		
			4.8 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import dagre from '@dagrejs/dagre'
 | 
						|
import {
 | 
						|
  cloneDeep,
 | 
						|
} from 'lodash-es'
 | 
						|
import type {
 | 
						|
  Edge,
 | 
						|
  Node,
 | 
						|
} from '../types'
 | 
						|
import {
 | 
						|
  BlockEnum,
 | 
						|
} from '../types'
 | 
						|
import {
 | 
						|
  CUSTOM_NODE,
 | 
						|
  NODE_LAYOUT_HORIZONTAL_PADDING,
 | 
						|
  NODE_LAYOUT_MIN_DISTANCE,
 | 
						|
  NODE_LAYOUT_VERTICAL_PADDING,
 | 
						|
} from '../constants'
 | 
						|
import { CUSTOM_ITERATION_START_NODE } from '../nodes/iteration-start/constants'
 | 
						|
import { CUSTOM_LOOP_START_NODE } from '../nodes/loop-start/constants'
 | 
						|
 | 
						|
export const getLayoutByDagre = (originNodes: Node[], originEdges: Edge[]) => {
 | 
						|
  const dagreGraph = new dagre.graphlib.Graph()
 | 
						|
  dagreGraph.setDefaultEdgeLabel(() => ({}))
 | 
						|
  const nodes = cloneDeep(originNodes).filter(node => !node.parentId && node.type === CUSTOM_NODE)
 | 
						|
  const edges = cloneDeep(originEdges).filter(edge => (!edge.data?.isInIteration && !edge.data?.isInLoop))
 | 
						|
  dagreGraph.setGraph({
 | 
						|
    rankdir: 'LR',
 | 
						|
    align: 'UL',
 | 
						|
    nodesep: 40,
 | 
						|
    ranksep: 60,
 | 
						|
    ranker: 'tight-tree',
 | 
						|
    marginx: 30,
 | 
						|
    marginy: 200,
 | 
						|
  })
 | 
						|
  nodes.forEach((node) => {
 | 
						|
    dagreGraph.setNode(node.id, {
 | 
						|
      width: node.width!,
 | 
						|
      height: node.height!,
 | 
						|
    })
 | 
						|
  })
 | 
						|
  edges.forEach((edge) => {
 | 
						|
    dagreGraph.setEdge(edge.source, edge.target)
 | 
						|
  })
 | 
						|
  dagre.layout(dagreGraph)
 | 
						|
  return dagreGraph
 | 
						|
}
 | 
						|
 | 
						|
export const getLayoutForChildNodes = (parentNodeId: string, originNodes: Node[], originEdges: Edge[]) => {
 | 
						|
  const dagreGraph = new dagre.graphlib.Graph()
 | 
						|
  dagreGraph.setDefaultEdgeLabel(() => ({}))
 | 
						|
 | 
						|
  const nodes = cloneDeep(originNodes).filter(node => node.parentId === parentNodeId)
 | 
						|
  const edges = cloneDeep(originEdges).filter(edge =>
 | 
						|
    (edge.data?.isInIteration && edge.data?.iteration_id === parentNodeId)
 | 
						|
    || (edge.data?.isInLoop && edge.data?.loop_id === parentNodeId),
 | 
						|
  )
 | 
						|
 | 
						|
  const startNode = nodes.find(node =>
 | 
						|
    node.type === CUSTOM_ITERATION_START_NODE
 | 
						|
    || node.type === CUSTOM_LOOP_START_NODE
 | 
						|
    || node.data?.type === BlockEnum.LoopStart
 | 
						|
    || node.data?.type === BlockEnum.IterationStart,
 | 
						|
  )
 | 
						|
 | 
						|
  if (!startNode) {
 | 
						|
    dagreGraph.setGraph({
 | 
						|
      rankdir: 'LR',
 | 
						|
      align: 'UL',
 | 
						|
      nodesep: 40,
 | 
						|
      ranksep: 60,
 | 
						|
      marginx: NODE_LAYOUT_HORIZONTAL_PADDING,
 | 
						|
      marginy: NODE_LAYOUT_VERTICAL_PADDING,
 | 
						|
    })
 | 
						|
 | 
						|
    nodes.forEach((node) => {
 | 
						|
      dagreGraph.setNode(node.id, {
 | 
						|
        width: node.width || 244,
 | 
						|
        height: node.height || 100,
 | 
						|
      })
 | 
						|
    })
 | 
						|
 | 
						|
    edges.forEach((edge) => {
 | 
						|
      dagreGraph.setEdge(edge.source, edge.target)
 | 
						|
    })
 | 
						|
 | 
						|
    dagre.layout(dagreGraph)
 | 
						|
    return dagreGraph
 | 
						|
  }
 | 
						|
 | 
						|
  const startNodeOutEdges = edges.filter(edge => edge.source === startNode.id)
 | 
						|
  const firstConnectedNodes = startNodeOutEdges.map(edge =>
 | 
						|
    nodes.find(node => node.id === edge.target),
 | 
						|
  ).filter(Boolean) as Node[]
 | 
						|
 | 
						|
  const nonStartNodes = nodes.filter(node => node.id !== startNode.id)
 | 
						|
  const nonStartEdges = edges.filter(edge => edge.source !== startNode.id && edge.target !== startNode.id)
 | 
						|
 | 
						|
  dagreGraph.setGraph({
 | 
						|
    rankdir: 'LR',
 | 
						|
    align: 'UL',
 | 
						|
    nodesep: 40,
 | 
						|
    ranksep: 60,
 | 
						|
    marginx: NODE_LAYOUT_HORIZONTAL_PADDING / 2,
 | 
						|
    marginy: NODE_LAYOUT_VERTICAL_PADDING / 2,
 | 
						|
  })
 | 
						|
 | 
						|
  nonStartNodes.forEach((node) => {
 | 
						|
    dagreGraph.setNode(node.id, {
 | 
						|
      width: node.width || 244,
 | 
						|
      height: node.height || 100,
 | 
						|
    })
 | 
						|
  })
 | 
						|
 | 
						|
  nonStartEdges.forEach((edge) => {
 | 
						|
    dagreGraph.setEdge(edge.source, edge.target)
 | 
						|
  })
 | 
						|
 | 
						|
  dagre.layout(dagreGraph)
 | 
						|
 | 
						|
  const startNodeSize = {
 | 
						|
    width: startNode.width || 44,
 | 
						|
    height: startNode.height || 48,
 | 
						|
  }
 | 
						|
 | 
						|
  const startNodeX = NODE_LAYOUT_HORIZONTAL_PADDING / 1.5
 | 
						|
  let startNodeY = 100
 | 
						|
 | 
						|
  let minFirstLayerX = Infinity
 | 
						|
  let avgFirstLayerY = 0
 | 
						|
  let firstLayerCount = 0
 | 
						|
 | 
						|
  if (firstConnectedNodes.length > 0) {
 | 
						|
    firstConnectedNodes.forEach((node) => {
 | 
						|
      if (dagreGraph.node(node.id)) {
 | 
						|
        const nodePos = dagreGraph.node(node.id)
 | 
						|
        avgFirstLayerY += nodePos.y
 | 
						|
        firstLayerCount++
 | 
						|
        minFirstLayerX = Math.min(minFirstLayerX, nodePos.x - nodePos.width / 2)
 | 
						|
      }
 | 
						|
    })
 | 
						|
 | 
						|
    if (firstLayerCount > 0) {
 | 
						|
      avgFirstLayerY /= firstLayerCount
 | 
						|
      startNodeY = avgFirstLayerY
 | 
						|
    }
 | 
						|
 | 
						|
    const minRequiredX = startNodeX + startNodeSize.width + NODE_LAYOUT_MIN_DISTANCE
 | 
						|
 | 
						|
    if (minFirstLayerX < minRequiredX) {
 | 
						|
      const shiftX = minRequiredX - minFirstLayerX
 | 
						|
 | 
						|
      nonStartNodes.forEach((node) => {
 | 
						|
        if (dagreGraph.node(node.id)) {
 | 
						|
          const nodePos = dagreGraph.node(node.id)
 | 
						|
          dagreGraph.setNode(node.id, {
 | 
						|
            x: nodePos.x + shiftX,
 | 
						|
            y: nodePos.y,
 | 
						|
            width: nodePos.width,
 | 
						|
            height: nodePos.height,
 | 
						|
          })
 | 
						|
        }
 | 
						|
      })
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  dagreGraph.setNode(startNode.id, {
 | 
						|
    x: startNodeX + startNodeSize.width / 2,
 | 
						|
    y: startNodeY,
 | 
						|
    width: startNodeSize.width,
 | 
						|
    height: startNodeSize.height,
 | 
						|
  })
 | 
						|
 | 
						|
  startNodeOutEdges.forEach((edge) => {
 | 
						|
    dagreGraph.setEdge(edge.source, edge.target)
 | 
						|
  })
 | 
						|
 | 
						|
  return dagreGraph
 | 
						|
}
 |