diff --git a/ui/src/game/Board.svelte b/ui/src/game/Board.svelte index 6f4557a..cf078dc 100644 --- a/ui/src/game/Board.svelte +++ b/ui/src/game/Board.svelte @@ -69,12 +69,16 @@ // toggle or an explicit recenter request (the hint) moves it. A recenter with no zoom change // pans straight to `focus`, since the board width is stable and the width-driven tween below // has nothing to ride (the case the owner hit: a hint taken while already zoomed in). - // The previous zoom state, to tell a zoom toggle from a recenter request. The board always mounts - // zoomed-out, so it starts false and is updated to the live zoom on each effect run. + // prevZoom/prevRecenter let the effect tell a zoom toggle from a recenter request. The board + // always mounts zoomed-out, so prevZoom starts false; both are updated on each effect run. let prevZoom = false; + let prevRecenter = 0; $effect(() => { const on = zoomed; - void recenter; // re-run on an explicit recenter request even when the zoom state is unchanged + // An explicit "scroll to focus now" request (the hint). Read into a variable that is USED below, + // so the reactive dependency survives minification (a bare `void recenter` could be dropped) and + // an incidental re-run never pans on its own. + const rc = recenter; const vp = viewport; if (!vp) return; const f = untrack(() => focus); @@ -91,11 +95,13 @@ finalST = Math.max(0, Math.min((f.row + 0.5) * cell - clientH / 2, fullW - clientH)); } const toggled = on !== prevZoom; + const recentered = rc !== prevRecenter; prevZoom = on; + prevRecenter = rc; if (!toggled) { - // No zoom change: an explicit recenter (the hint) pans straight to the focused word; a stale - // run with nothing focused leaves the scroll alone. - if (on && f) { + // No zoom change: pan straight to the focused word only on an explicit recenter request (the + // hint), never on an incidental re-run such as a viewport resize. + if (recentered && on && f) { vp.scrollLeft = finalSL; vp.scrollTop = finalST; }