Fix audit frontend MEDs

- score-reader template: load utils.js before theme.js — without
  lnUtils.safeStorage the saved theme/text-size never restored on
  score pages (AUDIT §5.1)
- search-filters: expand trailing-slash pathnames to .../index.html
  before the epistemicMeta lookup; clean-URL pages were silently
  bypassing every active filter (AUDIT §5.2)
- viz: treat cappuccino as a dark theme so charts stop rendering
  near-black marks on a dark brown background (AUDIT §5.3)
- collapse: namespace section-collapsed keys by pathname (Pandoc
  auto-slugs recur across essays) and go through safeStorage like the
  rest of the site (AUDIT §5.4)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Levi Neuwirth 2026-06-10 09:21:47 -04:00
parent 7ca937d98c
commit c64f3d63c0
4 changed files with 20 additions and 5 deletions

View File

@ -9,7 +9,11 @@
(function () { (function () {
'use strict'; 'use strict';
var PREFIX = 'section-collapsed:'; /* Keys are namespaced by pathname: Pandoc auto-slugs (#introduction,
#background) recur across essays, and an un-namespaced key would
collapse the same-named section on every page. */
var PREFIX = 'section-collapsed:' + location.pathname + ':';
var store = window.lnUtils && window.lnUtils.safeStorage;
function initHeading(heading) { function initHeading(heading) {
var level = parseInt(heading.tagName[1], 10); var level = parseInt(heading.tagName[1], 10);
@ -41,7 +45,7 @@
// Restore persisted state without transition flash. // Restore persisted state without transition flash.
var key = PREFIX + heading.id; var key = PREFIX + heading.id;
var collapsed = localStorage.getItem(key) === '1'; var collapsed = store ? store.get(key) === '1' : false;
function setCollapsed(c, animate) { function setCollapsed(c, animate) {
if (!animate) wrapper.style.transition = 'none'; if (!animate) wrapper.style.transition = 'none';
@ -80,7 +84,7 @@
void wrapper.offsetHeight; // force reflow void wrapper.offsetHeight; // force reflow
} }
setCollapsed(!isCollapsed, true); setCollapsed(!isCollapsed, true);
localStorage.setItem(key, isCollapsed ? '0' : '1'); if (store) store.set(key, isCollapsed ? '0' : '1');
}); });
// After open animation: release the height cap so late-rendering // After open animation: release the height cap so late-rendering

View File

@ -113,12 +113,17 @@
/* ---- URL extraction ---- */ /* ---- URL extraction ---- */
/* Normalise a URL to a pathname for lookup in epistemicMeta. /* Normalise a URL to a pathname for lookup in epistemicMeta.
Pagefind results use full URLs; semantic results use relative paths. */ Pagefind results use full URLs; semantic results use relative paths.
epistemicMeta keys are emitted as routed paths (".../index.html"),
while result links use the clean directory form (".../"), so the
trailing-slash form must be expanded before lookup. */
function normUrl(href) { function normUrl(href) {
if (!href) return null; if (!href) return null;
try { try {
var u = new URL(href, window.location.origin); var u = new URL(href, window.location.origin);
return u.pathname; var p = u.pathname;
if (p.charAt(p.length - 1) === '/') p += 'index.html';
return p;
} catch (e) { } catch (e) {
return href; return href;
} }

View File

@ -94,6 +94,9 @@
function isDark() { function isDark() {
var t = document.documentElement.dataset.theme; var t = document.documentElement.dataset.theme;
if (t === 'dark') return true; if (t === 'dark') return true;
/* cappuccino is a dark-brown theme (light text on #553a28) charts
need the dark palette or axis labels become unreadable. */
if (t === 'cappuccino') return true;
if (t === 'light') return false; if (t === 'light') return false;
return window.matchMedia('(prefers-color-scheme: dark)').matches; return window.matchMedia('(prefers-color-scheme: dark)').matches;
} }

View File

@ -7,6 +7,9 @@
<link rel="stylesheet" href="/css/base.css"> <link rel="stylesheet" href="/css/base.css">
<link rel="stylesheet" href="/css/components.css"> <link rel="stylesheet" href="/css/components.css">
<link rel="stylesheet" href="/css/score-reader.css"> <link rel="stylesheet" href="/css/score-reader.css">
<!-- utils.js must precede theme.js: theme.js reads saved settings via
window.lnUtils.safeStorage and silently restores nothing without it. -->
<script src="/js/utils.js"></script>
<script src="/js/theme.js"></script> <script src="/js/theme.js"></script>
</head> </head>
<body class="score-reader-page"> <body class="score-reader-page">