Files
olivv-cs-ComfyUI-FunPack/app/legacy/theme.js
T
digital-garbageandClaude Sonnet 5 29ac44ffcd feat: v4 UI port Phase A start -- module categories, legacy frontend served live
Two pieces of the "port v4's exact UI onto v5's backend" plan:

- core/contract.py: new optional ModuleSpec.category field (closed set:
  continuity, guidance, conditioning, sampling, post, system), validated in
  core/schema.py, threaded through core/registry.py's FIELDS and
  to_manifest(). Presentation grouping, independent of STAGE (execution
  order) -- a module can be stage="sampling" and category="guidance" at
  once. Only the 4 existing modules that actually render a settings panel
  (alg, sharpen, appearance, full_control) got a category assigned; the
  other 12 have none to categorize (no SETTINGS block) and stay
  uncategorised, which is valid.

- v4's frontend (movie_editor/frontend/*, ~29k lines, 61 files) copied
  verbatim into app/legacy/. core/routes.py's "/" route now serves
  legacy/index.html instead of the composer entry point; a <base> tag in
  that file resolves its own relative script/link tags against
  /funpack/app/legacy/ regardless of what URL it's served at, so every
  file moved as a unit with zero per-file path edits. Verified live: all
  61 assets load 200 OK, the real v4 onboarding screen renders pixel-for-
  pixel (it's the actual DOM/CSS, not a reimplementation), and several v4
  API calls (/api/media, /api/projects, /api/git/status) already succeed
  against v5's real backend by coincidence of shared route naming -- the
  rest 404/405 as expected, since api.js's ~90 functions haven't been
  rewired to v5's actual routes yet (next piece of this plan).

The old composer entry point (app/index.html, app/composer/**) stays in
the tree, unreferenced, until a later cleanup pass once nothing imports it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-14 23:58:51 +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.FunPackTheme = { get, resolved, apply, onChange, CHOICES };
})();