import React, { createContext, useContext, useEffect, useState, useCallback, useMemo } from 'react'; type Theme = 'light' | 'dark' | 'system'; interface ThemeContextType { theme: Theme; setTheme: (theme: Theme) => void; isDark: boolean; } const ThemeContext = createContext(undefined); const defineThemeColors = (isDark: boolean) => { const root = document.documentElement; if (isDark) { root.style.setProperty('--bg-primary', '#111827'); root.style.setProperty('--bg-secondary', '#1f2937'); root.style.setProperty('--text-primary', '#ffffff'); root.style.setProperty('--text-secondary', '#9ca3af'); root.style.setProperty('--accent-primary', '#60a5fa'); root.style.setProperty('--accent-secondary', '#3b82f6'); } else { root.style.setProperty('--bg-primary', '#ffffff'); root.style.setProperty('--bg-secondary', '#f3f4f6'); root.style.setProperty('--text-primary', '#111827'); root.style.setProperty('--text-secondary', '#4b5563'); root.style.setProperty('--accent-primary', '#2563eb'); root.style.setProperty('--accent-secondary', '#3b82f6'); } }; // 初始化主题函数 const initializeTheme = () => { const savedTheme = localStorage.getItem('theme') as Theme; const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches; const isDark = savedTheme === 'dark' || ((!savedTheme || savedTheme === 'system') && systemDark); // 立即应用主题 document.documentElement.classList.toggle('dark', isDark); defineThemeColors(isDark); return { theme: savedTheme || 'system', isDark }; }; export function ThemeProvider({ children }: { children: React.ReactNode }) { // 使用初始化函数设置初始状态 const initialTheme = useMemo(initializeTheme, []); const [theme, setTheme] = useState(initialTheme.theme); const [isDark, setIsDark] = useState(initialTheme.isDark); const updateTheme = useCallback((newTheme: Theme) => { const systemDark = window.matchMedia('(prefers-color-scheme: dark)').matches; const nextIsDark = newTheme === 'dark' || (newTheme === 'system' && systemDark); if (nextIsDark !== isDark) { requestAnimationFrame(() => { document.documentElement.classList.toggle('dark', nextIsDark); defineThemeColors(nextIsDark); setIsDark(nextIsDark); }); } }, [isDark]); useEffect(() => { localStorage.setItem('theme', theme); updateTheme(theme); }, [theme, updateTheme]); useEffect(() => { const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); const handler = () => { if (theme === 'system') { updateTheme('system'); } }; mediaQuery.addEventListener('change', handler); return () => mediaQuery.removeEventListener('change', handler); }, [theme, updateTheme]); const contextValue = useMemo(() => ({ theme, setTheme, isDark }), [theme, isDark]); return ( {children} ); } export function useTheme() { const context = useContext(ThemeContext); if (context === undefined) { throw new Error('useTheme must be used within a ThemeProvider'); } return context; }