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)
This commit is contained in:
balibabu
2025-07-18 19:26:24 +08:00
committed by GitHub
parent 24b719ddba
commit 77deaf390b
17 changed files with 48 additions and 73 deletions

View File

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

View File

@ -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 },
)}

View File

@ -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
}}

View File

@ -25,7 +25,7 @@ function InnerAgentNode({
return (
<ToolBar selected={selected} id={id} label={data.label}>
<NodeWrapper>
<NodeWrapper selected={selected}>
{isHeadAgent && (
<>
<CommonHandle

View File

@ -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}

View File

@ -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}

View File

@ -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"

View File

@ -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}

View File

@ -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"

View File

@ -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}

View File

@ -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,
)}
>

View File

@ -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>

View File

@ -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"

View File

@ -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}

View File

@ -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"