From 4546a686c3aa82f109e56ba9ec2e95349eeae632 Mon Sep 17 00:00:00 2001 From: Jonathan Hefner Date: Sat, 24 Jan 2026 16:52:49 -0600 Subject: [PATCH] Fix mermaid diagram arrowheads not rendering MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The `@boneskull/typedoc-plugin-mermaid` creates both dark and light theme variants of each diagram, each with identical marker IDs (e.g., `#arrowhead`). When mermaid renders SVGs at runtime, lines reference `url(#arrowhead)`, but the browser resolves this to the first match in document order — the hidden dark-theme SVG — causing arrows to be invisible. The fix removes dark-theme `
` elements at build time, leaving only one SVG per diagram with unique marker IDs. A CSS filter (`filter: invert(1) hue-rotate(180deg)`) handles dark mode styling instead. Co-Authored-By: Claude Opus 4.5 --- .../typedoc-plugin-fix-mermaid-entities.mjs | 60 +++++++++++++++++-- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/scripts/typedoc-plugin-fix-mermaid-entities.mjs b/scripts/typedoc-plugin-fix-mermaid-entities.mjs index 4ca92d9f..c663aca9 100644 --- a/scripts/typedoc-plugin-fix-mermaid-entities.mjs +++ b/scripts/typedoc-plugin-fix-mermaid-entities.mjs @@ -1,12 +1,15 @@ /** - * TypeDoc plugin that properly decodes HTML entities in mermaid code blocks. + * TypeDoc plugin that fixes mermaid diagram rendering issues. * - * This runs BEFORE the mermaid plugin and converts HTML entities back to raw - * characters, allowing mermaid to parse them correctly. + * 1. Decodes HTML entities in mermaid code blocks before the mermaid plugin + * processes them. The @boneskull/typedoc-plugin-mermaid converts < to #lt; + * and > to #gt;, but those aren't valid mermaid entities. * - * The @boneskull/typedoc-plugin-mermaid converts < to #lt; and > to #gt;, - * but those aren't valid mermaid entities (mermaid uses numeric codes like #60;). - * This plugin sidesteps the issue by decoding entities before mermaid sees them. + * 2. Removes dark-theme mermaid divs to fix duplicate marker IDs. The mermaid + * plugin creates both dark and light variants with identical marker IDs + * (e.g., `#arrowhead`), causing the browser to resolve references to the + * wrong SVG. By keeping only light-theme divs, we avoid duplicate IDs. + * CSS filters handle dark mode styling. */ import { Renderer } from "typedoc"; @@ -29,6 +32,18 @@ function decodeHtmlEntities(html) { .replace(/&/g, "&"); // Must be last } +/** + * CSS to invert mermaid diagrams in dark mode. + * Since we only render light-theme diagrams, we use CSS filters for dark mode. + */ +const darkModeStyles = ` + +`; + /** * TypeDoc plugin entry point. * @param {import('typedoc').Application} app @@ -51,4 +66,37 @@ export function load(app) { }, 200, ); + + // Use low priority (-100) to run after the mermaid plugin injects its content + app.renderer.on( + Renderer.EVENT_END_PAGE, + (page) => { + if (!page.contents) return; + if (!page.contents.includes('class="mermaid-block"')) return; + + // Remove dark-theme mermaid divs to avoid duplicate marker IDs + page.contents = page.contents.replace( + /
[\s\S]*?<\/div>/g, + "", + ); + + // Also remove the CSS that hides light-theme divs by default + // The mermaid plugin adds visibility:hidden until JS sets display:block + // Since we only have light divs now, make them visible immediately + page.contents = page.contents.replace( + /
/g, + '
', + ); + + // Add dark mode CSS filter before + const headEndIndex = page.contents.indexOf(""); + if (headEndIndex !== -1) { + page.contents = + page.contents.slice(0, headEndIndex) + + darkModeStyles + + page.contents.slice(headEndIndex); + } + }, + -100, + ); }