From 31c0aec2156fa3f55ad0cf4dcb17db57b23e0bd9 Mon Sep 17 00:00:00 2001 From: "@permadeath.com" Date: Sat, 10 Oct 2026 21:52:27 -0400 Subject: [PATCH] fix(web): keep callouts on the part of the screen nothing covers A callout whose box would land off the edge, under the top bar, the side panel, the legend or an open detail panel tries the other side and is otherwise left unshown, rather than being typed out where nobody can read it. Co-Authored-By: Claude Opus 5.5 Change-Id: I7d3d6d255205ae03846018a47eaa2d16385237cd --- web/app.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/web/app.js b/web/app.js index 02e86fc9..e7571443 100644 --- a/web/app.js +++ b/web/app.js @@ -1779,6 +1779,7 @@ */ function reserveFloatText() { if (!state.options.text) return; + openArea(); for (const float of state.floats) { float.box = null; if (!float.featured) continue; @@ -1798,6 +1799,9 @@ for (const flip of [1, -1]) { const side = float.side * flip; const box = calloutBox(point, side, float.rise, float.width); + // Words under a panel or off the edge are words nobody reads. + if (box.x < open.left || box.x + box.w > open.right) continue; + if (box.y < open.top || box.y + box.h > open.bottom) continue; if (!claim(box.x, box.y, box.w, box.h)) continue; float.side = side; float.box = box; @@ -1816,6 +1820,20 @@ } } + /** The part of the screen no panel or bar covers, in screen pixels. */ + const open = { left: 0, top: 0, right: 0, bottom: 0 }; + const detailRoot = document.getElementById("detail-root"); + + /** Measures `open` for this frame. */ + function openArea() { + const wide = view.width > 1100; + const detail = detailRoot.firstElementChild; + open.left = wide && detail && !detail.hidden ? 352 : 6; + open.top = 52; + open.right = view.width - (wide ? 346 : 6); + open.bottom = view.height - (wide ? 54 : 6); + } + /** Callouts are set in the plate's monospace: a name over a line. */ const CALLOUT_FONT = `12px ${Look.MONO}`; const CALLOUT_NAME_FONT = `600 10px ${Look.MONO}`; @@ -2919,11 +2937,11 @@ const point = toScreen(float.agent.rx, float.agent.ry); if (onScreen(point, 60)) { const t = age / PING_TTL; - const open = still ? 0.3 : 1 - (1 - t) ** 3; + const spread = still ? 0.3 : 1 - (1 - t) ** 3; ctx.strokeStyle = `rgba(255, 252, 242, ${(1 - t) ** 1.6 * 0.85 * emphasised})`; ctx.lineWidth = 1.25; ctx.beginPath(); - ctx.arc(point.x, point.y, 6 + open * 30, 0, Math.PI * 2); + ctx.arc(point.x, point.y, 6 + spread * 30, 0, Math.PI * 2); ctx.stroke(); } } -- knotmirror 0.0.1