87973eaa4a
Der hardcodierte Inline-Style auf body überschrieb --app-body-bg und ließ hellen Modus mit dunklem Seitenhintergrund erscheinen. Theme-Bootstrap und dynamisches theme-color ergänzen alle Scheme/Theme-Kombinationen. Co-authored-by: Cursor <cursoragent@cursor.com>
69 lines
2.6 KiB
TypeScript
69 lines
2.6 KiB
TypeScript
import { getColorSchemePreference as getStoredColorScheme, getThemePreference } from './userPreferences.js'
|
|
|
|
export type ColorSchemePreference = 'auto' | 'light' | 'dark'
|
|
export type ResolvedColorScheme = 'light' | 'dark'
|
|
export type AppTheme = 'ocean' | 'material' | 'cupertino'
|
|
|
|
const THEME_CLASSES = ['theme-ocean', 'theme-material', 'theme-cupertino'] as const
|
|
const SCHEME_CLASSES = ['scheme-light', 'scheme-dark'] as const
|
|
|
|
export function getColorSchemePreference(): ColorSchemePreference {
|
|
const stored = getStoredColorScheme()
|
|
if (stored === 'light' || stored === 'dark' || stored === 'auto') return stored
|
|
return 'auto'
|
|
}
|
|
|
|
export function resolveColorScheme(pref?: ColorSchemePreference): ResolvedColorScheme {
|
|
const preference = pref ?? getColorSchemePreference()
|
|
if (preference === 'light') return 'light'
|
|
if (preference === 'dark') return 'dark'
|
|
return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
|
|
}
|
|
|
|
export function resolveAppTheme(): AppTheme {
|
|
const configTheme = getThemePreference() || 'auto'
|
|
if (configTheme === 'material' || configTheme === 'cupertino' || configTheme === 'ocean') {
|
|
return configTheme
|
|
}
|
|
const userAgent = navigator.userAgent || navigator.vendor || ''
|
|
if (/iPad|iPhone|iPod|Macintosh/.test(userAgent)) return 'cupertino'
|
|
if (/Android|Linux/.test(userAgent)) return 'material'
|
|
return 'ocean'
|
|
}
|
|
|
|
function updateThemeColorMeta(root: HTMLElement): void {
|
|
const color = getComputedStyle(root).getPropertyValue('--app-theme-color').trim()
|
|
if (!color) return
|
|
let meta = document.querySelector('meta[name="theme-color"]')
|
|
if (!meta) {
|
|
meta = document.createElement('meta')
|
|
meta.setAttribute('name', 'theme-color')
|
|
document.head.appendChild(meta)
|
|
}
|
|
meta.setAttribute('content', color)
|
|
}
|
|
|
|
export function applyAppearanceToDocument(
|
|
theme: AppTheme = resolveAppTheme(),
|
|
scheme: ResolvedColorScheme = resolveColorScheme()
|
|
): void {
|
|
const root = document.documentElement
|
|
root.classList.remove(...THEME_CLASSES, ...SCHEME_CLASSES)
|
|
root.classList.add(`theme-${theme}`, `scheme-${scheme}`)
|
|
root.style.colorScheme = scheme
|
|
updateThemeColorMeta(root)
|
|
}
|
|
|
|
export function subscribeToSystemColorScheme(onChange: () => void): () => void {
|
|
const media = window.matchMedia('(prefers-color-scheme: dark)')
|
|
const handler = () => {
|
|
if (getColorSchemePreference() === 'auto') onChange()
|
|
}
|
|
media.addEventListener('change', handler)
|
|
return () => media.removeEventListener('change', handler)
|
|
}
|
|
|
|
export function notifyAppearanceChanged(): void {
|
|
window.dispatchEvent(new Event('appearance-changed'))
|
|
}
|