From 8e0a3e2991c771611bd8ad3808bd578d640be4dc Mon Sep 17 00:00:00 2001 From: Ramnique Singh <30795890+ramnique@users.noreply.github.com> Date: Mon, 20 Apr 2026 10:43:27 +0530 Subject: [PATCH] render tables in markdown --- apps/x/apps/renderer/package.json | 1 + .../src/components/markdown-editor.tsx | 18 ++++++++ apps/x/apps/renderer/src/styles/editor.css | 42 +++++++++++++++++++ apps/x/pnpm-lock.yaml | 14 +++++++ 4 files changed, 75 insertions(+) diff --git a/apps/x/apps/renderer/package.json b/apps/x/apps/renderer/package.json index 4bb837c9..a8c67a43 100644 --- a/apps/x/apps/renderer/package.json +++ b/apps/x/apps/renderer/package.json @@ -28,6 +28,7 @@ "@tiptap/extension-image": "^3.16.0", "@tiptap/extension-link": "^3.15.3", "@tiptap/extension-placeholder": "^3.15.3", + "@tiptap/extension-table": "^3.22.4", "@tiptap/extension-task-item": "^3.15.3", "@tiptap/extension-task-list": "^3.15.3", "@tiptap/pm": "^3.15.3", diff --git a/apps/x/apps/renderer/src/components/markdown-editor.tsx b/apps/x/apps/renderer/src/components/markdown-editor.tsx index 49915dc0..f106c0a5 100644 --- a/apps/x/apps/renderer/src/components/markdown-editor.tsx +++ b/apps/x/apps/renderer/src/components/markdown-editor.tsx @@ -7,6 +7,8 @@ import Image from '@tiptap/extension-image' import Placeholder from '@tiptap/extension-placeholder' import TaskList from '@tiptap/extension-task-list' import TaskItem from '@tiptap/extension-task-item' +import { TableKit, renderTableToMarkdown } from '@tiptap/extension-table' +import type { JSONContent, MarkdownRendererHelpers } from '@tiptap/react' import { ImageUploadPlaceholderExtension, createImageUploadHandler } from '@/extensions/image-upload' import { TaskBlockExtension } from '@/extensions/task-block' import { TrackBlockExtension } from '@/extensions/track-block' @@ -149,6 +151,17 @@ function serializeList(listNode: JsonNode, indent: number): string[] { return lines } +// Adapter for tiptap's first-party renderTableToMarkdown. Only renderChildren is +// actually invoked — the other helpers are stubs to satisfy the type. +const tableRenderHelpers: MarkdownRendererHelpers = { + renderChildren: (nodes) => { + const arr = Array.isArray(nodes) ? nodes : [nodes] + return arr.map(n => n.type === 'paragraph' ? nodeToText(n as JsonNode) : '').join('') + }, + wrapInBlock: (prefix, content) => prefix + content, + indent: (content) => content, +} + // Serialize a single top-level block to its markdown string. Empty paragraphs (or blank-marker // paragraphs) return '' to signal "blank line slot" for the join logic in serializeBlocksToMarkdown. function blockToMarkdown(node: JsonNode): string { @@ -192,6 +205,8 @@ function blockToMarkdown(node: JsonNode): string { return '```transcript\n' + (node.attrs?.data as string || '{}') + '\n```' case 'mermaidBlock': return '```mermaid\n' + (node.attrs?.data as string || '') + '\n```' + case 'table': + return renderTableToMarkdown(node as JSONContent, tableRenderHelpers).trim() case 'codeBlock': { const lang = (node.attrs?.language as string) || '' return '```' + lang + '\n' + nodeToText(node) + '\n```' @@ -697,6 +712,9 @@ export const MarkdownEditor = forwardRef