From e07d0a6e476f6fd6b953305f043c637da33e0401 Mon Sep 17 00:00:00 2001 From: Adam Teichert Date: Mon, 15 Dec 2025 23:01:35 -0700 Subject: [PATCH] caption element before table is placed as first element of the table (allows markdown tables to have caption) --- src/services/htmlMarkdownUtils.test.ts | 37 ++++++++++++++++++++++++++ src/services/htmlMarkdownUtils.ts | 19 ++++++++++--- 2 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 src/services/htmlMarkdownUtils.test.ts diff --git a/src/services/htmlMarkdownUtils.test.ts b/src/services/htmlMarkdownUtils.test.ts new file mode 100644 index 0000000..e466c35 --- /dev/null +++ b/src/services/htmlMarkdownUtils.test.ts @@ -0,0 +1,37 @@ +import { describe, it, expect } from 'vitest'; +import { markdownToHtmlNoImages } from './htmlMarkdownUtils'; + +describe('markdownToHtmlNoImages', () => { + it('moves caption into table when caption immediately precedes table', () => { + const markdown = `My Table + +| Header | +| --- | +| Cell |`; + const html = markdownToHtmlNoImages(markdown); + // We expect the caption to be inside the table + expect(html).toMatch(/\s*
My Table<\/caption>/); + }); + + it('renders table correctly without caption', () => { + const markdown = ` +| Header | +| --- | +| Cell |`; + const html = markdownToHtmlNoImages(markdown); + expect(html).toMatch(//); + expect(html).not.toMatch(/ + +| Header | +| --- | +| Cell |`; + const html = markdownToHtmlNoImages(markdown); + expect(html).toMatch(/
/); + expect(html).toContain('Header'); + expect(html).toContain('Cell'); + }); + + it('moves caption with attributes into table', () => { + const markdown = `My Table
\s*
My Table<\/caption>/); + }); +}); diff --git a/src/services/htmlMarkdownUtils.ts b/src/services/htmlMarkdownUtils.ts index abb905b..b53ef5d 100644 --- a/src/services/htmlMarkdownUtils.ts +++ b/src/services/htmlMarkdownUtils.ts @@ -125,8 +125,21 @@ export function markdownToHTMLSafe({ } export function markdownToHtmlNoImages(markdownString: string) { - const clean = DOMPurify.sanitize( - marked.parse(markdownString, { async: false, pedantic: false, gfm: true }) - ).replaceAll(/>[^<>]*<\/math>/g, ">"); + const parsedHtml = marked.parse(markdownString, { + async: false, + pedantic: false, + gfm: true, + }) as string; + + // Move caption inside table + const htmlWithCaptionInTable = parsedHtml.replace( + /(]*>[\s\S]*?<\/caption>)\s*(]*>)/g, + "$2$1" + ); + + const clean = DOMPurify.sanitize(htmlWithCaptionInTable).replaceAll( + />[^<>]*<\/math>/g, + ">" + ); return clean; }