2026-05-10 14:01:29 +08:00
|
|
|
import { ref, watch, computed } from 'vue'
|
2026-04-16 23:13:04 +08:00
|
|
|
|
2026-05-10 14:01:29 +08:00
|
|
|
export type BrightnessMode = 'light' | 'dark' | 'system'
|
|
|
|
|
export type ThemeStyle = 'ink' | 'comic'
|
2026-04-16 23:13:04 +08:00
|
|
|
|
2026-05-10 14:01:29 +08:00
|
|
|
const BRIGHTNESS_KEY = 'hermes_brightness'
|
|
|
|
|
const STYLE_KEY = 'hermes_style'
|
2026-04-16 23:13:04 +08:00
|
|
|
|
2026-05-10 14:01:29 +08:00
|
|
|
const brightness = ref<BrightnessMode>(
|
|
|
|
|
(localStorage.getItem(BRIGHTNESS_KEY) as BrightnessMode) || 'system',
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const style = ref<ThemeStyle>(
|
|
|
|
|
(localStorage.getItem(STYLE_KEY) as ThemeStyle) || 'ink',
|
2026-04-16 23:13:04 +08:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const isDark = ref(false)
|
2026-05-10 14:01:29 +08:00
|
|
|
const isComic = ref(false)
|
2026-04-16 23:13:04 +08:00
|
|
|
|
2026-05-10 14:01:29 +08:00
|
|
|
function resolveDark(b: BrightnessMode): boolean {
|
|
|
|
|
if (b === 'system') {
|
2026-04-16 23:13:04 +08:00
|
|
|
return window.matchMedia('(prefers-color-scheme: dark)').matches
|
|
|
|
|
}
|
2026-05-10 14:01:29 +08:00
|
|
|
return b === 'dark'
|
2026-04-16 23:13:04 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-10 14:01:29 +08:00
|
|
|
function applyClasses() {
|
|
|
|
|
const dark = resolveDark(brightness.value)
|
|
|
|
|
isDark.value = dark
|
|
|
|
|
isComic.value = style.value === 'comic'
|
|
|
|
|
document.documentElement.classList.toggle('dark', dark)
|
|
|
|
|
document.documentElement.classList.toggle('comic', isComic.value)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Initial
|
|
|
|
|
applyClasses()
|
2026-04-16 23:13:04 +08:00
|
|
|
|
|
|
|
|
// Listen for system preference changes
|
2026-05-10 14:01:29 +08:00
|
|
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', () => {
|
|
|
|
|
if (brightness.value === 'system') {
|
|
|
|
|
applyClasses()
|
2026-04-16 23:13:04 +08:00
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
2026-05-10 14:01:29 +08:00
|
|
|
// Persist & apply on change
|
|
|
|
|
watch(brightness, (b) => {
|
|
|
|
|
localStorage.setItem(BRIGHTNESS_KEY, b)
|
|
|
|
|
applyClasses()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
watch(style, (s) => {
|
|
|
|
|
localStorage.setItem(STYLE_KEY, s)
|
|
|
|
|
applyClasses()
|
2026-04-16 23:13:04 +08:00
|
|
|
})
|
|
|
|
|
|
|
|
|
|
export function useTheme() {
|
2026-05-10 14:01:29 +08:00
|
|
|
const themeName = computed(() => {
|
|
|
|
|
const b = isDark.value ? 'dark' : 'light'
|
|
|
|
|
return isComic.value ? `comic-${b}` : b
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
function setBrightness(b: BrightnessMode) {
|
|
|
|
|
brightness.value = b
|
2026-04-16 23:13:04 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-10 14:01:29 +08:00
|
|
|
function setStyle(s: ThemeStyle) {
|
|
|
|
|
style.value = s
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function toggleBrightness() {
|
|
|
|
|
brightness.value = isDark.value ? 'light' : 'dark'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function toggleStyle() {
|
|
|
|
|
style.value = isComic.value ? 'ink' : 'comic'
|
2026-04-16 23:13:04 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
2026-05-10 14:01:29 +08:00
|
|
|
brightness,
|
|
|
|
|
style,
|
2026-04-16 23:13:04 +08:00
|
|
|
isDark,
|
2026-05-10 14:01:29 +08:00
|
|
|
isComic,
|
|
|
|
|
themeName,
|
|
|
|
|
setBrightness,
|
|
|
|
|
setStyle,
|
|
|
|
|
toggleBrightness,
|
|
|
|
|
toggleStyle,
|
2026-04-16 23:13:04 +08:00
|
|
|
}
|
|
|
|
|
}
|