import { createContext, useCallback, useContext, useState, useEffect } from 'react' import { theme } from 'antd' import PropTypes from 'prop-types' const ThemeContext = createContext() const COLORS = { colorPrimary: '#0091FF', colorPrimaryDisabledDark: '#0e3a5b', colorPrimaryDisabledLight: '#e6f8ff', colorSuccess: '#30D158', colorWarning: '#FF9230', colorError: '#FF4245', colorInfo: '#0A84FF', colorLink: '#5AC8F5', colorCyan: '#5AC8F5', colorPink: '#FF69B4', colorPurple: '#6B5DFF', colorMagenta: '#FF00FF', colorVolcano: '#FF4500' } export const ThemeProvider = ({ children }) => { const [isSystem, setIsSystem] = useState(() => { const savedSystem = sessionStorage.getItem('isSystem') return savedSystem ? JSON.parse(savedSystem) : true }) const [isDarkMode, setIsDarkMode] = useState(() => { const savedTheme = sessionStorage.getItem('isDarkMode') return savedTheme ? JSON.parse(savedTheme) : true }) const [isCompact, setIsCompact] = useState(() => { const savedCompact = sessionStorage.getItem('isCompact') return savedCompact ? JSON.parse(savedCompact) : false }) const [primaryColorOverride, setPrimaryColorOverride] = useState(null) // System theme detection useEffect(() => { const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)') const handleSystemThemeChange = (e) => { if (isSystem) { setIsDarkMode(e.matches) } } // Set initial value if (isSystem) { setIsDarkMode(mediaQuery.matches) } // Add listener mediaQuery.addEventListener('change', handleSystemThemeChange) // Cleanup return () => mediaQuery.removeEventListener('change', handleSystemThemeChange) }, [isSystem]) useEffect(() => { sessionStorage.setItem('isSystem', JSON.stringify(isSystem)) }, [isSystem]) useEffect(() => { sessionStorage.setItem('isDarkMode', JSON.stringify(isDarkMode)) }, [isDarkMode]) useEffect(() => { sessionStorage.setItem('isCompact', JSON.stringify(isCompact)) }, [isCompact]) const toggleTheme = () => { if (isSystem) { setIsSystem(false) } setIsDarkMode(!isDarkMode) } const toggleSystem = () => { setIsSystem(!isSystem) if (!isSystem) { // When enabling system theme, update to match system preference setIsDarkMode(window.matchMedia('(prefers-color-scheme: dark)').matches) } } const toggleCompact = () => { setIsCompact(!isCompact) } const setThemeMode = useCallback((value) => { if (value === 'system') { setIsSystem(true) setIsDarkMode(window.matchMedia('(prefers-color-scheme: dark)').matches) return } setIsSystem(false) setIsDarkMode(value === 'dark') }, []) const setDensityMode = useCallback((value) => { setIsCompact(value === 'compact') }, []) const getThemeAlgorithm = () => { var baseAlgorithm if (isDarkMode == true) { baseAlgorithm = theme.darkAlgorithm } else { baseAlgorithm = theme.defaultAlgorithm } return isCompact ? [theme.compactAlgorithm, baseAlgorithm] : [baseAlgorithm] } const getColors = () => { return COLORS } // Set CSS custom properties for theme colors useEffect(() => { const root = document.documentElement root.style.setProperty( '--color-primary', primaryColorOverride == null ? COLORS.colorPrimary : primaryColorOverride ) root.style.setProperty( '--color-primary-disabled', isDarkMode ? COLORS.colorPrimaryDisabledDark : COLORS.colorPrimaryDisabledLight ) root.style.setProperty('--color-success', COLORS.colorSuccess) root.style.setProperty('--color-warning', COLORS.colorWarning) root.style.setProperty('--color-error', COLORS.colorError) root.style.setProperty('--color-info', COLORS.colorInfo) root.style.setProperty('--color-link', COLORS.colorLink) root.style.setProperty('--color-cyan', COLORS.colorCyan) root.style.setProperty('--color-pink', COLORS.colorPink) root.style.setProperty('--color-purple', COLORS.colorPurple) root.style.setProperty('--color-magenta', COLORS.colorMagenta) root.style.setProperty('--color-volcano', COLORS.colorVolcano) root.style.setProperty( '--layout-header-bg', isDarkMode ? '#141414' : '#ffffff' ) root.style.setProperty( '--layout-modal-bg', isDarkMode ? '#1f1f1f' : '#ffffff' ) }, [isDarkMode, primaryColorOverride]) const themeConfig = { algorithm: getThemeAlgorithm(), token: { ...COLORS, colorPrimary: primaryColorOverride == null ? COLORS.colorPrimary : primaryColorOverride, borderRadius: '12px' }, components: { Layout: { headerBg: isDarkMode ? '#141414' : '#ffffff' } } } return ( {children} ) } ThemeProvider.propTypes = { children: PropTypes.node.isRequired } // eslint-disable-next-line react-refresh/only-export-components export const useThemeContext = () => { const context = useContext(ThemeContext) if (!context) { throw new Error('useThemeContext must be used within a ThemeProvider') } return context }