Add a built-in Light theme
Replaces the old Default theme (which followed the OS color scheme) with a fixed warm, writing-focused Light theme: off-white editor surface, charcoal text, subtle warm-gray borders, and a restrained deep teal-green accent. The selector now offers Light and Neonflux. Old saves storing "default" are migrated to "light" in both the renderer and the main process, and the e2e scenario now covers Light + Neonflux application, light/dark checks, theme round-trips, and content preservation.
This commit is contained in:
parent
6679294231
commit
247f16baa5
5 changed files with 103 additions and 75 deletions
|
|
@ -762,10 +762,16 @@ const BOOK3_DIR = __BOOKDIR3_VALUE__;
|
|||
out.themeSelectOptions = themeSelect
|
||||
? Array.from(themeSelect.options).map((o) => o.value)
|
||||
: [];
|
||||
out.themeHasDefault = out.themeSelectOptions.includes("default");
|
||||
out.themeHasLight = out.themeSelectOptions.includes("light");
|
||||
out.themeHasNeonflux = out.themeSelectOptions.includes("neonflux");
|
||||
out.themeInitialAttr = document.documentElement.getAttribute("data-theme");
|
||||
out.themeInitialStored = await window.folio.getTheme();
|
||||
out.themeInitialAttr = document.documentElement.getAttribute("data-theme");
|
||||
// Old saves may hold "default", which must resolve to the Light theme
|
||||
// (migration). Valid saved themes must be restored exactly on startup.
|
||||
out.themeInitialConsistent =
|
||||
out.themeInitialStored === "default" || out.themeInitialStored === "light"
|
||||
? out.themeInitialAttr === "light"
|
||||
: out.themeInitialAttr === out.themeInitialStored;
|
||||
|
||||
// Switch to Neonflux: applies immediately and persists via IPC.
|
||||
themeSelect.value = "neonflux";
|
||||
|
|
@ -784,26 +790,55 @@ const BOOK3_DIR = __BOOKDIR3_VALUE__;
|
|||
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);
|
||||
const neonBodyBg = getComputedStyle(document.body).backgroundColor;
|
||||
out.themeNeonfluxDark = /rgb\((10|11|12|9|8),/.test(neonBodyBg);
|
||||
|
||||
// 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";
|
||||
// Switch to Light: applies immediately and persists.
|
||||
themeSelect.value = "light";
|
||||
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.themeLightAttr = document.documentElement.getAttribute("data-theme");
|
||||
out.themeLightStored = await window.folio.getTheme();
|
||||
const lightBg = themeVar("--color-bg");
|
||||
const lightAccent = themeVar("--color-accent");
|
||||
out.themeLightBg = lightBg;
|
||||
out.themeLightAccent = lightAccent;
|
||||
out.themeLightApplied =
|
||||
out.themeLightAttr === "light" &&
|
||||
out.themeLightStored === "light" &&
|
||||
lightBg.toLowerCase() === "#fffdf9" &&
|
||||
lightAccent.toLowerCase() === "#2f6b5f";
|
||||
|
||||
// Light is light: computed background must be a warm off-white, never the
|
||||
// near-black used by Neonflux.
|
||||
const lightBodyBg = getComputedStyle(document.body).backgroundColor;
|
||||
out.themeLightNotDark = !/rgb\((10|11|12|9|8),/.test(lightBodyBg);
|
||||
out.themeContentUnchanged2 = navBook.state.doc.toString() === docBeforeTheme;
|
||||
|
||||
// Round-trip: back to Neonflux restores it exactly.
|
||||
themeSelect.value = "neonflux";
|
||||
themeSelect.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
await navWait(200);
|
||||
out.themeRoundTripAttr = document.documentElement.getAttribute("data-theme");
|
||||
out.themeRoundTripStored = await window.folio.getTheme();
|
||||
out.themeRoundTripOk =
|
||||
out.themeRoundTripAttr === "neonflux" &&
|
||||
out.themeRoundTripStored === "neonflux" &&
|
||||
themeVar("--color-bg").toLowerCase() === "#0a0d14" &&
|
||||
themeVar("--color-accent").toLowerCase() === "#22d3ee";
|
||||
|
||||
// Leave the app on Light so subsequent runs start deterministically.
|
||||
themeSelect.value = "light";
|
||||
themeSelect.dispatchEvent(new Event("change", { bubbles: true }));
|
||||
await navWait(200);
|
||||
out.themeFinalAttr = document.documentElement.getAttribute("data-theme");
|
||||
out.themeFinalStored = await window.folio.getTheme();
|
||||
out.themeFinalLightOk = out.themeFinalAttr === "light" && out.themeFinalStored === "light";
|
||||
out.themeContentUnchanged3 = navBook.state.doc.toString() === docBeforeTheme;
|
||||
|
||||
const okFlags = [
|
||||
"dndMoved",
|
||||
"kbMoved",
|
||||
|
|
@ -876,13 +911,18 @@ const BOOK3_DIR = __BOOKDIR3_VALUE__;
|
|||
"navNextAfterReorderOk",
|
||||
"navBreaksOk",
|
||||
"themeSelectPresent",
|
||||
"themeHasDefault",
|
||||
"themeHasLight",
|
||||
"themeHasNeonflux",
|
||||
"themeInitialConsistent",
|
||||
"themeNeonfluxApplied",
|
||||
"themeNeonfluxDark",
|
||||
"themeContentUnchanged",
|
||||
"themeDefaultApplied",
|
||||
"themeLightApplied",
|
||||
"themeLightNotDark",
|
||||
"themeContentUnchanged2",
|
||||
"themeRoundTripOk",
|
||||
"themeFinalLightOk",
|
||||
"themeContentUnchanged3",
|
||||
];
|
||||
out.all = okFlags.every((f) => out[f] === true);
|
||||
} catch (e) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue