import { writable, derived } from 'svelte/store'; import { browser } from '$app/environment'; import { type ThemeColorMap, defaultColorMap } from '$lib/components/tui/utils'; // Import theme JSON files import archTheme from '$lib/assets/themes/arch.theme.json'; import catppuccinTheme from '$lib/assets/themes/catppuccin.theme.json'; import wintryTheme from '$lib/assets/themes/wintry.theme.json'; import roseTheme from '$lib/assets/themes/rose.theme.json'; import cerberusTheme from '$lib/assets/themes/cerberus.theme.json'; export type ColorTheme = 'arch' | 'catppuccin' | 'wintry' | 'rose' | 'cerberus'; export type Mode = 'dark' | 'light'; // Theme JSON structure export interface ThemeJson { name: string; icon: string; dark: { colors: Record; colorMap: ThemeColorMap; }; light: { colors: Record; colorMap: ThemeColorMap; }; } export interface ThemeColors { primary: string; secondary: string; accent: string; background: string; backgroundLight: string; text: string; textMuted: string; border: string; terminal: string; terminalPrompt: string; terminalUser: string; terminalPath: string; colorMap: ThemeColorMap; } // Load themes from JSON files const themes: Record = { arch: archTheme as ThemeJson, catppuccin: catppuccinTheme as ThemeJson, wintry: wintryTheme as ThemeJson, rose: roseTheme as ThemeJson, cerberus: cerberusTheme as ThemeJson }; // Export themes for external access export { themes }; // Build theme colors from JSON - merges colors into colorMap so formatter can use both function buildThemeColors(theme: ThemeJson, mode: Mode): ThemeColors { const modeData = theme[mode]; // Merge: defaults -> theme palette -> theme colors (so text, primary, etc. are correct hex values) const mergedColorMap: ThemeColorMap = { ...defaultColorMap, // Fallback defaults (hex values) ...(modeData.colorMap ?? {}), // Theme's color palette (hex values) // Also add theme colors so (&text), (&primary), etc. resolve to correct hex for this mode 'text': modeData.colors.text, 'textMuted': modeData.colors.textMuted, 'primary': modeData.colors.primary, 'secondary': modeData.colors.secondary, 'accent': modeData.colors.accent, 'background': modeData.colors.background, 'backgroundLight': modeData.colors.backgroundLight, 'border': modeData.colors.border, 'terminal': modeData.colors.terminal, 'terminalPrompt': modeData.colors.terminalPrompt, 'terminalUser': modeData.colors.terminalUser, 'terminalPath': modeData.colors.terminalPath, 'muted': modeData.colors.textMuted, // alias }; return { primary: modeData.colors.primary, secondary: modeData.colors.secondary, accent: modeData.colors.accent, background: modeData.colors.background, backgroundLight: modeData.colors.backgroundLight, text: modeData.colors.text, textMuted: modeData.colors.textMuted, border: modeData.colors.border, terminal: modeData.colors.terminal, terminalPrompt: modeData.colors.terminalPrompt, terminalUser: modeData.colors.terminalUser, terminalPath: modeData.colors.terminalPath, colorMap: mergedColorMap }; } // Build the theme colors map from JSON themes const themeColorsMap: Record = Object.fromEntries( Object.entries(themes).map(([key, theme]) => [ key, { dark: buildThemeColors(theme, 'dark'), light: buildThemeColors(theme, 'light') } ]) ) as Record; function getInitialMode(): Mode { if (browser) { const stored = localStorage.getItem('mode'); if (stored === 'dark' || stored === 'light') return stored; return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'; } return 'dark'; } function getInitialTheme(): ColorTheme { if (browser) { const stored = localStorage.getItem('colorTheme'); if (stored && stored in themeColorsMap) return stored as ColorTheme; } return 'arch'; } export const mode = writable(getInitialMode()); export const colorTheme = writable(getInitialTheme()); export const themeColors = derived([mode, colorTheme], ([$mode, $colorTheme]) => { return themeColorsMap[$colorTheme][$mode]; }); // Subscribe to persist changes if (browser) { mode.subscribe((value) => { localStorage.setItem('mode', value); document.documentElement.setAttribute('data-mode', value); }); colorTheme.subscribe((value) => { localStorage.setItem('colorTheme', value); document.documentElement.setAttribute('data-theme', value); }); } export function toggleMode() { mode.update((m) => (m === 'dark' ? 'light' : 'dark')); } export function setColorTheme(theme: ColorTheme) { colorTheme.set(theme); } // Generate theme options from loaded themes export const themeOptions: { value: ColorTheme; label: string; icon: string }[] = Object.entries(themes).map(([key, theme]) => ({ value: key as ColorTheme, label: theme.name, icon: theme.icon }));