Persistence: - bun:sqlite at ~/.config/agor/settings.db (WAL mode, 500ms busy_timeout) - 4 tables: schema_version, settings, projects, custom_themes - 5 RPC handlers: settings.get/set/getAll, projects get/set Theme system (LIVE switching): - All 17 themes ported from Tauri (4 Catppuccin + 7 Editor + 6 Deep Dark) - applyCssVars() sets 26 --ctp-* vars on document.documentElement - Parallel xterm ITheme mapping per theme - theme-store.svelte.ts: Svelte 5 rune store, persists to SQLite Font system: - font-store.svelte.ts: UI/terminal font family + size - Live CSS var application (--ui-font-family/size, --term-font-family/size) - onTermFontChange() callback registry for terminal instances - Persists all 4 font settings to SQLite AppearanceSettings wired: 17-theme grouped dropdown, font steppers Init on startup: restores saved theme + fonts from SQLite
86 lines
2.9 KiB
TypeScript
86 lines
2.9 KiB
TypeScript
/**
|
|
* Svelte 5 rune-based theme store.
|
|
* Applies all 26 --ctp-* CSS vars to document.documentElement instantly.
|
|
* Persists selection via settings RPC.
|
|
*
|
|
* Usage:
|
|
* import { themeStore } from './theme-store.svelte';
|
|
* themeStore.setTheme('tokyo-night');
|
|
* await themeStore.initTheme(rpc);
|
|
*/
|
|
|
|
import { applyCssVars, THEME_LIST, type ThemeId } from "./themes.ts";
|
|
|
|
const SETTING_KEY = "theme";
|
|
const DEFAULT_THEME: ThemeId = "mocha";
|
|
|
|
// ── Minimal RPC interface ─────────────────────────────────────────────────────
|
|
// Avoids importing the full Electroview class — just the subset we need.
|
|
|
|
interface SettingsRpc {
|
|
request: {
|
|
"settings.get"(p: { key: string }): Promise<{ value: string | null }>;
|
|
"settings.set"(p: { key: string; value: string }): Promise<{ ok: boolean }>;
|
|
};
|
|
}
|
|
|
|
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
|
|
function isValidThemeId(id: string): id is ThemeId {
|
|
return THEME_LIST.some((t) => t.id === id);
|
|
}
|
|
|
|
// ── Store ─────────────────────────────────────────────────────────────────────
|
|
|
|
function createThemeStore() {
|
|
let currentThemeId = $state<ThemeId>(DEFAULT_THEME);
|
|
let rpc: SettingsRpc | null = null;
|
|
|
|
/** Apply CSS vars immediately — synchronous, no flash. */
|
|
function applyToDocument(id: ThemeId): void {
|
|
applyCssVars(id);
|
|
currentThemeId = id;
|
|
}
|
|
|
|
/**
|
|
* Change the active theme.
|
|
* Applies CSS vars immediately and persists asynchronously.
|
|
*/
|
|
function setTheme(id: ThemeId): void {
|
|
applyToDocument(id);
|
|
if (rpc) {
|
|
rpc.request["settings.set"]({ key: SETTING_KEY, value: id }).catch((err) => {
|
|
console.error("[theme-store] Failed to persist theme:", err);
|
|
});
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Load persisted theme from settings on startup.
|
|
* Call once in App.svelte onMount.
|
|
*/
|
|
async function initTheme(rpcInstance: SettingsRpc): Promise<void> {
|
|
rpc = rpcInstance;
|
|
try {
|
|
const result = await rpc.request["settings.get"]({ key: SETTING_KEY });
|
|
const saved = result.value;
|
|
if (saved && isValidThemeId(saved)) {
|
|
applyToDocument(saved);
|
|
} else {
|
|
// Apply default to ensure vars are set even when nothing was persisted.
|
|
applyToDocument(DEFAULT_THEME);
|
|
}
|
|
} catch (err) {
|
|
console.error("[theme-store] Failed to load theme from settings:", err);
|
|
applyToDocument(DEFAULT_THEME);
|
|
}
|
|
}
|
|
|
|
return {
|
|
get currentTheme() { return currentThemeId; },
|
|
setTheme,
|
|
initTheme,
|
|
};
|
|
}
|
|
|
|
export const themeStore = createThemeStore();
|