Dragging a rack tile and dropping it back on the rack reorders it: the dragged tile is
lifted out (the drag ghost stands in) and the tiles at/after the pointer's drop slot slide
right to open a gap there, so the drop position is visible. On drop the rack and its stable
ids are permuted (reorderIndices, unit-tested). Reorder applies only with no pending tiles,
so it stays a clean permutation; dropping on a board cell still places as before. Server
persistence of the order follows (#4).
- Even zoom: interpolate the board scroll toward a pre-clamped target as the real width
grows/shrinks, so it magnifies A->B in one motion instead of lurching and snapping back
near the edges/centre. Recentre only on a zoom toggle, never on a focus change — so a
2nd+ placed tile and a hovered dragged tile no longer jump the board.
- Drag: highlight the aimed-at empty cell as a drop target; hover-hold auto-zoom now
fires only for the first (zoom-in) hold.
- Pinch zoom: two-finger spread/close toggles zoom toward the pinch midpoint (preventDefault
only for two touches, so one-finger scroll stays native); a second finger aborts a drag.
- Shuffle hop capped at 0.3s and disabled under reduce-motion.
- Make-move is a borderless icon button, disabled while the pending word is known illegal.
- Variant display names: english & russian_scrabble -> Scrabble/Скрэббл, erudit ->
Erudite/Эрудит; the in-game title shows the variant name (was always 'Scrabble').
Give each rack slot a stable id permuted with the letters on shuffle, so the keyed
rack reorders (rather than relabelling in place) and Svelte's animate directive fires.
hop flies each tile along a parabola (apogee ~half a tile height) with a duration that
scales with the horizontal distance (arc length): the longest 1<->7 swap takes ~0.5s,
shorter swaps land sooner. Ordinary reflow (place/recall) stays instant via a guard.
e2e locks that a shuffle preserves the rack's tile multiset.
- Board drag now auto-zooms toward a cell after holding the tile over it ~1s (#3).
- Profile is inline-editable: drop the Edit/Cancel toggle, form is always shown
for durable accounts; hint balance stays read-only; re-populate after link/merge (#5).
- A pending tile recalls by double-tap (same cell) or by dragging it back onto the
rack (unzoomed board); a single tap no longer recalls (#10).
- e2e: lock double-tap recall + single-tap no-op; drop the removed Edit-profile click.
Live play now exchanges per-variant alphabet indices instead of concrete
letters (rack out; submit-play, evaluate, exchange, word-check in). The client
caches each variant's (index, letter, value) table behind
StateRequest.include_alphabet and renders the rack and blank chooser from it,
dropping the hardcoded value/alphabet tables. History, the durable journal and
GCG stay decoded concrete characters (ARCHITECTURE §9.1, unchanged).
- pkg/fbs: new AlphabetEntry + PlayTile; StateView.rack -> [ubyte] + alphabet;
StateRequest.include_alphabet; SubmitPlay/Eval tiles -> [PlayTile];
Exchange tiles + CheckWord word -> [ubyte] (committed Go + TS regenerated).
- engine: AlphabetTable + a cached per-variant codec (LetterForIndex/EncodeRack/
DecodeTiles/DecodeWord) + BlankIndex sentinel; Go parity test.
- backend server edge maps index<->letter (new thin game.Service.GameVariant);
game.Service domain methods, engine.Game and the robot keep one letter-based
play path. The gateway forwards indices verbatim (no alphabet table).
- ui: lib/alphabet.ts in-memory cache; codec encodes/decodes indices; premiums.ts
is geometry-only; the mock seeds a fixture table; the UI normalises display to
upper case (codec + cache), leaving placement/board/checkword unchanged.
Parity moved to the Go engine.AlphabetTable test; premiums.ts loses its value
tables. Discharges TODO-4.
Second owner-review pass (iPhone simulator):
- Chat (and the modal) are sized in dvh so they shrink above the software keyboard,
keeping the start of the conversation on screen instead of pushed off the top.
- The profile away window returns to a native <input type="time" step="600"> (the iOS
wheel with 10-minute steps) instead of separate dropdowns; the timezone stays a
native offset <select>.
- A finished game reserves the rack's height (min-height) so the footer no longer
collapses when the final rack is empty — no layout jump versus an active game.
- New-game "play with friends" is made compact: a searchable, bounded-scroll friend
list, the game-type / move-time / hints controls as native selects in one row
(labels above), and Send invitation pinned at the bottom — it scales to many friends.