88 lines
2.5 KiB
TypeScript
88 lines
2.5 KiB
TypeScript
import { errorText, h } from "./dom.js";
|
|
|
|
type Mermaid = {
|
|
initialize(config: Record<string, unknown>): void;
|
|
render(id: string, text: string): Promise<{ svg: string }>;
|
|
};
|
|
|
|
declare global {
|
|
interface Window {
|
|
mermaid?: Mermaid;
|
|
}
|
|
}
|
|
|
|
let loading: Promise<Mermaid> | undefined;
|
|
let next = 0;
|
|
|
|
function load(): Promise<Mermaid> {
|
|
loading ??= new Promise((resolve, reject) => {
|
|
const script = h("script", { src: "/vendor/mermaid.min.js" });
|
|
script.onload = () => {
|
|
const m = window.mermaid!;
|
|
const css = getComputedStyle(document.documentElement);
|
|
const v = (name: string) => css.getPropertyValue(name).trim();
|
|
m.initialize({
|
|
startOnLoad: false,
|
|
securityLevel: "strict",
|
|
suppressErrorRendering: true,
|
|
theme: "base",
|
|
fontFamily: v("--font"),
|
|
themeVariables: {
|
|
fontFamily: v("--font"),
|
|
fontSize: "13px",
|
|
background: v("--bg"),
|
|
primaryColor: v("--surface"),
|
|
primaryTextColor: v("--text"),
|
|
primaryBorderColor: v("--border"),
|
|
secondaryColor: v("--accent-tint"),
|
|
tertiaryColor: v("--raised"),
|
|
lineColor: v("--muted"),
|
|
textColor: v("--text"),
|
|
noteBkgColor: v("--warn-tint"),
|
|
noteTextColor: v("--text"),
|
|
noteBorderColor: v("--border"),
|
|
},
|
|
});
|
|
resolve(m);
|
|
};
|
|
script.onerror = () => reject(new Error("couldn't load /vendor/mermaid.min.js"));
|
|
document.head.append(script);
|
|
});
|
|
return loading;
|
|
}
|
|
|
|
export function diagram(src: string): HTMLElement {
|
|
const el = h("div", { class: "mermaid-diagram" }, h("pre", { class: "mermaid-source" }, src));
|
|
void render(el, src);
|
|
return el;
|
|
}
|
|
|
|
const latest = new WeakMap<HTMLElement, number>();
|
|
|
|
export async function redraw(el: HTMLElement, src: string) {
|
|
const id = next++;
|
|
latest.set(el, id);
|
|
try {
|
|
const m = await load();
|
|
const { svg } = await m.render(`mermaid-${id}`, src);
|
|
if (latest.get(el) === id) {
|
|
el.innerHTML = svg;
|
|
}
|
|
} catch (err) {
|
|
if (latest.get(el) !== id) {
|
|
return;
|
|
}
|
|
el.replaceChildren(h("pre", { class: "mermaid-source" }, src), h("div", { class: "mermaid-error" }, `diagram not rendered: ${errorText(err)}`));
|
|
}
|
|
}
|
|
|
|
async function render(el: HTMLElement, src: string) {
|
|
try {
|
|
const m = await load();
|
|
const { svg } = await m.render(`mermaid-${next++}`, src);
|
|
el.innerHTML = svg;
|
|
} catch (err) {
|
|
el.append(h("div", { class: "mermaid-error" }, `diagram not rendered: ${errorText(err)}`));
|
|
}
|
|
}
|