From de4ac60aec1e49f97772500c6b49845e6fce97c3 Mon Sep 17 00:00:00 2001 From: Disco DeDisco Date: Wed, 15 Apr 2026 22:16:50 -0400 Subject: [PATCH] tooltips app TDD spike + kit bag refactor to .tt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New apps.tooltips: TooltipContent model, {% tooltip data %} inclusion tag, _tooltip.html partial with .tt/.tt-title/.tt-description etc. class contract; 34 tests green - Kit bag panel (_kit_bag_panel.html): .token-tooltip → .tt + child class renames (tt-title, tt-description, tt-shoptalk, tt-expiry) - game-kit.js attachTooltip: .token-tooltip → .tt selector - SCSS: .tt added alongside .token-tooltip for display:none default + hover rules in _wallet-tokens.scss and _game-kit.scss Co-Authored-By: Claude Sonnet 4.6 --- .../static/apps/dashboard/game-kit.js | 4 +- src/apps/tooltips/__init__.py | 0 src/apps/tooltips/admin.py | 10 + src/apps/tooltips/apps.py | 6 + src/apps/tooltips/migrations/0001_initial.py | 33 ++++ src/apps/tooltips/migrations/__init__.py | 0 src/apps/tooltips/models.py | 38 ++++ src/apps/tooltips/templatetags/__init__.py | 0 .../tooltips/templatetags/tooltip_tags.py | 31 ++++ src/apps/tooltips/tests/__init__.py | 0 .../tooltips/tests/integrated/__init__.py | 0 .../tooltips/tests/integrated/test_models.py | 127 +++++++++++++ .../tests/integrated/test_templatetags.py | 172 ++++++++++++++++++ src/core/settings.py | 1 + src/functional_tests/test_game_kit.py | 2 +- .../test_trinket_carte_blanche.py | 4 +- src/static_src/scss/_game-kit.scss | 6 +- src/static_src/scss/_wallet-tokens.scss | 6 +- src/templates/apps/tooltips/_tooltip.html | 69 +++++++ .../core/_partials/_kit_bag_panel.html | 40 ++-- 20 files changed, 520 insertions(+), 29 deletions(-) create mode 100644 src/apps/tooltips/__init__.py create mode 100644 src/apps/tooltips/admin.py create mode 100644 src/apps/tooltips/apps.py create mode 100644 src/apps/tooltips/migrations/0001_initial.py create mode 100644 src/apps/tooltips/migrations/__init__.py create mode 100644 src/apps/tooltips/models.py create mode 100644 src/apps/tooltips/templatetags/__init__.py create mode 100644 src/apps/tooltips/templatetags/tooltip_tags.py create mode 100644 src/apps/tooltips/tests/__init__.py create mode 100644 src/apps/tooltips/tests/integrated/__init__.py create mode 100644 src/apps/tooltips/tests/integrated/test_models.py create mode 100644 src/apps/tooltips/tests/integrated/test_templatetags.py create mode 100644 src/templates/apps/tooltips/_tooltip.html diff --git a/src/apps/dashboard/static/apps/dashboard/game-kit.js b/src/apps/dashboard/static/apps/dashboard/game-kit.js index 36cbaeb..4ad35eb 100644 --- a/src/apps/dashboard/static/apps/dashboard/game-kit.js +++ b/src/apps/dashboard/static/apps/dashboard/game-kit.js @@ -60,7 +60,7 @@ function attachTooltip(el) { el.addEventListener('mouseenter', function () { - var tooltip = el.querySelector('.token-tooltip'); + var tooltip = el.querySelector('.tt'); if (!tooltip) return; var rect = el.getBoundingClientRect(); tooltip.style.position = 'fixed'; @@ -69,7 +69,7 @@ tooltip.style.display = 'block'; }); el.addEventListener('mouseleave', function () { - var tooltip = el.querySelector('.token-tooltip'); + var tooltip = el.querySelector('.tt'); if (tooltip) tooltip.style.display = ''; }); } diff --git a/src/apps/tooltips/__init__.py b/src/apps/tooltips/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/apps/tooltips/admin.py b/src/apps/tooltips/admin.py new file mode 100644 index 0000000..4dc8d77 --- /dev/null +++ b/src/apps/tooltips/admin.py @@ -0,0 +1,10 @@ +from django.contrib import admin + +from .models import TooltipContent + + +@admin.register(TooltipContent) +class TooltipContentAdmin(admin.ModelAdmin): + list_display = ('slug', 'title', 'type_label', 'symbol') + search_fields = ('slug', 'title', 'type_label') + list_filter = ('type_label',) diff --git a/src/apps/tooltips/apps.py b/src/apps/tooltips/apps.py new file mode 100644 index 0000000..17fabbd --- /dev/null +++ b/src/apps/tooltips/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class TooltipsConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apps.tooltips' diff --git a/src/apps/tooltips/migrations/0001_initial.py b/src/apps/tooltips/migrations/0001_initial.py new file mode 100644 index 0000000..877588a --- /dev/null +++ b/src/apps/tooltips/migrations/0001_initial.py @@ -0,0 +1,33 @@ +# Generated by Django 6.0 on 2026-04-16 00:38 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='TooltipContent', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('slug', models.SlugField(unique=True)), + ('title', models.CharField(max_length=200)), + ('type_label', models.CharField(blank=True, max_length=100)), + ('symbol', models.CharField(blank=True, max_length=10)), + ('degree_str', models.CharField(blank=True, max_length=40)), + ('description', models.TextField(blank=True)), + ('shoptalk', models.TextField(blank=True)), + ('expiry', models.CharField(blank=True, max_length=200)), + ('effect', models.TextField(blank=True)), + ('extras', models.JSONField(default=dict)), + ], + options={ + 'ordering': ['slug'], + }, + ), + ] diff --git a/src/apps/tooltips/migrations/__init__.py b/src/apps/tooltips/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/apps/tooltips/models.py b/src/apps/tooltips/models.py new file mode 100644 index 0000000..87a28a7 --- /dev/null +++ b/src/apps/tooltips/models.py @@ -0,0 +1,38 @@ +from django.db import models + + +class TooltipContent(models.Model): + """ + Static tooltip content for game objects — planets, signs, houses, cards, etc. + + Required: slug (unique lookup key), title (display name). + All other fields are optional; omitted fields render nothing in the template. + + extras (JSONField) stores structured data that varies by tooltip type: + dignities — dict {role: body/sign string, …} + aspects — list [{symbol, type, body, orb}, …] + keywords_up / keywords_rev — list of strings (card upright/reversed keywords) + cautions — list [{title, type_label, shoptalk, effect}, …] + nav — dict {prv: slug, nxt: slug} for PRV/NXT navigation + """ + + slug = models.SlugField(unique=True) + title = models.CharField(max_length=200) + + # Optional display fields + type_label = models.CharField(max_length=100, blank=True) + symbol = models.CharField(max_length=10, blank=True) + degree_str = models.CharField(max_length=40, blank=True) + description = models.TextField(blank=True) + shoptalk = models.TextField(blank=True) + expiry = models.CharField(max_length=200, blank=True) + effect = models.TextField(blank=True) + + # Structured data for tabular / list / nav sections + extras = models.JSONField(default=dict) + + class Meta: + ordering = ['slug'] + + def __str__(self): + return self.title diff --git a/src/apps/tooltips/templatetags/__init__.py b/src/apps/tooltips/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/apps/tooltips/templatetags/tooltip_tags.py b/src/apps/tooltips/templatetags/tooltip_tags.py new file mode 100644 index 0000000..c2f9efe --- /dev/null +++ b/src/apps/tooltips/templatetags/tooltip_tags.py @@ -0,0 +1,31 @@ +from django import template + +register = template.Library() + + +@register.inclusion_tag('apps/tooltips/_tooltip.html') +def tooltip(data): + """Render the unified tooltip partial from a data dict. + + Minimum required key: 'title'. + All other keys are optional; missing keys render nothing. + extras (dict) may contain: dignities, aspects, keywords_up, + keywords_rev, cautions, nav. + """ + extras = data.get('extras', {}) or {} + return { + 'title': data.get('title', ''), + 'type_label': data.get('type_label', ''), + 'symbol': data.get('symbol', ''), + 'degree_str': data.get('degree_str', ''), + 'description': data.get('description', ''), + 'shoptalk': data.get('shoptalk', ''), + 'expiry': data.get('expiry', ''), + 'effect': data.get('effect', ''), + 'dignities': extras.get('dignities'), + 'aspects': extras.get('aspects'), + 'keywords_up': extras.get('keywords_up'), + 'keywords_rev':extras.get('keywords_rev'), + 'cautions': extras.get('cautions'), + 'nav': extras.get('nav'), + } diff --git a/src/apps/tooltips/tests/__init__.py b/src/apps/tooltips/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/apps/tooltips/tests/integrated/__init__.py b/src/apps/tooltips/tests/integrated/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/apps/tooltips/tests/integrated/test_models.py b/src/apps/tooltips/tests/integrated/test_models.py new file mode 100644 index 0000000..d3c3123 --- /dev/null +++ b/src/apps/tooltips/tests/integrated/test_models.py @@ -0,0 +1,127 @@ +from django.test import TestCase +from django.db import IntegrityError + +from apps.tooltips.models import TooltipContent + + +class TooltipContentModelTest(TestCase): + + # ── Required fields ─────────────────────────────────────────────────────── + + def test_can_create_with_slug_and_title_only(self): + tt = TooltipContent.objects.create(slug='sun', title='Sun') + self.assertEqual(TooltipContent.objects.count(), 1) + self.assertEqual(tt.slug, 'sun') + self.assertEqual(tt.title, 'Sun') + + def test_slug_is_unique(self): + TooltipContent.objects.create(slug='sun', title='Sun') + with self.assertRaises(IntegrityError): + TooltipContent.objects.create(slug='sun', title='Another Sun') + + def test_str_returns_title(self): + tt = TooltipContent(slug='moon', title='Moon') + self.assertEqual(str(tt), 'Moon') + + # ── Optional text fields ────────────────────────────────────────────────── + + def test_optional_text_fields_default_blank(self): + tt = TooltipContent.objects.create(slug='mars', title='Mars') + for field in ('type_label', 'symbol', 'degree_str', + 'description', 'shoptalk', 'expiry', 'effect'): + self.assertEqual(getattr(tt, field), '', msg=f'{field} should default blank') + + def test_can_set_all_text_fields(self): + tt = TooltipContent.objects.create( + slug='jupiter', + title='Jupiter', + type_label='Planet', + symbol='♃', + degree_str='14° 22′ Scorpio', + description='Planet of expansion and fortune.', + shoptalk='Ruler of Sagittarius.', + expiry='', + effect='Amplifies whichever house it occupies.', + ) + self.assertEqual(tt.type_label, 'Planet') + self.assertEqual(tt.symbol, '♃') + self.assertEqual(tt.degree_str, '14° 22′ Scorpio') + + # ── extras JSONField ────────────────────────────────────────────────────── + + def test_extras_defaults_to_empty_dict(self): + tt = TooltipContent.objects.create(slug='venus', title='Venus') + self.assertEqual(tt.extras, {}) + + def test_extras_stores_dignities(self): + tt = TooltipContent.objects.create( + slug='saturn', + title='Saturn', + extras={ + 'dignities': { + 'Domicile': 'Capricorn / Aquarius', + 'Exalted': 'Libra', + 'Exile': 'Cancer / Leo', + 'Fallen': 'Aries', + }, + }, + ) + tt.refresh_from_db() + self.assertEqual(tt.extras['dignities']['Exalted'], 'Libra') + + def test_extras_stores_aspects(self): + tt = TooltipContent.objects.create( + slug='mercury', + title='Mercury', + extras={ + 'aspects': [ + {'symbol': '△', 'type': 'Trine', 'body': 'Venus', 'orb': '2° 14′'}, + {'symbol': '□', 'type': 'Square', 'body': 'Mars', 'orb': '0° 42′'}, + ], + }, + ) + tt.refresh_from_db() + self.assertEqual(len(tt.extras['aspects']), 2) + self.assertEqual(tt.extras['aspects'][0]['type'], 'Trine') + + def test_extras_stores_keywords(self): + tt = TooltipContent.objects.create( + slug='the-schizo', + title='The Schizo, Leavened', + extras={ + 'keywords_up': ['willpower', 'skill', 'resourcefulness'], + 'keywords_rev': ['hubris', 'overreach'], + }, + ) + tt.refresh_from_db() + self.assertIn('willpower', tt.extras['keywords_up']) + + def test_extras_stores_cautions(self): + tt = TooltipContent.objects.create( + slug='the-schizo-cautions', + title='The Schizo, Leavened', + extras={ + 'cautions': [ + { + 'title': 'Caution!', + 'type_label': 'Rival Interaction', + 'shoptalk': '[Shoptalk forthcoming]', + 'effect': 'This card will reverse into I. The Pervert...', + }, + ], + }, + ) + tt.refresh_from_db() + self.assertEqual(tt.extras['cautions'][0]['type_label'], 'Rival Interaction') + + def test_extras_stores_nav(self): + """PRV/NXT nav dict for paired or sequenced tooltips.""" + tt = TooltipContent.objects.create( + slug='house-01', + title='House of Self', + extras={ + 'nav': {'prv': 'house-12', 'nxt': 'house-02'}, + }, + ) + tt.refresh_from_db() + self.assertEqual(tt.extras['nav']['nxt'], 'house-02') diff --git a/src/apps/tooltips/tests/integrated/test_templatetags.py b/src/apps/tooltips/tests/integrated/test_templatetags.py new file mode 100644 index 0000000..7d520b5 --- /dev/null +++ b/src/apps/tooltips/tests/integrated/test_templatetags.py @@ -0,0 +1,172 @@ +from django.template import Context, Template +from django.test import TestCase + + +def render_tooltip(data): + """Helper: render the {% tooltip %} tag with the given data dict.""" + tpl = Template("{% load tooltip_tags %}{% tooltip data %}") + return tpl.render(Context({'data': data})) + + +class TooltipTagRequiredFieldsTest(TestCase): + + def test_renders_title(self): + html = render_tooltip({'title': 'Sun'}) + self.assertIn('Sun', html) + self.assertIn('class="tt-title"', html) + + def test_wrapper_has_tt_class(self): + html = render_tooltip({'title': 'Moon'}) + self.assertIn('class="tt"', html) + + +class TooltipTagOptionalTextFieldsTest(TestCase): + + def test_renders_type_label_when_present(self): + html = render_tooltip({'title': 'Sun', 'type_label': 'Planet'}) + self.assertIn('Planet', html) + self.assertIn('tt-type', html) + + def test_omits_type_label_when_absent(self): + html = render_tooltip({'title': 'Sun'}) + self.assertNotIn('tt-type', html) + + def test_renders_symbol_when_present(self): + html = render_tooltip({'title': 'Sun', 'symbol': '☉'}) + self.assertIn('☉', html) + self.assertIn('tt-symbol', html) + + def test_omits_symbol_when_absent(self): + html = render_tooltip({'title': 'Sun'}) + self.assertNotIn('tt-symbol', html) + + def test_renders_degree_str_when_present(self): + html = render_tooltip({'title': 'Sun', 'degree_str': '14° 22′ Scorpio'}) + self.assertIn('14° 22′ Scorpio', html) + self.assertIn('tt-degree', html) + + def test_omits_degree_str_when_absent(self): + html = render_tooltip({'title': 'Sun'}) + self.assertNotIn('tt-degree', html) + + def test_renders_description_when_present(self): + html = render_tooltip({'title': 'Sun', 'description': 'Planet of vitality.'}) + self.assertIn('Planet of vitality.', html) + self.assertIn('tt-description', html) + + def test_renders_shoptalk_in_em_when_present(self): + html = render_tooltip({'title': 'Sun', 'shoptalk': 'Ruler of Leo.'}) + self.assertIn('Ruler of Leo.', html) + self.assertIn('tt-shoptalk', html) + self.assertIn('', html) + + def test_renders_expiry_when_present(self): + html = render_tooltip({'title': 'Pass', 'expiry': 'no expiry'}) + self.assertIn('no expiry', html) + self.assertIn('tt-expiry', html) + + def test_renders_effect_when_present(self): + html = render_tooltip({'title': 'Pass', 'effect': 'Admit All Entry'}) + self.assertIn('Admit All Entry', html) + self.assertIn('tt-effect', html) + + +class TooltipTagExtrasTest(TestCase): + + def test_renders_dignities_table_when_present(self): + data = { + 'title': 'Saturn', + 'extras': { + 'dignities': { + 'Domicile': 'Capricorn', + 'Exalted': 'Libra', + }, + }, + } + html = render_tooltip(data) + self.assertIn('tt-table--dignities', html) + self.assertIn('Capricorn', html) + self.assertIn('Exalted', html) + + def test_omits_dignities_when_absent(self): + html = render_tooltip({'title': 'Sun', 'extras': {}}) + self.assertNotIn('tt-table--dignities', html) + + def test_renders_aspects_list_when_present(self): + data = { + 'title': 'Mercury', + 'extras': { + 'aspects': [ + {'symbol': '△', 'type': 'Trine', 'body': 'Venus', 'orb': '2° 14′'}, + ], + }, + } + html = render_tooltip(data) + self.assertIn('tt-aspects', html) + self.assertIn('Trine', html) + self.assertIn('Venus', html) + + def test_omits_aspects_when_absent(self): + html = render_tooltip({'title': 'Sun', 'extras': {}}) + self.assertNotIn('tt-aspects', html) + + def test_renders_keyword_lists_when_present(self): + data = { + 'title': 'The Schizo', + 'extras': { + 'keywords_up': ['willpower', 'skill'], + 'keywords_rev': ['hubris'], + }, + } + html = render_tooltip(data) + self.assertIn('tt-keywords', html) + self.assertIn('willpower', html) + self.assertIn('hubris', html) + + def test_omits_keywords_when_absent(self): + html = render_tooltip({'title': 'Sun', 'extras': {}}) + self.assertNotIn('tt-keywords', html) + + def test_renders_fyi_cautions_section_when_present(self): + data = { + 'title': 'The Schizo', + 'extras': { + 'cautions': [ + { + 'title': 'Caution!', + 'type_label': 'Rival Interaction', + 'shoptalk': '[Shoptalk forthcoming]', + 'effect': 'This card will reverse...', + }, + ], + }, + } + html = render_tooltip(data) + self.assertIn('tt-fyi', html) + self.assertIn('tt-fyi--cautions', html) + self.assertIn('Rival Interaction', html) + + def test_omits_cautions_section_when_absent(self): + html = render_tooltip({'title': 'Sun', 'extras': {}}) + self.assertNotIn('tt-fyi--cautions', html) + + def test_renders_nav_when_present(self): + data = { + 'title': 'House of Self', + 'extras': { + 'nav': {'prv': 'house-12', 'nxt': 'house-02'}, + }, + } + html = render_tooltip(data) + self.assertIn('tt-nav', html) + self.assertIn('house-02', html) + self.assertIn('house-12', html) + + def test_omits_nav_when_absent(self): + html = render_tooltip({'title': 'Sun', 'extras': {}}) + self.assertNotIn('tt-nav', html) + + def test_no_extras_key_does_not_error(self): + """Tag should handle data with no 'extras' key gracefully.""" + html = render_tooltip({'title': 'Sun'}) + self.assertIn('Sun', html) diff --git a/src/core/settings.py b/src/core/settings.py index b09617e..3b87a9b 100644 --- a/src/core/settings.py +++ b/src/core/settings.py @@ -63,6 +63,7 @@ INSTALLED_APPS = [ 'apps.epic', 'apps.drama', # Custom apps + 'apps.tooltips', 'apps.ap', 'apps.api', 'apps.applets', diff --git a/src/functional_tests/test_game_kit.py b/src/functional_tests/test_game_kit.py index 935ba7e..66222c0 100644 --- a/src/functional_tests/test_game_kit.py +++ b/src/functional_tests/test_game_kit.py @@ -105,7 +105,7 @@ class GameKitTest(FunctionalTest): "arguments[0].dispatchEvent(new Event('mouseenter'))", deck_el ) tooltip = self.browser.find_element( - By.CSS_SELECTOR, "#id_kit_bag_dialog .kit-bag-deck .token-tooltip" + By.CSS_SELECTOR, "#id_kit_bag_dialog .kit-bag-deck .tt" ) self.wait_for(lambda: self.assertTrue(tooltip.is_displayed())) text = tooltip.text diff --git a/src/functional_tests/test_trinket_carte_blanche.py b/src/functional_tests/test_trinket_carte_blanche.py index 74e54a0..d6430ad 100644 --- a/src/functional_tests/test_trinket_carte_blanche.py +++ b/src/functional_tests/test_trinket_carte_blanche.py @@ -235,7 +235,7 @@ class CarteBlancheTest(FunctionalTest): # relying on hover visibility in headless Firefox. self.assertIn( "The Long Room", - carte_in_bag.find_element(By.CSS_SELECTOR, ".token-tooltip").get_attribute("textContent"), + carte_in_bag.find_element(By.CSS_SELECTOR, ".tt").get_attribute("textContent"), ) # Close kit bag self.browser.find_element(By.ID, "id_kit_btn").click() @@ -265,7 +265,7 @@ class CarteBlancheTest(FunctionalTest): ) self.assertNotIn( "The Long Room", - carte_in_bag.find_element(By.CSS_SELECTOR, ".token-tooltip").get_attribute("textContent"), + carte_in_bag.find_element(By.CSS_SELECTOR, ".tt").get_attribute("textContent"), ) self.browser.find_element(By.ID, "id_kit_btn").click() diff --git a/src/static_src/scss/_game-kit.scss b/src/static_src/scss/_game-kit.scss index 0c8c299..072343c 100644 --- a/src/static_src/scss/_game-kit.scss +++ b/src/static_src/scss/_game-kit.scss @@ -105,10 +105,12 @@ transition: filter 0.15s; padding: 0 0.125rem; - &:hover .token-tooltip { display: none; } // JS positions these as fixed + &:hover .token-tooltip, + &:hover .tt { display: none; } // JS positions these as fixed } - .token-tooltip { + .token-tooltip, + .tt { z-index: 9999; } diff --git a/src/static_src/scss/_wallet-tokens.scss b/src/static_src/scss/_wallet-tokens.scss index 57af2ba..0f3aa35 100644 --- a/src/static_src/scss/_wallet-tokens.scss +++ b/src/static_src/scss/_wallet-tokens.scss @@ -1,4 +1,5 @@ -.token-tooltip { +.token-tooltip, +.tt { display: none; width: 16rem; max-width: 16rem; @@ -66,7 +67,8 @@ transform: translateX(-50%); } - &:hover .token-tooltip { + &:hover .token-tooltip, + &:hover .tt { display: block; } } diff --git a/src/templates/apps/tooltips/_tooltip.html b/src/templates/apps/tooltips/_tooltip.html new file mode 100644 index 0000000..c20edfb --- /dev/null +++ b/src/templates/apps/tooltips/_tooltip.html @@ -0,0 +1,69 @@ +
+ +

