SPB Git
1.6 KB · 28 lines typescript
1"use client";2import { useSyncExternalStore } from "react";34/** Owner UI preferences kept in localStorage (device-level; nothing private beyond model choice). */5export interface Prefs {6  modelId: string | null;7  routingMode: "manual" | "fast" | "quality" | "economy" | "auto";8  effort: "low" | "medium" | "high" | "xhigh" | "max" | undefined;9  toolsEnabled: boolean;10  showReasoning: boolean;11  sidebarCollapsed: boolean;12  theme: "dark" | "light" | "system";13  sendWithEnter: boolean;14  fontSize: "sm" | "md" | "lg";15}16const DEFAULTS: Prefs = { modelId: null, routingMode: "manual", effort: undefined, toolsEnabled: true, showReasoning: true, sidebarCollapsed: false, theme: "dark", sendWithEnter: true, fontSize: "md" };17const KEY = "csa.prefs.v1";18let cache: Prefs | null = null;19const subs = new Set<() => void>();20function read(): Prefs { if (cache) return cache; if (typeof window === "undefined") return DEFAULTS; try { cache = { ...DEFAULTS, ...JSON.parse(localStorage.getItem(KEY) ?? "{}") }; } catch { cache = DEFAULTS; } return cache!; }21export function setPrefs(patch: Partial<Prefs>) { cache = { ...read(), ...patch }; localStorage.setItem(KEY, JSON.stringify(cache)); subs.forEach(s => s()); if (patch.theme) applyTheme(patch.theme); }22export function usePrefs(): Prefs { return useSyncExternalStore(cb => { subs.add(cb); return () => { subs.delete(cb); }; }, read, () => DEFAULTS); }23export function applyTheme(t: Prefs["theme"]) {24  const v = t === "system" ? (matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark") : t;25  document.documentElement.dataset.theme = v;26  document.cookie = `csa_theme=${t}; path=/; max-age=31536000; samesite=strict`;27}28
Files · main
Menu

Theme