fbd6df33b4
Adds System Preferences, Dark and Light themes, gated by the `hasThemeSwitcher` feature flag (off by default — dark stays the default theme for everyone). Old theme is now "Classic"and set as default. "System preferences" theme has both Light and Dark modes and uses your laptop settings to use a correct one. It has less color accents (specifically less colored text), and they are the same for both modes, only grayscale values change between them. And Light/Dark themes can be used separately. New Contrast setting is available for System Preferences, Dark and Light themes - it changes the contrast for the whole app. All new visual Settings live in Account.
26 lines
1.2 KiB
TypeScript
26 lines
1.2 KiB
TypeScript
import { z } from "zod";
|
|
|
|
// Shared between server (dashboard preferences) and client (theme UI, system
|
|
// theme sync) - must stay free of server-only imports.
|
|
export const ThemePreference = z.enum(["classic", "system", "dark", "light"]);
|
|
export type ThemePreference = z.infer<typeof ThemePreference>;
|
|
|
|
/** Coerce any stored/legacy value into a valid preference. Missing or unknown
|
|
* values fall back to `dark` - the new dark theme is the default (pinned, not
|
|
* system-resolved, so nobody gets surprised by light mode). */
|
|
export function normalizeThemePreference(value: unknown): ThemePreference {
|
|
const result = ThemePreference.safeParse(value);
|
|
return result.success ? result.data : "dark";
|
|
}
|
|
|
|
/** The default dark theme ships with a slight contrast bump. */
|
|
export const DEFAULT_THEME_CONTRAST = 50;
|
|
|
|
/** Interface contrast for the System themes, 0 to 100. Missing or invalid
|
|
* values fall back to the default bump. */
|
|
export function normalizeThemeContrast(value: unknown): number {
|
|
const num = typeof value === "string" ? Number(value) : value;
|
|
if (typeof num !== "number" || !Number.isFinite(num)) return DEFAULT_THEME_CONTRAST;
|
|
return Math.min(100, Math.max(0, Math.round(num)));
|
|
}
|