note palette modal: NVM closes confirm only; confirm visibility via style.display; swatch labels from _PALETTE_DEFS; recognitions block — TDD
Some checks failed
ci/woodpecker/push/pyswiss Pipeline was successful
ci/woodpecker/push/main Pipeline failed

- note-page.js: NVM hides confirm (style.display='none') — swatch modal stays open;
  _showConfirm/_hideConfirm use style.display to bypass CSS specificity issues;
  _doSetPalette reads data-palette-label before modal closes; appends
  .note-recognitions__palette-line w. dim+bold markup after OK
- billboard/views.py: import _PALETTE_DEFS; _PALETTE_LABELS dict; _palette_opts()
  enriches palette_options w. {name, label}; my_notes adds palette_label to note_items
- _note.scss: confirmed palette swatch uses gradient (palette vars cascade from
  palette-* class); hardcoded bardo/sheol bg overrides removed; .note-recognitions block
  w. .note-recognitions__header (tt-sign-section-header style) & __dim (tt-dim style);
  .note-swatch-label in terUser bold; .note-item__palette gradient; confirm display:none default
- my_notes.html: p.name/p.label replaces slice hack; data-palette-label on swatch rows;
  Recognitions block w. dim spans & strong values; removes hidden attr from confirm
- IT: test_palette_modal_renders_swatch_labels; test_also_saves_user_palette
- FT: NVM test corrected — modal stays open, confirm is_displayed() False; T2a URL fix

Code architected by Disco DeDisco <discodedisco@outlook.com>
Git commit message Co-Authored-By:
Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Disco DeDisco
2026-04-23 01:31:19 -04:00
parent cd5252c185
commit 7d4389a74a
6 changed files with 131 additions and 41 deletions

View File

