53863559f4
Retarget git remote, install path, and deploy docs from crypto_monitor to crypto_monitor_user. Co-authored-by: Cursor <cursoragent@cursor.com>
126 lines
3.6 KiB
JavaScript
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, "&")
|
|
.replace(/</g, "<")
|
|
.replace(/>/g, ">")
|
|
.replace(/"/g, """);
|
|
}
|
|
|
|
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() {},
|
|
};
|
|
})();
|