diff --git a/packages/app/src/components/markdown/renderer.test.ts b/packages/app/src/components/markdown/renderer.test.ts index 1489c815bc..214193d850 100644 --- a/packages/app/src/components/markdown/renderer.test.ts +++ b/packages/app/src/components/markdown/renderer.test.ts @@ -335,3 +335,16 @@ it("normalizes images following HTML code in the same HTML block", () => { expect(prepared.render(new Map([["shot.png", "blob:shot"]]))).toContain("![]()"); expect(prepared.render()).toContain("`\nx\n`"); }); + +it("preserves literal Markdown punctuation in unsupported and failed image labels", () => { + const label = String.raw`\*literal\* \[label\]`; + expect( + prepareMarkdownSource(`![${label}](badge.svg)`, undefined, { supportsSvg: false }).render(), + ).toBe(label); + expect( + prepareMarkdownSource(`![${label}](https://example.com/badge.png)`).render( + new Map(), + new Set(["https://example.com/badge.png"]), + ), + ).toBe(label); +}); diff --git a/packages/app/src/components/markdown/source.ts b/packages/app/src/components/markdown/source.ts index 89ea46929e..9c61d4f40f 100644 --- a/packages/app/src/components/markdown/source.ts +++ b/packages/app/src/components/markdown/source.ts @@ -90,7 +90,7 @@ export function prepareMarkdownSource( options.supportsSvg === false && /(?:\.svg(?:[?#]|$)|^data:image\/svg\+xml)/i.test(target.url) ) { - edits.push({ start, end, text: node.alt ?? "" }); + edits.push({ start, end, text: escapeMarkdownText(node.alt ?? "") }); return; } if (target) @@ -153,7 +153,7 @@ export function prepareMarkdownSource( end: image.end, text: url ? `![${image.alt.replace(/[[\]\\]/g, "\\$&")}](<${url}>${image.title ? ` ${JSON.stringify(image.title)}` : ""})` - : image.alt, + : escapeMarkdownText(image.alt), }); } let result = source; @@ -164,3 +164,7 @@ export function prepareMarkdownSource( }, }; } + +function escapeMarkdownText(text: string): string { + return text.replace(/[!-/:-@[-`{-~]/g, "\\$&"); +}