From 4b865762a1cb6a6a12515acd3af6435e54cf1924 Mon Sep 17 00:00:00 2001 From: Jan-Henrik Bruhn Date: Sat, 24 Jan 2026 16:17:23 +0100 Subject: [PATCH] Address PR review comments Edge calculation improvements: - Add zero radius/radii guards in circle and ellipse intersection functions - Add clamping for pill straight edge intersections to prevent overflow - Ensure intersection points stay within valid pill boundaries Handle improvements: - Add bidirectional connection support with overlapping source/target handles - Each edge now has both source and target handles (8 total per node) - Allows edges to connect in any direction from any side - Fixes handle type restrictions that prevented flexible connections Co-Authored-By: Claude Sonnet 4.5 --- src/components/Nodes/CustomNode.tsx | 85 +++++++++++++++++++++++++++-- src/components/Nodes/GroupNode.tsx | 85 +++++++++++++++++++++++++++-- src/utils/edgeUtils.ts | 20 ++++++- 3 files changed, 180 insertions(+), 10 deletions(-) diff --git a/src/components/Nodes/CustomNode.tsx b/src/components/Nodes/CustomNode.tsx index 820302d..8b76947 100644 --- a/src/components/Nodes/CustomNode.tsx +++ b/src/components/Nodes/CustomNode.tsx @@ -82,10 +82,13 @@ const CustomNode = ({ data, selected }: NodeProps) => { }} > {/* Invisible handles positioned around edges - center remains free for dragging */} - {/* Top edge handle */} + {/* Bidirectional handles (source + target overlapping at each edge) */} + + {/* Top edge handles */} ) => { cursor: "crosshair", }} /> - {/* Right edge handle */} + + {/* Right edge handles */} + ) => { cursor: "crosshair", }} /> - {/* Bottom edge handle */} + + {/* Bottom edge handles */} + ) => { cursor: "crosshair", }} /> - {/* Left edge handle */} + + + {/* Left edge handles */} + ) => { }} > {/* Invisible handles positioned around edges - center remains free for dragging */} - {/* Top edge handle */} + {/* Bidirectional handles (source + target overlapping at each edge) */} + + {/* Top edge handles */} ) => { cursor: 'crosshair', }} /> - {/* Right edge handle */} + + {/* Right edge handles */} + ) => { cursor: 'crosshair', }} /> - {/* Bottom edge handle */} + + {/* Bottom edge handles */} + ) => { cursor: 'crosshair', }} /> - {/* Left edge handle */} + + + {/* Left edge handles */} + 0.001 + let intersectX = Math.abs(dy) > 0.001 ? centerX + dx * (intersectY - centerY) / dy : centerX; + // Clamp intersection to the straight horizontal segment between the caps + intersectX = Math.min(Math.max(intersectX, leftCapX), rightCapX); + const normalAngle = side < 0 ? -Math.PI / 2 : Math.PI / 2; return { @@ -164,10 +177,13 @@ function getPillIntersection( // Calculate y position where line from target to center intersects the vertical edge // Line equation: (y - centerY) / (x - centerX) = dy / dx // Solving for y when x = intersectX: y = centerY + dy * (intersectX - centerX) / dx - const intersectY = Math.abs(dx) > 0.001 + let intersectY = Math.abs(dx) > 0.001 ? centerY + dy * (intersectX - centerX) / dx : centerY; + // Clamp intersection to the straight vertical segment between the caps + intersectY = Math.min(Math.max(intersectY, topCapY), bottomCapY); + const normalAngle = side < 0 ? Math.PI : 0; return {