feat: support workflow node fold (#2797)

* feat: support workflow node fold

* fix

* fix
This commit is contained in:
heheer
2024-09-26 15:44:04 +08:00
committed by GitHub
parent 5ad8c81ef3
commit 12d7ba5d73
15 changed files with 176 additions and 82 deletions

View File

@@ -104,6 +104,7 @@ export type FlowNodeItemType = FlowNodeTemplateType & {
response?: ChatHistoryItemResType; response?: ChatHistoryItemResType;
isExpired?: boolean; isExpired?: boolean;
}; };
isFolded?: boolean;
}; };
// store node type // store node type

View File

@@ -123,6 +123,7 @@ export const iconPaths = {
'core/chat/chatLight': () => import('./icons/core/chat/chatLight.svg'), 'core/chat/chatLight': () => import('./icons/core/chat/chatLight.svg'),
'core/chat/chatModelTag': () => import('./icons/core/chat/chatModelTag.svg'), 'core/chat/chatModelTag': () => import('./icons/core/chat/chatModelTag.svg'),
'core/chat/chevronDown': () => import('./icons/core/chat/chevronDown.svg'), 'core/chat/chevronDown': () => import('./icons/core/chat/chevronDown.svg'),
'core/chat/chevronRight': () => import('./icons/core/chat/chevronRight.svg'),
'core/chat/chevronSelector': () => import('./icons/core/chat/chevronSelector.svg'), 'core/chat/chevronSelector': () => import('./icons/core/chat/chevronSelector.svg'),
'core/chat/chevronUp': () => import('./icons/core/chat/chevronUp.svg'), 'core/chat/chevronUp': () => import('./icons/core/chat/chevronUp.svg'),
'core/chat/export/pdf': () => import('./icons/core/chat/export/pdf.svg'), 'core/chat/export/pdf': () => import('./icons/core/chat/export/pdf.svg'),

View File

@@ -0,0 +1,3 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 25 24" fill="none">
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.19277 5.29289C8.80224 5.68342 8.80224 6.31658 9.19277 6.70711L14.4857 12L9.19277 17.2929C8.80224 17.6834 8.80224 18.3166 9.19277 18.7071C9.58329 19.0976 10.2165 19.0976 10.607 18.7071L16.607 12.7071C16.9975 12.3166 16.9975 11.6834 16.607 11.2929L10.607 5.29289C10.2165 4.90237 9.58329 4.90237 9.19277 5.29289Z" />
</svg>

After

Width:  |  Height:  |  Size: 448 B

View File

@@ -1,6 +1,6 @@
import React, { useCallback, useMemo } from 'react'; import React, { useCallback, useMemo } from 'react';
import { BezierEdge, getBezierPath, EdgeLabelRenderer, EdgeProps } from 'reactflow'; import { BezierEdge, getBezierPath, EdgeLabelRenderer, EdgeProps } from 'reactflow';
import { Flex } from '@chakra-ui/react'; import { Box, Flex } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import { NodeOutputKeyEnum, RuntimeEdgeStatusEnum } from '@fastgpt/global/core/workflow/constants'; import { NodeOutputKeyEnum, RuntimeEdgeStatusEnum } from '@fastgpt/global/core/workflow/constants';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
@@ -27,6 +27,16 @@ const ButtonEdge = (props: EdgeProps) => {
targetHandleId, targetHandleId,
style style
} = props; } = props;
const parentNode = useMemo(() => {
for (const node of nodeList) {
if ((node.nodeId === source || node.nodeId === target) && node.parentNodeId) {
return nodeList.find((parent) => parent.nodeId === node.parentNodeId);
}
}
return undefined;
}, [nodeList, source, target]);
const defaultZIndex = useMemo( const defaultZIndex = useMemo(
() => (nodeList.find((node) => node.nodeId === source && node.parentNodeId) ? 1001 : 0), () => (nodeList.find((node) => node.nodeId === source && node.parentNodeId) ? 1001 : 0),
[nodeList, source] [nodeList, source]
@@ -127,55 +137,59 @@ const ButtonEdge = (props: EdgeProps) => {
})(); })();
return ( return (
<EdgeLabelRenderer> <EdgeLabelRenderer>
<Flex <Box hidden={parentNode?.isFolded}>
display={isHover || highlightEdge ? 'flex' : 'none'}
alignItems={'center'}
justifyContent={'center'}
position={'absolute'}
transform={`translate(-55%, -50%) translate(${labelX}px,${labelY}px)`}
pointerEvents={'all'}
w={'17px'}
h={'17px'}
bg={'white'}
borderRadius={'17px'}
cursor={'pointer'}
zIndex={9999}
onClick={() => onDelConnect(id)}
>
<MyIcon name={'core/workflow/closeEdge'} w={'100%'}></MyIcon>
</Flex>
{!isToolEdge && (
<Flex <Flex
display={isHover || highlightEdge ? 'flex' : 'none'}
alignItems={'center'} alignItems={'center'}
justifyContent={'center'} justifyContent={'center'}
position={'absolute'} position={'absolute'}
transform={arrowTransform} transform={`translate(-55%, -50%) translate(${labelX}px,${labelY}px)`}
pointerEvents={'all'} pointerEvents={'all'}
w={highlightEdge ? '14px' : '10px'} w={'17px'}
h={highlightEdge ? '14px' : '10px'} h={'17px'}
// bg={'white'} bg={'white'}
zIndex={highlightEdge ? 1000 : defaultZIndex} borderRadius={'17px'}
cursor={'pointer'}
zIndex={9999}
onClick={() => onDelConnect(id)}
> >
<MyIcon <MyIcon name={'core/workflow/closeEdge'} w={'100%'}></MyIcon>
name={'core/workflow/edgeArrow'}
w={'100%'}
color={edgeColor}
{...(highlightEdge
? {
fontWeight: 'bold'
}
: {})}
></MyIcon>
</Flex> </Flex>
)} {!isToolEdge && (
<Flex
alignItems={'center'}
justifyContent={'center'}
position={'absolute'}
transform={arrowTransform}
pointerEvents={'all'}
w={highlightEdge ? '14px' : '10px'}
h={highlightEdge ? '14px' : '10px'}
// bg={'white'}
zIndex={highlightEdge ? 1000 : defaultZIndex}
>
<MyIcon
name={'core/workflow/edgeArrow'}
w={'100%'}
color={edgeColor}
{...(highlightEdge
? {
fontWeight: 'bold'
}
: {})}
></MyIcon>
</Flex>
)}
</Box>
</EdgeLabelRenderer> </EdgeLabelRenderer>
); );
}, [ }, [
parentNode?.isFolded,
isHover, isHover,
highlightEdge, highlightEdge,
labelX, labelX,
labelY, labelY,
isToolEdge, isToolEdge,
defaultZIndex,
edgeColor, edgeColor,
targetPosition, targetPosition,
newTargetX, newTargetX,
@@ -214,7 +228,8 @@ const ButtonEdge = (props: EdgeProps) => {
targetY={newTargetY} targetY={newTargetY}
style={{ style={{
...edgeStyle, ...edgeStyle,
stroke: edgeColor stroke: edgeColor,
display: parentNode?.isFolded ? 'none' : 'block'
}} }}
/> />
); );
@@ -227,7 +242,8 @@ const ButtonEdge = (props: EdgeProps) => {
source, source,
target, target,
style, style,
highlightEdge highlightEdge,
parentNode?.isFolded
]); ]);
return ( return (

View File

@@ -1,5 +1,13 @@
import React, { useMemo } from 'react'; import React, { useCallback, useMemo } from 'react';
import { Background, ControlButton, MiniMap, Panel, useReactFlow, useViewport } from 'reactflow'; import {
Background,
ControlButton,
MiniMap,
MiniMapNodeProps,
Panel,
useReactFlow,
useViewport
} from 'reactflow';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../../context'; import { WorkflowContext } from '../../context';
import MyTooltip from '@fastgpt/web/components/common/MyTooltip'; import MyTooltip from '@fastgpt/web/components/common/MyTooltip';
@@ -26,7 +34,8 @@ const FlowController = React.memo(function FlowController() {
canUndo, canUndo,
workflowControlMode, workflowControlMode,
setWorkflowControlMode, setWorkflowControlMode,
mouseInCanvas mouseInCanvas,
nodeList
} = useContextSelector(WorkflowContext, (v) => v); } = useContextSelector(WorkflowContext, (v) => v);
const { t } = useTranslation(); const { t } = useTranslation();
@@ -52,6 +61,20 @@ const FlowController = React.memo(function FlowController() {
zoomOut(); zoomOut();
}); });
const MiniMapNode = useCallback(
({ x, y, width, height, color, id }: MiniMapNodeProps) => {
const node = nodeList.find((node) => node.nodeId === id);
const parentNode = nodeList.find((n) => n.nodeId === node?.parentNodeId);
if (parentNode?.isFolded) {
return null;
}
return <rect x={x} y={y} width={width} height={height} fill={color} />;
},
[nodeList]
);
const Render = useMemo(() => { const Render = useMemo(() => {
return ( return (
<> <>
@@ -64,6 +87,7 @@ const FlowController = React.memo(function FlowController() {
boxShadow: '0px 0px 1px rgba(19, 51, 107, 0.10), 0px 4px 10px rgba(19, 51, 107, 0.10)' boxShadow: '0px 0px 1px rgba(19, 51, 107, 0.10), 0px 4px 10px rgba(19, 51, 107, 0.10)'
}} }}
pannable pannable
nodeComponent={MiniMapNode}
/> />
<Panel <Panel
position={'bottom-right'} position={'bottom-right'}
@@ -180,9 +204,10 @@ const FlowController = React.memo(function FlowController() {
</> </>
); );
}, [ }, [
MiniMapNode,
workflowControlMode, workflowControlMode,
isMac,
t, t,
isMac,
undo, undo,
canUndo, canUndo,
redo, redo,

View File

@@ -13,7 +13,8 @@ import {
getNodesBounds, getNodesBounds,
Rect, Rect,
NodeRemoveChange, NodeRemoveChange,
NodeSelectionChange NodeSelectionChange,
Position
} from 'reactflow'; } from 'reactflow';
import { EDGE_TYPE, FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant'; import { EDGE_TYPE, FlowNodeTypeEnum } from '@fastgpt/global/core/workflow/node/constant';
import 'reactflow/dist/style.css'; import 'reactflow/dist/style.css';
@@ -30,6 +31,8 @@ import {
Input_Template_Node_Width Input_Template_Node_Width
} from '@fastgpt/global/core/workflow/template/input'; } from '@fastgpt/global/core/workflow/template/input';
import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node'; import { FlowNodeItemType } from '@fastgpt/global/core/workflow/type/node';
import { getHandleId } from '@fastgpt/global/core/workflow/utils';
import { IfElseResultEnum } from '@fastgpt/global/core/workflow/template/system/ifElse/constant';
/* /*
Compute helper lines for snapping nodes to each other Compute helper lines for snapping nodes to each other
@@ -367,8 +370,8 @@ export const useWorkflow = () => {
const checkNodeOverLoopNode = useMemoizedFn((node: Node) => { const checkNodeOverLoopNode = useMemoizedFn((node: Node) => {
if (!node) return; if (!node) return;
// 获取所有与当前节点相交的节点 // 获取所有与当前节点相交的节点,不包含折叠的节点
const intersections = getIntersectingNodes(node); const intersections = getIntersectingNodes(node).filter((node) => !node.data.isFolded);
// 获取所有与当前节点相交的节点中,类型为 loop 的节点 // 获取所有与当前节点相交的节点中,类型为 loop 的节点
const parentNode = intersections.find((item) => item.type === FlowNodeTypeEnum.loop); const parentNode = intersections.find((item) => item.type === FlowNodeTypeEnum.loop);
@@ -544,9 +547,19 @@ export const useWorkflow = () => {
/* connect */ /* connect */
const onConnectStart = useCallback( const onConnectStart = useCallback(
(event: any, params: OnConnectStartParams) => { (event: any, params: OnConnectStartParams) => {
if (!params.nodeId) return;
const sourceNode = nodes.find((node) => node.id === params.nodeId);
if (sourceNode?.data.isFolded) {
return onChangeNode({
nodeId: params.nodeId,
type: 'attr',
key: 'isFolded',
value: false
});
}
setConnectingEdge(params); setConnectingEdge(params);
}, },
[setConnectingEdge] [nodes, setConnectingEdge, onChangeNode]
); );
const onConnectEnd = useCallback(() => { const onConnectEnd = useCallback(() => {
setConnectingEdge(undefined); setConnectingEdge(undefined);
@@ -581,7 +594,7 @@ export const useWorkflow = () => {
connect connect
}); });
}, },
[onConnect, t, toast] [onConnect, t, toast, nodes]
); );
/* edge */ /* edge */

View File

@@ -22,7 +22,7 @@ import { WorkflowContext } from '../../../context';
const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => { const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
const { t } = useTranslation(); const { t } = useTranslation();
const { nodeId, inputs, outputs } = data; const { nodeId, inputs, outputs, isFolded } = data;
const { onChangeNode, nodeList } = useContextSelector(WorkflowContext, (v) => v); const { onChangeNode, nodeList } = useContextSelector(WorkflowContext, (v) => v);
const { nodeWidth, nodeHeight } = useMemo(() => { const { nodeWidth, nodeHeight } = useMemo(() => {
@@ -53,14 +53,14 @@ const NodeLoop = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
return ( return (
<NodeCard <NodeCard
selected={selected} selected={selected}
maxW={'full'} maxW="full"
minW={900} {...(!isFolded && {
minH={900} minW: '900px',
w={nodeWidth} minH: '900px',
h={nodeHeight} w: nodeWidth,
menuForbid={{ h: nodeHeight
copy: true })}
}} menuForbid={{ copy: true }}
{...data} {...data}
> >
<Container position={'relative'} flex={1}> <Container position={'relative'} flex={1}>

View File

@@ -94,17 +94,15 @@ const NodeLoopStart = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
<NodeCard <NodeCard
selected={selected} selected={selected}
{...data} {...data}
w={'420px'}
h={'176px'}
menuForbid={{ menuForbid={{
copy: true, copy: true,
delete: true, delete: true,
debug: true debug: true
}} }}
> >
<Box px={4}> <Box px={4} w={'420px'} h={'116px'}>
{!loopItemInputType ? ( {!loopItemInputType ? (
<EmptyTip text={t('workflow:loop_start_tip')} py={0} mt={0} iconSize={'32px'} /> <EmptyTip text={t('workflow:loop_start_tip')} py={0} mt={4} iconSize={'32px'} />
) : ( ) : (
<Box bg={'white'} borderRadius={'md'} overflow={'hidden'} border={'base'}> <Box bg={'white'} borderRadius={'md'} overflow={'hidden'} border={'base'}>
<TableContainer> <TableContainer>

View File

@@ -56,11 +56,6 @@ const NodeUserSelect = ({ data, selected }: NodeProps<FlowNodeItemType>) => {
value: options.filter((input) => input.key !== item.key) value: options.filter((input) => input.key !== item.key)
} }
}); });
onChangeNode({
nodeId,
type: 'delOutput',
key: item.key
});
}} }}
/> />
</MyTooltip> </MyTooltip>

View File

@@ -6,7 +6,13 @@ import { NodeOutputKeyEnum } from '@fastgpt/global/core/workflow/constants';
import { useContextSelector } from 'use-context-selector'; import { useContextSelector } from 'use-context-selector';
import { WorkflowContext } from '../../../../context'; import { WorkflowContext } from '../../../../context';
export const ConnectionSourceHandle = ({ nodeId }: { nodeId: string }) => { export const ConnectionSourceHandle = ({
nodeId,
isFoldNode
}: {
nodeId: string;
isFoldNode?: boolean;
}) => {
const connectingEdge = useContextSelector(WorkflowContext, (ctx) => ctx.connectingEdge); const connectingEdge = useContextSelector(WorkflowContext, (ctx) => ctx.connectingEdge);
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList); const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
const edges = useContextSelector(WorkflowContext, (v) => v.edges); const edges = useContextSelector(WorkflowContext, (v) => v.edges);
@@ -26,8 +32,7 @@ export const ConnectionSourceHandle = ({ nodeId }: { nodeId: string }) => {
const rightTargetConnected = edges.some( const rightTargetConnected = edges.some(
(edge) => edge.targetHandle === getHandleId(nodeId, 'target', Position.Right) (edge) => edge.targetHandle === getHandleId(nodeId, 'target', Position.Right)
); );
if (!isFoldNode && (!node || !node?.sourceHandle?.right || rightTargetConnected)) return null;
if (!node || !node?.sourceHandle?.right || rightTargetConnected) return null;
return ( return (
<SourceHandle <SourceHandle
@@ -102,7 +107,7 @@ export const ConnectionSourceHandle = ({ nodeId }: { nodeId: string }) => {
TopHandlee, TopHandlee,
BottomHandlee BottomHandlee
}; };
}, [connectingEdge, edges, nodeId, nodeList]); }, [connectingEdge, edges, nodeId, nodeList, isFoldNode]);
return showSourceHandle ? ( return showSourceHandle ? (
<> <>

View File

@@ -32,8 +32,8 @@ const MySourceHandle = React.memo(function MySourceHandle({
const node = useMemo(() => nodes.find((node) => node.data.nodeId === nodeId), [nodes, nodeId]); const node = useMemo(() => nodes.find((node) => node.data.nodeId === nodeId), [nodes, nodeId]);
const connected = edges.some((edge) => edge.sourceHandle === handleId); const connected = edges.some((edge) => edge.sourceHandle === handleId);
const nodeFolded = node?.data.isFolded && edges.some((edge) => edge.source === nodeId);
const nodeIsHover = hoverNodeId === nodeId; const nodeIsHover = hoverNodeId === nodeId;
const active = useMemo( const active = useMemo(
() => nodeIsHover || node?.selected || connectingEdge?.handleId === handleId, () => nodeIsHover || node?.selected || connectingEdge?.handleId === handleId,
[nodeIsHover, node?.selected, connectingEdge, handleId] [nodeIsHover, node?.selected, connectingEdge, handleId]
@@ -73,7 +73,7 @@ const MySourceHandle = React.memo(function MySourceHandle({
}; };
} }
if (connected) { if (connected || nodeFolded) {
return { return {
styles: { styles: {
...connectedStyle, ...connectedStyle,
@@ -89,7 +89,7 @@ const MySourceHandle = React.memo(function MySourceHandle({
styles: undefined, styles: undefined,
showAddIcon: false showAddIcon: false
}; };
}, [active, connected, highlightStyle, translateStr, transform, connectedStyle]); }, [active, connected, nodeFolded, highlightStyle, translateStr, transform, connectedStyle]);
const RenderHandle = useMemo(() => { const RenderHandle = useMemo(() => {
return ( return (

View File

@@ -1,4 +1,4 @@
import React, { useCallback, useEffect, useMemo, useState } from 'react'; import React, { useCallback, useMemo } from 'react';
import { Box, Button, Card, Flex, Image } from '@chakra-ui/react'; import { Box, Button, Card, Flex, Image } from '@chakra-ui/react';
import MyIcon from '@fastgpt/web/components/common/Icon'; import MyIcon from '@fastgpt/web/components/common/Icon';
import Avatar from '@fastgpt/web/components/common/Avatar'; import Avatar from '@fastgpt/web/components/common/Avatar';
@@ -61,7 +61,8 @@ const NodeCard = (props: Props) => {
menuForbid, menuForbid,
isTool = false, isTool = false,
isError = false, isError = false,
debugResult debugResult,
isFolded
} = props; } = props;
const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList); const nodeList = useContextSelector(WorkflowContext, (v) => v.nodeList);
@@ -81,7 +82,12 @@ const NodeCard = (props: Props) => {
[isTool, nodeList] [isTool, nodeList]
); );
const node = nodeList.find((node) => node.nodeId === nodeId); const { node, parentNode } = useMemo(() => {
const node = nodeList.find((node) => node.nodeId === nodeId);
const parentNode = nodeList.find((n) => n.nodeId === node?.parentNodeId);
return { node, parentNode };
}, [nodeList, nodeId]);
const { openConfirm: onOpenConfirmSync, ConfirmModal: ConfirmSyncModal } = useConfirm({ const { openConfirm: onOpenConfirmSync, ConfirmModal: ConfirmSyncModal } = useConfirm({
content: t('app:module.Confirm Sync') content: t('app:module.Confirm Sync')
}); });
@@ -155,6 +161,31 @@ const NodeCard = (props: Props) => {
{/* avatar and name */} {/* avatar and name */}
<Flex alignItems={'center'}> <Flex alignItems={'center'}>
<Box
mr={2}
cursor={'pointer'}
rounded={'sm'}
_hover={{ bg: 'myGray.200' }}
onClick={() => {
onChangeNode({
nodeId,
type: 'attr',
key: 'isFolded',
value: !isFolded
});
}}
>
{!isFolded ? (
<MyIcon name={'core/chat/chevronDown'} w={'24px'} h={'24px'} color={'myGray.500'} />
) : (
<MyIcon
name={'core/chat/chevronRight'}
w={'24px'}
h={'24px'}
color={'myGray.500'}
/>
)}
</Box>
<Avatar src={avatar} borderRadius={'sm'} objectFit={'contain'} w={'30px'} h={'30px'} /> <Avatar src={avatar} borderRadius={'sm'} objectFit={'contain'} w={'30px'} h={'30px'} />
<Box ml={3} fontSize={'md'} fontWeight={'medium'}> <Box ml={3} fontSize={'md'} fontWeight={'medium'}>
{t(name as any)} {t(name as any)}
@@ -250,19 +281,21 @@ const NodeCard = (props: Props) => {
ConfirmSyncModal, ConfirmSyncModal,
onOpenCustomTitleModal, onOpenCustomTitleModal,
onChangeNode, onChangeNode,
toast toast,
isFolded
]); ]);
const RenderHandle = useMemo(() => { const RenderHandle = useMemo(() => {
return ( return (
<> <>
<ConnectionSourceHandle nodeId={nodeId} /> <ConnectionSourceHandle nodeId={nodeId} isFoldNode={isFolded} />
<ConnectionTargetHandle nodeId={nodeId} /> <ConnectionTargetHandle nodeId={nodeId} />
</> </>
); );
}, [nodeId]); }, [nodeId, isFolded]);
return ( return (
<Flex <Flex
hidden={parentNode?.isFolded}
flexDirection={'column'} flexDirection={'column'}
minW={minW} minW={minW}
maxW={maxW} maxW={maxW}
@@ -298,7 +331,7 @@ const NodeCard = (props: Props) => {
> >
<NodeDebugResponse nodeId={nodeId} debugResult={debugResult} /> <NodeDebugResponse nodeId={nodeId} debugResult={debugResult} />
{Header} {Header}
{children} {!isFolded && children}
{RenderHandle} {RenderHandle}
<EditTitleModal maxLength={20} /> <EditTitleModal maxLength={20} />

View File

@@ -561,9 +561,9 @@ const WorkflowContextProvider = ({
const checkResults = checkWorkflowNodeAndConnection({ nodes, edges }); const checkResults = checkWorkflowNodeAndConnection({ nodes, edges });
if (!checkResults) { if (!checkResults) {
const storeNodes = uiWorkflow2StoreWorkflow({ nodes, edges }); const storeWorkflow = uiWorkflow2StoreWorkflow({ nodes, edges });
return storeNodes; return storeWorkflow;
} else if (!hideTip) { } else if (!hideTip) {
checkResults.forEach((nodeId) => onUpdateNodeError(nodeId, true)); checkResults.forEach((nodeId) => onUpdateNodeError(nodeId, true));
toast({ toast({

View File

@@ -25,7 +25,8 @@ export const uiWorkflow2StoreWorkflow = ({
version: item.data.version, version: item.data.version,
inputs: item.data.inputs, inputs: item.data.inputs,
outputs: item.data.outputs, outputs: item.data.outputs,
pluginId: item.data.pluginId pluginId: item.data.pluginId,
isFolded: item.data.isFolded
})); }));
// get all handle // get all handle
@@ -49,6 +50,8 @@ export const uiWorkflow2StoreWorkflow = ({
(item) => { (item) => {
// Not in react flow page // Not in react flow page
if (!reactFlowViewport) return true; if (!reactFlowViewport) return true;
const currentSourceNode = nodes.find((node) => node.data.nodeId === item.source);
if (currentSourceNode?.data.isFolded) return true;
return handleIdList.includes(item.sourceHandle) && handleIdList.includes(item.targetHandle); return handleIdList.includes(item.sourceHandle) && handleIdList.includes(item.targetHandle);
} }
); );

View File

@@ -532,7 +532,8 @@ export const compareSnapshot = (
name: node.data.name, name: node.data.name,
intro: node.data.intro, intro: node.data.intro,
avatar: node.data.avatar, avatar: node.data.avatar,
version: node.data.version version: node.data.version,
isFolded: node.data.isFolded
} }
})); }));
}; };