mirror of
				https://github.com/langgenius/dify.git
				synced 2025-10-30 18:33:30 +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 | ||
|  | } |