Draw a loop's way back above its nodes, clear of how tall they grow
Below the ports, the way back ran through any node taller than a fixed drop - most of them, once expanded - and its limit label sat hidden behind the node. It now runs above the tops of the nodes at both ends, which move only when a node is dragged, and follows them when one is. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
f2378216ee
commit
0c129a5f8d
|
|
@ -198,20 +198,21 @@ export class CustomConnectionComponent {
|
|||
|
||||
/**
|
||||
* A connection leading back runs from a node's output to one drawn before it, so the usual curve
|
||||
* would double back through both nodes. It goes round underneath them instead.
|
||||
* would double back through both nodes. It goes round above them instead.
|
||||
*/
|
||||
get drawnPath(): string {
|
||||
if (!this.isLoopBack || !this.start || !this.end) return this.path;
|
||||
const drop = this.loopDrop;
|
||||
const { x: sx, y: sy } = this.start;
|
||||
const { x: ex, y: ey } = this.end;
|
||||
const bottom = Math.max(sy, ey) + drop;
|
||||
return `M ${sx} ${sy} C ${sx + 140} ${sy}, ${sx + 140} ${bottom}, ${sx} ${bottom}`
|
||||
+ ` L ${ex} ${bottom} C ${ex - 140} ${bottom}, ${ex - 140} ${ey}, ${ex} ${ey}`;
|
||||
const top = this.loopTop;
|
||||
return `M ${sx} ${sy} C ${sx + 140} ${sy}, ${sx + 140} ${top}, ${sx} ${top}`
|
||||
+ ` L ${ex} ${top} C ${ex - 140} ${top}, ${ex - 140} ${ey}, ${ex} ${ey}`;
|
||||
}
|
||||
|
||||
private get loopDrop(): number {
|
||||
return 150;
|
||||
private get loopTop(): number {
|
||||
const ports = Math.min(this.start?.y ?? 0, this.end?.y ?? 0) - 90;
|
||||
const nodes = this.loopData?.__loopTop;
|
||||
return typeof nodes === 'number' ? Math.min(nodes, ports) : ports;
|
||||
}
|
||||
|
||||
get loopLabelX(): number {
|
||||
|
|
@ -219,7 +220,7 @@ export class CustomConnectionComponent {
|
|||
}
|
||||
|
||||
get loopLabelY(): number {
|
||||
return Math.max(this.start?.y ?? 0, this.end?.y ?? 0) + this.loopDrop - 17;
|
||||
return this.loopTop - 17;
|
||||
}
|
||||
|
||||
changeLimit(event: Event) {
|
||||
|
|
|
|||
|
|
@ -77,6 +77,8 @@ export type LoopAwareConnection = HFSchemes['Connection'] & {
|
|||
loop?: FlowLoopEdgeSettings;
|
||||
__loopBack?: boolean;
|
||||
__readonly?: boolean;
|
||||
/** Canvas y above both ends' nodes, where the way back is drawn so it crosses neither. */
|
||||
__loopTop?: number;
|
||||
};
|
||||
|
||||
export async function createEditor(
|
||||
|
|
@ -175,6 +177,18 @@ export async function createEditor(
|
|||
return context;
|
||||
});
|
||||
|
||||
area.addPipe((context: any) => {
|
||||
if (context?.type === 'nodetranslated') {
|
||||
const nodeId = String(context.data?.id ?? '');
|
||||
for (const connection of editor.getConnections() as LoopAwareConnection[]) {
|
||||
if (!connection.__loopBack || (connection.source !== nodeId && connection.target !== nodeId)) continue;
|
||||
connection.__loopTop = loopTop(area, connection);
|
||||
void area.update('connection', connection.id);
|
||||
}
|
||||
}
|
||||
return context;
|
||||
});
|
||||
|
||||
connection.addPreset(ConnectionPresets.classic.setup());
|
||||
|
||||
AreaExtensions.simpleNodesOrder(area);
|
||||
|
|
@ -765,13 +779,26 @@ export async function refreshLoopMarkers(
|
|||
for (const connection of data) {
|
||||
const loopBack = backEdges.has(connection.id);
|
||||
const readonly = resolvedRuntime?.readonly === true;
|
||||
if (connection.__loopBack === loopBack && connection.__readonly === readonly) continue;
|
||||
const top = loopBack ? loopTop(area, connection) : undefined;
|
||||
if (connection.__loopBack === loopBack && connection.__readonly === readonly && connection.__loopTop === top) continue;
|
||||
connection.__loopBack = loopBack;
|
||||
connection.__readonly = readonly;
|
||||
connection.__loopTop = top;
|
||||
await area.update('connection', connection.id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Above the tops of the nodes at both ends. Their tops, not their bottoms: a node's height changes
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a loop connection's iteration limit; null goes back to the default. The settings stay on the
|
||||
* connection either way, so it remains the one its author marked as leading back.
|
||||
|
|
|
|||
Loading…
Reference in New Issue