From a4f60ff9ea11a3fa26f7a845e7684b09c5501c4e Mon Sep 17 00:00:00 2001 From: Neil Date: Fri, 18 Sep 2026 03:42:00 -0700 Subject: [PATCH] fix(editor): preserve table cell line breaks --- .../rich-markdown-table-markdown.test.ts | 42 +++++++++++++++++++ .../editor/rich-markdown-table-markdown.ts | 14 ++++++- 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/components/editor/rich-markdown-table-markdown.test.ts b/src/renderer/src/components/editor/rich-markdown-table-markdown.test.ts index 819f226c833..b1bd334157b 100644 --- a/src/renderer/src/components/editor/rich-markdown-table-markdown.test.ts +++ b/src/renderer/src/components/editor/rich-markdown-table-markdown.test.ts @@ -88,8 +88,50 @@ describe('compact Markdown table serialization', () => { it('counts alignment markers inside the separator width', () => { const node = table(['left', 'center', 'right'], [['a', 'b', '42']]) + for (const [index, align] of ['left', 'center', 'right'].entries()) { + const header = node.content?.[0]?.content?.[index] + if (header) { + header.attrs = { align } + } + } const header = lines(renderTableToCompactMarkdown(node, helpers))[0] const rows = lines(renderTableToCompactMarkdown(node, helpers)) expect(rows[1]?.length).toBe(header?.length) + expect(rows[1]).toContain(':---') + expect(rows[1]).toContain(':----:') + expect(rows[1]).toContain('---:') + }) + + it('keeps multiple blocks in a cell on one Markdown table row', () => { + const node: JSONContent = { + type: 'table', + content: [ + { + type: 'tableRow', + content: [ + { + type: 'tableHeader', + content: [{ type: 'paragraph', content: [{ type: 'text', text: 'Name' }] }] + } + ] + }, + { + type: 'tableRow', + content: [ + { + type: 'tableCell', + content: [ + { type: 'paragraph', content: [{ type: 'text', text: 'first' }] }, + { type: 'paragraph', content: [{ type: 'text', text: 'second' }] } + ] + } + ] + } + ] + } + + const output = renderTableToCompactMarkdown(node, helpers) + expect(output).toContain('first
second') + expect(lines(output).every((line) => !line.includes('\n'))).toBe(true) }) }) diff --git a/src/renderer/src/components/editor/rich-markdown-table-markdown.ts b/src/renderer/src/components/editor/rich-markdown-table-markdown.ts index 3515d772d5a..6015943b88b 100644 --- a/src/renderer/src/components/editor/rich-markdown-table-markdown.ts +++ b/src/renderer/src/components/editor/rich-markdown-table-markdown.ts @@ -7,6 +7,7 @@ const MIN_COLUMN_WIDTH = 3 const OUTLIER_MEDIAN_FACTOR = 2.5 const MAX_COLUMN_WIDTH = 60 const MAX_ALIGNED_TABLE_WIDTH = 160 +const CELL_LINE_SEPARATOR = '\u001F' function collapseWhitespace(value: string): string { return value.replace(/\s+/g, ' ').trim() @@ -64,7 +65,18 @@ function separatorCell(width: number, align: TableCellAlign): string { function extractRows(node: JSONContent, helpers: MarkdownRendererHelpers): TableCell[][] { return (node.content ?? []).map((rowNode) => (rowNode.content ?? []).map((cellNode) => ({ - text: collapseWhitespace(cellNode.content ? helpers.renderChildren(cellNode.content) : ''), + text: collapseWhitespace( + (cellNode.content?.length ?? 0) > 1 + ? cellNode.content + .map((child) => helpers.renderChildren(child)) + .join(CELL_LINE_SEPARATOR) + .split(CELL_LINE_SEPARATOR) + .join('\n') + .replace(/[ \t]*\r?\n[ \t]*/g, '
') + : cellNode.content + ? helpers.renderChildren(cellNode.content) + : '' + ), isHeader: cellNode.type === 'tableHeader', align: normalizeAlign(cellNode.attrs) }))