johnandersen777.bsky.social/knotless
git that lives in your Atmosphere account
Forked from chadtmiller.com/knotless
0
0
// Mermaid diagrams in Markdown, drawn in the browser by Pele
// (https://pele.run). The server sends a ```mermaid block as code, so a
// page without scripts still shows its source; this swaps each block for
// its diagram, loading Pele, and each kind of diagram, only once a page
// has one. A diagram that cannot be drawn shows its source again.
import type { Mounted, MountOptions } from 'pele/lazy';
const BLOCKS = 'pre > code.language-mermaid';
/** Anyone with a repository writes diagrams, so their links are not vouched for. */
const OPTIONS: MountOptions = { linkRel: 'noopener nofollow ugc' };
/**
* An action drawing the Mermaid blocks in its element, again as the HTML it
* shows changes. The element is marked `data-drawing` until they are drawn:
* what the screenshots wait for.
*/
export function diagrams(el: HTMLElement, _html: string) {
let mounted: Mounted[] = [];
let run = 0;
const clear = () => {
run++;
mounted.forEach((m) => m.destroy());
mounted = [];
};
async function draw(id: number) {
const blocks = [...el.querySelectorAll<HTMLElement>(BLOCKS)];
if (!blocks.length) return;
let pele: typeof import('pele/lazy');
try {
pele = await import('pele/lazy');
} catch {
// Not loaded: the sources stay shown.
return;
}
for (const code of blocks) {
const pre = code.parentElement;
if (id !== run || !pre?.isConnected) return;
const box = document.createElement('div');
box.className = 'diagram';
pre.replaceWith(box);
let m: Mounted | undefined;
try {
m = await pele.mountAsync(box, code.textContent ?? '', OPTIONS);
} catch {
// Its kind of diagram failed to load.
}
if (m && m.error === undefined && id === run) {
mounted.push(m);
continue;
}
// Not drawn, or no longer wanted: the source again.
m?.destroy();
box.replaceWith(pre);
}
}
function start() {
const id = run;
el.dataset.drawing = '';
// After the HTML it is given is in place.
queueMicrotask(() =>
draw(id).finally(() => {
if (id === run) delete el.dataset.drawing;
})
);
}
start();
return {
update() {
clear();
start();
},
destroy: clear
};
}