From 26b11a0203e5dfb04b3c002fa10e597b50d33938 Mon Sep 17 00:00:00 2001 From: Lucio Lelii Date: Wed, 30 Sep 2026 08:53:16 +0200 Subject: [PATCH] fix(editor): follow only loop endpoints --- src/app/utilities/rete-editor.spec.ts | 8 ++++++++ src/app/utilities/rete-editor.ts | 27 ++++++++++++++++++--------- 2 files changed, 26 insertions(+), 9 deletions(-) diff --git a/src/app/utilities/rete-editor.spec.ts b/src/app/utilities/rete-editor.spec.ts index d806eba..7f2e08c 100644 --- a/src/app/utilities/rete-editor.spec.ts +++ b/src/app/utilities/rete-editor.spec.ts @@ -117,6 +117,14 @@ describe('loop route placement', () => { expect(loop.__loopBack).toBe(true); expect(loop.__loopTop).toBe(352); + + area.nodeViews.get('intermediate').position.y = 0; + await refreshLoopMarkers(editor, area, runtime); + expect(loop.__loopTop).toBe(352); + + area.nodeViews.get('router').position.y = 300; + await refreshLoopMarkers(editor, area, runtime); + expect(loop.__loopTop).toBe(252); }); }); diff --git a/src/app/utilities/rete-editor.ts b/src/app/utilities/rete-editor.ts index 7db4779..73fb065 100644 --- a/src/app/utilities/rete-editor.ts +++ b/src/app/utilities/rete-editor.ts @@ -222,6 +222,21 @@ export async function createEditor( return context; }); + area.addPipe((context: any) => { + if (context?.type === 'nodetranslated') { + // A loop follows only its own endpoints. A card merely crossed by the wire must not make the + // wire move away, while moving either endpoint keeps the wire 48px above that endpoint. + for (const connection of editor.getConnections() as LoopAwareConnection[]) { + if (!connection.__loopBack) continue; + const top = loopTop(area, connection); + if (top === connection.__loopTop) continue; + connection.__loopTop = top; + void area.update('connection', connection.id); + } + } + return context; + }); + connection.addPreset(ConnectionPresets.classic.setup()); AreaExtensions.simpleNodesOrder(area); @@ -961,13 +976,7 @@ export async function refreshLoopMarkers( for (const connection of connections) { const loopBack = backEdges.has(connection.id); const readonly = resolvedRuntime?.readonly === true; - // A loop gets its route when it becomes a loop. It then keeps that canvas height while cards - // move, so a person can drag a card across the wire instead of the wire perpetually fleeing up. - const top = loopBack - ? connection.__loopBack === true && typeof connection.__loopTop === 'number' - ? connection.__loopTop - : loopTop(area, connection) - : undefined; + const top = loopBack ? loopTop(area, connection) : undefined; if (connection.__loopBack === loopBack && connection.__readonly === readonly && connection.__loopTop === top) continue; connection.__loopBack = loopBack; connection.__readonly = readonly; @@ -977,8 +986,8 @@ export async function refreshLoopMarkers( } /** - * The initial route of a loop: 48px above its highest endpoint. Nodes in between are deliberately - * ignored, since they may subsequently be moved across this fixed route. + * A loop runs 48px above its highest endpoint. Nodes in between are deliberately ignored, so they + * may be moved across the wire without causing it to move. */ function loopTop(area: AreaPlugin, connection: LoopAwareConnection): number | undefined { const ends = [connection.source, connection.target]