import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useMemo, useState, ReactNode } from 'react'; import { API_BASE_URL } from '../constants'; import { useAuth } from './AuthContext'; import { applyThemeToDocument, readStoredThemePreference, resolveEffectiveTheme, writeStoredThemePreference, } from '../utils/themePreference'; import type { ThemePreference } from '../utils/themePreference'; export type Theme = ThemePreference; interface ThemeContextProps { theme: Theme; effectiveTheme: 'light' | 'dark'; setTheme: (theme: Theme) => Promise; } interface ThemeProviderProps { children: ReactNode; } const ThemeContext = createContext(undefined); export const ThemeProvider = ({ children }: ThemeProviderProps): JSX.Element => { const { isLoggedIn, userInfo, setUserInfo } = useAuth(); const [theme, setThemeState] = useState(readStoredThemePreference); const effectiveTheme = useMemo(() => resolveEffectiveTheme(theme), [theme]); // Account preferences are authoritative after login, but local optimistic changes update userInfo first. useEffect(() => { if (!isLoggedIn || !userInfo?.theme) { return; } setThemeState(userInfo.theme); writeStoredThemePreference(userInfo.theme); }, [isLoggedIn, userInfo?.theme]); const setTheme = useCallback(async (newTheme: Theme) => { setThemeState(newTheme); writeStoredThemePreference(newTheme); if (userInfo) { setUserInfo({ ...userInfo, theme: newTheme }); } if (!isLoggedIn) { return; } try { const response = await fetch(`${API_BASE_URL}/account/theme`, { method: 'PUT', credentials: 'include', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ theme: newTheme }), }); if (!response.ok) { const errorData = await response.json(); throw new Error(errorData.detail || 'Failed to update theme'); } } catch (error) { console.error('Error updating theme on server:', error); } }, [isLoggedIn, setUserInfo, userInfo]); useLayoutEffect(() => { applyThemeToDocument(effectiveTheme); (window as any).actionPanelAiSetTheme?.(effectiveTheme); }, [effectiveTheme]); const value: ThemeContextProps = { theme, effectiveTheme, setTheme, }; return {children}; }; export const useTheme = (): ThemeContextProps => { const context = useContext(ThemeContext); if (context === undefined) { throw new Error('useTheme must be used within a ThemeProvider'); } return context; };