57 lines
1.4 KiB
JavaScript
57 lines
1.4 KiB
JavaScript
import { computed, ref } from 'vue'
|
|
|
|
const STORAGE_KEY = 'ai-chat-theme'
|
|
const DEFAULT_THEME = 'light'
|
|
|
|
function readStoredTheme() {
|
|
try {
|
|
return localStorage.getItem(STORAGE_KEY) === 'dark' ? 'dark' : DEFAULT_THEME
|
|
} catch {
|
|
return DEFAULT_THEME
|
|
}
|
|
}
|
|
|
|
const theme = ref(readStoredTheme())
|
|
let transitionTimer
|
|
|
|
function applyTheme(value, animate = false) {
|
|
const nextTheme = value === 'dark' ? 'dark' : 'light'
|
|
const root = document.documentElement
|
|
|
|
if (animate) {
|
|
root.classList.add('theme-transitioning')
|
|
window.clearTimeout(transitionTimer)
|
|
transitionTimer = window.setTimeout(() => root.classList.remove('theme-transitioning'), 260)
|
|
}
|
|
|
|
root.dataset.theme = nextTheme
|
|
root.style.colorScheme = nextTheme
|
|
theme.value = nextTheme
|
|
}
|
|
|
|
export function initializeTheme() {
|
|
applyTheme(theme.value)
|
|
}
|
|
|
|
export function useTheme() {
|
|
const isDark = computed(() => theme.value === 'dark')
|
|
|
|
function setTheme(value) {
|
|
const nextTheme = value === 'dark' ? 'dark' : 'light'
|
|
applyTheme(nextTheme, true)
|
|
try {
|
|
localStorage.setItem(STORAGE_KEY, nextTheme)
|
|
} catch {
|
|
// Theme still works for the current session when storage is unavailable.
|
|
}
|
|
}
|
|
|
|
return { theme, isDark, setTheme }
|
|
}
|
|
|
|
if (typeof window !== 'undefined') {
|
|
window.addEventListener('storage', event => {
|
|
if (event.key === STORAGE_KEY) applyTheme(event.newValue)
|
|
})
|
|
}
|