game kit + role icons + tarot fan: in-use mini-portal label, FLIP cue polarity reset, role icon redraws
All checks were successful
ci/woodpecker/push/pyswiss Pipeline was successful
ci/woodpecker/push/main Pipeline was successful

Heterogeneous pre-existing changes (carried across multiple sessions, finally committed alongside the SIG SELECT exit sprint). Grouped:

- gameboard.js: _inUseLabel(roomName) — buildMiniContent renders "In-Use: <name>" on hover (cap 24 chars; overflow → 21 + "…"). Reads token.dataset.inUseRoomName for decks & token.dataset.currentRoomName for trinkets.
- _applet-game-kit.html: removes the inline <p class="tt-token-room-name"> + <p class="tt-deck-game-name"> paragraphs (now redundant — mini-portal carries the name); deck token gains data-in-use-room-name attr.
- gameboard tests: assertions retargeted at data-in-use-room-name + the mini-portal flow rather than the deleted inline paragraphs (test_views, test_deck_contribution, test_trinket_carte_blanche).

- game-kit.js: openFan + _testOpen reset _polarity = 'levity' so reopening the fan after FLIP-to-gravity always lands on the levity-painted face (the FLIP cue). The sessionStorage bookmark intentionally tracks card index only; polarity does NOT persist across reopen.
- _tarot_fan.html: SSR-default polarity flipped from levity to gravity (levity_emanation → gravity_emanation, levity_qualifier → gravity_qualifier, levity_reversal → gravity_reversal across upright + reversal faces). Pairs w. the JS polarity reset above so JS repaints to levity on open.
- FanStageSpec: 2 new specs — openFan polarity reset on reopen even after FLIP-to-gravity; sessionStorage stores no levity/gravity string.

- starter-role-*.svg (Alchemist, Builder, Economist, Narrator, Player, Shepherd): redrawn / re-cropped art — viewBox tightened from 288×560 to ~154×156, paths re-traced. No new role added; existing 6 swapped in place. New starter-role-blank.svg added as fallback for unmapped role codes (referenced by tray.js _ROLE_SCRAWL default → 'Blank').

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-05-03 22:28:32 -04:00
parent f78177778f
commit b1a11504f5
16 changed files with 298 additions and 154 deletions

View File

@@ -190,6 +190,10 @@ var GameKit = (function () {
function openFan(deckId) {
currentDeckId = deckId;
currentIndex = restorePosition(deckId);
// Always open on the levity-painted face — the FLIP cue. The bookmark
// (sessionStorage) intentionally tracks card index only, not polarity,
// so the styling matches the SSR gravity default until JS repaints.
_polarity = 'levity';
fetch('/gameboard/game-kit/deck/' + deckId + '/')
.then(function (r) { return r.text(); })
@@ -405,10 +409,12 @@ var GameKit = (function () {
_polarity = 'levity';
init();
},
// Test seam: skip fetch by reading already-rendered #id_fan_content
// Test seam: skip fetch by reading already-rendered #id_fan_content.
// Mirrors openFan's polarity reset so reopen-after-FLIP is exercisable.
_testOpen: function () {
cards = Array.from(fanContent.querySelectorAll('.fan-card'));
currentIndex = 0;
_polarity = 'levity';
updateFan();
},
_testNavigate: navigate,

View File

@@ -59,6 +59,13 @@ function initGameKitTooltips() {
});
// buildMiniContent — text-only status; DON/DOFF buttons live in the main portal.
// In-use rows append the room name as "In-Use: <name>", capped at 24 chars
// (overflow → 21 chars + "…"). The room name lives on the token element's
// data-in-use-room-name (decks) or data-current-room-name (trinkets).
function _inUseLabel(roomName) {
const full = 'In-Use: ' + roomName;
return full.length > 24 ? full.slice(0, 21) + '…' : full;
}
function buildMiniContent(token) {
const deckId = token.dataset.deckId;
const tokenId = token.dataset.tokenId;
@@ -67,14 +74,14 @@ function initGameKitTooltips() {
const inUseDeckIds = new Set((gameKit.dataset.inUseDeckIds || '').split(',').filter(Boolean));
if (deckId) {
if (inUseDeckIds.has(deckId)) {
miniPortal.textContent = 'In-Use';
miniPortal.textContent = _inUseLabel(token.dataset.inUseRoomName || '');
} else {
miniPortal.textContent = (equippedDeckId && deckId === equippedDeckId) ? 'Equipped' : 'Not Equipped';
}
} else if (tokenId) {
const currentRoomName = token.dataset.currentRoomName || '';
if (currentRoomName) {
miniPortal.textContent = 'In-Use';
miniPortal.textContent = _inUseLabel(currentRoomName);
} else {
miniPortal.textContent = (equippedId && tokenId === equippedId) ? 'Equipped' : 'Not Equipped';
}

View File

@@ -87,9 +87,9 @@ class GameboardDeckInUseTest(TestCase):
)
self.assertEqual(len(active_doff), 0)
def test_in_use_deck_tooltip_shows_game_name(self):
[label] = self.parsed.cssselect("#id_kit_earthman_deck .tt-deck-game-name")
self.assertIn("Wildfire", label.text_content())
def test_in_use_deck_carries_room_name_for_mini_portal(self):
[el] = self.parsed.cssselect("#id_kit_earthman_deck")
self.assertEqual("Wildfire", el.get("data-in-use-room-name"))
def test_non_in_use_deck_has_normal_don(self):
fiorentine = DeckVariant.objects.get(slug="fiorentine-minchiate")