Mermaid
https://scrapbox.io/files/6212e221996a18001db83b6e.png
例
code:example.mmd
graph LR
Mermaid--makes y'all-->Happy
UserScript
code:script.js
const mermaidElementClass = 'mermaid-managed-element';
function showElementTopTo(id, toShowElement) {
const elm = document.getElementById(id);
elm.insertBefore(toShowElement, elm.firstChild);
}
async function getFileContent(filename) {
const api = /api/code/${encodeURIComponent(scrapbox.Project.name)}/${encodeURIComponent(scrapbox.Page.title)}/${filename};
const response = await fetch(api);
return await response.text();
}
async function render() {
if (!scrapbox.Page.lines) {
return;
}
const { mermaidAPI } = mermaid;
const filenames = new Map();
for (let line of scrapbox.Page.lines) {
const lang = line.codeBlock?.lang;
const filename = line.codeBlock?.filename;
if (lang === 'mmd' && filename !== undefined) {
if (!filenames.has(filename)) {
filenames.set(filename, line);
}
}
}
const { id, section } = line;
const mmd = await getFileContent(filename);
const elementId = id + '-mermaid';
try {
mermaidAPI.parse(mmd);
} catch (e) {
document.getElementById(elementId)?.remove();
const errorElement = document.createElement('p');
errorElement.id = elementId;
errorElement.classList.add(mermaidElementClass);
errorElement.innerText = e.str;
errorElement.style.marginLeft = ${((line.codeBlock?.indent ?? 0) - 1) * 1.5}em;
errorElement.style.padding = '8px';
errorElement.style.backgroundColor = '#ffdddd';
errorElement.style.fontFamily = 'monospace';
errorElement.style.whiteSpace = 'pre';
showElementTopTo('L' + id, errorElement);
continue;
}
mermaidAPI.render('L' + id + '-mermaid', mmd, (svgCode) => {
document.getElementById(elementId)?.remove();
const svg = document.createElement('svg');
svg.id = elementId;
svg.classList.add(mermaidElementClass);
svg.style.marginLeft = ${((line.codeBlock?.indent ?? 0) - 1) * 1.5}em;
svg.innerHTML = svgCode;
showElementTopTo('L' + id, svg);
});
}
}
function cleanup() {
const mermaidManagedElements = document.querySelectorAll('.' + mermaidElementClass);
for (let element of mermaidManagedElements) {
element.remove();
}
}
async function loadScript(src) {
const script = document.createElement('script');
script.src = src;
const promise = new Promise(resolve => {
script.addEventListener('load', resolve);
});
document.body.appendChild(script);
return promise;
}
async function initialize() {
scrapbox.on('layout:changed', cleanup);
scrapbox.on('page:changed', async () => {
cleanup();
await render();
});
await render();
scrapbox.on('lines:changed', _.debounce(render, 1000));
}
initialize();