// Theme store — persists theme selection via settings bridge import { getSetting, setSetting } from '../../src/lib/stores/settings-store.svelte'; import { handleInfraError } from '../../src/lib/utils/handle-error'; import { type ThemeId, type ThemePalette, type CatppuccinFlavor, ALL_THEME_IDS, buildXtermTheme, buildXtermThemeFromPalette, applyCssVariables, applyPaletteDirect, type XtermTheme, } from '../../src/lib/styles/themes'; let currentTheme = $state('mocha'); let customPalette = $state(null); /** Registered theme-change listeners */ const themeChangeCallbacks = new Set<() => void>(); /** Register a callback invoked after every theme change. Returns an unsubscribe function. */ export function onThemeChange(callback: () => void): () => void { themeChangeCallbacks.add(callback); return () => { themeChangeCallbacks.delete(callback); }; } export function getCurrentTheme(): ThemeId { return currentTheme; } /** @deprecated Use getCurrentTheme() */ export function getCurrentFlavor(): CatppuccinFlavor { // Return valid CatppuccinFlavor or default to 'mocha' const catFlavors: string[] = ['latte', 'frappe', 'macchiato', 'mocha']; return catFlavors.includes(currentTheme) ? currentTheme as CatppuccinFlavor : 'mocha'; } export function getXtermTheme(): XtermTheme { if (customPalette) return buildXtermThemeFromPalette(customPalette); return buildXtermTheme(currentTheme); } /** Apply an arbitrary palette for live preview (does NOT persist) */ export function previewPalette(palette: ThemePalette): void { customPalette = palette; applyPaletteDirect(palette); for (const cb of themeChangeCallbacks) { try { cb(); } catch (e) { handleInfraError(e, 'theme.previewCallback'); } } } /** Clear custom palette preview, revert to current built-in theme */ export function clearPreview(): void { customPalette = null; applyCssVariables(currentTheme); for (const cb of themeChangeCallbacks) { try { cb(); } catch (e) { handleInfraError(e, 'theme.clearPreviewCallback'); } } } /** Set a custom theme as active (persists the custom theme ID) */ export async function setCustomTheme(id: string, palette: ThemePalette): Promise { customPalette = palette; applyPaletteDirect(palette); for (const cb of themeChangeCallbacks) { try { cb(); } catch (e) { handleInfraError(e, 'theme.customCallback'); } } try { await setSetting('theme', id); } catch (e) { handleInfraError(e, 'theme.persistCustom'); } } /** Check if current theme is a custom theme */ export function isCustomThemeActive(): boolean { return customPalette !== null; } /** Change theme, apply CSS variables, and persist to settings DB */ export async function setTheme(theme: ThemeId): Promise { currentTheme = theme; applyCssVariables(theme); // Notify all listeners (e.g. open xterm.js terminals) for (const cb of themeChangeCallbacks) { try { cb(); } catch (e) { handleInfraError(e, 'theme.changeCallback'); } } try { await setSetting('theme', theme); } catch (e) { handleInfraError(e, 'theme.persistSetting'); } } /** @deprecated Use setTheme() */ export async function setFlavor(flavor: CatppuccinFlavor): Promise { return setTheme(flavor); } /** Load saved theme from settings DB and apply. Call once on app startup. */ export async function initTheme(): Promise { try { const saved = await getSetting('theme'); if (saved) { if (saved.startsWith('custom:')) { // Custom theme — load palette from custom_themes storage const { loadCustomThemes } = await import('../../src/lib/styles/custom-themes'); const customs = await loadCustomThemes(); const match = customs.find(c => c.id === saved); if (match) { customPalette = match.palette; applyPaletteDirect(match.palette); } } else if (ALL_THEME_IDS.includes(saved as ThemeId)) { currentTheme = saved as ThemeId; } } } catch { // Fall back to default (mocha) — catppuccin.css provides Mocha defaults } // Always apply to sync CSS vars with current theme (skip if custom already applied) if (!customPalette && currentTheme !== 'mocha') { applyCssVariables(currentTheme); } // Apply saved font settings try { const [uiFont, uiSize, termFont, termSize] = await Promise.all([ getSetting('ui_font_family'), getSetting('ui_font_size'), getSetting('term_font_family'), getSetting('term_font_size'), ]); const root = document.documentElement.style; if (uiFont) root.setProperty('--ui-font-family', `'${uiFont}', sans-serif`); if (uiSize) root.setProperty('--ui-font-size', `${uiSize}px`); if (termFont) root.setProperty('--term-font-family', `'${termFont}', monospace`); if (termSize) root.setProperty('--term-font-size', `${termSize}px`); } catch { // Font settings are optional — defaults from catppuccin.css apply } }