{{ title }}

+ + {% if type_label %}

{{ type_label }}

{% endif %} + + {% if symbol %}{{ symbol }}{% endif %} + + {% if degree_str %}

{{ degree_str }}

{% endif %} + + {% if description %}

{{ description }}

{% endif %} + + {% if shoptalk %}

{{ shoptalk }}

{% endif %} + + {% if expiry %}

{{ expiry }}

{% endif %} + + {% if effect %}

{{ effect }}

{% endif %} + + {% if dignities %} + + {% for key, value in dignities.items %} + + {% endfor %} +
{{ key }}{{ value }}
+ {% endif %} + + {% if aspects %} +
    + {% for asp in aspects %} +
  • {{ asp.symbol }} {{ asp.type }} {{ asp.body }} {{ asp.orb }}
  • + {% endfor %} +
+ {% endif %} + + {% if keywords_up or keywords_rev %} +
+ {% if keywords_up %} +
    + {% for kw in keywords_up %}
  • {{ kw }}
  • {% endfor %} +
+ {% endif %} + {% if keywords_rev %} +
    + {% for kw in keywords_rev %}
  • {{ kw }}
  • {% endfor %} +
+ {% endif %} +
+ {% endif %} + + {% if cautions %} +
+ {% for caution in cautions %} +
+ {% if caution.type_label %}

