120 lines
3.9 KiB
JavaScript
120 lines
3.9 KiB
JavaScript
(() => {
|
|
const THEME_KEY = 'grok2api_theme_preference';
|
|
const THEME_LIGHT = 'light';
|
|
const THEME_DARK = 'dark';
|
|
const THEME_AUTO = 'auto';
|
|
|
|
const metaThemeColor = document.querySelector('meta[name="theme-color"]');
|
|
|
|
function safeGetPreference() {
|
|
try {
|
|
const stored = localStorage.getItem(THEME_KEY);
|
|
if (stored === THEME_LIGHT || stored === THEME_DARK || stored === THEME_AUTO) {
|
|
return stored;
|
|
}
|
|
} catch (e) {
|
|
// 忽略 localStorage 访问异常
|
|
}
|
|
return THEME_AUTO;
|
|
}
|
|
|
|
function safeSetPreference(preference) {
|
|
try {
|
|
localStorage.setItem(THEME_KEY, preference);
|
|
} catch (e) {
|
|
// 忽略 localStorage 写入异常
|
|
}
|
|
}
|
|
|
|
function resolvePreference(preference) {
|
|
if (preference === THEME_LIGHT || preference === THEME_DARK) {
|
|
return preference;
|
|
}
|
|
if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
|
|
return THEME_DARK;
|
|
}
|
|
return THEME_LIGHT;
|
|
}
|
|
|
|
function syncThemeMeta(resolvedTheme) {
|
|
if (!metaThemeColor) return;
|
|
metaThemeColor.setAttribute('content', resolvedTheme === THEME_DARK ? '#0b0f14' : '#fafafa');
|
|
}
|
|
|
|
function syncThemeButtons(preference, resolvedTheme) {
|
|
const buttons = document.querySelectorAll('[data-theme-toggle-btn]');
|
|
buttons.forEach((btn) => {
|
|
const nextLabel = resolvedTheme === THEME_DARK ? '日间模式' : '深夜模式';
|
|
const nextTitle = resolvedTheme === THEME_DARK ? '切换到日间模式' : '切换到深夜模式';
|
|
btn.textContent = nextLabel;
|
|
btn.setAttribute('title', nextTitle);
|
|
btn.classList.toggle('theme-active', resolvedTheme === THEME_DARK);
|
|
btn.setAttribute('aria-pressed', resolvedTheme === THEME_DARK ? 'true' : 'false');
|
|
btn.setAttribute('data-theme-preference', preference);
|
|
btn.setAttribute('data-theme-resolved', resolvedTheme);
|
|
});
|
|
}
|
|
|
|
function applyTheme(preference, persist = false) {
|
|
const normalized = (preference === THEME_LIGHT || preference === THEME_DARK || preference === THEME_AUTO)
|
|
? preference
|
|
: THEME_AUTO;
|
|
const resolved = resolvePreference(normalized);
|
|
const root = document.documentElement;
|
|
root.setAttribute('data-theme-preference', normalized);
|
|
root.setAttribute('data-theme', resolved);
|
|
syncThemeMeta(resolved);
|
|
syncThemeButtons(normalized, resolved);
|
|
if (persist) {
|
|
safeSetPreference(normalized);
|
|
}
|
|
}
|
|
|
|
function toggleTheme() {
|
|
const current = document.documentElement.getAttribute('data-theme') === THEME_DARK ? THEME_DARK : THEME_LIGHT;
|
|
const next = current === THEME_DARK ? THEME_LIGHT : THEME_DARK;
|
|
applyTheme(next, true);
|
|
}
|
|
|
|
function handleThemeButtonClick(event) {
|
|
const target = event.target;
|
|
if (!(target instanceof Element)) return;
|
|
const button = target.closest('[data-theme-toggle-btn]');
|
|
if (!button) return;
|
|
event.preventDefault();
|
|
toggleTheme();
|
|
}
|
|
|
|
function init() {
|
|
applyTheme(safeGetPreference(), false);
|
|
document.addEventListener('click', handleThemeButtonClick);
|
|
|
|
if (window.matchMedia) {
|
|
const media = window.matchMedia('(prefers-color-scheme: dark)');
|
|
const onSystemThemeChange = () => {
|
|
if (safeGetPreference() === THEME_AUTO) {
|
|
applyTheme(THEME_AUTO, false);
|
|
}
|
|
};
|
|
if (typeof media.addEventListener === 'function') {
|
|
media.addEventListener('change', onSystemThemeChange);
|
|
} else if (typeof media.addListener === 'function') {
|
|
media.addListener(onSystemThemeChange);
|
|
}
|
|
}
|
|
}
|
|
|
|
window.themeController = {
|
|
applyTheme,
|
|
refreshButtons() {
|
|
const preference = document.documentElement.getAttribute('data-theme-preference') || safeGetPreference();
|
|
const resolved = document.documentElement.getAttribute('data-theme') || resolvePreference(preference);
|
|
syncThemeButtons(preference, resolved);
|
|
syncThemeMeta(resolved);
|
|
},
|
|
getPreference: safeGetPreference,
|
|
};
|
|
|
|
init();
|
|
})();
|