Commit Graph

7 Commits

Author SHA1 Message Date
funman300 dd970215cc fix(engine): drop card-face border to remove gray-corner artifact
Player feedback after the 2-colour revert: "I do not like the
grey corners on the cards." The visible artifact was anti-
aliasing physics — the 1 px suit-coloured stroke (red for
hearts/diamonds, near-white for clubs/spades) faded through
gray pixels into the dark play surface at each rounded corner,
producing a visible "gray sliver" at the four arcs of every
card.

Fix: drop the stroke entirely. The card body fill defines the
shape against the play surface; the 5-unit brightness gap
between `#1a1a1a` body and `#151515` surface is enough to read
as a card edge without an explicit stroke. Anti-aliasing on a
fill-only rounded rect blends `#1a1a1a → #151515` over a few
pixels — barely perceptible compared to the
`stroke → transparent` gradient that produced the artifact.

### Changes

- `card_face_svg.rs`: removed `stroke="{colour}" stroke-width="2"`
  from the card body rect. Reverted the 1 px stroke inset back
  to `(x=0, y=0, width=256, height=384)` since there's no
  longer a stroke to keep inside the pixmap. Module-level
  comment updated to document the reasoning.
- `design-system.md` § Game Cards line 225 updated: "Border:
  1px solid in suit color" → "Border: none." with the
  artifact rationale recorded as audit trail.
