Selects join the design system at the element; entities decode at the parser
Eric, correctly: "Didn't learn from last time I see :)". The container select in the where-it-lives card was native — because the previous fix styled .editform select, the container, not the element. The rule is now bare `select` (every page here is ours), so the fourth dropdown gets it for free, and the CSS comment records the lesson. Same screenshot, same class of miss: the About card showed raw ’ entities — the export got the double-encoding fix, the app never did. Fixed at the PARSER this time (models.parse_things_full decodes once, so every consumer — app, export, games.json — receives plain text), with a render-time decode covering entries enriched before the fix. Verified in-browser: "Great Britain's history", apostrophe and all. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016jXZFSTZQKzAC8fqpWSz9g
This commit is contained in:
co-authored by
Claude Fable 5
parent
fb611e0c87
commit
f435cffea0
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import html
|
||||
import warnings
|
||||
import xml.etree.ElementTree as ET # parsing itself goes via defusedxml
|
||||
from dataclasses import dataclass, field
|
||||
@@ -228,7 +229,12 @@ def parse_things_full(xml_text: str) -> list[dict]:
|
||||
"type": item.get("type", "boardgame"),
|
||||
"name": name.get("value", "") if name is not None else "",
|
||||
"year": _attr_int(item.find("yearpublished")),
|
||||
"description": (item.findtext("description") or "").strip(),
|
||||
# BGG serves descriptions with entities still encoded
|
||||
# (’ etc): decode ONCE here so every consumer —
|
||||
# app, export, games.json — gets plain text
|
||||
"description": html.unescape(
|
||||
(item.findtext("description") or "").strip()
|
||||
),
|
||||
"image": (item.findtext("image") or "").strip(),
|
||||
"thumbnail": (item.findtext("thumbnail") or "").strip(),
|
||||
"min_players": _attr_int(item.find("minplayers")),
|
||||
|
||||
@@ -528,11 +528,16 @@ a.game:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
#pickfooter .edithint { flex: 1 1 100%; }
|
||||
.editform select {
|
||||
/* selects join the design system EVERYWHERE — the third time a native
|
||||
dropdown showed up in a card, the lesson finally took: style the
|
||||
element, not each container it appears in */
|
||||
select {
|
||||
font: inherit; font-size: .85rem; color: var(--ink);
|
||||
border: 2px solid var(--board-edge); border-radius: var(--radius);
|
||||
padding: .25rem .45rem; background: #fff; max-width: 24rem;
|
||||
}
|
||||
#storedform { display: flex; gap: .5rem; align-items: center; }
|
||||
#storedform select { flex: 1 1 auto; }
|
||||
|
||||
.empty {
|
||||
background: var(--board); border: 2px dashed var(--board-edge);
|
||||
|
||||
@@ -8,6 +8,14 @@
|
||||
"use strict";
|
||||
const KEY = decodeURIComponent(location.pathname.replace(/^\/library\/game\//, ""));
|
||||
|
||||
// entries enriched before descriptions were decoded at parse still hold
|
||||
// ’-style entities: decode once before escaping for display
|
||||
function decodeEntities(s) {
|
||||
const ta = document.createElement("textarea");
|
||||
ta.innerHTML = s;
|
||||
return ta.value;
|
||||
}
|
||||
|
||||
function chips(label, values) {
|
||||
if (!values || !values.length) return "";
|
||||
return `<div class="factrow"><b>${esc(label)}</b>
|
||||
@@ -228,7 +236,7 @@ function render(g) {
|
||||
${contains}
|
||||
${photos}
|
||||
${g.description && !local ? `<h2>About</h2>
|
||||
<div class="card prose"><p class="gdesc">${esc(g.description)}</p></div>` : ""}`;
|
||||
<div class="card prose"><p class="gdesc">${esc(decodeEntities(g.description))}</p></div>` : ""}`;
|
||||
wireLocal(g);
|
||||
wireStored(g);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user