feat(ui): merge Exchange/Pass into one action; drop dead Tournaments tab
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 47s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m0s

Lobby: remove the inert 🏆 Tournaments tab (it only raised a 'coming soon' toast); the lobby is back to three tabs, matching docs/FUNCTIONAL.md.

Game: fold the separate 🥺 Skip (pass) tab into the 🔄 tab, now Exchange/Pass. Its dialog passes when no tile is selected (button 'Pass without exchanging') and exchanges when tiles are ('Exchange N'). The tab is no longer gated on an empty bag (pass must stay reachable in the endgame); inside the dialog tile selection is disabled while the bag is below a full rack (bagLen >= RACK_SIZE).

The merge is UI-only. A pass is NOT an exchange of zero tiles: the rules allow an exchange only with a full rack left in the bag and forbid a zero-tile swap, while a pass is always legal; GCG (Poslfit) writes a pass as a bare '-' and an exchange as '-TILES'. Pass and exchange stay distinct end-to-end (wire, engine, history/GCG); the dialog dispatches the existing gateway.pass / gateway.exchange. No backend/wire/history/GCG change.

Docs: docs/UI_DESIGN.md, docs/FUNCTIONAL.md (+_ru), PRERELEASE.md. Tests: ui/e2e/game.spec.ts.
This commit is contained in:
Ilia Denisov
2026-06-13 16:41:55 +02:00
parent 1d41cf8222
commit a4e6727c70
9 changed files with 82 additions and 41 deletions
+4 -2
View File
@@ -102,8 +102,10 @@ settings and the game starts once every invitee has accepted — any decline can
expires after seven days.
### Playing a game
Place tiles, pass, exchange, or resign. Tiles are laid without choosing a
direction — the game infers the play's orientation, so a single tile that extends
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
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
+4 -2
View File
@@ -106,8 +106,10 @@ nudge) приходят от бота **этой партии** — по язы
ответа приглашение протухает через семь дней.
### Игровой процесс
Выкладывание фишек, пас, обмен или сдача. Фишки кладутся без выбора направления
игра сама определяет ориентацию хода, поэтому одна фишка, продолжающая уже лежащее
Выкладывание фишек, пас, обмен или сдача. Пас и обмен — один элемент управления:
без выбранных фишек — пас, с выбранными — обмен. Фишки кладутся без выбора
направления — игра сама определяет ориентацию хода, поэтому одна фишка, продолжающая
уже лежащее
слово (по столбцу или по строке), принимается. Ход проверяется по словарю партии при
сдаче и считается; безлимитный предпросмотр показывает слово (или слова), которое
образует предполагаемый ход, и его очки — либо что ход недопустим, — и ход можно
+7 -4
View File
@@ -142,15 +142,18 @@ Login uses `Screen`.
- **TapConfirm** (`components/TapConfirm.svelte`, logic in `lib/tapconfirm.ts`): the shared
tap-to-confirm control. A first tap arms a ~2 s window showing a **fading ✅** (no fade
under reduce-motion, but the window still holds); a tap on the ✅ within it confirms,
otherwise it reverts. Used by the **Skip** and **Hint** tabs (the icon morphs to ✅, no
label — replacing the old press-and-hold popover) and the in-game **add-friend 🤝**. The
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 🤝**. 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**: 🔄 Draw (disabled when the bag is empty), 🥺 Skip, 🛟 Hint (with a
remaining-count badge, disabled at zero); 🔀 Shuffle (no label, no confirm), which
- **Game tab bar**: 🔄 Exchange/Pass (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