a41281c495b259eaf6048e1db693d77119268ff2
11 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
2d1fadb50c |
feat(offline): implicit net-state model, two-tier version gate, unified lobby
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 20s
CI / ui (pull_request) Successful in 1m12s
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m54s
Land the offline-model redesign (ANDROID_PLAN.md O1-O7): replace the explicit offline toggle with a single detected net-state machine, unify the lobby, and add a two-tier client-version gate. Contour-safe: both version vars empty => dormant, the wire change is additive, so web/pwa/vk/tg behaviour is unchanged unless the gate is deliberately configured. O1 net-state reducer (test-first). O2 store + wiring (connection/offline shims; +@capacitor/network). O3 remove the offline toggle + migrate the pref. O4 two-tier gate: hard update_required degrades to an offline Update/Play-offline notice (not terminal); soft GATEWAY_RECOMMENDED_CLIENT_VERSION -> X-Update-Recommended nudge. O5 unified lobby (device-local + greyed-from-cache server games; self-set identity; closes G-step-0). O6 create flows (with-friends online/offline segment + offline dict guard). O7 docs. Telegram/VK stay online-only (contour review): the offline model is channel-gated via offlineCapable() (native + plain web; false in the mini-apps). offlineMode.active is hard-gated on it, so the whole model (blue chrome, unified/greyed lobby, transport kill switch, device-local vs_ai/hotseat create) stays inert in Telegram/VK regardless of the detected net state (closing a version-lock path that could have flipped them offline); and New Game's with-friends hides the online/offline segment there, leaving the remote invite alone. ARCHITECTURE already declared "Telegram/VK are exempt" - this makes the code match. Deploy/CI: wire the version gate through the deploy (GATEWAY_MIN_CLIENT_VERSION + GATEWAY_RECOMMENDED_CLIENT_VERSION as plain unprefixed vars via compose + ci.yaml + prod-deploy.yaml + write-prod-env.sh + .env.example) so the gate is live when set (test-contour commit-hash version fails open; empty => dormant). Disable the manual android-build CI workflow for now (rename .disabled). Bump the app bundle budget 127->130 KB for the added always-loaded wiring. Fix the UI Docker stage (gateway/Dockerfile): install --ignore-scripts so the Alpine/musl SPA build no longer tries to native-build sharp (a local android:assets tool, unused in the image); esbuild's binary is an optional dep so Vite still builds. Tests: gateway go green (two-tier gate over a real HTTP handler); docker build of the landing + gateway targets green locally; compose --no-interpolate confirms the gateway env; two new telegram.spec.ts e2e (offline signal keeps the chrome online + quick-match opponent choice visible => server enqueue; with-friends shows no pass-and-play), RED-verified; svelte-check 0/0, vitest 617, e2e 248 (chromium + webkit), build + bundle-size 127.8/130. |
||
|
|
77a690fcf6 |
feat(ui): move in-game status to the board — highlight, score badge, full-width rack
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Failing after 13s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Failing after 0s
CI / deploy (pull_request) Has been skipped
Remove the under-board status strip and relocate its signals: - bag count -> a badge on the exchange/pass control + the foot of the move table - whose-turn / win-lose -> a thin strip above the score plaques - the tentative-move caption -> the board itself: staged tiles tint green (legal) or pink (illegal), the board tiles a formed word runs through go a shade darker, and an orange score badge sits on the main word (digit sized like a tile value, clamped on-board) The word geometry (covered cells + badge anchor) is a new pure client-side helper (ui/src/lib/formed.ts), independent of the move evaluator, so it works on the local or network preview path alike; the badge's number still comes from the preview score. Rack: a seven-column grid filling the tray width in both layouts — square, full-width tiles — with the confirm control in the fixed 7th slot. Settings: a touch-only "Zoom the board" toggle (default on, device-local) gates the tile-placement auto-zoom; taking a hint while zoomed in now zooms out so the highlighted hint word is never left off-screen. Docs (FUNCTIONAL +_ru, UI_DESIGN, ARCHITECTURE) and e2e/unit tests updated. |
||
|
|
30770a759b |
feat(offline): gate the offline toggle on dictionary readiness
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 1m10s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 1s
CI / deploy (pull_request) Successful in 1m44s
Flipping the Settings toggle to offline now checks that every enabled variant's dictionary is on the device before entering offline mode: it fetches missing ones cache-first and waits up to ~5 s (raceOfflineReady + the lazy dict/offlineready), greying the toggle meanwhile. If they cannot be readied in time it stays online and shows a 'needs internet' note, while the fetch keeps warming the cache in the background so a later flip is instant. Leaving offline is never gated. Prevents entering a half-baked offline mode (no dawg -> cannot create/play a local game) when the background preload has not finished (poor connection, or an immediate flip right after install). - offline.ts: raceOfflineReady (pure, injected sleep; unit-tested red->green) - dict/offlineready.ts: ensureOfflineDicts (cache-first preloadDicts, lazy chunk) - offline.svelte.ts: requestOffline + TOGGLE_READY_BUDGET_MS - Settings.svelte: checking/needsData state, disabled toggle, inline note - i18n: settings.offlineChecking / settings.offlineNeedsData (en+ru) - docs: FUNCTIONAL(+_ru) offline story + ARCHITECTURE offline paragraph |
||
|
|
68ecd881d9 |
feat(offline): offline-mode state, Settings toggle + blue chrome (Phase C)
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 1m9s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m39s
The deliberate offline mode is now visible: an installed web-PWA user with a confirmed email can switch to offline in Settings, and the header turns blue with an "Offline" chip. (Network gating, the dict preload, the offline lobby + local-game creation, and the service-worker precache follow in later Phase C steps.) - offline.ts / offline.svelte.ts: a sticky, device-scoped reactive offline flag (offlineMode), distinct from connection.svelte's transient reachability signal, with the pure persistence + readiness helpers (offlineReady / missingDicts) unit-tested. - Settings.svelte: an Online / Offline toggle, shown only for an installed web PWA with a confirmed email (the SW-launch + durable-account preconditions). - Header.svelte: a blue-tinted nav (color-mix from the accent, so it tracks light/dark and a Telegram theme override) + an "Offline" chip while offline; the deliberate mode suppresses the transient "Connecting…" indicator. Behaviour-preserving online (the toggle is hidden and offlineMode is false there; e2e 196 green). App entry stays within its size budget (111.6 / 112 KB gzip). |
||
|
|
51147a1429 |
feat(pwa): installable web app + landing web entry
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 19s
CI / ui (pull_request) Successful in 1m8s
CI / conformance (pull_request) Successful in 9s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m43s
Make the web SPA an installable PWA and surface it to users: - manifest.webmanifest + an install-only service worker + 192/512/maskable icons (ui/public); PWA head tags in index.html; the .webmanifest MIME type registered in the gateway (the distroless image has no /etc/mime.types). - Platform-adaptive install CTA (components/InstallApp.svelte + lib/pwa): one-tap on Chromium, manual Add-to-Home-Screen instructions on iOS Safari, hidden elsewhere / once installed / inside a Mini App. Shown under the logged-out login card and at the bottom of Settings. - The landing gains a third entry linking /app/ (the brand tile), with a caption under all three (Telegram / VK / Веб-версия). The service worker is navigation-only (network-first, cached-shell fallback); hashed assets and the Connect stream are untouched. It exists to satisfy Chromium's installability requirement and is the single growth point for a future opt-in offline mode. Tests: pwa.ts unit tests; a webui probe (manifest/sw.js content-type + the SPA-fallback-to-HTML trap); e2e for the one-tap CTA, the iOS instructions modal and the landing web entry. Docs: ARCHITECTURE §13, FUNCTIONAL (+ru), gateway README. |
||
|
|
ab1ad998aa |
feat(ui): batch of UI polish tweaks
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 9s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 52s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m19s
- admin dashboard "Users" count excludes robots (CountUsers, humans only) - lobby finished-game delete reveal: background matches header/tab-bar (--bg-elev) - mobile: swipe up on the zoom-out board (no staged tiles) shuffles the rack - lobby: status icons -25%, game-row top/bottom padding halved - toast: info tier drifts up ~a tab-bar height while fading over 2s and dismisses on tap; error tier unchanged - game: confirm-move button stays pinned at the right edge; rack tiles shrink to fit so a full rack never overflows onto it - telegram: a successful invite-link redeem shows a welcome window pointing at the bot - settings: remove the grid-lines toggle; the board is always a gapless checkerboard - settings/comms hubs: the selected tab highlight wraps icon + label as a pill Docs: UI_DESIGN (toast, board surface, selected tab), PLAN; e2e updated for the removed grid-lines toggle. |
||
|
|
fc1261e078 |
UI: tab-bar navigation — drop the hamburger
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Has been skipped
CI / integration (pull_request) Has been skipped
CI / ui (pull_request) Successful in 39s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 59s
Replace Menu.svelte (hamburger) everywhere with tab-bar navigation: - Settings hub (SettingsHub) from the lobby ⚙️ tab: Settings/Profile/ Friends/About as in-place tabs, back → lobby; the lobby ⚙️ badge counts incoming friend requests (invitations keep their own lobby section). - Comms hub (CommsHub) from the move-history 💬: Chat/Dictionary tabs, back → game; Dictionary only while the game is active. - Game menu items relocate into the open history: 🏁 leave / 📤 export in the header, 🤝 add-friend per opponent card, 💬 comms; unread chat is badged on the score bar + the 💬. - TapConfirm (tap → fading ✅ → tap) replaces the Skip/Hint press-and-hold popovers and drives the add-friend confirm. - Fix the move-history "jump": the slid board is inert and the stage can't scroll, so a swipe up genuinely closes the history. Remove Menu.svelte + HoldConfirm.svelte. Docs: UI_DESIGN, FUNCTIONAL(+ru), PRERELEASE. UI check/unit/build/bundle/e2e (Chromium+WebKit) all green. |
||
|
|
71b054227a |
Stage 17 (#12): lines-off board variant (gapless checkerboard), Settings toggle
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 8s
CI / integration (pull_request) Successful in 13s
CI / ui (pull_request) Successful in 30s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 55s
Add a 'Grid lines' preference (default off): when off the board drops the 1px grid gaps for a gapless checkerboard (plain cells alternate shades; tiles get rounded corners and a soft right-side shadow so adjacent gapless tiles still read apart), saving ~14px of width. When on, the classic lined grid returns. Persisted with the other board-style prefs; wired through Board's new lines prop. e2e locks the default and the toggle. |
||
|
|
1d0bafaabb |
Stage 17: UI defect fixes (russian variant, Telegram theme/nav/banner, reconnect, hint zoom, plaque, history, transitions, per-game cache)
- #6 align the UI variant id to the backend canonical 'russian_scrabble' (type, variants, Lobby, mock, tests) — fixes the New->Russian 400 - #11/#12 inside Telegram force the colour scheme from WebApp.colorScheme (over OS prefers-color-scheme, fixing the Telegram Desktop breakage) and hide the theme switcher - #14/#15 nav bar takes Telegram's bg; announcement banner gets a dedicated subtle --ad-bg accent token - #16 suppress the reconnect banner while backgrounded and silently reconnect the live stream on return to the foreground - #17 hint zoom scrolls to the placement's bounding box, not the top-left - #19/#20 players plaque: active seat raised with side shadows, others sunk; tap toggles history - #21/#23 history: scrollbar-gutter:stable (no word jitter); fixed-height drawer pins the bottom shadow to the board - #3 (UI) disable nudge on the player's own turn - #18a directional screen slide transitions (forward in from the right, back reveals the lobby) - #13 per-game in-memory cache: instant render on re-entry + background refresh - e2e: openGame waits for the slide transition to settle |
||
|
|
38be7fea96 |
Stage 7 polish: app shell + nav + lobby + settings (Parts A/B/C)
- Screen.svelte shell: nav bar grows, ad+content+tabbar pinned bottom (mobile feel) - AdBanner.svelte + banner.ts rotator (params, mock long/short, linkify); Header CSS chevron + grow; Menu (bigger CSS hamburger); TabBar + HoldConfirm shared components; user-select:none - Lobby: hide-empty sections, tab order New/Tournaments/Stats, place-based result badges (result.ts) - Settings: Board style > Labels (beginner/classic/none) + prefs plumbing (boardlabels.ts); i18n keys + ru mirror |
||
|
|
453ddc5e94 |
Stage 7 (wip): UI shell, libs, mock transport, screens (lobby->game), e2e smoke
- plain Svelte 5 + TS + Vite (no SvelteKit); CSS-token design system (Telegram-ready), hash router, IndexedDB session - pure libs: domain model, premium/value maps ported from solver, board replay, placement state machine, i18n en/ru - in-memory mock transport + seed data; pnpm start runs lobby->active game->board with no backend - board: pointer-drag + tap placement, MakeMove (popup / 1s-hold commit), two-state zoom, blank chooser, exchange, hint, word-check, chat - Playwright smoke (mock) green; svelte-check clean; mock bundle ~37 KB gzip |