Explorar el Código

Feat: Adjust the style of the agent canvas connection line #3221 (#8922)

### 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
balibabu hace 3 meses
padre
commit
77deaf390b
No account linked to committer's email address

+ 0
- 31
web/src/pages/agent/canvas/edge/index.less Ver fichero

.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);
}

+ 4
- 6
web/src/pages/agent/canvas/edge/index.tsx Ver fichero

} from '@xyflow/react'; } from '@xyflow/react';
import useGraphStore from '../../store'; import useGraphStore from '../../store';


import { useTheme } from '@/components/theme-provider';
import { useFetchAgent } from '@/hooks/use-agent-request'; import { useFetchAgent } from '@/hooks/use-agent-request';
import { cn } from '@/lib/utils'; import { cn } from '@/lib/utils';
import { useMemo } from 'react'; import { useMemo } from 'react';
import { NodeHandleId, Operator } from '../../constant'; import { NodeHandleId, Operator } from '../../constant';
import styles from './index.less';


export function ButtonEdge({ export function ButtonEdge({
id, id,
targetY, targetY,
targetPosition, targetPosition,
}); });
const { theme } = useTheme();
const selectedStyle = useMemo(() => { const selectedStyle = useMemo(() => {
return selected ? { strokeWidth: 2, stroke: '#1677ff' } : {};
return selected ? { strokeWidth: 1, stroke: 'rgba(76, 164, 231, 1)' } : {};
}, [selected]); }, [selected]);


const onEdgeClick = () => { const onEdgeClick = () => {
// The set of elements following source // The set of elements following source
const slicedGraphPath = graphPath.slice(idx + 1); const slicedGraphPath = graphPath.slice(idx + 1);
if (slicedGraphPath.some((x) => x === target)) { if (slicedGraphPath.some((x) => x === target)) {
return { strokeWidth: 2, stroke: 'red' };
return { strokeWidth: 1, stroke: 'red' };
} }
} }
return {}; return {};
path={edgePath} path={edgePath}
markerEnd={markerEnd} markerEnd={markerEnd}
style={{ ...style, ...selectedStyle, ...highlightStyle }} style={{ ...style, ...selectedStyle, ...highlightStyle }}
className="text-text-sub-title"
/> />


<EdgeLabelRenderer> <EdgeLabelRenderer>
> >
<button <button
className={cn( 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', 'invisible',
{ visible }, { visible },
)} )}

+ 2
- 2
web/src/pages/agent/canvas/index.tsx Ver fichero

type: 'buttonEdge', type: 'buttonEdge',
markerEnd: 'logo', markerEnd: 'logo',
style: { 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 zIndex: 1001, // https://github.com/xyflow/xyflow/discussions/3498
}} }}

+ 1
- 1
web/src/pages/agent/canvas/node/agent-node.tsx Ver fichero



