Files
dekun 53863559f4 Initialize crypto_monitor_user (user edition) from monitor codebase.
Retarget git remote, install path, and deploy docs from crypto_monitor to crypto_monitor_user.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-17 16:18:13 +08:00

126 lines
3.6 KiB
JavaScript

/**
* 使用说明:中控 docs/help MD 章节.
*/
(function () {
const page = document.getElementById("page-help");
if (!page) return;
const tocEl = document.getElementById("help-toc-nav");
const statusEl = document.getElementById("help-load-status");
const docBody = document.getElementById("help-doc-body");
const docSource = document.getElementById("help-doc-source");
let sectionsMeta = [];
let activeKey = "quickstart";
let cache = {};
let bound = false;
async function apiFetch(url) {
const r = await fetch(url, { credentials: "same-origin" });
const data = await r.json();
if (!r.ok || !data.ok) throw new Error((data && data.msg) || r.statusText || "请求失败");
return data;
}
function esc(s) {
return String(s ?? "")
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
function sectionFromHash() {
const h = (window.location.hash || "").replace(/^#/, "").trim().toLowerCase();
if (!h) return null;
return sectionsMeta.some((s) => s.key === h) ? h : null;
}
function setHash(key) {
const next = `#${key}`;
if (window.location.hash !== next) {
history.replaceState(null, "", `/help${next}`);
}
}
function renderToc() {
if (!tocEl) return;
tocEl.innerHTML = sectionsMeta
.map(
(s) =>
`<button type="button" class="help-toc-item${s.key === activeKey ? " is-active" : ""}" data-key="${esc(s.key)}">${esc(s.label)}</button>`
)
.join("");
tocEl.querySelectorAll(".help-toc-item").forEach((btn) => {
btn.addEventListener("click", () => {
const key = btn.getAttribute("data-key");
if (!key || key === activeKey) return;
activeKey = key;
setHash(key);
renderToc();
void loadSection(key);
});
});
}
function renderSection(data) {
if (docBody) docBody.innerHTML = data.content_html || "";
if (docSource) {
docSource.textContent = data.md_source ? `来源: ${data.md_source}` : "";
}
if (statusEl) statusEl.textContent = "";
}
async function loadSection(key) {
if (cache[key]) {
renderSection(cache[key]);
return;
}
if (statusEl) statusEl.textContent = "加载中…";
try {
const data = await apiFetch(`/api/help/${encodeURIComponent(key)}`);
cache[key] = data;
renderSection(data);
} catch (err) {
if (statusEl) statusEl.textContent = "";
if (docBody) docBody.innerHTML = `<p class="muted">${esc(err.message || "加载失败")}</p>`;
}
}
async function loadMeta() {
const data = await apiFetch("/api/help/meta");
sectionsMeta = data.sections || [];
const fromHash = sectionFromHash();
if (fromHash) activeKey = fromHash;
else if (sectionsMeta.length && !sectionsMeta.some((s) => s.key === activeKey)) {
activeKey = sectionsMeta[0].key;
}
renderToc();
await loadSection(activeKey);
if (!sectionFromHash() && activeKey) setHash(activeKey);
}
function bindOnce() {
if (bound) return;
bound = true;
window.addEventListener("hashchange", () => {
const key = sectionFromHash();
if (!key || key === activeKey) return;
activeKey = key;
renderToc();
void loadSection(key);
});
}
window.hubHelpPage = {
init() {
bindOnce();
void loadMeta().catch((err) => {
if (statusEl) statusEl.textContent = "";
if (docBody) docBody.innerHTML = `<p class="muted">${esc(err.message || "加载失败")}</p>`;
});
},
destroy() {},
};
})();