feat(hub): add dark/light theme toggle with moon and sun icons

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-06-04 12:03:48 +08:00
parent b394e495ca
commit d1914df46f
6 changed files with 327 additions and 19 deletions
+61
View File
@@ -0,0 +1,61 @@
/** 中控主题:暗色(默认)/ 亮色,localStorage hub-theme */
(function (global) {
const KEY = "hub-theme";
const META = { dark: "#0b0e18", light: "#e8eef5" };
function normalize(theme) {
return theme === "light" ? "light" : "dark";
}
function get() {
try {
return normalize(localStorage.getItem(KEY));
} catch (_) {
return "dark";
}
}
function apply(theme) {
const t = normalize(theme);
const root = document.documentElement;
root.setAttribute("data-theme", t);
try {
localStorage.setItem(KEY, t);
} catch (_) {}
const meta = document.querySelector('meta[name="theme-color"]');
if (meta) meta.setAttribute("content", META[t]);
root.style.colorScheme = t;
document.dispatchEvent(new CustomEvent("hub-theme-change", { detail: { theme: t } }));
return t;
}
function toggle() {
return apply(get() === "dark" ? "light" : "dark");
}
function syncToggleUI(root) {
const scope = root || document;
scope.querySelectorAll(".theme-toggle-btn[data-theme-value]").forEach((btn) => {
const on = btn.getAttribute("data-theme-value") === get();
btn.classList.toggle("is-active", on);
btn.setAttribute("aria-pressed", on ? "true" : "false");
});
}
function initToggleUI(root) {
const scope = root || document;
syncToggleUI(scope);
scope.querySelectorAll(".theme-toggle-btn[data-theme-value]").forEach((btn) => {
if (btn.dataset.themeBound === "1") return;
btn.dataset.themeBound = "1";
btn.addEventListener("click", () => {
apply(btn.getAttribute("data-theme-value"));
syncToggleUI(scope);
});
});
document.addEventListener("hub-theme-change", () => syncToggleUI(scope));
}
apply(get());
global.HubTheme = { KEY, get, apply, toggle, syncToggleUI, initToggleUI };
})(typeof window !== "undefined" ? window : globalThis);