diff --git a/web/docs/code-standards.md b/web/docs/code-standards.md index f72e2e6..eecbfbe 100644 --- a/web/docs/code-standards.md +++ b/web/docs/code-standards.md @@ -127,8 +127,8 @@ function randomANumberInRow(weights) { ## Internal Navigation -- Use `import { base } from '$app/paths'` for internal links to preserve basePath across deployments. -- Example: `Host page` works on root (`""`) and subpath (`/loto`) equally. +- Use `import { base } from '$app/paths'` for internal links/asset URLs to preserve basePath across deployments. +- Example: `` resolves on both root (`""`) and subpath (`/loto`) builds. ## Import Organization diff --git a/web/docs/codebase-summary.md b/web/docs/codebase-summary.md index a498b08..9f9ef1b 100644 --- a/web/docs/codebase-summary.md +++ b/web/docs/codebase-summary.md @@ -12,9 +12,9 @@ | File | Purpose | |------|---------| | `src/lib/PlayerBoard.svelte` | Reusable player card (9×9 grid rendered as 3 stacked 3×9 mini-cards: Tân Tân / An khang thịnh vượng / Tân Tân tốt nhất). Tall (3:5 on mobile; wider on sm+) cells with condensed bold black numbers (`tan-tan-num` font stack), white number cells, purple empty cells by default. Handles crossed state, bingo popup, "Chờ X" toast. Accepts `storagePrefix` prop for multi-card isolation. Empty cells use `--empty-cell-bg` CSS var from settings store. | -| `src/lib/SettingsButton.svelte` | Gear icon + modal. 4 fieldsets: Giao diện (theme auto/light/dark), Chế độ quản trò (master mode toggle), Tự động xổ (auto-call + speed 1–10s), Màu ô trống (10 Excel color swatches). Reset-to-default button. Mounted on both `/` and `/master` headers. | +| `src/lib/SettingsButton.svelte` | Gear icon + modal. 4 fieldsets: Giao diện (theme auto/light/dark), Chế độ quản trò (master mode toggle), Tự động xổ (auto-call + speed 1–10s), Màu ô trống (10 Excel color swatches). Reset-to-default button. Mounted on `/`. | | `src/lib/MasterPanel.svelte` | Host controls. New game / draw, "Số vừa xổ" hero token, "Thứ tự đã xổ" history list, 11×9 last-digit-aligned tracking grid (with circular tokens + draw-order overlay). No host's own player card (the player already has one above). "Xổ số" / "Bắt đầu / Dừng" button bound to auto-call. Mounted conditionally on `/` when `settings.masterMode === true`. | -| `src/lib/PageFooter.svelte` | Footer with tagline ("Made by miti99 with ❤️ SVG icon") + link. Mounted on `/` and `/master`. | +| `src/lib/PageFooter.svelte` | Footer with tagline ("Made by miti99 with ❤️ SVG icon") + link. Mounted on `/`. | ### Game Logic | File | Purpose | diff --git a/web/docs/deployment-guide.md b/web/docs/deployment-guide.md index 631e016..645b86d 100644 --- a/web/docs/deployment-guide.md +++ b/web/docs/deployment-guide.md @@ -35,9 +35,11 @@ No GitHub Actions involved; no repo secrets needed. ## GitHub Pages (redirect-only) `.github/workflows/deploy-github-pages.yml` no longer builds the app. -It generates two tiny HTML pages (`/loto/index.html` and -`/loto/master/index.html`) that immediately redirect to the canonical -URL on Cloudflare Pages. +It generates a tiny HTML page at `/loto/index.html` that immediately +redirects to the canonical URL on Cloudflare Pages, plus a copy at +`/loto/master/index.html` to catch legacy `/master` bookmarks (the app +itself is single-page now; Cloudflare's `_redirects` collapses unknown +paths back to `/`). The redirect uses both `` (no-JS fallback) and a tiny inline script that preserves path / query / hash: @@ -46,8 +48,9 @@ and a tiny inline script that preserves path / query / hash: location.replace("https://loto.miti99.com" + path + search + hash); ``` -So `tiennm99.github.io/loto/` → `loto.miti99.com/` and -`tiennm99.github.io/loto/master` → `loto.miti99.com/master`. +So `tiennm99.github.io/loto/` → `loto.miti99.com/` and any legacy +`tiennm99.github.io/loto/master` link → `loto.miti99.com/` (after the +intermediate `/master` redirect is collapsed by `_redirects`). The redirect runs on every push to `main`. If you want full GH Pages serving back (instead of the redirect), restore the prior version of diff --git a/web/docs/development-roadmap.md b/web/docs/development-roadmap.md index 3632c07..0564c6a 100644 --- a/web/docs/development-roadmap.md +++ b/web/docs/development-roadmap.md @@ -1,35 +1,8 @@ # Development Roadmap -This document tracks **future work only**. Completed features live in git commit history, not here. - -## Currently Implemented Features - -The app is fully functional for core gameplay (Lô tô hội chợ Tân Tân variant): -- 9×9 player card with **exactly 5 per row and 5 per column**, columns - ascending top-to-bottom -- Cell marking (toggle crossed state); player can keep playing after Kinh -- Bingo detection and "Kinh!" celebration popup -- "Chờ X" waiting notifications -- Host number drawing from 1–90 deck -- 11×9 last-digit-aligned master board with draw-order overlay for fast - Kinh verification -- Player card rendered as 3 stacked Tân Tân mini-cards - (Minh Tân / Loại đặc biệt / Tấn tài tấn lộc) with cross-hatch dividers -- Settings modal: 4 fieldsets (theme auto/light/dark, master mode toggle, - auto-call speed 1–10s, 10 color presets), persisted to `loto_settings` -- Theme switcher (auto detects OS pref, explicit light/dark modes) -- Master mode toggle (shows MasterPanel on `/`) -- Auto-call timer with speed slider -- Mobile-responsive cells (aspect-ratio + text scaling) -- MasterPanel extracted component (reused on `/` and `/master`) -- PageFooter with tagline + creator link -- Host's own player card (isolated instance) -- localStorage persistence -- Dark mode -- Mobile responsive -- Offline capable -- Unit tests (Vitest: 26 game-logic tests + 27 settings-store tests, 53 total passing) - covering constraint validation, persistence, theme/master-mode/auto-call logic, and error handling +This document tracks **future work only**. Completed features live in +git commit history and `plans/`, not here. For "what's in the app right +now", see `docs/project-overview-pdr.md` and `docs/codebase-summary.md`. ## Idea Phase @@ -63,7 +36,7 @@ Full undo/redo stack with history navigation. Adds complexity to state managemen ### Component Tests (Planned) - PlayerBoard with mocked localStorage, $state/$derived verification -- Master page with different game states +- MasterPanel with different game states ### E2E Tests - Player flow: generate card → click cells → verify bingo popup diff --git a/web/docs/project-overview-pdr.md b/web/docs/project-overview-pdr.md index 0e331db..ee51966 100644 --- a/web/docs/project-overview-pdr.md +++ b/web/docs/project-overview-pdr.md @@ -71,11 +71,11 @@ That format is intentionally out of scope. ## Architecture Overview -Two public pages: -1. **`/`** — Player page. Generate a card, click cells to mark them, see bingo popup and waiting toasts. -2. **`/master`** — Host page. Control number drawing, view 9×10 master board (tracking called vs uncalled), and host's own player card. +Single page (`/`): +- Always renders the player card (generate, mark, bingo popup, "Chờ N" toasts). +- When `settings.masterMode === true`, mounts MasterPanel below the player card — host controls, draw history, and the host's own player card. -State is entirely client-side. Each page/card instance uses a unique localStorage prefix (e.g., `"loto"` for player, `"loto_master"` for host's state, `"loto_master_card"` for host's player card). +State is entirely client-side. Each card / panel instance uses a unique localStorage prefix (`"loto"` for the player card, `"loto_master"` for host draw state, `"loto_master_card"` for the host's own card). ## Deployment @@ -104,8 +104,8 @@ State is entirely client-side. Each page/card instance uses a unique localStorag - [x] Master mode toggle to show MasterPanel on player page. - [x] Auto-call timer (1–10s speed) with start/stop button. - [x] Mobile-responsive layout (aspect-square sm:aspect-[3/5] cells, text scaling). -- [x] MasterPanel extracted; mounted conditionally on `/` and directly on `/master`. -- [x] PageFooter with tagline + link on both pages. +- [x] MasterPanel extracted; mounted conditionally on `/` when master mode is on. +- [x] PageFooter with tagline + link. ## Visual Language diff --git a/web/docs/system-architecture.md b/web/docs/system-architecture.md index d76f557..bcebb57 100644 --- a/web/docs/system-architecture.md +++ b/web/docs/system-architecture.md @@ -7,20 +7,15 @@ Entry (+layout.svelte) ├─ onMount: loadSettings() — restore all 5 settings keys from loto_settings, │ apply CSS vars, toggle on theme/OS pref, setup auto-call effect │ - ├─ / (Player Page) - │ ├─ Load loto_grid, loto_crossed from localStorage - │ ├─ Display 9×9 PlayerBoard (empty cells use --empty-cell-bg from settings) - │ ├─ Generate new grid on button click - │ ├─ Mark/unmark cells on click - │ ├─ Show bingo popup + "Chờ X" toasts - │ ├─ [if settings.masterMode === true] - │ │ └─ Mount MasterPanel (host controls + draw history) - │ └─ PageFooter (tagline + miti99 link) - │ - └─ /master (Host Page) - ├─ MasterPanel (state, controls, draw history; same as above) - ├─ Back link to / - └─ PageFooter + └─ / (single page) + ├─ Load loto_grid, loto_crossed from localStorage + ├─ Display 9×9 PlayerBoard (empty cells use --empty-cell-bg from settings) + ├─ Generate new grid on button click + ├─ Mark/unmark cells on click + ├─ Show bingo popup + "Chờ X" toasts + ├─ [if settings.masterMode === true] + │ └─ Mount MasterPanel (host controls + draw history) + └─ PageFooter (tagline + miti99 link) ``` ## State Model @@ -67,32 +62,38 @@ All keys are JSON stringified. Corruption is silent (returns null). ## basePath & Asset Resolution -### Production Mode +### Production (Cloudflare Pages, default) ``` -NODE_ENV=production +npm run build +basePath="" (root) +Output: build/index.html +Deploy: loto.miti99.com (root domain) +``` + +### Production (GitHub Pages, manual) +``` +npm run build:gh basePath="/loto" -Output: /loto/index.html, /loto/_next/... -GitHub Pages serves: https://user.github.io/loto +Output: build/index.html with /loto prefix +Deploy: https://tiennm99.github.io/loto ``` -### Development Mode (Local) +### Development (Local) ``` -NEXT_DEV_PROFILE not set +npm run dev basePath="" (empty) -Dev server: http://localhost:3000 +Dev server: http://localhost:5173 ``` -### Code-Server Mode +### Development (Code-Server) ``` -NEXT_DEV_PROFILE=codeserver -CODESERVER_HOST= -CODESERVER_PORT=3000 (or env) +npm run dev:codeserver +.env.local: CODESERVER_HOST + CODESERVER_PORT basePath="/absproxy/{PORT}" -HMR Origin: CODESERVER_HOST -Access: https:///absproxy/3000/ +Access: https:///absproxy/{PORT}/ ``` -**Note**: `/absproxy/{port}` preserves the basePath through the proxy. `/proxy/{port}` strips it before forwarding, breaking HMR. +**Note**: Use `/absproxy/{port}` — `/proxy/{port}` strips the path prefix and breaks the SvelteKit base path. ## Client-Only Architecture @@ -102,9 +103,9 @@ All pages are client-only (no SSR) because: - State initialization (grid, crossed) must run in browser Files that are client-only: -- `src/routes/+page.svelte` (player page) -- `src/routes/master/+page.svelte` (host page) -- `src/lib/PlayerBoard.svelte` (shared component) +- `src/routes/+page.svelte` (single page) +- `src/lib/PlayerBoard.svelte` (player card component) +- `src/lib/MasterPanel.svelte` (host panel, mounted when `settings.masterMode`) ## Data Flow: Mark a Cell diff --git a/web/jsconfig.json b/web/jsconfig.json index 457c304..a8ef6e9 100644 --- a/web/jsconfig.json +++ b/web/jsconfig.json @@ -1,14 +1,7 @@ { + "extends": "./.svelte-kit/tsconfig.json", "compilerOptions": { - "module": "esnext", - "moduleResolution": "bundler", - "target": "es2022", "allowJs": true, - "checkJs": false, - "paths": { - "$lib": ["./src/lib"], - "$lib/*": ["./src/lib/*"] - } - }, - "include": ["src/**/*", "vite.config.js", "svelte.config.js"] + "checkJs": false + } }