Reflects that Track B (window polish) and Track G (modal + score animations) are now landed, and brings the resume prompt and release-readiness scope in line with the post-commit state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
18 KiB
Solitaire Quest — UX Overhaul Session Handoff
Last updated: 2026-04-30 — Phase 3 complete + Phase 4 in progress (Track B landed on disk, Track G subset in flight via background agent).
⚠️ In-progress work at pause time
Smoke-test passed; Phase 4 was started. Pushed HEAD is 534870a. The working tree has uncommitted work that is NOT pushed:
Track B — window polish (on disk, ready to commit)
- File:
solitaire_app/src/main.rs(+44 lines) - What landed:
- X11/Wayland WM_CLASS via
Window::name = Some("solitaire-quest".into()) - Default position
WindowPosition::Centered(MonitorSelection::Primary) install_crash_log_hook()wraps the default panic hook to also append acrash.lognext tosettings.json. Usesstd::time::SystemTime(no new chrono dep). Falls through silently if the data dir is unavailable.
- X11/Wayland WM_CLASS via
- Skipped this round (deferred):
- App icon hookup — no artwork asset exists yet; add the loader path when art lands.
- Persisted window geometry — needs a
Settingsschema migration. - F11 fullscreen toggle — already wired in
input_plugin.rs:114, no change needed.
- Build status:
cargo build -p solitaire_appclean;cargo clippy -p solitaire_app -- -D warningsclean. - Suggested commit subject:
feat(app): window polish — class name, centered position, crash-log hook
Track G subset — modal open animation + score-change feedback (in flight)
- A background agent (
general-purpose, no worktree) was launched against this turn's tree to:- Extend
spawn_modalinsolitaire_engine/src/ui_modal.rswith aModalEnteringcomponent +advance_modal_entersystem that animates scrim alpha 0 →SCRIMand card scale 0.96 → 1.0 overMOTION_MODAL_SECS. RespectsAnimSpeed::Instantviascaled_duration. Animate-OUT path is intentionally out of scope. - In
solitaire_engine/src/hud_plugin.rs, add aScorePulse1.0→1.1→1.0 readout pulse overMOTION_SCORE_PULSE_SECSand a floating "+N" Text2d (only for ≥ +50 jumps) that drifts up ~40 px and fades overMOTION_SCORE_PULSE_SECS * 2. - Tests for both behaviours.
- Extend
- State at pause: the agent had partial edits in
solitaire_engine/src/ui_modal.rs(visible viagit status) — at least one unused-import warning was already surfacing. It had not reported back when this snapshot was taken. - Resume options for the next session:
- Wait for the notification. The agent runs in background; if Claude Code is still alive, the completion notification will fire.
- Inspect and finish manually.
git diff solitaire_engine/src/ui_modal.rs solitaire_engine/src/hud_plugin.rsto see what landed; finish or revert and restart with a tighter prompt. - Discard and restart.
git restore solitaire_engine/src/ui_modal.rs solitaire_engine/src/hud_plugin.rsthen relaunch the agent with the prompt below.
Next-session workflow at pause
- Verify the workspace builds cleanly with all in-flight changes:
cargo build --workspace && cargo clippy --workspace -- -D warnings && cargo test --workspace. The Track Bmain.rschange is independent — even if Track G is reverted, B compiles on its own. - If Track B is clean and Track G is incomplete or broken: commit Track B first using the subject above, then deal with Track G.
- If both are clean: commit each as a separate landing — one feature per commit per project convention.
- Use:
git -c user.name=funman300 -c user.email=root@vscode.infinity commit -m "<subject>" - Push with
git push origin master(requires interactive credentials ongit.aleshym.co).
Original Track G subset prompt (for relaunch if needed)
The agent's full brief is preserved here verbatim — paste into a fresh agent if the current one is unrecoverable:
Two UI/UX polish items from track G. Tree clean at HEAD `534870a`.
Sub-agents CANNOT git commit — stage your work; orchestrator commits.
G1. Modal open animation: extend spawn_modal in ui_modal.rs with a
ModalEntering component + advance_modal_enter system that animates
scrim alpha 0 → SCRIM and card scale 0.96 → 1.0 over MOTION_MODAL_SECS.
Use scaled_duration for AnimSpeed respect; ease-out curve t*(2-t).
Register the system in UiModalPlugin::build. Animate-OUT is OUT of
scope. Add ≥2 tests covering ModalEntering presence on spawn and
removal after duration elapses.
G2. Score-change feedback in hud_plugin.rs: ScorePulse component that
scales the score Text 1.0→1.1→1.0 over MOTION_SCORE_PULSE_SECS using
triangular curve. Plus a floating "+N" Text2d (only for ≥ +50 jumps)
in ACCENT_PRIMARY that drifts up 40 px and fades over
MOTION_SCORE_PULSE_SECS * 2. Add ≥2 tests for floater spawn on +50
and despawn after lifetime, plus ≥1 test that +5 does NOT spawn.
Hard requirements: workspace build + clippy --workspace -- -D warnings
+ test --workspace all green. Touch ONLY ui_modal.rs, hud_plugin.rs,
optionally ui_theme.rs for new tokens (don't think you'll need any).
DO NOT touch solitaire_app/src/main.rs (parallel work).
Where we are (Phase 3)
Phase 3 of the UX overhaul brief is done. The whole engine has been migrated to the ui_theme design-token system + ui_modal scaffold. Animation system upgraded. Final literal sweep landed. The work spans 17 commits this session, from the foundation (e14852c) through to the final sweep (54e024c).
Design direction (already saved as project memory)
- Tone: Balatro — chunky readable type, theatrical hierarchy, satisfying micro-interactions.
- Palette: Midnight Purple base (
BG_BASE#1A0F2E→BG_ELEVATED#2D1B69→BG_ELEVATED_HI#3A2580→BG_ELEVATED_TOP#482F97) + Balatro yellow primary accent (ACCENT_PRIMARY#FFD23F) + warm magenta secondary (ACCENT_SECONDARY#FF6B9D). - See memory/project_ux_overhaul_2026-04.md for the full direction.
Top complaints from the original smoke test — all closed
- HUD too cluttered. ✅ Closed by
73cad7e— readouts now sit in a 4-tier vertical stack with progressive disclosure of penalty/bonus tiers. - Y/N keyboard prompts feel like debug panels. ✅ Closed across Confirm, GameOver, Pause, Forfeit, and Settings modals — every prompt now has real Primary/Secondary/Tertiary buttons with hover/press feedback.
Foundation (done)
solitaire_engine/src/ui_theme.rs— every design token: colours, 5-rung typography scale, 4-multiple spacing scale, three radius rungs, monotonically-ordered z-index hierarchy, motion durations withscaled_duration(speed)helper.solitaire_engine/src/ui_modal.rs—spawn_modalscaffold +spawn_modal_header/spawn_modal_body_text/spawn_modal_actions/spawn_modal_buttonhelpers +ButtonVariantenum (Primary / Secondary / Tertiary) +paint_modal_buttonssystem.UiModalPluginregistered insolitaire_app/src/main.rs.
Commits this session (Phase 3, latest first)
54e024c chore(engine): final literal-to-token sweep
3a01318 feat(engine): upgrade animations — curves, scoped settle, deal jitter, cascade rotation
79d3917 chore(data): derive Copy on AnimSpeed
ba019c0 feat(engine): convert SettingsPanel to modal scaffold + Done button
18d7c12 feat(engine): convert OnboardingPlugin to 3-slide modal flow
cb93bd9 fix(engine): pin modals via GlobalZIndex and surface forfeit-no-op toast
6723416 feat(engine): convert PauseScreen to modal + add ForfeitConfirmScreen
afb0879 docs: add SESSION_HANDOFF.md mid-overhaul checkpoint
3b619b8 feat(engine): convert HomeScreen to modal scaffold + Done button
37681cf feat(engine): convert LeaderboardScreen to modal scaffold + Done button
99064ce feat(engine): convert ProfileScreen to modal scaffold + Done button
de4dba6 feat(engine): convert AchievementsScreen to modal scaffold + Done button
75fc3aa feat(engine): convert StatsScreen to modal scaffold + Done button
deb034c feat(engine): convert HelpScreen to real-button modal with kbd-chip rows
242b5fe feat(engine): convert GameOverScreen to real-button modal
3f922ed feat(engine): convert ConfirmNewGameScreen to real-button modal
8da62bd feat(engine): add ui_modal primitive (scaffold + button variants)
73cad7e feat(engine): restructure HUD into 4-tier layout, adopt design tokens
e14852c feat(engine): add ui_theme.rs design-token module
Test status: cargo build --workspace clean, cargo clippy --workspace -- -D warnings clean, 819 tests pass / 0 failed / 8 ignored.
Smoke-test checklist
The whole overhaul is on disk. Worth running through once end-to-end:
- Run the game.
cargo run -p solitaire_app --features bevy/dynamic_linking. - HUD layout reads as 4 stacked tiers (Score / Mode / Penalty / Selection) with the new midnight-purple palette.
- Open every overlay —
S(Stats),A(Achievements),P(Profile),O(Settings),L(Leaderboard),M(Home),F1(Help). Each is a centred card on a uniform scrim with a yellowDone/Closeprimary button. Hover/press states on every button. - Settings. Four sections (Audio / Gameplay / Cosmetic / Sync). Body scrolls within the modal on small windows;
Donebutton stays fixed at the bottom regardless of scroll. Card-back / Background pickers tint the selected swatch withSTATE_SUCCESS. - Confirm flow. Click
New Gamewhile a game is in progress — the abandon-current-game modal has real Cancel/Confirm buttons.Y/Enterand the yellow primary button start a new game;N/Escand the secondary button cancel. - Pause + Forfeit. Press
Esc— pause modal shows real Resume / Forfeit buttons. Forfeit button opens a Cancel/Forfeit confirmation modal stacked above the pause modal (z-index ordered correctly viaGlobalZIndex). - First-run onboarding. Delete
settings.json(or setfirst_run_complete = false) — three-slide flow shows: Welcome → How to play → Keyboard shortcuts. Navigate withNext/Backbuttons or→/←accelerators.Escskips on slide 0. - Animations.
- Slide a card to a pile — motion curves through
SmoothSnap(slight overshoot + settle), not linear lerp. - Drop a card on a valid destination — only the moved cards bounce; the rest of the table stays still.
- Start a new game — deal stagger is no longer mechanically uniform; cards land with subtle ±10% timing variation.
- Win a game — cascade now uses
Expressivecurve with per-card ±15° Z-rotation, screen shake driven by the newMOTION_WIN_SHAKE_*tokens.
- Slide a card to a pile — motion curves through
- Resize the window — cards still snap, no "snap-back-and-forth" jitter.
- Win modal — restyled with the design tokens: midnight-purple card, yellow
Play Againbutton.
Open follow-ups (not blockers)
- Home / Help redundancy. Home is still a kbd-reference modal that mostly duplicates Help. Three options: (1) keep as-is, (2) convert into a true mode launcher (Classic / Daily / Zen / Challenge / Time Attack cards, locked options visibly disabled below level 5), (3) drop entirely now that the action bar covers everything Home does. Worth asking the user which direction they want.
- Forfeit countdown toast is now superseded by the Forfeit modal (
6723416). Confirm the toast path is no longer reachable when smoke-testing. - Sub-rung pixel sizes (1 px borders, 64/80/110/150/160 px fixed widths, 28/36/50 px specific spacings) were intentionally left as literals during the step-10 sweep — they're below the smallest
SPACE_*rung. If the design system grows a "fine" spacing tier in the future, those become candidates for migration.
Resume prompt for the next session
You are a senior Rust + Bevy developer working toward a public release
of Solitaire Quest. Working directory: /home/manage/Rusty_Solitare.
Branch: master. Apply that lens to every decision: prefer shipping
quality (polish, packaging, defaults, credits, crash safety) over
greenfield features. If something is half-done, the question is
"finish for v1 or cut for v1?" not "what else can we add?".
State: HEAD=0066ca6. Phase 3 of the UX overhaul is shipped. cargo
build / clippy --workspace -- -D warnings / test --workspace all
green — 819 tests pass / 0 fail / 8 ignored.
READ FIRST (in order, before doing anything):
1. SESSION_HANDOFF.md — full state, smoke-test checklist, follow-ups
2. CLAUDE.md — hard rules (UI-first, no panics, etc.)
3. ARCHITECTURE.md §1, §15, §17 — design principles, platform
targets, deployment guide
4. ~/.claude/projects/-home-manage-Rusty-Solitare/memory/MEMORY.md
— saved feedback / project context
GATING SIGNAL — ASK FIRST, DON'T ASSUME:
Before proposing new work, ask: "Did the smoke-test (items 1-10 in
SESSION_HANDOFF.md) pass, or did anything regress?" If a regression
exists, fix it before opening any new thread.
LIKELY NEXT DIRECTIONS — surface for the user to choose, don't pick
unilaterally. All framed through "what does v1 release need?":
A. Home modal decision (open in SESSION_HANDOFF.md).
- keep as kbd-reference (duplicates Help — release-blocking
confusion?)
- repurpose as mode launcher (Classic / Daily / Zen / Challenge /
Time Attack cards, locked options below level 5)
- drop (action bar already covers every action)
B. Window + release polish — `solitaire_app/src/main.rs:34-48`
currently sets only title + resolution + min size. For public
release the window needs:
- app icon (taskbar / dock / alt-tab) — Bevy `Window::window_icon`
or platform `set_window_icon`; ship a .png/.ico asset.
- window class / app id (`Window::name`) so X11/Wayland and
Windows group taskbar entries correctly.
- persist size + position across launches (Settings already
saves to JSON; add `window_geometry` field).
- F11 (or a Settings toggle) wired to real fullscreen mode.
- centered default position on first launch (Bevy supports
`WindowPosition::Centered`).
- present_mode + vsync verification — make sure Linux/macOS
don't ship at uncapped 4000 fps.
- panic hook (`std::panic::set_hook`) that writes a crash
report next to the save files instead of silently exiting.
- macOS Info.plist / Windows .ico bundling — ARCHITECTURE.md
§17 currently only covers server deploy.
C. Sound-design audit. The scoped settle bounce (3a01318) means
audio_plugin.rs trigger sites may fire less often than before;
verify card_place / card_flip / card_invalid still feel right.
D. Sync flow end-to-end on a real second machine. Server
scaffolding exists but the register → push → pull → restore-on-
other-device round trip hasn't been exercised against the new
Settings sync section.
E. Achievement unlock completeness. ARCHITECTURE.md §11 lists 18.
The three hidden ones (speed_and_skill, comeback, zen_winner)
are most likely to be untested. For release, every advertised
achievement needs to actually fire.
F. Release-readiness backlog:
- README / store-page copy / screenshots
- LICENSE + third-party credits (xCards art, FiraMono, Bevy)
- SemVer + a v0.1.0 git tag
- itch.io / Steam packaging per platform (ARCHITECTURE.md §15)
- App signing — macOS notarization, Windows Authenticode,
Linux AppImage
- Telemetry / crash reporting — opt-in, off by default; or
confirm we ship without and rely on player reports
G. UI/UX professional polish — Phase 3 shipped the design system;
v1 wants the difference between "consistent" and "feels
intentional":
- Microcopy pass: every button label, empty state, error
message, and onboarding line reviewed for voice + clarity.
Pick one verb per concept ("Done" vs "Close" vs "OK") and
apply it everywhere.
- Empty / loading / error states: Leaderboard before any
scores, Stats before any games, Sync UI before login.
Today these are likely blank panels.
- Modal open/close animation: `MOTION_MODAL_SECS` token exists
in `ui_theme.rs:255` but isn't wired up — modals
appear/disappear instantly. Add scale-from-0.96 + scrim fade
per the token's doc comment.
- Tooltips on HUD readouts and settings labels. Bevy has no
built-in tooltip; build a small one. Hover a number to learn
what it counts.
- Accessibility: verify the AAA-contrast claim on
`ACCENT_PRIMARY` over `BG_BASE` (ui_theme.rs:65). Confirm
`AnimSpeed::Instant` disables every new animation (slide
curve, scoped settle, deal jitter, cascade rotation). Add
focus rings on `Button` entities for keyboard navigation.
- Typography choice: FiraMono is one weight, monospace for
everything. Consider shipping a second proportional face for
body + headings, keep mono for numerics (HUD score, timer).
Or commit to mono and lean into the "calm coder" feel — pick
deliberately and document the decision.
- Onboarding artwork: the 3 slides are text + buttons. For
release, stylised illustrations (or simple animated card
props on each slide) elevate the first-launch feel.
- Score-change feedback: floating "+N" numbers when score
jumps; pulse on the readout when value crosses a milestone.
`MOTION_SCORE_PULSE_SECS` is already a token.
- Splash / loading screen: today the window goes straight to
gameplay. A 1-2 second branded splash signals "real game"
vs "rust prototype".
- Hit-target audit: every interactive element ≥ 32 px on
desktop. Settings has 28 px icon buttons (`ICON_BUTTON_PX`
in settings_plugin.rs); revisit.
- Win-moment design: the cascade is good; consider a score-
breakdown reveal, streak callout, "share your time"
affordance for v1.
WORKFLOW NOTES:
- Commits use:
git -c user.name=funman300 -c user.email=root@vscode.infinity commit -m "..."
- Sub-agents can Edit/Write but CANNOT `git commit`. Brief them to
stage + verify only; orchestrator commits on their behalf.
See memory/feedback_agent_commit_limit.md.
- Remote push needs interactive credentials on git.aleshym.co; the
user runs `git push origin master` themselves.
- Every commit must pass build / clippy / test. Pause-and-verify
is the user's preferred cadence — one feature per commit.
OPEN AT THE START: ask (1) did smoke-test pass, (2) which of A–G to
pursue first. Do not assume.