diff --git a/web/src/components/collapse.tsx b/web/src/components/collapse.tsx index 46b171229..722652d12 100644 --- a/web/src/components/collapse.tsx +++ b/web/src/components/collapse.tsx @@ -5,6 +5,8 @@ import { } from '@/components/ui/collapsible'; import { cn } from '@/lib/utils'; import { CollapsibleProps } from '@radix-ui/react-collapsible'; +import { ChevronDown, ChevronUp } from 'lucide-react'; +import * as React from 'react'; import { PropsWithChildren, ReactNode, @@ -67,3 +69,53 @@ export function Collapse({ ); } + +export type NodeCollapsibleProps = { + items?: T; + children: (item: T[0], idx: number) => ReactNode; + className?: string; +}; +export function NodeCollapsible({ + items = [] as unknown as T, + children, + className, +}: NodeCollapsibleProps) { + const [isOpen, setIsOpen] = React.useState(false); + + const nextClassName = cn('space-y-2', className); + + const nextItems = items.every((x) => Array.isArray(x)) ? items.flat() : items; + + return ( + + {nextItems.slice(0, 3).map(children)} + + {nextItems.slice(3).map(children)} + + {nextItems.length > 3 && ( + e.stopPropagation()} + className="absolute left-1/2 -translate-x-1/2 bottom-0 translate-y-1/2 cursor-pointer" + > +
+ {isOpen ? ( + + ) : ( + + )} +
+
+ )} +
+ ); +} diff --git a/web/src/components/ui/button.tsx b/web/src/components/ui/button.tsx index bd1c443bc..500081cf2 100644 --- a/web/src/components/ui/button.tsx +++ b/web/src/components/ui/button.tsx @@ -17,7 +17,7 @@ const buttonVariants = cva( outline: 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50', secondary: - 'bg-bg-input text-secondary-foreground shadow-xs hover:bg-bg-input/80', + 'bg-bg-input text-text-primary shadow-xs hover:bg-bg-input/80 border border-border-button', ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50', link: 'text-primary underline-offset-4 hover:underline', diff --git a/web/src/pages/agent/canvas/node/retrieval-node.tsx b/web/src/pages/agent/canvas/node/retrieval-node.tsx index 31cfa78f9..75c9e6bd4 100644 --- a/web/src/pages/agent/canvas/node/retrieval-node.tsx +++ b/web/src/pages/agent/canvas/node/retrieval-node.tsx @@ -1,3 +1,4 @@ +import { NodeCollapsible } from '@/components/collapse'; import { RAGFlowAvatar } from '@/components/ragflow-avatar'; import { useFetchKnowledgeList } from '@/hooks/knowledge-hooks'; import { IRetrievalNode } from '@/interfaces/database/flow'; @@ -44,8 +45,8 @@ function InnerRetrievalNode({ [styles.nodeHeader]: knowledgeBaseIds.length > 0, })} > -
- {knowledgeBaseIds.map((id) => { + + {(id) => { const item = knowledgeList.find((y) => id === y.id); const label = getLabel(id); @@ -63,8 +64,8 @@ function InnerRetrievalNode({ ); - })} -
+ }} + ); diff --git a/web/src/pages/agent/canvas/node/tool-node.tsx b/web/src/pages/agent/canvas/node/tool-node.tsx index b5212f3ad..9f9733537 100644 --- a/web/src/pages/agent/canvas/node/tool-node.tsx +++ b/web/src/pages/agent/canvas/node/tool-node.tsx @@ -1,3 +1,4 @@ +import { NodeCollapsible } from '@/components/collapse'; import { IAgentForm, IToolNode } from '@/interfaces/database/agent'; import { Handle, NodeProps, Position } from '@xyflow/react'; import { get } from 'lodash'; @@ -51,32 +52,38 @@ function InnerToolNode({ isConnectable={isConnectable} className="!bg-accent-primary !size-2" > -
    - {tools.map((x) => ( - -
    - - {x.component_name} -
    -
    - ))} + + {(x) => { + if ('mcp_id' in x) { + const mcp = x as unknown as IAgentForm['mcp'][number]; + return ( + + {findMcpById(mcp.mcp_id)?.name} + + ); + } - {mcpList.map((x) => ( - - {findMcpById(x.mcp_id)?.name} - - ))} -
+ const tool = x as unknown as IAgentForm['tools'][number]; + return ( + +
+ + {tool.component_name} +
+
+ ); + }} + ); } diff --git a/web/src/pages/data-flow/canvas/node/parser-node.tsx b/web/src/pages/data-flow/canvas/node/parser-node.tsx index c3d67b880..15539d0b8 100644 --- a/web/src/pages/data-flow/canvas/node/parser-node.tsx +++ b/web/src/pages/data-flow/canvas/node/parser-node.tsx @@ -1,3 +1,4 @@ +import { NodeCollapsible } from '@/components/collapse'; import { BaseNode } from '@/interfaces/database/agent'; import { NodeProps, Position } from '@xyflow/react'; import { memo } from 'react'; @@ -37,17 +38,18 @@ function ParserNode({ isConnectableEnd={false} > -
- {data.form?.setups.map((x, idx) => ( + + + {(x, idx) => ( Parser {idx + 1} {t(`dataflow.fileFormatOptions.${x.fileFormat}`)} - ))} -
+ )} + ); }