Commit graph

14 commits

Author SHA1 Message Date
ed18b11dc9 Add self-loop (self-reference) support for parallel edges
- Add getSelfLoopParams() with shape-aware geometry for all node shapes
  (rectangle, roundedRectangle, circle, ellipse, pill)
- Anchor loops at the top-right corner: innermost loop is tightest
  (label closest to node), outer parallel loops fan progressively higher
- Fix ellipse source point which was incorrectly placed at the bottom
- Fix horizontal pill source Y which was at center instead of top edge
- Apply loopOffset to ellipse/circle to differentiate parallel self-loops
- Handle GraphEditor parallel edge sorting for self-referencing edges
- Fix loopLevel overflow for 6+ parallel self-loops via Math.max(0, ...)
- Add tests for self-loop detection, geometry, and parallel self-loops

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 15:33:52 +01:00
676f1a61da Fix critical issues and add comprehensive unit tests
Critical Fixes:
- Replace timestamp-based edge IDs with crypto.randomUUID() to prevent collisions
- Update generateEdgeId to use format: edge_<source>_<target>_<uuid>
- Fix stale test that incorrectly tested for duplicate prevention
- Update test to verify parallel edges ARE allowed (multiple edges between same nodes)

Unit Tests:
- Add comprehensive test suite for edgeUtils.ts (33 tests)
- Test calculateEdgeOffsetMultiplier: 1-6 edges, symmetric distribution
- Test calculatePerpendicularOffset: horizontal, vertical, diagonal, edge cases
- Test groupParallelEdges: bidirectional, multiple groups, special characters
- Test generateEdgeId: uniqueness and format validation
- Use toBeCloseTo for floating-point comparisons to handle IEEE 754 signed zero

All 433 tests passing

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-05 14:44:52 +01:00
ce7f6c2eed Improve label staggering and remove parallel badge
- Simplify label positioning formula: symmetric 10% stagger per offset unit
- Remove parallel edge badge (X relations) - not needed since all labels show
- Labels now closer to center with consistent formula
- Better balance between separation and readability

Example for 3 edges:
- offset -1: label at t=0.4 (toward source)
- offset 0: label at t=0.5 (center)
- offset +1: label at t=0.6 (toward target)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-05 14:14:22 +01:00
fdef63e8bd Fix parallel edge detection and rendering for same-direction edges
This completes the parallel edge offset feature by fixing several critical issues:

**Fixed Issues:**
1. React Flow's addEdge was preventing duplicate edges in GraphEditor
2. graphStore's addEdge was also using React Flow's addEdge (duplicate prevention)
3. Edge deduplication in visibleEdges was removing parallel edges
4. Normalized key parsing failed due to underscores in node IDs

**Changes:**
- Remove React Flow's addEdge from both GraphEditor and graphStore
- Use unique edge IDs (timestamp-based) to allow multiple same-direction edges
- Use edge.id as map key for normal edges (not source_target)
- Change parallel group key separator from _ to <-> to handle node IDs with underscores
- Add isValidConnection={() => true} to bypass React Flow connection validation
- Reduce endpoint offset from 50% to 10% to keep edges close to nodes
- All edge labels now visible (removed center-edge-only restriction)

**Result:**
- Multiple edges in same direction now work correctly
- Edges fan out beautifully with 80px base offset
- Bidirectional edges properly separated
- Minimized group aggregation still works
- All 517 tests pass

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-05 14:08:59 +01:00
3daedbc0d8 Implement parallel edge offset for overlapping relations
Add core logic to detect and offset parallel edges (multiple edges between
the same two nodes) to make them visually distinguishable.

Features:
- Detect parallel edges using groupParallelEdges() utility
- Calculate perpendicular offset for each edge in a parallel group
- Distribute edges evenly around the center line (±0.5, ±1, ±1.5, etc.)
- Apply offset to Bezier control points for smooth curved paths
- Base offset of 30px provides clear visual separation

Technical implementation:
- Added calculatePerpendicularOffset() to compute offset vectors
- Added calculateEdgeOffsetMultiplier() for even distribution
- Extended getFloatingEdgeParams() to accept offsetMultiplier parameter
- Added offsetMultiplier and parallelGroupSize to RelationData type
- Updated GraphEditor to detect parallel edges and assign offsets
- Updated CustomEdge to apply offsets when rendering

Design documents included:
- EDGE_OVERLAP_UX_PROPOSAL.md: Complete UX design and implementation plan
- EDGE_OVERLAP_VISUAL_GUIDE.md: Visual specifications and design tokens

All 517 tests pass.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-05 13:25:34 +01:00
094fd6d957 Extract rounded rectangle corner radius as a constant
Some checks failed
CI / test (push) Has been cancelled
Changes:
- Add ROUNDED_RECTANGLE_RADIUS = 24 to src/constants.ts
- Update RoundedRectangleShape to import and use the constant
- Update edgeUtils.ts to import and use the constant
- Ensures consistency between component rendering and edge calculations

Improves maintainability by having a single source of truth for the
rounded rectangle corner radius value.
2026-01-24 16:54:50 +01:00
603c767403 Add rounded rectangle intersection handling for proper edge routing
Rounded rectangles now have shape-aware edge intersections that follow
the curved corners instead of treating them as sharp corners.

Implementation:
- Add getRoundedRectangleIntersection() function
- Detects when intersection point is near a corner
- Uses circular arc intersection for corners (24px radius)
- Falls back to straight edge calculation for non-corner intersections
- Ensures arrows smoothly follow the rounded contours

