Commit Graph

1069 Commits

Author SHA1 Message Date
funman300 fc4ea17789 Merge pull request 'polish(engine): standard empty/loading/error states (Phase L)' (#184) from feat/empty-states into master
Test / fmt (push) Successful in 4s
Test / test (push) Successful in 4m51s
Build and Deploy / build-and-push (push) Failing after 8m20s
Web E2E / web-e2e (push) Successful in 9m18s
2026-07-14 03:08:55 +00:00
funman300 02bcc8b4af polish(engine): standard empty/loading/error states (Phase L)
Test / fmt (pull_request) Successful in 5s
Test / test (pull_request) Successful in 4m20s
New ui_modal::spawn_empty_state — glyph + headline + optional detail,
centred, with next-step actions composed alongside rather than
configured in. Glyphs restricted to FiraMono-covered ranges (suits,
arrows, ASCII) per the section-10/11 Android constraint.

Swept every improvised surface onto it:
- Theme store: loading / error / empty-catalog branches
- Leaderboard: fetching / error / be-the-first branches
- You-hub Replays tab: dedicated no-replays state (selector and
  actions no longer spawn dead controls when history is empty)
- Stats: first-launch nudge above the em-dash grid

The Account tab's sync row was deliberately NOT swept: it is a live
status + action row, not an empty state, and its deeper treatment
belongs to Phase M's sync-transparency work.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 20:03:09 -07:00
funman300 d4d0bde0c0 docs(changelog): cut 0.47.0 — What's-new card, tips, UI scale, cheat sheet
Android Release / build-apk (push) Successful in 5m10s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
v0.47.0
2026-07-13 19:04:24 -07:00
funman300 cbbab3d09f Merge pull request 'feat(engine): hold-/ hotkey cheat sheet + unified binding table (Phase J)' (#183) from feat/keyboard-completeness into master
Test / fmt (push) Successful in 4s
Test / test (push) Successful in 4m53s
Build and Deploy / build-and-push (push) Failing after 7m49s
Web E2E / web-e2e (push) Successful in 8m48s
2026-07-14 02:04:06 +00:00
funman300 36605751cd feat(engine): hold-/ hotkey cheat sheet + unified binding table (Phase J)
Test / fmt (pull_request) Successful in 4s
Test / test (pull_request) Successful in 4m33s
Phase J audit findings: the visible focus ring the doc asked for
already exists (FocusOverlay singleton, 2px accent ring, breathing
pulse, reduce-motion aware — landed after the doc was written), and
post-Phase-C every modal already opens and dismisses keyboard-only.
The real gaps were two silently diverging static hotkey tables
(onboarding's slide vs Help's list — onboarding still said 'Mode
Launcher (then 1-5)') and no way to see the bindings mid-game.

- New crate::hotkeys module owns THE binding table (21 rows, verified
  against a grep inventory of every just_pressed(KeyCode::..) handler);
  rows carry an `essential` flag — the onboarding slide teaches that
  subset, the cheat sheet shows everything.
- New cheat_sheet_plugin: hold `/` for a right-anchored reference
  overlay of every binding; release hides it. Deliberately not a
  spawn_modal modal (momentary reference, closer to a tooltip), never
  spawns while a modal owns the screen (also keeps it out of the
  seed-entry field), inert on touch builds.
- Onboarding's stale local table deleted in favour of the shared one
  (copy updated: Home naming, hold-to-repeat undo, added H).

5 new tests (table integrity, essential-subset size, uniqueness,
show/hide driver, modal suppression). Workspace + clippy green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 18:58:23 -07:00
funman300 4d9a07727a Merge pull request 'feat(engine): UI scale setting — 90/100/115/130% (Phase K)' (#182) from feat/ui-scale into master
Test / fmt (push) Successful in 5s
Test / test (push) Successful in 4m46s
Build and Deploy / build-and-push (push) Failing after 6m38s
Web E2E / web-e2e (push) Successful in 8m23s
2026-07-14 01:45:01 +00:00
funman300 513dee8fa9 Merge pull request 'feat(engine): contextual one-time tips — stall hint + radial teach (Phase I complete)' (#181) from feat/contextual-tips into master
Test / fmt (push) Successful in 4s
Test / test (push) Successful in 4m32s
Build and Deploy / build-and-push (push) Failing after 6m58s
Web E2E / web-e2e (push) Successful in 8m49s
2026-07-14 01:43:51 +00:00
funman300 19ddf86c7b Merge pull request 'feat(engine): one-shot What's-new card after updates (Phase I)' (#180) from feat/whats-new-card into master
Test / fmt (push) Successful in 5s
Test / test (push) Successful in 4m23s
Build and Deploy / build-and-push (push) Failing after 6m36s
Web E2E / web-e2e (push) Successful in 8m43s
2026-07-14 01:43:47 +00:00
funman300 28be65f092 feat(engine): UI scale setting — 90/100/115/130% (Phase K)
Test / fmt (pull_request) Successful in 4s
Test / test (pull_request) Successful in 4m22s
Settings -> Accessibility gains a UI Scale row cycling four steps,
applied live through bevy::ui::UiScale: every menu, modal, and HUD
element scales while the table itself stays window-fit via
compute_layout. New Settings::ui_scale (serde default 1.0, sanitized
clamp to [0.9, 1.3]).

Safe-area anchors and modal scrim padding pre-divide physical insets
by the UI scale so post-multiplication lands exactly on the system
bars — without this, 90% would sink the bottom action bar into the
Android gesture zone. The anchor systems also re-run on UiScale
changes, not just inset changes.

Deferred from Phase K (noted in the doc): seeding the setting from the
Android system font scale on first run (needs JNI), and the 44px
touch-target audit (on-device).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 18:39:19 -07:00
funman300 65913de2cb feat(engine): contextual one-time tips — stall hint, radial menu teach (Phase I)
Test / fmt (pull_request) Successful in 4s
Test / test (pull_request) Successful in 4m15s
Two situation-fired teaches, each shown exactly once and recorded in
Settings like shown_achievement_onboarding:

- Stall tip: 45s with no board change in an active, started game
  (clock frozen while paused / a modal is open) points at Hint with
  platform-correct copy (H on desktop, bottom-bar Hint on touch).
- Radial teach: a player 15 moves into a game who has never opened the
  radial menu learns the long-press / right-click gesture. Organic
  radial use marks the tip done silently — nobody is taught what they
  already know.

Tips ride the queued InfoToastEvent path, so they render in the unified
toast stack and never interrupt play. This completes Phase I.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 18:16:56 -07:00
funman300 25f1fd27d9 feat(engine): one-shot What's-new card after updates (Phase I)
Test / fmt (pull_request) Successful in 5s
Test / test (pull_request) Successful in 5m0s
ObtainX updates install silently, so shipped features went unnoticed.
On the first launch where the running release differs from the new
Settings::last_seen_whats_new, a dismissible card summarises the
latest CHANGELOG.md section (embedded; its top version doubles as the
app's release identity — no build-time version plumbing). Internal
sections are dropped and bullets reduce to their bold lead sentence.

Launch beat: splash -> onboarding (first run) -> what's-new -> Home;
spawn_home_on_launch waits on the new WhatsNewPending resource. Fresh
installs never see the card — onboarding completion stamps the current
version silently. The seen-stamp persists on spawn, not dismissal, so
the card can never nag twice.

8 new tests (changelog parsing incl. the real embedded file, upgrade/
seen/fresh-install gating, dismissal). Workspace + clippy green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 18:05:15 -07:00
funman300 9a0d6496c5 docs(changelog): cut 0.46.0 — Phase H feedback polish
Android Release / build-apk (push) Successful in 5m36s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
v0.46.0
2026-07-13 17:50:29 -07:00
funman300 7669a1bb56 Merge pull request 'feat(engine): theme-store preview thumbnails (Phase H part 2)' (#179) from feat/theme-store-previews into master
Test / fmt (push) Successful in 4s
Test / test (push) Successful in 4m41s
Build and Deploy / build-and-push (push) Successful in 8m56s
Web E2E / web-e2e (push) Successful in 7m33s
2026-07-14 00:46:46 +00:00
funman300 ddee605874 feat(engine): hint ghost-motion preview (Phase H)
Test / fmt (pull_request) Successful in 4s
Test / test (pull_request) Successful in 4m20s
A hint now spawns a translucent copy of the hinted card that glides to
the suggested destination twice (0.7s per pass, SmoothSnap easing, tail
fade so the loop reads as a repeat), alongside the existing static
source/destination highlights. Auto-disabled under reduce-motion — the
static highlights remain the whole story. The ghost despawns on timer,
on a fresh hint, or the moment the board changes (a preview of a stale
board is worse than none).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 17:40:53 -07:00
funman300 ae8118d7df feat(engine): theme-store preview thumbnails (Phase H part 2)
Test / fmt (pull_request) Successful in 4s
Test / test (pull_request) Successful in 4m36s
The live catalog has advertised preview_url per theme since the store
shipped, but the client never fetched it — rows were text-only. The
modal now downloads each advertised preview PNG off the main thread
(same AsyncComputeTaskPool + Tokio pattern as the catalog fetch and
avatar image), decodes it into an Image asset, and renders a thumbnail
sized by the theme's own card_aspect at the head of the row. Previews
pop in as they arrive and are cached for the session, so reopening the
store is instant. Failures log and leave the row text-only; the decode
path is skipped entirely under MinimalPlugins (no Assets<Image>).

solitaire_data grows ThemeStoreClient::fetch_preview with a 512 KB cap
(previews carry no checksum — decorative only).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 17:26:47 -07:00
funman300 739c94d3bb Merge pull request 'polish(engine): Phase H part 1 — unified toast stack, onboarding fixes' (#178) from feat/feedback-polish into master
Test / fmt (push) Successful in 4s
Test / test (push) Successful in 4m24s
Build and Deploy / build-and-push (push) Successful in 9m8s
Web E2E / web-e2e (push) Successful in 8m15s
2026-07-14 00:14:03 +00:00
funman300 3388169329 fix(engine): platform-correct onboarding copy; Home waits for first-run onboarding (Phase H)
Test / fmt (pull_request) Successful in 4s
Test / test (pull_request) Successful in 4m29s
- The how-to-play slide told touch players to left/right-click; Android
  now gets tap/double-tap copy pointing at the bottom-bar Hint button.
- On a fresh profile the Home auto-show spawned underneath the
  onboarding modal, stacking two scrims (v0.44.0 emulator smoke
  finding). spawn_home_on_launch now waits until first_run_complete
  and the onboarding modal is gone, so the launch beat is onboarding,
  then Home, then the table.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 16:52:16 -07:00
funman300 0e07e1d1ad refactor(engine): unify toasts into one bottom-anchored stack (Phase H)
Queued and immediate toasts used two subtly different geometries and
staggered anchors purely to dodge each other when simultaneous. Both
paths now spawn geometry-free ToastNodes that adopt_toasts_into_stack
slots into a single persistent ToastStackRoot flex column — one anchor,
one style, simultaneous toasts stack upward. The root rides
SafeAreaAnchoredBottom and clears the Phase F touch action bar.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 16:48:46 -07:00
funman300 83f37bada6 docs(changelog): cut 0.45.0 — Phase F touch action bar
Android Release / build-apk (push) Successful in 6m6s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
v0.45.0
2026-07-13 16:22:33 -07:00
funman300 d989d07fe7 Merge pull request 'feat(engine): Phase F touch action bar — thumb-reach Undo/Draw/Hint + hold-to-repeat undo' (#177) from feat/bottom-action-bar into master
Test / fmt (push) Successful in 4s
Test / test (push) Successful in 4m24s
Build and Deploy / build-and-push (push) Successful in 8m53s
Web E2E / web-e2e (push) Successful in 8m3s
2026-07-13 23:22:15 +00:00
funman300 fa54c58bc4 feat(engine): Phase F touch action bar — thumb-reach Undo/Draw/Hint + hold-to-repeat undo
Test / fmt (pull_request) Successful in 5s
Test / test (pull_request) Successful in 4m25s
Touch layout (USE_TOUCH_UI_LAYOUT) restructures the bottom action bar
to five buttons: an enlarged Undo / Draw / Hint trio (96x64px targets,
1.35x labels) between compact Menu and Pause. Draw is new — it fires
the same DrawRequestEvent as tapping the stock, so the most frequent
action no longer needs a reach to the top of a tall folded screen.
Help, Modes, and New Game leave the touch bar (they live in Menu ->
System, the Home grid, and Home's hero respectively). Desktop keeps
the seven-button bar unchanged (decision 5: touch-only).

Holding Undo now steps back repeatedly after a 0.45s delay (5.5/s),
each step through the normal request path so the scoring penalty
applies. New self-ambiguous DrawRequestWriters set keeps the ambiguity
gate at zero with the fourth DrawRequestEvent writer.

6 new hud_plugin tests; workspace suite + clippy green.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 16:07:10 -07:00
funman300 dd304913df docs(ui): record Phase F decision 5 — bottom action bar is touch-only
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 15:49:15 -07:00
funman300 48ad6b6618 docs(changelog): cut 0.44.0 — Phase B home hierarchy
Android Release / build-apk (push) Successful in 7m48s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
v0.44.0
2026-07-13 15:16:27 -07:00
funman300 81ac4a5383 Merge pull request 'ci(test): host-persistent target dir; fast fmt gate; drop dead rust-cache' (#176) from ci/host-persistent-target into master
Test / fmt (push) Successful in 4s
Test / test (push) Successful in 4m27s
2026-07-13 22:14:39 +00:00
funman300 f51ee7b234 Merge pull request 'feat(engine): Phase B home hierarchy — Continue card, hero New Game, deal-options disclosure' (#175) from feat/home-hierarchy into master
Build and Deploy / build-and-push (push) Successful in 9m11s
Web E2E / web-e2e (push) Successful in 8m30s
Test / test (push) Successful in 37m46s
2026-07-13 20:55:37 +00:00
funman300 d593b61af8 ci(test): host-persistent target dir; fast fmt gate; drop dead rust-cache
Test / fmt (pull_request) Successful in 5s
Test / test (pull_request) Successful in 33m27s
The Gitea actions cache on this instance stores caches (1.85 GB saves
confirmed in run 597) but never restores them — every run back through
run 584+ logs 'No cache found' even for exact keys saved an hour
earlier, including master-to-master. Every CI run has therefore been a
full cold build (~37 min), plus ~4 min tarring a cache nobody reads.

- Point CARGO_TARGET_DIR at a persistent path on the rust-host runner
  (host executor — filesystem carries over between runs), with a 40 GiB
  prune guard. Warm runs drop to minutes without touching the broken
  cache API.
- Drop Swatinem/rust-cache (pure overhead until the server is fixed).
- Split cargo fmt --check into a seconds-long fmt job gating the heavy
  test job, so a formatting slip can't burn a 35-minute build again
  (run 600).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 13:53:34 -07:00
funman300 fc87b13e5b style: cargo fmt
Test / test (pull_request) Successful in 37m46s
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 13:11:54 -07:00
funman300 560470b86b feat(engine): Phase B home hierarchy — Continue card, hero New Game, deal-options disclosure
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>
2026-07-13 13:08:07 -07:00
funman300 1fded5ff16 docs(ui): record Phase B decisions 2-4 (deal-options disclosure, top-level Time Attack/Seed cards, in-engine iteration)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-13 12:40:17 -07:00
funman300 65585c61ad Merge pull request 'test(e2e): read v4 recording.instructions in cycle regression gate' (#174) from fix/cycle-metrics-v4-recording into master
Build and Deploy / build-and-push (push) Successful in 2m42s
Web E2E / web-e2e (push) Successful in 9m33s
2026-07-10 21:56:13 +00:00
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