fix(editor): follow only loop endpoints

This commit is contained in:
Lucio Lelii 2026-09-30 08:53:16 +02:00
parent 11bcfb5973
commit 26b11a0203
2 changed files with 26 additions and 9 deletions

View File

@ -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);
});
});

View File

@ -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<HFSchemes, AreaExtra>, connection: LoopAwareConnection): number | undefined {
const ends = [connection.source, connection.target]