560470b86b
Test / test (pull_request) Failing after 10s
Restructures the Home overlay per docs/ui-redesign-2026-07.md Phase B: - Continue card (mode / elapsed / score) when a game is in progress; clicking returns to the table - Hero New Game replays the persisted Settings::last_mode (new field, serde-default Classic) with one tap; locked modes fall back to Classic - Deal options (draw 1/3, winnable-only, difficulty tiers) move into a disclosure under the hero — off the Home top level (decision 2) - Compact symmetric 2x3 mode grid; descriptions on wide viewports only - Stats strip moves to the bottom (right pane on wide) - Two-pane body on wide viewports (>= 1000 logical px) via new ui_modal::spawn_modal_sized (default card stays 720 px) - Cancel becomes "Back to table" and only renders while a live (un-won) game exists 10 new home_plugin tests; all 936 engine tests + clippy green. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
209 lines
11 KiB
Markdown
209 lines
11 KiB
Markdown
# Menu UX Redesign — July 2026
|
||
|
||
Status: IN PROGRESS. A / E / C / G shipped in v0.43.0; B implemented on
|
||
`feat/home-hierarchy` (2026-07-13); F / H and the I–M backlog remain open.
|
||
Visual identity (Terminal / base16-eighties) is settled and out of
|
||
scope — this is about **structure and interaction**, not colors or type.
|
||
|
||
## Diagnosis (from code survey, 2026-07-07)
|
||
|
||
| Surface | Today | Pain |
|
||
| --- | --- | --- |
|
||
| Settings | One scrolling modal, 5 section labels, ~37 control rows, single Done | Scroll-hunting for any toggle; endless on the folded (cover) screen; every new feature (theme store, import) stretches it further |
|
||
| Home | Modal launcher: stats strip, draw-mode row, difficulty rows, 6 equal mode cards, Cancel | No "Continue" primacy; deal options (draw/difficulty/winnable) crowd the mode choice; six cards weighted identically though Classic dominates play |
|
||
| HUD menu | Popover with 7 destinations (Help, Modes, Stats, Achievements, Profile, Settings, Leaderboard) | "Modes" duplicates Home's job; flat list, no grouping; each item opens another modal — modal-on-modal navigation |
|
||
| Global | Everything is a modal over the felt | No hierarchy; near-square unfolded Fold renders one narrow wasted column |
|
||
|
||
## Phase A — Settings tabs (highest pain, lowest risk)
|
||
|
||
- Replace the single scroll with **tabs**: `Audio · Gameplay · Appearance ·
|
||
Accessibility · Account`.
|
||
- *Appearance* = today's Cosmetic (card back, background, card theme,
|
||
theme store, import).
|
||
- *Accessibility* = extracted from Gameplay/Cosmetic: color-blind,
|
||
high-contrast, reduce-motion, touch input mode, tooltip delay.
|
||
- *Account* = Sync + Privacy merged.
|
||
- Narrow screens: tab chips in a row under the header. Wide (aspect > ~1.2 —
|
||
unfolded Fold, desktop): left rail, rows in two columns.
|
||
- Only the active tab's rows are spawned → each tab is ≤ 8 rows and fits the
|
||
cover screen without scrolling.
|
||
- **Implementation constraint:** pure `ui.rs` re-layout + one `SettingsTab`
|
||
resource. The `SettingsButton` enum, input handlers, and persistence are
|
||
untouched, so the 25 settings tests and the ambiguity gate stay green.
|
||
|
||
## Phase B — Home becomes a real home
|
||
|
||
- **Hierarchy, top to bottom:**
|
||
1. `Continue` card (only when a game is in progress) — mode, elapsed, score.
|
||
2. Hero `New Game` — one tap, reuses last mode + deal options.
|
||
3. Compact 2×3 mode grid (Classic, Daily, Zen, Challenge, Time Attack,
|
||
Seed) — smaller cards, glyph + name + one-line description on wide only.
|
||
4. Stats strip moves to the bottom (or right pane when unfolded).
|
||
- **Deal options** (draw 1/3, difficulty, winnable-only) move off the top
|
||
level into a disclosure on the Classic card / New Game hero — they are
|
||
Classic-mode concerns, not global ones.
|
||
- Cancel becomes `Back to table` and only renders when a game exists.
|
||
- Unfolded Fold / desktop: two panes — modes left, Continue + stats +
|
||
daily/weekly right.
|
||
|
||
## Phase C — HUD menu consolidation
|
||
|
||
- Drop `Modes` from the popover (Home owns mode selection; HUD's New-Game
|
||
path opens Home).
|
||
- Group the remaining six: **Play** (Home) · **You** (Profile, Stats,
|
||
Achievements) · **Community** (Leaderboard) · **System** (Settings, Help) —
|
||
section dividers in the existing popover, not a new widget.
|
||
- Dismissal audit: Esc / scrim-tap / Done must behave identically on every
|
||
modal (most already do via `ScrimDismissible`; sweep the stragglers).
|
||
|
||
## Phase D — Validation gates
|
||
|
||
- Optional Stitch mockups (Terminal design-system asset
|
||
`assets/0f1652274fea460585d0b331a9ddbb06`, project 593811793268308763) for
|
||
Home + Settings before implementing Phase B. Phase A is safe to build
|
||
straight in-engine.
|
||
- Per-phase gates: full test suite, clippy `-D warnings`, ambiguity gate at 0,
|
||
Android checklist (§15.3), on-device pass on the Fold 7 in BOTH postures.
|
||
|
||
## Sequencing & risk
|
||
|
||
| Phase | Size | Risk | Depends on |
|
||
| --- | --- | --- | --- |
|
||
| A — Settings tabs | ~1 session | Low (layout-only) | — |
|
||
| B — Home hierarchy | 1–2 sessions | Medium (touches launch flow) | D mockups if wanted |
|
||
| C — Menu grouping | small | Low | B (Modes removal) |
|
||
|
||
## Phase E — "You" hub (reuses the Phase A tab component)
|
||
|
||
Profile, Stats, Achievements, and the replay browser are four separate
|
||
modals today, reached through the HUD popover one at a time. Fold them
|
||
into ONE tabbed hub — `Profile · Stats · Achievements · Replays` — using
|
||
the same tab widget Phase A builds for Settings. Cuts the HUD popover to
|
||
four destinations and makes the tab component pay for itself twice.
|
||
Existing per-screen systems keep their markers/handlers; only the outer
|
||
shell changes (same trick as Phase A).
|
||
|
||
## Phase F — Mobile ergonomics (Fold-first)
|
||
|
||
- **Bottom action bar** on touch: Undo, Hint, Draw within thumb reach at
|
||
the bottom safe-area edge (via the existing `SafeAreaAnchoredBottom`),
|
||
score-only band stays on top. Desktop keeps the current top band.
|
||
Biggest one-handed-play win available; folded posture is tall and
|
||
top-heavy today.
|
||
- **Hold-to-repeat Undo** — press-and-hold steps back repeatedly
|
||
(respecting the existing undo scoring penalty), instead of tap-tap-tap.
|
||
- Both are additive; game stays fully playable with the top band alone
|
||
(UI-first rule §3.3 satisfied).
|
||
|
||
## Phase G — Post-win flow
|
||
|
||
Win summary today is a stats dump with a close. Give it an action
|
||
hierarchy: **Play again** (same mode/options, primary) · **Share replay**
|
||
(the upload already returns a share URL; surface it here with copy
|
||
feedback) · **Watch replay** · quiet stats below. Rematch loop drops from
|
||
4 taps to 1.
|
||
|
||
## Phase H — Feedback polish (small, bundling candidates)
|
||
|
||
- Unify queued vs. immediate toast styling (two subtly different styles
|
||
exist today) and anchor both to one position; consider a 5-item toast
|
||
history on the stats screen.
|
||
- Theme-store modal: render the preview PNGs the server already serves
|
||
(v1 is text-only rows).
|
||
- Hint: optional ghost-motion preview of the suggested move instead of a
|
||
static highlight (auto-disabled under reduce-motion).
|
||
|
||
## Phase I — Onboarding & discoverability
|
||
|
||
The first-run onboarding exists, but everything learned after it is
|
||
invisible: the radial menu (long-press/right-click), hint cycling,
|
||
tap-to-toggle HUD chrome, hotkeys. Add **contextual one-time tips** —
|
||
fired by the situation, not a tour (first stall → hint tip; first
|
||
long-press-able stack → radial tip; each shows once, stored in
|
||
Settings like `shown_achievement_onboarding` already is). Plus a
|
||
one-shot **"What's new"** card on first launch after a version bump —
|
||
ObtainX updates are silent today, so shipped features go unnoticed
|
||
(nobody will find the theme store on their own).
|
||
|
||
## Phase J — Keyboard & focus completeness (desktop + web)
|
||
|
||
`ui_focus` already gives deterministic Esc order and tab-walk
|
||
(`FocusRow`/`Focusable`), and `KeyboardDragState` exists — but full
|
||
keyboard-only play has never been audited end to end. Deliverables: every
|
||
modal reachable/dismissable without a mouse, a visible focus ring styled
|
||
to the Terminal system (current focus state is subtle), and a hotkey
|
||
cheat-sheet overlay (hold `?`) generated from the actual bindings instead
|
||
of the static Help text. Web build (Rhys) benefits most.
|
||
|
||
## Phase K — UI scale & touch-target accessibility
|
||
|
||
All type sizes flow through `TYPE_*` tokens and layout through
|
||
`compute_layout` — which makes a **UI scale setting** (90/100/115/130 %)
|
||
cheap to wire and genuinely useful on the Fold's dense cover screen.
|
||
Pair with: respect Android system font scale on first run (seed the
|
||
setting from it), and a one-time audit that every interactive element
|
||
meets the 44 px logical minimum in BOTH Fold postures (the pill buttons
|
||
and picker swatches are the suspects). Lands in the Accessibility tab
|
||
that Phase A creates.
|
||
|
||
## Phase L — Empty & loading states, standardized
|
||
|
||
Async/empty surfaces each improvise today: leaderboard with no entries,
|
||
stats with no games, replay browser with no replays, theme store
|
||
loading/error (just added), sync status text. Define ONE pattern —
|
||
glyph + one-line explanation + a single next-step action — as a
|
||
`spawn_empty_state` helper in `ui_modal`, and sweep all five surfaces
|
||
onto it. Small, mechanical, big perceived-quality win.
|
||
|
||
## Phase M — Sync transparency & data stewardship
|
||
|
||
The sync layer returns `ConflictReport`s ("data is never silently
|
||
discarded") — but no UI ever shows them; players can't tell what a merge
|
||
did. Add: a post-sync summary line ("merged, 2 conflicts kept newer
|
||
values" → tap for detail in the Account tab), a visible last-synced
|
||
timestamp (exists as `SyncStatus::LastSynced`, barely surfaced), and
|
||
local **data export/import** (zip of the JSON saves) for device
|
||
migrations without a server. Closes the trust loop the server work
|
||
opened.
|
||
|
||
## Roadmap tie-ins (already in flight elsewhere)
|
||
|
||
- **Spider mode** (feat/spider-core, PR #157): Home's mode grid gains a
|
||
7th card once engine work lands — the 2×3 grid in Phase B should be
|
||
designed as N-card flow from day one. Spider UI also needs positional
|
||
card→entity keys (documented in spider.rs module docs).
|
||
- **Show solution** (feat/solution-line, in flight): lands in the pause
|
||
menu; Phase C's grouping should leave room for it under a "Game"
|
||
cluster if the pause menu grows.
|
||
|
||
## Sequencing (updated)
|
||
|
||
| Phase | Size | Risk | Depends on |
|
||
| --- | --- | --- | --- |
|
||
| A — Settings tabs | ~1 session | Low | — (DECIDED: tabs) |
|
||
| E — "You" hub | ~1 session | Low | A's tab component |
|
||
| C — Menu grouping | small | Low | E (popover shrinks) |
|
||
| B — Home hierarchy | 1–2 sessions | Medium | D mockups if wanted |
|
||
| F — Bottom action bar | ~1 session | Medium (input paths) | — |
|
||
| G — Post-win flow | small | Low | — |
|
||
| H — Feedback polish | small each | Low | — |
|
||
| I — Onboarding & discoverability | ~1 session | Low | ships best after B/F land |
|
||
| J — Keyboard & focus completeness | ~1 session | Low | A/E (fewer modals to audit) |
|
||
| K — UI scale & touch targets | ~1 session | Medium (layout-wide) | A (Accessibility tab) |
|
||
| L — Empty-state standardization | small | Low | — |
|
||
| M — Sync transparency & export | ~1 session | Low–Medium | A (Account tab) |
|
||
|
||
Suggested order: **A → E → C** (one arc: the tab component and the menu
|
||
slimming), then **B**, then **F/G/H** as independent follow-ups.
|
||
|
||
## Open decisions
|
||
|
||
1. ~~Settings: tabs vs. sub-pages~~ — **DECIDED 2026-07-07: tabs.**
|
||
2. ~~Deal options: disclosure on Classic card vs. keep global row~~ —
|
||
**DECIDED 2026-07-09: disclosure on the Classic card / New Game hero.**
|
||
3. ~~Time Attack + Seed: top-level cards vs. "More" card~~ —
|
||
**DECIDED 2026-07-09: top-level cards, symmetric 2×3 grid.**
|
||
4. ~~Stitch mockups for Phase B, or iterate directly in-engine~~ —
|
||
**DECIDED 2026-07-09: directly in-engine.**
|
||
5. Phase F bottom bar: touch-only (proposed) or also desktop?
|