A.5 my_sea.html central sig card image-rendering + SCSS lift-out fix — TDD. Sprint A.5 of [[project-image-based-deck-face-rendering]]: second visible surface after my_sign A.3. When the user's equipped deck is image-equipped (Minchiate today), the central significator card in the Celtic-Cross-style spread (.sig-stage-card.sea-sig-card inside .sea-pos-core) renders the transparent-PNG <img> w. contour-following arcana-color drop-shadow stroke + tray-card silhouette black shadow — same visual identity as my_sign's saved-sig stage card so the user's "this is my sig" anchor reads the same across both surfaces. Server-side template branch on significator.deck_variant.has_card_images: image branch renders <img class="sig-stage-card-img" src="{{ significator.image_url }}"> + adds .sig-stage-card--image marker class + data-arcana-key="{{ arcana }}" for the stroke-color selector; text branch keeps the existing corner-rank + suit-icon render unchanged (Earthman, RWS). No JS needed — central sig is statically rendered (vs my_sign's stage card which is JS-populated from the picker grid). Critical SCSS lift-out: the A.3 .sig-stage-card--image rule lived nested inside .sig-stage .sig-stage-card, scoped to my_sign.html's stage container only. my_sea's central sig isn't inside .sig-stage (lives in .sea-pos-core), so the rule wasn't applying — image rendered at native pixel dimensions (~620×1024 PNG) instead of being constrained to the card container, showing only a top-left portion (user bug-report 2026-05-25 PM: "It doesn't scale the img down for the sig — just a portion of the full img"). Fix: moved the entire .sig-stage-card.sig-stage-card--image { ... } block OUT of the .sig-stage nest into top-level scope so it applies to ANY .sig-stage-card carrying the --image class regardless of parent (my_sign's .sig-stage, my_sea's .sea-pos-core, future room.html's table center, future deck-bag UI). Same lift-out also expands the display: none list to include .fan-corner-rank + > i.fa-solid — these elements appear in my_sea's text-mode central sig and need hiding when image-mode kicks in (my_sign's text mode uses the wrapped .fan-card-corner + .fan-card-face classes which were already covered). 2 new ITs in MySeaPickerPhaseTemplateTest: image-equipped Minchiate sig renders .sig-stage-card--image class + <img> w. correct v2-convention src; non-image Earthman keeps .fan-corner-rank text + lacks --image class. Earthman Minchiate test fixture needs the super-nomad + super-schizo Note unlocks (granted manually via Note.grant_if_new since the post_save signal only fires on initial user creation, and we promote-to-superuser AFTER create) to let Il Matto (MAJOR 0) through _filter_major_unlocks. Tests: 2 new green; 1300/1300 IT+UT total green (70s; +2 from 750fef8's 1298). Visual verify pending: refresh /gameboard/my-sea/ w. Minchiate equipped + Il Matto as sig → central sig card should now scale the back image to fit the card container instead of showing a top-left crop. Sea Stage modal + drawn-card slot rendering (the bigger A.5 scope) still pending — they go through stage-card.js + the my-sea draw fetch endpoint, which need data-attr + JSON-payload extensions in a follow-up commit

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-05-25 01:20:07 -04:00
parent 750fef890e
commit 82813e9fc1
3 changed files with 135 additions and 59 deletions

View File

@@ -969,6 +969,64 @@ class MySeaPickerPhaseTemplateTest(TestCase):
self.assertContains(response, "sea-pos-leave")
self.assertContains(response, "sea-pos-loom")
def test_sea_sig_card_renders_image_when_deck_has_card_images(self):
"""Sprint A.5 — central sig card on /gameboard/my-sea/ carries the
`.sig-stage-card--image` marker class + an <img.sig-stage-card-img>
child pointing at the deck's image asset when the user's equipped
deck is image-equipped (Minchiate today). Mirrors A.3's my_sign.html
image-mode treatment so the central sig + the Sea Stage modal render
with consistent visual identity."""
from apps.epic.models import DeckVariant, TarotCard
minchiate = DeckVariant.objects.get(slug="minchiate-fiorentine-1860-1890")
# Override the auto-equipped Earthman w. Minchiate + pick Il Matto as
# the user's sig (it's MAJOR rank 0 → permitted by personal_sig_cards
# IF user has the super-nomad Note unlock; superuser auto-gets it).
self.user.is_superuser = True
self.user.save()
# Re-run the post_save Note grants for the now-superuser by manually
# granting (signal only fires on initial create).
from apps.drama.models import Note
Note.grant_if_new(self.user, "super-nomad")
Note.grant_if_new(self.user, "super-schizo")
self.user.unlocked_decks.add(minchiate)
self.user.equipped_deck = minchiate
il_matto = TarotCard.objects.get(deck_variant=minchiate, slug="il-matto")
self.user.significator = il_matto
self.user.save(update_fields=["equipped_deck", "significator"])
import lxml.html
response = self.client.get(reverse("my_sea"))
parsed = lxml.html.fromstring(response.content)
[sig_card] = parsed.cssselect(".sea-sig-card")
self.assertIn(
"sig-stage-card--image", sig_card.get("class", ""),
"Sig card must carry --image marker class for Minchiate-equipped user",
)
[img] = sig_card.cssselect("img.sig-stage-card-img")
self.assertIn(
"minchiate-fiorentine-1860-1890-trumps-00-il-matto.png",
img.get("src", ""),
"Image src must point at the v2-convention Il Matto asset",
)
def test_sea_sig_card_renders_text_when_deck_has_no_images(self):
"""Earthman (has_card_images=False) keeps the existing corner-rank +
suit-icon text render — the image branch only applies to image-decks."""
# Default setUp leaves the user on auto-equipped Earthman.
import lxml.html
response = self.client.get(reverse("my_sea"))
parsed = lxml.html.fromstring(response.content)
[sig_card] = parsed.cssselect(".sea-sig-card")
self.assertNotIn("sig-stage-card--image", sig_card.get("class", ""))
self.assertEqual(
len(sig_card.cssselect("img.sig-stage-card-img")), 0,
"Non-image deck must not render the <img> in the sig card",
)
self.assertEqual(
len(sig_card.cssselect(".fan-corner-rank")), 1,
"Non-image deck falls through to corner-rank text render",
)
def test_picker_renders_six_card_only_positions_for_spread_switch(self):
# Crown / lay / cross sit in the DOM unconditionally so iter 3's
# SPREAD dropdown can reveal them via CSS attribute swap (data-