Commit Graph

1039 Commits

Author SHA1 Message Date
funman300 0583a8ffae test(e2e): read v4 recording.instructions in cycle regression gate
Test / test (pull_request) Successful in 36m28s
The cycle regression gate still read payload.moves, which schema v4
removed in favour of recording.instructions, so every game reported
replay_history_mismatch:0/N and games_with_issues tripped the
--require-zero-issues gate on master (runs 587/594/596).

Verified locally: 12-game gate run reports 0 issues.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 14:18:35 -07:00
funman300 4d9d710a02 Merge pull request 'test(e2e): update replay payload specs to schema v4' (#172) from fix/web-e2e-replay-v4-shape into master
Build and Deploy / build-and-push (push) Successful in 2m32s
Web E2E / web-e2e (push) Failing after 9m10s
2026-07-10 21:01:27 +00:00
funman300 3fbee9ce30 Merge pull request 'fix(core): save files store the deal via upstream serializers (schema v6)' (#171) from fix/save-schema-v6-session-recording into master
Build and Deploy / build-and-push (push) Successful in 10m35s
Web E2E / web-e2e (push) Failing after 9m21s
Test / test (push) Successful in 36m35s
2026-07-10 18:12:28 +00:00
funman300 fd98f46267 test(e2e): update replay payload specs to schema v4
Test / test (pull_request) Successful in 37m24s
PR #170 changed the web replay payload (moves list -> embedded session
recording) but missed these Playwright specs, which only run on master
pushes and so failed post-merge. Assertions now match the v4 shape:
schema_version 4, recording.initial_state present, moves at
recording.instructions.

Verified locally against a real server with freshly built wasm bundles:
full suite 18/18 green, including the five play_canvas specs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 11:07:08 -07:00
funman300 07044f439c fix(core): save files store the deal via upstream serializers (schema v6)
Test / test (pull_request) Successful in 36m38s
GameState's save serde had the same latent flaw PR #170 removed from
replays: v5 persisted seed + saved_moves and re-dealt the board from the
seed on load, so any RNG or upstream upgrade that shifts the seed->deal
mapping would invalidate every in-progress save (graceful error, but the
player loses their game). v6 persists the existing SessionRecording
payload (upstream card_game session serde: config + dealt board +
instructions) instead; seed stays as presentation metadata only.

- v4/v5 files still load through the legacy seed path and are rewritten
  as v6 on the next save; v6 files load from the recording and replay
  with per-instruction validation, so tampered or corrupt files surface
  an error rather than a silently wrong board
- saved_moves() removed (dead once serialisation reads recording())
- tests: v6 round-trip via storage, seed-corruption immunity, v5 legacy
  load, missing-recording rejection, invalid-history rejection

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 10:21:01 -07:00
funman300 c60d465711 docs(changelog): cut 0.43.3 — replay schema v4
Android Release / build-apk (push) Successful in 7m6s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
v0.43.3
2026-07-10 09:57:04 -07:00
funman300 ce2b29f5df Merge pull request 'fix(replay): store the deal via upstream card_game serializers (schema v4)' (#170) from fix/replay-schema-v4-session-recording into master
Build and Deploy / build-and-push (push) Successful in 11m32s
Web E2E / web-e2e (push) Failing after 8m58s
Test / test (push) Successful in 38m3s
2026-07-10 16:56:33 +00:00
funman300 4cb4212829 fix(replay): store the deal via upstream card_game serializers (schema v4)
Test / test (pull_request) Successful in 36m34s
Replays previously persisted only seed + moves and re-dealt the board
from the seed at playback time, so any change to the seed->deal mapping
(RNG bumps, upstream upgrades) silently invalidated every existing
replay. Schema v4 instead embeds a SessionRecording - the upstream
card_game Session serde ({config, initial_state, instructions}) - so
playback rebuilds the exact recorded board; seed/draw_mode/mode remain
caption metadata only.

- core: SessionRecording newtype delegating to Session<Klondike> serde;
  GameState::recording() / from_recording(); from_instructions_unchecked
  fixture helper; serde_json added to dev-deps (tests only)
- data: Replay v4 (recording replaces moves); v1-v3 files rejected by
  the existing version gate
- engine: win-recording and sync upload freeze game.recording();
  playback rebuilds from the recording; Playing carries the extracted
  move list (+ Box<Replay> for clippy large_enum_variant)
- wasm: replay_export() builds the full v4 upload payload so JS never
  hand-assembles it (the old game.js path hardcoded schema_version: 2
  and corrupted u64 seeds via Math.round); ReplayPlayer::from_json
  enforces schema_version == 4 with a descriptive error
- web: game.js/play.html use replay_export; replay.js surfaces player
  construction errors in the caption instead of dying silently
- server: mode validation accepts data-carrying GameMode variants
  (Difficulty uploads previously 400'd against the String field)

Both replays on prod are May-era v1 rows with empty move lists - every
shared replay was already unplayable; the viewer now says why.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 09:10:38 -07:00
funman300 fce0266b47 Merge pull request 'chore: delete dead code approved from the PR #166 sweep' (#169) from chore/dead-code-removals into master
Build and Deploy / build-and-push (push) Successful in 11m15s
Web E2E / web-e2e (push) Successful in 9m32s
Android Release / build-apk (push) Successful in 5m18s
Test / test (push) Successful in 36m51s
v0.43.2
2026-07-09 23:46:47 +00:00
funman300 8d80f95bd0 chore(engine): apply rustfmt after dead-code removals
Test / test (pull_request) Successful in 35m45s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 15:02:15 -07:00
funman300 48f0907f78 Merge remote-tracking branch 'origin/master' into chore/dead-code-removals 2026-07-09 14:59:29 -07:00
funman300 dda6a25439 Merge pull request 'fix(engine): double-click moves exactly the clicked run (#158)' (#167) from fix/158-double-click-exact-stack into master
Build and Deploy / build-and-push (push) Successful in 8m53s
Web E2E / web-e2e (push) Successful in 9m13s
Test / test (push) Successful in 35m35s
2026-07-09 21:58:15 +00:00
funman300 1aad3251ba Merge pull request 'ci(web): build wasm in the deploy pipeline instead of committing it (#156)' (#168) from fix/156-wasm-in-ci into master
Build and Deploy / build-and-push (push) Successful in 11m35s
Web E2E / web-e2e (push) Successful in 9m15s
Test / test (push) Successful in 37m3s
2026-07-09 21:57:56 +00:00
funman300 b26200f948 chore: delete dead code approved from the PR #166 sweep
Test / test (pull_request) Failing after 18s
Three items the multi-agent sweep flagged, now removed with user
approval (§8 for the solitaire_sync changes):

- WinCascadePlugin: never registered; handle_win_cascade in
  AnimationPlugin is the live win cascade and builds its own targets.
  Its now-orphaned helpers (win_scatter_targets, cascade_delay,
  WIN_CASCADE_INTERVAL_SECS) had no callers outside their own tests
  and go with it.
- SyncCompleteEvent: written by the pull-completion system, zero
  readers — UI reads SyncStatusResource instead.
- solitaire_sync::ApiError: unused by client and server; the merge_at
  crate-root re-export goes too (merge::merge_at stays for the merge
  module's own use).

ARCHITECTURE.md updated to match.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 13:28:09 -07:00
funman300 ea8d8eee9a ci(web): build wasm in the deploy pipeline instead of committing it (#156)
Test / test (pull_request) Successful in 35m33s
The wasm bundles in solitaire_server/web/pkg/ are no longer tracked. The
web-wasm-rebuild workflow (which rebuilt them in CI and committed them
back to master) is gone; instead:

- solitaire_server/Dockerfile gains a wasm-builder stage that runs
  build_wasm.sh with the same pinned toolchain (wasm-bindgen 0.2.120,
  wasm-pack 0.14.0, binaryen 130) and the runtime image copies pkg/
  from it — the image build is now the artifacts' single source of truth.
- web-e2e builds the wasm before Playwright runs, and its trigger paths
  now include the wasm-feeding crates it actually tests.
- docker-build triggers on solitaire_data/** and build_wasm.sh too, so
  every wasm-affecting change redeploys.
- Self-hosters serving /web or /play from a source checkout run
  ./build_wasm.sh once (script header and ARCHITECTURE.md updated).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 13:15:37 -07:00
funman300 adbcb8f59a fix(engine): double-click moves exactly the clicked run (#158)
Test / test (pull_request) Successful in 36m48s
Double-click/tap auto-move previously tried the run's top card alone
before the full run headed by the clicked card, so a top card with a
foundation move hijacked the intended whole-stack move. Both handlers
now share auto_move_for_run: a lone top card goes foundation-first, a
multi-card run moves whole to a tableau or not at all. The double-click
key is now the clicked card, so two clicks on different cards of the
same stack no longer register as a double-click.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 12:55:43 -07:00
Gitea CI 15c924c3dc chore(web): regenerate wasm artifacts
Build and Deploy / build-and-push (push) Successful in 6m13s
Web E2E / web-e2e (push) Successful in 5m8s
2026-07-09 19:11:44 +00:00
funman300 ede58f9666 Merge pull request 'fix(engine): restore 60 fps Android card animations + dead-code sweep' (#166) from fix/android-anim-redraw into master
Build and Deploy / build-and-push (push) Successful in 5m52s
Web E2E / web-e2e (push) Successful in 6m42s
Web WASM Rebuild / rebuild (push) Successful in 8m56s
Android Release / build-apk (push) Successful in 5m53s
Test / test (push) Successful in 36m7s
v0.43.1
2026-07-09 18:51:06 +00:00
funman300 ccfb9394e0 chore: remove dead code and stale doc claims found in multi-agent sweep
Test / test (pull_request) Successful in 37m58s
Compiler-verified via RUSTFLAGS=--force-warn dead_code plus workspace-wide
reference greps; three parallel audit agents covered the engine crate, the
other eight crates, and Copilot commit-message-vs-diff drift.

Removed:
- replay_overlay/input.rs: 19 orphaned twins (~950 lines) of items also
  defined in mod.rs — the glob re-export made the mod.rs copies win and
  the file-level #![allow(dead_code)] hid the corpses. The live keyboard/
  button handlers and ReplayScrubKeyHold stay; the allow is retired.
- retarget_animation (never called; doc examples were its only refs)
- ScanThemesRequestEvent (never registered/written/read; its doc claimed
  a handle_scan_themes consumer that does not exist)
- _VEC3_REFERENCED workaround const + now-unneeded Vec3 import
- solitaire_data: load_stats/save_stats/time_attack_session_with_now
  default-path wrappers (the _from/_to variants are the live API) and
  surplus re-export names (settings MIN/MAX bounds, token loaders)
- solitaire_core: Session re-export (no external consumer)
- solitaire_wasm: ReplayPlayer::is_finished (no JS caller)
- solitaire_app: build_app wrapper (real entry is run())

Doc fixes:
- audio_plugin: WAV count 5→7, add FoundationCompletedEvent table row,
  drop bogus 'placeholder' label, bevy_kira_audio→kira
- ToastVariant::Warning: variant is live (5 writer plugins); dropped the
  stale allow(dead_code) and its 'currently unused' comment

Deliberately kept: Spider module (staged forward work), WinCascadePlugin
(documented alternative cascade, pending owner decision), SyncCompleteEvent
and solitaire_sync ApiError/merge_at (§8 change-controlled, flagged to owner).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 11:48:33 -07:00
funman300 b5c1ba4867 fix(engine): emit RequestRedraw from animation systems — restores 60 fps card animation on Android
Commit 38e4c03 (shipped in v0.40.0) switched Android's focused_mode to
reactive_low_power(100 ms) on the premise that every animation tick
system writes RequestRedraw while it has active work. The writers were
never actually added — the diff only contained the WinitSettings change,
imports, and add_message registrations. Result: with no touch input,
nothing wakes the winit loop during a card slide except the 100 ms
fallback ceiling, so animations render at ~10 fps on Android. Desktop
and web keep Continuous mode, which is why only Android was affected
(reported by Rhys the day v0.40.0 shipped; confirmed absent on v0.39.1).

Adds the missing MessageWriter<RequestRedraw> to all seven systems the
original commit message named:
- advance_card_animations (CardAnimationPlugin)
- advance_card_anims (AnimationPlugin — deal/win cascade/slides)
- tick_shake_anim, tick_settle_anim, tick_foundation_flourish
  (FeedbackAnimPlugin)
- drive_toast_display (AnimationPlugin — toast countdown)
- drive_auto_complete (AutoCompletePlugin — step-interval keepalive)

Each writes one RequestRedraw per frame while active work exists
(including delay phases, which also need per-frame ticks). Regression
test asserts an active CardAnimation emits RequestRedraw and an idle
board does not.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-09 11:25:23 -07:00
Gitea CI d179d9d582 chore(web): regenerate wasm artifacts
Build and Deploy / build-and-push (push) Successful in 6m46s
Web E2E / web-e2e (push) Successful in 8m27s
2026-07-09 05:53:00 +00:00
funman300 db1cc58f3a Merge pull request 'refactor(core): align Spider with upstream card_game idioms' (#162) from refactor/spider-upstream-idioms into master
Build and Deploy / build-and-push (push) Successful in 6m17s
Web E2E / web-e2e (push) Successful in 6m11s
Web WASM Rebuild / rebuild (push) Successful in 10m7s
Android Release / build-apk (push) Successful in 6m21s
Test / test (push) Successful in 36m4s
v0.43.0
2026-07-09 05:26:50 +00:00
funman300 255b781420 Merge pull request 'feat(engine): group HUD menu into Play/You/Community/System + Esc dismissal audit' (#165) from feat/menu-grouping into master
Web WASM Rebuild / rebuild (push) Has been cancelled
Build and Deploy / build-and-push (push) Successful in 2m12s
Test / test (push) Successful in 36m2s
2026-07-09 05:10:02 +00:00
funman300 5b5d587818 Merge pull request 'feat(engine): win-summary action hierarchy — Play Again, Watch, Share' (#164) from feat/win-flow into master
Web WASM Rebuild / rebuild (push) Has been cancelled
Build and Deploy / build-and-push (push) Successful in 2m22s
Test / test (push) Successful in 36m55s
2026-07-09 05:09:35 +00:00
Gitea CI 2b2e7a7f2c chore(web): regenerate wasm artifacts
Build and Deploy / build-and-push (push) Successful in 5m57s
Web E2E / web-e2e (push) Successful in 9m40s
2026-07-09 05:09:10 +00:00
funman300 5c4d440b31 Merge pull request 'feat(engine): You hub — Profile/Stats/Achievements/Replays in one tabbed modal (Phase E)' (#161) from feat/you-hub into master
Web WASM Rebuild / rebuild (push) Has been cancelled
Build and Deploy / build-and-push (push) Successful in 2m18s
Test / test (push) Successful in 38m25s
2026-07-09 05:02:07 +00:00
funman300 374858ab6d Merge pull request 'feat(engine): tabbed Settings panel — Phase A of the menu redesign' (#160) from feat/settings-tabs into master
Build and Deploy / build-and-push (push) Successful in 2m23s
Web WASM Rebuild / rebuild (push) Waiting to run
Test / test (push) Successful in 37m11s
2026-07-09 04:59:11 +00:00
funman300 38b82d4858 feat(engine): win-summary action hierarchy — Play Again, Watch, Share
Test / test (pull_request) Successful in 36m24s
Phase G of docs/ui-redesign-2026-07.md. The win modal now leads with
actions and reads the stats recap quietly below them:

- Play Again: primary, full-row, Enter accelerator unchanged; still
  fires NewGameRequestEvent::default() (same mode; deal options from
  Settings) so the rematch is one tap.
- Watch Replay / Share Replay: secondary pair reusing the global
  stats_plugin markers (WatchReplayButton / CopyShareLinkButton), so
  both act on the just-won replay — SelectedReplayIndex snaps to 0 on
  every win. This also fixes the old win-modal handler picking
  replays.last(), which after the newest-first history refactor was
  the OLDEST replay, not the newest.
- Watch closes the celebration overlay (new close_overlay_on_watch_-
  replay system); Share keeps it open and relies on the existing
  copy-feedback toasts.
- Stats recap (score breakdown reveal, time, XP, achievements) moves
  below the actions; the Time line drops from headline/primary to
  body/secondary styling.

Tests: 4 new (action presence, Play Again close+request, Watch closes,
Share keeps open) on a manual-clock fixture that steps the 0.5 s
celebration delay deterministically. Workspace suite green, workspace
clippy -D warnings clean, fmt applied.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 20:37:32 -07:00
funman300 4700bd7912 fix(engine): close Settings, Help, Leaderboard, and theme store on Esc
Test / test (pull_request) Successful in 10m16s
Phase C dismissal audit: Esc / scrim-tap / Done must behave the same
on every modal. Stragglers found and fixed — none of these had any
Esc path (pause's toggle guard swallowed the key while they were
open):

- Settings: Esc clears SettingsScreen, gated on being the topmost
  modal so a stacked sync-setup / theme-store dialog owns Esc
- Help: Esc closes alongside F1/Done (the code comment already
  claimed an Esc path existed — now it does)
- Leaderboard: Esc closes when topmost; the display-name dialog
  stacked above it now Esc-cancels like sync-setup's dialog
- Theme store: Esc closes (always topmost when open)

Scrim-tap opt-ins are unchanged — ui_modal documents which modals
deliberately stay non-dismissible on outside clicks.

Tests: escape_closes_help_screen, escape_closes_settings_screen_flag.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 20:31:55 -07:00
funman300 d4448bf0cd feat(engine): group HUD menu popover into Play/You/Community/System sections
Phase C of docs/ui-redesign-2026-07.md. The Modes row is gone — Home
owns mode selection, so the popover's Play section carries a Home row
firing the new ToggleHomeRequestEvent (read by toggle_home_screen
alongside the existing M accelerator). Section headers are quiet
caption-size labels inside the existing panel widget, not a new
widget. The action-bar Modes button and its popover are untouched
(their removal is Phase B territory when Home gains hierarchy).

- MenuOption: Modes variant replaced by Home; rows grouped Play (Home)
  · You (Profile, Stats, Achievements) · Community (Leaderboard) ·
  System (Settings, Help)
- handle_menu_option_click no longer chains into spawn_modes_popover
- Tests: tooltip sweep updated (7 rows still), new
  toggle_home_event_opens_home_screen covers the popover's open path

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-08 20:31:44 -07:00
Gitea CI e1d91bee73 ci(web-wasm-rebuild): keep on Docker runner (host executor incompatible with taiki-e) [skip ci] 2026-07-08 15:18:40 -07:00
Gitea CI 9dcd25b3e7 ci(web-wasm-rebuild): set RUNNER_OS/ARCH on the taiki-e step (step-level env)
Web WASM Rebuild / rebuild (push) Failing after 1m54s
2026-07-08 15:01:12 -07:00
Gitea CI 2ef0bce1ea ci(web-wasm-rebuild): set RUNNER_OS/RUNNER_ARCH for host-executor runner
Web WASM Rebuild / rebuild (push) Failing after 1m54s
2026-07-08 14:53:42 -07:00
Gitea CI 326ef6894a ci: route web-wasm-rebuild workflow to the rust-host runner (CT 107)
Web WASM Rebuild / rebuild (push) Failing after 2m0s
2026-07-08 13:27:34 -07:00
Gitea CI caaafe34e0 ci(test): route test workflow to the rust-host runner (CT 107)
Test / test (push) Failing after 1m3s
2026-07-08 12:27:34 -07:00
Gitea CI 2254693a7a build(wasm): honor CARGO_TARGET_DIR in build_wasm.sh [skip ci] 2026-07-08 12:25:09 -07:00
Gitea CI 4ba646738c chore(web): regenerate wasm artifacts
Build and Deploy / build-and-push (push) Successful in 6m24s
Web E2E / web-e2e (push) Successful in 5m33s
2026-07-08 03:08:06 +00:00
funman300 251d35bc28 Merge pull request 'feat: Show solution — winning_line core API + auto-playback (card_game bucket 4)' (#159) from feat/solution-line into master
Build and Deploy / build-and-push (push) Successful in 6m20s
Test / test (push) Successful in 11m24s
Web E2E / web-e2e (push) Successful in 4m46s
Web WASM Rebuild / rebuild (push) Successful in 8m10s
2026-07-08 01:08:58 +00:00
funman300 1d2b6dc5de refactor(core): align Spider with upstream card_game idioms
Test / test (pull_request) Successful in 37m4s
Addresses upstream author review of the Spider core (PR #157):

- RNG: drop the hand-rolled SplitMix64 + Fisher-Yates; deals now use
  rand::rngs::StdRng seeded via seed_from_u64 + SliceRandom::shuffle,
  exactly like klondike::with_seed. solitaire_core gains the same
  pinned rand dep klondike uses (0.10.1, std_rng only — already in the
  lock, no new transitive deps). Deals for a given seed change; Spider
  has no persisted games yet, so nothing breaks.
- Enums over integers: pile indices and card counts in the instruction
  type are now SpiderTableau (Tableau1..Tableau10, with an ALL const)
  and RunLength (Run1..Run13); out-of-range piles and zero-card moves
  are unrepresentable. Rank comparisons use Rank::checked_add instead
  of u8 arithmetic.
- Fixed-size containers: build_deck returns Stack<104> instead of
  Vec<Card>; possible_instructions is a const-iterated SpiderIter +
  validity filter (klondike's KlondikeIter pattern) instead of
  collecting into a Vec.
- Upstream naming: SpiderGame -> Spider (matches Klondike),
  tableau_face_up/_down -> tableau_face_up_cards/_down_cards,
  stock_len -> stock() accessor, with_rng added alongside with_seed.
- Solver access: SpiderGameState now exposes session(), the same
  escape hatch GameState has — the wrapper no longer pretends to hide
  solve(), which SessionConfig budgets already gate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 17:58:57 -07:00
funman300 fe3c3aed31 fix(ci): cap cargo to two parallel jobs so concurrent test-binary links fit runner memory
Test / test (push) Successful in 36m31s
Runs 514/516/519 (incl. master itself) died on 'ld terminated with
signal 9' — the workspace now links enough large test binaries that
per-core parallel linking OOMs the runner even at line-tables-only
debuginfo. CARGO_BUILD_JOBS=2 keeps at most two links in flight.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 17:33:36 -07:00
Gitea CI d0e4ce796b chore(web): regenerate wasm artifacts
Build and Deploy / build-and-push (push) Successful in 6m24s
Web E2E / web-e2e (push) Successful in 4m12s
2026-07-08 00:24:36 +00:00
funman300 9f038250d9 feat(engine): You hub — Profile/Stats/Achievements/Replays in one tabbed modal
Test / test (pull_request) Failing after 8m41s
Phase E of docs/ui-redesign-2026-07.md. New you_hub_plugin owns the
modal shell (header, shared tab chips, single Done); each tab's content
is a body builder extracted from its original plugin with every marker
component unchanged, so per-row update/scroll/selector systems keep
working. The replay selector gets its own Replays tab (Watch/Copy
buttons move into the tab body).

- Toggle*RequestEvents + P/S/A accelerators open the hub on the right
  tab, switch tabs in place, or toggle closed on a same-tab request;
  Esc/Done/scrim-click close
- Legacy ProfileScreen/StatsScreen/AchievementsScreen markers ride the
  hub scrim for the active tab — external queries and tests keep their
  meaning
- Standalone toggle/close systems and per-screen Done buttons removed
  (ProfileCloseButton, StatsCloseButton, AchievementsCloseButton)
- Tests: 2 new hub lifecycle tests; profile/stats/achievements modal
  tests adapted (fixtures add YouHubPlugin; selector tests target the
  Replays tab). Engine suite 916 green, clippy -D warnings, fmt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 17:10:56 -07:00
funman300 0c69d6859d refactor(engine): extract shared spawn_tab_chip widget into ui_modal
Settings' tab_chip becomes a thin wrapper; the You hub (Phase E) will
reuse the same widget so tabbed modals stay visually identical.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:52:31 -07:00
funman300 50d6d41d85 feat(engine): tabbed Settings panel — Phase A of the menu redesign
Test / test (pull_request) Failing after 7m15s
Replaces the 37-row single-scroll settings modal with five tabs
(Audio, Gameplay, Appearance, Accessibility, Account); only the active
tab's rows spawn, so every tab fits without scroll-hunting.

- SettingsTab + ActiveSettingsTab (session-only) + SettingsTabButton
  chips under the modal header; rebuild-on-switch mirrors the
  leaderboard despawn/respawn pattern via a shared build_panel helper
- Accessibility tab gathers color-blind, high-contrast, reduce-motion,
  touch-input, and tooltip-delay from the old Gameplay/Cosmetic mix;
  Account = Sync + Privacy
- SettingsButton enum, input handlers, and persistence untouched
- Tests: per-tab Focusable/Tooltip sweeps, tab-switch rebuild test,
  picker/thumbnail tests pinned to the Appearance tab

Plan: docs/ui-redesign-2026-07.md (also added, phases A–M)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:47:06 -07:00
funman300 9e4d4a6716 Merge pull request 'feat(core): Spider rules as a second card_game::Game implementation' (#157) from feat/spider-core into master
Build and Deploy / build-and-push (push) Successful in 6m3s
Web E2E / web-e2e (push) Successful in 5m7s
Web WASM Rebuild / rebuild (push) Successful in 7m10s
Test / test (push) Successful in 9m31s
2026-07-07 23:40:43 +00:00
funman300 ea1014285d feat(engine): Show solution — auto-play the winning line from the pause menu
Test / test (pull_request) Successful in 15m21s
New SolutionPlaybackPlugin: the pause modal's 'Show solution' button
resumes the game and requests a solve of the live position via
GameState::winning_line on AsyncComputeTaskPool (never blocks the main
thread; stale results discarded via a move_count snapshot). The line
then plays back one instruction per 0.45 s through the normal
MoveRequestEvent / DrawRequestEvent pipeline — animations, scoring,
undo history and win detection behave as if the player made the moves.
Tableau run counts are decoded against the live state at step time.

Playback cancels on Esc, pause, undo / new-game requests, and any
rejected move (the signature of the player diverging the board).
Toasts cover search start, line found, unwinnable, and budget-
exhausted outcomes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:33:49 -07:00
Gitea CI 4f849a23b8 chore(web): regenerate wasm artifacts
Build and Deploy / build-and-push (push) Successful in 6m20s
Web E2E / web-e2e (push) Successful in 4m55s
2026-07-07 23:31:55 +00:00
funman300 2c2f0b592a feat(core): Spider rules as a second card_game::Game implementation
Test / test (pull_request) Successful in 7m37s
Two-deck (104-card) Spider on the upstream Stack/Pile containers,
exercising the multi-deck Card encoding for the first time:

- SpiderGame: 10-pile deal (4x6 + 6x5), build-down-any-suit,
  same-suit-run pickup, deal-10 gated on no empty pile, automatic
  K->A run removal, win at 8 runs
- SpiderSuits difficulty (1/2/4 suits over the same 104 cards);
  1- and 2-suit games contain identical Card values by construction
  (documented — engine entity mapping will need positional keys)
- Seeded deals via inline SplitMix64 + Fisher-Yates (core has no
  rand dep; Spider's seed space is deliberately self-contained)
- SpiderGameState session wrapper mirroring GameState conventions:
  Result<_, MoveError> mutations, upstream undo/score bookkeeping,
  Microsoft-style scoring (500 base, -1/move, +100/run, -1/undo)
- 17 unit tests + card-conservation/validity proptest over random
  legal walks; stacked-deal win-path test included

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:20:51 -07:00
funman300 299f6bfea7 feat(core): winning_line — full solver line via Solution::clean_solution
New GameState::winning_line(moves_budget, states_budget) returns the
complete instruction sequence to a win (Ok(None) when unwinnable or
already won, Err on budget exhaustion), compacting the raw DFS trace
with the previously unused card_game Solution::clean_solution and
stripping foundation→foundation no-ops when the stripped line still
replays to a win — an internal replay check guarantees the returned
sequence always applies cleanly via apply_instruction.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-07 16:19:12 -07:00
funman300 8c0eb3dfab Merge pull request 'fix(engine): force full board repaint when the decor-view poller fires (#130)' (#155) from fix/130-forced-repaint into master
Build and Deploy / build-and-push (push) Successful in 2m13s
Test / test (push) Successful in 8m7s
Web WASM Rebuild / rebuild (push) Successful in 7m29s
2026-07-07 23:14:11 +00:00