fix(ui): recentre the board on a hint taken while already zoomed in
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Successful in 48s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 57s

The board only scrolled to the hint's word when the hint also toggled zoom (the
zoom-out case): the recenter effect tracked `zoomed` and read `focus` untracked,
so a hint taken while ALREADY zoomed in — no zoom change — never recentred and
the board stayed parked where the player was looking.

Add a `recenter` nonce the hint bumps; the board's scroll effect tracks it and,
when it fires without a zoom toggle, pans straight to `focus` (the board width is
stable, so the width-driven zoom tween has nothing to ride). Placing a 2nd+ tile
or hovering a dragged tile still set `focus` without the nonce, so the board
never jumps on those — the original no-jump intent is preserved.

e2e: zoom into the corner, then hint (the mock plays at the centre) — the board
pans toward the centre. Verified RED without the fix (both engines), GREEN with.
This commit is contained in:
Ilia Denisov
2026-06-14 11:44:04 +02:00
parent 56dbf86472
commit f3914af793
3 changed files with 69 additions and 4 deletions
+9
View File
@@ -47,6 +47,10 @@
let zoomed = $state(false);
let selected = $state<number | null>(null);
let focus = $state<{ row: number; col: number } | null>(null);
// Bumped to ask the board to scroll to `focus` even when the zoom state does not change — the
// hint recentring an already-zoomed board on the word it just played (a plain focus change is
// deliberately ignored by the board so placing/dragging tiles never jumps it).
let recenter = $state(0);
// A stable id per rack slot, permuted together with the letters on shuffle, so the rack
// tiles fly to their new positions (Rack's hop animation) instead of relabelling in place.
let rackIds = $state<number[]>([]);
@@ -629,6 +633,10 @@
row: Math.round((Math.min(...rows) + Math.max(...rows)) / 2),
col: Math.round((Math.min(...cols) + Math.max(...cols)) / 2),
};
// Ask the board to scroll to the hint word. A zoom-out board zooms in (and centres) on
// the next line; this nonce also recentres a board that is already zoomed in, where the
// unchanged zoom state would otherwise leave it parked where the player was looking.
recenter++;
}
if (isCoarse()) zoomed = true;
view = { ...view, hintsRemaining: h.hintsRemaining };
@@ -929,6 +937,7 @@
lines={app.boardLines}
locale={app.locale}
{focus}
{recenter}
{dropTarget}
oncell={onCell}
ontogglezoom={(r, c) => { focus = { row: r, col: c }; if (!gameOver) zoomed = !zoomed; }}