Aligns /play with the landing page and app color scheme — same
bg, panel, accent, and felt tokens from ui_theme.rs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replay viewer was using the old midnight-purple palette. Both pages now
use the exact color tokens from ui_theme.rs — matching the desktop and
Android app exactly.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Without top:0;left:0, Firefox and other non-Chrome engines place
absolute elements at the content edge (padding offset = 20px) before
the JS transform is applied, shifting slots 20px below/right of cards.
Cards already had explicit top:0;left:0; slots now match.
.recycle-label also had top:50%;left:50% which combined with the JS
inline transform would place the ↺ symbol halfway across the board.
Changed to top:0;left:0 so JS transform is the sole position source.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- header: position sticky so HUD/controls never scroll off screen
- .card .corner.bottom: remove rotate(180deg) — ♠ rotated looks like ♥,
causing players to misread suit on the bottom corner
- main: add min-width:0 so flex container doesn't push board off-edge
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add `SolitaireGame` WASM binding to `solitaire_wasm` exposing draw(),
move_cards(), undo(), auto_complete_step(), and state() — all backed by
the real solitaire_core rules engine.
Add /play route to solitaire_server serving a full vanilla-JS
interactive Klondike game (game.html / game.css / game.js). Features:
drag-and-drop card moves (mouse + touch via PointerEvents), click stock
to draw, double-click card to auto-move to foundation, undo, draw-1/3
toggle, new game, auto-complete animation, win overlay, seed display.
Rebuild solitaire_wasm.js + solitaire_wasm_bg.wasm.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The replay viewer's renderer used to wipe and rebuild every card
from scratch on every step (`board.replaceChildren()`). Each step
was a discrete redraw — fine for correctness, abrupt for the eye.
Restructured to a persistent card-element model:
- `#board` is now a positioned context (relative) instead of a
CSS grid. The dashed empty-pile placeholders are absolutely-
positioned `.slot` elements painted once at bootstrap.
- Each card lives as a sibling of the slots, absolutely-positioned
with `transform: translate(x, y)`. The CSS transition on
`transform` (280 ms cubic-bezier) runs every move as a flight
rather than a redraw.
- `cardEls: Map<id, HTMLElement>` persists across renders. Cards
unchanged between steps don't re-create their DOM at all.
- Z-index is set per-render from the card's pile index so a card
flying out from the bottom of a tableau passes behind the cards
above it.
- Newly-spawned cards (rare — only on Restart) fade in at their
target position via a `requestAnimationFrame` opacity flip;
cards that disappear (also rare) fade out and despawn after the
220 ms fade.
- `will-change: transform` lets the browser composite the
animation, keeping it smooth on low-spec hardware.
Restart now drops every existing card before resetting so the
fresh deal looks like a new game, not a continuation.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Wires the WASM module from the previous commit into a minimal web
viewer served at <server>/replays/<id>. Two new server routes:
- `GET /replays/:id` — returns the same embedded HTML page for any
id; the page itself reads the path from window.location in JS and
fetches the replay JSON via /api/replays/:id.
- `/web/*` — ServeDir for the static assets (replay.css, replay.js,
and the wasm-bindgen-generated pkg/).
Web layer:
- index.html — header, board, controls, status. Module script.
- replay.css — midnight-purple palette matching the desktop client,
dark felt board, CSS-grid pile layout, tableau fan via per-card
inline `top` offset.
- replay.js — fetches the replay, instantiates the wasm
ReplayPlayer, drives state(), step(). Controls: Restart, Play/Pause
toggle, Step. Auto-tick at 600 ms.
- pkg/ — generated by wasm-bindgen (committed so deployers don't
have to install wasm-bindgen-cli + the wasm32 target).
`tower-http = "0.6"` added to solitaire_server with the `fs` feature
for ServeDir.
To regenerate pkg/ after a solitaire_wasm change:
RUSTFLAGS='--cfg getrandom_backend="wasm_js"' \
cargo build -p solitaire_wasm \
--target wasm32-unknown-unknown --release
wasm-bindgen --target web \
--out-dir solitaire_server/web/pkg --no-typescript \
target/wasm32-unknown-unknown/release/solitaire_wasm.wasm
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>