Files
olivv-cs-ComfyUI-FunPack/app/shell/theme.js
T
digital-garbage fd0a05d982 composer: the token layer, and a catalogue that reads it
Cleaned scales from v4's measured usage: 11 control heights to 4, 19 radii to 6,
9px named as a real type step, one reduced-motion guard because every duration
is a token now.

Derived tokens are declared on [data-theme] as well as :root -- a custom
property's var()s are substituted where it is declared, so on :root alone a
scoped light subtree inherits the dark accent.

The catalogue reads the tokens out of the stylesheet instead of listing them, so
it cannot drift; anything it has no renderer for shows up as Ungrouped.
2026-08-28 14:19:02 +03:00

85 lines
2.8 KiB
JavaScript

// Colour scheme: dark (the original cutting-room palette), light, or auto.
//
// "auto" is resolved HERE rather than in CSS: a @media (prefers-color-scheme)
// block cannot be reused across the two selectors it would need, so the palette
// would have to be written twice and would drift. The stylesheet only ever sees
// data-theme="dark" or "light"; this file decides which.
//
// Loaded before every other script (and re-applied inline in <head>) so the
// first paint is already in the right theme — a dark flash on a light setup is
// the one bug a theme switcher cannot apologise for.
(function () {
const LS_KEY = "funpack_theme";
const CHOICES = ["dark", "light", "auto"];
const listeners = new Set();
let mq = null;
function stored() {
try {
const v = localStorage.getItem(LS_KEY);
return CHOICES.includes(v) ? v : "dark";
} catch (_) {
return "dark";
}
}
function systemIsLight() {
return !!(window.matchMedia && window.matchMedia("(prefers-color-scheme: light)").matches);
}
function resolve(choice) {
return choice === "auto" ? (systemIsLight() ? "light" : "dark") : choice;
}
// data-theme is what the stylesheet reads; data-theme-pref is the choice behind it
// ("auto" resolves to one of the other two). The theme changes on an explicit click
// and nothing else, so these two always move together.
function stamp(choice) {
const resolved = resolve(choice);
document.documentElement.setAttribute("data-theme", resolved);
document.documentElement.setAttribute("data-theme-pref", choice);
return resolved;
}
// Only while the preference is "auto" does the OS get a vote; a user who picked
// light explicitly should not flip at sunset.
function watchSystem(choice) {
if (!window.matchMedia) return;
if (!mq) {
mq = window.matchMedia("(prefers-color-scheme: light)");
const onChange = () => { if (get() === "auto") apply("auto"); };
if (mq.addEventListener) mq.addEventListener("change", onChange);
else if (mq.addListener) mq.addListener(onChange);
}
void choice;
}
function get() {
return document.documentElement.getAttribute("data-theme-pref") || stored();
}
function resolved() {
return document.documentElement.getAttribute("data-theme") || "dark";
}
function apply(choice, { persist = true } = {}) {
if (!CHOICES.includes(choice)) choice = "dark";
const res = stamp(choice);
if (persist) {
try { localStorage.setItem(LS_KEY, choice); } catch (_) {}
}
watchSystem(choice);
listeners.forEach((fn) => { try { fn(choice, res); } catch (_) {} });
return res;
}
function onChange(fn) {
listeners.add(fn);
return () => listeners.delete(fn);
}
apply(stored(), { persist: false });
window.ComposerTheme = { get, resolved, apply, onChange, CHOICES };
})();