a067a2fd01
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 11s
CI / integration (pull_request) Successful in 19s
CI / ui (pull_request) Successful in 1m14s
CI / conformance (pull_request) Successful in 11s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m50s
Reword every code comment and doc that referenced ANDROID_PLAN.md or its stage anchors (§E, G-step-0, O1) so the living docs no longer depend on the plan file: .claude/CLAUDE.md, deploy/README.md, docs/ARCHITECTURE.md, docs/TESTING.md, ui/README.md, ui/android/.gitignore, ui/e2e/native.spec.ts, ui/src/lib/netstate.ts. Re-enable the manual signed-APK workflow (android-build.yaml.disabled -> android-build.yaml; the CI host already has the Android SDK; workflow_dispatch-only, so it does not auto-run on this PR). Add docs/ICONS.md — the single-master icon/logo format reference (web, PWA, Android, future iOS, store listings).
92 lines
5.1 KiB
Markdown
92 lines
5.1 KiB
Markdown
# scrabble-ui
|
||
|
||
Pure-HTML5 game client — **plain Svelte 5 (runes) + TypeScript + Vite**, no
|
||
SvelteKit. Talks to the `gateway` over **Connect-RPC + FlatBuffers**; embeddable in
|
||
platform webviews and packageable to native via Capacitor.
|
||
|
||
The **playable slice**: sign in (guest / email), the "my games" lobby,
|
||
auto-match, the board (place tiles by drag or tap, pass, exchange, resign), hint,
|
||
word-check + complaint, per-game chat and nudge, the live in-app stream, i18n (en/ru),
|
||
theme, and the profile. **Social** surfaces add friends/blocks (with one-time friend codes),
|
||
friend-game invitations, profile editing + email binding, the statistics screen, the
|
||
lobby notification badge, and the in-game history + GCG export (share or download,
|
||
finished games only).
|
||
|
||
## Scripts
|
||
|
||
```sh
|
||
pnpm install
|
||
pnpm start # mock mode (VITE_MOCK): lobby -> game with no backend, :5173
|
||
pnpm dev # against a running gateway (Vite proxies /scrabble.edge.v1.Gateway -> :8081)
|
||
pnpm check # svelte-check / tsc
|
||
pnpm test:unit # Vitest (pure logic + FlatBuffers codec)
|
||
pnpm test:e2e # Playwright smoke against the mock
|
||
pnpm build # static bundle into dist/ (prod app ~97 KB gzip JS; per-chunk budget: scripts/bundle-size.mjs)
|
||
pnpm codegen # regenerate src/gen from edge.proto + scrabble.fbs (dev-time)
|
||
```
|
||
|
||
`GATEWAY_URL` overrides the dev proxy target; `VITE_GATEWAY_URL` sets the runtime
|
||
gateway origin for a packaged (non-proxied) build. `VITE_TELEGRAM_BOT_ID`
|
||
enables the "Link Telegram" web sign-in (the Login Widget) — inert until the site
|
||
domain is registered with BotFather (`/setdomain`); `VITE_TELEGRAM_LINK` is the
|
||
friend-invite Mini App link for the single bot (full URL `https://t.me/<bot>/<app>`).
|
||
`VITE_TELEGRAM_GAME_CHANNEL_NAME` is the "Play in Telegram" link shown on the landing page.
|
||
The **native (Capacitor) build** additionally reads `VITE_DICT_VERSION` (the bundled-dictionary version
|
||
the offline path requests, matching the packaged DAWGs), `VITE_PAYMENTS_DISABLED=1` (hide in-app purchases
|
||
in the RuStore MVP), `VITE_RUSTORE_URL` (the update overlay's store target; empty until published) and
|
||
`VITE_APP_VERSION` (`git describe --tags` → the `X-Client-Version` gate header) — all wired by
|
||
`.gitea/workflows/android-build.yaml`.
|
||
|
||
The build has **two entries**: the game SPA (`index.html`, served at `/app/` and
|
||
`/telegram/`) and a lightweight landing page (`landing.html`, served at `/`).
|
||
|
||
## How it talks to the gateway
|
||
|
||
A single Connect `Execute(message_type, payload)` carries every unary op; the request
|
||
and response bodies are **FlatBuffers** tables (`pkg/fbs/scrabble.fbs`) in `payload`.
|
||
The session token rides in `Authorization: Bearer`; a domain failure comes back in
|
||
`result_code`. `Subscribe` is the live event stream; its game events carry a state **delta**
|
||
that `lib/gamedelta.ts` applies to the per-game cache (`lib/gamecache.ts`), so a move renders without
|
||
a follow-up `game.state` (a gap falls back to a refetch). `lib/transport.ts` is the real
|
||
client; `lib/mock/` is an in-memory fake selected by `MODE === 'mock'` (and tree-shaken
|
||
out of production). Both speak the plain `lib/model.ts` types via `lib/codec.ts`.
|
||
|
||
**No board on the wire:** `StateView` is a summary + rack only, so the client
|
||
reconstructs the 15×15 board by replaying the decoded move journal (`game.history`).
|
||
**The play loop is alphabet-agnostic:** the rack and the play / exchange /
|
||
word-check requests carry **alphabet indices**, and the client caches each variant's
|
||
`(index, letter, value)` table — sent once behind `StateRequest.include_alphabet` — in
|
||
`lib/alphabet.ts`, rendering the rack and blank chooser from it. **Premium squares**
|
||
(`lib/premiums.ts`) stay a client-side geometry map **ported from
|
||
`scrabble-solver/rules/rules.go`** (pinned by a Vitest parity test); **tile values and the
|
||
alphabet now come from the server table** (their parity lives in the Go `engine.AlphabetTable`
|
||
test). Board, tiles and effects are pure CSS + Unicode — no image/font/SVG assets.
|
||
|
||
## Codegen
|
||
|
||
`src/gen/` is **committed**; CI builds it, it is not regenerated there (the same model
|
||
as the Go committed jet/fbs output). `pnpm codegen` runs `flatc --ts` on
|
||
`../pkg/fbs/scrabble.fbs` and `buf generate` (`protoc-gen-es`) on the edge proto. Needs
|
||
`flatc` 23.5.26 and `buf` on PATH.
|
||
|
||
## Theming
|
||
|
||
Design tokens are CSS custom properties (`src/app.css`); light/dark follows
|
||
`prefers-color-scheme` or an explicit choice in Settings. The token system is
|
||
**Telegram-themeParams-ready** (`lib/theme.ts`) — a Mini App can override the tokens at
|
||
runtime; the Telegram SDK itself is wired in the Telegram stage.
|
||
|
||
## Layout
|
||
|
||
```
|
||
src/
|
||
lib/ model, client facade, transport (+ mock), codec, board replay,
|
||
placement state machine, premiums (geometry), alphabet cache, stats, share,
|
||
i18n, theme, session, router, app store
|
||
components/ Header, Menu (+ badge), Modal, Toast, TabBar, Screen
|
||
screens/ Login, Lobby, NewGame, Profile, Settings, About, Friends, Stats
|
||
game/ Game, Board, Rack, Controls, MakeMove, Chat
|
||
gen/ committed edge codegen (FlatBuffers + Connect)
|
||
e2e/ Playwright smoke + social specs (mock)
|
||
```
|