Files
MeBox/web/src/components/useThemeMode.ts
T
truewhile b0fe40142a Rebrand MMTL to MeBox (name, logo, Docker image) (#17)
* Rebrand MMTL to MeBox across codebase and assets

Rename the project display name, Go module path, environment variable
prefix (MEBOX_*), Docker image references, and UI branding from MMTL/mmtl
to MeBox/mebox. Replace logo assets with the new MeBox icon and keep
legacy SQLite migration support for existing mmtl.db deployments.

Co-authored-by: truewhile <truewhile@users.noreply.github.com>

* Fix logo icons: use cube-only crop without truncated text

Previous icon generation cropped too much of the source image, including
partial MeBox wordmark text that was cut off in square icon containers.
Regenerate logo-64/192/512, favicon, and SVG from cube-only region.

Co-authored-by: truewhile <truewhile@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: truewhile <truewhile@users.noreply.github.com>
2026-09-02 16:26:28 +08:00

66 lines
2.0 KiB
TypeScript

import { useCallback, useEffect, useState } from 'react'
export type ThemeMode = 'light' | 'dark' | 'system'
export type ResolvedTheme = 'light' | 'dark'
const THEME_STORAGE_KEY = 'mebox.theme'
const THEME_LEGACY_STORAGE_KEY = 'mediastationgo.theme'
const DARK_QUERY = '(prefers-color-scheme: dark)'
function readStoredTheme(): ThemeMode {
if (typeof window === 'undefined') return 'system'
const value = window.localStorage.getItem(THEME_STORAGE_KEY) || window.localStorage.getItem(THEME_LEGACY_STORAGE_KEY)
return value === 'light' || value === 'dark' || value === 'system' ? value : 'system'
}
function systemTheme(): ResolvedTheme {
if (typeof window === 'undefined') return 'light'
return window.matchMedia(DARK_QUERY).matches ? 'dark' : 'light'
}
function resolveTheme(mode: ThemeMode): ResolvedTheme {
return mode === 'system' ? systemTheme() : mode
}
function applyTheme(mode: ThemeMode) {
if (typeof document === 'undefined') return
const resolved = resolveTheme(mode)
const root = document.documentElement
root.dataset.themeMode = mode
root.dataset.theme = resolved
root.style.colorScheme = resolved
}
export function initializeThemeMode() {
applyTheme(readStoredTheme())
}
export function useThemeMode() {
const [mode, setModeState] = useState<ThemeMode>(() => readStoredTheme())
const [resolvedTheme, setResolvedTheme] = useState<ResolvedTheme>(() => resolveTheme(readStoredTheme()))
useEffect(() => {
applyTheme(mode)
setResolvedTheme(resolveTheme(mode))
window.localStorage.setItem(THEME_STORAGE_KEY, mode)
}, [mode])
useEffect(() => {
const media = window.matchMedia(DARK_QUERY)
const update = () => {
if (mode === 'system') {
applyTheme(mode)
setResolvedTheme(resolveTheme(mode))
}
}
media.addEventListener('change', update)
return () => media.removeEventListener('change', update)
}, [mode])
const setMode = useCallback((nextMode: ThemeMode) => {
setModeState(nextMode)
}, [])
return { mode, resolvedTheme, setMode }
}