From 1e68a5fec00c84cf62bc368dfcfa02c9d63a85ad Mon Sep 17 00:00:00 2001 From: heheer Date: Sun, 4 Jan 2026 19:06:48 +0800 Subject: [PATCH] connection handle (#6178) --- .../Flow/hooks/useWorkflow.tsx | 25 +++++++-- .../nodes/render/Handle/ConnectionHandle.tsx | 53 ++++++++++++++----- 2 files changed, 60 insertions(+), 18 deletions(-) diff --git a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/hooks/useWorkflow.tsx b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/hooks/useWorkflow.tsx index 9a2f68bf3e..c27df9eee0 100644 --- a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/hooks/useWorkflow.tsx +++ b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/hooks/useWorkflow.tsx @@ -827,10 +827,27 @@ export const useWorkflow = () => { // Prevent native context menu from showing e.preventDefault(); - setMenu({ - top: e.clientY + 6, - left: e.clientX - 12 - }); + // Context menu dimensions + const contextMenuWidth = 120; + const contextMenuHeight = 120; + + const viewportWidth = window.innerWidth; + const viewportHeight = window.innerHeight; + const margin = 10; + + let top = e.clientY + 6; + let left = e.clientX - 12; + + // Check right boundary + if (left + contextMenuWidth + margin > viewportWidth) { + left = Math.max(margin, e.clientX - contextMenuWidth); + } + + // Check bottom boundary + if (top + contextMenuHeight + margin > viewportHeight) { + top = Math.max(margin, viewportHeight - contextMenuHeight - margin); + } + setMenu({ top, left }); }, [setMenu] ); diff --git a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/Handle/ConnectionHandle.tsx b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/Handle/ConnectionHandle.tsx index 793b16cc74..22e2fb7c19 100644 --- a/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/Handle/ConnectionHandle.tsx +++ b/projects/app/src/pageComponents/app/detail/WorkflowComponents/Flow/nodes/render/Handle/ConnectionHandle.tsx @@ -1,11 +1,12 @@ import React, { useMemo } from 'react'; import { Position } from 'reactflow'; import { MySourceHandle, MyTargetHandle } from '.'; -import { getHandleId } from '@fastgpt/global/core/workflow/utils'; -import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants'; +import { getHandleId, getElseIFLabel } from '@fastgpt/global/core/workflow/utils'; +import { NodeInputKeyEnum, NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants'; import { useContextSelector } from 'use-context-selector'; import { WorkflowBufferDataContext } from '../../../../context/workflowInitContext'; import { WorkflowActionsContext } from '../../../../context/workflowActionsContext'; +import { FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; export const ConnectionSourceHandle = ({ nodeId, @@ -28,24 +29,48 @@ export const ConnectionSourceHandle = ({ })(); const RightHandle = (() => { + // When the node is folded and has multiple branches, only render the first output. + if (node?.isFolded) { + const firstHandleId = (() => { + if (node.flowNodeType === FlowNodeTypeEnum.userSelect) { + const options = node?.inputs?.find( + (input) => input.key === NodeInputKeyEnum.userSelectOptions + )?.value; + if (options && options.length > 0) { + return getHandleId(nodeId, 'source', options[0].key); + } + } else if (node.flowNodeType === FlowNodeTypeEnum.ifElseNode) { + return getHandleId(nodeId, 'source', getElseIFLabel(0)); + } else if (node.flowNodeType === FlowNodeTypeEnum.classifyQuestion) { + const options = node?.inputs?.find( + (input) => input.key === NodeInputKeyEnum.agents + )?.value; + if (options && options.length > 0) { + return getHandleId(nodeId, 'source', options[0].key); + } + } + })(); + + if (firstHandleId) { + return ( + + ); + } + } + const handleId = getHandleId(nodeId, sourceType, Position.Right); const rightTargetConnected = edges.some( (edge) => edge.targetHandle === getHandleId(nodeId, 'target', Position.Right) ); - /* - If the node is folded and has outputs, must show the handle - hide handle when: - - not folded - - not node - - not sourceHandle - - already connected - */ - if ( - !(node?.isFolded && node?.outputs.length) && - (!node || !node?.showSourceHandle || rightTargetConnected) - ) + if (!node || !node?.showSourceHandle || rightTargetConnected) { return null; + } return (