Skip to content

Commit 93df614

Browse files
Merge pull request #347 from jonathanhefner/fix-typedoc-mermaid-diagram-arrowheads
Fix mermaid diagram arrowheads not rendering
2 parents 6c767c4 + 4546a68 commit 93df614

File tree

1 file changed

+54
-6
lines changed

1 file changed

+54
-6
lines changed

scripts/typedoc-plugin-fix-mermaid-entities.mjs

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
/**
2-
* TypeDoc plugin that properly decodes HTML entities in mermaid code blocks.
2+
* TypeDoc plugin that fixes mermaid diagram rendering issues.
33
*
4-
* This runs BEFORE the mermaid plugin and converts HTML entities back to raw
5-
* characters, allowing mermaid to parse them correctly.
4+
* 1. Decodes HTML entities in mermaid code blocks before the mermaid plugin
5+
* processes them. The @boneskull/typedoc-plugin-mermaid converts < to #lt;
6+
* and > to #gt;, but those aren't valid mermaid entities.
67
*
7-
* The @boneskull/typedoc-plugin-mermaid converts < to #lt; and > to #gt;,
8-
* but those aren't valid mermaid entities (mermaid uses numeric codes like #60;).
9-
* This plugin sidesteps the issue by decoding entities before mermaid sees them.
8+
* 2. Removes dark-theme mermaid divs to fix duplicate marker IDs. The mermaid
9+
* plugin creates both dark and light variants with identical marker IDs
10+
* (e.g., `#arrowhead`), causing the browser to resolve references to the
11+
* wrong SVG. By keeping only light-theme divs, we avoid duplicate IDs.
12+
* CSS filters handle dark mode styling.
1013
*/
1114

1215
import { Renderer } from "typedoc";
@@ -29,6 +32,18 @@ function decodeHtmlEntities(html) {
2932
.replace(/&/g, "&"); // Must be last
3033
}
3134

35+
/**
36+
* CSS to invert mermaid diagrams in dark mode.
37+
* Since we only render light-theme diagrams, we use CSS filters for dark mode.
38+
*/
39+
const darkModeStyles = `
40+
<style>
41+
:root[data-theme="dark"] .mermaid-block .mermaid svg {
42+
filter: invert(1) hue-rotate(180deg);
43+
}
44+
</style>
45+
`;
46+
3247
/**
3348
* TypeDoc plugin entry point.
3449
* @param {import('typedoc').Application} app
@@ -51,4 +66,37 @@ export function load(app) {
5166
},
5267
200,
5368
);
69+
70+
// Use low priority (-100) to run after the mermaid plugin injects its content
71+
app.renderer.on(
72+
Renderer.EVENT_END_PAGE,
73+
(page) => {
74+
if (!page.contents) return;
75+
if (!page.contents.includes('class="mermaid-block"')) return;
76+
77+
// Remove dark-theme mermaid divs to avoid duplicate marker IDs
78+
page.contents = page.contents.replace(
79+
/<div class="mermaid dark">[\s\S]*?<\/div>/g,
80+
"",
81+
);
82+
83+
// Also remove the CSS that hides light-theme divs by default
84+
// The mermaid plugin adds visibility:hidden until JS sets display:block
85+
// Since we only have light divs now, make them visible immediately
86+
page.contents = page.contents.replace(
87+
/<div class="mermaid light">/g,
88+
'<div class="mermaid" style="display: block">',
89+
);
90+
91+
// Add dark mode CSS filter before </head>
92+
const headEndIndex = page.contents.indexOf("</head>");
93+
if (headEndIndex !== -1) {
94+
page.contents =
95+
page.contents.slice(0, headEndIndex) +
96+
darkModeStyles +
97+
page.contents.slice(headEndIndex);
98+
}
99+
},
100+
-100,
101+
);
54102
}

0 commit comments

Comments
 (0)