138 lines
3.4 KiB
JavaScript
138 lines
3.4 KiB
JavaScript
import { apiFetch } from "./api.js";
|
|
import { dom } from "./dom.js";
|
|
import { state } from "./state.js";
|
|
|
|
function escapeHtml(text) {
|
|
return text
|
|
.replaceAll("&", "&")
|
|
.replaceAll("<", "<")
|
|
.replaceAll(">", ">");
|
|
}
|
|
|
|
function slugify(text) {
|
|
return text
|
|
.toLowerCase()
|
|
.trim()
|
|
.replace(/[^\w\u4e00-\u9fa5]+/g, "-")
|
|
.replace(/^-+|-+$/g, "");
|
|
}
|
|
|
|
function parseMarkdown(text) {
|
|
const lines = text.split(/\r?\n/);
|
|
const blocks = [];
|
|
const headings = [];
|
|
let inCode = false;
|
|
let codeBuffer = [];
|
|
let paragraph = [];
|
|
|
|
const flushParagraph = () => {
|
|
if (!paragraph.length) {
|
|
return;
|
|
}
|
|
blocks.push(`<p>${escapeHtml(paragraph.join(" "))}</p>`);
|
|
paragraph = [];
|
|
};
|
|
|
|
const flushCode = () => {
|
|
if (!codeBuffer.length) {
|
|
return;
|
|
}
|
|
blocks.push(`<pre><code>${escapeHtml(codeBuffer.join("\n"))}</code></pre>`);
|
|
codeBuffer = [];
|
|
};
|
|
|
|
lines.forEach((line) => {
|
|
if (line.startsWith("```")) {
|
|
if (inCode) {
|
|
flushCode();
|
|
} else {
|
|
flushParagraph();
|
|
}
|
|
inCode = !inCode;
|
|
return;
|
|
}
|
|
|
|
if (inCode) {
|
|
codeBuffer.push(line);
|
|
return;
|
|
}
|
|
|
|
const heading = line.match(/^(#{1,4})\s+(.*)$/);
|
|
if (heading) {
|
|
flushParagraph();
|
|
const level = heading[1].length;
|
|
const textValue = heading[2].trim();
|
|
const id = slugify(textValue);
|
|
headings.push({ level, text: textValue, id });
|
|
blocks.push(`<h${level} id="${id}">${escapeHtml(textValue)}</h${level}>`);
|
|
return;
|
|
}
|
|
|
|
if (!line.trim()) {
|
|
flushParagraph();
|
|
return;
|
|
}
|
|
|
|
paragraph.push(line.trim());
|
|
});
|
|
|
|
flushParagraph();
|
|
flushCode();
|
|
|
|
return { html: blocks.join(""), headings };
|
|
}
|
|
|
|
async function loadDoc(url, emptyMessage) {
|
|
const text = await apiFetch(url);
|
|
const { html, headings } = parseMarkdown(text || "");
|
|
|
|
dom.apiDocContent.innerHTML = html || `<p>${emptyMessage}</p>`;
|
|
dom.apiDocToc.innerHTML = headings.length
|
|
? headings
|
|
.map(
|
|
(item) =>
|
|
`<a class="doc-toc-item level-${item.level}" href="#${item.id}">${escapeHtml(item.text)}</a>`,
|
|
)
|
|
.join("")
|
|
: "<div class=\"muted\">未解析到标题</div>";
|
|
|
|
dom.apiDocToc.querySelectorAll("a").forEach((link) => {
|
|
link.addEventListener("click", (event) => {
|
|
event.preventDefault();
|
|
const id = link.getAttribute("href")?.slice(1);
|
|
if (!id) {
|
|
return;
|
|
}
|
|
const target = dom.apiDocContent.querySelector(`#${CSS.escape(id)}`);
|
|
if (target) {
|
|
const offset = target.getBoundingClientRect().top - dom.apiDocContent.getBoundingClientRect().top;
|
|
dom.apiDocContent.scrollBy({ top: offset, behavior: "smooth" });
|
|
}
|
|
});
|
|
});
|
|
}
|
|
|
|
export async function openApiDocDrawer() {
|
|
const title = dom.apiDocDrawer.querySelector("h3");
|
|
if (title) title.textContent = "API.md";
|
|
dom.apiDocDrawer.classList.remove("hidden");
|
|
if (state.docDrawerSource !== "api") {
|
|
state.docDrawerSource = "api";
|
|
await loadDoc("/api/docs/api-md", "API.md 为空");
|
|
}
|
|
}
|
|
|
|
export async function openReadmeDrawer() {
|
|
const title = dom.apiDocDrawer.querySelector("h3");
|
|
if (title) title.textContent = "README.md";
|
|
dom.apiDocDrawer.classList.remove("hidden");
|
|
if (state.docDrawerSource !== "readme") {
|
|
state.docDrawerSource = "readme";
|
|
await loadDoc("/api/docs/readme-md", "README.md 为空");
|
|
}
|
|
}
|
|
|
|
export function closeApiDocDrawer() {
|
|
dom.apiDocDrawer.classList.add("hidden");
|
|
}
|