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
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.
This commit is contained in:
@@ -435,7 +435,11 @@ Key points:
|
||||
through a **session-gated `GET /dict/{variant}/{version}`** edge route
|
||||
(immutable; cached in IndexedDB best-effort) and reused across sessions; any
|
||||
miss, storage eviction or a bad-connection breaker falls back to the network
|
||||
`evaluate`. The warm-up overlay is `docs/UI_DESIGN.md`.
|
||||
`evaluate`. The warm-up overlay is `docs/UI_DESIGN.md`. The **on-board rendering** of a staged
|
||||
play — the legality tint, the cells a formed word covers, and where the score badge anchors — is a
|
||||
separate **pure geometry helper** (`ui/src/lib/formed.ts`) derived from the board and the staged
|
||||
tiles, so it works regardless of which eval path produced the preview (the badge's number still
|
||||
comes from the preview's score): legality and score stay with the eval, geometry with the board.
|
||||
|
||||
## 6. Game rules
|
||||
|
||||
|
||||
+10
-5
@@ -204,13 +204,16 @@ client, and refuses a guest's friend request, friend code or invitation outright
|
||||
|
||||
### Playing a game
|
||||
Place tiles, pass, exchange, or resign. Pass and exchange share one control —
|
||||
choosing no tiles passes the turn, choosing tiles exchanges them. Tiles are laid
|
||||
choosing no tiles passes the turn, choosing tiles exchanges them. The tiles left in the
|
||||
bag are shown as a count on that control and at the foot of the move table. Tiles are laid
|
||||
without choosing a direction — the game infers the play's orientation, so a single
|
||||
tile that extends
|
||||
an existing word (down a column or across a row) is accepted. A play is validated
|
||||
against the game's dictionary at submit time and scored; an unlimited preview
|
||||
reports the word(s) a tentative move would form and its score, or that it is not
|
||||
legal, and the move is offered for submission only once it is confirmed legal. The preview is
|
||||
against the game's dictionary at submit time and scored; an unlimited preview shows
|
||||
**on the board itself** whether the tentative move is legal — the staged tiles turn a light
|
||||
green once they form a word, a shade darker on the board tiles the word runs through, or a
|
||||
calm pink when they form none — and an **orange badge** on the main word carries the move's
|
||||
score. The confirm control is offered only once the move is legal. The preview is
|
||||
computed **on-device** for an instant response once the game's dictionary has loaded — a
|
||||
brief warm-up shows on the first open otherwise — and falls back to the server whenever the
|
||||
local dictionary is unavailable. The dictionary check tool is
|
||||
@@ -372,7 +375,9 @@ an email or Telegram and merging accounts are covered under "Accounts, linking &
|
||||
merge". Inside the Telegram Mini App, Telegram's own ⋮ menu also offers a **Settings**
|
||||
entry that opens this screen, and your display preferences (theme, board-label style and
|
||||
reduce-motion — not the interface language, which follows your account) sync across your
|
||||
Telegram devices.
|
||||
Telegram devices. On a touch device the settings also offer a **Zoom the board** toggle (on by
|
||||
default): with it off, dropping a tile no longer auto-magnifies the board toward it. It is a
|
||||
per-device preference and is hidden on desktop, where the board already fits and never auto-zooms.
|
||||
|
||||
**Preferences (which variants you can be matched into).** A profile setting picks the game
|
||||
variants — Erudite, Russian Scrabble and English Scrabble, shown **Erudite-first** — you allow
|
||||
|
||||
+11
-5
@@ -212,13 +212,16 @@ e-mail) либо ввод фразы. Активные игры форфейтя
|
||||
|
||||
### Игровой процесс
|
||||
Выкладывание фишек, пас, обмен или сдача. Пас и обмен — один элемент управления:
|
||||
без выбранных фишек — пас, с выбранными — обмен. Фишки кладутся без выбора
|
||||
без выбранных фишек — пас, с выбранными — обмен. Число оставшихся в мешке фишек
|
||||
показано счётчиком на этом элементе и внизу таблицы ходов. Фишки кладутся без выбора
|
||||
направления — игра сама определяет ориентацию хода, поэтому одна фишка, продолжающая
|
||||
уже лежащее
|
||||
слово (по столбцу или по строке), принимается. Ход проверяется по словарю партии при
|
||||
сдаче и считается; безлимитный предпросмотр показывает слово (или слова), которое
|
||||
образует предполагаемый ход, и его очки — либо что ход недопустим, — и ход можно
|
||||
отправить только после подтверждения, что он допустим. Предпросмотр считается **на устройстве**
|
||||
сдаче и считается; безлимитный предпросмотр показывает **прямо на доске**, допустим ли
|
||||
предполагаемый ход: выложенные фишки становятся светло-зелёными, когда образуют слово
|
||||
(и чуть темнее — фишки доски, через которые проходит слово), или спокойно-розовыми,
|
||||
когда слова нет, — а **оранжевый бейдж** на основном слове несёт очки хода. Кнопка
|
||||
подтверждения появляется только когда ход допустим. Предпросмотр считается **на устройстве**
|
||||
и отвечает мгновенно, как только словарь партии загружен — иначе при первом открытии показывается
|
||||
короткий прогрев, — и уходит на сервер, если локальный словарь недоступен. Инструмент проверки слова безлимитный и
|
||||
предлагает пожаловаться на любой результат; для найденного слова он также даёт ссылку на
|
||||
@@ -381,7 +384,10 @@ UTC; при создании аккаунта она подставляется
|
||||
Mini App пункт **Settings** в системном меню «⋮» Telegram также открывает этот экран, а
|
||||
ваши настройки отображения (тема, стиль подписей клеток и reduce-motion — кроме языка
|
||||
интерфейса, который следует за аккаунтом) синхронизируются между вашими устройствами в
|
||||
Telegram.
|
||||
Telegram. На сенсорном устройстве в настройках также есть переключатель **«Приближать
|
||||
доску»** (по умолчанию включён): если выключить, при кидании фишки доска больше не
|
||||
приближается к ней автоматически. Это настройка на устройство, и она скрыта на десктопе,
|
||||
где доска и так помещается целиком и авто-зума нет.
|
||||
|
||||
**Предпочтения (в какие варианты тебя можно подбирать).** Настройка профиля задаёт варианты
|
||||
игры — Эрудит, русский Scrabble и английский Scrabble, показанные **сначала Эрудит**, — в
|
||||
|
||||
+31
-20
@@ -179,16 +179,16 @@ e2e and the screenshots.
|
||||
back near the edges. It **recentres only on a zoom-in** — placing a 2nd+ tile or
|
||||
hovering a dragged tile never jumps the board. On touch the first tile placement auto-zooms
|
||||
in centred on the target, and **holding a dragged tile over a cell ~1 s** auto-zooms there
|
||||
the first time. A **swipe down on the zoom-out board** opens the history, but only when the
|
||||
the first time — both gated by the **Zoom the board** setting (on by default; the toggle is
|
||||
shown only on a touch device). A **swipe down on the zoom-out board** opens the history, but only when the
|
||||
board is scrolled to its top so it never fights the stage's own vertical scroll (the conflict
|
||||
that once retired this gesture) — and it is suppressed on the zoomed board, where the
|
||||
one-finger drag pans (and on desktop / the landscape iframe, where a mouse cannot drag-scroll the
|
||||
viewport natively, a **drag-to-pan** handler moves the zoomed board instead — active only while
|
||||
zoomed, off pending tiles, past a small threshold, swallowing the trailing click). History also
|
||||
opens on a **tap of the score bar** and closes on a tap or
|
||||
an **upward swipe** of the then-inert board (below). A **hint** auto-zooms centred on the
|
||||
hint's placement, not
|
||||
the top-left.
|
||||
an **upward swipe** of the then-inert board (below). Taking a **hint** while zoomed in **zooms
|
||||
out** to the whole board, so the hint's word — highlighted (below) — is never left off-screen.
|
||||
- **Placing & recall** (`Game.svelte`): a rack tile is placed by tap-then-tap or by
|
||||
dragging it onto a cell; while a dragged tile is carried over the board, the aimed-at empty
|
||||
cell is **highlighted as a drop target** (an accent ring). A pending tile is taken back by a
|
||||
@@ -224,7 +224,7 @@ e2e and the screenshots.
|
||||
are pinned to the card's edges.
|
||||
Unread chat is also badged on the score bar itself, so it shows with the history closed.
|
||||
- **Vertical fit & keyboard**: when the game does not fit the viewport, only the
|
||||
board area scrolls vertically (`Screen` `column` mode; the score bar, status, rack and tab
|
||||
board area scrolls vertically (`Screen` `column` mode; the turn strip, score bar, 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
|
||||
@@ -235,23 +235,31 @@ e2e and the screenshots.
|
||||
(`min(100cqw, 100cqh)`, height-driven in `Board.svelte`'s `.scaler.land`) inside a
|
||||
`container-type: size` pane, shrinking by width when the column is narrow — the board has the
|
||||
**lowest priority**, so the left panel is never squeezed. The **left panel** stacks, top to
|
||||
bottom: the score plaques, the **always-open** history (docked and scrolling with its header
|
||||
sticky — no slide-down drawer, no score-bar toggle), the status line (bag · turn · score
|
||||
preview), the rack (+ the ✅ make control) and, pinned at the bottom, the controls tab bar. Board
|
||||
bottom: the **turn/result strip**, the score plaques, the **always-open** history (docked and
|
||||
scrolling with its header sticky and the **bag count** pinned at its foot — no slide-down drawer,
|
||||
no score-bar toggle), the rack (with the ✅ confirm control in its fixed 7th slot) and, pinned at
|
||||
the bottom, the controls tab bar. Board
|
||||
**zoom works as in portrait** (double-tap / pinch / placement auto-zoom), but the viewport is the
|
||||
full pane: zoom-out shows the height-fitted square centred, and zoom-in magnifies the board past
|
||||
the pane and pans within it, occupying the full width up to the left panel (the focus-centred
|
||||
scroll is set directly, without the portrait width-progress tween). Only the history open/close
|
||||
swipes are dropped (it is always open) and the nav bar does not grow (`growNav` off). The
|
||||
portrait layout is byte-for-byte unchanged; both layouts render from the same snippets, so the
|
||||
behaviour and markup stay single-sourced. The rack tiles size to the (fixed-width) panel rather
|
||||
than the viewport width so seven tiles never overflow the column.
|
||||
- **Highlights**: pending tiles use a slightly darker tile background (no outline). The
|
||||
behaviour and markup stay single-sourced. The rack is a **seven-column grid** filling the tray
|
||||
width in both layouts, so the tiles are square and as large as the row allows (the confirm ✅ takes
|
||||
the fixed 7th slot while a play is staged).
|
||||
- **Highlights**: while composing a play the staged tiles are **tinted by legality** — a light
|
||||
green once they form a word (a shade darker on the committed board tiles the word runs through),
|
||||
a calm pink when they form none — and an **orange score badge** sits on a corner of the main
|
||||
word's tile, its digit sized like a tile's point value (so it scales with the zoom) and clamped
|
||||
to stay on the board. Off-turn or with no preview the staged tiles keep the plain pending fill.
|
||||
The colours are theme tokens in `app.css` (`--tile-pending-legal` / `-illegal`, `--tile-formed`,
|
||||
`--score-badge`); the geometry — which cells a word covers and where the badge anchors — is a
|
||||
pure client-side helper (`lib/formed.ts`), independent of the move evaluator. The
|
||||
last completed word keeps the normal tile background; instead its letters — not the point
|
||||
values — are drawn in the recent-move colour, in both themes. It is static while it is the
|
||||
opponent's turn (our word), and a 1 s flash (the letter pulses between its normal colour
|
||||
and the recent-move colour) when it is our turn (their word). While placing, only the
|
||||
pending tiles are highlighted.
|
||||
and the recent-move colour) when it is our turn (their word).
|
||||
- **Bonus-square labels** — a Settings choice (`boardlabels.ts`): `beginner` shows a
|
||||
split `3×` / `word` (localized слово/буква), `classic` a single `3W` / `3С`, `none`
|
||||
nothing. Default **beginner**.
|
||||
@@ -267,18 +275,21 @@ e2e and the screenshots.
|
||||
otherwise it reverts. Used by the **Hint** tab (the icon morphs to ✅, no label — replacing
|
||||
the old press-and-hold popover) and the in-game **add-friend 🤝** and **block ✖️** card
|
||||
controls. The **Drop game** action keeps its `Modal` confirmation (a destructive, less-frequent action).
|
||||
- **MakeMove / Reset**: when ≥1 tile is pending the rack collapses its used slots
|
||||
and shifts left, a **borderless ✅ icon button** (styled like a tab, not a filled accent
|
||||
button) beside the rack commits the move — no popover, and disabled while the pending word
|
||||
is known illegal; the 🔀 Shuffle tab is replaced by a **↩️ Reset** tab.
|
||||
- **Game tab bar**: 🔄 Exchange/Pass (opens a dialog — pick tiles to **Exchange N**, or pick
|
||||
- **Confirm / Reset**: while ≥1 tile is pending a **borderless ✅ icon button** (styled like a
|
||||
tab, not a filled accent button) takes the rack's **fixed 7th slot** and commits the move — no
|
||||
popover, and disabled while the pending word is known illegal; the 🔀 Shuffle tab is replaced by
|
||||
a **↩️ Reset** tab.
|
||||
- **Game tab bar**: 🔄 Exchange/Pass (a **bag-count badge** on its icon while the bag is non-empty;
|
||||
opens a dialog — pick tiles to **Exchange N**, or pick
|
||||
none to **Pass without exchanging**; pass is always available on your turn, exchange only
|
||||
while the bag still holds a full rack, below which the tiles disable and only the pass
|
||||
remains), 🛟 Hint (with a remaining-count badge, disabled at zero); 🔀 Shuffle (no label,
|
||||
no confirm), which
|
||||
**animates** — tiles hop along a low parabola to their new slots (duration scaled by the
|
||||
distance, the longest ≤ 0.3 s; off under reduce-motion) with a short haptic shake. The
|
||||
under-board slot shows the **Scores: N** preview. The screen **title** is the variant's
|
||||
distance, the longest ≤ 0.3 s; off under reduce-motion) with a short haptic shake. Whose turn it
|
||||
is — or the viewer's result once the game ends — shows in a **thin strip above the score
|
||||
plaques**; the tentative move's score rides the board badge (above), not an under-board caption.
|
||||
The screen **title** is the variant's
|
||||
display name (Scrabble / Скрэббл / Erudite / Эрудит), not a constant "Scrabble".
|
||||
|
||||
## Advertising banner (`components/AdBanner.svelte` in `components/Header.svelte`, `lib/banner.ts` + `lib/bannerEngine.ts`)
|
||||
|
||||
Reference in New Issue
Block a user