{{ caution.type_label }}

{% endif %} + {% if caution.shoptalk %}

{{ caution.shoptalk }}

{% endif %} + {% if caution.effect %}

{{ caution.effect }}

{% endif %} +
+ {% endfor %} +
+ {% endif %} + + {% if nav %} +
+ {{ nav.prv }} + {{ nav.nxt }} +
+ {% endif %} + +
diff --git a/src/templates/core/_partials/_kit_bag_panel.html b/src/templates/core/_partials/_kit_bag_panel.html index f4031e3..e07c169 100644 --- a/src/templates/core/_partials/_kit_bag_panel.html +++ b/src/templates/core/_partials/_kit_bag_panel.html @@ -4,12 +4,12 @@
-
-

{{ equipped_deck.name }}{% if equipped_deck.is_default %} (Default){% endif %}

-

{{ equipped_deck.card_count }}-card Tarot deck

- placeholder comment -

active

-

Stock version

+
+

{{ equipped_deck.name }}{% if equipped_deck.is_default %} (Default){% endif %}

+

{{ equipped_deck.card_count }}-card Tarot deck

+

placeholder comment

+

active

+

Stock version

@@ -43,13 +43,13 @@ {% else %} {% endif %} -
-

{{ token.tooltip_name }}

-

{{ token.tooltip_description }}

