import { onMounted, onUnmounted } from 'vue' import { useData } from 'vitepress' const EXPLICIT_KEY = 'mengstack-theme-explicit' const VP_KEY = 'vitepress-theme-appearance' const DAY_START = 8 const NIGHT_START = 18 let initialized = false let _isDark = null let _handleToggle = null function isTimeDark() { const h = new Date().getHours() return h < DAY_START || h >= NIGHT_START } function getSystemPref() { if (typeof window === 'undefined' || !window.matchMedia) return null if (window.matchMedia('(prefers-color-scheme: dark)').matches) return true if (window.matchMedia('(prefers-color-scheme: light)').matches) return false return null } export function initThemeManager() { if (initialized) return { isDark: _isDark, handleToggle: _handleToggle } const vp = useData() const isDark = vp.isDark initialized = true _isDark = isDark let explicitChoice = null let timer = null let sysListener = null let observer = null let suppressObs = false function sync(value) { suppressObs = true isDark.value = value try { localStorage.setItem(VP_KEY, String(value)) document.documentElement.classList.toggle('dark', value) } catch {} setTimeout(() => { suppressObs = false }, 150) } function doAutoSwitch() { const shouldBeDark = isTimeDark() if (isDark.value !== shouldBeDark) { sync(shouldBeDark) } } function handleToggle() { explicitChoice = !isDark.value try { localStorage.setItem(EXPLICIT_KEY, String(explicitChoice)) } catch {} if (timer) { clearInterval(timer); timer = null } sync(explicitChoice) } _handleToggle = handleToggle onMounted(() => { try { const stored = localStorage.getItem(EXPLICIT_KEY) if (stored !== null) explicitChoice = stored === 'true' } catch {} if (explicitChoice !== null) { sync(explicitChoice) } else { const sysPref = getSystemPref() if (sysPref !== null) { sync(sysPref) } else { sync(isTimeDark()) } } timer = setInterval(() => { if (explicitChoice !== null) return if (getSystemPref() !== null) return doAutoSwitch() }, 30000) if (typeof window !== 'undefined' && window.matchMedia) { const mql = window.matchMedia('(prefers-color-scheme: dark)') sysListener = (e) => { if (explicitChoice !== null) return sync(e.matches) } mql.addEventListener('change', sysListener) } if (typeof document !== 'undefined') { observer = new MutationObserver(() => { if (suppressObs) return const dark = document.documentElement.classList.contains('dark') if (explicitChoice === null) { explicitChoice = dark try { localStorage.setItem(EXPLICIT_KEY, String(explicitChoice)) } catch {} } isDark.value = dark try { localStorage.setItem(VP_KEY, String(dark)) } catch {} }) observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] }) } }) onUnmounted(() => { if (timer) clearInterval(timer) if (sysListener && typeof window !== 'undefined' && window.matchMedia) { window.matchMedia('(prefers-color-scheme: dark)').removeEventListener('change', sysListener) } if (observer) observer.disconnect() initialized = false }) return { isDark, handleToggle } }