docs: cut v0.21.0 — visual-identity completion + palette refresh
Promotes the [Unreleased] section to [0.21.0] dated 2026-05-08
and opens a fresh empty [Unreleased]. The cycle's three through-
lines:
- **Card-face / suit / card-back artwork migration.** Closes
the v0.20.0 thread that explicitly deferred card-face palette
migration. 10 commits across 2 days landed both rendering
paths (assets/cards/*.png fallback + the bundled-default
theme SVGs that include_bytes!()-embed into the binary) on
identical Terminal art generated by shared face_svg /
back_svg builders. The card_face_svg_pin integration test
guards rasteriser drift via FNV-1a on raw RGBA bytes.
- **Splash + replay-overlay polish.** Closes Resume-prompt
Options B (splash cursor pulse + scanline overlay) and C
(replay banner ▌ label + GAME caption + MOVE chip + scrub
bar). Splash gets the SplashFadable scaffold that lets
future overlays fade N >> 3 elements via one marker + one
global lerp query.
- **ACCENT_PRIMARY palette swap.** Late-cycle stakeholder
decision: cyan #6fc2ef → brick red #a54242. Touches every
primary-accent surface across the engine. RED_SUIT_COLOUR_CBM
swapped from cyan to lime #acc267 in lockstep so the colour-
blind alternative stays hue-distinct from the new red-family
primary.
Three sign-off follow-ups surfaced once a human booted the
running game; all matched the same shape ("fallback path the
chrome migration walked past"): the embedded default theme
overrode the new PNGs, the table backgrounds were a separate
PNG path the v0.20.0 chrome migration didn't touch, and the
action-button row's font_size: 16.0 literal slipped through the
typography migration audit. All recorded under "Fixed".
Phase 8 (sync) and Phase Android runtime gaps (JNI bridges,
APK launch verification on device) remain open and roll
forward.
cargo clippy --workspace --all-targets -- -D warnings clean.
1184 passing / 0 failing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+264
-1
@@ -6,9 +6,272 @@ project follows [Semantic Versioning](https://semver.org/).
|
|||||||
|
|
||||||
## [Unreleased]
|
## [Unreleased]
|
||||||
|
|
||||||
No threads in flight. v0.20.0 cut on 2026-05-07; CHANGELOG accumulates
|
No threads in flight. v0.21.0 cut on 2026-05-08; CHANGELOG accumulates
|
||||||
the next cycle here.
|
the next cycle here.
|
||||||
|
|
||||||
|
## [0.21.0] — 2026-05-08
|
||||||
|
|
||||||
|
Closes the visual-identity arc opened in v0.20.0. Three through-lines
|
||||||
|
landed: the **card-face / suit / card-back artwork migration** that
|
||||||
|
v0.20.0 deliberately deferred, the **splash boot-screen + replay-
|
||||||
|
overlay polish** that closes Resume-prompt Options B and C, and a
|
||||||
|
late-cycle **`ACCENT_PRIMARY` palette swap** from cyan `#6fc2ef` to
|
||||||
|
brick red `#a54242` after a quick stakeholder review on the
|
||||||
|
shipped art.
|
||||||
|
|
||||||
|
The card-face arc is the largest piece by commit count (10 of the
|
||||||
|
25 post-tag commits) and shape: it ports both rendering paths
|
||||||
|
production traverses — the PNG fallback at `assets/cards/*.png`
|
||||||
|
and the bundled-default theme SVGs at
|
||||||
|
`solitaire_engine/assets/themes/default/*.svg` that
|
||||||
|
`include_bytes!()`-embed into the binary and override the PNGs at
|
||||||
|
runtime — to identical Terminal-aesthetic art generated by the
|
||||||
|
same `face_svg` / `back_svg` builders. A new
|
||||||
|
`card_face_svg_pin` integration test pins rasteriser output via
|
||||||
|
FNV-1a on raw RGBA bytes, so future `usvg`/`resvg` upgrades or
|
||||||
|
intentional builder edits surface as test failures rather than
|
||||||
|
silent visual drift. The pin test fired three times during the
|
||||||
|
arc (text→path glyph fix, glyph orientation tweak, palette swap)
|
||||||
|
and rebaselined cleanly each time via the empty-then-paste
|
||||||
|
bootstrap pattern baked into the test.
|
||||||
|
|
||||||
|
Three sign-off follow-ups surfaced once a human booted the
|
||||||
|
running game and they all matched the same shape — "fallback
|
||||||
|
path the chrome migration walked past": the embedded default
|
||||||
|
theme overrode the new PNGs at runtime, the table backgrounds
|
||||||
|
were a separate PNG path that the v0.20.0 chrome migration
|
||||||
|
didn't touch, and the action-button row's `font_size: 16.0`
|
||||||
|
literal slipped through the typography migration audit. All
|
||||||
|
three are recorded under "Fixed" below.
|
||||||
|
|
||||||
|
Phase 8 (sync) and the Phase Android runtime gaps (JNI bridges,
|
||||||
|
APK launch verification on device) remain open and roll forward.
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
- **Card-face SVG generator pipeline** (`5623368` plan doc,
|
||||||
|
`3a4bb63` PoC, `babe5cc` full generator, `48b28d2` pin test).
|
||||||
|
`solitaire_engine/examples/card_face_generator.rs` writes 52
|
||||||
|
face PNGs + 5 back PNGs into `assets/cards/` and 53 theme SVGs
|
||||||
|
into `solitaire_engine/assets/themes/default/`, all from the
|
||||||
|
shared `face_svg` / `back_svg` builders in the new
|
||||||
|
`solitaire_engine::assets::card_face_svg` module. Run with
|
||||||
|
`cargo run --example card_face_generator --release`. The PoC
|
||||||
|
(`card_face_poc.rs`) stays alongside as historical record of
|
||||||
|
the per-card grain proof. Pin test `card_face_svg_pin`
|
||||||
|
guards rasterised output via inline FNV-1a so the arc has
|
||||||
|
test-time coverage of both intentional builder edits (rebase
|
||||||
|
via empty-then-paste) and unintentional dependency-upgrade
|
||||||
|
drift.
|
||||||
|
- **Background generator example** (in `8719f77`).
|
||||||
|
`solitaire_engine/examples/background_generator.rs` emits 5
|
||||||
|
flat Terminal-palette play-surface PNGs at 120 × 168, the
|
||||||
|
same tile size the legacy felt textures used (the runtime
|
||||||
|
stretches to `window_size * 2.0` so source resolution is
|
||||||
|
immaterial). All 5 slots stay in the near-black family —
|
||||||
|
`#151515` canonical, `#0a0a0a` deeper, `#1a1a1a` elevated,
|
||||||
|
`#121820` cool tint, `#201812` warm tint.
|
||||||
|
- **Splash boot-screen port** (`cacb19c`). Full mockup-spec
|
||||||
|
splash: header, boot log, progress bar, palette swatches,
|
||||||
|
version footer, plus the `SplashFadable` scaffold that lets
|
||||||
|
any future overlay fade `N >> 3` elements via one marker +
|
||||||
|
one global lerp query (replaces the `Without<X>, Without<Y>`
|
||||||
|
exclusion pattern that the legacy splash hit at three
|
||||||
|
siblings).
|
||||||
|
- **Splash trailing cursor pulse** (`29136d8`). Trailing
|
||||||
|
6×12 px Node, sine-pulsed, multiplied with the global splash
|
||||||
|
fade — the "multiply, don't override" pattern that resolves
|
||||||
|
the original `cacb19c` skip-rationale. Closes Option B half 1
|
||||||
|
from the SESSION_HANDOFF Resume prompt.
|
||||||
|
- **Splash tiled scanline overlay** (`a27cf5a`). Runtime-
|
||||||
|
generated 2×2 RGBA8 texture tiled via `NodeImageMode::Tiled`;
|
||||||
|
per-pixel alpha × tint alpha gives multiplicative fade
|
||||||
|
integration without new abstractions. Closes Option B
|
||||||
|
half 2.
|
||||||
|
- **Replay overlay scrub bar** (`c84d9f4`). 1px accent fill at
|
||||||
|
the bottom of the banner, mirroring `cursor / total`. Per-
|
||||||
|
frame updater + scrub-pct unit tests.
|
||||||
|
- **Replay overlay banner label port** (`6204db8`). The
|
||||||
|
"▌ replay" headline picks up the cursor-block treatment that
|
||||||
|
aligns it with the splash boot-screen idiom.
|
||||||
|
- **Replay overlay GAME caption** (`54005d5`). `GAME #YYYY-DDD`
|
||||||
|
game-identifier caption beneath the headline. Mirrors the
|
||||||
|
mockup's right-anchored ID but stays grouped with the headline
|
||||||
|
so the two pieces of "this is a replay of game X" read as one
|
||||||
|
unit.
|
||||||
|
- **Replay overlay MOVE chip** (`e080b49`). `MOVE N/M` progress
|
||||||
|
readout wrapped in a 1px accent-bordered chip — discrete
|
||||||
|
callout rather than free-floating text. Closes Option C from
|
||||||
|
the SESSION_HANDOFF Resume prompt (paired with `54005d5`).
|
||||||
|
- **Terminal desktop-adaptation spec** (`39b8496`).
|
||||||
|
`docs/ui-mockups/desktop-adaptation.md` — the rules-based
|
||||||
|
companion to the 24-mockup library. Closes the spec gap
|
||||||
|
exposed when 23 of 24 mockups turned out to be mobile-only;
|
||||||
|
any future plugin port should read this first and apply the
|
||||||
|
universal rules before consulting the per-screen table.
|
||||||
|
- **`solitaire_engine::assets::card_face_svg` module**
|
||||||
|
(`48b28d2`). Public SVG builders (`face_svg`, `back_svg`,
|
||||||
|
`suit_path_d`) extracted from the example so the pin test
|
||||||
|
could call them — examples can't be referenced from
|
||||||
|
`tests/`. The generator and the test now share the same
|
||||||
|
source-of-truth, so the pin guards both rendering paths
|
||||||
|
the engine consults.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
- **`ACCENT_PRIMARY` swapped from cyan `#6fc2ef` to brick red
|
||||||
|
`#a54242`** (`a292a7e`). Project-wide palette decision after
|
||||||
|
initial rollout. Affects every cyan-accented surface — splash
|
||||||
|
boot screen, home menu glyphs, action chevrons, replay
|
||||||
|
overlay banner + scrub fill + chip border, achievement
|
||||||
|
checkmarks, leaderboard #1 indicator, radial menu fill, focus
|
||||||
|
ring, card-back canonical badge. `RED_SUIT_COLOUR_CBM`
|
||||||
|
swapped in lockstep from cyan to lime `#acc267` so the
|
||||||
|
colour-blind alternative stays hue-distinct from the new
|
||||||
|
red-family primary. Comment doc strings throughout the
|
||||||
|
engine retuned from "cyan" to "accent" / "primary-accent" so
|
||||||
|
future palette changes don't require comment churn. Spec doc
|
||||||
|
`design-system.md` updated in lockstep with historical
|
||||||
|
references preserved as audit trail.
|
||||||
|
- **Card-face / suit / card-back constants migrated to Terminal
|
||||||
|
palette in lockstep with new artwork** (`e8bf9d7`). Five
|
||||||
|
constants flipped: `CARD_FACE_COLOUR` → `#1a1a1a` (was
|
||||||
|
off-white `#fafaf2`), `RED_SUIT_COLOUR` → `#fb9fb1` (was deep
|
||||||
|
red `#c71f26`), `BLACK_SUIT_COLOUR` → `#d0d0d0` (was near-
|
||||||
|
black `#141414`), `CARD_FACE_COLOUR_RED_CBM` renamed to
|
||||||
|
`RED_SUIT_COLOUR_CBM` and repurposed from a face-background
|
||||||
|
tint to a suit-glyph swap (the Terminal face is uniformly
|
||||||
|
`CARD_FACE_COLOUR` regardless of CBM; CBM only swaps red
|
||||||
|
suits to a hue-distinct alternative in the glyph itself).
|
||||||
|
`card_back_colour()` retuned to the 5 base16-eighties accent
|
||||||
|
colours matching `BACK_ACCENTS`. `face_colour()` deleted —
|
||||||
|
the function collapsed to a constant once the Terminal face
|
||||||
|
became uniform. `text_colour()` gained a `color_blind: bool`
|
||||||
|
parameter to surface the CBM swap on the constant-fallback
|
||||||
|
path (the production path bakes glyphs into the PNG, but
|
||||||
|
tests under `MinimalPlugins` still need the CBM-aware
|
||||||
|
fallback). Four `face_colour` CBM tests collapsed into two
|
||||||
|
`text_colour` CBM tests in the same commit.
|
||||||
|
- **Default-theme SVG art regenerated to Terminal aesthetic**
|
||||||
|
(`a14200a`). `solitaire_engine/assets/themes/default/*.svg`
|
||||||
|
— the bundled-default theme that
|
||||||
|
`include_bytes!()`-embeds into the binary — was still the
|
||||||
|
legacy vector-playing-cards art post-`e8bf9d7`. The PNG
|
||||||
|
migration alone didn't change what production rendered
|
||||||
|
because `apply_theme_to_card_image_set` overrides
|
||||||
|
`CardImageSet.faces[..]` at startup with the theme's
|
||||||
|
rasterised SVG handles. Both rendering paths now agree:
|
||||||
|
same `face_svg` / `back_svg` builders feed both paths, and
|
||||||
|
the pin test guards both.
|
||||||
|
- **Card glyphs render upright in both corners** (`dd101b3`).
|
||||||
|
The traditional 180° inverted-corner-indicator rotation on
|
||||||
|
the bottom-right glyph was dropped at user preference —
|
||||||
|
single-orientation digital play doesn't benefit from the
|
||||||
|
flipped-readback convention. Both glyphs now render in the
|
||||||
|
same upright orientation. `design-system.md` § Game Cards
|
||||||
|
line 220 updated in lockstep — the deviation from
|
||||||
|
traditional playing-card layout is documented in the spec,
|
||||||
|
not just the code.
|
||||||
|
- **Action-button row typography aligned to `TYPE_BODY`**
|
||||||
|
(`ae84dc1`). Was a hardcoded `font_size: 16.0` literal that
|
||||||
|
the v0.20.0 typography-migration audit walked past. Brings
|
||||||
|
it in line with the `TYPE_*` token system every other text
|
||||||
|
element in `hud_plugin` already routes through, and trims
|
||||||
|
~12% off label widths so the action-button row no longer
|
||||||
|
collides with the left-anchored HUD column at portrait /
|
||||||
|
narrow window widths. Pairs with a horizontal-padding step-
|
||||||
|
down from `VAL_SPACE_3` to `VAL_SPACE_2`: ~96 px reclaimed
|
||||||
|
across the 6-button row.
|
||||||
|
- **Table backgrounds flattened to solid Terminal colours**
|
||||||
|
(`8719f77`). Replaces the legacy felt-texture PNGs at
|
||||||
|
`assets/backgrounds/bg_*.png` with 5 flat near-black
|
||||||
|
variants per design-system.md (Terminal play surface is
|
||||||
|
flat; no felt, no gradient). On-disk tile weight drops
|
||||||
|
from ~16 KB average to ~100 bytes per tile; runtime
|
||||||
|
appearance flips from green felt to flat `#151515`.
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
- **Card suit glyphs rendered as near-invisible "tofu" marks**
|
||||||
|
(`af414b6`). The bundled `FiraMono` in
|
||||||
|
`svg_loader::shared_fontdb` doesn't carry usable U+2660-2666
|
||||||
|
glyphs at the requested size — usvg silently substituted a
|
||||||
|
default-size fallback regardless of `font-size="20"` /
|
||||||
|
`font-size="64"`. Switched suit-glyph rendering from `<text>`
|
||||||
|
elements to inline SVG `<path>` elements via a new
|
||||||
|
`suit_path_d` helper authoring each suit as a single closed
|
||||||
|
perimeter in a 32×32 logical box. Path-based rendering
|
||||||
|
bypasses the font system entirely — same bytes on every
|
||||||
|
machine, no fontdb dependency, no substitution risk. Same
|
||||||
|
path data renders correctly whether filled (♥ ♠) or outlined
|
||||||
|
(♦ ♣ — the always-on color-blind glyph differentiation).
|
||||||
|
- **Default-theme SVGs were overriding new PNG artwork at
|
||||||
|
runtime** (`a14200a`). The PNG migration in `e8bf9d7` looked
|
||||||
|
correct under `cargo test` (the constant-fallback path
|
||||||
|
matched) but a real `cargo run` showed legacy white cards
|
||||||
|
because `theme::plugin::apply_theme_to_card_image_set`
|
||||||
|
overlays the bundled-default theme's rasterised SVGs onto
|
||||||
|
`CardImageSet.faces[..]` at startup, and those SVGs were
|
||||||
|
still legacy. Fixed by regenerating both rendering paths
|
||||||
|
from the same `face_svg` / `back_svg` builders. The
|
||||||
|
migration plan flagged "Theme system — out of scope here";
|
||||||
|
that was a planning miss documented in the SESSION_HANDOFF.
|
||||||
|
- **Top-bar HUD column collided with action-button row at
|
||||||
|
portrait window widths** (`ae84dc1`). Both nodes were
|
||||||
|
absolute-positioned siblings at `top: VAL_SPACE_2` without a
|
||||||
|
shared flex parent, so they could overlap horizontally when
|
||||||
|
the window narrowed past their combined natural widths.
|
||||||
|
Fixed via the typography + padding tightening described
|
||||||
|
under "Changed" — minimal-blast-radius fix; the structural
|
||||||
|
fix (shared `JustifyContent::SpaceBetween` parent) stays
|
||||||
|
open as a follow-up if narrower windows surface.
|
||||||
|
- **Table-surface fill was still legacy green felt despite
|
||||||
|
v0.20.0's chrome-migration claim** (`8719f77`). Commit
|
||||||
|
`651f406` retuned in-engine constants but the runtime path
|
||||||
|
loads from `assets/backgrounds/bg_0.png`, an on-disk PNG that
|
||||||
|
the migration didn't touch. Same shape as the default-theme
|
||||||
|
override above — token migration walked past a fallback
|
||||||
|
rendering path. Fixed by regenerating the 5 background PNGs.
|
||||||
|
|
||||||
|
### Stats
|
||||||
|
|
||||||
|
- **1184 passing tests / 0 failing** across the workspace
|
||||||
|
(net +8 from v0.20.0's 1176 baseline). New tests this cycle:
|
||||||
|
the scrub-bar pair (`scrub_pct_covers_state_corners`,
|
||||||
|
`overlay_scrub_fill_tracks_cursor`); the splash boot-screen
|
||||||
|
pair (`splash_renders_terminal_boot_screen_content`,
|
||||||
|
`fadables_start_transparent_and_reach_full_alpha`); the
|
||||||
|
splash-polish pair (`build_scanline_image_has_expected_2x2_rgba_bytes`,
|
||||||
|
`scanline_overlay_spawns_and_fades_with_splash`); the
|
||||||
|
card-face pin (one integration test in
|
||||||
|
`card_face_svg_pin.rs` that exercises 57 rasteriser outputs
|
||||||
|
through 57 hash comparisons in a single
|
||||||
|
`#[test]`-marked function); and the CBM consolidation that
|
||||||
|
rewrote four `face_colour` tests as two `text_colour` CBM
|
||||||
|
tests in the same commit (net 0 to count, clean rewrite).
|
||||||
|
- Zero clippy warnings under `cargo clippy --workspace
|
||||||
|
--all-targets -- -D warnings`.
|
||||||
|
- `cargo test --workspace` clean.
|
||||||
|
|
||||||
|
### Documentation
|
||||||
|
|
||||||
|
- `docs/ui-mockups/card-face-migration.md` (`5623368`) — the
|
||||||
|
multi-session lockstep migration plan that the card-face arc
|
||||||
|
followed step-by-step. Now reads as historical record of
|
||||||
|
closed work; lessons documented under "Process notes" in
|
||||||
|
SESSION_HANDOFF.md.
|
||||||
|
- `docs/ui-mockups/desktop-adaptation.md` (`39b8496`) — rules-
|
||||||
|
based companion to the 24-mockup library. Required reading
|
||||||
|
before any future plugin port.
|
||||||
|
- `docs/ui-mockups/design-system.md` updates: § Game Cards
|
||||||
|
line 220 (glyph orientation), CTA / suit-red-cb / Card-back
|
||||||
|
badge / Primary button / Bottom-bar active-icon palette
|
||||||
|
retunes for the cyan→red swap. Historical references
|
||||||
|
preserved as audit trail.
|
||||||
|
- Multiple `SESSION_HANDOFF.md` refreshes (`a65e5b8`,
|
||||||
|
`13ae160`, `44f5972`, `73ac67d`, `ef54cde`, `d109c32`)
|
||||||
|
recording Options B / C / D closures and process notes.
|
||||||
|
|
||||||
## [0.20.0] — 2026-05-07
|
## [0.20.0] — 2026-05-07
|
||||||
|
|
||||||
Two through-lines closed: a full **Android port** (build target,
|
Two through-lines closed: a full **Android port** (build target,
|
||||||
|
|||||||
Reference in New Issue
Block a user