From aa7fe7fda98a55eb447a58f68bc0db5260427368 Mon Sep 17 00:00:00 2001 From: Kinflou Date: Fri, 4 Sep 2026 03:12:09 +0800 Subject: [PATCH] sim: anchor connection wires at the port dots MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `drawWires` ran box-centre to box-centre, so the line passed under the instance rows and never touched the `.node-port` dots it's supposed to connect. Anchor each end at the port's real (transform-aware) rect centre, in the same coordinate space `canvasPoint` uses — the drag-to-connect line starts there too. Falls back to the row centre in the embed view, which has no port. --- app/src/sim/ui/view.ts | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/app/src/sim/ui/view.ts b/app/src/sim/ui/view.ts index cd2776a..4c5e7cc 100644 --- a/app/src/sim/ui/view.ts +++ b/app/src/sim/ui/view.ts @@ -417,8 +417,7 @@ export function createSim(opts: SimOpts = {}): SimView { function startConnectDrag(e: PointerEvent, from: ModelInstance) { hoverId = null; - const fromRow = nodeEl(from.id); - const a = fromRow ? center(fromRow) : { x: 0, y: 0 }; + const a = portAnchor(from.id); const temp = document.createElementNS(SVGNS, "line"); temp.setAttribute("class", "wire-drag"); for (const [k, v] of [ @@ -471,11 +470,9 @@ export function createSim(opts: SimOpts = {}): SimView { } if (!model || !sim) return; for (const conn of model.connections) { - const c = nodeEl(conn.clientId); - const s = nodeEl(conn.serverId); - if (!c || !s) continue; - const a = center(c); - const b = center(s); + if (!nodeEl(conn.clientId) || !nodeEl(conn.serverId)) continue; + const a = portAnchor(conn.clientId); + const b = portAnchor(conn.serverId); const refused = !!sim.connection_error(conn.id) || sim.connection_dead(conn.id); let cls = refused ? "wire-refused" : "wire-live"; if (!refused && faultsActive(conn.faults)) cls = "wire-faulty"; @@ -495,16 +492,19 @@ export function createSim(opts: SimOpts = {}): SimView { } } - function center(elm: HTMLElement) { - let x = elm.offsetWidth / 2; - let y = elm.offsetHeight / 2; - let e: HTMLElement | null = elm; - while (e && e !== canvasEl) { - x += e.offsetLeft; - y += e.offsetTop; - e = e.offsetParent as HTMLElement | null; - } - return { x, y }; + /** Where a wire attaches for an instance: the centre of its `.node-port` dot + * (or the row itself in the embed view, which has no port), in the same + * canvas coordinate space `canvasPoint` produces. */ + function portAnchor(instanceId: string): { x: number; y: number } { + const row = nodeEl(instanceId); + const el = row?.querySelector(".node-port") ?? row; + if (!el) return { x: 0, y: 0 }; + const r = el.getBoundingClientRect(); + const cr = canvasEl.getBoundingClientRect(); + return { + x: r.left + r.width / 2 - cr.left + canvasEl.scrollLeft, + y: r.top + r.height / 2 - cr.top + canvasEl.scrollTop, + }; } // ── frame-log sources ────────────────────────────────────────────────