From f6bffd1f5736c8c2a76e51357d15f9dde719ccf8 Mon Sep 17 00:00:00 2001 From: Ilia Denisov Date: Sat, 6 Jun 2026 12:55:46 +0200 Subject: [PATCH] Stage 17 (contour round 3): Telegram Mini Apps polish, board scroll, keyboard overlay MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Telegram (lib/telegram.ts): chrome colours (setHeaderColor/setBackgroundColor/setBottomBarColor) match Telegram's header/bg/bottom bar to the app; native BackButton on sub-screens (app chevron hidden in TG); HapticFeedback on tile place/commit/error; enableClosingConfirmation while a game is open; disableVerticalSwipes so swipe-to-minimise doesn't fight tile drag / board scroll - #9 board-only vertical scroll: Screen 'column' mode lets the board area scroll while score/status/rack/tab bar stay fixed (zoom keeps its own scroll) - #10 check-word dialog opens in Modal keyboard-overlay mode (top-anchored, keyboard overlays the empty area) — no resize/relayout jank; other modals stay keyboard-aware - docs: UI_DESIGN Telegram integration + vertical fit/keyboard; PLAN round 2-3 follow-ups --- PLAN.md | 15 ++++++ docs/UI_DESIGN.md | 31 +++++++++---- ui/src/App.svelte | 12 ++++- ui/src/components/Header.svelte | 7 ++- ui/src/components/Modal.svelte | 14 +++++- ui/src/components/Screen.svelte | 10 +++- ui/src/game/Game.svelte | 21 +++++++-- ui/src/lib/app.svelte.ts | 30 +++++++++++- ui/src/lib/telegram.ts | 82 +++++++++++++++++++++++++++++++++ 9 files changed, 204 insertions(+), 18 deletions(-) diff --git a/PLAN.md b/PLAN.md index fd0b767..7ae8a4a 100644 --- a/PLAN.md +++ b/PLAN.md @@ -1254,6 +1254,21 @@ provided cert) at the contour caddy; prod VPN; rollback. and `/_gm/grafana` with one identical realm and Grafana serves anonymously, so the repeated prompt is a browser Basic-Auth scoping quirk (likely Safari/Desktop), not infra — left for the owner to re-verify, no server change. **Multi-word history (#22)** was already implemented (all formed words shown). + - **Contour-verification follow-ups** (rounds 2–3, from live testing): the Grafana + double-password was its **Live WebSocket** tripping caddy Basic-Auth — Grafana Live is + disabled (`GF_LIVE_MAX_CONNECTIONS=0`) and the admin console links to Grafana; the + move-duration panel was invisible because the deploy reseed (`rm -rf`) left the + config-only services on a stale bind mount — the deploy now **force-recreates** + caddy/otelcol/prometheus/tempo/grafana; the **per-user rate limit** was raised 120/40 → + 300/80 and the UI no longer reloads on the echo of its own move; the iOS/Telegram + reconnect banner gained a resume **grace window** (visibilitychange + pageshow/pagehide + + Telegram `activated`/`deactivated`); **Telegram Mini Apps polish** was adopted — + chrome colours (`setHeaderColor`/`setBackgroundColor`/`setBottomBarColor`), native + **BackButton**, **HapticFeedback**, **closing confirmation** in a game, + **disableVerticalSwipes**; the players-plaque highlight was inverted so the active seat + pops; the make-move popover became a direct **✅** with a tab-bar **↩️ Reset**; the hint + button disables at zero hints; plus **board-only vertical scroll** (#9) and a + **keyboard-overlay** check-word dialog (#10). ## Deferred TODOs (cross-stage) diff --git a/docs/UI_DESIGN.md b/docs/UI_DESIGN.md index cd05eae..2df7f8c 100644 --- a/docs/UI_DESIGN.md +++ b/docs/UI_DESIGN.md @@ -36,15 +36,22 @@ Login uses `Screen`. - **Screen transitions** (Stage 17, `App.svelte`): navigation slides directionally — a screen entered from the lobby flies in from the right; returning to the lobby reveals it from the left (back). Transitions are local (so they do not play on first load) and - collapse to nothing under reduce-motion. A per-game in-memory cache (`lib/gamecache.ts`) - renders a re-opened game instantly and refreshes it in the background, removing the - blank-loading flash on lobby ↔ game navigation. -- **Telegram theme** (Stage 17): inside the Mini App the colour scheme is forced from - `Telegram.WebApp.colorScheme` (over the OS `prefers-color-scheme`, which leaks into the - Telegram Desktop webview and otherwise fights it), the Settings theme switcher is hidden, - the nav bar takes Telegram's background (`header_bg_color`), and a live stream dropped by - a background suspend silently reconnects on return to the foreground (the connection - banner is suppressed while hidden). + collapse to nothing under reduce-motion. Per-game and lobby in-memory caches + (`lib/gamecache.ts`, `lib/lobbycache.ts`) render a re-opened game or the lobby instantly + and refresh in the background, removing the blank-loading flash and the lobby's "draw-in" + on lobby ↔ game navigation. +- **Telegram integration** (Stage 17, `lib/telegram.ts`): inside the Mini App the colour + scheme is forced from `Telegram.WebApp.colorScheme` (over the OS `prefers-color-scheme`, + which leaks into the Telegram Desktop webview and otherwise fights it) and the Settings + theme switcher is hidden; the nav bar takes Telegram's background and `setHeaderColor` / + `setBackgroundColor` / `setBottomBarColor` paint Telegram's own chrome to match; the + native header **BackButton** drives back-navigation (the app's chevron is hidden in + Telegram); **HapticFeedback** fires on tile placement / commit / error; **closing + confirmation** is enabled while a game is open; **vertical swipes** (swipe-to-minimise) + are disabled so they don't fight tile drag or the board scroll; and a live stream dropped + by a background suspend reconnects silently on return — the connection banner is + suppressed while hidden and for a short grace after resume (visibilitychange + + pageshow/pagehide + Telegram `activated`/`deactivated`). ## Tiles & board @@ -66,6 +73,12 @@ Login uses `Screen`. shadow) pins to the board as the board slides down, instead of tracking the table as moves accumulate; its scrollbar gutter is reserved so the centred word column does not jitter. A move's row lists every word it formed (the main word first). +- **Vertical fit & keyboard** (Stage 17): when the game does not fit the viewport, only the + board area scrolls vertically (`Screen` `column` mode; the score bar, status, rack and tab + bar stay fixed), while zoom keeps its own scroll. The check-word dialog opens in + `Modal` keyboard-overlay mode — the small sheet is top-anchored and the soft keyboard + overlays the empty area below, so the layout doesn't resize/jank; other modals stay + keyboard-aware (they size to the area above the keyboard). - **Highlights**: pending tiles use a slightly darker tile background (no outline). The last completed word gets a dark tile background — static while it is the opponent's turn (our word), and a 1 s flash when it is our turn (their word). While placing, only diff --git a/ui/src/App.svelte b/ui/src/App.svelte index 6247051..f58157f 100644 --- a/ui/src/App.svelte +++ b/ui/src/App.svelte @@ -2,8 +2,9 @@ import { onMount } from 'svelte'; import { cubicOut } from 'svelte/easing'; import { app, bootstrap } from './lib/app.svelte'; - import { router } from './lib/router.svelte'; + import { navigate, router } from './lib/router.svelte'; import { t } from './lib/i18n/index.svelte'; + import { insideTelegram, telegramBackButton } from './lib/telegram'; import Toast from './components/Toast.svelte'; import Login from './screens/Login.svelte'; import Lobby from './screens/Lobby.svelte'; @@ -19,6 +20,15 @@ void bootstrap(); }); + // Inside Telegram, drive its native header back button: show it on any sub-screen + // (everything returns to the lobby root), hide it on the lobby/login. The app's own + // back chevron is hidden in Telegram (Header.svelte) so only the native one shows. + $effect(() => { + if (!insideTelegram()) return; + const name = router.route.name; + telegramBackButton(name !== 'lobby' && name !== 'login', () => navigate('/')); + }); + // Screen transitions: the lobby is the navigation root. Entering a screen from the // lobby slides it in from the right (forward); returning to the lobby slides the // screen out to the right and reveals the lobby (back). Transitions are local, so diff --git a/ui/src/components/Header.svelte b/ui/src/components/Header.svelte index f95ff0c..e39b0a6 100644 --- a/ui/src/components/Header.svelte +++ b/ui/src/components/Header.svelte @@ -1,14 +1,19 @@