fix(ui): ensure GFM tables render in WebChat markdown (#20410)

- Pass gfm:true + breaks:true explicitly to marked.parse() so table
  support is guaranteed even if global setOptions() is bypassed or
  reset by a future refactor (defense-in-depth)
- Add display:block + overflow-x:auto to .chat-text table so wide
  multi-column tables scroll horizontally instead of being clipped
  by the parent overflow-x:hidden chat container
- Add regression tests for GFM table rendering in markdown.test.ts
This commit is contained in:
Ash (Bug Lab)
2026-03-03 00:42:36 +05:30
committed by Peter Steinberger
parent 346d3590fb
commit 5084621f43
3 changed files with 39 additions and 0 deletions

View File

@@ -1923,7 +1923,10 @@
margin-top: 0.75em;
border-collapse: collapse;
width: 100%;
max-width: 100%;
font-size: 13px;
display: block;
overflow-x: auto;
}
.chat-text :where(th, td) {

View File

@@ -48,4 +48,38 @@ describe("toSanitizedMarkdownHtml", () => {
expect(html).not.toContain("javascript:");
expect(html).not.toContain("src=");
});
it("renders GFM markdown tables (#20410)", () => {
const md = [
"| Feature | Status |",
"|---------|--------|",
"| Tables | ✅ |",
"| Borders | ✅ |",
].join("\n");
const html = toSanitizedMarkdownHtml(md);
expect(html).toContain("<table");
expect(html).toContain("<thead");
expect(html).toContain("<th>");
expect(html).toContain("Feature");
expect(html).toContain("Tables");
expect(html).not.toContain("|---------|");
});
it("renders GFM tables surrounded by text (#20410)", () => {
const md = [
"Text before.",
"",
"| Col1 | Col2 |",
"|------|------|",
"| A | B |",
"",
"Text after.",
].join("\n");
const html = toSanitizedMarkdownHtml(md);
expect(html).toContain("<table");
expect(html).toContain("Col1");
expect(html).toContain("Col2");
// Pipes from table delimiters must not appear as raw text
expect(html).not.toContain("|------|");
});
});

View File

@@ -117,6 +117,8 @@ export function toSanitizedMarkdownHtml(markdown: string): string {
}
const rendered = marked.parse(`${truncated.text}${suffix}`, {
renderer: htmlEscapeRenderer,
gfm: true,
breaks: true,
}) as string;
const sanitized = DOMPurify.sanitize(rendered, sanitizeOptions);
if (input.length <= MARKDOWN_CACHE_MAX_CHARS) {