The pixel-art card theme generated via Claude Design (53 PNGs at
256x384, ~340 KB total) now ships embedded in the binary alongside
the existing default SVG theme. Players see the new theme in the
picker out of the box without needing to drop files into
~/.local/share/solitaire_quest/themes/.
solitaire_engine/assets/themes/rusty-pixel/:
- 53 PNGs (52 face cards + 1 back) at 256x384
- theme.ron declaring meta.id = "rusty-pixel",
card_aspect = (2, 3), pixel_art = true
assets/sources.rs:
- New constants RUSTY_PIXEL_THEME_MANIFEST_URL,
RUSTY_PIXEL_THEME_MANIFEST_PATH,
RUSTY_PIXEL_THEME_MANIFEST_BYTES.
- New embed_rusty_pixel_png! macro mirroring embed_default_svg!.
- New RUSTY_PIXEL_THEME_PNGS table — 53 entries, one per file.
- New rusty_pixel_theme_png_bytes(filename) lookup helper
mirroring default_theme_svg_bytes for the thumbnail cache.
- New populate_embedded_rusty_pixel_theme(app) registers the
manifest + every PNG into Bevy's EmbeddedAssetRegistry.
- AssetSourcesPlugin::build now calls both populate functions
so the picker has both themes loadable from the binary alone.
theme/registry.rs:
- New rusty_pixel_entry() returns the bundled metadata.
- build_registry now inserts default + rusty-pixel ahead of the
user-dir scan, and filters user themes whose id collides with
a bundled built-in. Bundled wins on collision because it's
guaranteed complete; the user's overriding copy may be partial
or stale.
- Updated existing tests for the new len()=2-instead-of-1 baseline.
- New test user_theme_id_collision_with_bundled_is_dropped pins
the dedup contract.
theme/plugin.rs:
- load_initial_theme + react_to_settings_theme_change now both
consult a new manifest_url_for(theme_id) helper that routes
bundled built-ins through embedded:// and unknown ids through
themes://. Drops the previous hard-coded "default →
DEFAULT_THEME_MANIFEST_URL else themes://" branch.
- read_theme_preview_bytes also checks the rusty-pixel embed
table before falling through to the user-dir filesystem read,
so the picker chip's thumbnail works on a fresh install where
the user-dir doesn't exist.
Workspace: 1172 passing tests / 0 failing, was 1171 (+1 net from
the new collision test). cargo clippy --workspace --all-targets
-- -D warnings clean. Binary grows by ~340 KB (the 53 bundled
PNGs).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replaces the previous xCards-derived card faces (LGPL-3.0) with
hayeah/playing-cards-assets, which itself derives from the
public-domain vector-playing-cards Google Code project. The whole
package is MIT now — see CREDITS.md for the new attribution table
and the simpler license summary.
solitaire_engine/assets/themes/default/
52 face SVGs (clubs/diamonds/hearts/spades × ace/2-10/jack/queen/
king) — copied from hayeah, renamed to the canonical
`{suit}_{rank}.svg` form `CardKey::manifest_name` produces. The
bundled default theme manifest references each by the same name.
back.svg — original midnight-purple-themed card back, hand-written
to match the project's design tokens (BG_BASE / BG_ELEVATED /
ACCENT_PRIMARY / ACCENT_SECONDARY). MIT, original work.
assets/cards/faces/{RANK}{SUIT}.png
52 PNGs regenerated from the new SVGs at 750-tall via resvg 0.47.
These remain the legacy backwards-compat path that
`card_plugin::load_card_images` reads at startup; once the runtime
theme system finishes loading the embedded default theme, the
CardImageSet's face handles are overwritten with the SVG-rendered
variants and these PNGs become moot. Keeping them in place avoids
a brief blank-card flash before the async theme load completes.
solitaire_engine/src/assets/sources.rs
embed_default_svg!() macro + DEFAULT_THEME_SVGS table that bundles
every face + the back into the binary at compile time via
include_bytes!. populate_embedded_default_theme now iterates the
table so the EmbeddedAssetRegistry is populated under the same
asset paths the manifest references.
CREDITS.md
License summary collapses from MIT + LGPL-3.0 + OFL-1.1 to MIT +
OFL-1.1 (the OFL still applies to FiraMono). The hayeah upstream
URL replaces the previously-blank xCards entry.
cargo build / clippy --workspace --all-targets -- -D warnings / test
--workspace all green (960 passed, 0 failed, 9 ignored).
Implements Phase 3 of CARD_PLAN.md — the embedded:// + themes:// asset
sources the card-theme system loads from. The bundled default-theme
manifest ships in the binary via Bevy's EmbeddedAssetRegistry; user
themes load from user_theme_dir() through a FileAssetReader-backed
source registered as `themes://`.
Registration is split across:
register_theme_asset_sources(&mut App)
Called BEFORE DefaultPlugins. Registers `themes://` while
AssetSourceBuilders is still mutable.
AssetSourcesPlugin
Added AFTER DefaultPlugins. Populates the EmbeddedAssetRegistry
that AssetPlugin's build step would otherwise overwrite.
Constants exposed for downstream consumers:
USER_THEMES = "themes" (asset-source name)
DEFAULT_THEME_MANIFEST_URL = "embedded://solitaire_engine/assets/themes/default/theme.ron"
Includes a stub default theme.ron (52 face slots + back) so
`ThemeManifest::validate()` accepts it today; PROVENANCE.md documents
the plan to drop in real SVG art (hayeah/playing-cards-assets) in a
follow-up.
4 new tests covering source registration, embedded-registry
population, manifest validation against the embedded stub, and the
manifest-URL constant matching the embedded asset path.
cargo check --workspace --all-targets / clippy --workspace
--all-targets -- -D warnings clean. cargo test could not be run in
this turn because the C linker (cc) is unexpectedly absent from the
sandbox; the test bodies compile cleanly under cargo check --tests
and will run on a normal toolchain.