// ===== State ===== let state = { status: {}, kernel: null, settings: {}, meta: {}, runtime: null, // /api/v1/runtime 的运行态小快照(调度器/驻留子/通道) pluginMeta: {}, disabledPlugins: [], settingsPlugins: ["core"], selectedSection: "core", messages: [], chatLoading: false, chatStage: "", pipelinePhase: "", // 当前阶段(SSE stage 事件驱动总览页) pipelineTimer: null, stageTrail: [], // 本轮已发生的事件轨迹(工具/输出调用,供阶段管道展示循环) toolFlash: false, // 本轮新到一条工具调用:本轮渲染后给「工具」格放一次滑入动画 healthResult: null, starmapInit: false, starmapLoading: false, starmapData: null, chatHistory: [], terminals: [], cmdHistory: [], termScreens: {}, chatStick: true, pendingTools: [], eventSource: null, chatFinalIdx: -1, chatOffset: 0, // 分段历史:当前已加载消息在服务端全量中的起始下标 chatTotal: 0, // 服务端历史总条数 chatHasMore: false, // 是否还有更早历史可向上加载 chatLastSeq: 0, // 增量轮询游标:已合并到本地的最大消息 seq lang: localStorage.getItem("ha-lang") || "zh", }; // ===== I18n ===== window._i18n = { navOverview: ["概览", "Overview"], navChat: ["对话", "Chat"], navPlugins: ["插件", "Plugins"], navSettings: ["设置", "Settings"], navAdapters: ["适配器", "Adapters"], navKernel: ["内核", "Kernel"], navLogout: ["退出登录", "Logout"], themeToggle: ["切换亮色/暗色模式", "Toggle theme"], }; function __(zh, en) { return state.lang === "en" ? en : zh; } function L() { return state.lang; } function toggleLang() { state.lang = state.lang === "zh" ? "en" : "zh"; localStorage.setItem("ha-lang", state.lang); document.querySelectorAll("[data-i18n]").forEach(function (el) { var k = el.getAttribute("data-i18n"); var m = window._i18n && window._i18n[k]; if (m) el.textContent = __(m[0], m[1]); }); renderAll(); } function applyI18n() { var lang = state.lang; var btn = document.getElementById("lang-btn"); if (btn) btn.textContent = lang === "zh" ? "EN" : "中"; document.querySelectorAll("[data-i18n]").forEach(function (el) { var k = el.getAttribute("data-i18n"); var m = window._i18n && window._i18n[k]; if (m) el.textContent = lang === "en" ? m[1] : m[0]; }); } // ===== Theme ===== var ICON_SUN = ''; var ICON_MOON = ''; function setTheme(name) { document.documentElement.setAttribute("data-theme", name); var btn = document.getElementById("theme-btn"); if (btn) btn.innerHTML = name === "light" ? ICON_SUN : ICON_MOON; } function saveTheme(name) { localStorage.setItem("ha-theme", name); setTheme(name); } function toggleTheme() { var cur = document.documentElement.getAttribute("data-theme"); saveTheme(cur === "light" ? "dark" : "light"); } var PALETTES = { // mono 放第一位:它现在是默认配色(黑白单调),也应当是选择器里最显眼那个。 mono: "#c2c7d0", sakura: "#ff7fac", cyan: "#2dd4bf", violet: "#a78bfa", emerald: "#34d399", amber: "#fbbf24", blue: "#60a5fa", }; function setColor(name) { document.documentElement.setAttribute("data-color", name); localStorage.setItem("ha-color", name); // 配色变了要强制重画运行态:拓扑的归属配色跟着 data-color 走, // 而数据签名没变,不置空 _rtSig 的话图会一直停在旧色。 _rtSig = null; if (document.getElementById("rt-sec-topo")) { try { renderRuntime(); } catch (e) {} } var pop = document.getElementById("palette-pop"); if (!pop) return; var btns = pop.querySelectorAll("button.cdot"); for (var i = 0; i < btns.length; i++) { btns[i].className = btns[i].getAttribute("data-c") === name ? "cdot on" : "cdot"; } } function applyBgImg(url) { url = (url || "").trim(); if (!url) { document.documentElement.style.setProperty("--bg-img", "none"); localStorage.removeItem("ha-bg-img"); return; } document.documentElement.style.setProperty( "--bg-img", 'url("' + url.replace(/"/g, '\\"') + '")', ); localStorage.setItem("ha-bg-img", url); } function applyBgBlur(n) { n = Math.max(0, Math.min(30, Number(n) || 0)); document.documentElement.style.setProperty("--bg-blur", String(n)); localStorage.setItem("ha-bg-blur", String(n)); var v = document.getElementById("bg-blur-val"); if (v) v.textContent = n + "px"; var r = document.getElementById("bg-blur-range"); if (r) r.value = String(n); } function toggleAppearance() { var pop = document.getElementById("palette-pop"); if (!pop) return; var on = pop.classList.contains("on"); if (!pop.querySelector("button.cdot")) { var cur = localStorage.getItem("ha-color") || "mono"; var img = localStorage.getItem("ha-bg-img") || ""; var blur = localStorage.getItem("ha-bg-blur") || "0"; var dots = ""; Object.keys(PALETTES).forEach(function (k) { dots += '"; }); pop.innerHTML = "
" + escHtml(text) + ""; } if ( typeof DOMPurify !== "undefined" && typeof DOMPurify.sanitize === "function" ) { try { return DOMPurify.sanitize(html, { USE_PROFILES: { html: true } }); } catch (e) {} } return html .replace(/