generated at
Mermaid

Mermaid.jsをScrapboxで使えるようにするUserScript

example.mmd
graph LR Mermaid--makes y'all-->Happy

UserScript
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); } } } for (let [filename, line] of filenames) { 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() { await loadScript('https://cdnjs.cloudflare.com/ajax/libs/mermaid/9.1.3/mermaid.min.js'); scrapbox.on('layout:changed', cleanup); scrapbox.on('page:changed', async () => { cleanup(); await render(); }); await render(); scrapbox.on('lines:changed', _.debounce(render, 1000)); } initialize();