const THEME_STORAGE_KEY = "xiaobaiTheme"; let activeThemeTransition = null; let themeSwitchSequence = 0; function syncThemeControl() { const theme = document.documentElement.dataset.theme === "dark" ? "dark" : "light"; const button = document.querySelector("#themeToggle"); if (!button) return; const dark = theme === "dark"; const label = dark ? "切换到日间模式" : "切换到夜间模式"; button.title = label; button.setAttribute("aria-label", label); button.setAttribute("aria-pressed", String(dark)); button.querySelector("i")?.setAttribute("data-lucide", dark ? "sun" : "moon"); } function clearThemeTransitionEffects() { document.querySelectorAll(".row-enter, .row-pending, .view-entering").forEach((element) => { element.classList.remove("row-enter", "row-pending", "view-entering"); element.style.removeProperty("--row-delay"); }); } function redrawThemeSensitiveVisuals() { if (!elements.stockPreview.hidden && state.stockPreviewPayload) { selectStockPreviewChart(state.stockPreviewChart); } if (elements.stockDialog.open) { if (state.stockDetailChartMode === "intraday" && state.stockDetailIntraday?.points?.length) { drawIntradayCanvas( elements.priceChart, state.stockDetailIntraday.points, [], state.stockDetailIntraday.meta?.previous_close, ); } else if (state.stockDetail?.prices) drawPriceChart(state.stockDetail.prices); } if (elements.entityDetailDialog.open) { if (state.entityDetailChartMode === "intraday" && state.entityDetailIntraday?.points?.length) { drawIntradayCanvas( elements.entityDetailChart, state.entityDetailIntraday.points, [], state.entityDetailIntraday.meta?.previous_close, ); } else if (state.entityDetailPayload?.series) { drawEntityDetailChart(state.entityDetailPayload.series); } } if (state.activeView === "sentimentCycleView" && state.sentimentHistory) { drawSentimentTrendChart(state.sentimentHistory.rows || []); } if (state.activeView === "heavenView") { if (state.heavenPanel === "fortune" && state.heavenSetup?.field) { renderQiFieldCanvas(state.heavenSetup.field.balance || [], { intro: false }); drawQiUseConnections(false); } if (state.heavenPanel === "heart") startHeartDust(); } } function commitTheme(normalized, persist) { document.documentElement.dataset.theme = normalized; document.documentElement.style.colorScheme = normalized; if (persist) { try { localStorage.setItem(THEME_STORAGE_KEY, normalized); } catch (_error) { // The selected theme still applies for the current page when storage is unavailable. } } syncThemeControl(); refreshIcons(); redrawThemeSensitiveVisuals(); } function applyTheme(theme, persist = true) { const normalized = theme === "dark" ? "dark" : "light"; const root = document.documentElement; if (root.dataset.theme === normalized) { commitTheme(normalized, persist); return; } const sequence = ++themeSwitchSequence; activeThemeTransition?.skipTransition?.(); clearThemeTransitionEffects(); root.classList.add("theme-switching"); const update = () => commitTheme(normalized, persist); const finish = () => { if (sequence !== themeSwitchSequence) return; clearThemeTransitionEffects(); root.classList.remove("theme-switching"); activeThemeTransition = null; }; const reducedMotion = window.matchMedia?.("(prefers-reduced-motion: reduce)").matches; if (!reducedMotion && typeof document.startViewTransition === "function") { activeThemeTransition = document.startViewTransition(update); activeThemeTransition.finished.then(finish, finish); return; } update(); requestAnimationFrame(() => requestAnimationFrame(finish)); } function toggleTheme() { applyTheme(document.documentElement.dataset.theme === "dark" ? "light" : "dark"); } function bindThemeEvents() { document.querySelector("#themeToggle").addEventListener("click", toggleTheme); window.addEventListener("resize", redrawThemeSensitiveVisuals); }