feat: miller column browser, offline cache, search, breadcrumbs
- Dynamic miller column sizing with flexbox layout - Root column (160px), middle slices (48px), active column (flex) - Auto-scroll browser to rightmost column on navigation - Offline indicator UI and IndexedDB cache integration - /api/documents endpoint for client-side document caching - Breadcrumb navigation in document content area - FTS5 search with SQLite virtual table - Client-side Mermaid and KaTeX rendering via CDN - Deep sample content (advanced-topics/raft-deep-dive) - Border removal (only search button keeps radius) - Full viewport layout with proper borders between sidebar/content
This commit is contained in:
81
apps/server/internal/httpserver/static/render.js
Normal file
81
apps/server/internal/httpserver/static/render.js
Normal file
@@ -0,0 +1,81 @@
|
||||
(function () {
|
||||
function renderMermaid() {
|
||||
if (typeof mermaid === "undefined") return;
|
||||
const blocks = document.querySelectorAll("pre code.language-mermaid");
|
||||
blocks.forEach(function (block) {
|
||||
const pre = block.parentElement;
|
||||
const container = document.createElement("div");
|
||||
container.className = "mermaid";
|
||||
container.textContent = block.textContent;
|
||||
pre.replaceWith(container);
|
||||
});
|
||||
mermaid.initialize({ startOnLoad: false });
|
||||
mermaid.run({ querySelector: ".mermaid" });
|
||||
}
|
||||
|
||||
function renderMath() {
|
||||
if (typeof katex === "undefined") return;
|
||||
|
||||
const blocks = document.querySelectorAll("pre code.language-math");
|
||||
blocks.forEach(function (block) {
|
||||
const pre = block.parentElement;
|
||||
const container = document.createElement("div");
|
||||
container.className = "katex-block";
|
||||
try {
|
||||
katex.render(block.textContent.trim(), container, {
|
||||
displayMode: true,
|
||||
throwOnError: false,
|
||||
});
|
||||
pre.replaceWith(container);
|
||||
} catch (e) {
|
||||
console.warn("KaTeX render failed:", e);
|
||||
}
|
||||
});
|
||||
|
||||
const markdownBody = document.querySelector(".markdown-body");
|
||||
if (!markdownBody) return;
|
||||
|
||||
const html = markdownBody.innerHTML;
|
||||
let newHtml = html;
|
||||
|
||||
newHtml = newHtml.replace(/\$\$([\s\S]+?)\$\$/g, function (match, tex) {
|
||||
try {
|
||||
return katex.renderToString(tex.trim(), {
|
||||
displayMode: true,
|
||||
throwOnError: false,
|
||||
});
|
||||
} catch (e) {
|
||||
return match;
|
||||
}
|
||||
});
|
||||
|
||||
newHtml = newHtml.replace(/\$([^\s$][^$]*?)\$/g, function (match, tex) {
|
||||
try {
|
||||
return katex.renderToString(tex.trim(), {
|
||||
displayMode: false,
|
||||
throwOnError: false,
|
||||
});
|
||||
} catch (e) {
|
||||
return match;
|
||||
}
|
||||
});
|
||||
|
||||
if (newHtml !== html) {
|
||||
markdownBody.innerHTML = newHtml;
|
||||
}
|
||||
}
|
||||
|
||||
function init() {
|
||||
renderMermaid();
|
||||
renderMath();
|
||||
}
|
||||
|
||||
window.renderMermaid = renderMermaid;
|
||||
window.renderMath = renderMath;
|
||||
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", init);
|
||||
} else {
|
||||
init();
|
||||
}
|
||||
})();
|
||||
Reference in New Issue
Block a user