diff --git a/.changeset/fifty-hats-obey.md b/.changeset/fifty-hats-obey.md new file mode 100644 index 0000000000..57a9f4e5db --- /dev/null +++ b/.changeset/fifty-hats-obey.md @@ -0,0 +1,5 @@ +--- +'@udecode/plate-markdown': patch +--- + +Fix serialize code-block and indent-list. Add support for equation and inline-equation. diff --git a/packages/markdown/src/lib/serializer/defaultSerializeMdNodesOptions.ts b/packages/markdown/src/lib/serializer/defaultSerializeMdNodesOptions.ts index c0fc686727..4b2d8ce94f 100644 --- a/packages/markdown/src/lib/serializer/defaultSerializeMdNodesOptions.ts +++ b/packages/markdown/src/lib/serializer/defaultSerializeMdNodesOptions.ts @@ -24,9 +24,17 @@ export const defaultSerializeMdNodesOptions: SerializeMdOptions['nodes'] = { code: { isLeaf: true, type: 'code' }, code_block: { serialize: (children, node) => - `\n\`\`\`${node.language || ''}\n${children}\n\`\`\`\n`, + `\n\`\`\`${node.lang || ''}\n${children}\`\`\`\n`, type: 'code_block', }, + code_line: { + serialize: (children) => `${children}\n`, + type: 'code_line', + }, + equation: { + serialize: (children, node) => `$$\n${node.texExpression}\n$$`, + type: 'equation', + }, h1: { serialize: (children) => `\n# ${children}\n`, type: 'h1' }, h2: { serialize: (children) => `\n## ${children}\n`, type: 'h2' }, h3: { serialize: (children) => `\n### ${children}\n`, type: 'h3' }, @@ -52,6 +60,10 @@ export const defaultSerializeMdNodesOptions: SerializeMdOptions['nodes'] = { }, type: 'img', }, + inline_equation: { + serialize: (children, node) => `$${node.texExpression}$`, + type: 'inline_equation', + }, italic: { isLeaf: true, type: 'italic' }, li: { serialize: (children, node, { listDepth = 0, nodes }) => { @@ -89,9 +101,15 @@ export const defaultSerializeMdNodesOptions: SerializeMdOptions['nodes'] = { type: 'ol', }, p: { - serialize: (children, node, { ulListStyleTypes = [] }) => { + serialize: (children, node, { nodes, ulListStyleTypes = [] }) => { const listStyleType = node.listStyleType; + const isInTableCell = + node.parent?.type === nodes.td.type || + node.parent?.type === nodes.th.type; + + const breakTag = isInTableCell ? `
` : `\n`; + if (listStyleType) { let pre = ''; @@ -103,8 +121,6 @@ export const defaultSerializeMdNodesOptions: SerializeMdOptions['nodes'] = { const listStart = node.listStart ?? 1; const isOL = !ulListStyleTypes.includes(listStyleType); - const treatAsLeaf = - node.children.length === 1 && isLeafNode(node.children[0]); // https://github.com/remarkjs/remark-react/issues/65 if (isOL && listDepth > 0) { @@ -112,14 +128,63 @@ export const defaultSerializeMdNodesOptions: SerializeMdOptions['nodes'] = { } // TODO: support all styles - return `${pre}${isOL ? listStart + '.' : '-'} ${children}${treatAsLeaf ? '\n' : ''}`; + return `${pre}${isOL ? listStart + '.' : '-'} ${children}${breakTag}`; } - return `\n${children}\n`; + const pre = isInTableCell ? '' : '\n'; + + return `${pre}${children}${breakTag}`; }, type: 'p', }, strikethrough: { isLeaf: true, type: 'strikethrough' }, + table: { + serialize: (children) => { + const lines = children.split('\n').filter(Boolean); + + // Line 0 is the header row + const headerLine = lines[0].trim(); + + // Remove extra "|" from both sides + let lineTrimmed = headerLine; + + if (lineTrimmed.startsWith('|')) { + lineTrimmed = lineTrimmed.slice(1); + } + if (lineTrimmed.endsWith('|')) { + lineTrimmed = lineTrimmed.slice(0, -1); + } + + // Generate "---" separators based on number of columns + const cols = lineTrimmed.split('|').length; + const separator = `| ${Array(cols).fill('---').join(' | ')} |`; + + // Insert separator line into array + lines.splice(1, 0, separator); + + // Join back into string + return lines.join('\n'); + }, + type: 'table', + }, + td: { + serialize: (children) => { + return `| ${children}`; + }, + type: 'td', + }, + th: { + serialize: (children) => { + return `| ${children}`; + }, + type: 'th', + }, + tr: { + serialize: (children) => { + return `${children} |\n`; + }, + type: 'tr', + }, ul: { serialize: (children, _, { listDepth }) => { const newLineAfter = listDepth === 0 ? '\n' : ''; diff --git a/packages/markdown/src/lib/serializer/serializeMdNode.ts b/packages/markdown/src/lib/serializer/serializeMdNode.ts index 5027c14804..7a40bb906f 100644 --- a/packages/markdown/src/lib/serializer/serializeMdNode.ts +++ b/packages/markdown/src/lib/serializer/serializeMdNode.ts @@ -206,8 +206,17 @@ export function serializeMdNode( type = nodes.p.type!; children = opts.breakTag; } - // Skip nodes that are empty, not a list and not void. - if (children === '' && !node.parent?.isList && !elOptions?.isVoid) { + + // Skip nodes that are empty, not a list not in a table cell and not void . + const isInTableCell = + node.parent?.type === nodes.td.type || node.parent?.type === nodes.th.type; + + if ( + children === '' && + !node.parent?.isList && + !elOptions?.isVoid && + !isInTableCell + ) { return; } if (isLeafNode(node)) { diff --git a/packages/markdown/src/lib/serializer/types.ts b/packages/markdown/src/lib/serializer/types.ts index 66a3c71b9b..b8333b89bb 100644 --- a/packages/markdown/src/lib/serializer/types.ts +++ b/packages/markdown/src/lib/serializer/types.ts @@ -4,6 +4,8 @@ export type MdNodeTypes = { bold: string; code: string; code_block: string; + code_line: string; + equation: string; h1: string; h2: string; h3: string; @@ -12,11 +14,16 @@ export type MdNodeTypes = { h6: string; hr: string; img: string; + inline_equation: string; italic: string; li: string; ol: string; p: string; strikethrough: string; + table: string; + td: string; + th: string; + tr: string; ul: string; underline: string; }; @@ -40,7 +47,7 @@ export interface MdElementType extends NodeType { break?: boolean; caption?: (MdElementType | MdLeafType)[]; indent?: number; - language?: string; + lang?: string; listStart?: number; listStyleType?: string; url?: string;