mirror of
https://github.com/tiennm99/sokoban.git
synced 2026-10-11 03:13:52 +00:00
Update codebase summary, system architecture, changelog, roadmap and PDR to cover the new mobile input layer, haptics, gesture blocking and PWA. Refresh README features. Add the brainstorm, plan and review artifacts under plans/.
4.9 KiB
4.9 KiB
Brainstorm — Mobile Comfort for Sokoban
- Date: 2026-04-27
- Branch: main
- Repo: tiennm99/sokoban (Svelte 5 + Vite, DOM-rendered tiles)
Problem
Game is keyboard-only (arrows / WASD / U / Z / R / Esc). On phones it's unplayable: no input affordance, browser gestures (pull-to-refresh, double-tap zoom, long-press select) interfere, no thumb-friendly layout, no offline install.
User initially asked for tap-to-walk pathfinding, then pivoted to a holistic "comfortable for mobile users" goal.
Decisions (Approved)
| Area | Decision |
|---|---|
| Input | On-screen D-pad, tap-only (no auto-repeat) |
| Layout | Bottom-right D-pad, bottom-left action stack (Undo / Restart / Levels) |
| Visibility | @media (pointer: coarse) — touch devices only; desktop unchanged |
| Top HUD on mobile | Status only: LVL n Moves Best |
| Browser gestures | Block pull-to-refresh, double-tap zoom, long-press select on game area |
| Big-level fit | Lower minTile 10→16; reserve ~120px for bottom controls in computeTileSize; rely on existing .board-wrap scroll for finale dungeons |
| Safe areas | env(safe-area-inset-*) on bottom controls only |
| Haptics | navigator.vibrate(10) on box push, vibrate(60) on win. No buzz on plain step or wall bump |
| PWA | Full offline via vite-plugin-pwa (workbox); manifest, SW autoUpdate, 192/512 icons |
Approaches considered & rejected
| Approach | Why rejected |
|---|---|
| Tap-to-walk + BFS pathfinding | More UX surface (cancel, push semantics, animation queue); user picked D-pad |
| Swipe gestures | Less discoverable; gesture conflict with scrolling on big levels |
| Tap + swipe hybrid | Higher complexity for marginal benefit |
| Hold-to-repeat D-pad | User chose tap-only — explicit intent matches Sokoban's "every move counts" |
| Pinch-zoom + 2-finger pan | Defer; auto-fit + scroll wrapper is sufficient |
| Manifest-only PWA (no SW) | User wants full offline |
Architecture
GameView.svelte
├─ top HUD (mobile: status only)
├─ Board.svelte (touch-action: none)
├─ MobileControls.svelte NEW (pointer: coarse only)
│ ├─ left stack: UNDO / RESTART / LEVELS
│ └─ right D-pad: ▲ ◀ ▶ ▼
└─ overlay (win)
lib/core/
└─ haptics.js NEW pulse(ms) — no-op fallback
vite.config.js +vite-plugin-pwa
public/manifest.webmanifest generated
index.html viewport meta + theme-color
D-pad calls existing tryMove(dx,dy) / undo() / restart() — no BoardModel changes. Haptics fires from GameView.syncFromModel() by comparing previous box positions. No pathfinding, no animation queue.
Files
New
src/views/MobileControls.svelte— D-pad + action stack (~90 LOC)src/lib/core/haptics.js— ~15 LOCpublic/manifest.webmanifest+ icons (or generated by plugin)
Modified
src/views/GameView.svelte— render<MobileControls>, wire haptics, adjust top HUD on mobile, updatecomputeTileSizemarginsrc/views/Board.svelte—touch-action: none,user-select: nonesrc/views/AppButton.svelte—touch-action: manipulationsrc/app.css— global selection lock, safe-area helpersindex.html— viewport metamaximum-scale=1, user-scalable=no, theme-colorvite.config.js—vite-plugin-pwapackage.json— add devDep
Risks & Mitigations
| Risk | Mitigation |
|---|---|
iOS Safari ignores navigator.vibrate |
Acceptable — feature degrades silently |
| GH Pages base path breaks SW scope | Set base correctly in vite.config and manifest start_url / scope |
| D-pad overlaps board on tiny landscape screens | Reserve ~120px vertical margin in computeTileSize; left stack collapses to 2 rows if needed |
maximum-scale=1 blocks user accessibility zoom |
Acceptable trade-off for game UX; alternatively keep zoom but rely on touch-action: none on board only |
| Service worker stale-cache after deploy | registerType: 'autoUpdate' + workbox skipWaiting |
Success Criteria
- Phone (≤480px width): D-pad visible, all 155 levels playable one-handed, no browser pull-to-refresh, no text selection on long-press
- Push a box → short vibrate; solve a level → longer vibrate (where supported)
- "Add to Home Screen" works; opens fullscreen; plays offline after first load
- Desktop layout unchanged (D-pad hidden, keyboard intact)
- Lighthouse PWA installability check passes
Out of scope
- Tap-to-walk pathfinding (originally requested, replaced by D-pad)
- Swipe gestures
- Pinch-zoom / pan
- Orientation lock
- Settings toggle for haptics on/off
Unresolved questions
- Should
maximum-scale=1be relaxed if accessibility-zoom matters more than block-double-tap-zoom? (Currently chose strict for cleaner UX) - Icon source: reuse
public/favicon.pngor generate a Nord-themed maskable icon set? - Do we want a tiny in-game settings toggle later (haptics on/off, theme)?