bg.png (295 KB) and logo.png (24 KB) had zero references in src,
index.html, or any CSS, but matched the workbox precache glob and
shipped to every install. Removing them drops the precache from
555 KB to 235 KB (-58%). Update the docs note.
Also add the review-fixes plan to plans/.
Register VitePWA in the production config with autoUpdate, Nord theme
colors, /sokoban/ start_url and scope, 192/512 plus a maskable icon.
Workbox precaches the built bundle so subsequent visits run offline.
Generate matching pwa-192/512 and apple-touch-icon PNGs in public/ so
"Add to Home Screen" picks up a proper icon on iOS and Android.
Menu exposes a Donate button; the win dialog offers a Buy Me a Coffee
link. Both open a shared modal showing the VietQR code from
public/assets/qr.jpg. A persistent footer credits miti99.com.
Replace Phaser 3 with Svelte 5 as the rendering and UI layer. The
framework-agnostic core (level parser, board model, progress store,
microban level data) moves from src/game/core → src/lib/core with zero
code changes. Scenes and the hand-rolled button factory are gone; in
their place:
- src/App.svelte root router (menu / levels / game)
- src/views/MenuView title + play + progress + hints
- src/views/LevelSelectView paginated 5x4 grid with native <button>s
- src/views/GameView owns BoardModel, handles input, HUD, win
- src/views/Board purely presentational DOM renderer
- src/views/AppButton shared themed wrapper for native <button>
- src/app.css Nord palette ported to CSS variables
GameView uses a non-reactive BoardModel ref and syncs plain snapshot
fields (player, boxes, moves, won) into $state after every mutation —
Board consumes only plain props, so Svelte reactivity stays predictable
and the core class stays framework-agnostic. GameView is keyed on
levelIndex in App, so changing level remounts with fresh state.
Native <button> everywhere kills the click-hitbox class of bugs.
Animations are now CSS transform transitions (110ms) instead of tweens.
Bundle shrinks from ~1.5 MB Phaser to ~65 kB JS / 23 kB gzipped — about
60x smaller. Removed: phaser, terser, src/game, log.js (analytics
ping), phasermsg vite plugin, manual Phaser chunks, terser config,
public/style.css. Scripts simplified to dev/build.
Docs updated: codebase summary, architecture, code standards,
changelog, roadmap, README.
Replace the 3 hand-crafted JSON levels with the full Microban set (100 solvable
puzzles by David W. Skinner) stored as XSB text and parsed at runtime. Rework
the game into kebab-case modules under a 200-LOC budget:
- core/level-parser: XSB parse with flood-fill interior detection
- core/board-model: pure move/undo/win logic, Phaser-free
- core/progress-store: localStorage persistence with graceful fallback
- core/theme: Nord palette, fonts, responsive tile sizer
- ui/button-factory: one rounded button impl with hover/press
- ui/board-renderer: animated tile/wall/goal/box/player drawing
- scenes: menu, paginated level select (5x4 grid, 5 pages), game
Add WASD movement, U/Z undo, R restart, Esc to menu, live move counter,
best-move tracking per level, win overlay with next/levels actions, and a
radial-gradient CSS backdrop.
Drop the dead Arcade Physics wiring, the broken manual shutdown/destroy code,
the unused main.js self-import, and the hardcoded 3-level registry state.
Add docs/ and refresh README.