return ( return (
<ToolBar selected={selected} id={id} label={data.label}> <ToolBar selected={selected} id={id} label={data.label}>
<NodeWrapper>
<NodeWrapper selected={selected}>
{isHeadAgent && ( {isHeadAgent && (
<> <>
<CommonHandle <CommonHandle

+ 2
- 2
web/src/pages/agent/canvas/node/begin-node.tsx Ver fichero

import { NodeWrapper } from './node-wrapper'; import { NodeWrapper } from './node-wrapper';


// TODO: do not allow other nodes to connect to this node // 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 { t } = useTranslation();
const inputs: Record<string, BeginQuery> = get(data, 'form.inputs', {}); const inputs: Record<string, BeginQuery> = get(data, 'form.inputs', {});


return ( return (
<NodeWrapper>
<NodeWrapper selected={selected}>
<CommonHandle <CommonHandle
type="source" type="source"
position={Position.Right} position={Position.Right}

+ 1
- 1
web/src/pages/agent/canvas/node/categorize-node.tsx Ver fichero

const { positions } = useBuildCategorizeHandlePositions({ data, id }); const { positions } = useBuildCategorizeHandlePositions({ data, id });
return ( return (
<ToolBar selected={selected} id={id} label={data.label}> <ToolBar selected={selected} id={id} label={data.label}>
<NodeWrapper>
<NodeWrapper selected={selected}>
<CommonHandle <CommonHandle
type="target" type="target"
position={Position.Left} position={Position.Left}

+ 1
- 1
web/src/pages/agent/canvas/node/index.tsx Ver fichero

}: NodeProps<IRagNode>) { }: NodeProps<IRagNode>) {
return ( return (
<ToolBar selected={selected} id={id} label={data.label}> <ToolBar selected={selected} id={id} label={data.label}>
<NodeWrapper>
<NodeWrapper selected={selected}>
<CommonHandle <CommonHandle
id={NodeHandleId.End} id={NodeHandleId.End}
type="target" type="target"

+ 2
- 1
web/src/pages/agent/canvas/node/iteration-node.tsx Ver fichero

function InnerIterationStartNode({ function InnerIterationStartNode({
isConnectable = true, isConnectable = true,
id, id,
selected,
}: NodeProps<IIterationStartNode>) { }: NodeProps<IIterationStartNode>) {
return ( return (
<NodeWrapper className="w-20">
<NodeWrapper className="w-20" selected={selected}>
<CommonHandle <CommonHandle
type="source" type="source"
position={Position.Right} position={Position.Right}

+ 1
- 1
web/src/pages/agent/canvas/node/logic-node.tsx Ver fichero

}: NodeProps<ILogicNode>) { }: NodeProps<ILogicNode>) {
return ( return (
<ToolBar selected={selected} id={id} label={data.label}> <ToolBar selected={selected} id={id} label={data.label}>
<NodeWrapper>
<NodeWrapper selected={selected}>
<CommonHandle <CommonHandle
id="c" id="c"
type="source" type="source"

+ 1
- 1
web/src/pages/agent/canvas/node/message-node.tsx Ver fichero

const messages: string[] = get(data, 'form.messages', []); const messages: string[] = get(data, 'form.messages', []);
return ( return (
<ToolBar selected={selected} id={id} label={data.label}> <ToolBar selected={selected} id={id} label={data.label}>
<NodeWrapper>
<NodeWrapper selected={selected}>
<CommonHandle <CommonHandle
type="target" type="target"
position={Position.Left} position={Position.Left}

+ 5
- 5
web/src/pages/agent/canvas/node/node-wrapper.tsx Ver fichero

import { cn } from '@/lib/utils'; 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 ( return (
<section <section
className={cn( className={cn(
'bg-background-header-bar p-2.5 rounded-md w-[200px] text-xs', 'bg-background-header-bar p-2.5 rounded-md w-[200px] text-xs',
{ 'border border-background-checked': selected },
className, className,
)} )}
> >

+ 5
- 2
web/src/pages/agent/canvas/node/note-node/index.tsx Ver fichero

text: z.string(), text: z.string(),
}); });


function NoteNode({ data, id }: NodeProps<INoteNode>) {
function NoteNode({ data, id, selected }: NodeProps<INoteNode>) {
const { t } = useTranslation(); const { t } = useTranslation();


const form = useForm<z.infer<typeof FormSchema>>({ const form = useForm<z.infer<typeof FormSchema>>({
useWatchFormChange(id, form); useWatchFormChange(id, form);


return ( 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}> <NodeResizeControl minWidth={190} minHeight={128} style={controlStyle}>
<ResizeIcon /> <ResizeIcon />
</NodeResizeControl> </NodeResizeControl>

+ 1
- 1
web/src/pages/agent/canvas/node/retrieval-node.tsx Ver fichero



return ( return (
<ToolBar selected={selected} id={id} label={data.label}> <ToolBar selected={selected} id={id} label={data.label}>
<NodeWrapper>
<NodeWrapper selected={selected}>
<CommonHandle <CommonHandle
id={NodeHandleId.End} id={NodeHandleId.End}
type="target" type="target"

+ 1
- 1
web/src/pages/agent/canvas/node/switch-node.tsx Ver fichero

const { positions } = useBuildSwitchHandlePositions({ data, id }); const { positions } = useBuildSwitchHandlePositions({ data, id });
return ( return (
<ToolBar selected={selected} id={id} label={data.label}> <ToolBar selected={selected} id={id} label={data.label}>
<NodeWrapper>
<NodeWrapper selected={selected}>
<CommonHandle <CommonHandle
type="target" type="target"
position={Position.Left} position={Position.Left}

+ 6
- 2
web/src/pages/agent/canvas/node/tool-node.tsx Ver fichero

import useGraphStore from '../../store'; import useGraphStore from '../../store';
import { NodeWrapper } from './node-wrapper'; 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 { edges, getNode } = useGraphStore((state) => state);
const upstreamAgentNodeId = edges.find((x) => x.target === id)?.source; const upstreamAgentNodeId = edges.find((x) => x.target === id)?.source;
const upstreamAgentNode = getNode(upstreamAgentNodeId); const upstreamAgentNode = getNode(upstreamAgentNodeId);
); );


return ( return (
<NodeWrapper>
<NodeWrapper selected={selected}>
<Handle <Handle
id={NodeHandleId.End} id={NodeHandleId.End}
type="target" type="target"

+ 1
- 10
web/src/pages/agent/debug-content/index.tsx Ver fichero

import { BeginQuery } from '../interface'; import { BeginQuery } from '../interface';
import { FileUploadDirectUpload } from './uploader'; 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 = [ const StringFields = [
BeginQueryType.Line, BeginQueryType.Line,
BeginQueryType.Paragraph, BeginQueryType.Paragraph,
} else if (type === BeginQueryType.Boolean) { } else if (type === BeginQueryType.Boolean) {
fieldSchema = z.boolean(); fieldSchema = z.boolean();
value = false; value = false;
} else if (type === BeginQueryType.Integer) {
} else if (type === BeginQueryType.Integer || type === 'float') {
fieldSchema = z.coerce.number(); fieldSchema = z.coerce.number();
} else { } else {
fieldSchema = z.record(z.any()); fieldSchema = z.record(z.any());

+ 14
- 5
web/src/pages/agent/form-sheet/single-debug-sheet/index.tsx Ver fichero

import CopyToClipboard from '@/components/copy-to-clipboard'; import CopyToClipboard from '@/components/copy-to-clipboard';
import { Sheet, SheetContent, SheetHeader } from '@/components/ui/sheet'; 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 { IModalProps } from '@/interfaces/common';
import { cn } from '@/lib/utils';
import { isEmpty } from 'lodash'; import { isEmpty } from 'lodash';
import { X } from 'lucide-react'; import { X } from 'lucide-react';
import { useCallback, useMemo } from 'react'; import { useCallback, useMemo } from 'react';
import JsonView from 'react18-json-view'; import JsonView from 'react18-json-view';
import 'react18-json-view/src/style.css'; import 'react18-json-view/src/style.css';
import DebugContent from '../../debug-content'; import DebugContent from '../../debug-content';
import { transferInputsArrayToObject } from '../../form/begin-form/use-watch-change';
import { buildBeginInputListFromObject } from '../../form/begin-form/utils'; import { buildBeginInputListFromObject } from '../../form/begin-form/utils';


interface IProps { interface IProps {
const onOk = useCallback( const onOk = useCallback(
(nextValues: any[]) => { (nextValues: any[]) => {
if (componentId) { if (componentId) {
debugSingle({ component_id: componentId, params: nextValues });
debugSingle({
component_id: componentId,
params: transferInputsArrayToObject(nextValues),
});
} }
}, },
[componentId, debugSingle], [componentId, debugSingle],
submitButtonDisabled={list.length === 0} submitButtonDisabled={list.length === 0}
></DebugContent> ></DebugContent>
{!isEmpty(data) ? ( {!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"> <div className="flex justify-between p-2">
<span>JSON</span> <span>JSON</span>
<CopyToClipboard text={content}></CopyToClipboard> <CopyToClipboard text={content}></CopyToClipboard>
src={data} src={data}
displaySize displaySize
collapseStringsAfterLength={100000000000} 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> </div>
) : null} ) : null}

Cargando…
Cancelar
Guardar