- `card_face_svg_pin.rs` rebaselined: all 52 face hashes drift
  (every card's perimeter pixels changed); 5 back hashes
  unchanged.

Workspace clippy + cargo test --workspace clean. 1191 passing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:41:54 -07:00
funman300 ddb65403c2 feat(engine): revert to traditional 2-colour deck with saturated red + near-white
Per player feedback after the brief 4-colour-deck experiment:
"can we make the card suit colors the same as a regular
solitaire game would." Reverts the 4-colour split (`62b61cc`)
and bumps both 2-colour hues to read more like a real
Microsoft-Solitaire-on-dark-mode deck.

### Constants

- `RED_SUIT_COLOUR`: `#fb9fb1` (Terminal pink, then briefly
  hearts-only) → `#e35353` (saturated red). More chromatic, less
  pastel; reads as "the red suit" rather than "a Terminal-
  themed pink." Visually distinct from `ACCENT_PRIMARY`
  `#a54242` (the brick-red CTA accent) so chrome and suit don't
  collapse to the same hue.
- `BLACK_SUIT_COLOUR`: `#d0d0d0` (matched `TEXT_PRIMARY`) →
  `#e8e8e8` (near-white). Bumped slightly brighter so it reads
  as a chromatic-neutral counterpart to the new saturated red,
  not as "the same gray as body text." `TEXT_PRIMARY_HC`
  (`#f5f5f5`) is still brighter for the high-contrast boost
  path.
- `RED_SUIT_COLOUR_HC`: `#ff8aa0` (pinkish boost matching the
  v0.21.0 pink default) → `#ff6868` (brighter saturated red).
  Now reads as "more chromatic" than the new default red, not
  "less saturated."
- `DIAMOND_SUIT_COLOUR` and `CLUB_SUIT_COLOUR` deleted — the
  4-colour split is gone, hearts/diamonds re-pair under
  `RED_SUIT_COLOUR` and clubs/spades under
  `BLACK_SUIT_COLOUR`.

### `card_face_svg.rs`

- Module-level constants collapse from four (`SUIT_HEART` /
  `SUIT_DIAMOND` / `SUIT_CLUB` / `SUIT_SPADE`) back to two
  (`SUIT_RED` / `SUIT_DARK`) at the new saturated-red /
  near-white values.
- `suit_paint()` reverts to the 2-colour pairing: hearts
  filled-red, diamonds outlined-red, spades filled-near-white,
  clubs outlined-near-white. Filled-vs-outlined glyph
  differentiation stays the always-on CBM fallback.

### `card_plugin.rs`

- `text_colour()` reverts to a `card.suit.is_red()`
  bifurcation. Comment block updated to reflect the new
  truth table: red suits → saturated red (or CBM lime / HC
  brighter red); dark suits → near-white (or HC brighter
  near-white).

### Tests

Test block restructured back to the pre-4-colour shape: two
red/black pairing tests instead of one 4-colour distinctness
test. CBM/HC compose tests retuned to the 2-colour world (red
suits compose, dark suits compose; no separate diamonds-immune
or clubs-immune cases). 1191 passing / 0 failing — net 0 from
the prior commit (3 tests removed: the 4-colour distinctness
test + the diamonds/clubs-immune test; 2 tests added back: the
red-pairing + dark-pairing tests; existing tests amended to
new colour assumptions).

### `card_face_svg_pin`

All 52 face hashes drift (every suit's colour shifted); 5 back
hashes unchanged. Surgical rebaseline.

### `design-system.md`

§Suit Colors retitled "Two-color traditional pairing", table
updated with the new hex values, CBM section text simplified
back to red→lime swap on both red suits.

Workspace clippy clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:35:36 -07:00
funman300 62b61cc786 feat(engine): switch card fronts to 4-colour deck
Hearts pink (`#fb9fb1`), Diamonds gold (`#ddb26f`), Clubs lime
(`#acc267`), Spades gray (`#d0d0d0`) — each suit picks up its
own base16-eighties accent so a player scanning the table can
distinguish the suit by hue alone (faster recognition than the
2-colour traditional red/black scheme; common in poker decks).
All four colours already exist in the palette as semantic
state-token accents, so this is a pure remapping at the suit-
glyph site, not a palette extension.

The outlined-glyph differentiation (♦ ♣ outlined, ♥ ♠ filled)
is preserved on top of the colour split — it stays the always-
on colour-blind fallback per `design-system.md` §Accessibility,
and matters more than ever now that CBM hearts (lime) and
default clubs (lime) share a hue.

### Changes

- `card_face_svg.rs`: split `SUIT_RED` / `SUIT_DARK` into four
  per-suit constants (`SUIT_HEART` / `SUIT_DIAMOND` / `SUIT_CLUB`
  / `SUIT_SPADE`). `suit_paint()` returns each suit's own
  colour. Card border picks up the suit colour automatically
  via the existing `(colour, paint)` destructure.
- `card_plugin.rs`: new `DIAMOND_SUIT_COLOUR` + `CLUB_SUIT_COLOUR`
  constants; `text_colour()` rewritten as a per-suit match (was
  red/black bifurcation). Both rendering paths (PNG production +
  constant fallback under MinimalPlugins) stay in lockstep.
- CBM behaviour clarified: only hearts swap to lime now;
  diamonds + clubs + spades are already hue-distinct from
  the heart pink and stay unchanged. Under CBM the heart
  (lime) and club (lime) share a hue but stay distinguishable
  via the always-on filled-vs-outlined glyph differentiation.
- HC behaviour: only hearts (→ HC red) and spades (→ HC white)
  have defined boosts. Diamonds (gold) and clubs (lime) are
  already mid-luminance accents and stay at their default.
  New test `text_colour_diamonds_and_clubs_are_immune_to_accessibility_flags`
  pins all four flag combinations as no-ops for the gold +
  lime suits.
- `design-system.md` §Suit Colors retitled "Four-color deck"
  with the 4-colour table; CBM section text updated to
  describe the hearts-only swap and the hearts/clubs hue
  collision under CBM.
- `card_face_svg_pin.rs` rebaselined: 26 hashes drift
  (13 clubs + 13 diamonds — the two suits whose colours
  changed). Hearts, spades, and the 5 backs all keep their
  prior hashes. Surgical scope, exactly what the pin test
  was designed to surface.

### Tests

1191 passing / 0 failing — net 0 from the prior baseline:
two old 2-colour tests removed
(`text_colour_is_red_for_hearts_and_diamonds`,
`text_colour_is_black_for_clubs_and_spades`), one consolidated
4-colour test added
(`text_colour_4_colour_deck_assigns_each_suit_its_own_hue`)
plus a pairwise-distinct invariant guard, and one new test
covering the gold/lime suits' immunity to CBM/HC flags. Six
existing CBM/HC tests rewritten to use only the suits each flag
actually affects under the new scheme (hearts for CBM, hearts +
spades for HC).

Workspace clippy clean.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 12:00:55 -07:00
funman300 a292a7ead0 feat(engine): swap ACCENT_PRIMARY from cyan #6fc2ef to brick red #a54242
Project-wide palette shift at user request. Replaces the cyan
primary accent everywhere it surfaces — 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, etc. — with `#a54242` from the same base16-eighties
family as the existing pink suit colour.

Knock-on changes that all land in this commit per the
lockstep rule:

- ui_theme.rs: ACCENT_PRIMARY (#a54242), ACCENT_PRIMARY_HOVER
  (#c25e5e brightened companion), FOCUS_RING (same hue, 0.85
  alpha). Module-level palette comment + STOCK_BADGE_FG +
  CARD_SHADOW_ALPHA_DRAG doc strings updated to match.
- card_plugin.rs: card_back_colour(0) now returns the brick-red
  ACCENT_PRIMARY (was cyan). RED_SUIT_COLOUR_CBM swapped from
  cyan to lime #acc267 — the CBM alternative needs to stay
  hue-distinct from the new red-family primary, lime is the
  next-best non-red base16-eighties accent. text_colour doc
  + CBM tests renamed cyan→lime in lockstep
  (text_colour_color_blind_mode_swaps_red_suits_to_lime).
- card_face_svg.rs: BACK_ACCENTS[0] now "#a54242" (canonical
  Terminal back).
- splash_plugin.rs / ui_modal.rs / replay_overlay.rs /
  selection_plugin.rs: descriptive "cyan" comments swapped to
  "accent" / "primary-accent" wording so the doc strings stay
  decoupled from any specific hue. Future palette tweaks won't
  require comment churn.
- design-system.md: YAML token frontmatter updated (primary,
  surface-tint, suit-red-cb, primary-container,
  on-primary-container, inverse-primary). Palette table gains
  a project-specific `base08` slot for the new red. CTA /
  Selection / Card-back badge / Primary button / Bottom-bar
  active-icon / glow / CBM swap text all retuned. Historical
  references preserved (e.g. "Was cyan #6fc2ef before the
  2026-05-08 swap") so the audit trail stays in the spec.
- card_face_svg_pin.rs: rebaselined. Exactly one hash drift
  (back_0 — the canonical Terminal back's badge changed
  colour). Other 56 hashes identical (face SVGs don't
  reference the accent; back_1..4 use unchanged accents). The
  one-hash-drift signal confirms the change scope was
  surgical.

Workspace clippy + cargo test --workspace clean, 1184 passing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 10:30:35 -07:00
funman300 dd101b3d54 fix(engine): render bottom-right card glyph upright (no 180° rotation)
The user noticed the bottom-right large suit glyphs were
rendering upside-down — point-up hearts, stem-up spades — because
the SVG transform pipeline applied a `rotate(180)` to match the
traditional playing-card inverted-corner convention.

That convention exists so a card reads correctly when flipped or
read from the opposite side of the table. Single-orientation
digital play doesn't benefit from it; most modern digital decks
have abandoned it. User preference is upright.

Drops the rotate from face_svg's bottom-right `<g transform>`
and adjusts the translate so the visible glyph still lands at
(178, 286)–(242, 350) — same screen footprint, same scale, just
no flip.

design-system.md § Game Cards updated in lockstep — line 220
no longer says "rotated 180°", instead documents the deliberate
deviation from the traditional convention.

Knock-on lockstep changes in this commit:
- EXPECTED in tests/card_face_svg_pin.rs rebaselined: 52 face
  hashes shift, 5 back hashes unchanged.
- assets/cards/faces/*.png regenerated (52 face PNGs).
- solitaire_engine/assets/themes/default/*_*.svg regenerated
  (52 theme face SVGs that production rasterises at startup).

Workspace clippy + cargo test --workspace clean. Pin test
passes against the new hashes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 10:09:55 -07:00
funman300 af414b6aed fix(engine): render card suit glyphs as SVG paths instead of text
The user's first post-migration screenshot showed near-invisible
suit glyphs on every card — the rank rendered at correct size but
the ♠ ♥ ♦ ♣ marks were tiny dots regardless of the requested
20px / 64px font-size.

Root cause: the bundled FiraMono in svg_loader::shared_fontdb
doesn't carry usable Unicode suit glyphs (U+2660-2666). usvg
silently fell back to a substitute rendering at default size,
producing the "tofu" effect.

Fixes by replacing the `<text>` glyph rendering with inline SVG
paths. `suit_path_d(suit)` returns a single closed-perimeter path
authored in a 32 × 32 logical box, then face_svg wraps it in two
`<g transform>` blocks (top-left small + bottom-right rotated
large). 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
from the design system).

Knock-on changes that must land in this commit per the migration
plan's lockstep rule:

- `EXPECTED` in tests/card_face_svg_pin.rs rebaselined: 52 face
  hashes change (text → path), 5 back hashes unchanged
  (back_svg untouched). The bootstrap pattern in the test
  handled the rebaseline cleanly — empty EXPECTED, re-run,
  paste, re-run.
- assets/cards/faces/*.png regenerated (the 52 face PNGs).
- solitaire_engine/assets/themes/default/*_*.svg regenerated
  (the 52 theme face SVGs that production rasterises at
  startup). Both rendering paths must agree.

Workspace clippy + cargo test --workspace clean. Pin test
passes against the new hashes.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 10:02:04 -07:00
funman300 48b28d29f8 test(engine): pin card-face SVG output against rasteriser drift
Step 3 of the migration plan in docs/ui-mockups/card-face-migration.md.

Extracts face_svg / back_svg + palette constants from the
card_face_generator example into a new
solitaire_engine::assets::card_face_svg module so an integration
test can call them. The example becomes a thin wrapper.

The new tests/card_face_svg_pin.rs hashes the raw RGBA8 pixel
bytes from rasterising every face × suit + every back accent and
compares each FNV-1a fingerprint against an embedded constant.
Catches silent rendering drift if usvg / resvg / tiny_skia / the
bundled FiraMono ever change in a way that perturbs pixels.

Hashing is FNV-1a inline (~5 lines) rather than adding sha2 or
blake3 — cryptographic strength isn't load-bearing here, just
stable byte fingerprints.

When the SVG builders intentionally change, empty EXPECTED to
`&[]` and re-run the test once; it panics with the new hashes
formatted as Rust source ready to paste back in.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 09:21:00 -07:00