From 6dc260de153897da3a0083c7015317a8f74b8883 Mon Sep 17 00:00:00 2001 From: Lum1104 Date: Fri, 27 Mar 2026 09:48:07 +0800 Subject: [PATCH] feat(dashboard): add theme engine with CSS variable injection Co-Authored-By: Claude Opus 4.6 --- .../dashboard/src/themes/theme-engine.ts | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 understand-anything-plugin/packages/dashboard/src/themes/theme-engine.ts diff --git a/understand-anything-plugin/packages/dashboard/src/themes/theme-engine.ts b/understand-anything-plugin/packages/dashboard/src/themes/theme-engine.ts new file mode 100644 index 0000000..23004ca --- /dev/null +++ b/understand-anything-plugin/packages/dashboard/src/themes/theme-engine.ts @@ -0,0 +1,56 @@ +import type { ThemeConfig } from "./types.ts"; +import { getAccent, getPreset } from "./presets.ts"; + +export function hexToRgb(hex: string): string { + const h = hex.replace("#", ""); + const n = parseInt(h, 16); + return `${(n >> 16) & 255}, ${(n >> 8) & 255}, ${n & 255}`; +} + +function deriveFromAccent(accentHex: string, isDark: boolean): Record { + const rgb = hexToRgb(accentHex); + return { + "color-border-subtle": `rgba(${rgb}, ${isDark ? 0.12 : 0.1})`, + "color-border-medium": `rgba(${rgb}, ${isDark ? 0.25 : 0.18})`, + "glass-bg": isDark ? "rgba(20, 20, 20, 0.8)" : "rgba(255, 255, 255, 0.8)", + "glass-bg-heavy": isDark ? "rgba(20, 20, 20, 0.95)" : "rgba(255, 255, 255, 0.95)", + "glass-border": `rgba(${rgb}, ${isDark ? 0.1 : 0.08})`, + "glass-border-heavy": `rgba(${rgb}, ${isDark ? 0.15 : 0.12})`, + "scrollbar-thumb": `rgba(${rgb}, 0.2)`, + "scrollbar-thumb-hover": `rgba(${rgb}, 0.35)`, + "glow-accent": `rgba(${rgb}, 0.15)`, + "glow-accent-strong": `rgba(${rgb}, 0.4)`, + "glow-accent-pulse": `rgba(${rgb}, 0.6)`, + "color-edge": `rgba(${rgb}, 0.3)`, + "color-edge-dim": `rgba(${rgb}, 0.08)`, + "color-edge-dot": `rgba(${rgb}, 0.15)`, + "color-accent-overlay-bg": `rgba(${rgb}, 0.05)`, + "color-accent-overlay-border": `rgba(${rgb}, 0.25)`, + "kbd-bg": `rgba(${rgb}, 0.1)`, + }; +} + +export function applyTheme(config: ThemeConfig): void { + const preset = getPreset(config.presetId); + const accent = getAccent(preset, config.accentId); + const style = document.documentElement.style; + + // 1. Apply base preset colors + for (const [key, value] of Object.entries(preset.colors)) { + style.setProperty(`--color-${key}`, value); + } + + // 2. Apply accent colors from swatch + style.setProperty("--color-accent", accent.accent); + style.setProperty("--color-accent-dim", accent.accentDim); + style.setProperty("--color-accent-bright", accent.accentBright); + + // 3. Apply derived values + const derived = deriveFromAccent(accent.accent, preset.isDark); + for (const [key, value] of Object.entries(derived)) { + style.setProperty(`--${key}`, value); + } + + // 4. Set data-theme for CSS-only selectors + document.documentElement.setAttribute("data-theme", preset.isDark ? "dark" : "light"); +}