Commit Graph

1039 Commits

Author SHA1 Message Date
funman300 dbe2addc30 Merge pull request 'fix(engine): resize pile-marker outline and watermark children on relayout' (#131) from fix/pile-marker-child-resize into master
Build and Deploy / build-and-push (push) Successful in 2m19s
Web WASM Rebuild / rebuild (push) Successful in 8m12s
Android Release / build-apk (push) Successful in 5m6s
v0.41.1
2026-07-06 21:28:23 +00:00
funman300 c286593415 fix(engine): resize pile-marker outline and watermark children on relayout
Found during Fold 7 on-device verification of v0.41.0: on_window_resized
resized the marker fill sprite but never its children, so after any
resize (fold/unfold, rotation) the outline frame and the A/K watermark
kept their spawn-time size — rendering as oversized grey slabs over the
empty foundation slots.

The resize handler now re-derives both children from the new layout
(outline = card_size + 2*PILE_MARKER_OUTLINE_WIDTH, watermark font =
card_size.x * 0.28 — same formulas as spawn). Adds a regression test
and an Android-gated relayout log line (width/height/insets) as the
evidence channel for the remaining foldable layout bug (#130).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 14:28:09 -07:00
Gitea CI ddba5c0b26 chore(web): regenerate wasm artifacts
Build and Deploy / build-and-push (push) Successful in 6m52s
Web E2E / web-e2e (push) Successful in 4m41s
2026-07-06 21:14:34 +00:00
funman300 5b37f35eb8 Merge pull request 'refactor(engine): split card_plugin runtime code into submodules' (#129) from refactor/card-plugin-submodules into master
Build and Deploy / build-and-push (push) Successful in 2m25s
Web WASM Rebuild / rebuild (push) Successful in 8m36s
2026-07-06 21:01:24 +00:00
funman300 d1e87765af refactor(engine): split card_plugin runtime code into submodules
Final runtime split for #118: the 2,536-line mod.rs becomes seven
focused submodules along existing system boundaries —

  mod.rs      574  fan-step helpers, CardImageSet, markers, plugin build
  sync.rs     748  asset loading + card entity lifecycle (spawn/update/position)
  layout.rs   351  resize snapping, in-place resize, tableau fan spread
  stock.rs    291  empty-stock recycle hint + count badge
  highlights.rs 276 hint/right-click highlights, cursor hit-testing
  labels.rs   208  desktop text labels + Android corner labels
  anim.rs     200  flip animation, drag shadows

Moved items are pub(super); code moved verbatim apart from relocating
the two stock colour constants next to their consumers. No behaviour
change; all 70 card tests pass unchanged.

Refs #118

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 14:01:10 -07:00
funman300 b3b53c4adf Merge pull request 'refactor(engine): split hud_plugin runtime code into submodules' (#128) from refactor/hud-plugin-submodules into master
Web WASM Rebuild / rebuild (push) Has been cancelled
Build and Deploy / build-and-push (push) Successful in 2m23s
2026-07-06 20:54:17 +00:00
funman300 ba76936aba refactor(engine): split hud_plugin runtime code into submodules
Continues #118 after settings_plugin (PR #127): the 2,725-line mod.rs
becomes five focused submodules along existing system boundaries —

  mod.rs        553  markers, resources, popover enums, plugin build
  spawn.rs      552  band / columns / avatar / action-bar construction
  interaction.rs 616 button handlers, Modes/Menu popovers, tap gesture
  fx.rs         430  action fades, score pulses/floaters, streak flourish
  updates.rs    612  HUD text / typography / visibility updaters

Moved items are pub(super) (fx re-exported pub for HudActionFade and
format_time_limit consumers). Code moved verbatim; no behaviour change;
all 44 hud tests pass unchanged.

Refs #118

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:54:04 -07:00
funman300 59ba7ba4c3 Merge pull request 'refactor(engine): split settings_plugin runtime code into submodules' (#127) from refactor/settings-plugin-submodules into master
Web WASM Rebuild / rebuild (push) Has been cancelled
Build and Deploy / build-and-push (push) Successful in 2m10s
2026-07-06 20:46:10 +00:00
funman300 1ca1efb3b6 refactor(engine): split settings_plugin runtime code into submodules
Second phase of #118 for settings_plugin: the 2,857-line mod.rs becomes
four focused submodules along existing system boundaries —

  mod.rs     561  types, markers, SettingsButton, plugin build, persistence
  input.rs   632  button/hotkey handlers, focus attachment, scrolling
  updates.rs 495  per-frame value-text updater systems + label helpers
  ui.rs    1,231  spawn_settings_panel + row builders + thumbnails

Moved items are pub(super); mod.rs glob-imports the submodules so
system registration and tests keep their bare names. No behaviour
change; all 29 settings tests pass unchanged.

Refs #118

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:45:57 -07:00
Gitea CI c5ad487256 chore(web): regenerate wasm artifacts
Build and Deploy / build-and-push (push) Successful in 6m2s
Web E2E / web-e2e (push) Successful in 5m16s
2026-07-06 20:41:54 +00:00
funman300 8274581edf Merge pull request 'docs(changelog): cut v0.41.0 section' (#126) from docs/changelog-v0.41.0 into master
Android Release / build-apk (push) Successful in 5m50s
v0.41.0
2026-07-06 20:27:43 +00:00
funman300 704e70f60a docs(changelog): cut v0.41.0 section
Renames Unreleased to 0.41.0 (2026-07-06), noting it consolidates the
v0.40.x patch tags that were cut without sectioning. Adds entries for
today's safe-area resume re-poll fix (#116) and the plugin module
test-split phase (#118).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:27:42 -07:00
funman300 b81b314197 Merge pull request 'refactor(engine): move hud/settings/game/input plugin tests into tests.rs files' (#125) from refactor/split-plugin-tests into master
Build and Deploy / build-and-push (push) Successful in 2m19s
Web WASM Rebuild / rebuild (push) Successful in 8m15s
2026-07-06 20:20:10 +00:00
funman300 a2375d2fd9 refactor(engine): move hud/settings/game/input plugin tests into tests.rs files
Continues #118 after card_plugin (PR #124): the four remaining
oversized plugin files become module directories with their trailing
#[cfg(test)] blocks extracted verbatim into sibling tests.rs files,
following the replay_overlay/ pattern.

  hud_plugin:      3,598 -> 2,725 + 872   (44 tests)
  settings_plugin: 3,512 -> 2,857 + 654   (25 tests)
  game_plugin:     2,562 -> 1,310 + 1,251 (39 tests)
  input_plugin:    2,540 -> 1,832 + 707   (31 tests)

Pure mechanical moves via git mv — no runtime code changes; all 139
tests preserved and passing.

Refs #118

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:19:56 -07:00
funman300 3f5e4f4290 Merge pull request 'refactor(engine): move card_plugin tests into card_plugin/tests.rs' (#124) from refactor/card-plugin-split-tests into master
Web WASM Rebuild / rebuild (push) Has been cancelled
Build and Deploy / build-and-push (push) Successful in 2m23s
2026-07-06 20:16:55 +00:00
funman300 0797e9a993 refactor(engine): move card_plugin tests into card_plugin/tests.rs
First increment of the module-split plan: card_plugin.rs becomes
card_plugin/{mod.rs, tests.rs} (2,536 + 1,592 lines), following the
replay_overlay/ pattern. Pure mechanical move via git mv — no runtime
code changes; all 70 tests preserved and passing.

Refs #118

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:16:21 -07:00
funman300 c53b42342b Merge pull request 'chore(scripts): add Gitea deploy watcher' (#123) from chore/commit-watch-deploy into master 2026-07-06 20:10:33 +00:00
funman300 123aa6d099 chore(scripts): add Gitea deploy watcher
Polls the docker-build / web-wasm-rebuild workflow runs and prints a
compact status block until the newest deploy is live. Reads the API
token from ~/.config/tea/config.yml and never prints it.

Closes #119

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:10:24 -07:00
funman300 331d932c4b Merge pull request 'docs(core): record unlimited stock recycling as an intentional rules decision' (#122) from docs/recycle-rule-decision into master
Web WASM Rebuild / rebuild (push) Has been cancelled
Build and Deploy / build-and-push (push) Successful in 6m32s
Web E2E / web-e2e (push) Successful in 5m1s
2026-07-06 20:10:00 +00:00
Gitea CI 716e5f04cf chore(web): regenerate wasm artifacts
Build and Deploy / build-and-push (push) Successful in 6m40s
Web E2E / web-e2e (push) Successful in 6m17s
2026-07-06 20:03:54 +00:00
funman300 ef9d914b99 docs(core): record unlimited stock recycling as an intentional rules decision
Resolves the Draw-1 recycle question from the June 500-game audit:
unlimited recycling with upstream score penalties is deliberate,
matching mainstream digital solitaire rather than strict 3-pass
tournament rules. The difficulty seed catalog and the winnable-deal
solver are verified under this rule, so a hard pass limit must not be
introduced casually.

- ARCHITECTURE.md: new 'Rules decisions' note in the solitaire_core
  section
- GameState::draw() doc comment points at the decision record
- New lock-in test draw_one_recycling_is_unlimited_by_design asserts
  10+ recycles are never rejected

Closes #117

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:03:22 -07:00
funman300 005efa2ee4 Merge pull request 'fix(engine): re-poll safe-area insets after app resume' (#121) from fix/safe-area-resume-repoll into master
Build and Deploy / build-and-push (push) Successful in 3m18s
Web WASM Rebuild / rebuild (push) Successful in 8m43s
2026-07-06 19:52:59 +00:00
funman300 1190ed3ce6 fix(engine): re-poll safe-area insets after app resume
refresh_insets gated its loop on insets.is_populated(), so once insets
resolved at first launch, rearm_on_resumed's poll-counter reset was a
no-op and JNI was never queried again. Insets that changed while the
app was backgrounded (fold/unfold, rotation, gesture/3-button nav
switch) stayed stale until process restart.

Gate the loop on the poll counter alone and settle a cycle by
exhausting it once a populated reading arrives. The cached resource is
rewritten only when the value actually differs, so resumes where
nothing moved trigger no change detection and no relayout — preserving
the no-flash resume behaviour. Also correct the stale on_app_resumed
doc that still described the old inset-zeroing approach.

Closes #116

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 12:50:01 -07:00
Gitea CI 2869e1c34e chore(web): regenerate wasm artifacts
Build and Deploy / build-and-push (push) Successful in 5m50s
Web E2E / web-e2e (push) Successful in 4m55s
2026-06-26 21:25:24 +00:00
funman300 783f01628e Merge pull request 'fix(engine): raise dynamic tableau fan cap to fill tall viewports' (#115) from fix/cover-screen-fan-cap into master
Build and Deploy / build-and-push (push) Successful in 2m8s
Web WASM Rebuild / rebuild (push) Successful in 7m24s
Android Release / build-apk (push) Successful in 5m17s
v0.40.3
2026-06-26 21:15:53 +00:00
funman300 94a6feb5db fix(engine): raise dynamic tableau fan cap to fill tall viewports
MAX_DYNAMIC_FAN_FRAC 0.6 -> 0.9 so the dynamic tableau fill spreads
further on very tall / narrow viewports (e.g. a foldable cover screen),
which were left ~40% empty at the cap. Fills the unfolded near-square
screen to ~100% and lets the cover screen fill further (and the rest fills
as columns deepen during play). Normal phones are unaffected — their fill
fraction is already below the cap. apply_dynamic_tableau_fan still floors
at TABLEAU_FAN_FRAC and deeper columns drive the fraction down, so nothing
overflows and hit-testing stays in sync.

Vertical centring of the residual was investigated but dropped: a 21:9
phone is aspect-identical to the cover screen, so centring can't be
targeted to foldables without also disconnecting the board from the HUD on
tall phones.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 13:56:31 -07:00
Gitea CI 3daaf47689 chore(web): regenerate wasm artifacts
Build and Deploy / build-and-push (push) Successful in 6m3s
Web E2E / web-e2e (push) Successful in 4m47s
2026-06-26 20:20:59 +00:00
funman300 c00656ec8f Merge pull request 'docs(changelog): note foldable tableau fill and card-move jank fixes' (#114) from docs/changelog-v0.40.2 into master
Android Release / build-apk (push) Successful in 5m3s
v0.40.2
2026-06-26 20:12:47 +00:00
funman300 aea2167eee docs(changelog): note foldable tableau fill and card-move jank fixes
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 13:12:45 -07:00
funman300 0ed91ea24c Merge pull request 'fix(engine): fill tableau fan to viewport on all aspect ratios' (#113) from fix/foldable-tableau-fan-fill into master
Build and Deploy / build-and-push (push) Successful in 2m3s
Web WASM Rebuild / rebuild (push) Successful in 7m27s
2026-06-26 20:11:33 +00:00
funman300 18af49c0f3 fix(engine): fill tableau fan to viewport on all aspect ratios
On a near-square viewport (e.g. an unfolded Galaxy Fold) a fresh deal left
the bottom ~40% of the screen empty: the dynamic fan (update_tableau_fan_frac)
measured only face-up column depth and returned early at a fresh deal (face-up
depth 1), so it never spread the tableau, and the deep face-down stacks were
ignored. The cold-start deal also never fired StateChangedEvent, and on Android
the safe-area-inset resize (frames 1-3) reset the fan to compute_layout's
sparse worst-case value, so even the post-move fill was wiped.

Move the fill into layout::apply_dynamic_tableau_fan, driven by each column's
TOTAL weighted depth (face-down cards count, scaled by the face-down/face-up
step ratio) so the deepest column fills the available height. Run it in three
places so every path stays filled: PostStartup (cold-start deal), on
StateChangedEvent (moves), and inside on_window_resized after compute_layout
(safe-area resize + fold/unfold). MAX_DYNAMIC_FAN_FRAC caps the spread so a
near-empty column keeps readable overlap; TABLEAU_FAN_FRAC floors it. Deeper
columns drive the fraction down so everything still fits — no overflow.
card_position/card_positions read the same fractions, so hit-testing stays
in sync.

Adds regression tests: cold-start deal fills the fan, and a resize re-fills it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 13:11:00 -07:00
Gitea CI e208245036 chore(web): regenerate wasm artifacts
Build and Deploy / build-and-push (push) Successful in 6m48s
Web E2E / web-e2e (push) Successful in 4m47s
2026-06-26 19:11:53 +00:00
funman300 0247efcb07 Merge pull request 'perf(engine): rebuild card children only on appearance change' (#112) from fix/card-move-anim-jank into master
Build and Deploy / build-and-push (push) Successful in 2m1s
Web WASM Rebuild / rebuild (push) Successful in 6m40s
2026-06-26 19:03:19 +00:00
funman300 1d5266a811 perf(engine): rebuild card children only on appearance change
Every move fired StateChangedEvent -> sync_cards, which rebuilt the full
visual for all 52 cards: despawning and respawning every child entity
(drop-shadow, border frame, and on Android a Text2d corner label needing
a glyph re-layout) even for the ~50 cards that did not move. That ~250
entity despawn/spawns plus 52 text re-layouts in a single frame spiked
the StateChangedEvent frame and stuttered the slide animation on
high-resolution devices (reported on a Galaxy Fold 7).

Add a CardChildrenKey component capturing the only inputs the child
entities depend on (face_up, card_size, color_blind, high_contrast).
update_card_entity now rebuilds children only when that key changes (a
flip, resize/fold, or accessibility toggle); a position-only move just
updates the Transform. The Sprite is still refreshed every sync (a cheap
handle swap), so theme/card-back image changes need no child rebuild.

Adds a regression test asserting an appearance-neutral StateChangedEvent
no longer despawns/respawns card label children.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 12:02:42 -07:00
funman300 26283b5478 Merge pull request 'fix(android): sign release APK v2+v3 only (drop invalid v1 JAR signature)' (#111) from fix/apk-signing-v1-obtainium into master
Android Release / build-apk (push) Successful in 5m15s
v0.40.1
2026-06-25 18:16:57 +00:00
funman300 3627e9f9cf fix(android): sign release APK v2+v3 only (drop invalid v1 JAR signature)
The apksigner step relied on auto scheme selection, which produced an APK
carrying invalid v1 (JAR) signature files: META-INF/*.SF and *.RSA were
present but failed v1 verification (apksigner reports `v1 scheme: false`
while v2/v3 verify). Android installs such an APK fine via v2/v3, but
Obtainium parses the legacy v1 certificate at install time, gets an empty
cert list, and crashes with:

  RangeError (length): Invalid value: valid value range is empty: 0

This is why the app adds fine in Obtainium (Gitea API only) but fails on
install (APK parse). minSdk is 26, so v1/JAR signing is unnecessary —
sign explicit v2+v3 only (matching modern Android tooling for minSdk >= 24)
and pass --min-sdk-version 26. Adds a post-sign guard that fails the build
if any META-INF v1 signature files remain.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 11:16:38 -07:00
funman300 b81a79c51c Merge pull request 'docs(handoff): mark physical-device smoke test as the only v0.40.0 item left' (#110) from docs/handoff-only-smoke-test into master 2026-06-25 17:48:31 +00:00
funman300 968721eeb4 docs(handoff): mark physical-device smoke test as the only v0.40.0 item left
Elevates the physical-device smoke test as the single remaining task for the
v0.40.0 release and clarifies the Matomo validation is an independent task,
not a release blocker.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 10:48:16 -07:00
funman300 780e82ca4b Merge pull request 'docs(handoff): record v0.40.0 release' (#109) from docs/handoff-v0.40.0 into master 2026-06-25 17:46:06 +00:00
funman300 207747db4b docs(handoff): record v0.40.0 release
Updates SESSION_HANDOFF.md with the v0.40.0 Android release (PRs #105/#106/#108),
the pre-release validation performed, and the still-open physical-device smoke test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 10:45:49 -07:00
funman300 c66baceb10 Merge pull request 'docs(android): update NDK reference to 30.0.14904198' (#108) from docs/android-ndk-version into master
Android Release / build-apk (push) Successful in 5m39s
v0.40.0
2026-06-25 17:37:03 +00:00
funman300 329f224ffd docs(android): update NDK reference to 30.0.14904198
The setup doc pinned NDK 26.3.11579264, but newer NDKs build fine
(verified locally on 30.0.14904198 / build-tools 37.0.0: cross-compile,
android-target clippy, and a full signed arm64-v8a APK). Note that the
exact versions are not load-bearing and build_android_apk.sh
auto-discovers the newest installed NDK/build-tools.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 10:36:48 -07:00
Gitea CI 2fc190ee42 chore(web): regenerate wasm artifacts
Build and Deploy / build-and-push (push) Successful in 6m22s
Web E2E / web-e2e (push) Successful in 4m39s
2026-06-25 17:20:48 +00:00
funman300 060efaee7b Merge pull request 'docs(changelog): note Draw-Three waste fan hit-test fix' (#107) from docs/changelog-waste-fan into master 2026-06-25 17:13:51 +00:00
funman300 ef599ffa17 docs(changelog): note Draw-Three waste fan hit-test fix
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 10:13:39 -07:00
funman300 22334e0dd5 Merge pull request 'fix(engine): share Draw-Three waste fan step between renderer and hit-test' (#106) from fix/draw-three-waste-fan-hittest into master
Build and Deploy / build-and-push (push) Successful in 1m51s
Web WASM Rebuild / rebuild (push) Successful in 7m36s
2026-06-25 17:11:26 +00:00
funman300 942b9c2161 fix(engine): share Draw-Three waste fan step between renderer and hit-test
The waste fan x-offset was computed two ways: the renderer used
tableau_col_step * 0.224 while the hit-test hard-coded card_size.x * 0.28.
These coincide on desktop (col_step = 1.25*cw) but drift on Android, where
tighter column spacing (H_GAP_DIVISOR=32, col_step ~= 1.03*cw) makes the
renderer fan at ~0.231*cw. The top fanned waste card's sprite then sits
~14px left of its click target, so dragging the visible top card grabs
the card beneath it.

Extract waste_fan_step() and tableau_col_step() as the single source for
both the renderer (card_plugin::card_positions) and the hit-test
(input_plugin::card_position) so they can no longer diverge. Add a
regression test that simulates Android-tight spacing and asserts the hit
target tracks the renderer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 10:10:57 -07:00
funman300 fde863a4e4 Merge pull request 'test(engine): regression tests for waste-card draggability' (#105) from test/waste-draggable-regression into master
Build and Deploy / build-and-push (push) Successful in 2m6s
Web WASM Rebuild / rebuild (push) Successful in 6m42s
2026-06-25 16:58:15 +00:00
funman300 0cf5fc4293 test(engine): regression tests for waste-card draggability
Adds two find_draggable_at tests covering the reported stock/waste
drag bug: clicking the visible top of a multi-card waste must pick the
top index (not the buffer card beneath), and a lone waste card must
still be draggable. Both pass against current logic, pinning the
correct behaviour.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 09:56:56 -07:00
Gitea CI 79ddfbc034 chore(web): regenerate wasm artifacts
Build and Deploy / build-and-push (push) Successful in 6m3s
Web E2E / web-e2e (push) Successful in 4m1s
2026-06-24 17:40:04 +00:00