Follow-up to a54201e. The previous commit added ScrimDismissible to
Stats, Achievements, and Help; this one extends the same one-line
opt-in to the remaining three read-only modals so the click-outside-
to-close gesture is consistent across every informational surface.
Each modal now has the same shape: capture the scrim from
spawn_modal, attach ScrimDismissible after the build closure
returns. Three lines per file plus the import; no behaviour change
to the modal content itself.
Settings, Onboarding, Pause, Forfeit confirm, ConfirmNewGame, and
the win/game-over modals continue to opt OUT — all carry unsaved
or destructive state where an accidental scrim click would lose
work.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pressing M already opens the Home modal (which is the Mode Launcher
post-v0.11) and Tab cycles focus through the cards. The remaining
gap was direct keyboard activation of a specific mode — players had
to tab-and-enter or click. A new modal-scoped digit handler closes
that gap:
1 → Classic (NewGameRequestEvent)
2 → Daily Challenge (StartDailyChallengeRequestEvent)
3 → Zen (StartZenRequestEvent, gated at level 5)
4 → Challenge (StartChallengeRequestEvent, gated at level 5)
5 → Time Attack (StartTimeAttackRequestEvent, gated at level 5)
handle_home_digit_keys runs only when HomeScreen exists and short-
circuits otherwise — the digit keys can't accidentally launch a
mode mid-game. Locked modes (level < CHALLENGE_UNLOCK_LEVEL) silent-
no-op rather than firing a toast, mirroring the click-on-locked-card
behaviour without the InfoToastEvent (the click path's toast is the
authoritative "level too low" surface).
The HomePlugin Update tuple is now .chain()ed because the Bevy 0.18
parallel scheduler would otherwise let handle_home_card_click,
handle_home_cancel_button, and the new digit handler all queue a
HomeScreen despawn concurrently — the second buffer apply panics
on the already-despawned entity.
help_plugin gains a new "Mode Launcher (M)" section with the digit
rows and a level-5 unlock note. onboarding's slide-3 hotkey table
gets one new line ("M — Open Mode Launcher (then 1-5 to pick)") so
first-run players see the full path. The help-modal canonical list
now mirrors the onboarding teach.
Four new headless tests pin the contract: Digit1 launches Classic
and closes the modal; Digit3 at level 0 is a no-op (modal stays
open); Digit3 at unlock level launches Zen and closes; digit keys
outside the modal fire no events at all.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The HUD action bar (Menu / Undo / Pause / Help / Modes / New Game) and
the five Home mode-launcher cards now participate in keyboard focus,
extending Phase 1's modal-only coverage.
The HUD focus group activates only when no modal is open and the
mouse is hovering an action-bar button — the design decision avoids
stealing Tab from selection_plugin's card-selection nav for the
common "playing on the board" case. Once engaged, Tab/Shift-Tab cycles
the bar in spawn order and Enter activates. Moving the mouse off the
bar clears focus so the ring doesn't linger.
Home mode cards opt into FocusGroup::Modal(home_scrim) via an
ancestry-walking system that mirrors the Phase 1 attach helper, so
spawn_mode_card's signature is unchanged. Locked cards (Zen,
Challenge, Time Attack at level <5) get the Disabled marker so Tab
skips them and Enter is a no-op — mirroring the existing visual
locked state with real keyboard semantics.
handle_focus_keys gains a Hud-on-hover branch in its active-group
resolver and a clear_hud_focus_on_unhover system. Together they
implement the agreed UX: focus follows hover when the bar is active,
Tab cycles within the hovered group, and the ring disappears the
instant the mouse leaves.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The Home modal was previously a keyboard-shortcut reference card that
mostly duplicated Help. It now opens directly into a Mode Launcher:
five mode cards (Classic, Daily Challenge, Zen, Challenge, Time
Attack) stacked vertically with a Cancel button at the bottom.
Each card dispatches the canonical request event already used by the
HUD modes-popover (NewGameRequestEvent, StartDailyChallengeRequestEvent,
StartZenRequestEvent, StartChallengeRequestEvent,
StartTimeAttackRequestEvent), so level gates, daily-seed lookup, and
session setup all flow through the existing handlers — Home is just
another entry point.
The three modes that unlock at level 5 (Zen, Challenge, Time Attack)
render with reduced opacity and a "Reach level 5 to unlock" caption
when locked; clicking a locked card is a deliberate no-op so the
player can pick a different mode without dismissing the modal.
The keyboard-shortcut reference is dropped entirely — Help (F1) still
covers it. M continues to toggle the modal open and closed.
Adds 5 new headless tests covering card spawn, locked-state click,
unlocked-state click, Classic launch + close, and Cancel close.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Phase 3 step 5f of the UX overhaul. Closes the per-overlay
conversion phase: every read-only overlay (Help, Stats, Achievements,
Profile, Leaderboard, and now Home) sits inside the same ui_modal
scaffold, picks colours from ui_theme, and dismisses via a real
"Done" primary button alongside its keyboard accelerator.
Home modal:
- Header: "Solitaire Quest"
- Mode badge: "Current mode: <mode>" in ACCENT_PRIMARY (yellow)
- Two sections (Game Controls / Screens), each rendering keyboard
shortcuts as kbd-chip rows — the same pattern Help uses, so the
two reference screens read consistently. Section titles use
STATE_INFO.
- "L" leaderboard row added so the screens list is now complete.
- Actions: primary Done button with the M hotkey chip.
- handle_home_close_button is the click counterpart to M.
Home overlap with Help is intentional during the overhaul — both
exist as hotkey references for now. A future commit can repurpose
Home as a true mode launcher (the proposal called for this) or
remove it entirely if Help is sufficient. Either path is easier with
both screens already in the consistent shape.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Round 7 — Input & feedback
- H key cycles hints; F1 opens help (conflict resolved)
- N key cancels active Time Attack session
- Hint text distinguishes "draw from stock" vs "recycle waste"
- Forfeit (G) clears AutoCompleteState so chime does not bleed into new deal
- Zen mode timer clears immediately on Z press
- HUD shows recycle count in both draw modes
- Settings scroll position persisted across open/close
Round 8 — Polish & clarity
- Undo unavailable fires "Nothing to undo" toast
- Streak-break toast on forfeit/abandon when streak > 1
- F11 fullscreen toggle with toast; documented in help and home screens
- H-after-win, new-game countdown expiry, Tab-no-cards toasts
- Win cascade duration/stagger scales with animation speed setting
- Draw-Three cycle counter HUD ("Cycle: N/3")
- Forfeit requires G×2 confirmation within 3 s (mirrors N key)
Round 9 — Game feel & information
- Escape dismisses game-over/stuck overlay (PausePlugin skips Escape when overlay visible)
- Shake animation on rejected drag before snap-back
- Forfeit countdown cancels when any other key is pressed (U/H/D/Z/Space)
- Tab wrap-around fires "Back to first card" toast
- HUD selection indicator shows active Tab-selected pile in yellow
- Challenge time-limit HUD turns orange < 60s, red < 30s
- Win summary shows XP breakdown (+50 base, +25 no-undo, +N speed)
- Game-over overlay: "No more moves available" with clear N/Escape/G instructions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>