+
+

{{ token.tooltip_name }}

+

{{ token.tooltip_description }}

{% if token.tooltip_shoptalk %} - {{ token.tooltip_shoptalk }} +

{{ token.tooltip_shoptalk }}

{% endif %} -

{{ token.tooltip_expiry }}

+

{{ token.tooltip_expiry }}

{% with room_html=token.tooltip_room_html %} {% if room_html %}{{ room_html|safe }}{% endif %} {% endwith %} @@ -72,13 +72,13 @@ data-token-type="{{ free_token.token_type }}" > -
-

{{ free_token.tooltip_name }}{% if free_count > 1 %} (×{{ free_count }}){% endif %}

-

{{ free_token.tooltip_description }}

+
+

{{ free_token.tooltip_name }}{% if free_count > 1 %} (×{{ free_count }}){% endif %}

+

{{ free_token.tooltip_description }}

{% if free_token.tooltip_shoptalk %} - {{ free_token.tooltip_shoptalk }} +

{{ free_token.tooltip_shoptalk }}

{% endif %} -

{{ free_token.tooltip_expiry }}

+

{{ free_token.tooltip_expiry }}

{% endif %} @@ -90,10 +90,10 @@ data-token-type="{{ tithe_token.token_type }}" > -
-

{{ tithe_token.tooltip_name }}{% if tithe_count > 1 %} (×{{ tithe_count }}){% endif %}

-

{{ tithe_token.tooltip_description }}

-

{{ tithe_token.tooltip_expiry }}

+
+

{{ tithe_token.tooltip_name }}{% if tithe_count > 1 %} (×{{ tithe_count }}){% endif %}

+

{{ tithe_token.tooltip_description }}

+

{{ tithe_token.tooltip_expiry }}

{% endif %}