feat(ui): vs-AI comms trim, overlay confirm button, recall-to-slot drag
CI / changes (pull_request) Successful in 1s
CI / unit (pull_request) Successful in 10s
CI / integration (pull_request) Successful in 14s
CI / ui (pull_request) Successful in 51s
CI / gate (pull_request) Successful in 0s
CI / deploy (pull_request) Successful in 1m8s

- vs-AI: the comms hub drops the Chat tab (Dictionary only); a finished AI
  game has no comms at all, so its 💬 history-header entry is hidden too.
- Confirm-move : redone as an absolute overlay pinned to the right edge (its
  right edge sits under the preview caption), so the rack keeps a fixed tile
  size — this reverts the tile-shrink that resized the letters at 6 tiles.
- Recall: dragging a placed tile back to the rack now drops it at the slot the
  pointer is over (drag-to-position, a gap opens), instead of snapping to its
  original slot; double-tap still recalls to the origin. New pure recallToSlot.

Tests: placement recallToSlot units; e2e recall-to-position; vs-AI comms e2e
updated. Docs: UI_DESIGN + FUNCTIONAL (+ru).
This commit is contained in:
Ilia Denisov
2026-06-19 09:54:30 +02:00
parent b5688d4848
commit 4adb608ad1
10 changed files with 179 additions and 45 deletions
+4 -2
View File
@@ -93,7 +93,7 @@ invitation — so a player still sees and plays existing games of any language.
**Quick game** lets you choose your opponent — an **AI** (the default) or a **random player**.
With **AI** you start at once against a 🤖 that joins and replies immediately: there is no waiting,
chat and nudge are off, add-friend is never shown, and the word-check tool still works; instead of a
there is no chat or nudge — only the word-check tool — add-friend is never shown; instead of a
per-move clock you lose only after **7 days without a move** (so you can abandon an AI game freely,
and the AI itself never runs out of time). Choosing a **random player** is auto-match
(always 2 players), which drops you **straight into the game and you wait inside it**: if it is your turn you
@@ -211,7 +211,9 @@ is awaited at most once per hour (the
nudge is part of the game chat); both the in-app toast and the out-of-app push (delivered
via the platform) **name the nudger** ("<opponent>: waiting for your move").
Chat and the word-check tool share one **comms screen** with **💬 chat** / **🔎 dictionary**
tabs, reached from the 💬 in the move-history header (with a back to the game). An unread chat
tabs, reached from the 💬 in the move-history header (with a back to the game). An AI game has no
chat, so its comms screen is the word-check alone — and once an AI game is finished (no chat, the
dictionary closed) the 💬 entry disappears entirely. An unread chat
entry — a message **or a nudge** from an opponent — raises a small **red dot** next to the game
in the **lobby** and on the game's **score bar**. Opening the **move history** counts as reading
the chat, even without entering it: the dot clears and the 💬 icon **fade-blinks twice**. A nudge
+4 -2
View File
@@ -96,7 +96,7 @@ nudge) приходят от бота **этой партии** — по язы
**Быстрая игра** даёт выбрать соперника — **ИИ** (по умолчанию) или **случайного игрока**. С **ИИ**
вы сразу начинаете против 🤖, который присоединяется и отвечает мгновенно: ожидания нет, чат и nudge
выключены, «добавить в друзья» не показывается, а проверка слова работает; вместо лимита на ход вы
нет — только проверка слова, — «добавить в друзья» не показывается; вместо лимита на ход вы
проигрываете только после **7 дней без хода** (так что ИИ-партию можно спокойно забросить, а сам ИИ
никогда не «просрочит» ход). Выбор **случайного игрока** — это авто-подбор (всегда 2 игрока), который
сразу **помещает вас в игру, и вы ждёте соперника прямо
@@ -217,7 +217,9 @@ nudge) приходят от бота **этой партии** — по язы
и внеприложенческий push (доставляется через платформу) **называют отправителя**
(«<соперник>: жду вашего хода»).
Чат и инструмент проверки слова — один **экран связи** со вкладками **💬 чат** / **🔎 словарь**,
открываемый по 💬 в шапке истории ходов (с кнопкой «назад» в партию). Непрочитанная запись чата —
открываемый по 💬 в шапке истории ходов (с кнопкой «назад» в партию). В ИИ-партии чата нет, поэтому её
экран связи — только проверка слова; а после завершения ИИ-партии (чата нет, словарь закрыт) сам вход
💬 исчезает. Непрочитанная запись чата —
сообщение **или nudge** от соперника — зажигает маленькую **красную точку** рядом с партией в
**лобби** и на **строке счёта** партии. Открытие **истории ходов** считается прочтением чата, даже
без захода в него: точка гаснет, а значок 💬 **дважды мигает**. Nudge также гаснет в момент, когда
+9 -6
View File
@@ -32,7 +32,8 @@ except Login uses `Screen`.
bottom tab bar whose tabs switch its body **in place** (state, not navigation), so the
back control always returns to the hub's parent (Settings → lobby, comms → game). Settings
hub tabs: ⚙️ Settings / 👤 Profile / 🤝 Friends / ️ About (Friends hidden for guests);
comms hub tabs: 💬 Chat / 🔎 Dictionary (Dictionary only while the game is active). The
comms hub tabs: 💬 Chat / 🔎 Dictionary (Dictionary only while the game is active; an
honest-AI game has no Chat, so it shows the Dictionary alone). The
routes `/settings|/profile|/friends|/about` and `/game/:id/{chat,check}` survive as hub
entry points (so a Telegram friend-code deep-link still lands on the Friends tab). The
**Feedback** screen is its own deeper route `/feedback` (back → `/about`, the Info tab), so
@@ -110,8 +111,9 @@ except Login uses `Screen`.
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
**double-tap** or by **dragging it back onto the rack** (unzoomed board only — when zoomed
the one-finger gesture scrolls). A single tap no longer recalls (too easy to trigger); a
recalled tile returns to its original rack slot.
the one-finger gesture scrolls). A single tap no longer recalls (too easy to trigger). A
**double-tap** returns the tile to its original rack slot; **dragging it back** drops it at the
rack slot the pointer is over (a gap opens there), the same drag-to-position as a rack reorder.
- **Players plaque & history** (`Game.svelte`): the seats above the board share
the width evenly; the seat whose turn it is is **raised** (a drop shadow on its sides)
while the others read **sunk in** (an inset shadow). A tap on the plaque toggles
@@ -284,8 +286,9 @@ enabled on the first, uncached load) and flip in place when an event refreshes t
under Start game notes the wait can take a while (the app may be closed meanwhile). With **AI**
selected there is no wait: the move-clock line instead reads **"Loss after 7 days of inactivity"**
and the "searching" hint is hidden; the game starts already seated, the opponent shows as **🤖**
everywhere (score card, lobby row, turn line), the add-friend 🤝 is never drawn, and the chat's
send field and 🛎️ nudge stay **disabled** while the 🔎 dictionary word-check keeps working.
everywhere (score card, lobby row, turn line), the add-friend 🤝 is never drawn, and there is
**no chat at all**: the comms hub drops the 💬 Chat tab and offers only the 🔎 dictionary
word-check (the 🛎️ nudge is likewise gone).
- **Statistics** (`screens/Stats.svelte`, the lobby ✏️ tab): a 2-column grid of stat
cards (games / wins / draws / losses / moves / hint-share / best game / win-rate) —
pure numbers, no charts; hint-share is a one-decimal percentage in the active locale's
@@ -318,7 +321,7 @@ enabled on the first, uncached load) and flip in place when an event refreshes t
never in an honest-AI game (throwaway practice). Confirming a resign reveals the full board:
it closes the history drawer (portrait) and zooms the board out.
- **Finished game**: the board keeps no last-word highlight and no zoom; the history header
offers *Export GCG* (not *Drop game*; an AI game offers neither) and the comms hub hides the 🔎 *Dictionary* tab; and
offers *Export GCG* (not *Drop game*; an AI game offers neither) and the comms hub hides the 🔎 *Dictionary* tab — and a **finished AI game has no comms at all** (no chat, dictionary closed), so its 💬 entry is dropped from the header too; and
the footer (rack + tab bar) is **drawn but inert** (greyed, non-interactive) rather than
hidden, so the layout does not jump. Chat send / nudge are the ⬆️ / 🛎️ icons. The input
row is turn-driven: on your turn the message field shows until your one allowed message is