116 lines
4.0 KiB
JavaScript
116 lines
4.0 KiB
JavaScript
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);
|
|
}
|