Files
olivv-cs-ComfyUI-FunPack/app/ui/shell/theme.js
T

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 };
})();