fix(engine): regenerate default theme SVGs to Terminal aesthetic

Step 4's PNG regeneration left the cards looking unchanged at
runtime because the PNGs at assets/cards/ are only the *fallback*
art — production renders the bundled-default theme's SVGs, which
get include_bytes!()-embedded into the binary by
solitaire_engine::assets::sources and applied to CardImageSet at
startup by theme::plugin::apply_theme_to_card_image_set. Those
SVGs were still the legacy vector-playing-cards art.

Extends card_face_generator to write SVGs into both runtime
paths in lockstep:

1. assets/cards/{faces,backs}/*.png — fallback art (unchanged
   from step 4).
2. solitaire_engine/assets/themes/default/*.svg — what production
   actually renders. 52 face SVGs + 1 back SVG, generated from
   the same face_svg / back_svg builders as the PNGs so the two
   paths can never visually diverge.

Adds two helper functions to card_face_svg:

- theme_suit_token (clubs/diamonds/hearts/spades — lowercase
  full word, matching CardKey::manifest_name)
- theme_rank_token (ace/2..10/jack/queen/king — same)

The theme back uses BACK_ACCENTS[0] (canonical Terminal cyan).
The other four accents only live as PNG fallbacks because the
theme system carries one back per theme.

Net SVG diff: -14884 / +940 lines — the legacy vector-playing-
cards SVGs were ~300 lines each of Inkscape-authored paths;
the Terminal SVGs are ~10 lines of programmatic output.

Workspace clippy + cargo test --workspace clean. Pin test
unaffected (the SVG builders themselves did not change).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
funman300
2026-05-08 09:40:24 -07:00
parent e8bf9d79da
commit a14200ac2f
55 changed files with 854 additions and 14798 deletions
@@ -1,324 +1,16 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="384" viewBox="0 0 256 384">
<rect x="1" y="1" width="254" height="382" rx="16" ry="16"
fill="#1a1a1a" stroke="#fb9fb1" stroke-width="2"/>
<!-- http://code.google.com/p/vector-playing-cards/ -->
<!-- Top-left rank + small suit glyph. -->
<text x="14" y="44" font-family="Fira Mono" font-size="36" font-weight="700"
fill="#fb9fb1">4</text>
<text x="14" y="68" font-family="Fira Mono" font-size="20"
fill="none" stroke="#fb9fb1" stroke-width="3">&#x2666;</text>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="167.0869141pt"
height="242.6669922pt"
viewBox="0 0 167.0869141 242.6669922"
xml:space="preserve"
id="svg2"
version="1.1"
inkscape:version="0.48.0 r9654"
sodipodi:docname="4_of_diamonds.svg"
inkscape:export-filename="/home/byron/art/cards/final/PNGs/4_of_diamonds.png"
inkscape:export-xdpi="215.44792"
inkscape:export-ydpi="215.44792"><metadata
id="metadata43"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs41"><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3781"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3773"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3775" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3777" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3773"
id="radialGradient3957"
cx="-0.15782039"
cy="-8.8345356"
fx="-0.15782039"
fy="-8.8345356"
r="7.9997029"
gradientTransform="matrix(-1.5842693,-0.02349808,0.03071979,-2.4775745,-0.24856378,-26.713507)"
gradientUnits="userSpaceOnUse" /><linearGradient
id="linearGradient3959"><stop
style="stop-color:#000000;stop-opacity:1;"
offset="0"
id="stop3961" /><stop
style="stop-color:#000000;stop-opacity:0.64885497;"
offset="1"
id="stop3963" /></linearGradient><radialGradient
r="81.902771"
fy="509.47577"
fx="168.02475"
cy="509.47577"
cx="168.02475"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
gradientUnits="userSpaceOnUse"
id="radialGradient3975"
xlink:href="#linearGradient3784-4"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-5"
id="radialGradient3929"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-5"><stop
style="stop-color:#ffffff;stop-opacity:0.48854962;"
offset="0"
id="stop3786-8-0" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-3" /></linearGradient><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient3784-4-1"
id="radialGradient3927"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(1.2565605,-0.77740644,0.33663816,0.5361257,-221.20213,359.24256)"
cx="168.02475"
cy="509.47577"
fx="168.02475"
fy="509.47577"
r="81.902771" /><linearGradient
id="linearGradient3784-4-1"><stop
style="stop-color:#ffffff;stop-opacity:0.23664123;"
offset="0"
id="stop3786-8-03" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-6" /></linearGradient><linearGradient
id="linearGradient3768"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop3770" /><stop
style="stop-color:#df0000;stop-opacity:0.67175573;"
offset="1"
id="stop3772" /></linearGradient><linearGradient
id="linearGradient3784-4-6"><stop
style="stop-color:#ffffff;stop-opacity:0.31297711;"
offset="0"
id="stop3786-8-8" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-8" /></linearGradient><radialGradient
r="81.902771"
fy="492.63205"
fx="159.35434"
cy="492.63205"
cx="159.35434"
gradientTransform="matrix(1.0894779,-0.71513803,0.44645273,0.65626582,-244.93331,290.9185)"
gradientUnits="userSpaceOnUse"
id="radialGradient4013-8"
xlink:href="#linearGradient3784-4-2"
inkscape:collect="always" /><linearGradient
id="linearGradient3784-4-2"><stop
style="stop-color:#ffffff;stop-opacity:0.29007635;"
offset="0"
id="stop3786-8-1" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-5" /></linearGradient><linearGradient
id="linearGradient2984"><stop
style="stop-color:#df0000;stop-opacity:1;"
offset="0"
id="stop2986" /><stop
style="stop-color:#df0000;stop-opacity:0.64122134;"
offset="1"
id="stop2988" /></linearGradient><linearGradient
id="linearGradient3784-4-4"><stop
style="stop-color:#ffffff;stop-opacity:0.4351145;"
offset="0"
id="stop3786-8-8-2" /><stop
style="stop-color:#000000;stop-opacity:0;"
offset="1"
id="stop3788-6-1" /></linearGradient><radialGradient
r="81.902771"
fy="511.22299"
fx="171.48665"
cy="511.22299"
cx="171.48665"
gradientTransform="matrix(1.1529891,-0.67391547,0.39482025,0.67549043,-233.63262,270.40076)"
gradientUnits="userSpaceOnUse"
id="radialGradient3100"
xlink:href="#linearGradient3784-4-4"
inkscape:collect="always" /><radialGradient
inkscape:collect="always"
xlink:href="#linearGradient2984"
id="radialGradient3137"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(-1.1224159,0.00551393,-0.00908973,-1.8503101,-0.0293938,-10.227695)"
cx="1.6632675e-13"
cy="-3.2337365"
fx="1.6632675e-13"
fy="-3.2337365"
r="8" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1680"
inkscape:window-height="977"
id="namedview39"
showgrid="false"
inkscape:zoom="1.7208768"
inkscape:cx="72.124594"
inkscape:cy="147.27218"
inkscape:window-x="0"
inkscape:window-y="25"
inkscape:window-maximized="1"
inkscape:current-layer="svg2" />
<g
id="Layer_x0020_1"
style="fill-rule:nonzero;clip-rule:nonzero;stroke:#000000;stroke-miterlimit:4;">
<path
style="fill:#FFFFFF;stroke-width:0.5;"
d="M166.8369141,235.5478516c0,3.7773438-3.0869141,6.8691406-6.8710938,6.8691406H7.1108398c-3.7749023,0-6.8608398-3.0917969-6.8608398-6.8691406V7.1201172C0.25,3.3427734,3.3359375,0.25,7.1108398,0.25h152.8549805 c3.7841797,0,6.8710938,3.0927734,6.8710938,6.8701172v228.4277344z"
id="path5" />
<g
style="stroke:none;"
id="g7">
<g
id="g9">
</g>
</g>
<g
id="g15">
</g>
<g
id="g19">
</g>
<g
style="stroke:none;"
id="g23">
<g
id="g25">
</g>
</g>
<g
style="stroke:none;"
id="g31">
<g
id="g33">
</g>
</g>
</g>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="7.8456664"
y="26.413288"
id="text3788"
sodipodi:linespacing="125%"><tspan
sodipodi:role="line"
id="tspan3790"
x="7.8456664"
y="26.413288"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">4</tspan></text>
<text
xml:space="preserve"
style="font-size:32px;font-style:normal;font-weight:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#df0000;fill-opacity:1;stroke:none;font-family:Bitstream Vera Sans"
x="-159.48785"
y="-216.71518"
id="text3788-4"
sodipodi:linespacing="125%"
transform="scale(-1,-1)"><tspan
sodipodi:role="line"
id="tspan3790-3"
x="-159.48785"
y="-216.71518"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;fill:#df0000;fill-opacity:1;font-family:Arial;-inkscape-font-specification:Arial">4</tspan></text>
<g
transform="matrix(1.4769065,0,0,1.4769065,16.968095,44.236162)"
id="layer1-2-6"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(1.4769065,0,0,1.4769065,150.62089,198.50346)"
id="layer1-2-6-4"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-9"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,112.20553,184.02194)"
id="layer1-2-6-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,112.20553,55.619539)"
id="layer1-2-6-8-2"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,54.128726,184.02194)"
id="layer1-2-6-8-8"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-8"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g><g
transform="matrix(2.5882908,0,0,2.5882908,54.128726,55.619539)"
id="layer1-2-6-8-2-4"><path
style="fill:#df0000"
inkscape:connector-curvature="0"
id="dl-6-8-6-3"
d="M 3.2433274,-4.7253274 C 1.1263274,-7.5893274 0,-10.5 0,-10.5 c 0,0 -1.1263274,2.9106726 -3.2433274,5.7746726 C -5.3613274,-1.8623274 -8,0 -8,0 -8,0 -5.3613274,1.8613274 -3.2433274,4.7263274 -1.1263274,7.5893274 0,10.5 0,10.5 0,10.5 1.1263274,7.5893274 3.2433274,4.7263274 5.3613274,1.8613274 8,0 8,0 8,0 5.3613274,-1.8623274 3.2433274,-4.7253274 z"
sodipodi:nodetypes="ccccccccc" /></g></svg>
<!-- Bottom-right large suit glyph, rotated 180° about its own
baseline anchor so the glyph reads upside-down. -->
<text x="242" y="350" font-family="Fira Mono" font-size="64"
text-anchor="end" fill="none" stroke="#fb9fb1" stroke-width="3"
transform="rotate(180 242 332)">&#x2666;</text>
</svg>

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 819 B