Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions app/web/components/markdown/mermaid.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import mermaid, { type MermaidConfig } from "mermaid";

export const myMermaid = {
memoMermaids: new Map<string, string>(),
// null marks a definition that failed to render: mermaid parse errors are
// deterministic, so the same source is never re-attempted (fixing the
// diagram changes the source and therefore the key)
memoMermaids: new Map<string, string | null>(),
incId: 0,

initialize(config: MermaidConfig) {
Expand All @@ -15,13 +18,20 @@ export const myMermaid = {
renderMemoized(element?: HTMLElement) {
const documentMermaids = (element ?? document).querySelectorAll(".mermaid");

const memoMermaids = new Map<string, string>();
const memoMermaids = new Map<string, string | null>();
const pendingMermaids: Element[] = [];

for (const dMermaid of documentMermaids) {
const renderedDefinition = dMermaid.getAttribute("data-rendered");
if (renderedDefinition) {
memoMermaids.set(renderedDefinition, dMermaid.innerHTML);
// carry the clean svg forward instead of re-reading innerHTML:
// pantsdown's script wraps the svg with pan/zoom controls after
// render, and serializing those back into the memo would
// duplicate them (dead, listener-less) on every re-render
memoMermaids.set(
renderedDefinition,
this.memoMermaids.get(renderedDefinition) ?? dMermaid.innerHTML,
);
continue;
}

Expand All @@ -33,6 +43,9 @@ export const myMermaid = {
memoMermaids.set(definition, svg);
dMermaid.setAttribute("data-rendered", definition);
dMermaid.innerHTML = svg;
} else if (svg === null) {
// known-broken definition: keep showing the source, don't retry
memoMermaids.set(definition, null);
} else {
pendingMermaids.push(dMermaid);
}
Expand All @@ -59,18 +72,18 @@ export const myMermaid = {

let svg = this.memoMermaids.get(definition);

if (!svg) {
if (svg === undefined) {
try {
const { svg: newSvg } = await mermaid.render(`mermaid-${++this.incId}`, definition);
svg = newSvg;
} catch (_) {
//
svg = null;
}
}

memoMermaids.set(definition, svg);
if (!svg) continue;

memoMermaids.set(definition, svg);
dMermaid.innerHTML = svg;
}

Expand Down
6 changes: 3 additions & 3 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
"isbinaryfile": "6.0.0",
"mermaid": "^11.16.0",
"opener": "^1.5.2",
"pantsdown": "2.2.8",
"pantsdown": "2.3.0",
"react": "^19.2.8",
"react-dom": "^19.2.8",
"reconnecting-websocket": "^4.4.0",
Expand Down