mirror of
https://seed.radicle.dev/rad:z4V1sjrXqjvFdnCUbxPFqd5p4DtH5.git
synced 2026-10-18 19:30:27 +08:00
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.
This commit is contained in:
@@ -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;
|
||||
}
|
||||
</style>
|
||||
|
||||
{#if frontMatter && frontMatter.length > 0}
|
||||
|
||||
BIN
Binary file not shown.
@@ -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 });
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user