Fixes issue where edge arrows didn't correctly follow rounded rectangle
outer contours.
2026-01-24 16:53:00 +01:00
4b865762a1 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 <noreply@anthropic.com>
2026-01-24 16:17:23 +01:00
318cdee15c Fix lint errors: change @ts-ignore to @ts-expect-error and fix type assertions 2026-01-24 16:05:50 +01:00
8d71da76b2 Add shape-aware edge connections and edge-only handles
Improvements:
- Edges now follow actual node shape contours (circle, ellipse, pill, rectangle)
- Smooth arrow rotation using normal vectors at intersection points
- Custom bezier curves with control points aligned to shape normals
- Edge-only handles (30px strips) leaving center free for node dragging
- Proper offset calculations to prevent edge-shape overlap

Technical changes:
- Add getCircleIntersection() for perfect circle geometry
- Add getEllipseIntersection() with gradient-based normals
- Add getPillIntersection() for stadium shape (rounded caps + straight sides)
- Update getFloatingEdgeParams() to accept and use node shapes
- CustomEdge determines shapes from nodeType config and creates custom bezier paths
- Replace full-node handles with 4 edge-positioned handles (top/right/bottom/left)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-24 16:03:34 +01:00
c9c888d0ac Implement whole-node easy-connect handle system with floating edges
Migrated from 4-position handle system (top/right/bottom/left) to React Flow's
easy-connect pattern where the entire node surface is connectable and edges
dynamically route to the nearest point on the node border.

Key changes:
- Migration utility removes old 4-position handle references for backwards compatibility
- Full-coverage invisible handles on CustomNode and GroupNode (maximized state)
- Floating edges use node.measured dimensions and node.internals.positionAbsolute
- useInternalNode hook for correct absolute positioning of nodes in groups
- All edges now omit handle fields, allowing dynamic border calculations

This improves UX by making nodes easier to connect (whole surface vs tiny handles)
and edges intelligently route to optimal connection points.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-01-24 13:01:04 +01:00
7c49ad0baa feat: render edges between minimized groups with floating edges
Implements proper edge rendering when both source and target actors are
in minimized groups. Edges are now correctly rerouted to connect the
minimized group nodes with dynamic floating edge calculations.

Key changes:
- Add edge rerouting logic to redirect edges from hidden actors to their
  parent minimized groups
- Implement edge deduplication to prevent multiple edges between same
  groups when multiple actors have connections
- Fix floating edge calculations with fallback dimensions to prevent NaN
- Use canonical parentId property instead of actorIds array for accuracy
- Create constants.ts for MINIMIZED_GROUP_WIDTH/HEIGHT (220x80)
- Replace magic numbers throughout codebase with named constants
- Remove sourceHandle/targetHandle properties when routing to groups to
  avoid React Flow validation errors

Technical details:
- GraphEditor: Build actor-to-group mapping using parentId, deduplicate
  edges with Map keyed by source_target, strip handle properties
- CustomEdge: Use floating edge params for minimized groups on both ends
- edgeUtils: Add fallback dimensions and division-by-zero guards in
  getNodeIntersection and getEdgePosition
- graphStore: Use constants for minimized group dimensions

Edges between minimized groups now render with proper Bezier curves and
dynamic connection points that adapt to group positions.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-21 11:28:48 +02:00
b1e634d3c4 feat: add group minimize/maximize with floating edges and React Flow v12
Implements comprehensive group minimize/maximize functionality and migrates
to React Flow v12 (@xyflow/react) with improved edge routing.

## Group Minimize/Maximize Features:
- Minimized groups render as compact 220×80px solid rectangles
- Original dimensions preserved in metadata and restored on maximize
- Child actors hidden (not filtered) to prevent React Flow state issues
- Solid color backgrounds (transparency removed for minimized state)
- Internal edges filtered out when group is minimized
- Dimension sync before minimize ensures correct size on maximize

## Floating Edges:
- Dynamic edge routing for connections to/from minimized groups
- Edges connect to closest point on minimized group border
- Regular actors maintain fixed handle connections
- Smooth transitions when toggling group state

## React Flow v12 Migration:
- Updated package from 'reactflow' to '@xyflow/react'
- Changed imports to named imports (ReactFlow is now named)
- Updated CSS imports to '@xyflow/react/dist/style.css'
- Fixed NodeProps/EdgeProps to use full Node/Edge types
- Added Record<string, unknown> to data interfaces for v12 compatibility
- Replaced useStore(state => state.connectionNodeId) with useConnection()
- Updated nodeInternals to nodeLookup (renamed in v12)
- Fixed event handler types for v12 API changes

## Edge Label Improvements:
- Added explicit z-index (1000) to edge labels via EdgeLabelRenderer
- Labels now properly render above edge paths

## Type Safety & Code Quality:
- Removed all 'any' type assertions in useDocumentHistory
- Fixed missing React Hook dependencies
- Fixed unused variable warnings
- All ESLint checks passing (0 errors, 0 warnings)
- TypeScript compilation clean

## Bug Fixes:
- Group drag positions now properly persisted to store
- Minimized group styling (removed dotted border, padding)
- Node visibility using 'hidden' property instead of array filtering
- Dimension sync prevents actors from disappearing on maximize

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-20 11:52:44 +02:00
f56f928dcf Initial commit 2025-10-10 11:15:51 +02:00