### What problem does this PR solve? Feat: Adjust the style of the agent canvas connection line #3221 ### Type of change - [x] New Feature (non-breaking change which adds functionality)tags/v0.20.0
| @@ -1,31 +0,0 @@ | |||
| .edgeButton { | |||
| width: 14px; | |||
| height: 14px; | |||
| background: #eee; | |||
| border: 1px solid #fff; | |||
| padding: 0; | |||
| cursor: pointer; | |||
| border-radius: 50%; | |||
| font-size: 10px; | |||
| line-height: 1; | |||
| } | |||
| .edgeButton:hover { | |||
| box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.08); | |||
| } | |||
| .edgeButtonDark { | |||
| width: 14px; | |||
| height: 14px; | |||
| background: #0e0c0c; | |||
| border: 1px solid #fff; | |||
| padding: 0; | |||
| cursor: pointer; | |||
| border-radius: 50%; | |||
| font-size: 10px; | |||
| line-height: 1; | |||
| } | |||
| .edgeButtonDark:hover { | |||
| box-shadow: 0 0 2px 2px rgba(0, 0, 0, 0.08); | |||
| } | |||
| @@ -7,12 +7,10 @@ import { | |||
| } from '@xyflow/react'; | |||
| import useGraphStore from '../../store'; | |||
| import { useTheme } from '@/components/theme-provider'; | |||
| import { useFetchAgent } from '@/hooks/use-agent-request'; | |||
| import { cn } from '@/lib/utils'; | |||
| import { useMemo } from 'react'; | |||
| import { NodeHandleId, Operator } from '../../constant'; | |||
| import styles from './index.less'; | |||
| export function ButtonEdge({ | |||
| id, | |||
| @@ -39,9 +37,8 @@ export function ButtonEdge({ | |||
| targetY, | |||
| targetPosition, | |||
| }); | |||
| const { theme } = useTheme(); | |||
| const selectedStyle = useMemo(() => { | |||
| return selected ? { strokeWidth: 2, stroke: '#1677ff' } : {}; | |||
| return selected ? { strokeWidth: 1, stroke: 'rgba(76, 164, 231, 1)' } : {}; | |||
| }, [selected]); | |||
| const onEdgeClick = () => { | |||
| @@ -71,7 +68,7 @@ export function ButtonEdge({ | |||
| // The set of elements following source | |||
| const slicedGraphPath = graphPath.slice(idx + 1); | |||
| if (slicedGraphPath.some((x) => x === target)) { | |||
| return { strokeWidth: 2, stroke: 'red' }; | |||
| return { strokeWidth: 1, stroke: 'red' }; | |||
| } | |||
| } | |||
| return {}; | |||
| @@ -91,6 +88,7 @@ export function ButtonEdge({ | |||
| path={edgePath} | |||
| markerEnd={markerEnd} | |||
| style={{ ...style, ...selectedStyle, ...highlightStyle }} | |||
| className="text-text-sub-title" | |||
| /> | |||
| <EdgeLabelRenderer> | |||
| @@ -108,7 +106,7 @@ export function ButtonEdge({ | |||
| > | |||
| <button | |||
| className={cn( | |||
| theme === 'dark' ? styles.edgeButtonDark : styles.edgeButton, | |||
| 'size-3.5 border border-text-delete-red text-text-delete-red rounded-full leading-none', | |||
| 'invisible', | |||
| { visible }, | |||
| )} | |||
| @@ -202,8 +202,8 @@ function AgentCanvas({ drawerVisible, hideDrawer }: IProps) { | |||
| type: 'buttonEdge', | |||
| markerEnd: 'logo', | |||
| style: { | |||
| strokeWidth: 2, | |||
| stroke: 'rgb(202 197 245)', | |||
| strokeWidth: 1, | |||
| stroke: 'rgba(91, 93, 106, 1)', | |||
| }, | |||
| zIndex: 1001, // https://github.com/xyflow/xyflow/discussions/3498 | |||
| }} | |||
| @@ -25,7 +25,7 @@ function InnerAgentNode({ | |||
| return ( | |||
| <ToolBar selected={selected} id={id} label={data.label}> | |||
| <NodeWrapper> | |||
| <NodeWrapper selected={selected}> | |||
| {isHeadAgent && ( | |||
| <> | |||
| <CommonHandle | |||
| @@ -18,12 +18,12 @@ import styles from './index.less'; | |||
| import { NodeWrapper } from './node-wrapper'; | |||
| // TODO: do not allow other nodes to connect to this node | |||
| function InnerBeginNode({ data, id }: NodeProps<IBeginNode>) { | |||
| function InnerBeginNode({ data, id, selected }: NodeProps<IBeginNode>) { | |||
| const { t } = useTranslation(); | |||
| const inputs: Record<string, BeginQuery> = get(data, 'form.inputs', {}); | |||
| return ( | |||
| <NodeWrapper> | |||
| <NodeWrapper selected={selected}> | |||
| <CommonHandle | |||
| type="source" | |||
| position={Position.Right} | |||
| @@ -19,7 +19,7 @@ export function InnerCategorizeNode({ | |||
| const { positions } = useBuildCategorizeHandlePositions({ data, id }); | |||
| return ( | |||
| <ToolBar selected={selected} id={id} label={data.label}> | |||
| <NodeWrapper> | |||
| <NodeWrapper selected={selected}> | |||
| <CommonHandle | |||
| type="target" | |||
| position={Position.Left} | |||
| @@ -16,7 +16,7 @@ function InnerRagNode({ | |||
| }: NodeProps<IRagNode>) { | |||
| return ( | |||
| <ToolBar selected={selected} id={id} label={data.label}> | |||
| <NodeWrapper> | |||
| <NodeWrapper selected={selected}> | |||
| <CommonHandle | |||
| id={NodeHandleId.End} | |||
| type="target" | |||
| @@ -67,9 +67,10 @@ export function InnerIterationNode({ | |||
| function InnerIterationStartNode({ | |||
| isConnectable = true, | |||
| id, | |||
| selected, | |||
| }: NodeProps<IIterationStartNode>) { | |||
| return ( | |||
| <NodeWrapper className="w-20"> | |||
| <NodeWrapper className="w-20" selected={selected}> | |||
| <CommonHandle | |||
| type="source" | |||
| position={Position.Right} | |||
| @@ -15,7 +15,7 @@ export function InnerLogicNode({ | |||
| }: NodeProps<ILogicNode>) { | |||
| return ( | |||
| <ToolBar selected={selected} id={id} label={data.label}> | |||
| <NodeWrapper> | |||
| <NodeWrapper selected={selected}> | |||
| <CommonHandle | |||
| id="c" | |||
| type="source" | |||
| @@ -21,7 +21,7 @@ function InnerMessageNode({ | |||
| const messages: string[] = get(data, 'form.messages', []); | |||
| return ( | |||
| <ToolBar selected={selected} id={id} label={data.label}> | |||
| <NodeWrapper> | |||
| <NodeWrapper selected={selected}> | |||
| <CommonHandle | |||
| type="target" | |||
| position={Position.Left} | |||
| @@ -1,14 +1,14 @@ | |||
| import { cn } from '@/lib/utils'; | |||
| import { HTMLAttributes, PropsWithChildren } from 'react'; | |||
| import { HTMLAttributes } from 'react'; | |||
| export function NodeWrapper({ | |||
| children, | |||
| className, | |||
| }: PropsWithChildren & HTMLAttributes<HTMLDivElement>) { | |||
| type IProps = HTMLAttributes<HTMLDivElement> & { selected?: boolean }; | |||
| export function NodeWrapper({ children, className, selected }: IProps) { | |||
| return ( | |||
| <section | |||
| className={cn( | |||
| 'bg-background-header-bar p-2.5 rounded-md w-[200px] text-xs', | |||
| { 'border border-background-checked': selected }, | |||
| className, | |||
| )} | |||
| > | |||
| @@ -24,7 +24,7 @@ const FormSchema = z.object({ | |||
| text: z.string(), | |||
| }); | |||
| function NoteNode({ data, id }: NodeProps<INoteNode>) { | |||
| function NoteNode({ data, id, selected }: NodeProps<INoteNode>) { | |||
| const { t } = useTranslation(); | |||
| const form = useForm<z.infer<typeof FormSchema>>({ | |||
| @@ -37,7 +37,10 @@ function NoteNode({ data, id }: NodeProps<INoteNode>) { | |||
| useWatchFormChange(id, form); | |||
| return ( | |||
| <NodeWrapper className="p-0 w-full h-full flex flex-col rounded-md "> | |||
| <NodeWrapper | |||
| className="p-0 w-full h-full flex flex-col rounded-md " | |||
| selected={selected} | |||
| > | |||
| <NodeResizeControl minWidth={190} minHeight={128} style={controlStyle}> | |||
| <ResizeIcon /> | |||
| </NodeResizeControl> | |||
| @@ -35,7 +35,7 @@ function InnerRetrievalNode({ | |||
| return ( | |||
| <ToolBar selected={selected} id={id} label={data.label}> | |||
| <NodeWrapper> | |||
| <NodeWrapper selected={selected}> | |||
| <CommonHandle | |||
| id={NodeHandleId.End} | |||
| type="target" | |||
| @@ -65,7 +65,7 @@ function InnerSwitchNode({ id, data, selected }: NodeProps<ISwitchNode>) { | |||
| const { positions } = useBuildSwitchHandlePositions({ data, id }); | |||
| return ( | |||
| <ToolBar selected={selected} id={id} label={data.label}> | |||
| <NodeWrapper> | |||
| <NodeWrapper selected={selected}> | |||
| <CommonHandle | |||
| type="target" | |||
| position={Position.Left} | |||
| @@ -8,7 +8,11 @@ import { useFindMcpById } from '../../hooks/use-find-mcp-by-id'; | |||
| import useGraphStore from '../../store'; | |||
| import { NodeWrapper } from './node-wrapper'; | |||
| function InnerToolNode({ id, isConnectable = true }: NodeProps<IToolNode>) { | |||
| function InnerToolNode({ | |||
| id, | |||
| isConnectable = true, | |||
| selected, | |||
| }: NodeProps<IToolNode>) { | |||
| const { edges, getNode } = useGraphStore((state) => state); | |||
| const upstreamAgentNodeId = edges.find((x) => x.target === id)?.source; | |||
| const upstreamAgentNode = getNode(upstreamAgentNodeId); | |||
| @@ -29,7 +33,7 @@ function InnerToolNode({ id, isConnectable = true }: NodeProps<IToolNode>) { | |||
| ); | |||
| return ( | |||
| <NodeWrapper> | |||
| <NodeWrapper selected={selected}> | |||
| <Handle | |||
| id={NodeHandleId.End} | |||
| type="target" | |||
| @@ -20,15 +20,6 @@ import { BeginQueryType } from '../constant'; | |||
| import { BeginQuery } from '../interface'; | |||
| import { FileUploadDirectUpload } from './uploader'; | |||
| export const BeginQueryComponentMap = { | |||
| [BeginQueryType.Line]: 'string', | |||
| [BeginQueryType.Paragraph]: 'string', | |||
| [BeginQueryType.Options]: 'string', | |||
| [BeginQueryType.File]: 'file', | |||
| [BeginQueryType.Integer]: 'number', | |||
| [BeginQueryType.Boolean]: 'boolean', | |||
| }; | |||
| const StringFields = [ | |||
| BeginQueryType.Line, | |||
| BeginQueryType.Paragraph, | |||
| @@ -68,7 +59,7 @@ const DebugContent = ({ | |||
| } else if (type === BeginQueryType.Boolean) { | |||
| fieldSchema = z.boolean(); | |||
| value = false; | |||
| } else if (type === BeginQueryType.Integer) { | |||
| } else if (type === BeginQueryType.Integer || type === 'float') { | |||
| fieldSchema = z.coerce.number(); | |||
| } else { | |||
| fieldSchema = z.record(z.any()); | |||
| @@ -1,8 +1,8 @@ | |||
| import CopyToClipboard from '@/components/copy-to-clipboard'; | |||
| import { Sheet, SheetContent, SheetHeader } from '@/components/ui/sheet'; | |||
| import { useDebugSingle } from '@/hooks/flow-hooks'; | |||
| import { useFetchInputForm } from '@/hooks/use-agent-request'; | |||
| import { useDebugSingle, useFetchInputForm } from '@/hooks/use-agent-request'; | |||
| import { IModalProps } from '@/interfaces/common'; | |||
| import { cn } from '@/lib/utils'; | |||
| import { isEmpty } from 'lodash'; | |||
| import { X } from 'lucide-react'; | |||
| import { useCallback, useMemo } from 'react'; | |||
| @@ -10,6 +10,7 @@ import { useTranslation } from 'react-i18next'; | |||
| import JsonView from 'react18-json-view'; | |||
| import 'react18-json-view/src/style.css'; | |||
| import DebugContent from '../../debug-content'; | |||
| import { transferInputsArrayToObject } from '../../form/begin-form/use-watch-change'; | |||
| import { buildBeginInputListFromObject } from '../../form/begin-form/utils'; | |||
| interface IProps { | |||
| @@ -32,7 +33,10 @@ const SingleDebugSheet = ({ | |||
| const onOk = useCallback( | |||
| (nextValues: any[]) => { | |||
| if (componentId) { | |||
| debugSingle({ component_id: componentId, params: nextValues }); | |||
| debugSingle({ | |||
| component_id: componentId, | |||
| params: transferInputsArrayToObject(nextValues), | |||
| }); | |||
| } | |||
| }, | |||
| [componentId, debugSingle], | |||
| @@ -58,7 +62,11 @@ const SingleDebugSheet = ({ | |||
| submitButtonDisabled={list.length === 0} | |||
| ></DebugContent> | |||
| {!isEmpty(data) ? ( | |||
| <div className="mt-4 rounded-md bg-slate-200 border border-neutral-200"> | |||
| <div | |||
| className={cn('mt-4 rounded-md border', { | |||
| [`border-text-delete-red`]: !isEmpty(data._ERROR), | |||
| })} | |||
| > | |||
| <div className="flex justify-between p-2"> | |||
| <span>JSON</span> | |||
| <CopyToClipboard text={content}></CopyToClipboard> | |||
| @@ -67,7 +75,8 @@ const SingleDebugSheet = ({ | |||
| src={data} | |||
| displaySize | |||
| collapseStringsAfterLength={100000000000} | |||
| className="w-full h-[800px] break-words overflow-auto p-2 bg-slate-100" | |||
| className="w-full h-[800px] break-words overflow-auto p-2" | |||
| dark | |||
| /> | |||
| </div> | |||
| ) : null} | |||