mermaid js
All checks were successful
Build Org Website / build (push) Successful in 46s

This commit is contained in:
gitea-actions
2026-05-18 15:10:47 +01:00
parent d08965126f
commit 5cccf8a822
7 changed files with 414 additions and 73 deletions

View File

@@ -238,64 +238,101 @@ document.addEventListener("DOMContentLoaded", () => {
});
// Make Mermaid diagrams zoomable
window.addEventListener('load', function() {
// Initialize Mermaid (if not already)
mermaid.initialize({ startOnLoad: false, theme: 'neutral' });
// Make Mermaid diagrams use the active site theme and zoom controls.
(function () {
function cssVar(name, fallback) {
const value = getComputedStyle(document.documentElement).getPropertyValue(name).trim();
return value || fallback;
}
// Find all Mermaid divs
document.querySelectorAll('.mermaid').forEach(el => {
// Decode HTML entities
el.innerHTML = el.innerHTML
.replace(/>/g, '>')
.replace(/&lt;/g, '<')
.replace(/&amp;/g, '&');
function mermaidConfig() {
return {
startOnLoad: false,
theme: "base",
themeVariables: {
background: cssVar("--bg", "#faf9f6"),
mainBkg: cssVar("--surface", "#ffffff"),
primaryColor: cssVar("--surface", "#ffffff"),
primaryTextColor: cssVar("--fg", "#000000"),
primaryBorderColor: cssVar("--accent", "#2563eb"),
secondaryColor: cssVar("--surface-soft", "#f3f1eb"),
tertiaryColor: cssVar("--bg", "#faf9f6"),
clusterBkg: cssVar("--surface-soft", "#f3f1eb"),
clusterBorder: cssVar("--border", "#d7d7d7"),
lineColor: cssVar("--border", "#d7d7d7"),
textColor: cssVar("--fg", "#000000"),
edgeLabelBackground: cssVar("--surface", "#ffffff"),
fontFamily: cssVar("--font-body", "Noto, system-ui, sans-serif")
},
flowchart: {
htmlLabels: true,
curve: "basis"
}
};
}
// Wrap in container if not already wrapped
if (!el.parentElement.classList.contains('mermaid-container')) {
const container = document.createElement('div');
container.classList.add('mermaid-container');
function decodeMermaidSource(source) {
const textarea = document.createElement("textarea");
textarea.innerHTML = source;
return textarea.value.trim();
}
// Add zoom controls
const controls = document.createElement('div');
controls.classList.add('mermaid-zoom-controls');
async function renderMermaid() {
if (!window.mermaid) return;
const diagrams = document.querySelectorAll(".mermaid");
if (!diagrams.length) return;
diagrams.forEach((el) => {
if (!el.dataset.source) {
el.dataset.source = decodeMermaidSource(el.textContent || el.innerHTML);
}
el.removeAttribute("data-processed");
el.innerHTML = el.dataset.source;
});
mermaid.initialize(mermaidConfig());
await mermaid.run({ querySelector: ".mermaid" });
wrapMermaidDiagrams();
}
function wrapMermaidDiagrams() {
document.querySelectorAll(".mermaid").forEach((el) => {
if (el.closest(".mermaid-container")) return;
const container = document.createElement("div");
container.className = "mermaid-container";
const controls = document.createElement("div");
controls.className = "mermaid-zoom-controls";
controls.innerHTML = `
<button class="zoom-in">+</button>
<button class="zoom-out">-</button>
<button class="zoom-in" type="button" aria-label="Zoom in">+</button>
<button class="zoom-out" type="button" aria-label="Zoom out">-</button>
`;
container.appendChild(controls);
// Move the mermaid diagram into container
container.appendChild(el.cloneNode(true));
el.replaceWith(container);
container.appendChild(controls);
container.appendChild(el);
// Select the SVG inside
const svg = container.querySelector('.mermaid svg');
let scale = 1;
// Zoom functions
controls.querySelector('.zoom-in').addEventListener('click', () => {
scale += 0.1;
const setScale = (next) => {
const svg = container.querySelector(".mermaid svg");
if (!svg) return;
scale = Math.max(0.2, Math.min(3, next));
svg.style.transform = `scale(${scale})`;
});
};
controls.querySelector('.zoom-out').addEventListener('click', () => {
scale = Math.max(0.1, scale - 0.1);
svg.style.transform = `scale(${scale})`;
controls.querySelector(".zoom-in").addEventListener("click", () => setScale(scale + 0.1));
controls.querySelector(".zoom-out").addEventListener("click", () => setScale(scale - 0.1));
container.addEventListener("wheel", (e) => {
if (!e.ctrlKey) return;
e.preventDefault();
setScale(scale + (e.deltaY < 0 ? 0.05 : -0.05));
});
});
}
// Optional: scroll wheel zoom
container.addEventListener('wheel', (e) => {
if (e.ctrlKey) { // zoom only with ctrl+wheel
e.preventDefault();
if (e.deltaY < 0) scale += 0.05;
else scale = Math.max(0.1, scale - 0.05);
svg.style.transform = `scale(${scale})`;
}
});
}
});
// Finally, render Mermaid diagrams
mermaid.run({ querySelector: '.mermaid' });
});
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", renderMermaid);
} else {
renderMermaid();
}
})();

View File

@@ -799,11 +799,14 @@ body.no-sidenotes {
/* Mermaid container for zooming and panning */
.mermaid-container {
overflow: auto; /* allow scrolling */
overflow: auto;
width: 100%;
max-height: 800px; /* adjust to your page */
border: 1px solid #ccc; /* optional, visual boundary */
max-height: 800px;
border: 1px solid var(--border);
border-radius: 8px;
background: color-mix(in oklab, var(--surface) 88%, var(--bg) 12%);
position: relative;
margin: 1.5rem 0;
}
/* Make SVG scale properly */
@@ -825,13 +828,14 @@ body.no-sidenotes {
.mermaid-zoom-controls button {
padding: 5px 10px;
margin-left: 5px;
background: #007bff;
color: white;
border: none;
background: var(--surface);
color: var(--fg);
border: 1px solid var(--border);
border-radius: 4px;
cursor: pointer;
}
.mermaid-zoom-controls button:hover {
background: #0056b3;
background: var(--accent);
color: var(--surface);
}