/* LokiStack 文档站交互:搜索 / 代码复制 / 目录高亮 / 移动端导航(参考 opsxapi 体系) */ (function () { "use strict"; /* ---------- 代码块:语言标签 + 复制按钮 + 轻量高亮 ---------- */ document.querySelectorAll("article.md pre").forEach(function (pre) { var code = pre.querySelector("code"); if (!code) return; var lang = (code.className.match(/language-([\w-]+)/) || [, ""])[1]; if (lang) { var tag = document.createElement("span"); tag.className = "lang-tag"; tag.textContent = lang; pre.appendChild(tag); } // 复制按钮 var btn = document.createElement("button"); btn.className = "copy-btn"; btn.type = "button"; btn.textContent = "复制"; btn.addEventListener("click", function () { navigator.clipboard.writeText(code.textContent).then( function () { btn.textContent = "已复制 ✓"; setTimeout(function () { btn.textContent = "复制"; }, 1400); }, function () { btn.textContent = "复制失败"; } ); }); pre.appendChild(btn); // JSON / bash 轻量高亮 if (lang === "json") highlightJSON(code); else if (lang === "bash" || lang === "sh" || lang === "shell") highlightBash(code); }); function esc(s) { return s.replace(/&/g, "&").replace(//g, ">"); } function highlightJSON(code) { var src = code.textContent; code.innerHTML = esc(src).replace( /("(?:\\.|[^"\\])*")(\s*:)?|\b(true|false|null)\b|(-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?)/g, function (m, str, colon, kw, num) { if (str && colon) return '' + str + "" + colon; if (str) return '' + str + ""; if (kw) return '' + kw + ""; if (num) return '' + num + ""; return m; } ); } function highlightBash(code) { var lines = code.textContent.split("\n"); code.innerHTML = lines .map(function (line) { if (/^\s*#/.test(line)) return '' + esc(line) + ""; return esc(line).replace(/(^|\s)(-{1,2}[\w-]+)/g, '$1$2'); }) .join("\n"); } /* ---------- blockquote 提示样式约定:> **提示** / > **注意** ---------- */ document.querySelectorAll("article.md blockquote").forEach(function (bq) { var t = (bq.textContent || "").trim(); if (/^(注意|警告|⚠)/.test(t)) bq.classList.add("warn"); else if (/^(提示|说明|💡|Tip)/i.test(t)) bq.classList.add("tip"); }); /* ---------- 搜索 ---------- */ var input = document.getElementById("search-input"); var panel = document.getElementById("search-panel"); var entries = null; var selIndex = -1; function ensureIndex(cb) { if (entries) return cb(); fetch("/docs/search-data.json") .then(function (r) { return r.json(); }) .then(function (data) { entries = data; cb(); }) .catch(function () { entries = []; cb(); }); } function snippet(text, q) { var i = text.toLowerCase().indexOf(q); if (i < 0) return text.slice(0, 60) + "…"; var start = Math.max(0, i - 24); return (start > 0 ? "…" : "") + text.slice(start, i + q.length + 40) + "…"; } function doSearch() { var q = input.value.trim().toLowerCase(); selIndex = -1; if (!q) { panel.hidden = true; return; } ensureIndex(function () { var hits = []; entries.forEach(function (e) { var titleHit = e.title.toLowerCase().indexOf(q) >= 0; var idx = e.text.toLowerCase().indexOf(q); if (titleHit || idx >= 0) { hits.push({ slug: e.slug, title: e.title, group: e.group, score: (titleHit ? 10 : 0) + (idx === 0 ? 3 : 0), snippet: titleHit ? snippet(e.text, q) : snippet(e.text, q), }); } }); hits.sort(function (a, b) { return b.score - a.score; }); hits = hits.slice(0, 8); if (!hits.length) { panel.innerHTML = '