ui : wrap markdown tables in a scroll container

Markdown tables render as a bare <table>, which keeps its content-driven minimum width and can stretch the chat column past the window. The table-wrapper CSS already existed, but nothing produced the wrapper.

Add a rehype plugin that wraps each table in div.table-wrapper, following the existing enhance-* plugins.

Assisted-by: pi:deepseek-ai/DeepSeek-V4.1-Flash
This commit is contained in:
Aleksander Grygier
2026-09-18 20:57:40 +02:00
parent 1600819c34
commit 35fc09b231
2 changed files with 36 additions and 0 deletions
@@ -11,6 +11,7 @@ import { rehypeEnhanceCodeBlocks } from './plugins/rehype/enhance-code-blocks';
import { rehypeEnhanceLinks } from './plugins/rehype/enhance-links';
import { rehypeEnhanceMermaidBlocks } from './plugins/rehype/enhance-mermaid-blocks';
import { rehypeEnhanceSvgBlocks } from './plugins/rehype/enhance-svg-blocks';
import { rehypeEnhanceTables } from './plugins/rehype/enhance-tables';
import { rehypeFileBadge } from './plugins/rehype/file-badge';
import { rehypeMermaidPre } from './plugins/rehype/mermaid-pre';
import { rehypeRtlSupport } from './plugins/rehype/rehype-rtl-support';
@@ -73,6 +74,7 @@ function buildPipeline({
languages: lowlightAll
}) // Add syntax highlighting
.use(rehypeRestoreTableHtml) // Restore limited HTML (e.g. <br>, <ul>) inside Markdown tables
.use(rehypeEnhanceTables) // Wrap tables in a horizontal scroll container
.use(rehypeEnhanceLinks) // Add target="_blank" to links
.use(rehypeFileBadge) // Render file:// anchors as inline badge chips
.use(rehypeMermaidPre) // Convert mermaid blocks to <pre class="mermaid">
@@ -0,0 +1,34 @@
/**
* Rehype plugin to wrap tables in a horizontal scroll container.
*
* A bare <table> keeps its content-driven minimum width, which propagates up
* the layout and can stretch the chat column past the window. Wrapping in
* div.table-wrapper makes the wrapper the scroll container (styled in
* markdown-content.css), so wide tables scroll in place instead.
*/
import type { Element, ElementContent, Root } from 'hast';
import type { Plugin } from 'unified';
import { visit } from 'unist-util-visit';
export const rehypeEnhanceTables: Plugin<[], Root> = () => {
return (tree: Root) => {
visit(tree, 'element', (node: Element, index, parent) => {
if (node.tagName !== 'table' || !parent || index === undefined) return;
// already wrapped (e.g. nested tables in raw HTML input)
const parentClass = parent.type === 'element' ? parent.properties?.className : undefined;
if (Array.isArray(parentClass) && parentClass.includes('table-wrapper')) return;
const wrapper: Element = {
children: [node as ElementContent],
properties: { className: ['table-wrapper'] },
tagName: 'div',
type: 'element'
};
parent.children[index] = wrapper;
});
};
};