/** * 四所实例主题:默认暗色;单独登录用 instance-theme;中控 iframe/SSO 随 hub-theme 联动。 */ (function (global) { const STANDALONE_KEY = "instance-theme"; const META = { dark: "#0b0d14", light: "#d8e2ec" }; function normalize(theme) { return theme === "light" ? "light" : "dark"; } function isHubLinked() { try { const p = new URLSearchParams(location.search); if (p.get("embed") === "1") return true; const ht = p.get("hub_theme"); if (ht === "light" || ht === "dark") return true; } catch (_) {} try { if (window.self !== window.top) return true; } catch (_) { return true; } return false; } function themeFromUrl() { try { const t = new URLSearchParams(location.search).get("hub_theme"); if (t === "light" || t === "dark") return t; } catch (_) {} return null; } function getStandalone() { try { return normalize(localStorage.getItem(STANDALONE_KEY)); } catch (_) { return "dark"; } } function setStandalone(theme) { try { localStorage.setItem(STANDALONE_KEY, normalize(theme)); } catch (_) {} } function get() { if (isHubLinked()) { return themeFromUrl() || _linkedTheme || "dark"; } return getStandalone(); } let _linkedTheme = null; /** 模板内联暗色 → 亮色(切换时重写 style 属性) */ const INLINE_HEX_LIGHT = { "#cfd3ef": "#1a2838", "#8892b0": "#4a6078", "#9aa3c4": "#4a6078", "#8b95a8": "#4a6078", "#8b95b8": "#4a6078", "#6a7598": "#4a6078", "#7d8799": "#4a6078", "#6d7689": "#4a6078", "#dbe4ff": "#142232", "#f0f2ff": "#142232", "#e8ecf4": "#142232", "#c5cce0": "#4a6078", "#b8c4ff": "#142232", "#8fc8ff": "#006e9a", "#6ab8ff": "#006e9a", "#6eb5ff": "#006e9a", "#101522": "#ffffff", "#121726": "#ffffff", "#141423": "#ffffff", "#24243b": "#b8c8d8", "#252a45": "#b8c8d8", "#252538": "#eef3f8", "#1a1a29": "#f6f9fc", "#2e2e45": "#b8c8d8", "#2b2b43": "#d0dae4", "#151a2a": "#eef3f8", "#141a2a": "#ffffff", "#141923": "#ffffff", "#141a2e": "#ffffff", "#0f1424": "#f6f9fc", "#0f1420": "#f6f9fc", "#0f1117": "#d8e2ec", "#1a2034": "#eef3f8", "#1a2030": "#ffffff", "#1f3a5a": "#e8eef5", "#2f2f44": "#dde5ec", "#2a3f6c": "rgba(0,110,154,0.14)", "#304164": "rgba(0,95,140,0.22)", "#2a3150": "#b8c8d8", "#2a3152": "#b8c8d8", "#3a5a8a": "rgba(0,95,140,0.35)", "#2a3348": "#b8c8d8", "#243050": "rgba(0,75,115,0.16)", "#2a3558": "#d0dae4", "#3a4468": "#c8d4e0", "#3a4a66": "#b8c8d8", "#3a3f52": "#dde5ec", "#3d4659": "#b8c8d8", "#1f2740": "#eef3f8", "#1f2a44": "rgba(0,110,154,0.1)", "#1f4a3a": "#e8f5ef", "#2a4a7a": "#e8eef5", "#3a3048": "#eef3f8", "#d4b8ff": "#5b4fc7", "#e6e8ef": "#1a2838", }; function remapInlineStyle(style, theme) { if (!style) return style; if (theme !== "light") return style; let out = style; for (const [from, to] of Object.entries(INLINE_HEX_LIGHT)) { out = out.replace(new RegExp(from.replace("#", "\\#"), "gi"), to); } return out; } function syncInlineStyles(theme, root) { const scope = root || document; scope.querySelectorAll("[style]").forEach((el) => { const raw = el.getAttribute("style"); if (!raw) return; if (!el.dataset.instStyleBase) { el.dataset.instStyleBase = raw; } const base = el.dataset.instStyleBase; el.setAttribute("style", theme === "light" ? remapInlineStyle(base, "light") : base); }); } function apply(theme, opts) { const options = opts || {}; const linked = isHubLinked(); const t = normalize(theme); if (linked) { _linkedTheme = t; } else if (!options.skipStore) { setStandalone(t); } const root = document.documentElement; root.setAttribute("data-theme", t); const meta = document.querySelector('meta[name="theme-color"]'); if (meta) meta.setAttribute("content", META[t]); root.style.colorScheme = t; syncInlineStyles(t); syncToggleUI(); document.dispatchEvent( new CustomEvent("instance-theme-change", { detail: { theme: t, hubLinked: linked } }) ); return t; } function syncToggleUI(root) { const scope = root || document; const linked = isHubLinked(); const toggle = scope.querySelector(".instance-theme-toggle"); if (toggle) { toggle.classList.toggle("is-hub-linked", linked); toggle.setAttribute("aria-hidden", linked ? "true" : "false"); } if (linked) return; scope.querySelectorAll(".theme-toggle-btn[data-theme-value]").forEach((btn) => { const on = btn.getAttribute("data-theme-value") === getStandalone(); 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", () => { if (isHubLinked()) return; apply(btn.getAttribute("data-theme-value")); }); }); } function initFromHubMessage(data) { if (!data || data.type !== "hub-theme-sync") return; if (!isHubLinked()) return; apply(data.theme, { skipStore: true }); } function boot() { if (isHubLinked()) { apply(themeFromUrl() || "dark", { skipStore: true }); window.addEventListener("message", (ev) => initFromHubMessage(ev.data)); try { window.parent.postMessage({ type: "instance-theme-ready" }, "*"); } catch (_) {} } else { apply(getStandalone()); } function observeDynamicLists() { ["journal-list", "review-list"].forEach((id) => { const el = document.getElementById(id); if (!el || el.dataset.instThemeObserved === "1") return; el.dataset.instThemeObserved = "1"; new MutationObserver(() => syncInlineStyles(get())).observe(el, { childList: true, subtree: true, }); }); } const onReady = () => { initToggleUI(); syncInlineStyles(get()); observeDynamicLists(); }; if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", onReady); } else { onReady(); } document.addEventListener("instance-theme-change", (ev) => { const t = ev.detail && ev.detail.theme; if (t) syncInlineStyles(t); }); } boot(); global.InstanceTheme = { STANDALONE_KEY, isHubLinked, get, apply, initToggleUI, syncToggleUI, syncInlineStyles, }; })(typeof window !== "undefined" ? window : globalThis);