Draw a loop's way back above every node between its ends

It cleared the two nodes it connects and nothing else, so when the
loop's own steps sat higher - the usual case - its top ran hidden behind
them, limit label included. It now clears every node between its ends,
and follows any of them being moved.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Lucio Lelii 2026-09-25 14:57:56 +02:00
parent 32b62d10a2
commit f2ade91000
1 changed files with 17 additions and 7 deletions

View File

@ -184,10 +184,12 @@ export async function createEditor(
area.addPipe((context: any) => {
if (context?.type === 'nodetranslated') {
const nodeId = String(context.data?.id ?? '');
// Any node moved can be one the way back has to clear, not only one of its two ends.
for (const connection of editor.getConnections() as LoopAwareConnection[]) {
if (!connection.__loopBack || (connection.source !== nodeId && connection.target !== nodeId)) continue;
connection.__loopTop = loopTop(area, connection);
if (!connection.__loopBack) continue;
const top = loopTop(area, connection);
if (top === connection.__loopTop) continue;
connection.__loopTop = top;
void area.update('connection', connection.id);
}
}
@ -813,10 +815,18 @@ export async function refreshLoopMarkers(
* as it is expanded or edited, its position only when it is moved.
*/
function loopTop(area: AreaPlugin<HFSchemes, AreaExtra>, connection: LoopAwareConnection): number | undefined {
const tops = [connection.source, connection.target]
.map((id) => area.nodeViews.get(id)?.position.y)
.filter((y): y is number => typeof y === 'number');
return tops.length ? Math.min(...tops) - 48 : undefined;
const ends = [connection.source, connection.target]
.map((id) => area.nodeViews.get(id))
.filter((view): view is NonNullable<typeof view> => view != null);
if (!ends.length) return undefined;
// The way back runs across everything between its two ends, so it has to clear the tallest of
// those nodes too - the loop's own steps usually sit there - not only the two it connects.
const left = Math.min(...ends.map((view) => view.position.x));
const right = Math.max(...ends.map((view) => view.position.x + view.element.offsetWidth));
const tops = [...area.nodeViews.values()]
.filter((view) => view.position.x + view.element.offsetWidth >= left && view.position.x <= right)
.map((view) => view.position.y);
return Math.min(...tops, ...ends.map((view) => view.position.y)) - 48;
}
/**