Add a reusable theme system with a Neonflux theme
Introduce a theme layer on top of the existing CSS custom properties. All color tokens now live in themes.css, scoped by a data-theme attribute on <html>, so a theme is just a new token block plus a dropdown option - no code changes needed. The Default theme preserves the existing light/dark behavior; Neonflux adds an electric-cyan dark theme. The active theme is chosen via a Theme selector in the sidebar footer and persisted in a new main-process settings file (userData settings.json) through getTheme/setTheme IPC, so it survives restarts. Hard-coded on-accent, selection, shadow and backdrop colors across the renderer were converted to semantic tokens so every theme stays consistent.
This commit is contained in:
parent
7011d0655b
commit
ad0329bbf9
12 changed files with 346 additions and 52 deletions
|
|
@ -714,6 +714,57 @@ const BOOK3_DIR = __BOOKDIR3_VALUE__;
|
|||
out.navBreakCount = navBreaks.length;
|
||||
out.navBreaksOk = navBreaks.length >= 1 && navBreaks.some((el) => el.textContent.startsWith("# Two"));
|
||||
|
||||
// --- theme system: selector, immediate switching, persistence ---
|
||||
const themeSelect = document.getElementById("themeSelect");
|
||||
const themeVar = (name) =>
|
||||
getComputedStyle(document.documentElement).getPropertyValue(name).trim();
|
||||
const docBeforeTheme = navBook.state.doc.toString();
|
||||
out.themeSelectPresent = !!themeSelect;
|
||||
out.themeSelectOptions = themeSelect
|
||||
? Array.from(themeSelect.options).map((o) => o.value)
|
||||
: [];
|
||||
out.themeHasDefault = out.themeSelectOptions.includes("default");
|
||||
out.themeHasNeonflux = out.themeSelectOptions.includes("neonflux");
|
||||
out.themeInitialAttr = document.documentElement.getAttribute("data-theme");
|
||||
out.themeInitialStored = await window.folio.getTheme();
|
||||
|
||||
// Switch to Neonflux: applies immediately and persists via IPC.
|
||||
themeSelect.value = "neonflux";
|
||||
themeSelect.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
await navWait(200);
|
||||
out.themeNeonfluxAttr = document.documentElement.getAttribute("data-theme");
|
||||
out.themeNeonfluxStored = await window.folio.getTheme();
|
||||
const neonBg = themeVar("--color-bg");
|
||||
const neonAccent = themeVar("--color-accent");
|
||||
out.themeNeonfluxBg = neonBg;
|
||||
out.themeNeonfluxAccent = neonAccent;
|
||||
out.themeNeonfluxApplied =
|
||||
out.themeNeonfluxAttr === "neonflux" &&
|
||||
out.themeNeonfluxStored === "neonflux" &&
|
||||
neonBg.toLowerCase() === "#0a0d14" &&
|
||||
neonAccent.toLowerCase() === "#22d3ee";
|
||||
|
||||
// Neonflux is dark: computed background of the app should be near-black.
|
||||
const bodyBg = getComputedStyle(document.body).backgroundColor;
|
||||
out.themeNeonfluxDark = /rgb\((10|11|12|9|8),/.test(bodyBg);
|
||||
|
||||
// Switching themes must not touch the manuscript content.
|
||||
out.themeContentUnchanged = navBook.state.doc.toString() === docBeforeTheme;
|
||||
|
||||
// Switch back to Default: applies immediately and persists.
|
||||
themeSelect.value = "default";
|
||||
themeSelect.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
await navWait(200);
|
||||
out.themeDefaultAttr = document.documentElement.getAttribute("data-theme");
|
||||
out.themeDefaultStored = await window.folio.getTheme();
|
||||
const defaultBg = themeVar("--color-bg");
|
||||
out.themeDefaultBg = defaultBg;
|
||||
out.themeDefaultApplied =
|
||||
out.themeDefaultAttr === "default" &&
|
||||
out.themeDefaultStored === "default" &&
|
||||
(defaultBg.toLowerCase() === "#ffffff" || defaultBg.toLowerCase() === "#0f0f12");
|
||||
out.themeContentUnchanged2 = navBook.state.doc.toString() === docBeforeTheme;
|
||||
|
||||
const okFlags = [
|
||||
"dndMoved",
|
||||
"kbMoved",
|
||||
|
|
@ -781,6 +832,14 @@ const BOOK3_DIR = __BOOKDIR3_VALUE__;
|
|||
"navFollowsNewOrder",
|
||||
"navNextAfterReorderOk",
|
||||
"navBreaksOk",
|
||||
"themeSelectPresent",
|
||||
"themeHasDefault",
|
||||
"themeHasNeonflux",
|
||||
"themeNeonfluxApplied",
|
||||
"themeNeonfluxDark",
|
||||
"themeContentUnchanged",
|
||||
"themeDefaultApplied",
|
||||
"themeContentUnchanged2",
|
||||
];
|
||||
out.all = okFlags.every((f) => out[f] === true);
|
||||
} catch (e) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue