fix: explicit handle IDs — forward edges use top/bottom, feedback uses sides

Prevent React Flow from auto-routing forward edges to side handles.
All handles now have explicit IDs and all edges specify sourceHandle/targetHandle.
This commit is contained in:
2026-05-14 15:54:21 +08:00
parent 5e783e7a24
commit 019d8c1ee9
3 changed files with 6 additions and 5 deletions
@@ -45,7 +45,7 @@ export function RoleNode(props: NodeProps) {
}}
title={data.description}
>
<Handle type="target" position={Position.Top} style={handleStyle} isConnectable={false} />
<Handle type="target" position={Position.Top} id="top-in" style={handleStyle} isConnectable={false} />
<Handle type="target" position={Position.Left} id="left-in" style={handleStyle} isConnectable={false} />
<Handle type="target" position={Position.Right} id="right-in" style={handleStyle} isConnectable={false} />
<Handle type="source" position={Position.Left} id="left-out" style={handleStyle} isConnectable={false} />
@@ -67,7 +67,7 @@ export function RoleNode(props: NodeProps) {
{data.description}
</div>
)}
<Handle type="source" position={Position.Bottom} style={handleStyle} isConnectable={false} />
<Handle type="source" position={Position.Bottom} id="bottom-out" style={handleStyle} isConnectable={false} />
</div>
);
}
@@ -45,11 +45,12 @@ export function TerminalNode(props: NodeProps) {
<Handle
type="source"
position={Position.Bottom}
id="bottom-out"
style={handleStyle}
isConnectable={false}
/>
) : (
<Handle type="target" position={Position.Top} style={handleStyle} isConnectable={false} />
<Handle type="target" position={Position.Top} id="top-in" style={handleStyle} isConnectable={false} />
)}
{isStart ? "▶" : "■"}
</div>
@@ -216,8 +216,8 @@ function computeLayout(input: LayoutInput): LayoutResult {
id: edgeKey(e),
source: e.from,
target: e.to,
sourceHandle: isFeedback ? (feedbackSide === "left" ? "left-out" : "right-out") : undefined,
targetHandle: isFeedback ? (feedbackSide === "left" ? "left-in" : "right-in") : undefined,
sourceHandle: isFeedback ? (feedbackSide === "left" ? "left-out" : "right-out") : "bottom-out",
targetHandle: isFeedback ? (feedbackSide === "left" ? "left-in" : "right-in") : "top-in",
type: "condition",
data: {
condition: e.condition,