From 0a78920bffc9e582e7d7351c4be4aa0121348699 Mon Sep 17 00:00:00 2001 From: balibabu Date: Mon, 27 Oct 2025 19:37:52 +0800 Subject: [PATCH] Feat: Modify the style of the agent operator form #10703 (#10821) ### What problem does this PR solve? Feat: Modify the style of the agent operator form #10703 ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- web/src/components/collapse.tsx | 2 +- web/src/components/large-model-form-field.tsx | 2 +- .../originui/select-with-search.tsx | 2 +- web/src/components/ui/input.tsx | 2 +- web/src/components/ui/select.tsx | 4 +- web/src/components/ui/separator.tsx | 2 +- web/src/components/ui/sheet.tsx | 4 +- web/src/pages/agent/form-sheet/next.tsx | 48 +++------- .../pages/agent/form-sheet/title-input.tsx | 61 +++++++++++++ .../agent/form/agent-form/agent-tools.tsx | 39 ++++++--- web/src/pages/agent/form/agent-form/index.tsx | 87 ++++++++----------- .../agent/form/begin-form/query-table.tsx | 87 +++++++++---------- .../form/components/prompt-editor/index.css | 14 --- .../form/components/prompt-editor/index.tsx | 6 +- .../prompt-editor/variable-node.tsx | 4 +- .../prompt-editor/variable-picker-plugin.tsx | 8 +- web/src/pages/agent/hooks/use-is-mcp.ts | 11 +++ 17 files changed, 204 insertions(+), 179 deletions(-) create mode 100644 web/src/pages/agent/form-sheet/title-input.tsx create mode 100644 web/src/pages/agent/hooks/use-is-mcp.ts diff --git a/web/src/components/collapse.tsx b/web/src/components/collapse.tsx index 3482929cc..463ff57cc 100644 --- a/web/src/components/collapse.tsx +++ b/web/src/components/collapse.tsx @@ -74,7 +74,7 @@ export function Collapse({
{rightContent}
- {children} + {children} ); } diff --git a/web/src/components/large-model-form-field.tsx b/web/src/components/large-model-form-field.tsx index 49b9d4db4..e805eb541 100644 --- a/web/src/components/large-model-form-field.tsx +++ b/web/src/components/large-model-form-field.tsx @@ -70,7 +70,7 @@ export function LargeModelFormField({ diff --git a/web/src/components/originui/select-with-search.tsx b/web/src/components/originui/select-with-search.tsx index 031fdddb1..4a1e1e74e 100644 --- a/web/src/components/originui/select-with-search.tsx +++ b/web/src/components/originui/select-with-search.tsx @@ -140,7 +140,7 @@ export const SelectWithSearch = forwardRef< ref={ref} disabled={disabled} className={cn( - 'bg-background hover:bg-background border-input w-full justify-between px-3 font-normal outline-offset-0 outline-none focus-visible:outline-[3px] [&_svg]:pointer-events-auto', + '!bg-bg-input hover:bg-background border-input w-full justify-between px-3 font-normal outline-offset-0 outline-none focus-visible:outline-[3px] [&_svg]:pointer-events-auto', triggerClassName, )} > diff --git a/web/src/components/ui/input.tsx b/web/src/components/ui/input.tsx index 580f9b9c7..e2acff4ac 100644 --- a/web/src/components/ui/input.tsx +++ b/web/src/components/ui/input.tsx @@ -31,7 +31,7 @@ const Input = React.forwardRef( span]:line-clamp-1', + 'flex h-8 w-full items-center bg-bg-input justify-between rounded-md border border-input px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1', className, )} {...props} @@ -291,7 +291,7 @@ export const RAGFlowSelect = forwardRef< onReset={handleReset} allowClear={allowClear} ref={ref} - className={cn('bg-bg-base', triggerClassName)} + className={triggerClassName} > {label} diff --git a/web/src/components/ui/separator.tsx b/web/src/components/ui/separator.tsx index c8fe673be..ba5ccfc60 100644 --- a/web/src/components/ui/separator.tsx +++ b/web/src/components/ui/separator.tsx @@ -18,7 +18,7 @@ const Separator = React.forwardRef< decorative={decorative} orientation={orientation} className={cn( - 'shrink-0 bg-border', + 'shrink-0 bg-border-button', orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]', className, )} diff --git a/web/src/components/ui/sheet.tsx b/web/src/components/ui/sheet.tsx index c24d58ff2..7ac191fef 100644 --- a/web/src/components/ui/sheet.tsx +++ b/web/src/components/ui/sheet.tsx @@ -31,7 +31,7 @@ const SheetOverlay = React.forwardRef< SheetOverlay.displayName = SheetPrimitive.Overlay.displayName; const sheetVariants = cva( - 'fixed z-50 gap-4 bg-text-title-invert rounded-lg p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500', + 'fixed z-50 gap-4 bg-bg-base rounded-lg p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500', { variants: { side: { @@ -73,7 +73,7 @@ const SheetContent = React.forwardRef< {children} {closeIcon ? ( - + Close ) : null} diff --git a/web/src/pages/agent/form-sheet/next.tsx b/web/src/pages/agent/form-sheet/next.tsx index 69b6af455..5c759a5e5 100644 --- a/web/src/pages/agent/form-sheet/next.tsx +++ b/web/src/pages/agent/form-sheet/next.tsx @@ -1,4 +1,3 @@ -import { Input } from '@/components/ui/input'; import { Sheet, SheetContent, @@ -10,17 +9,17 @@ import { IModalProps } from '@/interfaces/common'; import { RAGFlowNodeType } from '@/interfaces/database/flow'; import { cn } from '@/lib/utils'; import { lowerFirst } from 'lodash'; -import { Play, X } from 'lucide-react'; -import { useMemo } from 'react'; -import { BeginId, Operator } from '../constant'; +import { CirclePlay, X } from 'lucide-react'; +import { Operator } from '../constant'; import { AgentFormContext } from '../context'; import { RunTooltip } from '../flow-tooltip'; -import { useHandleNodeNameChange } from '../hooks/use-change-node-name'; +import { useIsMcp } from '../hooks/use-is-mcp'; import OperatorIcon from '../operator-icon'; import useGraphStore from '../store'; import { needsSingleStepDebugging } from '../utils'; import { FormConfigMap } from './form-config-map'; import SingleDebugSheet from './single-debug-sheet'; +import { TitleInput } from './title-input'; interface IProps { node?: RAGFlowNodeType; @@ -48,17 +47,7 @@ const FormSheet = ({ const OperatorForm = currentFormMap?.component ?? EmptyContent; - const { name, handleNameBlur, handleNameChange } = useHandleNodeNameChange({ - id: node?.id, - data: node?.data, - }); - - const isMcp = useMemo(() => { - return ( - operatorName === Operator.Tool && - Object.values(Operator).every((x) => x !== clickedToolId) - ); - }, [clickedToolId, operatorName]); + const isMcp = useIsMcp(operatorName); const { t } = useTranslate('flow'); @@ -75,36 +64,19 @@ const FormSheet = ({
- - {isMcp ? ( -
MCP Config
- ) : ( -
- - {node?.id === BeginId ? ( - {t(BeginId)} - ) : ( - - )} -
- )} - + {needsSingleStepDebugging(operatorName) && ( - )} - +
{isMcp || ( - + {t( `${lowerFirst(operatorName === Operator.Tool ? clickedToolId : operatorName)}Description`, )} diff --git a/web/src/pages/agent/form-sheet/title-input.tsx b/web/src/pages/agent/form-sheet/title-input.tsx new file mode 100644 index 000000000..e876e97ba --- /dev/null +++ b/web/src/pages/agent/form-sheet/title-input.tsx @@ -0,0 +1,61 @@ +import { Input } from '@/components/ui/input'; +import { RAGFlowNodeType } from '@/interfaces/database/agent'; +import { PenLine } from 'lucide-react'; +import { useCallback, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { BeginId, Operator } from '../constant'; +import { useHandleNodeNameChange } from '../hooks/use-change-node-name'; +import { useIsMcp } from '../hooks/use-is-mcp'; + +type TitleInputProps = { + node?: RAGFlowNodeType; +}; + +export function TitleInput({ node }: TitleInputProps) { + const { t } = useTranslation(); + const { name, handleNameBlur, handleNameChange } = useHandleNodeNameChange({ + id: node?.id, + data: node?.data, + }); + + const operatorName: Operator = node?.data.label as Operator; + + const isMcp = useIsMcp(operatorName); + + const [isEditingMode, setIsEditingMode] = useState(false); + + const switchIsEditingMode = useCallback(() => { + setIsEditingMode((prev) => !prev); + }, []); + + const handleBlur = useCallback(() => { + handleNameBlur(); + setIsEditingMode(false); + }, [handleNameBlur]); + + if (isMcp) { + return
MCP Config
; + } + + return ( +
+ {node?.id === BeginId ? ( + {t(BeginId)} + ) : isEditingMode ? ( + + ) : ( +
+ {name} + +
+ )} +
+ ); +} diff --git a/web/src/pages/agent/form/agent-form/agent-tools.tsx b/web/src/pages/agent/form/agent-form/agent-tools.tsx index 567d79164..2ac473686 100644 --- a/web/src/pages/agent/form/agent-form/agent-tools.tsx +++ b/web/src/pages/agent/form/agent-form/agent-tools.tsx @@ -28,18 +28,31 @@ import { useDeleteAgentNodeMCP } from './tool-popover/use-update-mcp'; import { useDeleteAgentNodeTools } from './tool-popover/use-update-tools'; import { useGetAgentMCPIds, useGetAgentToolNames } from './use-get-tools'; +type ToolCardProps = React.HTMLAttributes & + PropsWithChildren & { + isNodeTool?: boolean; + }; + export function ToolCard({ children, className, + isNodeTool = true, ...props -}: PropsWithChildren & React.HTMLAttributes) { +}: ToolCardProps) { const element = useMemo(() => { return ( - + {children} ); - }, [children, className, props]); + }, [children, className, isNodeTool, props]); if (children === Operator.Code) { return ( @@ -67,13 +80,13 @@ function ActionButton({ deleteRecord, record, edit }: ActionButtonProps) { }, [deleteRecord, record]); return ( -
+
- +
); } @@ -102,10 +115,10 @@ export function AgentTools() { return (
- {t('flow.tools')} -
    + {t('flow.tools')} +
      {toolNames.map((x) => ( - +
      {x} @@ -118,7 +131,7 @@ export function AgentTools() { ))} {mcpIds.map((id) => ( - + {findMcpById(id)?.name} - {t('flow.agent')} -
        + {t('flow.agent')} +
          {subBottomAgentNodeIds.map((id) => { const currentNode = getNode(id); return ( - + {currentNode?.data.name} - - {isSubAgent && } - - {findLlmByUuid(llmId)?.model_type === LlmModelType.Image2text && ( - + {isSubAgent && } + + {findLlmByUuid(llmId)?.model_type === LlmModelType.Image2text && ( + + )} + ( + + {t('flow.systemPrompt')} + + + + )} - - - + /> + {isSubAgent || ( ( - {t('flow.systemPrompt')} + {t('flow.userPrompt')} - +
          + +
          )} /> -
          - {isSubAgent || ( - - {/* */} - ( - - {t('flow.userPrompt')} - -
          - -
          -
          -
          - )} - /> -
          )} - - - - - + + + {t('flow.advancedSettings')}
      }> - +
      )} - +
      diff --git a/web/src/pages/agent/form/begin-form/query-table.tsx b/web/src/pages/agent/form/begin-form/query-table.tsx index 5701c49b1..19d84bd67 100644 --- a/web/src/pages/agent/form/begin-form/query-table.tsx +++ b/web/src/pages/agent/form/begin-form/query-table.tsx @@ -147,53 +147,48 @@ export function QueryTable({ data = [], deleteRecord, showModal }: IProps) { }); return ( -
      -
      - - - {table.getHeaderGroups().map((headerGroup) => ( - - {headerGroup.headers.map((header) => { - return ( - - {header.isPlaceholder - ? null - : flexRender( - header.column.columnDef.header, - header.getContext(), - )} - - ); - })} +
      +
      + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} + + ); + })} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} - ))} - - - {table.getRowModel().rows?.length ? ( - table.getRowModel().rows.map((row) => ( - - {row.getVisibleCells().map((cell) => ( - - {flexRender( - cell.column.columnDef.cell, - cell.getContext(), - )} - - ))} - - )) - ) : ( - - )} - -
      -
      + )) + ) : ( + + )} + +
      ); } diff --git a/web/src/pages/agent/form/components/prompt-editor/index.css b/web/src/pages/agent/form/components/prompt-editor/index.css index 8f3050647..a7abebc5f 100644 --- a/web/src/pages/agent/form/components/prompt-editor/index.css +++ b/web/src/pages/agent/form/components/prompt-editor/index.css @@ -1,5 +1,4 @@ .typeahead-popover { - background: #fff; box-shadow: 0px 5px 10px rgba(0, 0, 0, 0.3); border-radius: 8px; position: fixed; @@ -10,16 +9,6 @@ list-style: none; margin: 0; max-height: 200px; - overflow-y: scroll; -} - -.typeahead-popover ul::-webkit-scrollbar { - display: none; -} - -.typeahead-popover ul { - -ms-overflow-style: none; - scrollbar-width: none; } .typeahead-popover ul li { @@ -28,7 +17,6 @@ font-size: 14px; outline: none; cursor: pointer; - border-radius: 8px; } .typeahead-popover ul li.selected { @@ -37,7 +25,6 @@ .typeahead-popover li { margin: 0 8px 0 8px; - color: #050505; cursor: pointer; line-height: 16px; font-size: 15px; @@ -45,7 +32,6 @@ align-content: center; flex-direction: row; flex-shrink: 0; - background-color: #fff; border: 0; } diff --git a/web/src/pages/agent/form/components/prompt-editor/index.tsx b/web/src/pages/agent/form/components/prompt-editor/index.tsx index c0242293a..afff80e29 100644 --- a/web/src/pages/agent/form/components/prompt-editor/index.tsx +++ b/web/src/pages/agent/form/components/prompt-editor/index.tsx @@ -89,7 +89,7 @@ function PromptContent({ return (
      {showToolbar && (
      @@ -107,7 +107,7 @@ function PromptContent({ )} { decorate(): ReactNode { let content: ReactNode = ( -
      {this.__label}
      +
      {this.__label}
      ); if (this.__parentLabel) { content = ( @@ -62,7 +62,7 @@ export class VariableNode extends DecoratorNode { ); } return ( -
      +
      {content}
      ); diff --git a/web/src/pages/agent/form/components/prompt-editor/variable-picker-plugin.tsx b/web/src/pages/agent/form/components/prompt-editor/variable-picker-plugin.tsx index cb4905b45..42f06b2cc 100644 --- a/web/src/pages/agent/form/components/prompt-editor/variable-picker-plugin.tsx +++ b/web/src/pages/agent/form/components/prompt-editor/variable-picker-plugin.tsx @@ -91,13 +91,13 @@ function VariablePickerMenuItem({ id={'typeahead-item-' + index} >
      - {option.title} + {option.title}
        {option.options.map((x) => (
      • selectOptionAndCleanUp(x)} - className="hover:bg-slate-300 p-1" + className="hover:bg-bg-card p-1 text-text-primary rounded-sm" > {x.label}
      • @@ -335,8 +335,8 @@ export default function VariablePickerMenuPlugin({ const nextOptions = buildNextOptions(); return anchorElementRef.current && nextOptions.length ? ReactDOM.createPortal( -
        -
          +
          +
            {nextOptions.map((option, i: number) => ( state.clickedToolId); + + return ( + operatorName === Operator.Tool && + Object.values(Operator).every((x) => x !== clickedToolId) + ); +}