/* Brain of Reese — Theme view module (phase 91, task 05; phase 92, * task 04; COLORS ONLY since phase 190, task 02): the admin palette * editor — the three brand strings moved to the admin Settings panel * (A1: that panel is their single writable owner). * * The phase-76 shell-view-module contract (the tuning.js / tokens.js * shape): the router (assets/router.js) lazy-imports this module on * FIRST show of #view-theme and calls mount(root) ONCE (mount-once, * hide-forever — root is the view's
, every * DOM lookup scoped to it). The initSharedHeader() call never happens * here: in the shell the shared header boots exactly once, via the * chat module (app.js) at shell boot. * * What the editor does: * * • gate — fetchIsAdmin() (the SAME cached /api/whoami promise * header.js exports, zero extra requests): admin hides #theme-gate * and reveals #theme-content; anonymous / token-user keeps the * gate (the #nav-theme link is already hidden by header.js — the * gate is the DIRECT-URL case, the #tokens-gate pattern). No * /api/ui-settings request is ever made outside the admin branch. * • load — GET /api/ui-settings → populate the 17 color inputs with * the EFFECTIVE values (the resolver's DB-over-env / * DB-over-built-in merge): the tab always shows the live theme — * the built-in palette when the row is empty. A failed fetch keeps * the static form (the built-in values ship in the inputs) and * shows #theme-error with a retry (the loadHealth house style — * never a blanked panel). The response still carries the 3 brand * strings (read-only, old-client compat) — populate() simply has no * field to write them into. * • live preview (colors only) — on `input` of any of the 17 color * pickers the value is written straight onto as an inline * custom property, so the WHOLE page repaints (every view, the * header) while the owner is picking. The brand strings are not * this tab's surface at all since phase 190 (A1) — the Settings * panel edits them (and, from phase 190 task 03, applies them to * the open document on save). * • favicon live preview (phase 177, A4 — owner-confirmed * 2026-10-13) — a pick of one of the THREE driving fields * (``surface`` / ``brand`` / ``brand_ink`` — the artwork's * color roles: hexagon fill, hexagon stroke + center dot, * 4 spokes) ALSO repaints the tab icon: the * ``link[rel="icon"]`` href is set to * ``themeFaviconDataUri(...)`` — a ``data:image/svg+xml,`` * URI (``encodeURIComponent``-ed) whose SVG is the * CLIENT-SIDE MIRROR of ``app.core.theming.favicon_svg`` * (the same artwork, the same roles — * ``tests/unit/test_theme_frontend.py`` keeps the two from * diverging: the artwork constants are pinned against the * server source, the ``PAIRS``-mirror house pattern). * Rebuilt, not patched: a ``surface`` pick repaints with the * CURRENT ``brand`` / ``brand_ink`` picks (the helper reads * the three inputs' values). The ``isHex`` guard (belt- * and-braces — the color inputs are always valid hex, the * inputs are the ONLY source of the values, so there is no * XSS path; the house "never build HTML" rule binds the DOM, * the URI is built from constants + validated hexes) returns * ``null`` on a non-hex input and the link is left * untouched. The other 14 picks never touch the link. * RESTORE TO THE ROUTE — the settled palette always wins: on * every SETTLED read (Save success, after the * ``applyServedTheme`` reconcile; Reset success, after the * reconcile; the Reset-failure ``clearPreview()`` path — the * picks are stale; the ``bor:view-refresh`` re-show, after * its ``applyServedTheme``) the href goes back to * ``"/favicon.svg"`` — the route re-reads the DB per request * (NO process cache), so the restored URL is always fresh, * and the page is ``no-cache``, so the browser revalidates * the favicon with it. CSP (phase 123, per D4): ``data:`` is * legal for the favicon via the ``img-src 'self' data:`` * carve-out — CSP3 governs ``link[rel=icon]`` by * ``img-src``, and the carve-out is image-scoped (``data:`` * never becomes a script/style/fetch source). * • served-theme sync (phase 92, defect 1) — the phase-91 defect: * Save/Reset removed the preview overrides, and the page then * fell back to the