fix(notes): show raw-text fallback when markdown render fails
All checks were successful
CI / verify (push) Successful in 1m2s

A throwing markdown-it plugin propagated uncaught and left the note
silently blank. Wrap the central render in try/catch and degrade to the
note's escaped raw text with a gentle notice, so content stays readable.
This commit is contained in:
Julien Calixte
2026-06-29 00:00:20 +02:00
parent 1b47101340
commit a04d169bd8
3 changed files with 49 additions and 1 deletions

View File

@@ -25,6 +25,7 @@ import {
} from "@/utils/decodeBase64ToUTF8"
import { html5Media } from "@/utils/markdown/markdown-html5-media"
import { markdownItTablerIcons } from "@/utils/markdown/markdown-it-tabler-icons"
import { renderFallback } from "@/utils/markdown/renderFallback"
const TIKZ_BUNDLE_URL =
"https://cdn.jsdelivr.net/gh/artisticat1/obsidian-tikzjax@0.5.2/tikzjax.js"
@@ -399,7 +400,14 @@ const renderMarkdown = (content: string, env?: Record<string, unknown>) => {
// unhighlighted because nothing else invalidates the computed.
if (content.includes("```")) void shikijiReady.value
slugger.reset()
try {
return env ? md.render(content, env) : md.render(content)
} catch (error) {
// Never let a plugin failure blank the note or dump a raw error —
// degrade to the note's raw text.
console.error("markdown render failed", error)
return renderFallback(content)
}
}
export const renderCodeFile = async ({

View File

@@ -0,0 +1,26 @@
import { describe, expect, it } from "vitest"
import { renderFallback } from "./renderFallback"
describe("renderFallback", () => {
it("shows the raw content inside a pre with a gentle notice", () => {
const html = renderFallback("# hello")
expect(html).toContain("note-render-fallback")
expect(html).toContain("couldn't be fully rendered")
expect(html).toContain("<pre># hello</pre>")
})
it("escapes HTML so raw content can't inject markup into v-html", () => {
const html = renderFallback('<img src=x onerror="alert(1)"> & "q"')
expect(html).toContain(
"&lt;img src=x onerror=&quot;alert(1)&quot;&gt; &amp; &quot;q&quot;"
)
expect(html).not.toContain("<img src=x")
})
it("escapes ampersands before other entities", () => {
expect(renderFallback("&lt;")).toContain("<pre>&amp;lt;</pre>")
})
})

View File

@@ -0,0 +1,14 @@
const escapeHtml = (value: string): string =>
value
.replaceAll("&", "&amp;")
.replaceAll("<", "&lt;")
.replaceAll(">", "&gt;")
.replaceAll('"', "&quot;")
/**
* Calm degradation for a note that failed to render: never a raw error dump
* or a silent blank — show the note's raw text (escaped) with a gentle notice,
* so the content is still readable.
*/
export const renderFallback = (content: string): string =>
`<div class="note-render-fallback"><p><em>This note couldn't be fully rendered — showing its raw text.</em></p><pre>${escapeHtml(content)}</pre></div>`