@@ -85,6 +85,54 @@ const state = writable<RoutingState>({
8585/** Generation counter — incremented on each recalculateAllRoutes call to cancel stale async work */
8686let routingGeneration = 0 ;
8787
88+ function findConnection ( connectionId : string ) : Connection | undefined {
89+ return get ( graphStore . connections ) . find ( ( c ) => c . id === connectionId ) ;
90+ }
91+
92+ /**
93+ * Persist new waypoints for a connection, then either recompute the route
94+ * synchronously (when port positions are available) or invalidate the cached
95+ * route so a fresh calculation runs on the next read.
96+ *
97+ * Shared by addUserWaypoint, addUserWaypointAtIndex, removeUserWaypoint, and
98+ * moveWaypoint — they all do the same thing after they've decided what the
99+ * new waypoint list should look like.
100+ */
101+ function applyWaypointUpdate (
102+ connection : Connection ,
103+ updatedWaypoints : Waypoint [ ] ,
104+ getPortInfo : ( ( nodeId : string , portIndex : number , isOutput : boolean ) => PortInfo | null ) | undefined
105+ ) : void {
106+ graphStore . updateConnectionWaypoints ( connection . id , updatedWaypoints ) ;
107+
108+ if ( getPortInfo ) {
109+ const $state = get ( state ) ;
110+ const sourceInfo = getPortInfo ( connection . sourceNodeId , connection . sourcePortIndex , true ) ;
111+ const targetInfo = getPortInfo ( connection . targetNodeId , connection . targetPortIndex , false ) ;
112+
113+ if ( sourceInfo && targetInfo ) {
114+ const userWaypoints = getUserWaypoints ( updatedWaypoints ) ;
115+ const result = computeRoute (
116+ sourceInfo . position ,
117+ targetInfo . position ,
118+ sourceInfo . direction ,
119+ targetInfo . direction ,
120+ $state . grid ,
121+ userWaypoints
122+ ) ;
123+
124+ state . update ( ( s ) => {
125+ const routes = new Map ( s . routes ) ;
126+ routes . set ( connection . id , result ) ;
127+ return { ...s , routes } ;
128+ } ) ;
129+ return ;
130+ }
131+ }
132+
133+ routingStore . invalidateRoute ( connection . id ) ;
134+ }
135+
88136/**
89137 * Routing store - manages route calculations and caching
90138 */
@@ -501,50 +549,17 @@ export const routingStore = {
501549 ) : string | null {
502550 let waypointId : string | null = null ;
503551 historyStore . mutate ( ( ) => {
504- const connections = get ( graphStore . connections ) ;
505- const connection = connections . find ( ( c ) => c . id === connectionId ) ;
552+ const connection = findConnection ( connectionId ) ;
506553 if ( ! connection ) return ;
507554
508555 waypointId = generateId ( ) ;
509- const newWaypoint : Waypoint = {
510- id : waypointId ,
511- position,
512- isUserWaypoint : true
513- } ;
556+ const newWaypoint : Waypoint = { id : waypointId , position, isUserWaypoint : true } ;
514557
515- // Get existing user waypoints (filter out auto waypoints)
558+ // Drop auto waypoints — they'll be regenerated from the new user-waypoint set.
516559 const existingUserWaypoints = getUserWaypoints ( connection . waypoints ) ;
517560 const updatedWaypoints = [ ...existingUserWaypoints , newWaypoint ] ;
518561
519- graphStore . updateConnectionWaypoints ( connectionId , updatedWaypoints ) ;
520-
521- // Immediately recalculate route if we have port info (prevents full recalc)
522- if ( getPortInfo ) {
523- const $state = get ( state ) ;
524- const sourceInfo = getPortInfo ( connection . sourceNodeId , connection . sourcePortIndex , true ) ;
525- const targetInfo = getPortInfo ( connection . targetNodeId , connection . targetPortIndex , false ) ;
526-
527- if ( sourceInfo && targetInfo ) {
528- const result = computeRoute (
529- sourceInfo . position ,
530- targetInfo . position ,
531- sourceInfo . direction ,
532- targetInfo . direction ,
533- $state . grid ,
534- updatedWaypoints
535- ) ;
536-
537- state . update ( ( s ) => {
538- const routes = new Map ( s . routes ) ;
539- routes . set ( connectionId , result ) ;
540- return { ...s , routes } ;
541- } ) ;
542- return ;
543- }
544- }
545-
546- // Fallback: just invalidate
547- routingStore . invalidateRoute ( connectionId ) ;
562+ applyWaypointUpdate ( connection , updatedWaypoints , getPortInfo ) ;
548563 } ) ;
549564 return waypointId ;
550565 } ,
@@ -562,56 +577,20 @@ export const routingStore = {
562577 ) : string | null {
563578 let waypointId : string | null = null ;
564579 historyStore . mutate ( ( ) => {
565- const connections = get ( graphStore . connections ) ;
566- const connection = connections . find ( ( c ) => c . id === connectionId ) ;
580+ const connection = findConnection ( connectionId ) ;
567581 if ( ! connection ) return ;
568582
569583 waypointId = generateId ( ) ;
570- const newWaypoint : Waypoint = {
571- id : waypointId ,
572- position,
573- isUserWaypoint : true
574- } ;
584+ const newWaypoint : Waypoint = { id : waypointId , position, isUserWaypoint : true } ;
575585
576- // Get existing user waypoints
577586 const existingUserWaypoints = getUserWaypoints ( connection . waypoints ) ;
578-
579- // Insert at the specified index
580587 const updatedWaypoints = [
581588 ...existingUserWaypoints . slice ( 0 , insertIndex ) ,
582589 newWaypoint ,
583590 ...existingUserWaypoints . slice ( insertIndex )
584591 ] ;
585592
586- graphStore . updateConnectionWaypoints ( connectionId , updatedWaypoints ) ;
587-
588- // Immediately recalculate route if we have port info (prevents full recalc)
589- if ( getPortInfo ) {
590- const $state = get ( state ) ;
591- const sourceInfo = getPortInfo ( connection . sourceNodeId , connection . sourcePortIndex , true ) ;
592- const targetInfo = getPortInfo ( connection . targetNodeId , connection . targetPortIndex , false ) ;
593-
594- if ( sourceInfo && targetInfo ) {
595- const result = computeRoute (
596- sourceInfo . position ,
597- targetInfo . position ,
598- sourceInfo . direction ,
599- targetInfo . direction ,
600- $state . grid ,
601- updatedWaypoints
602- ) ;
603-
604- state . update ( ( s ) => {
605- const routes = new Map ( s . routes ) ;
606- routes . set ( connectionId , result ) ;
607- return { ...s , routes } ;
608- } ) ;
609- return ;
610- }
611- }
612-
613- // Fallback: just invalidate
614- routingStore . invalidateRoute ( connectionId ) ;
593+ applyWaypointUpdate ( connection , updatedWaypoints , getPortInfo ) ;
615594 } ) ;
616595 return waypointId ;
617596 } ,
@@ -626,49 +605,21 @@ export const routingStore = {
626605 getPortInfo ?: ( nodeId : string , portIndex : number , isOutput : boolean ) => PortInfo | null
627606 ) : void {
628607 historyStore . mutate ( ( ) => {
629- const connections = get ( graphStore . connections ) ;
630- const connection = connections . find ( ( c ) => c . id === connectionId ) ;
608+ const connection = findConnection ( connectionId ) ;
631609 if ( ! connection ?. waypoints ) return ;
632610
633611 const updatedWaypoints = connection . waypoints . filter (
634612 ( w ) => w . id !== waypointId || ! w . isUserWaypoint
635613 ) ;
636614
637- graphStore . updateConnectionWaypoints ( connectionId , updatedWaypoints ) ;
638-
639- // Immediately recalculate route if we have port info (prevents flicker)
640- if ( getPortInfo ) {
641- const $state = get ( state ) ;
642- const sourceInfo = getPortInfo ( connection . sourceNodeId , connection . sourcePortIndex , true ) ;
643- const targetInfo = getPortInfo ( connection . targetNodeId , connection . targetPortIndex , false ) ;
644-
645- if ( sourceInfo && targetInfo ) {
646- const userWaypoints = getUserWaypoints ( updatedWaypoints ) ;
647- const result = computeRoute (
648- sourceInfo . position ,
649- targetInfo . position ,
650- sourceInfo . direction ,
651- targetInfo . direction ,
652- $state . grid ,
653- userWaypoints
654- ) ;
655-
656- state . update ( ( s ) => {
657- const routes = new Map ( s . routes ) ;
658- routes . set ( connectionId , result ) ;
659- return { ...s , routes } ;
660- } ) ;
661- return ;
662- }
663- }
664-
665- // Fallback: just invalidate
666- routingStore . invalidateRoute ( connectionId ) ;
615+ applyWaypointUpdate ( connection , updatedWaypoints , getPortInfo ) ;
667616 } ) ;
668617 } ,
669618
670619 /**
671- * Move a waypoint to a new position and recalculate route
620+ * Move a waypoint to a new position and recalculate route.
621+ * Not wrapped in historyStore.mutate — the caller owns the drag transaction
622+ * so a single drag becomes one undo entry rather than one per move event.
672623 * @param getPortInfo - Optional callback to get port info for route recalculation
673624 */
674625 moveWaypoint (
@@ -677,44 +628,14 @@ export const routingStore = {
677628 newPosition : Position ,
678629 getPortInfo ?: ( nodeId : string , portIndex : number , isOutput : boolean ) => PortInfo | null
679630 ) : void {
680- const connections = get ( graphStore . connections ) ;
681- const connection = connections . find ( ( c ) => c . id === connectionId ) ;
631+ const connection = findConnection ( connectionId ) ;
682632 if ( ! connection ?. waypoints ) return ;
683633
684634 const updatedWaypoints = connection . waypoints . map ( ( w ) =>
685635 w . id === waypointId ? { ...w , position : newPosition } : w
686636 ) ;
687637
688- graphStore . updateConnectionWaypoints ( connectionId , updatedWaypoints ) ;
689-
690- // If getPortInfo is provided, recalculate route immediately
691- if ( getPortInfo ) {
692- const $state = get ( state ) ;
693- const sourceInfo = getPortInfo ( connection . sourceNodeId , connection . sourcePortIndex , true ) ;
694- const targetInfo = getPortInfo ( connection . targetNodeId , connection . targetPortIndex , false ) ;
695-
696- if ( sourceInfo && targetInfo ) {
697- const userWaypoints = getUserWaypoints ( updatedWaypoints ) ;
698- const result = computeRoute (
699- sourceInfo . position ,
700- targetInfo . position ,
701- sourceInfo . direction ,
702- targetInfo . direction ,
703- $state . grid ,
704- userWaypoints
705- ) ;
706-
707- state . update ( ( s ) => {
708- const routes = new Map ( s . routes ) ;
709- routes . set ( connectionId , result ) ;
710- return { ...s , routes } ;
711- } ) ;
712- return ;
713- }
714- }
715-
716- // Fallback: just invalidate
717- routingStore . invalidateRoute ( connectionId ) ;
638+ applyWaypointUpdate ( connection , updatedWaypoints , getPortInfo ) ;
718639 } ,
719640
720641 /**
0 commit comments