From a35909c6b15402174f364be372ad2dad83a39441 Mon Sep 17 00:00:00 2001 From: dax Date: Tue, 4 Aug 2026 10:09:19 +0000 Subject: pathways: chevrons float fully off the tile and only for the current tile; hover zone covers them; ghost hide is debounced - chevrons sit entirely in the gap outside the tile (never half-on the photo) and are drawn only for the current tile, killing the mosaic clutter - nav zones extend 44px outward so the whole chevron is inside the hit area; previously the chevron straddled the edge and its outer half was outside the zone, so mousing across it flipped the ghost on/off (the flicker) - ghost hide is debounced 80ms and cancelled on the next hover target, so crossing the gap between a zone and its dropzone no longer blinks the ghost - setCurrent re-renders navs so chevrons track the current tile --- public/app.js | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'public/app.js') diff --git a/public/app.js b/public/app.js index 978cdf7..35d9898 100644 --- a/public/app.js +++ b/public/app.js @@ -194,6 +194,7 @@ function setCurrent(id, rect) { if (!userZoomed) scale = worldScale() center() getExits(id) + refreshAllNav() } function renderNav(id) { @@ -202,6 +203,7 @@ function renderNav(id) { t.el.querySelectorAll(".nav").forEach((n) => n.remove()) const exits = exitsCache.get(id) if (!exits) return + const isCurrent = cur && cur.id === id const nav = document.createElement("div") nav.className = "nav" for (const ex of exits) { @@ -210,15 +212,16 @@ function renderNav(id) { zone.addEventListener("pointermove", () => { if (adjacentTile(id, ex.dir)) return zone.classList.add("preview") + cancelHideGhost() showGhost(choosePlacement(id, ex.dir)) }) zone.addEventListener("pointerleave", () => { zone.classList.remove("preview") - hideGhost() + scheduleHideGhost() }) zone.addEventListener("pointerdown", (e) => e.stopPropagation()) zone.addEventListener("click", (e) => { e.stopPropagation(); traverseFrom(id, ex.dir) }) - if (!adjacentTile(id, ex.dir)) { + if (!adjacentTile(id, ex.dir) && isCurrent) { const chev = document.createElement("div") chev.className = "nav-chev" chev.innerHTML = ARROW_SVG[ex.dir] @@ -366,6 +369,15 @@ function hideGhost() { ghostKey = "" } +let hideGhostTimer = 0 +function scheduleHideGhost() { + clearTimeout(hideGhostTimer) + hideGhostTimer = setTimeout(hideGhost, 80) +} +function cancelHideGhost() { + clearTimeout(hideGhostTimer) +} + const zones = new Map() function clearTileZones(id) { const zs = zones.get(id) @@ -393,8 +405,8 @@ function renderTileZones(id) { z.style.top = p.rect.y + "px" z.style.width = p.rect.w + "px" z.style.height = p.rect.h + "px" - z.addEventListener("pointermove", () => showGhost(p)) - z.addEventListener("pointerleave", () => hideGhost()) + z.addEventListener("pointermove", () => { cancelHideGhost(); showGhost(p) }) + z.addEventListener("pointerleave", () => scheduleHideGhost()) z.addEventListener("pointerdown", (e) => e.stopPropagation()) z.addEventListener("click", () => traverseFrom(id, dir)) world.appendChild(z) -- cgit v1.3.1