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
|
|
@ -432,10 +432,13 @@ ipcMain.handle("folio:getClipboardText", () => {
|
|||
});
|
||||
|
||||
// Theme preference. Stored as a plain string so future themes can be added
|
||||
// without touching the main process.
|
||||
ipcMain.handle("folio:getTheme", () => getSetting("theme", "default"));
|
||||
// without touching the main process. "default" from older builds maps to
|
||||
// "light" in the renderer, so it is normalized here too.
|
||||
ipcMain.handle("folio:getTheme", () => getSetting("theme", "light"));
|
||||
ipcMain.handle("folio:setTheme", (_e, id: unknown) => {
|
||||
setSetting("theme", typeof id === "string" && id ? id : "default");
|
||||
const theme =
|
||||
typeof id === "string" && id && id !== "default" ? id : "light";
|
||||
setSetting("theme", theme);
|
||||
return { ok: true };
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
<div class="theme-control">
|
||||
<label class="theme-label" for="themeSelect">Theme</label>
|
||||
<select id="themeSelect" class="theme-select" title="Interface theme">
|
||||
<option value="default">Default</option>
|
||||
<option value="light">Light</option>
|
||||
<option value="neonflux">Neonflux</option>
|
||||
</select>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -6,28 +6,37 @@ export interface ThemeApi {
|
|||
setTheme: (id: string) => Promise<unknown>;
|
||||
}
|
||||
|
||||
function selector(): HTMLSelectElement {
|
||||
export function selector(): HTMLSelectElement {
|
||||
return document.getElementById("themeSelect") as HTMLSelectElement;
|
||||
}
|
||||
|
||||
// Older builds stored the theme as "default"; that now maps to the Light
|
||||
// theme, which replaced it as Folio's default light appearance.
|
||||
const LIGHT = "light";
|
||||
|
||||
function normalizeTheme(id: unknown): string {
|
||||
if (typeof id !== "string" || !id || id === "default") return LIGHT;
|
||||
return id;
|
||||
}
|
||||
|
||||
export function applyTheme(id: string): void {
|
||||
const theme = typeof id === "string" && id ? id : "default";
|
||||
const theme = normalizeTheme(id);
|
||||
document.documentElement.setAttribute("data-theme", theme);
|
||||
const sel = selector();
|
||||
if (sel && sel.value !== theme) sel.value = theme;
|
||||
}
|
||||
|
||||
export function currentTheme(): string {
|
||||
return document.documentElement.getAttribute("data-theme") || "default";
|
||||
return normalizeTheme(document.documentElement.getAttribute("data-theme"));
|
||||
}
|
||||
|
||||
// Load the saved theme and wire the selector. Unknown stored values fall back
|
||||
// to Default (the :root base), so adding a theme later is safe for old saves.
|
||||
// to Light (the :root base), so adding a theme later is safe for old saves.
|
||||
export async function initTheme(api: ThemeApi): Promise<void> {
|
||||
let saved = "default";
|
||||
let saved = LIGHT;
|
||||
try {
|
||||
const value = await api.getTheme();
|
||||
if (typeof value === "string" && value) saved = value;
|
||||
if (typeof value === "string" && value) saved = normalizeTheme(value);
|
||||
} catch {
|
||||
// keep the default on any IPC failure
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,9 +4,8 @@
|
|||
just a block of variable values — adding one means dropping a
|
||||
`:root[data-theme="..."]` block here plus an <option> in the theme selector.
|
||||
|
||||
Default mirrors Folio's original appearance: light, with an automatic dark
|
||||
variant that follows the OS color scheme. Neonflux is a fixed dark,
|
||||
futuristic theme. */
|
||||
Light is Folio's default: a warm, writing-focused light scheme. Neonflux is
|
||||
a fixed dark, futuristic theme. */
|
||||
|
||||
/* ---- shared (non-color) tokens ---- */
|
||||
:root {
|
||||
|
|
@ -17,58 +16,35 @@
|
|||
--gap: 16px;
|
||||
}
|
||||
|
||||
/* ---- Default (light) ---- */
|
||||
/* ---- Light (default) ----
|
||||
A clean, professional writing theme. Warm off-white chrome keeps the app
|
||||
soft instead of harsh, while a warm-white editor surface and charcoal text
|
||||
stay comfortable for long-form reading. Borders and dividers are subtle
|
||||
warm grays; the accent is a restrained deep teal-green so interactive and
|
||||
selected elements stay clearly visible without shouting. */
|
||||
:root,
|
||||
:root[data-theme="default"] {
|
||||
--color-bg: #ffffff;
|
||||
--color-panel: #f7f7f8;
|
||||
--color-border: #e2e2e7;
|
||||
--color-text: #1a1a1d;
|
||||
--color-muted: #6b6b76;
|
||||
--color-accent: #2563eb;
|
||||
--color-accent-hover: #1d4ed8;
|
||||
:root[data-theme="light"] {
|
||||
--color-bg: #fffdf9;
|
||||
--color-panel: #f5f1e9;
|
||||
--color-border: #e4e0d5;
|
||||
--color-text: #2b2925;
|
||||
--color-muted: #7c786d;
|
||||
--color-accent: #2f6b5f;
|
||||
--color-accent-hover: #26584e;
|
||||
|
||||
--color-on-accent: #ffffff;
|
||||
--color-on-accent-soft: rgba(255, 255, 255, 0.15);
|
||||
--color-on-accent-border: rgba(255, 255, 255, 0.3);
|
||||
--color-on-accent-soft: rgba(255, 255, 255, 0.18);
|
||||
--color-on-accent-border: rgba(255, 255, 255, 0.35);
|
||||
|
||||
--color-selection: rgba(37, 99, 235, 0.25);
|
||||
--color-selection-strong: rgba(37, 99, 235, 0.5);
|
||||
--color-selection: rgba(47, 107, 95, 0.22);
|
||||
--color-selection-strong: rgba(47, 107, 95, 0.45);
|
||||
|
||||
--color-shadow: rgba(0, 0, 0, 0.25);
|
||||
--color-shadow-strong: rgba(0, 0, 0, 0.3);
|
||||
--color-backdrop: rgba(0, 0, 0, 0.45);
|
||||
--color-shadow: rgba(45, 40, 32, 0.18);
|
||||
--color-shadow-strong: rgba(45, 40, 32, 0.26);
|
||||
--color-backdrop: rgba(45, 40, 32, 0.4);
|
||||
|
||||
--color-scrollbar: rgba(0, 0, 0, 0.18);
|
||||
--color-scrollbar-hover: rgba(0, 0, 0, 0.32);
|
||||
}
|
||||
|
||||
/* ---- Default (dark, follows OS preference) ---- */
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root:not([data-theme]),
|
||||
:root[data-theme="default"] {
|
||||
--color-bg: #0f0f12;
|
||||
--color-panel: #18181e;
|
||||
--color-border: #2a2a32;
|
||||
--color-text: #e6e6ec;
|
||||
--color-muted: #9a9aa6;
|
||||
--color-accent: #4a94f0;
|
||||
--color-accent-hover: #6ab0ff;
|
||||
|
||||
--color-on-accent: #ffffff;
|
||||
--color-on-accent-soft: rgba(255, 255, 255, 0.15);
|
||||
--color-on-accent-border: rgba(255, 255, 255, 0.3);
|
||||
|
||||
--color-selection: rgba(74, 148, 240, 0.25);
|
||||
--color-selection-strong: rgba(74, 148, 240, 0.5);
|
||||
|
||||
--color-shadow: rgba(0, 0, 0, 0.55);
|
||||
--color-shadow-strong: rgba(0, 0, 0, 0.65);
|
||||
--color-backdrop: rgba(0, 0, 0, 0.6);
|
||||
|
||||
--color-scrollbar: rgba(255, 255, 255, 0.15);
|
||||
--color-scrollbar-hover: rgba(255, 255, 255, 0.3);
|
||||
}
|
||||
--color-scrollbar: rgba(45, 40, 32, 0.22);
|
||||
--color-scrollbar-hover: rgba(45, 40, 32, 0.38);
|
||||
}
|
||||
|
||||
/* ---- Neonflux ----
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue