diff --git a/web/src/components/xyflow/base-node.tsx b/web/src/components/xyflow/base-node.tsx index 5ecc1b4d9..222afdaa1 100644 --- a/web/src/components/xyflow/base-node.tsx +++ b/web/src/components/xyflow/base-node.tsx @@ -9,7 +9,7 @@ export const BaseNode = forwardRef<
) { - const { theme } = useTheme(); const getNode = useGraphStore((state) => state.getNode); const edges = useGraphStore((state) => state.edges); @@ -26,34 +26,25 @@ function InnerAgentNode({ }, [edges, getNode, id]); return ( - -
+ + {isNotParentAgent && ( <> - - + + > )} -
+
); } diff --git a/web/src/pages/agent/canvas/node/begin-node.tsx b/web/src/pages/agent/canvas/node/begin-node.tsx index 87fed0ee8..c13db7c6f 100644 --- a/web/src/pages/agent/canvas/node/begin-node.tsx +++ b/web/src/pages/agent/canvas/node/begin-node.tsx @@ -1,8 +1,6 @@ -import { useTheme } from '@/components/theme-provider'; import { IBeginNode } from '@/interfaces/database/flow'; -import { Handle, NodeProps, Position } from '@xyflow/react'; +import { NodeProps, Position } from '@xyflow/react'; import { Flex } from 'antd'; -import classNames from 'classnames'; import get from 'lodash/get'; import { memo } from 'react'; import { useTranslation } from 'react-i18next'; @@ -10,42 +8,31 @@ import { BeginQueryType, BeginQueryTypeIconMap, Operator, - operatorMap, } from '../../constant'; import { BeginQuery } from '../../interface'; import OperatorIcon from '../../operator-icon'; +import { CommonHandle } from './handle'; import { RightHandleStyle } from './handle-icon'; import styles from './index.less'; +import { NodeWrapper } from './node-wrapper'; // TODO: do not allow other nodes to connect to this node -function InnerBeginNode({ selected, data }: NodeProps) { +function InnerBeginNode({ data }: NodeProps) { const { t } = useTranslation(); const query: BeginQuery[] = get(data, 'form.query', []); - const { theme } = useTheme(); + return ( -
- + + > - +
{t(`flow.begin`)}
@@ -68,7 +55,7 @@ function InnerBeginNode({ selected, data }: NodeProps) { ); })}
-
+ ); } diff --git a/web/src/pages/agent/canvas/node/handle.tsx b/web/src/pages/agent/canvas/node/handle.tsx new file mode 100644 index 000000000..212208e60 --- /dev/null +++ b/web/src/pages/agent/canvas/node/handle.tsx @@ -0,0 +1,17 @@ +import { cn } from '@/lib/utils'; +import { Handle, HandleProps } from '@xyflow/react'; +import { Plus } from 'lucide-react'; + +export function CommonHandle({ className, ...props }: HandleProps) { + return ( + + + + ); +} diff --git a/web/src/pages/agent/canvas/node/index.tsx b/web/src/pages/agent/canvas/node/index.tsx index 286c3e080..d67ec040c 100644 --- a/web/src/pages/agent/canvas/node/index.tsx +++ b/web/src/pages/agent/canvas/node/index.tsx @@ -6,6 +6,7 @@ import { memo } from 'react'; import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; import styles from './index.less'; import NodeHeader from './node-header'; +import { ToolBar } from './toolbar'; function InnerRagNode({ id, @@ -15,33 +16,35 @@ function InnerRagNode({ }: NodeProps) { const { theme } = useTheme(); return ( -
- - - -
+ +
+ + + +
+
); } diff --git a/web/src/pages/agent/canvas/node/message-node.tsx b/web/src/pages/agent/canvas/node/message-node.tsx index 5ed80dcbc..5c76df9a2 100644 --- a/web/src/pages/agent/canvas/node/message-node.tsx +++ b/web/src/pages/agent/canvas/node/message-node.tsx @@ -1,13 +1,15 @@ -import { useTheme } from '@/components/theme-provider'; import { IMessageNode } from '@/interfaces/database/flow'; -import { Handle, NodeProps, Position } from '@xyflow/react'; +import { NodeProps, Position } from '@xyflow/react'; import { Flex } from 'antd'; import classNames from 'classnames'; import { get } from 'lodash'; import { memo } from 'react'; +import { CommonHandle } from './handle'; import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; import styles from './index.less'; import NodeHeader from './node-header'; +import { NodeWrapper } from './node-wrapper'; +import { ToolBar } from './toolbar'; function InnerMessageNode({ id, @@ -16,52 +18,43 @@ function InnerMessageNode({ selected, }: NodeProps) { const messages: string[] = get(data, 'form.messages', []); - const { theme } = useTheme(); return ( -
- - - 0, - })} - > + + + + + 0, + })} + > - - {messages.map((message, idx) => { - return ( -
- {message} -
- ); - })} -
-
+ + {messages.map((message, idx) => { + return ( +
+ {message} +
+ ); + })} +
+ + ); } diff --git a/web/src/pages/agent/canvas/node/node-header.tsx b/web/src/pages/agent/canvas/node/node-header.tsx index 216657103..9647af1ed 100644 --- a/web/src/pages/agent/canvas/node/node-header.tsx +++ b/web/src/pages/agent/canvas/node/node-header.tsx @@ -1,20 +1,7 @@ -import { useTranslate } from '@/hooks/common-hooks'; -import { Flex } from 'antd'; -import { Copy, Play, Trash2 } from 'lucide-react'; -import { Operator, operatorMap } from '../../constant'; +import { cn } from '@/lib/utils'; +import { memo } from 'react'; +import { Operator } from '../../constant'; import OperatorIcon from '../../operator-icon'; -import { needsSingleStepDebugging } from '../../utils'; -import NodeDropdown from './dropdown'; -import { NextNodePopover } from './popover'; - -import { - TooltipContent, - TooltipNode, - TooltipTrigger, -} from '@/components/xyflow/tooltip-node'; -import { Position } from '@xyflow/react'; -import { PropsWithChildren, memo } from 'react'; -import { RunTooltip } from '../../flow-tooltip'; interface IProps { id: string; label: string; @@ -24,55 +11,20 @@ interface IProps { wrapperClassName?: string; } -const ExcludedRunStateOperators = [Operator.Answer]; - -export function RunStatus({ id, name, label }: IProps) { - const { t } = useTranslate('flow'); - return ( -
- {needsSingleStepDebugging(label) && ( - - - // data-play is used to trigger single step debugging - )} - - - {t('operationResults')} - - -
- ); -} - const InnerNodeHeader = ({ label, - id, name, - gap = 4, className, wrapperClassName, }: IProps) => { return ( -
- {!ExcludedRunStateOperators.includes(label as Operator) && ( - - )} - - +
+
+ {name} - - +
); }; @@ -80,37 +32,3 @@ const InnerNodeHeader = ({ const NodeHeader = memo(InnerNodeHeader); export default NodeHeader; - -function IconWrapper({ children }: PropsWithChildren) { - return ( -
- {children} -
- ); -} - -type ToolBarProps = { - selected?: boolean | undefined; -} & PropsWithChildren; - -export function ToolBar({ selected, children }: ToolBarProps) { - return ( - - {children} - - -
- - - - - - - - - -
-
-
- ); -} diff --git a/web/src/pages/agent/canvas/node/node-wrapper.tsx b/web/src/pages/agent/canvas/node/node-wrapper.tsx new file mode 100644 index 000000000..8754cf103 --- /dev/null +++ b/web/src/pages/agent/canvas/node/node-wrapper.tsx @@ -0,0 +1,18 @@ +import { cn } from '@/lib/utils'; +import { HTMLAttributes, PropsWithChildren } from 'react'; + +export function NodeWrapper({ + children, + className, +}: PropsWithChildren & HTMLAttributes) { + return ( +
+ {children} +
+ ); +} diff --git a/web/src/pages/agent/canvas/node/retrieval-node.tsx b/web/src/pages/agent/canvas/node/retrieval-node.tsx index 708aebd05..2b2171961 100644 --- a/web/src/pages/agent/canvas/node/retrieval-node.tsx +++ b/web/src/pages/agent/canvas/node/retrieval-node.tsx @@ -1,15 +1,17 @@ -import { useTheme } from '@/components/theme-provider'; import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks'; import { IRetrievalNode } from '@/interfaces/database/flow'; import { UserOutlined } from '@ant-design/icons'; -import { Handle, NodeProps, Position } from '@xyflow/react'; +import { NodeProps, Position } from '@xyflow/react'; import { Avatar, Flex } from 'antd'; import classNames from 'classnames'; import { get } from 'lodash'; import { memo, useMemo } from 'react'; +import { CommonHandle } from './handle'; import { LeftHandleStyle, RightHandleStyle } from './handle-icon'; import styles from './index.less'; import NodeHeader from './node-header'; +import { NodeWrapper } from './node-wrapper'; +import { ToolBar } from './toolbar'; function InnerRetrievalNode({ id, @@ -18,7 +20,6 @@ function InnerRetrievalNode({ selected, }: NodeProps) { const knowledgeBaseIds: string[] = get(data, 'form.kb_ids', []); - const { theme } = useTheme(); const { list: knowledgeList } = useFetchKnowledgeList(true); const knowledgeBases = useMemo(() => { return knowledgeBaseIds.map((x) => { @@ -32,58 +33,52 @@ function InnerRetrievalNode({ }, [knowledgeList, knowledgeBaseIds]); return ( -
- - - 0, - })} - > - - {knowledgeBases.map((knowledge) => { - return ( -
- - } - src={knowledge.avatar} - /> - - {knowledge.name} + + + + + 0, + })} + > + + {knowledgeBases.map((knowledge) => { + return ( +
+ + } + src={knowledge.avatar} + /> + + {knowledge.name} + - -
- ); - })} -
-
+
+ ); + })} + + + ); } diff --git a/web/src/pages/agent/canvas/node/switch-node.tsx b/web/src/pages/agent/canvas/node/switch-node.tsx index a7c25dcfa..1fbd5919e 100644 --- a/web/src/pages/agent/canvas/node/switch-node.tsx +++ b/web/src/pages/agent/canvas/node/switch-node.tsx @@ -1,16 +1,16 @@ import { IconFont } from '@/components/icon-font'; -import { useTheme } from '@/components/theme-provider'; import { Card, CardContent } from '@/components/ui/card'; import { ISwitchCondition, ISwitchNode } from '@/interfaces/database/flow'; -import { Handle, NodeProps, Position } from '@xyflow/react'; -import classNames from 'classnames'; +import { NodeProps, Position } from '@xyflow/react'; import { memo, useCallback } from 'react'; import { SwitchOperatorOptions } from '../../constant'; import { useGetComponentLabelByValue } from '../../hooks/use-get-begin-query'; +import { CommonHandle } from './handle'; import { RightHandleStyle } from './handle-icon'; import { useBuildSwitchHandlePositions } from './hooks'; -import styles from './index.less'; -import NodeHeader, { ToolBar } from './node-header'; +import NodeHeader from './node-header'; +import { NodeWrapper } from './node-wrapper'; +import { ToolBar } from './toolbar'; const getConditionKey = (idx: number, length: number) => { if (idx === 0 && length !== 1) { @@ -58,32 +58,16 @@ const ConditionBlock = ({ function InnerSwitchNode({ id, data, selected }: NodeProps) { const { positions } = useBuildSwitchHandlePositions({ data, id }); - const { theme } = useTheme(); return ( - -
- + + - + > +
{positions.map((position, idx) => { return ( @@ -103,20 +87,19 @@ function InnerSwitchNode({ id, data, selected }: NodeProps) { > )}
- + > ); })}
- +
); } diff --git a/web/src/pages/agent/canvas/node/toolbar.tsx b/web/src/pages/agent/canvas/node/toolbar.tsx new file mode 100644 index 000000000..53a5f8a11 --- /dev/null +++ b/web/src/pages/agent/canvas/node/toolbar.tsx @@ -0,0 +1,74 @@ +import { + TooltipContent, + TooltipNode, + TooltipTrigger, +} from '@/components/xyflow/tooltip-node'; +import { Position } from '@xyflow/react'; +import { Copy, Play, Trash2 } from 'lucide-react'; +import { MouseEventHandler, PropsWithChildren, useCallback } from 'react'; +import { Operator } from '../../constant'; +import { useDuplicateNode } from '../../hooks'; +import useGraphStore from '../../store'; + +function IconWrapper({ children }: PropsWithChildren) { + return ( +
+ {children} +
+ ); +} + +type ToolBarProps = { + selected?: boolean | undefined; + label: string; + id: string; +} & PropsWithChildren; + +export function ToolBar({ selected, children, label, id }: ToolBarProps) { + const deleteNodeById = useGraphStore((store) => store.deleteNodeById); + const deleteIterationNodeById = useGraphStore( + (store) => store.deleteIterationNodeById, + ); + + const deleteNode: MouseEventHandler = useCallback( + (e) => { + e.stopPropagation(); + if (label === Operator.Iteration) { + deleteIterationNodeById(id); + } else { + deleteNodeById(id); + } + }, + [deleteIterationNodeById, deleteNodeById, id, label], + ); + + const duplicateNode = useDuplicateNode(); + + const handleDuplicate: MouseEventHandler = useCallback( + (e) => { + e.stopPropagation(); + duplicateNode(id, label); + }, + [duplicateNode, id, label], + ); + + return ( + + {children} + + +
+ + + + + + + + + +
+
+
+ ); +} diff --git a/web/src/pages/agent/operator-icon.tsx b/web/src/pages/agent/operator-icon.tsx index e5498d668..3c2479492 100644 --- a/web/src/pages/agent/operator-icon.tsx +++ b/web/src/pages/agent/operator-icon.tsx @@ -9,7 +9,7 @@ interface IProps { } export const OperatorIconMap = { - [Operator.Retrieval]: 'retrival-0', + [Operator.Retrieval]: 'KR', // [Operator.Generate]: MergeCellsOutlined, // [Operator.Answer]: SendOutlined, [Operator.Begin]: CirclePlay,