File tree Expand file tree Collapse file tree
apps/desktop/src/features/ai/components Expand file tree Collapse file tree Original file line number Diff line number Diff line change @@ -220,6 +220,32 @@ describe("MarkdownContent", () => {
220220 ) . toBeInTheDocument ( ) ;
221221 } ) ;
222222
223+ it ( "does not crash on markdown links with malformed URI encoding" , ( ) => {
224+ renderComponent (
225+ < MarkdownContent
226+ content = "Use [100% notes](/vault/100% notes.md)."
227+ pillMetrics = { pillMetrics }
228+ /> ,
229+ ) ;
230+
231+ expect (
232+ screen . getByRole ( "button" , { name : "100% notes" } ) ,
233+ ) . toBeInTheDocument ( ) ;
234+ } ) ;
235+
236+ it ( "does not crash on absolute vault file paths with literal percent signs" , ( ) => {
237+ renderComponent (
238+ < MarkdownContent
239+ content = "Open `/vault/100% notes.md`."
240+ pillMetrics = { pillMetrics }
241+ /> ,
242+ ) ;
243+
244+ expect (
245+ screen . getByRole ( "button" , { name : "100% notes" } ) ,
246+ ) . toBeInTheDocument ( ) ;
247+ } ) ;
248+
223249 it ( "renders unified diff code blocks with exact line gutters" , ( ) => {
224250 renderComponent (
225251 < MarkdownContent
Original file line number Diff line number Diff line change @@ -47,6 +47,14 @@ interface MarkdownContentProps {
4747 chatFontSize ?: number ;
4848}
4949
50+ function safeDecodeUriComponent ( value : string ) {
51+ try {
52+ return decodeURIComponent ( value ) ;
53+ } catch {
54+ return value ;
55+ }
56+ }
57+
5058function parseVaultReference ( value : string ) {
5159 const trimmed = value . trim ( ) ;
5260 const match = / ^ ( .* ?\. m d ) (?: : ( \d + ) | # L ( \d + ) ) ? $ / i. exec ( trimmed ) ;
@@ -459,7 +467,9 @@ function renderInlineMarkdown(
459467 const linkMatch = / \[ ( [ ^ \] ] + ) \] \( ( [ ^ ) ] + ) \) / . exec ( full ) ;
460468 if ( linkMatch ) {
461469 const url = linkMatch [ 2 ] ;
462- const decoded = decodeURIComponent ( url ) ;
470+ // Assistant output may include literal "%" characters in links.
471+ // Keep rendering resilient when the URL is not valid URI-encoded text.
472+ const decoded = safeDecodeUriComponent ( url ) ;
463473 const parsedUrlReference = resolveVaultNoteReference ( decoded , {
464474 allowRelative : true ,
465475 } ) ;
@@ -597,7 +607,7 @@ function renderInlineMarkdown(
597607 }
598608 } else if ( match [ 6 ] ) {
599609 // Absolute vault file path.
600- const filePath = decodeURIComponent ( full ) ;
610+ const filePath = safeDecodeUriComponent ( full ) ;
601611 const excalidrawRef = parseExcalidrawReference ( filePath ) ;
602612 const pdfPathRef = parsePdfReference ( filePath ) ;
603613 const textFileRef = parseTextFileReference ( filePath ) ;
You can’t perform that action at this time.
0 commit comments