From d98d519fd34cbdf4957711990d3f4191ae96a9d9 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Tue, 3 Feb 2026 19:42:24 +0000 Subject: [PATCH] feat(ui): persist theme preference Persist system/light/dark theme mode in app config and default new installs to system so the UI follows OS theme unless overridden. --- packages/ui/src/lib/theme.tsx | 17 ++++------------- packages/ui/src/stores/preferences.tsx | 4 ++-- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/packages/ui/src/lib/theme.tsx b/packages/ui/src/lib/theme.tsx index 654bb772..61ad6e4f 100644 --- a/packages/ui/src/lib/theme.tsx +++ b/packages/ui/src/lib/theme.tsx @@ -78,12 +78,12 @@ const resolvePaletteColors = (dark: boolean): ResolvedPaletteColors => { export function ThemeProvider(props: { children: JSX.Element }) { const mediaQuery = typeof window !== "undefined" ? window.matchMedia("(prefers-color-scheme: dark)") : null - const { themePreference } = useConfig() + const { themePreference, setThemePreference } = useConfig() const [isDark, setIsDarkSignal] = createSignal(true) - const [themeMode, setThemeModeSignal] = createSignal(themePreference()) - const [hasUserOverride, setHasUserOverride] = createSignal(false) const [themeRevision, setThemeRevision] = createSignal(0) + const themeMode = () => themePreference() as ThemeMode + const resolveDarkTheme = () => { const mode = themeMode() if (mode === "dark") return true @@ -111,12 +111,6 @@ export function ThemeProvider(props: { children: JSX.Element }) { applyResolvedTheme() }) - createEffect(() => { - const preference = themePreference() - if (hasUserOverride()) return - setThemeModeSignal(preference) - }) - onMount(() => { if (!mediaQuery) return const handleSystemThemeChange = () => { @@ -131,10 +125,7 @@ export function ThemeProvider(props: { children: JSX.Element }) { }) const setThemeMode = (mode: ThemeMode) => { - setHasUserOverride(true) - setThemeModeSignal(mode) - // Persistence is intentionally implemented later. - // When we wire it up, this should call `setThemePreference(mode)`. + setThemePreference(mode) } const cycleThemeMode = () => { diff --git a/packages/ui/src/stores/preferences.tsx b/packages/ui/src/stores/preferences.tsx index 95d623cf..f3213c6e 100644 --- a/packages/ui/src/stores/preferences.tsx +++ b/packages/ui/src/stores/preferences.tsx @@ -194,7 +194,7 @@ const [isConfigLoaded, setIsConfigLoaded] = createSignal(false) const preferences = createMemo(() => internalConfig().preferences) const recentFolders = createMemo(() => internalConfig().recentFolders ?? []) const opencodeBinaries = createMemo(() => internalConfig().opencodeBinaries ?? []) -const themePreference = createMemo(() => internalConfig().theme ?? "dark") +const themePreference = createMemo(() => internalConfig().theme ?? "system") let loadPromise: Promise | null = null function normalizeConfig(config?: ConfigData | null): ConfigData { @@ -202,7 +202,7 @@ function normalizeConfig(config?: ConfigData | null): ConfigData { preferences: normalizePreferences(config?.preferences), recentFolders: (config?.recentFolders ?? []).map((folder) => ({ ...folder })), opencodeBinaries: (config?.opencodeBinaries ?? []).map((binary) => ({ ...binary })), - theme: config?.theme ?? "dark", + theme: config?.theme ?? "system", } }