nanobot/webui/src/hooks/useTheme.ts
Xubin Ren e5be4dac7a Optimize WebUI streaming and long history rendering
Batch stream deltas, window long transcripts, lazy-load syntax highlighting, and refine activity/composer interactions.

Add title refresh retries plus tests for streaming, windowing, code blocks, and live activity behavior.
2026-05-17 17:04:57 +08:00

70 lines
1.6 KiB
TypeScript

import {
createContext,
createElement,
useCallback,
useContext,
useEffect,
useState,
type ReactNode,
} from "react";
type Theme = "light" | "dark";
const STORAGE_KEY = "nanobot-webui.theme";
const ThemeContext = createContext<Theme>("light");
function readStored(): Theme | null {
try {
const v = localStorage.getItem(STORAGE_KEY);
return v === "light" || v === "dark" ? v : null;
} catch {
return null;
}
}
function applyTheme(theme: Theme): void {
const root = document.documentElement;
if (theme === "dark") root.classList.add("dark");
else root.classList.remove("dark");
}
export function useTheme(): {
theme: Theme;
toggle: () => void;
setTheme: (t: Theme) => void;
} {
const [theme, setThemeState] = useState<Theme>(() => {
const stored = readStored();
if (stored) return stored;
if (typeof window !== "undefined" && window.matchMedia) {
return window.matchMedia("(prefers-color-scheme: dark)").matches
? "dark"
: "light";
}
return "light";
});
useEffect(() => {
applyTheme(theme);
try {
localStorage.setItem(STORAGE_KEY, theme);
} catch {
// ignore
}
}, [theme]);
const setTheme = useCallback((t: Theme) => setThemeState(t), []);
const toggle = useCallback(
() => setThemeState((t) => (t === "dark" ? "light" : "dark")),
[],
);
return { theme, toggle, setTheme };
}
export function ThemeProvider({ theme, children }: { theme: Theme; children: ReactNode }) {
return createElement(ThemeContext.Provider, { value: theme }, children);
}
export function useThemeValue(): Theme {
return useContext(ThemeContext);
}