From 6fb702f886bcd15e1dd262ff25c6313ea88e57a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C5=ABdolfs=20O=C5=A1i=C5=86=C5=A1?= Date: Thu, 7 May 2026 14:39:26 +0200 Subject: [PATCH] Recover gracefully from mermaid render errors When mermaid.render fails, suppress mermaid's "Syntax error" overlay and show our own small warning above the original code block so the source stays visible. Subsequent diagrams in the same document still render. --- src/components/Markdown.svelte | 49 +++++++++++++++++--------- tests/fixtures/repos/markdown.tar.bz2 | Bin 5769 -> 5865 bytes tests/visual/desktop/markdown.spec.ts | 14 +++++--- 3 files changed, 43 insertions(+), 20 deletions(-) diff --git a/src/components/Markdown.svelte b/src/components/Markdown.svelte index 13d48a50d..ca211ed1a 100644 --- a/src/components/Markdown.svelte +++ b/src/components/Markdown.svelte @@ -10,6 +10,9 @@ mermaid.initialize({ startOnLoad: false, securityLevel: "strict", + // Don't let mermaid inject its own "Syntax error" bomb into the + // document; we render a custom warning instead. + suppressErrorRendering: true, }); return mermaid; }); @@ -254,31 +257,36 @@ const language = className.slice(prefix.length); - // Handle mermaid diagrams if (language === "mermaid") { const mermaid = await loadMermaid(); - const mermaidCode = node.textContent || ""; + const diagramId = `mermaid-${crypto.randomUUID()}`; try { - // First, validate the syntax without rendering - const isValid = await mermaid.parse(mermaidCode, { - suppressErrors: true, - }); - - if (isValid) { - // Generate a unique id for this diagram - const diagramId = `mermaid-${Math.random().toString(36).substr(2, 9)}`; - - // Try to render the mermaid diagram - const { svg } = await mermaid.render(diagramId, mermaidCode); - - // Replace the code block with the rendered diagram + const { svg } = await mermaid.render( + diagramId, + node.textContent ?? "", + ); + // The component may have re-rendered or unmounted while we were + // awaiting; in that case `preWrapper` is detached and there's + // nothing to replace. + if (preWrapper.isConnected) { const mermaidDiv = document.createElement("div"); mermaidDiv.innerHTML = svg; mermaidDiv.classList.add("mermaid-diagram"); preWrapper.replaceWith(mermaidDiv); } } catch (error) { - // On any error, keep the raw text visible (do nothing, let it render as code) + console.warn("Not able to render mermaid diagram", error); + if (preWrapper.isConnected) { + const warning = document.createElement("div"); + warning.classList.add("mermaid-error"); + const icon = document.createElement("radicle-icon-small"); + icon.setAttribute("name", "warning"); + warning.appendChild(icon); + const text = document.createElement("span"); + text.textContent = "Couldn't render diagram"; + warning.appendChild(text); + preWrapper.before(warning); + } } continue; } @@ -562,6 +570,15 @@ max-width: 100%; height: auto; } + + .markdown :global(.mermaid-error) { + display: flex; + align-items: center; + gap: 0.375rem; + color: var(--color-feedback-warning-text); + font: var(--txt-body-s-regular); + margin: 1rem 0 0.25rem; + } {#if frontMatter && frontMatter.length > 0} diff --git a/tests/fixtures/repos/markdown.tar.bz2 b/tests/fixtures/repos/markdown.tar.bz2 index 4153d642e057ff7fca5d61f1ced2e5819a651e9a..467a8f4425e11142f48fe9ae0bbc8ac33f0e443b 100644 GIT binary patch literal 5865 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 HXjoPt?;}{~ diff --git a/tests/visual/desktop/markdown.spec.ts b/tests/visual/desktop/markdown.spec.ts index 0b4a2c5ce..b17cf50bc 100644 --- a/tests/visual/desktop/markdown.spec.ts +++ b/tests/visual/desktop/markdown.spec.ts @@ -125,20 +125,26 @@ test("math", async ({ page }) => { await expect(page).toHaveScreenshot({ fullPage: true }); }); -test("mermaid diagram", async ({ page }) => { +test("mermaid diagrams", async ({ page }) => { await page.goto(`${markdownUrl}/tree/main/mermaid.md`, { waitUntil: "networkidle", }); - await expect(page.locator(".mermaid-diagram svg")).toBeVisible(); + await expect(page.locator(".mermaid-diagram svg")).toHaveCount(2); await expect(page).toHaveScreenshot({ fullPage: true }); }); -test("broken mermaid falls back to code block", async ({ page }) => { +test("broken mermaid shows warning and does not block subsequent diagrams", async ({ + page, +}) => { await page.goto(`${markdownUrl}/tree/main/mermaid-broken.md`, { waitUntil: "networkidle", }); + // Our custom warning replaces mermaid's built-in error overlay. + await expect(page.locator(".mermaid-error")).toBeVisible(); + // The original source for the broken diagram stays visible. await expect(page.locator("pre code.language-mermaid")).toBeVisible(); - await expect(page.locator(".mermaid-diagram")).toHaveCount(0); + // The valid diagram following the broken one still renders. + await expect(page.locator(".mermaid-diagram svg")).toHaveCount(1); await expect(page).toHaveScreenshot({ fullPage: true }); });