@@ -37,6 +37,16 @@
return m ? m[1] : '';
}
function _showConfirm(modal) {
var el = modal && modal.querySelector('.note-palette-confirm');
if (el) el.style.display = 'flex';
}
function _hideConfirm(modal) {
var el = modal && modal.querySelector('.note-palette-confirm');
if (el) el.style.display = 'none';
}
// ── modal lifecycle ───────────────────────────────────────────────────────
function _openModal() {
@@ -49,8 +59,7 @@
_wireModal();
}
_activeItem.classList.add('note-item--active');
var confirmEl = _activeModal().querySelector('.note-palette-confirm');
if (confirmEl) confirmEl.hidden = true;
_hideConfirm(_activeModal());
}
function _closeModal() {
@@ -68,14 +77,12 @@
clearTimeout(_dismissTimer);
_dismissTimer = null;
_revertBodyPalette();
// Remove .previewing from any swatch in the active modal
var modal = _activeModal();
if (modal) {
modal.querySelectorAll('.note-swatch-body.previewing').forEach(function (s) {
s.classList.remove('previewing');
});
var confirmEl = modal.querySelector('.note-palette-confirm');
if (confirmEl) confirmEl.hidden = true;
_hideConfirm(modal);
}
_selectedPalette = null;
_originalPalette = null;
@@ -90,7 +97,6 @@
modal.querySelectorAll('.note-swatch-body').forEach(function (body) {
body.addEventListener('click', function (e) {
e.stopPropagation();
// Clear any in-progress preview first
if (_selectedPalette) _revertPreview();
_selectedPalette = _paletteClass(body.parentElement);
@@ -98,11 +104,8 @@
body.classList.add('previewing');
_swapBodyPalette(_selectedPalette);
_showConfirm(modal);
var confirmEl = modal.querySelector('.note-palette-confirm');
if (confirmEl) confirmEl.hidden = false;
// Auto-revert after 10s
_dismissTimer = setTimeout(function () {
_revertPreview();
}, 10000);
@@ -117,7 +120,7 @@
});
});
// Confirm NVM → revert preview, hide confirm
// Confirm NVM → revert preview only; main swatch modal stays open
modal.querySelectorAll('.note-palette-confirm .btn.btn-cancel').forEach(function (btn) {
btn.addEventListener('click', function (e) {
e.stopPropagation();
@@ -135,7 +138,11 @@
var url = _activeItem.dataset.setPaletteUrl;
var palette = _selectedPalette;
var item = _activeItem;
// Body already shows new palette from preview — keep it by not reverting.
// Read label from swatch row while modal is still in DOM
var swatchRow = _activeModal() && _activeModal().querySelector('.' + palette + '[data-palette-label]');
var paletteLabel = swatchRow
? swatchRow.dataset.paletteLabel
: palette.slice(8).replace(/^\w/, function (c) { return c.toUpperCase(); });
fetch(url, {
method: 'POST',
credentials: 'same-origin',
@@ -154,13 +161,19 @@
swatch.className = 'note-item__palette ' + palette;
imageBox.parentNode.replaceChild(swatch, imageBox);
}
var list = item.querySelector('.note-recognitions__list');
if (list && !item.querySelector('.note-recognitions__palette-line')) {
var li = document.createElement('li');
li.className = 'note-recognitions__palette-line';
li.innerHTML = '<span class="note-recognitions__dim">Palette:</span> <strong>' + paletteLabel + '</strong>';
list.appendChild(li);
}
});
}
// ── init ──────────────────────────────────────────────────────────────────
function _init() {
// Image-box click → open modal
document.querySelectorAll('.note-item__image-box').forEach(function (box) {
box.addEventListener('click', function (e) {
e.stopPropagation();
@@ -169,7 +182,7 @@
});
});
// Body click → dismiss modal (and revert any preview)
// Body click → dismiss modal and revert any preview
document.body.addEventListener('click', function () {
if (_selectedPalette) _revertPreview();
_closeModal();

View File

@@ -214,6 +214,17 @@ class NotePageViewTest(TestCase):
self.assertContains(response, 'class="note-item__description"')
self.assertContains(response, 'class="note-item__image-box"')
def test_palette_modal_renders_swatch_labels(self):
"""Each palette option in the swatch modal should display its human-readable
label next to the swatch body so the user knows what they are choosing."""
Note.objects.create(
user=self.user, slug="stargazer", earned_at=timezone.now()
)
response = self.client.get("/billboard/my-notes/")
self.assertContains(response, 'class="note-swatch-label"')
self.assertContains(response, "Bardo")
self.assertContains(response, "Sheol")
class NoteSetPaletteViewTest(TestCase):
def setUp(self):

View File

@@ -8,10 +8,13 @@ from django.shortcuts import redirect, render
from apps.applets.utils import applet_context, apply_applet_toggle
from apps.dashboard.forms import LineForm
from apps.dashboard.models import Post
from apps.dashboard.views import _PALETTE_DEFS
from apps.drama.models import GameEvent, Note, ScrollPosition
from apps.epic.models import Room
from apps.epic.utils import rooms_for_user
_PALETTE_LABELS = {p["name"]: p["label"] for p in _PALETTE_DEFS}
def _recent_posts(user, limit=3):
return (
@@ -88,11 +91,15 @@ def room_scroll(request, room_id):
})
def _palette_opts(names):
return [{"name": n, "label": _PALETTE_LABELS.get(n, n)} for n in names]
_NOTE_META = {
"stargazer": {
"title": "Stargazer",
"description": "You saved your first personal sky chart.",
"palette_options": ["palette-bardo", "palette-sheol"],
"palette_options": _palette_opts(["palette-bardo", "palette-sheol"]),
},
"schizo": {
"title": "Schizo",
@@ -136,6 +143,7 @@ def my_notes(request):
"title": _NOTE_META.get(n.slug, {}).get("title", n.slug),
"description": _NOTE_META.get(n.slug, {}).get("description", ""),
"palette_options": _NOTE_META.get(n.slug, {}).get("palette_options", []),
"palette_label": _PALETTE_LABELS.get(n.palette, "") if n.palette else "",
}
for n in qs
]