From a40eab9c06b17e07502c5603a6af61cde057b873 Mon Sep 17 00:00:00 2001 From: Nick the Sick Date: Fri, 3 Apr 2026 10:37:14 +0200 Subject: [PATCH 1/4] feat: replace unified.js ecosystem with custom markdown parser/serializer MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the remark/rehype/unified pipeline (~89 transitive packages) with two custom, zero-dependency files for markdown↔HTML conversion. The unified ecosystem was overkill for BlockNote's bounded feature set and required 6 custom plugins to work around its abstractions. New files: - markdownToHtml.ts: block tokenizer + inline parser with recursive list handling - htmlToMarkdown.ts: DOM-based serializer with proper list nesting and table support Removes 12 direct dependencies: unified, remark-parse, remark-stringify, remark-gfm, remark-rehype, rehype-parse, rehype-stringify, rehype-remark, rehype-format, hast-util-from-dom, unist-util-visit, @types/hast Co-Authored-By: Claude Opus 4.6 (1M context) --- packages/core/package.json | 12 - .../api/exporters/markdown/htmlToMarkdown.ts | 678 ++++++ .../exporters/markdown/markdownExporter.ts | 23 +- .../util/addSpacesToCheckboxesRehypePlugin.ts | 42 - .../convertVideoToMarkdownRehypePlugin.ts | 19 - .../util/removeUnderlinesRehypePlugin.ts | 39 - .../__snapshots__/nestedLists.test.ts.snap | 229 ++- .../api/parsers/html/util/nestedLists.test.ts | 13 +- .../api/parsers/markdown/markdownToHtml.ts | 945 +++++++++ .../src/api/parsers/markdown/parseMarkdown.ts | 103 +- packages/xl-ai/package.json | 3 - pnpm-lock.yaml | 268 --- .../blocknoteHTML/audio/basic.html | 22 + .../blocknoteHTML/audio/button.html | 20 + .../blocknoteHTML/audio/noName.html | 21 + .../blocknoteHTML/complex/document.html | 87 + .../blocknoteHTML/heading/h1.html | 9 + .../blocknoteHTML/heading/h2.html | 9 + .../blocknoteHTML/heading/h3.html | 9 + .../blocknoteHTML/heading/h4.html | 9 + .../blocknoteHTML/heading/h5.html | 9 + .../blocknoteHTML/heading/h6.html | 9 + .../blocknoteHTML/heading/styled.html | 12 + .../blocknoteHTML/heading/toggleable.html | 38 + .../blocknoteHTML/image/withCaption.html | 31 + .../blocknoteHTML/link/withCode.html | 18 + .../lists/numberedListStart.html | 25 + .../blocknoteHTML/paragraph/multiple.html | 23 + .../blocknoteHTML/quote/basic.html | 9 + .../blocknoteHTML/quote/multiple.html | 16 + .../blocknoteHTML/quote/nested.html | 18 + .../blocknoteHTML/quote/styled.html | 14 + .../blocknoteHTML/quote/withLink.html | 16 + .../blocknoteHTML/style/backgroundColor.html | 11 + .../blocknoteHTML/style/bold.html | 11 + .../blocknoteHTML/style/boldItalicStrike.html | 15 + .../blocknoteHTML/style/code.html | 11 + .../blocknoteHTML/style/combined.html | 13 + .../blocknoteHTML/style/italic.html | 11 + .../blocknoteHTML/style/mixedInParagraph.html | 15 + .../blocknoteHTML/style/strike.html | 11 + .../blocknoteHTML/style/textColor.html | 11 + .../blocknoteHTML/style/underline.html | 11 + .../blocknoteHTML/table/advancedExample.html | 82 + .../table/cellTextAlignment.html | 33 + .../blocknoteHTML/table/emptyCells.html | 37 + .../blocknoteHTML/table/hardBreakInCell.html | 33 + .../table/headerRowsAndCols.html | 44 + .../blocknoteHTML/table/linksInCells.html | 50 + .../blocknoteHTML/table/singleCell.html | 25 + .../table/styledCellContent.html | 45 + .../blocknoteHTML/video/withCaption.html | 32 + .../__snapshots__/html/audio/basic.html | 5 + .../__snapshots__/html/audio/button.html | 1 + .../__snapshots__/html/audio/noName.html | 4 + .../__snapshots__/html/complex/document.html | 27 + .../export/__snapshots__/html/heading/h1.html | 1 + .../export/__snapshots__/html/heading/h2.html | 1 + .../export/__snapshots__/html/heading/h3.html | 1 + .../export/__snapshots__/html/heading/h4.html | 1 + .../export/__snapshots__/html/heading/h5.html | 1 + .../export/__snapshots__/html/heading/h6.html | 1 + .../__snapshots__/html/heading/styled.html | 4 + .../html/heading/toggleable.html | 6 + .../__snapshots__/html/image/withCaption.html | 8 + .../__snapshots__/html/link/withCode.html | 10 + .../html/lists/numberedListStart.html | 8 + .../html/paragraph/multiple.html | 3 + .../__snapshots__/html/quote/basic.html | 1 + .../__snapshots__/html/quote/multiple.html | 2 + .../__snapshots__/html/quote/nested.html | 2 + .../__snapshots__/html/quote/styled.html | 6 + .../__snapshots__/html/quote/withLink.html | 8 + .../html/style/backgroundColor.html | 8 + .../export/__snapshots__/html/style/bold.html | 3 + .../html/style/boldItalicStrike.html | 7 + .../export/__snapshots__/html/style/code.html | 3 + .../__snapshots__/html/style/combined.html | 5 + .../__snapshots__/html/style/italic.html | 3 + .../html/style/mixedInParagraph.html | 7 + .../__snapshots__/html/style/strike.html | 3 + .../__snapshots__/html/style/textColor.html | 8 + .../__snapshots__/html/style/underline.html | 3 + .../html/table/advancedExample.html | 67 + .../html/table/cellTextAlignment.html | 18 + .../__snapshots__/html/table/emptyCells.html | 22 + .../html/table/hardBreakInCell.html | 18 + .../html/table/headerRowsAndCols.html | 29 + .../html/table/linksInCells.html | 35 + .../__snapshots__/html/table/singleCell.html | 10 + .../html/table/styledCellContent.html | 30 + .../__snapshots__/html/video/withCaption.html | 8 + .../__snapshots__/markdown/audio/basic.md | 1 + .../__snapshots__/markdown/audio/button.md | 1 + .../__snapshots__/markdown/audio/noName.md | 1 + .../markdown/complex/document.md | 19 + .../__snapshots__/markdown/complex/misc.md | 2 +- .../__snapshots__/markdown/heading/h1.md | 1 + .../__snapshots__/markdown/heading/h2.md | 1 + .../__snapshots__/markdown/heading/h3.md | 1 + .../__snapshots__/markdown/heading/h4.md | 1 + .../__snapshots__/markdown/heading/h5.md | 1 + .../__snapshots__/markdown/heading/h6.md | 1 + .../__snapshots__/markdown/heading/styled.md | 1 + .../markdown/heading/toggleable.md | 3 + .../markdown/image/withCaption.md | 3 + .../__snapshots__/markdown/link/withCode.md | 1 + .../markdown/lists/numberedListStart.md | 3 + .../markdown/paragraph/multiple.md | 5 + .../__snapshots__/markdown/quote/basic.md | 1 + .../__snapshots__/markdown/quote/multiple.md | 3 + .../__snapshots__/markdown/quote/nested.md | 3 + .../__snapshots__/markdown/quote/styled.md | 1 + .../__snapshots__/markdown/quote/withLink.md | 1 + .../markdown/style/backgroundColor.md | 1 + .../__snapshots__/markdown/style/bold.md | 1 + .../markdown/style/boldItalicStrike.md | 1 + .../__snapshots__/markdown/style/code.md | 1 + .../__snapshots__/markdown/style/combined.md | 1 + .../__snapshots__/markdown/style/italic.md | 1 + .../markdown/style/mixedInParagraph.md | 1 + .../__snapshots__/markdown/style/strike.md | 1 + .../__snapshots__/markdown/style/textColor.md | 1 + .../__snapshots__/markdown/style/underline.md | 1 + .../markdown/table/advancedExample.md | 6 + .../markdown/table/cellTextAlignment.md | 3 + .../markdown/table/emptyCells.md | 4 + .../markdown/table/hardBreakInCell.md | 4 + .../markdown/table/headerRowsAndCols.md | 3 + .../markdown/table/linksInCells.md | 4 + .../markdown/table/singleCell.md | 3 + .../markdown/table/styledCellContent.md | 4 + .../markdown/video/withCaption.md | 3 + .../__snapshots__/nodes/audio/basic.json | 20 + .../__snapshots__/nodes/audio/button.json | 20 + .../__snapshots__/nodes/audio/noName.json | 20 + .../__snapshots__/nodes/complex/document.json | 222 ++ .../__snapshots__/nodes/heading/h1.json | 26 + .../__snapshots__/nodes/heading/h2.json | 26 + .../__snapshots__/nodes/heading/h3.json | 26 + .../__snapshots__/nodes/heading/h4.json | 26 + .../__snapshots__/nodes/heading/h5.json | 26 + .../__snapshots__/nodes/heading/h6.json | 26 + .../__snapshots__/nodes/heading/styled.json | 35 + .../nodes/heading/toggleable.json | 53 + .../nodes/image/withCaption.json | 22 + .../__snapshots__/nodes/link/withCode.json | 52 + .../nodes/lists/numberedListStart.json | 48 + .../nodes/paragraph/multiple.json | 68 + .../__snapshots__/nodes/quote/basic.json | 23 + .../__snapshots__/nodes/quote/multiple.json | 44 + .../__snapshots__/nodes/quote/nested.json | 50 + .../__snapshots__/nodes/quote/styled.json | 45 + .../__snapshots__/nodes/quote/withLink.json | 38 + .../nodes/style/backgroundColor.json | 32 + .../__snapshots__/nodes/style/bold.json | 29 + .../nodes/style/boldItalicStrike.json | 35 + .../__snapshots__/nodes/style/code.json | 29 + .../__snapshots__/nodes/style/combined.json | 32 + .../__snapshots__/nodes/style/italic.json | 29 + .../nodes/style/mixedInParagraph.json | 60 + .../__snapshots__/nodes/style/strike.json | 29 + .../__snapshots__/nodes/style/textColor.json | 32 + .../__snapshots__/nodes/style/underline.json | 29 + .../nodes/table/advancedExample.json | 265 +++ .../nodes/table/cellTextAlignment.json | 89 + .../__snapshots__/nodes/table/emptyCells.json | 104 + .../nodes/table/hardBreakInCell.json | 74 + .../nodes/table/headerRowsAndCols.json | 160 ++ .../nodes/table/linksInCells.json | 142 ++ .../__snapshots__/nodes/table/singleCell.json | 45 + .../nodes/table/styledCellContent.json | 136 ++ .../nodes/video/withCaption.json | 22 + .../export/exportTestInstances.ts | 1826 ++++++++++++++--- .../__snapshots__/markdown/markdown/bold.json | 21 + .../markdown/markdown/boldItalic.json | 22 + .../markdown/markdown/bulletList.json | 36 + .../markdown/markdown/checkList.json | 38 + .../markdown/markdown/codeBlock.json | 17 + .../markdown/markdown/complexDocument.json | 106 + .../markdown/markdown/divider.json | 43 + .../markdown/markdown/hardBreak.json | 20 + .../markdown/markdown/headingLevels.json | 59 + .../markdown/markdown/image.json | 16 + .../markdown/markdown/inlineCode.json | 21 + .../markdown/markdown/italic.json | 21 + .../__snapshots__/markdown/markdown/link.json | 35 + .../markdown/markdown/mixedStyles.json | 50 + .../markdown/markdown/multipleParagraphs.json | 36 + .../markdown/markdown/nestedLists.json | 54 + .../markdown/markdown/numberedList.json | 36 + .../markdown/markdown/paragraph.json | 19 + .../markdown/markdown/quote.json | 18 + .../markdown/markdown/strike.json | 21 + .../markdown/markdown/table.json | 123 ++ .../markdown/markdown/video.json | 16 + .../exportParseEqualityTestInstances.ts | 454 ++++ .../exportParseEquality/runTests.test.ts | 14 + .../markdown/adjacentFormattedRuns.json | 35 + .../__snapshots__/markdown/adjacentLinks.json | 36 + .../markdown/backslashEscapes.json | 19 + .../markdown/blockquoteMultiline.json | 18 + .../markdown/blockquoteWithCode.json | 30 + .../markdown/blockquoteWithLink.json | 34 + .../__snapshots__/markdown/boldOnly.json | 21 + .../markdown/boldUnderscore.json | 21 + .../markdown/checkListBasic.json | 56 + .../markdown/checkListMixed.json | 55 + .../markdown/checkListNested.json | 57 + .../markdown/codeBlockBasic.json | 17 + .../markdown/codeBlockPython.json | 18 + .../markdown/codeBlockTildes.json | 17 + .../markdown/codeBlockWithLanguage.json | 18 + .../markdown/codeBlockWithSpecialChars.json | 19 + .../markdown/complexDocument.json | 442 ++++ .../markdown/deeplyNestedLists.json | 73 + .../__snapshots__/markdown/emptyString.json | 13 + .../markdown/hardBreakBackslash.json | 20 + .../markdown/hardBreakMultiple.json | 21 + .../__snapshots__/markdown/headingH1.json | 21 + .../__snapshots__/markdown/headingH2.json | 21 + .../__snapshots__/markdown/headingH3.json | 21 + .../__snapshots__/markdown/headingH4.json | 21 + .../__snapshots__/markdown/headingH5.json | 21 + .../__snapshots__/markdown/headingH6.json | 21 + .../markdown/headingThenCode.json | 36 + .../markdown/headingWithInlineStyles.json | 52 + .../markdown/horizontalRuleAsterisks.json | 43 + .../markdown/horizontalRuleDashes.json | 43 + .../markdown/horizontalRuleUnderscores.json | 43 + .../__snapshots__/markdown/imageWithAlt.json | 16 + .../__snapshots__/markdown/inlineCode.json | 31 + .../markdown/inlineCodeWithSpecialChars.json | 31 + .../__snapshots__/markdown/inlineImage.json | 19 + .../__snapshots__/markdown/italicOnly.json | 21 + .../markdown/italicUnderscore.json | 21 + .../__snapshots__/markdown/linkAndText.json | 35 + .../__snapshots__/markdown/linkBasic.json | 25 + .../markdown/linkInParagraph.json | 35 + .../markdown/linkWithStyledContent.json | 27 + .../markdown/listWithStyledItems.json | 83 + .../markdown/mixedInlineContent.json | 78 + .../markdown/mixedListTypes.json | 90 + .../markdown/multipleImages.json | 30 + .../markdown/multipleParagraphs.json | 53 + .../markdown/nestedBulletLists.json | 89 + .../markdown/nestedEmphasis.json | 22 + .../markdown/nestedEmphasisComplex.json | 36 + .../markdown/nestedOrderedLists.json | 71 + .../markdown/onlyWhitespace.json | 13 + .../markdown/orderedListStart.json | 54 + .../markdown/paragraphContinuation.json | 19 + .../__snapshots__/markdown/setextH1.json | 21 + .../__snapshots__/markdown/setextH2.json | 21 + .../markdown/strikethroughOnly.json | 21 + .../markdown/tableAlignment.json | 132 ++ .../__snapshots__/markdown/tableBasic.json | 135 ++ .../markdown/tableFollowedByParagraph.json | 114 + .../markdown/tableThreeColumns.json | 132 ++ .../markdown/tableWithInlineFormatting.json | 141 ++ .../markdown/tableWithLinks.json | 103 + .../parse/parseTestInstances.ts | 591 ++++++ .../exportParseEqualityTestExecutors.ts | 28 + 263 files changed, 12014 insertions(+), 904 deletions(-) create mode 100644 packages/core/src/api/exporters/markdown/htmlToMarkdown.ts delete mode 100644 packages/core/src/api/exporters/markdown/util/addSpacesToCheckboxesRehypePlugin.ts delete mode 100644 packages/core/src/api/exporters/markdown/util/convertVideoToMarkdownRehypePlugin.ts delete mode 100644 packages/core/src/api/exporters/markdown/util/removeUnderlinesRehypePlugin.ts create mode 100644 packages/core/src/api/parsers/markdown/markdownToHtml.ts create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/audio/basic.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/audio/button.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/audio/noName.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/complex/document.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h1.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h2.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h3.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h4.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h5.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h6.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/styled.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/toggleable.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/withCaption.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/link/withCode.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/lists/numberedListStart.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/paragraph/multiple.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/quote/basic.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/quote/multiple.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/quote/nested.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/quote/styled.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/quote/withLink.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/backgroundColor.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/bold.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/boldItalicStrike.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/code.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/combined.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/italic.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/mixedInParagraph.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/strike.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/textColor.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/underline.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/advancedExample.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/cellTextAlignment.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/emptyCells.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/hardBreakInCell.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/headerRowsAndCols.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/linksInCells.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/singleCell.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/styledCellContent.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/video/withCaption.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/audio/basic.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/audio/button.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/audio/noName.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/complex/document.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h1.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h2.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h3.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h4.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h5.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h6.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/styled.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/toggleable.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/image/withCaption.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/link/withCode.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/lists/numberedListStart.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/paragraph/multiple.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/quote/basic.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/quote/multiple.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/quote/nested.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/quote/styled.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/quote/withLink.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/style/backgroundColor.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/style/bold.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/style/boldItalicStrike.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/style/code.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/style/combined.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/style/italic.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/style/mixedInParagraph.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/style/strike.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/style/textColor.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/style/underline.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/table/advancedExample.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/table/cellTextAlignment.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/table/emptyCells.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/table/hardBreakInCell.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/table/headerRowsAndCols.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/table/linksInCells.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/table/singleCell.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/table/styledCellContent.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/html/video/withCaption.html create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/audio/basic.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/audio/button.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/audio/noName.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/complex/document.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h1.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h2.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h3.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h4.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h5.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h6.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/styled.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/toggleable.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/image/withCaption.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/link/withCode.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/lists/numberedListStart.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/paragraph/multiple.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/quote/basic.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/quote/multiple.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/quote/nested.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/quote/styled.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/quote/withLink.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/backgroundColor.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/bold.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/boldItalicStrike.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/code.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/combined.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/italic.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/mixedInParagraph.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/strike.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/textColor.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/underline.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/advancedExample.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/cellTextAlignment.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/emptyCells.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/hardBreakInCell.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/headerRowsAndCols.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/linksInCells.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/singleCell.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/styledCellContent.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/markdown/video/withCaption.md create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/audio/basic.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/audio/button.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/audio/noName.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/complex/document.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h1.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h2.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h3.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h4.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h5.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h6.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/styled.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/toggleable.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/image/withCaption.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/link/withCode.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/lists/numberedListStart.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/paragraph/multiple.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/quote/basic.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/quote/multiple.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/quote/nested.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/quote/styled.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/quote/withLink.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/backgroundColor.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/bold.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/boldItalicStrike.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/code.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/combined.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/italic.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/mixedInParagraph.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/strike.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/textColor.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/underline.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/advancedExample.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/cellTextAlignment.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/emptyCells.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/hardBreakInCell.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/headerRowsAndCols.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/linksInCells.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/singleCell.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/styledCellContent.json create mode 100644 tests/src/unit/core/formatConversion/export/__snapshots__/nodes/video/withCaption.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/bold.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/boldItalic.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/bulletList.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/checkList.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/codeBlock.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/complexDocument.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/divider.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/hardBreak.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/headingLevels.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/image.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/inlineCode.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/italic.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/link.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/mixedStyles.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/multipleParagraphs.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/nestedLists.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/numberedList.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/paragraph.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/quote.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/strike.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/table.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/video.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/adjacentFormattedRuns.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/adjacentLinks.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/backslashEscapes.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/blockquoteMultiline.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/blockquoteWithCode.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/blockquoteWithLink.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/boldOnly.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/boldUnderscore.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/checkListBasic.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/checkListMixed.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/checkListNested.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/codeBlockBasic.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/codeBlockPython.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/codeBlockTildes.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/codeBlockWithLanguage.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/codeBlockWithSpecialChars.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/complexDocument.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/deeplyNestedLists.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/emptyString.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/hardBreakBackslash.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/hardBreakMultiple.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH1.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH2.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH3.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH4.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH5.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH6.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingThenCode.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingWithInlineStyles.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/horizontalRuleAsterisks.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/horizontalRuleDashes.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/horizontalRuleUnderscores.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/imageWithAlt.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/inlineCode.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/inlineCodeWithSpecialChars.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/inlineImage.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/italicOnly.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/italicUnderscore.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/linkAndText.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/linkBasic.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/linkInParagraph.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/linkWithStyledContent.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/listWithStyledItems.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/mixedInlineContent.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/mixedListTypes.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/multipleImages.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/multipleParagraphs.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/nestedBulletLists.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/nestedEmphasis.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/nestedEmphasisComplex.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/nestedOrderedLists.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/onlyWhitespace.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/orderedListStart.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/paragraphContinuation.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/setextH1.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/setextH2.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/strikethroughOnly.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableAlignment.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableBasic.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableFollowedByParagraph.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableThreeColumns.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableWithInlineFormatting.json create mode 100644 tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableWithLinks.json diff --git a/packages/core/package.json b/packages/core/package.json index 682bb74b5a..fde4d8f08c 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -107,23 +107,12 @@ "@tiptap/pm": "^3.13.0", "emoji-mart": "^5.6.0", "fast-deep-equal": "^3.1.3", - "hast-util-from-dom": "^5.0.1", "prosemirror-highlight": "^0.15.1", "prosemirror-model": "^1.25.4", "prosemirror-state": "^1.4.4", "prosemirror-tables": "^1.8.3", "prosemirror-transform": "^1.11.0", "prosemirror-view": "^1.41.4", - "rehype-format": "^5.0.1", - "rehype-parse": "^9.0.1", - "rehype-remark": "^10.0.1", - "rehype-stringify": "^10.0.1", - "remark-gfm": "^4.0.1", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.1.2", - "remark-stringify": "^11.0.0", - "unified": "^11.0.5", - "unist-util-visit": "^5.0.0", "uuid": "^8.3.2", "y-prosemirror": "^1.3.7", "y-protocols": "^1.0.6", @@ -131,7 +120,6 @@ }, "devDependencies": { "@types/emoji-mart": "^3.0.14", - "@types/hast": "^3.0.4", "@types/uuid": "^8.3.4", "eslint": "^8.57.1", "jsdom": "^25.0.1", diff --git a/packages/core/src/api/exporters/markdown/htmlToMarkdown.ts b/packages/core/src/api/exporters/markdown/htmlToMarkdown.ts new file mode 100644 index 0000000000..be663a9b70 --- /dev/null +++ b/packages/core/src/api/exporters/markdown/htmlToMarkdown.ts @@ -0,0 +1,678 @@ +/** + * Custom HTML-to-Markdown serializer for BlockNote. + * Replaces the unified/rehype-remark pipeline with a direct DOM-based implementation. + * + * Input: HTML string from createExternalHTMLExporter + * Output: GFM-compatible markdown string + */ + +/** + * Convert an HTML string (from BlockNote's external HTML exporter) to markdown. + */ +export function htmlToMarkdown(html: string): string { + const parser = new DOMParser(); + const doc = parser.parseFromString(html, "text/html"); + const result = serializeChildren(doc.body, { indent: "", inList: false }); + return result.trim() + "\n"; +} + +interface SerializeContext { + indent: string; // current indentation prefix for list nesting + inList: boolean; // whether we're inside a list +} + +// ─── Main Serializer ───────────────────────────────────────────────────────── + +function serializeChildren(node: Node, ctx: SerializeContext): string { + let result = ""; + const children = Array.from(node.childNodes); + + for (let i = 0; i < children.length; i++) { + const child = children[i]; + result += serializeNode(child, ctx); + } + + return result; +} + +function serializeNode(node: Node, ctx: SerializeContext): string { + if (node.nodeType === Node.TEXT_NODE) { + return node.textContent || ""; + } + + if (node.nodeType !== Node.ELEMENT_NODE) { + return ""; + } + + const el = node as HTMLElement; + const tag = el.tagName.toLowerCase(); + + switch (tag) { + case "p": + return serializeParagraph(el, ctx); + case "h1": + case "h2": + case "h3": + case "h4": + case "h5": + case "h6": + return serializeHeading(el, ctx); + case "blockquote": + return serializeBlockquote(el, ctx); + case "pre": + return serializeCodeBlock(el, ctx); + case "ul": + return serializeUnorderedList(el, ctx); + case "ol": + return serializeOrderedList(el, ctx); + case "table": + return serializeTable(el, ctx); + case "hr": + return ctx.indent + "***\n\n"; + case "img": + return serializeImage(el, ctx); + case "video": + return serializeVideo(el, ctx); + case "audio": + return serializeAudio(el, ctx); + case "figure": + return serializeFigure(el, ctx); + case "a": + // Block-level link (file block) + return serializeBlockLink(el, ctx); + case "details": + return serializeDetails(el, ctx); + case "div": + // Page break or generic container — serialize children + return serializeChildren(el, ctx); + case "br": + return ""; + default: + return serializeChildren(el, ctx); + } +} + +// ─── Block Serializers ─────────────────────────────────────────────────────── + +function serializeParagraph(el: HTMLElement, ctx: SerializeContext): string { + const content = serializeInlineContent(el); + // Trim leading/trailing hard breaks (matching remark behavior) + const trimmed = trimHardBreaks(content); + if (ctx.inList) { + return trimmed; + } + return ctx.indent + trimmed + "\n\n"; +} + +function serializeHeading(el: HTMLElement, ctx: SerializeContext): string { + const level = parseInt(el.tagName[1], 10); + const prefix = "#".repeat(level) + " "; + const content = serializeInlineContent(el); + return ctx.indent + prefix + content + "\n\n"; +} + +function serializeBlockquote(el: HTMLElement, ctx: SerializeContext): string { + // Check if blockquote contains block-level elements (like

) + const blockChildren = Array.from(el.children).filter((child) => { + const tag = child.tagName.toLowerCase(); + return ["p", "ul", "ol", "pre", "blockquote", "table", "hr"].includes(tag); + }); + + let content: string; + if (blockChildren.length > 0) { + // Has block-level children — serialize each + const parts: string[] = []; + for (const child of blockChildren) { + const tag = child.tagName.toLowerCase(); + if (tag === "p") { + parts.push(serializeInlineContent(child as HTMLElement)); + } else { + const innerCtx: SerializeContext = { indent: "", inList: false }; + parts.push(serializeNode(child, innerCtx).trim()); + } + } + content = parts.join("\n\n"); + } else { + // No block-level children — treat entire content as inline + content = serializeInlineContent(el); + } + + const lines = content.split("\n"); + return lines.map((line) => ctx.indent + "> " + line).join("\n") + "\n\n"; +} + +function serializeCodeBlock(el: HTMLElement, ctx: SerializeContext): string { + const codeEl = el.querySelector("code"); + if (!codeEl) return ""; + + const language = + codeEl.getAttribute("data-language") || + extractLanguageFromClass(codeEl.className) || + ""; + + // Extract code content, handling
elements as newlines + const code = extractCodeContent(codeEl); + + // For empty code blocks, don't add a newline between the fences + if (!code) { + return ctx.indent + "```" + language + "\n```\n\n"; + } + + return ( + ctx.indent + + "```" + + language + + "\n" + + code + + (code.endsWith("\n") ? "" : "\n") + + "```\n\n" + ); +} + +function extractCodeContent(el: Element): string { + let result = ""; + for (const child of Array.from(el.childNodes)) { + if (child.nodeType === Node.TEXT_NODE) { + result += child.textContent || ""; + } else if (child.nodeType === Node.ELEMENT_NODE) { + const tag = (child as HTMLElement).tagName.toLowerCase(); + if (tag === "br") { + result += "\n"; + } else { + result += extractCodeContent(child as Element); + } + } + } + return result; +} + +function extractLanguageFromClass(className: string): string { + const match = className.match(/language-(\S+)/); + return match ? match[1] : ""; +} + +function serializeUnorderedList( + el: HTMLElement, + ctx: SerializeContext +): string { + let result = ""; + const items = Array.from(el.children).filter( + (child) => child.tagName.toLowerCase() === "li" + ); + + for (const item of items) { + result += serializeListItem(item as HTMLElement, "bullet", ctx); + } + + return result; +} + +function serializeOrderedList(el: HTMLElement, ctx: SerializeContext): string { + let result = ""; + const items = Array.from(el.children).filter( + (child) => child.tagName.toLowerCase() === "li" + ); + const startNum = parseInt(el.getAttribute("start") || "1", 10); + + for (let i = 0; i < items.length; i++) { + const num = startNum + i; + result += serializeListItem(items[i] as HTMLElement, "ordered", ctx, num); + } + + return result; +} + +function serializeListItem( + el: HTMLElement, + listType: "bullet" | "ordered", + ctx: SerializeContext, + num?: number +): string { + // Check for checkbox (task list) - direct children only + let checkbox: HTMLInputElement | null = null; + let details: HTMLElement | null = null; + + for (const child of Array.from(el.children)) { + const tag = child.tagName.toLowerCase(); + if (tag === "input" && (child as HTMLInputElement).type === "checkbox") { + checkbox = child as HTMLInputElement; + } + if (tag === "details") { + details = child as HTMLElement; + } + } + + let marker: string; + let markerWidth: number; + + if (checkbox) { + const state = checkbox.checked ? "[x]" : "[ ]"; + marker = `* ${state} `; + // For child indentation, use bullet width (2), not full checkbox marker width + markerWidth = 2; + } else if (listType === "ordered") { + marker = `${num}. `; + markerWidth = marker.length; + } else { + marker = "* "; + markerWidth = 2; + } + + // Collect the item's inline content + let inlineContent: string; + let firstContentEl: Element | null; + + if (details) { + // Toggle item: get content from summary + const summary = details.querySelector("summary"); + const summaryP = summary?.querySelector("p"); + firstContentEl = details; + inlineContent = summaryP ? serializeInlineContent(summaryP) : ""; + } else { + firstContentEl = getFirstContentElement(el, checkbox); + inlineContent = firstContentEl ? serializeInlineContent(firstContentEl) : ""; + } + + let result = ctx.indent + marker + inlineContent + "\n\n"; + + // Serialize child content (nested lists, continuation paragraphs, etc.) + const childIndent = ctx.indent + " ".repeat(markerWidth); + const childCtx: SerializeContext = { indent: childIndent, inList: true }; + + // For toggle items, also serialize children inside the details element + if (details) { + const summary = details.querySelector("summary"); + for (const child of Array.from(details.children)) { + if (child === summary) continue; + const childTag = child.tagName.toLowerCase(); + if (childTag === "p") { + const content = serializeInlineContent(child as HTMLElement); + result += childIndent + content + "\n\n"; + } else { + result += serializeNode(child, childCtx); + } + } + } + + const children = Array.from(el.children); + for (const child of children) { + const childTag = child.tagName.toLowerCase(); + + // Skip the first content element and checkbox + if (child === firstContentEl || (child as HTMLElement) === checkbox) continue; + if (childTag === "input") continue; + + // Nested lists and other block content + if (childTag === "ul" || childTag === "ol") { + result += serializeNode(child, childCtx); + } else if (childTag === "p") { + // Continuation paragraph within list item + const content = serializeInlineContent(child as HTMLElement); + result += childIndent + content + "\n\n"; + } else { + result += serializeNode(child, childCtx); + } + } + + return result; +} + +function getFirstContentElement( + li: HTMLElement, + checkbox: HTMLInputElement | null +): HTMLElement | null { + for (const child of Array.from(li.children)) { + if (child === checkbox) continue; + if (child.tagName.toLowerCase() === "input") continue; + const tag = child.tagName.toLowerCase(); + if (tag === "p" || tag === "span") return child as HTMLElement; + } + return null; +} + +// ─── Table Serializer ──────────────────────────────────────────────────────── + +function serializeTable(el: HTMLElement, ctx: SerializeContext): string { + // First, determine column count from colgroup or first row + const colgroup = el.querySelector("colgroup"); + let colCount = 0; + + if (colgroup) { + colCount = colgroup.querySelectorAll("col").length; + } + + const rows: string[][] = []; + let hasHeader = false; + + // Collect all rows, handling colspan/rowspan + const trElements = el.querySelectorAll("tr"); + // Build a grid to handle colspan/rowspan + const grid: (string | null)[][] = []; + + trElements.forEach((tr, rowIdx) => { + if (!grid[rowIdx]) grid[rowIdx] = []; + const cellElements = tr.querySelectorAll("th, td"); + let gridCol = 0; + + cellElements.forEach((cell) => { + // Find next empty column in this row + while (grid[rowIdx][gridCol] !== undefined) gridCol++; + + if (rowIdx === 0 && cell.tagName.toLowerCase() === "th") { + hasHeader = true; + } + + const content = serializeInlineContent(cell as HTMLElement).trim(); + const colspan = parseInt(cell.getAttribute("colspan") || "1", 10); + const rowspan = parseInt(cell.getAttribute("rowspan") || "1", 10); + + // Fill the grid + for (let r = 0; r < rowspan; r++) { + for (let c = 0; c < colspan; c++) { + const ri = rowIdx + r; + if (!grid[ri]) grid[ri] = []; + grid[ri][gridCol + c] = r === 0 && c === 0 ? content : ""; + } + } + + gridCol += colspan; + }); + + // Update colCount + if (grid[rowIdx]) { + colCount = Math.max(colCount, grid[rowIdx].length); + } + }); + + // Convert grid to rows + for (const gridRow of grid) { + const row: string[] = []; + for (let c = 0; c < colCount; c++) { + row.push(gridRow && gridRow[c] !== undefined ? (gridRow[c] ?? "") : ""); + } + rows.push(row); + } + + if (rows.length === 0) return ""; + + // Determine column widths + const colWidths: number[] = []; + for (let c = 0; c < colCount; c++) { + let maxWidth = 3; // minimum width for separator "---" + for (const row of rows) { + const cellWidth = c < row.length ? row[c].length : 0; + maxWidth = Math.max(maxWidth, cellWidth); + } + // Use minimum of 10 to match remark output + colWidths.push(Math.max(maxWidth, 10)); + } + + let result = ""; + + if (hasHeader) { + result += ctx.indent + formatTableRow(rows[0], colWidths, colCount) + "\n"; + result += ctx.indent + formatSeparatorRow(colWidths, colCount) + "\n"; + for (let r = 1; r < rows.length; r++) { + result += + ctx.indent + formatTableRow(rows[r], colWidths, colCount) + "\n"; + } + } else { + // No header — emit empty header + separator + const emptyRow = new Array(colCount).fill(""); + result += ctx.indent + formatTableRow(emptyRow, colWidths, colCount) + "\n"; + result += ctx.indent + formatSeparatorRow(colWidths, colCount) + "\n"; + for (const row of rows) { + result += + ctx.indent + formatTableRow(row, colWidths, colCount) + "\n"; + } + } + + result += "\n"; + return result; +} + +function formatTableRow( + cells: string[], + colWidths: number[], + colCount: number +): string { + const parts: string[] = []; + for (let c = 0; c < colCount; c++) { + const cell = c < cells.length ? cells[c] : ""; + parts.push(" " + cell.padEnd(colWidths[c]) + " "); + } + return "|" + parts.join("|") + "|"; +} + +function formatSeparatorRow(colWidths: number[], colCount: number): string { + const parts: string[] = []; + for (let c = 0; c < colCount; c++) { + parts.push(" " + "-".repeat(colWidths[c]) + " "); + } + return "|" + parts.join("|") + "|"; +} + +// ─── Media Serializers ─────────────────────────────────────────────────────── + +function serializeImage(el: HTMLElement, ctx: SerializeContext): string { + const src = el.getAttribute("src") || ""; + const alt = el.getAttribute("alt") || ""; + if (!src) { + return ctx.indent + "Add image\n\n"; + } + return ctx.indent + `![${alt}](${src})\n\n`; +} + +function serializeVideo(el: HTMLElement, ctx: SerializeContext): string { + const src = + el.getAttribute("src") || el.getAttribute("data-url") || ""; + const name = el.getAttribute("data-name") || el.getAttribute("title") || ""; + if (!src) { + return ctx.indent + "Add video\n\n"; + } + return ctx.indent + `![${name}](${src})\n\n`; +} + +function serializeAudio(el: HTMLElement, ctx: SerializeContext): string { + const src = el.getAttribute("src") || ""; + if (!src) return ""; + // Audio has no visible representation in markdown; output as link with empty text + return ctx.indent + `[](${src})\n\n`; +} + +function serializeFigure(el: HTMLElement, ctx: SerializeContext): string { + let result = ""; + + // Find the media element + const img = el.querySelector("img"); + const video = el.querySelector("video"); + const audio = el.querySelector("audio"); + const link = el.querySelector("a"); + + if (img) { + const src = img.getAttribute("src") || ""; + const alt = img.getAttribute("alt") || ""; + result += ctx.indent + `![${alt}](${src})\n\n`; + } else if (video) { + const src = + video.getAttribute("src") || video.getAttribute("data-url") || ""; + const name = + video.getAttribute("data-name") || video.getAttribute("title") || ""; + result += ctx.indent + `![${name}](${src})\n\n`; + } else if (audio) { + const src = audio.getAttribute("src") || ""; + result += ctx.indent + `[](${src})\n\n`; + } else if (link) { + result += serializeBlockLink(link as HTMLElement, ctx); + } + + // Caption + const figcaption = el.querySelector("figcaption"); + if (figcaption) { + const caption = figcaption.textContent?.trim() || ""; + if (caption) { + result += ctx.indent + caption + "\n\n"; + } + } + + return result; +} + +function serializeBlockLink(el: HTMLElement, ctx: SerializeContext): string { + const href = el.getAttribute("href") || ""; + const text = el.textContent?.trim() || ""; + if (!href) return ctx.indent + text + "\n\n"; + return ctx.indent + `[${text}](${href})\n\n`; +} + +function serializeDetails(el: HTMLElement, ctx: SerializeContext): string { + // Toggle heading or toggle list item + const summary = el.querySelector("summary"); + if (!summary) return serializeChildren(el, ctx); + + // Check if summary contains a heading + const heading = summary.querySelector("h1, h2, h3, h4, h5, h6"); + if (heading) { + let result = serializeHeading(heading as HTMLElement, ctx); + // Also serialize non-summary children of details + for (const child of Array.from(el.children)) { + if (child !== summary) { + result += serializeNode(child, ctx); + } + } + return result; + } + + // Otherwise serialize the summary content + return serializeChildren(summary, ctx); +} + +// ─── Inline Content Serializer ─────────────────────────────────────────────── + +function serializeInlineContent(el: Element): string { + let result = ""; + + for (const child of Array.from(el.childNodes)) { + if (child.nodeType === Node.TEXT_NODE) { + result += child.textContent || ""; + } else if (child.nodeType === Node.ELEMENT_NODE) { + const childEl = child as HTMLElement; + const tag = childEl.tagName.toLowerCase(); + + switch (tag) { + case "strong": + case "b": { + const inner = serializeInlineContent(childEl); + const { content, trailing } = extractTrailingWhitespace(inner); + if (content) { + result += `**${content}**${trailing}`; + } else { + // All whitespace — just output it without emphasis + result += trailing; + } + break; + } + case "em": + case "i": { + const inner = serializeInlineContent(childEl); + const { content, trailing } = extractTrailingWhitespace(inner); + if (content) { + result += `*${content}*${trailing}`; + } else { + result += trailing; + } + break; + } + case "s": + case "del": + result += `~~${serializeInlineContent(childEl)}~~`; + break; + case "code": + result += "`" + (childEl.textContent || "") + "`"; + break; + case "u": + // No markdown equivalent — strip the tag, keep content + result += serializeInlineContent(childEl); + break; + case "a": { + const href = childEl.getAttribute("href") || ""; + const text = serializeInlineContent(childEl); + result += `[${text}](${href})`; + break; + } + case "br": + result += "\\\n"; + break; + case "span": + // Color spans, etc. — strip the tag, keep content + result += serializeInlineContent(childEl); + break; + case "img": { + const src = childEl.getAttribute("src") || ""; + const alt = childEl.getAttribute("alt") || ""; + result += `![${alt}](${src})`; + break; + } + case "video": { + const src = + childEl.getAttribute("src") || + childEl.getAttribute("data-url") || + ""; + const name = + childEl.getAttribute("data-name") || + childEl.getAttribute("title") || + ""; + result += `![${name}](${src})`; + break; + } + case "p": + // Paragraph inside inline context (e.g., table cell) + result += serializeInlineContent(childEl); + break; + case "input": + // Checkbox in task list — handled at block level + break; + default: + result += serializeInlineContent(childEl); + break; + } + } + } + + return result; +} + +/** + * Extract trailing whitespace from emphasis content. + * Moves trailing spaces outside the emphasis delimiters to produce valid markdown. + * E.g., `Bold ` → `**Bold** ` instead of `**Bold **`. + */ +function extractTrailingWhitespace(text: string): { + content: string; + trailing: string; +} { + const match = text.match(/^(.*?)(\s*)$/); + if (match) { + return { content: match[1], trailing: match[2] }; + } + return { content: text, trailing: "" }; +} + +/** + * Escape leading character after emphasis if it could break parsing. + * For example, "Heading" after "**Bold **" — the 'H' should be escaped + * if the trailing space was escaped. + */ + +/** + * Trim leading/trailing hard breaks from inline content. + * Matches remark behavior where
at start/end of paragraph is dropped. + */ +function trimHardBreaks(content: string): string { + // Remove leading hard breaks + let result = content.replace(/^(\\\n)+/, ""); + // Remove trailing hard breaks (including trailing backslash) + result = result.replace(/(\\\n)+$/, ""); + result = result.replace(/\\$/, ""); + return result; +} diff --git a/packages/core/src/api/exporters/markdown/markdownExporter.ts b/packages/core/src/api/exporters/markdown/markdownExporter.ts index 23aad8db7c..2f73616dc0 100644 --- a/packages/core/src/api/exporters/markdown/markdownExporter.ts +++ b/packages/core/src/api/exporters/markdown/markdownExporter.ts @@ -1,9 +1,4 @@ import { Schema } from "prosemirror-model"; -import rehypeParse from "rehype-parse"; -import rehypeRemark from "rehype-remark"; -import remarkGfm from "remark-gfm"; -import remarkStringify from "remark-stringify"; -import { unified } from "unified"; import { PartialBlock } from "../../../blocks/defaultBlocks.js"; import type { BlockNoteEditor } from "../../../editor/BlockNoteEditor.js"; @@ -13,25 +8,11 @@ import { StyleSchema, } from "../../../schema/index.js"; import { createExternalHTMLExporter } from "../html/externalHTMLExporter.js"; -import { removeUnderlines } from "./util/removeUnderlinesRehypePlugin.js"; -import { addSpacesToCheckboxes } from "./util/addSpacesToCheckboxesRehypePlugin.js"; -import { convertVideoToMarkdown } from "./util/convertVideoToMarkdownRehypePlugin.js"; +import { htmlToMarkdown } from "./htmlToMarkdown.js"; // Needs to be sync because it's used in drag handler event (SideMenuPlugin) export function cleanHTMLToMarkdown(cleanHTMLString: string) { - const markdownString = unified() - .use(rehypeParse, { fragment: true }) - .use(convertVideoToMarkdown) - .use(removeUnderlines) - .use(addSpacesToCheckboxes) - .use(rehypeRemark) - .use(remarkGfm) - .use(remarkStringify, { - handlers: { text: (node) => node.value }, - }) - .processSync(cleanHTMLString); - - return markdownString.value as string; + return htmlToMarkdown(cleanHTMLString); } export function blocksToMarkdown< diff --git a/packages/core/src/api/exporters/markdown/util/addSpacesToCheckboxesRehypePlugin.ts b/packages/core/src/api/exporters/markdown/util/addSpacesToCheckboxesRehypePlugin.ts deleted file mode 100644 index 7c03eb9a64..0000000000 --- a/packages/core/src/api/exporters/markdown/util/addSpacesToCheckboxesRehypePlugin.ts +++ /dev/null @@ -1,42 +0,0 @@ -import { Element as HASTElement, Parent as HASTParent } from "hast"; -import { fromDom } from "hast-util-from-dom"; - -/** - * Rehype plugin which adds a space after each checkbox input element. This is - * because remark doesn't add any spaces between the checkbox input and the text - * itself, but these are needed for correct Markdown syntax. - */ -export function addSpacesToCheckboxes() { - const helper = (tree: HASTParent) => { - if (tree.children && "length" in tree.children && tree.children.length) { - for (let i = tree.children.length - 1; i >= 0; i--) { - const child = tree.children[i]; - const nextChild = - i + 1 < tree.children.length ? tree.children[i + 1] : undefined; - - // Checks for paragraph element after checkbox input element. - if ( - child.type === "element" && - child.tagName === "input" && - child.properties?.type === "checkbox" && - nextChild?.type === "element" && - nextChild.tagName === "p" - ) { - // Converts paragraph to span, otherwise remark will think it needs to - // be on a new line. - nextChild.tagName = "span"; - // Adds a space after the checkbox input element. - nextChild.children.splice( - 0, - 0, - fromDom(document.createTextNode(" ")) as HASTElement, - ); - } else { - helper(child as HASTParent); - } - } - } - }; - - return helper; -} diff --git a/packages/core/src/api/exporters/markdown/util/convertVideoToMarkdownRehypePlugin.ts b/packages/core/src/api/exporters/markdown/util/convertVideoToMarkdownRehypePlugin.ts deleted file mode 100644 index a7de2e3442..0000000000 --- a/packages/core/src/api/exporters/markdown/util/convertVideoToMarkdownRehypePlugin.ts +++ /dev/null @@ -1,19 +0,0 @@ -import { Parent as HASTParent } from "hast"; -import { visit } from "unist-util-visit"; - -// Originally, rehypeParse parses videos as links, which is incorrect. -export function convertVideoToMarkdown() { - return (tree: HASTParent) => { - visit(tree, "element", (node, index, parent) => { - if (parent && node.tagName === "video") { - const src = node.properties?.src || node.properties?.["data-url"] || ""; - const name = - node.properties?.title || node.properties?.["data-name"] || ""; - parent.children[index!] = { - type: "text", - value: `![${name}](${src})`, - }; - } - }); - }; -} diff --git a/packages/core/src/api/exporters/markdown/util/removeUnderlinesRehypePlugin.ts b/packages/core/src/api/exporters/markdown/util/removeUnderlinesRehypePlugin.ts deleted file mode 100644 index 5b455d1b53..0000000000 --- a/packages/core/src/api/exporters/markdown/util/removeUnderlinesRehypePlugin.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Element as HASTElement, Parent as HASTParent } from "hast"; - -/** - * Rehype plugin which removes tags. Used to remove underlines before converting HTML to markdown, as Markdown - * doesn't support underlines. - */ -export function removeUnderlines() { - const removeUnderlinesHelper = (tree: HASTParent) => { - let numChildElements = tree.children.length; - - for (let i = 0; i < numChildElements; i++) { - const node = tree.children[i]; - - if (node.type === "element") { - // Recursively removes underlines from child elements. - removeUnderlinesHelper(node); - - if ((node as HASTElement).tagName === "u") { - // Lifts child nodes outside underline element, deletes the underline element, and updates current index & - // the number of child elements. - if (node.children.length > 0) { - tree.children.splice(i, 1, ...node.children); - - const numElementsAdded = node.children.length - 1; - numChildElements += numElementsAdded; - i += numElementsAdded; - } else { - tree.children.splice(i, 1); - - numChildElements--; - i--; - } - } - } - } - }; - - return removeUnderlinesHelper; -} diff --git a/packages/core/src/api/parsers/html/util/__snapshots__/nestedLists.test.ts.snap b/packages/core/src/api/parsers/html/util/__snapshots__/nestedLists.test.ts.snap index 68c0a1c817..1db488255b 100644 --- a/packages/core/src/api/parsers/html/util/__snapshots__/nestedLists.test.ts.snap +++ b/packages/core/src/api/parsers/html/util/__snapshots__/nestedLists.test.ts.snap @@ -1,129 +1,144 @@ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html exports[`Lift nested lists > Lifts multiple bullet lists 1`] = ` -" -

-" +"
    +
  • + Bullet List Item 1 +
    • +
    • + Nested Bullet List Item 1 +
    • +
    • + Nested Bullet List Item 2 +
    • +
      +
    • + Nested Bullet List Item 3 +
    • +
    • + Nested Bullet List Item 4 +
    • +
    +
  • + Bullet List Item 2 +
  • +
" `; exports[`Lift nested lists > Lifts multiple bullet lists with content in between 1`] = ` -" -
    -
    -
  • Bullet List Item 1
  • -
    -
      -
    • Nested Bullet List Item 1
    • -
    • Nested Bullet List Item 2
    • -
    -
    -
    -
    -
  • In between content
  • -
    -
      -
    • Nested Bullet List Item 3
    • -
    • Nested Bullet List Item 4
    • -
    -
    -
    -
  • Bullet List Item 2
  • -
-" +"
    +
  • + Bullet List Item 1 +
    • +
    • + Nested Bullet List Item 1 +
    • +
    • + Nested Bullet List Item 2 +
    • +
  • + In between content +
    • +
    • + Nested Bullet List Item 3 +
    • +
    • + Nested Bullet List Item 4 +
    • +
    +
  • + Bullet List Item 2 +
  • +
" `; exports[`Lift nested lists > Lifts nested bullet lists 1`] = ` -" -
    -
    -
  • Bullet List Item 1
  • -
    -
      -
    • Nested Bullet List Item 1
    • -
    • Nested Bullet List Item 2
    • -
    -
    -
    -
  • Bullet List Item 2
  • -
-" +"
    +
  • + Bullet List Item 1 +
    • +
    • + Nested Bullet List Item 1 +
    • +
    • + Nested Bullet List Item 2 +
    • +
    +
  • + Bullet List Item 2 +
  • +
" `; exports[`Lift nested lists > Lifts nested bullet lists with content after nested list 1`] = ` -" -
    -
    -
  • Bullet List Item 1
  • -
    -
      -
    • Nested Bullet List Item 1
    • -
    • Nested Bullet List Item 2
    • -
    -
    -
    -
  • More content in list item 1
  • -
  • Bullet List Item 2
  • -
-" +"
    +
  • + Bullet List Item 1 +
    • +
    • + Nested Bullet List Item 1 +
    • +
    • + Nested Bullet List Item 2 +
    • +
  • + More content in list item 1 +
  • +
  • + Bullet List Item 2 +
  • +
" `; exports[`Lift nested lists > Lifts nested bullet lists without li 1`] = ` -" -
    Bullet List Item 1 -
      -
    • Nested Bullet List Item 1
    • -
    • Nested Bullet List Item 2
    • -
    -
  • Bullet List Item 2
  • -
-" +"
    + Bullet List Item 1 +
      +
    • + Nested Bullet List Item 1 +
    • +
    • + Nested Bullet List Item 2 +
    • +
    +
  • + Bullet List Item 2 +
  • +
" `; exports[`Lift nested lists > Lifts nested mixed lists 1`] = ` -" -
    -
    -
  1. Numbered List Item 1
  2. -
    -
      -
    • Bullet List Item 1
    • -
    • Bullet List Item 2
    • -
    -
    -
    -
  3. Numbered List Item 2
  4. -
-" +"
    +
  1. + Numbered List Item 1 +
    • +
    • + Bullet List Item 1 +
    • +
    • + Bullet List Item 2 +
    • +
    +
  2. + Numbered List Item 2 +
  3. +
" `; exports[`Lift nested lists > Lifts nested numbered lists 1`] = ` -" -
    -
    -
  1. Numbered List Item 1
  2. -
    -
      -
    1. Nested Numbered List Item 1
    2. -
    3. Nested Numbered List Item 2
    4. -
    -
    -
    -
  3. Numbered List Item 2
  4. -
-" +"
    +
  1. + Numbered List Item 1 +
    1. +
    2. + Nested Numbered List Item 1 +
    3. +
    4. + Nested Numbered List Item 2 +
    5. +
    +
  2. + Numbered List Item 2 +
  3. +
" `; diff --git a/packages/core/src/api/parsers/html/util/nestedLists.test.ts b/packages/core/src/api/parsers/html/util/nestedLists.test.ts index 03fadebefe..e695efa9c4 100644 --- a/packages/core/src/api/parsers/html/util/nestedLists.test.ts +++ b/packages/core/src/api/parsers/html/util/nestedLists.test.ts @@ -1,20 +1,9 @@ import { describe, expect, it } from "vitest"; import { nestedListsToBlockNoteStructure } from "./nestedLists.js"; -import { unified } from "unified"; -import rehypeParse from "rehype-parse"; -import rehypeFormat from "rehype-format"; -import rehypeStringify from "rehype-stringify"; async function testHTML(html: string) { const htmlNode = nestedListsToBlockNoteStructure(html); - - const pretty = await unified() - .use(rehypeParse, { fragment: true }) - .use(rehypeFormat) - .use(rehypeStringify) - .process(htmlNode.innerHTML); - - expect(pretty.value).toMatchSnapshot(); + expect(htmlNode.innerHTML).toMatchSnapshot(); } describe("Lift nested lists", () => { diff --git a/packages/core/src/api/parsers/markdown/markdownToHtml.ts b/packages/core/src/api/parsers/markdown/markdownToHtml.ts new file mode 100644 index 0000000000..b4ad425f09 --- /dev/null +++ b/packages/core/src/api/parsers/markdown/markdownToHtml.ts @@ -0,0 +1,945 @@ +import { isVideoUrl } from "../../../util/string.js"; + +/** + * Custom markdown-to-HTML converter for BlockNote. + * Replaces the unified/remark/rehype pipeline with a direct, minimal implementation + * that handles exactly the markdown features BlockNote needs. + */ + +// ─── HTML Escaping ─────────────────────────────────────────────────────────── + +function escapeHtml(str: string): string { + return str + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """); +} + +// ─── Inline Parser ─────────────────────────────────────────────────────────── + +/** + * Parse inline markdown syntax and return HTML. + * Handles: bold, italic, bold+italic, strikethrough, inline code, + * links, images (with video detection), hard line breaks, backslash escapes. + */ +function parseInline(text: string): string { + let result = ""; + let i = 0; + + while (i < text.length) { + // Backslash escape + if (text[i] === "\\" && i + 1 < text.length) { + const next = text[i + 1]; + // Hard line break: backslash at end of line + if (next === "\n") { + result += "
\n"; + i += 2; + continue; + } + // Escapable characters + if ("\\`*_{}[]()#+-.!~|>".includes(next)) { + result += escapeHtml(next); + i += 2; + continue; + } + } + + // Inline code (highest priority for inline) + if (text[i] === "`") { + const codeResult = parseInlineCode(text, i); + if (codeResult) { + result += codeResult.html; + i = codeResult.end; + continue; + } + } + + // Images ![alt](url) + if (text[i] === "!" && text[i + 1] === "[") { + const imgResult = parseImage(text, i); + if (imgResult) { + result += imgResult.html; + i = imgResult.end; + continue; + } + } + + // Links [text](url) + if (text[i] === "[") { + const linkResult = parseLink(text, i); + if (linkResult) { + result += linkResult.html; + i = linkResult.end; + continue; + } + } + + // Strikethrough ~~text~~ + if (text[i] === "~" && text[i + 1] === "~") { + const strikeResult = parseDelimited(text, i, "~~", "", ""); + if (strikeResult) { + result += strikeResult.html; + i = strikeResult.end; + continue; + } + } + + // Bold+Italic ***text*** or ___text___ + if ( + (text[i] === "*" && text[i + 1] === "*" && text[i + 2] === "*") || + (text[i] === "_" && text[i + 1] === "_" && text[i + 2] === "_") + ) { + const delimiter = text.substring(i, i + 3); + const tripleResult = parseDelimited( + text, + i, + delimiter, + "", + "" + ); + if (tripleResult) { + result += tripleResult.html; + i = tripleResult.end; + continue; + } + } + + // Bold **text** or __text__ + if ( + (text[i] === "*" && text[i + 1] === "*") || + (text[i] === "_" && text[i + 1] === "_") + ) { + const delimiter = text.substring(i, i + 2); + const boldResult = parseDelimited( + text, + i, + delimiter, + "", + "" + ); + if (boldResult) { + result += boldResult.html; + i = boldResult.end; + continue; + } + } + + // Italic *text* or _text_ + if (text[i] === "*" || text[i] === "_") { + const delimiter = text[i]; + const italicResult = parseDelimited( + text, + i, + delimiter, + "", + "" + ); + if (italicResult) { + result += italicResult.html; + i = italicResult.end; + continue; + } + } + + // Newline within paragraph (soft break) + if (text[i] === "\n") { + result += "\n"; + i++; + continue; + } + + // Regular character + result += escapeHtml(text[i]); + i++; + } + + return result; +} + +function parseInlineCode( + text: string, + start: number +): { html: string; end: number } | null { + // Count opening backticks + let openCount = 0; + let i = start; + while (i < text.length && text[i] === "`") { + openCount++; + i++; + } + + // Find matching closing backticks + let j = i; + while (j < text.length) { + if (text[j] === "`") { + let closeCount = 0; + const closeStart = j; + while (j < text.length && text[j] === "`") { + closeCount++; + j++; + } + if (closeCount === openCount) { + let code = text.substring(i, closeStart); + // Strip one leading and one trailing space if both exist + if ( + code.length >= 2 && + code[0] === " " && + code[code.length - 1] === " " + ) { + code = code.substring(1, code.length - 1); + } + return { + html: `${escapeHtml(code)}`, + end: j, + }; + } + } else { + j++; + } + } + return null; +} + +function parseImage( + text: string, + start: number +): { html: string; end: number } | null { + // ![alt](url) or ![alt](url "title") + const altStart = start + 2; // after ![ + const altEnd = text.indexOf("]", altStart); + if (altEnd === -1) return null; + + if (text[altEnd + 1] !== "(") return null; + + const urlStart = altEnd + 2; + const parenEnd = findClosingParen(text, urlStart - 1); + if (parenEnd === -1) return null; + + const alt = text.substring(altStart, altEnd); + let urlContent = text.substring(urlStart, parenEnd).trim(); + let title: string | undefined; + + // Check for title in quotes + const titleMatch = urlContent.match(/^(\S+)\s+"([^"]*)"$/); + if (titleMatch) { + urlContent = titleMatch[1]; + title = titleMatch[2]; + } + + const url = urlContent; + + if (isVideoUrl(url)) { + // Match remark-rehype behavior: data-name comes from the title, not alt + return { + html: ``, + end: parenEnd + 1, + }; + } + + return { + html: `${escapeHtml(alt)}`, + end: parenEnd + 1, + }; +} + +function parseLink( + text: string, + start: number +): { html: string; end: number } | null { + // [text](url) + const textStart = start + 1; + const textEnd = findClosingBracket(text, start); + if (textEnd === -1) return null; + + if (text[textEnd + 1] !== "(") return null; + + const urlStart = textEnd + 2; + const parenEnd = findClosingParen(text, textEnd + 1); + if (parenEnd === -1) return null; + + const linkText = text.substring(textStart, textEnd); + const url = text.substring(urlStart, parenEnd).trim(); + + return { + html: `${parseInline(linkText)}`, + end: parenEnd + 1, + }; +} + +function findClosingBracket(text: string, openPos: number): number { + let depth = 0; + for (let i = openPos; i < text.length; i++) { + if (text[i] === "\\" && i + 1 < text.length) { + i++; // skip escaped + continue; + } + if (text[i] === "[") depth++; + if (text[i] === "]") { + depth--; + if (depth === 0) return i; + } + } + return -1; +} + +function findClosingParen(text: string, openPos: number): number { + let depth = 0; + for (let i = openPos; i < text.length; i++) { + if (text[i] === "\\" && i + 1 < text.length) { + i++; + continue; + } + if (text[i] === "(") depth++; + if (text[i] === ")") { + depth--; + if (depth === 0) return i; + } + } + return -1; +} + +function parseDelimited( + text: string, + start: number, + delimiter: string, + openTag: string, + closeTag: string +): { html: string; end: number } | null { + const len = delimiter.length; + const afterOpen = start + len; + + if (afterOpen >= text.length) return null; + + // Opening delimiter must not be followed by whitespace + if (text[afterOpen] === " " || text[afterOpen] === "\t") return null; + + // Find closing delimiter + let j = afterOpen; + while (j < text.length) { + // Skip escaped characters + if (text[j] === "\\" && j + 1 < text.length) { + j += 2; + continue; + } + + if (text.substring(j, j + len) === delimiter) { + // Closing delimiter must not be preceded by whitespace + if (text[j - 1] === " " || text[j - 1] === "\t") { + j++; + continue; + } + + // For single-char delimiters, don't accept closer if it's part of a + // multi-char run (e.g., don't treat second * in ** as italic closer) + if ( + len === 1 && + j > 0 && + text[j - 1] === delimiter[0] + ) { + j++; + continue; + } + + const inner = text.substring(afterOpen, j); + if (inner.length === 0) { + j++; + continue; + } + + return { + html: openTag + parseInline(inner) + closeTag, + end: j + len, + }; + } + j++; + } + + return null; +} + +// ─── Block-Level Types ─────────────────────────────────────────────────────── + +interface BlockToken { + type: string; +} + +interface HeadingToken extends BlockToken { + type: "heading"; + level: number; + content: string; +} + +interface ParagraphToken extends BlockToken { + type: "paragraph"; + content: string; +} + +interface CodeBlockToken extends BlockToken { + type: "codeBlock"; + language: string; + code: string; +} + +interface BlockquoteToken extends BlockToken { + type: "blockquote"; + content: string; +} + +interface HorizontalRuleToken extends BlockToken { + type: "hr"; +} + +interface ListItemToken extends BlockToken { + type: "listItem"; + listType: "bullet" | "ordered" | "task"; + indent: number; + content: string; + start?: number; // for ordered lists + checked?: boolean; // for task lists + childContent?: string; // recursively parsed content within this item +} + +interface TableToken extends BlockToken { + type: "table"; + headers: string[]; + rows: string[][]; + alignments: ("left" | "center" | "right" | null)[]; +} + +type Token = + | HeadingToken + | ParagraphToken + | CodeBlockToken + | BlockquoteToken + | HorizontalRuleToken + | ListItemToken + | TableToken; + +// ─── Block-Level Tokenizer ────────────────────────────────────────────────── + +function tokenize(markdown: string): Token[] { + const lines = markdown.split("\n"); + const tokens: Token[] = []; + let i = 0; + let prevLineWasBlank = true; // treat start of document as after blank + + while (i < lines.length) { + const line = lines[i]; + + // Blank line — skip + if (line.trim() === "") { + prevLineWasBlank = true; + i++; + continue; + } + + // Fenced code block + const fenceMatch = line.match(/^(`{3,}|~{3,})(.*)$/); + if (fenceMatch) { + const fence = fenceMatch[1]; + const fenceChar = fence[0]; + const fenceLen = fence.length; + const language = fenceMatch[2].trim(); + const codeLines: string[] = []; + i++; + while (i < lines.length) { + const closingMatch = lines[i].match( + new RegExp(`^${fenceChar}{${fenceLen},}\\s*$`) + ); + if (closingMatch) { + i++; + break; + } + codeLines.push(lines[i]); + i++; + } + tokens.push({ + type: "codeBlock", + language: language || "", + code: codeLines.join("\n"), + }); + prevLineWasBlank = false; + continue; + } + + // ATX Heading + const headingMatch = line.match(/^(#{1,6})\s+(.+?)(?:\s+#+)?$/); + if (headingMatch) { + tokens.push({ + type: "heading", + level: headingMatch[1].length, + content: headingMatch[2], + }); + prevLineWasBlank = false; + i++; + continue; + } + + // Horizontal rule: ---, ***, ___ (3+ chars, optionally with spaces) + if (/^(\s{0,3})([-*_])\s*(\2\s*){2,}$/.test(line)) { + // Setext H2: --- immediately after a paragraph (no blank line between) + const prevToken = tokens[tokens.length - 1]; + if ( + !prevLineWasBlank && + line.trim().match(/^-+$/) && + prevToken && + prevToken.type === "paragraph" + ) { + const para = prevToken as ParagraphToken; + tokens[tokens.length - 1] = { + type: "heading", + level: 2, + content: para.content, + }; + prevLineWasBlank = false; + i++; + continue; + } + tokens.push({ type: "hr" }); + prevLineWasBlank = false; + i++; + continue; + } + + // Setext heading detection: check if next line is === or --- + if (i + 1 < lines.length) { + const nextLine = lines[i + 1]; + if (/^={1,}\s*$/.test(nextLine) && line.trim().length > 0) { + tokens.push({ + type: "heading", + level: 1, + content: line.trim(), + }); + prevLineWasBlank = false; + i += 2; + continue; + } + // Setext H2 --- handled in HR section above + } + + // Table: detect by looking for separator row + const tableResult = tryParseTable(lines, i); + if (tableResult) { + tokens.push(tableResult.token); + i = tableResult.nextLine; + prevLineWasBlank = false; + continue; + } + + // Blockquote + if (/^\s{0,3}>/.test(line)) { + const quoteLines: string[] = []; + while (i < lines.length && /^\s{0,3}>/.test(lines[i])) { + // Remove the > prefix + quoteLines.push(lines[i].replace(/^\s{0,3}>\s?/, "")); + i++; + } + tokens.push({ + type: "blockquote", + content: quoteLines.join("\n"), + }); + prevLineWasBlank = false; + continue; + } + + // List item (bullet, ordered, or task) + const listItemMatch = line.match( + /^(\s*)([-*+]|\d+[.)])(\s+)(\[[ xX]\] )?(.*)$/ + ); + if (listItemMatch) { + const indent = listItemMatch[1].length; + const marker = listItemMatch[2]; + const markerSpaces = listItemMatch[3]; + const checkbox = listItemMatch[4]; + const firstLineContent = listItemMatch[5]; + + let listType: "bullet" | "ordered" | "task"; + let start: number | undefined; + let checked: boolean | undefined; + + if (checkbox) { + listType = "task"; + checked = checkbox.trim() !== "[ ]"; + } else if (/^\d+[.)]$/.test(marker)) { + listType = "ordered"; + start = parseInt(marker, 10); + } else { + listType = "bullet"; + } + + // Content indent = column where content actually starts + const contentIndent = + indent + + marker.length + + markerSpaces.length + + (checkbox ? checkbox.length : 0); + + // Minimum indent for child content: anything indented past the marker + // (sub-lists can start at indent > marker position) + const minChildIndent = indent + 1; + + // Helper to check if a line belongs to this list item + function belongsToItem(lineStr: string): boolean { + if (lineStr.trim() === "") return true; // blank lines checked separately + const lineInd = lineStr.match(/^\s*/)![0].length; + // Lines at contentIndent are continuation text + if (lineInd >= contentIndent) return true; + // Lines between marker and content column that start a sub-list + if ( + lineInd >= minChildIndent && + lineStr.match(/^\s*([-*+]|\d+[.)])\s+/) + ) { + return true; + } + return false; + } + + // Consume ALL subsequent lines that belong to this list item + i++; + const subLines: string[] = []; + while (i < lines.length) { + const cur = lines[i]; + + if (cur.trim() === "") { + // Blank line: include if followed by content that belongs to this item + let lookAhead = i + 1; + while (lookAhead < lines.length && lines[lookAhead].trim() === "") { + lookAhead++; + } + if (lookAhead < lines.length && belongsToItem(lines[lookAhead])) { + subLines.push(""); + i++; + continue; + } + break; + } + + if (!belongsToItem(cur)) break; + + // Strip indent: for lines at contentIndent+, strip contentIndent chars; + // for sub-list lines between minChildIndent and contentIndent, strip minChildIndent + const lineIndent = cur.match(/^\s*/)![0].length; + if (lineIndent >= contentIndent) { + subLines.push(cur.substring(contentIndent)); + } else { + // Sub-list item between minChildIndent and contentIndent + subLines.push(cur.substring(minChildIndent)); + } + i++; + } + + // Build the list item token + // If there are sub-lines, they become child content (recursively tokenized) + // Don't trim — preserve relative indentation of sub-lines + const childContent = subLines.join("\n").replace(/^\n+|\n+$/g, ""); + tokens.push({ + type: "listItem", + listType, + indent, + content: firstLineContent.trim(), + start, + checked, + childContent: childContent || undefined, + }); + prevLineWasBlank = false; + continue; + } + + // Paragraph (default) + const paraLines: string[] = [line]; + i++; + while (i < lines.length) { + const nextLine = lines[i]; + // Stop paragraph on blank line + if (nextLine.trim() === "") break; + // Stop on block-level element + if (/^(#{1,6})\s/.test(nextLine)) break; + if (/^(`{3,}|~{3,})/.test(nextLine)) break; + if (/^\s{0,3}>/.test(nextLine)) break; + if (/^(\s{0,3})([-*_])\s*(\2\s*){2,}$/.test(nextLine)) break; + if (/^\s*([-*+]|\d+[.)])\s+/.test(nextLine)) break; + if (/^\s*\|(.+\|)+\s*$/.test(nextLine)) break; + // Check if next-next line is setext marker + if ( + i + 1 < lines.length && + /^[=-]+\s*$/.test(lines[i + 1]) && + nextLine.trim().length > 0 + ) { + break; + } + paraLines.push(nextLine); + i++; + } + tokens.push({ + type: "paragraph", + content: paraLines.join("\n"), + }); + prevLineWasBlank = false; + } + + return tokens; +} + +function tryParseTable( + lines: string[], + start: number +): { token: TableToken; nextLine: number } | null { + // A table needs at least a header row and a separator row + if (start + 1 >= lines.length) return null; + + const headerLine = lines[start]; + const separatorLine = lines[start + 1]; + + // Check separator line format: | --- | --- | or | :--- | ---: | + if (!/^\s*\|(\s*:?-+:?\s*\|)+\s*$/.test(separatorLine)) return null; + + // Check header line format: | ... | ... | + if (!/^\s*\|(.+\|)+\s*$/.test(headerLine)) return null; + + const headers = parsePipeCells(headerLine); + const alignments = parseAlignments(separatorLine); + + const rows: string[][] = []; + let i = start + 2; + while (i < lines.length) { + const line = lines[i]; + if (!/^\s*\|(.+\|)+\s*$/.test(line)) break; + rows.push(parsePipeCells(line)); + i++; + } + + return { + token: { + type: "table", + headers, + rows, + alignments, + }, + nextLine: i, + }; +} + +function parsePipeCells(line: string): string[] { + // Trim leading/trailing pipes and split + const trimmed = line.trim(); + const withoutOuterPipes = trimmed.startsWith("|") + ? trimmed.substring(1) + : trimmed; + const content = withoutOuterPipes.endsWith("|") + ? withoutOuterPipes.substring(0, withoutOuterPipes.length - 1) + : withoutOuterPipes; + + // Split by pipes, handling escaped pipes + const cells: string[] = []; + let current = ""; + for (let i = 0; i < content.length; i++) { + if (content[i] === "\\" && i + 1 < content.length && content[i + 1] === "|") { + current += "|"; + i++; + } else if (content[i] === "|") { + cells.push(current.trim()); + current = ""; + } else { + current += content[i]; + } + } + cells.push(current.trim()); + + return cells; +} + +function parseAlignments( + separatorLine: string +): ("left" | "center" | "right" | null)[] { + const cells = parsePipeCells(separatorLine); + return cells.map((cell) => { + const trimmed = cell.trim(); + const left = trimmed.startsWith(":"); + const right = trimmed.endsWith(":"); + if (left && right) return "center"; + if (right) return "right"; + if (left) return "left"; + return null; + }); +} + +// ─── HTML Emitter ──────────────────────────────────────────────────────────── + +function tokensToHtml(tokens: Token[]): string { + let html = ""; + let i = 0; + + while (i < tokens.length) { + const token = tokens[i]; + + switch (token.type) { + case "heading": { + const t = token as HeadingToken; + html += `${parseInline(t.content)}`; + i++; + break; + } + + case "paragraph": { + const t = token as ParagraphToken; + html += `

${parseInline(t.content)}

`; + i++; + break; + } + + case "codeBlock": { + const t = token as CodeBlockToken; + const langAttr = t.language + ? ` data-language="${escapeHtml(t.language)}"` + : ""; + html += `
${escapeHtml(t.code)}
`; + i++; + break; + } + + case "blockquote": { + const t = token as BlockquoteToken; + // Recursively parse blockquote content as markdown + const innerTokens = tokenize(t.content); + const innerHtml = tokensToHtml(innerTokens); + html += `
${innerHtml}
`; + i++; + break; + } + + case "hr": + html += `
`; + i++; + break; + + case "listItem": { + // Collect consecutive list items and build nested list structure + const listHtml = emitListItems(tokens, i); + html += listHtml.html; + i = listHtml.nextIndex; + break; + } + + case "table": { + const t = token as TableToken; + html += emitTable(t); + i++; + break; + } + + default: + i++; + } + } + + return html; +} + +function emitListItems( + tokens: Token[], + startIdx: number +): { html: string; nextIndex: number } { + let html = ""; + let i = startIdx; + let currentListType: "bullet" | "ordered" | null = null; + + while (i < tokens.length && tokens[i].type === "listItem") { + const item = tokens[i] as ListItemToken; + const effectiveType = getEffectiveListType(item.listType); + + // Check if we need to switch list type + if (currentListType !== null && currentListType !== effectiveType) { + // Close current list, open new one + html += ``; + currentListType = null; + } + + // Open list if needed + if (currentListType === null) { + if (effectiveType === "ordered") { + const startAttr = + item.start !== undefined && item.start !== 1 + ? ` start="${item.start}"` + : ""; + html += ``; + } else { + html += `
    `; + } + currentListType = effectiveType; + } + + // Emit list item + if (item.listType === "task") { + const checkedAttr = item.checked ? " checked" : ""; + html += `
  • ${parseInline(item.content)}

    `; + } else { + html += `
  • ${parseInline(item.content)}

    `; + } + + // Render child content (nested items, continuation paragraphs, etc.) + if (item.childContent) { + const childTokens = tokenize(item.childContent); + html += tokensToHtml(childTokens); + } + + html += `
  • `; + i++; + } + + // Close the list + if (currentListType !== null) { + html += ``; + } + + return { html, nextIndex: i }; +} + +function getEffectiveListType( + listType: "bullet" | "ordered" | "task" +): "bullet" | "ordered" { + return listType === "ordered" ? "ordered" : "bullet"; +} + +function emitTable(table: TableToken): string { + let html = ""; + + // Header row + html += ""; + for (let c = 0; c < table.headers.length; c++) { + const align = table.alignments[c]; + const alignAttr = align ? ` align="${align}"` : ""; + html += `${parseInline(table.headers[c])}`; + } + html += ""; + + // Body rows + if (table.rows.length > 0) { + html += ""; + for (const row of table.rows) { + html += ""; + for (let c = 0; c < table.headers.length; c++) { + const cell = c < row.length ? row[c] : ""; + const align = table.alignments[c]; + const alignAttr = align ? ` align="${align}"` : ""; + html += `${parseInline(cell)}`; + } + html += ""; + } + html += ""; + } + + html += "
    "; + return html; +} + +// ─── Public API ────────────────────────────────────────────────────────────── + +/** + * Convert a markdown string to an HTML string. + * This is a direct replacement for the unified/remark/rehype pipeline. + */ +export function markdownToHtml(markdown: string): string { + const tokens = tokenize(markdown); + return tokensToHtml(tokens); +} diff --git a/packages/core/src/api/parsers/markdown/parseMarkdown.ts b/packages/core/src/api/parsers/markdown/parseMarkdown.ts index e98ef00baa..e1741e214e 100644 --- a/packages/core/src/api/parsers/markdown/parseMarkdown.ts +++ b/packages/core/src/api/parsers/markdown/parseMarkdown.ts @@ -1,11 +1,4 @@ import { Schema } from "prosemirror-model"; -import remarkGfm from "remark-gfm"; -import remarkParse from "remark-parse"; -import remarkRehype, { - defaultHandlers as remarkRehypeDefaultHandlers, -} from "remark-rehype"; -import rehypeStringify from "rehype-stringify"; -import { unified } from "unified"; import { Block } from "../../../blocks/defaultBlocks.js"; import { @@ -14,102 +7,10 @@ import { StyleSchema, } from "../../../schema/index.js"; import { HTMLToBlocks } from "../html/parseHTML.js"; -import { isVideoUrl } from "../../../util/string.js"; - -// modified version of https://github.com/syntax-tree/mdast-util-to-hast/blob/main/lib/handlers/code.js -// that outputs a data-language attribute instead of a CSS class (e.g.: language-typescript) -function code(state: any, node: any) { - const value = node.value ? node.value : ""; - /** @type {Properties} */ - const properties: any = {}; - - if (node.lang) { - // changed line - properties["data-language"] = node.lang; - } - - // Create ``. - /** @type {Element} */ - let result: any = { - type: "element", - tagName: "code", - properties, - children: [{ type: "text", value }], - }; - - if (node.meta) { - result.data = { meta: node.meta }; - } - - state.patch(node, result); - result = state.applyData(node, result); - - // Create `
    `.
    -  result = {
    -    type: "element",
    -    tagName: "pre",
    -    properties: {},
    -    children: [result],
    -  };
    -  state.patch(node, result);
    -  return result;
    -}
    -
    -function video(state: any, node: any) {
    -  const url = String(node?.url || "");
    -  const title = node?.title ? String(node.title) : undefined;
    -
    -  let result: any = {
    -    type: "element",
    -    tagName: "video",
    -    properties: {
    -      src: url,
    -      "data-name": title,
    -      "data-url": url,
    -      controls: true,
    -    },
    -    children: [],
    -  };
    -  state.patch?.(node, result);
    -  result = state.applyData ? state.applyData(node, result) : result;
    -
    -  return result;
    -}
    +import { markdownToHtml } from "./markdownToHtml.js";
     
     export function markdownToHTML(markdown: string): string {
    -  const htmlString = unified()
    -    .use(remarkParse)
    -    .use(remarkGfm)
    -    .use(remarkRehype, {
    -      handlers: {
    -        ...(remarkRehypeDefaultHandlers as any),
    -        image: (state: any, node: any) => {
    -          const url = String(node?.url || "");
    -
    -          if (isVideoUrl(url)) {
    -            return video(state, node);
    -          } else {
    -            return remarkRehypeDefaultHandlers.image(state, node);
    -          }
    -        },
    -        code,
    -        blockquote: (state: any, node: any) => {
    -          const result = {
    -            type: "element",
    -            tagName: "blockquote",
    -            properties: {},
    -            // The only difference from the original is that we don't wrap the children with line endings
    -            children: state.wrap(state.all(node), false),
    -          };
    -          state.patch(node, result);
    -          return state.applyData(node, result);
    -        },
    -      },
    -    })
    -    .use(rehypeStringify)
    -    .processSync(markdown);
    -
    -  return htmlString.value as string;
    +  return markdownToHtml(markdown);
     }
     
     export function markdownToBlocks<
    diff --git a/packages/xl-ai/package.json b/packages/xl-ai/package.json
    index 12bb4814aa..9dc34d6552 100644
    --- a/packages/xl-ai/package.json
    +++ b/packages/xl-ai/package.json
    @@ -89,9 +89,6 @@
         "react": "^19.2.3",
         "react-dom": "^19.2.3",
         "react-icons": "^5.5.0",
    -    "remark-parse": "^11.0.0",
    -    "remark-stringify": "^11.0.0",
    -    "unified": "^11.0.5",
         "y-prosemirror": "^1.3.7"
       },
       "devDependencies": {
    diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
    index 66bf5e1962..f21869d1be 100644
    --- a/pnpm-lock.yaml
    +++ b/pnpm-lock.yaml
    @@ -4718,9 +4718,6 @@ importers:
           fast-deep-equal:
             specifier: ^3.1.3
             version: 3.1.3
    -      hast-util-from-dom:
    -        specifier: ^5.0.1
    -        version: 5.0.1
           prosemirror-highlight:
             specifier: ^0.15.1
             version: 0.15.1(@shikijs/types@4.0.2)(@types/hast@3.0.4)(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-transform@1.12.0)(prosemirror-view@1.41.8)
    @@ -4739,36 +4736,6 @@ importers:
           prosemirror-view:
             specifier: ^1.41.4
             version: 1.41.8
    -      rehype-format:
    -        specifier: ^5.0.1
    -        version: 5.0.1
    -      rehype-parse:
    -        specifier: ^9.0.1
    -        version: 9.0.1
    -      rehype-remark:
    -        specifier: ^10.0.1
    -        version: 10.0.1
    -      rehype-stringify:
    -        specifier: ^10.0.1
    -        version: 10.0.1
    -      remark-gfm:
    -        specifier: ^4.0.1
    -        version: 4.0.1
    -      remark-parse:
    -        specifier: ^11.0.0
    -        version: 11.0.0
    -      remark-rehype:
    -        specifier: ^11.1.2
    -        version: 11.1.2
    -      remark-stringify:
    -        specifier: ^11.0.0
    -        version: 11.0.0
    -      unified:
    -        specifier: ^11.0.5
    -        version: 11.0.5
    -      unist-util-visit:
    -        specifier: ^5.0.0
    -        version: 5.1.0
           uuid:
             specifier: ^8.3.2
             version: 8.3.2
    @@ -4785,9 +4752,6 @@ importers:
           '@types/emoji-mart':
             specifier: ^3.0.14
             version: 3.0.14
    -      '@types/hast':
    -        specifier: ^3.0.4
    -        version: 3.0.4
           '@types/uuid':
             specifier: ^8.3.4
             version: 8.3.4
    @@ -5228,15 +5192,6 @@ importers:
           react-icons:
             specifier: ^5.5.0
             version: 5.6.0(react@19.2.4)
    -      remark-parse:
    -        specifier: ^11.0.0
    -        version: 11.0.0
    -      remark-stringify:
    -        specifier: ^11.0.0
    -        version: 11.0.0
    -      unified:
    -        specifier: ^11.0.5
    -        version: 11.0.5
           y-prosemirror:
             specifier: ^1.3.7
             version: 1.3.7(prosemirror-model@1.25.4)(prosemirror-state@1.4.4)(prosemirror-view@1.41.8)(y-protocols@1.0.7(yjs@13.6.30))(yjs@13.6.30)
    @@ -12875,39 +12830,6 @@ packages:
         resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
         engines: {node: '>= 0.4'}
     
    -  hast-util-embedded@3.0.0:
    -    resolution: {integrity: sha512-naH8sld4Pe2ep03qqULEtvYr7EjrLK2QHY8KJR6RJkTUjPGObe1vnx585uzem2hGra+s1q08DZZpfgDVYRbaXA==}
    -
    -  hast-util-format@1.1.0:
    -    resolution: {integrity: sha512-yY1UDz6bC9rDvCWHpx12aIBGRG7krurX0p0Fm6pT547LwDIZZiNr8a+IHDogorAdreULSEzP82Nlv5SZkHZcjA==}
    -
    -  hast-util-from-dom@5.0.1:
    -    resolution: {integrity: sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q==}
    -
    -  hast-util-from-html@2.0.3:
    -    resolution: {integrity: sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw==}
    -
    -  hast-util-from-parse5@8.0.3:
    -    resolution: {integrity: sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==}
    -
    -  hast-util-has-property@3.0.0:
    -    resolution: {integrity: sha512-MNilsvEKLFpV604hwfhVStK0usFY/QmM5zX16bo7EjnAEGofr5YyI37kzopBlZJkHD4t887i+q/C8/tr5Q94cA==}
    -
    -  hast-util-is-body-ok-link@3.0.1:
    -    resolution: {integrity: sha512-0qpnzOBLztXHbHQenVB8uNuxTnm/QBFUOmdOSsEn7GnBtyY07+ENTWVFBAnXd/zEgd9/SUG3lRY7hSIBWRgGpQ==}
    -
    -  hast-util-is-element@3.0.0:
    -    resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==}
    -
    -  hast-util-minify-whitespace@1.0.1:
    -    resolution: {integrity: sha512-L96fPOVpnclQE0xzdWb/D12VT5FabA7SnZOUMtL1DbXmYiHJMXZvFkIZfiMmTCNJHUeO2K9UYNXoVyfz+QHuOw==}
    -
    -  hast-util-parse-selector@4.0.0:
    -    resolution: {integrity: sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==}
    -
    -  hast-util-phrasing@3.0.1:
    -    resolution: {integrity: sha512-6h60VfI3uBQUxHqTyMymMZnEbNl1XmEGtOxxKYL7stY2o601COo62AWAYBQR9lZbYXYSBoxag8UpPRXK+9fqSQ==}
    -
       hast-util-to-estree@3.1.3:
         resolution: {integrity: sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w==}
     
    @@ -12917,21 +12839,12 @@ packages:
       hast-util-to-jsx-runtime@2.3.6:
         resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==}
     
    -  hast-util-to-mdast@10.1.2:
    -    resolution: {integrity: sha512-FiCRI7NmOvM4y+f5w32jPRzcxDIz+PUqDwEqn1A+1q2cdp3B8Gx7aVrXORdOKjMNDQsD1ogOr896+0jJHW1EFQ==}
    -
       hast-util-to-string@3.0.1:
         resolution: {integrity: sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A==}
     
    -  hast-util-to-text@4.0.2:
    -    resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==}
    -
       hast-util-whitespace@3.0.0:
         resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==}
     
    -  hastscript@9.0.1:
    -    resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==}
    -
       headers-polyfill@4.0.3:
         resolution: {integrity: sha512-IScLbePpkvO846sIwOtOTDjutRMWdXdJmXdMvk6gCBHxFO8d+QKOQedyZSxFTTFYRSmlgSTDtXqqq4pcenBXLQ==}
     
    @@ -12965,9 +12878,6 @@ packages:
       html-void-elements@3.0.0:
         resolution: {integrity: sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==}
     
    -  html-whitespace-sensitive-tag-names@3.0.1:
    -    resolution: {integrity: sha512-q+310vW8zmymYHALr1da4HyXUQ0zgiIwIicEfotYPWGN0OJVEN/58IJ3A4GBYcEq3LGAZqKb+ugvP0GNB9CEAA==}
    -
       htmlfy@0.6.7:
         resolution: {integrity: sha512-r8hRd+oIM10lufovN+zr3VKPTYEIvIwqXGucidh2XQufmiw6sbUXFUFjWlfjo3AnefIDTyzykVzQ8IUVuT1peQ==}
     
    @@ -14980,24 +14890,9 @@ packages:
         resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==}
         hasBin: true
     
    -  rehype-format@5.0.1:
    -    resolution: {integrity: sha512-zvmVru9uB0josBVpr946OR8ui7nJEdzZobwLOOqHb/OOD88W0Vk2SqLwoVOj0fM6IPCCO6TaV9CvQvJMWwukFQ==}
    -
    -  rehype-minify-whitespace@6.0.2:
    -    resolution: {integrity: sha512-Zk0pyQ06A3Lyxhe9vGtOtzz3Z0+qZ5+7icZ/PL/2x1SHPbKao5oB/g/rlc6BCTajqBb33JcOe71Ye1oFsuYbnw==}
    -
    -  rehype-parse@9.0.1:
    -    resolution: {integrity: sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag==}
    -
       rehype-recma@1.0.0:
         resolution: {integrity: sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw==}
     
    -  rehype-remark@10.0.1:
    -    resolution: {integrity: sha512-EmDndlb5NVwXGfUa4c9GPK+lXeItTilLhE6ADSaQuHr4JUlKw9MidzGzx4HpqZrNCt6vnHmEifXQiiA+CEnjYQ==}
    -
    -  rehype-stringify@10.0.1:
    -    resolution: {integrity: sha512-k9ecfXHmIPuFVI61B9DeLPN0qFHfawM6RsuX48hoqlaKSF61RskNjSm1lI8PhBEM0MRdLxVVm4WmTqJQccH9mA==}
    -
       remark-gfm@4.0.1:
         resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==}
     
    @@ -15680,9 +15575,6 @@ packages:
       trim-lines@3.0.1:
         resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==}
     
    -  trim-trailing-lines@2.1.0:
    -    resolution: {integrity: sha512-5UR5Biq4VlVOtzqkm2AZlgvSlDJtME46uV0br0gENbwN4l5+mMKT4b9gJKqWtuL2zAIqajGJGuvbCbcAJUZqBg==}
    -
       trough@2.2.0:
         resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==}
     
    @@ -15843,9 +15735,6 @@ packages:
       unified@11.0.5:
         resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==}
     
    -  unist-util-find-after@5.0.0:
    -    resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==}
    -
       unist-util-is@6.0.1:
         resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==}
     
    @@ -15956,9 +15845,6 @@ packages:
         resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
         engines: {node: '>= 0.8'}
     
    -  vfile-location@5.0.3:
    -    resolution: {integrity: sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==}
    -
       vfile-message@4.0.3:
         resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==}
     
    @@ -16126,9 +16012,6 @@ packages:
       wcwidth@1.0.1:
         resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==}
     
    -  web-namespaces@2.0.1:
    -    resolution: {integrity: sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==}
    -
       web-streams-polyfill@4.2.0:
         resolution: {integrity: sha512-0rYDzGOh9EZpig92umN5g5D/9A1Kff7k0/mzPSSCY8jEQeYkgRMoY7LhbXtUCWzLCMX0TUE9aoHkjFNB7D9pfA==}
         engines: {node: '>= 8'}
    @@ -24441,79 +24324,6 @@ snapshots:
         dependencies:
           function-bind: 1.1.2
     
    -  hast-util-embedded@3.0.0:
    -    dependencies:
    -      '@types/hast': 3.0.4
    -      hast-util-is-element: 3.0.0
    -
    -  hast-util-format@1.1.0:
    -    dependencies:
    -      '@types/hast': 3.0.4
    -      hast-util-embedded: 3.0.0
    -      hast-util-minify-whitespace: 1.0.1
    -      hast-util-phrasing: 3.0.1
    -      hast-util-whitespace: 3.0.0
    -      html-whitespace-sensitive-tag-names: 3.0.1
    -      unist-util-visit-parents: 6.0.2
    -
    -  hast-util-from-dom@5.0.1:
    -    dependencies:
    -      '@types/hast': 3.0.4
    -      hastscript: 9.0.1
    -      web-namespaces: 2.0.1
    -
    -  hast-util-from-html@2.0.3:
    -    dependencies:
    -      '@types/hast': 3.0.4
    -      devlop: 1.1.0
    -      hast-util-from-parse5: 8.0.3
    -      parse5: 7.3.0
    -      vfile: 6.0.3
    -      vfile-message: 4.0.3
    -
    -  hast-util-from-parse5@8.0.3:
    -    dependencies:
    -      '@types/hast': 3.0.4
    -      '@types/unist': 3.0.3
    -      devlop: 1.1.0
    -      hastscript: 9.0.1
    -      property-information: 7.1.0
    -      vfile: 6.0.3
    -      vfile-location: 5.0.3
    -      web-namespaces: 2.0.1
    -
    -  hast-util-has-property@3.0.0:
    -    dependencies:
    -      '@types/hast': 3.0.4
    -
    -  hast-util-is-body-ok-link@3.0.1:
    -    dependencies:
    -      '@types/hast': 3.0.4
    -
    -  hast-util-is-element@3.0.0:
    -    dependencies:
    -      '@types/hast': 3.0.4
    -
    -  hast-util-minify-whitespace@1.0.1:
    -    dependencies:
    -      '@types/hast': 3.0.4
    -      hast-util-embedded: 3.0.0
    -      hast-util-is-element: 3.0.0
    -      hast-util-whitespace: 3.0.0
    -      unist-util-is: 6.0.1
    -
    -  hast-util-parse-selector@4.0.0:
    -    dependencies:
    -      '@types/hast': 3.0.4
    -
    -  hast-util-phrasing@3.0.1:
    -    dependencies:
    -      '@types/hast': 3.0.4
    -      hast-util-embedded: 3.0.0
    -      hast-util-has-property: 3.0.0
    -      hast-util-is-body-ok-link: 3.0.1
    -      hast-util-is-element: 3.0.0
    -
       hast-util-to-estree@3.1.3:
         dependencies:
           '@types/estree': 1.0.8
    @@ -24569,46 +24379,14 @@ snapshots:
         transitivePeerDependencies:
           - supports-color
     
    -  hast-util-to-mdast@10.1.2:
    -    dependencies:
    -      '@types/hast': 3.0.4
    -      '@types/mdast': 4.0.4
    -      '@ungap/structured-clone': 1.3.0
    -      hast-util-phrasing: 3.0.1
    -      hast-util-to-html: 9.0.5
    -      hast-util-to-text: 4.0.2
    -      hast-util-whitespace: 3.0.0
    -      mdast-util-phrasing: 4.1.0
    -      mdast-util-to-hast: 13.2.1
    -      mdast-util-to-string: 4.0.0
    -      rehype-minify-whitespace: 6.0.2
    -      trim-trailing-lines: 2.1.0
    -      unist-util-position: 5.0.0
    -      unist-util-visit: 5.1.0
    -
       hast-util-to-string@3.0.1:
         dependencies:
           '@types/hast': 3.0.4
     
    -  hast-util-to-text@4.0.2:
    -    dependencies:
    -      '@types/hast': 3.0.4
    -      '@types/unist': 3.0.3
    -      hast-util-is-element: 3.0.0
    -      unist-util-find-after: 5.0.0
    -
       hast-util-whitespace@3.0.0:
         dependencies:
           '@types/hast': 3.0.4
     
    -  hastscript@9.0.1:
    -    dependencies:
    -      '@types/hast': 3.0.4
    -      comma-separated-tokens: 2.0.3
    -      hast-util-parse-selector: 4.0.0
    -      property-information: 7.1.0
    -      space-separated-tokens: 2.0.2
    -
       headers-polyfill@4.0.3: {}
     
       hermes-estree@0.25.1: {}
    @@ -24643,8 +24421,6 @@ snapshots:
     
       html-void-elements@3.0.0: {}
     
    -  html-whitespace-sensitive-tag-names@3.0.1: {}
    -
       htmlfy@0.6.7: {}
     
       htmlparser2@8.0.2:
    @@ -26951,22 +26727,6 @@ snapshots:
         dependencies:
           jsesc: 3.1.0
     
    -  rehype-format@5.0.1:
    -    dependencies:
    -      '@types/hast': 3.0.4
    -      hast-util-format: 1.1.0
    -
    -  rehype-minify-whitespace@6.0.2:
    -    dependencies:
    -      '@types/hast': 3.0.4
    -      hast-util-minify-whitespace: 1.0.1
    -
    -  rehype-parse@9.0.1:
    -    dependencies:
    -      '@types/hast': 3.0.4
    -      hast-util-from-html: 2.0.3
    -      unified: 11.0.5
    -
       rehype-recma@1.0.0:
         dependencies:
           '@types/estree': 1.0.8
    @@ -26975,20 +26735,6 @@ snapshots:
         transitivePeerDependencies:
           - supports-color
     
    -  rehype-remark@10.0.1:
    -    dependencies:
    -      '@types/hast': 3.0.4
    -      '@types/mdast': 4.0.4
    -      hast-util-to-mdast: 10.1.2
    -      unified: 11.0.5
    -      vfile: 6.0.3
    -
    -  rehype-stringify@10.0.1:
    -    dependencies:
    -      '@types/hast': 3.0.4
    -      hast-util-to-html: 9.0.5
    -      unified: 11.0.5
    -
       remark-gfm@4.0.1:
         dependencies:
           '@types/mdast': 4.0.4
    @@ -27824,8 +27570,6 @@ snapshots:
     
       trim-lines@3.0.1: {}
     
    -  trim-trailing-lines@2.1.0: {}
    -
       trough@2.2.0: {}
     
       ts-api-utils@2.5.0(typescript@5.9.3):
    @@ -28000,11 +27744,6 @@ snapshots:
           trough: 2.2.0
           vfile: 6.0.3
     
    -  unist-util-find-after@5.0.0:
    -    dependencies:
    -      '@types/unist': 3.0.3
    -      unist-util-is: 6.0.1
    -
       unist-util-is@6.0.1:
         dependencies:
           '@types/unist': 3.0.3
    @@ -28129,11 +27868,6 @@ snapshots:
     
       vary@1.1.2: {}
     
    -  vfile-location@5.0.3:
    -    dependencies:
    -      '@types/unist': 3.0.3
    -      vfile: 6.0.3
    -
       vfile-message@4.0.3:
         dependencies:
           '@types/unist': 3.0.3
    @@ -28422,8 +28156,6 @@ snapshots:
         dependencies:
           defaults: 1.0.4
     
    -  web-namespaces@2.0.1: {}
    -
       web-streams-polyfill@4.2.0: {}
     
       webidl-conversions@3.0.1: {}
    diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/audio/basic.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/audio/basic.html
    new file mode 100644
    index 0000000000..bba236e2d5
    --- /dev/null
    +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/audio/basic.html
    @@ -0,0 +1,22 @@
    +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/audio/button.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/audio/button.html new file mode 100644 index 0000000000..2dceaf9f4f --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/audio/button.html @@ -0,0 +1,20 @@ +
    +
    +
    +
    +
    +
    +
    + + + +
    +

    Add audio

    +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/audio/noName.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/audio/noName.html new file mode 100644 index 0000000000..9753d9671a --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/audio/noName.html @@ -0,0 +1,21 @@ +
    +
    +
    +
    +
    + +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/complex/document.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/complex/document.html new file mode 100644 index 0000000000..160d6a8d9c --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/complex/document.html @@ -0,0 +1,87 @@ +
    +
    +
    +
    +

    Document Title

    +
    +
    +
    +
    +
    +
    +

    Introduction paragraph.

    +
    +
    +
    +
    +
    +
    +

    Section 1

    +
    +
    +
    +
    +
    +
    +

    + Text with + bold + and + a link + . +

    +
    +
    +
    +
    +
    +
    +

    First point

    +
    +
    +
    +
    +
    +
    +

    Second point

    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    +
    A notable quote
    +
    +
    +
    +
    +
    +
    +
    + +
    +
    +          const x = 42;
    +        
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h1.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h1.html new file mode 100644 index 0000000000..3e3e513852 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h1.html @@ -0,0 +1,9 @@ +
    +
    +
    +
    +

    Heading 1

    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h2.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h2.html new file mode 100644 index 0000000000..7ffe42afa3 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h2.html @@ -0,0 +1,9 @@ +
    +
    +
    +
    +

    Heading 2

    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h3.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h3.html new file mode 100644 index 0000000000..437867e0d9 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h3.html @@ -0,0 +1,9 @@ +
    +
    +
    +
    +

    Heading 3

    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h4.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h4.html new file mode 100644 index 0000000000..1ef4e627ff --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h4.html @@ -0,0 +1,9 @@ +
    +
    +
    +
    +

    Heading 4

    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h5.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h5.html new file mode 100644 index 0000000000..f44690aa57 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h5.html @@ -0,0 +1,9 @@ +
    +
    +
    +
    +
    Heading 5
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h6.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h6.html new file mode 100644 index 0000000000..5daca5fbdf --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/h6.html @@ -0,0 +1,9 @@ +
    +
    +
    +
    +
    Heading 6
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/styled.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/styled.html new file mode 100644 index 0000000000..31df3416ba --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/styled.html @@ -0,0 +1,12 @@ +
    +
    +
    +
    +

    + Bold + Heading +

    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/toggleable.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/toggleable.html new file mode 100644 index 0000000000..2982ce3673 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/heading/toggleable.html @@ -0,0 +1,38 @@ +
    +
    +
    +
    +
    +
    + +

    Toggle Heading

    +
    +
    +
    +
    +
    +
    +
    +

    Child content

    +
    +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/withCaption.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/withCaption.html new file mode 100644 index 0000000000..bca753588e --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/image/withCaption.html @@ -0,0 +1,31 @@ +
    +
    +
    +
    +
    +
    + Example Image + + +
    +

    This is a caption

    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/link/withCode.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/link/withCode.html new file mode 100644 index 0000000000..cd77ac4bc2 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/link/withCode.html @@ -0,0 +1,18 @@ +
    +
    +
    +
    +

    + See the + docs + for + config +

    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/lists/numberedListStart.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/lists/numberedListStart.html new file mode 100644 index 0000000000..62b0458466 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/lists/numberedListStart.html @@ -0,0 +1,25 @@ +
    +
    +
    +
    +

    Item 5

    +
    +
    +
    +
    +
    +
    +

    Item 6

    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/paragraph/multiple.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/paragraph/multiple.html new file mode 100644 index 0000000000..a5c65cdb68 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/paragraph/multiple.html @@ -0,0 +1,23 @@ +
    +
    +
    +
    +

    First paragraph

    +
    +
    +
    +
    +
    +
    +

    Second paragraph

    +
    +
    +
    +
    +
    +
    +

    Third paragraph

    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/quote/basic.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/quote/basic.html new file mode 100644 index 0000000000..278aafa3ec --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/quote/basic.html @@ -0,0 +1,9 @@ +
    +
    +
    +
    +
    This is a quote
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/quote/multiple.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/quote/multiple.html new file mode 100644 index 0000000000..99c7aa530e --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/quote/multiple.html @@ -0,0 +1,16 @@ +
    +
    +
    +
    +
    First quote
    +
    +
    +
    +
    +
    +
    +
    Second quote
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/quote/nested.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/quote/nested.html new file mode 100644 index 0000000000..da3b3d1215 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/quote/nested.html @@ -0,0 +1,18 @@ +
    +
    +
    +
    +
    Parent quote
    +
    +
    +
    +
    +
    +

    Nested paragraph

    +
    +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/quote/styled.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/quote/styled.html new file mode 100644 index 0000000000..244868c2c6 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/quote/styled.html @@ -0,0 +1,14 @@ +
    +
    +
    +
    +
    + Bold + and + italic + quote +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/quote/withLink.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/quote/withLink.html new file mode 100644 index 0000000000..bfdbb5339e --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/quote/withLink.html @@ -0,0 +1,16 @@ +
    +
    +
    +
    +
    + Quote with + a link +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/backgroundColor.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/backgroundColor.html new file mode 100644 index 0000000000..da1b939f67 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/backgroundColor.html @@ -0,0 +1,11 @@ +
    +
    +
    +
    +

    + Highlighted text +

    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/bold.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/bold.html new file mode 100644 index 0000000000..1834b5c3ca --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/bold.html @@ -0,0 +1,11 @@ +
    +
    +
    +
    +

    + Bold text +

    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/boldItalicStrike.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/boldItalicStrike.html new file mode 100644 index 0000000000..f8baaa4507 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/boldItalicStrike.html @@ -0,0 +1,15 @@ +
    +
    +
    +
    +

    + + + All styles + + +

    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/code.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/code.html new file mode 100644 index 0000000000..31aa7a6dba --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/code.html @@ -0,0 +1,11 @@ +
    +
    +
    +
    +

    + Inline code +

    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/combined.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/combined.html new file mode 100644 index 0000000000..3bcf4491b2 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/combined.html @@ -0,0 +1,13 @@ +
    +
    +
    +
    +

    + + Bold and italic + +

    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/italic.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/italic.html new file mode 100644 index 0000000000..265708f07d --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/italic.html @@ -0,0 +1,11 @@ +
    +
    +
    +
    +

    + Italic text +

    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/mixedInParagraph.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/mixedInParagraph.html new file mode 100644 index 0000000000..5bc3a5a734 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/mixedInParagraph.html @@ -0,0 +1,15 @@ +
    +
    +
    +
    +

    + Normal + bold + italic + code + strike +

    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/strike.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/strike.html new file mode 100644 index 0000000000..294425c21f --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/strike.html @@ -0,0 +1,11 @@ +
    +
    +
    +
    +

    + Strikethrough text +

    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/textColor.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/textColor.html new file mode 100644 index 0000000000..0e6799d766 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/textColor.html @@ -0,0 +1,11 @@ +
    +
    +
    +
    +

    + Colored text +

    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/underline.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/underline.html new file mode 100644 index 0000000000..29ab7e88cf --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/style/underline.html @@ -0,0 +1,11 @@ +
    +
    +
    +
    +

    + Underline text +

    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/advancedExample.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/advancedExample.html new file mode 100644 index 0000000000..1327b27445 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/advancedExample.html @@ -0,0 +1,82 @@ +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + + + + + + + + +
    +

    This row has headers

    +
    +

    + This is + RED +

    +
    +

    Text is Blue

    +
    +

    + This spans 2 columns +
    + and 2 rows +

    +
    +

    Sooo many features

    +
    +

    +
    +

    A cell

    +
    +

    Another Cell

    +
    +

    Aligned center

    +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/cellTextAlignment.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/cellTextAlignment.html new file mode 100644 index 0000000000..8b1b1756b0 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/cellTextAlignment.html @@ -0,0 +1,33 @@ +
    +
    +
    +
    +
    +
    + + + + + + + + + + + +
    +

    Left

    +
    +

    Center

    +
    +

    Right

    +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/emptyCells.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/emptyCells.html new file mode 100644 index 0000000000..b3564081f0 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/emptyCells.html @@ -0,0 +1,37 @@ +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + +
    +

    Has content

    +
    +

    +
    +

    +
    +

    Also has content

    +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/hardBreakInCell.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/hardBreakInCell.html new file mode 100644 index 0000000000..d0c5618ffc --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/hardBreakInCell.html @@ -0,0 +1,33 @@ +
    +
    +
    +
    +
    +
    + + + + + + + + + +
    +

    + Line 1 +
    + Line 2 +

    +
    +

    Normal cell

    +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/headerRowsAndCols.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/headerRowsAndCols.html new file mode 100644 index 0000000000..945741a785 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/headerRowsAndCols.html @@ -0,0 +1,44 @@ +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + + + + +
    +

    Corner

    +
    +

    Column Header 1

    +
    +

    Column Header 2

    +
    +

    Row Header 1

    +
    +

    Data 1

    +
    +

    Data 2

    +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/linksInCells.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/linksInCells.html new file mode 100644 index 0000000000..e15974a432 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/linksInCells.html @@ -0,0 +1,50 @@ +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + +
    +

    + Visit + Example +

    +
    +

    Plain cell

    +
    +

    Data

    +
    +

    + Link +

    +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/singleCell.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/singleCell.html new file mode 100644 index 0000000000..63c4da11a1 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/singleCell.html @@ -0,0 +1,25 @@ +
    +
    +
    +
    +
    +
    + + + + + + + +
    +

    Only cell

    +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/styledCellContent.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/styledCellContent.html new file mode 100644 index 0000000000..9d8035d5c5 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/table/styledCellContent.html @@ -0,0 +1,45 @@ +
    +
    +
    +
    +
    +
    + + + + + + + + + + + + + +
    +

    + Bold +

    +
    +

    + Italic +

    +
    +

    + Strike +

    +
    +

    + Code +

    +
    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/video/withCaption.html b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/video/withCaption.html new file mode 100644 index 0000000000..9de3da2aca --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/blocknoteHTML/video/withCaption.html @@ -0,0 +1,32 @@ +
    +
    +
    +
    +
    +
    + + + +
    +

    Video caption

    +
    +
    +
    +
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/audio/basic.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/audio/basic.html new file mode 100644 index 0000000000..d2a69001ba --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/audio/basic.html @@ -0,0 +1,5 @@ + \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/audio/button.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/audio/button.html new file mode 100644 index 0000000000..51c429c20a --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/audio/button.html @@ -0,0 +1 @@ +

    Add audio

    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/audio/noName.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/audio/noName.html new file mode 100644 index 0000000000..1699b58c73 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/audio/noName.html @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/complex/document.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/complex/document.html new file mode 100644 index 0000000000..bcc9d52e69 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/complex/document.html @@ -0,0 +1,27 @@ +

    Document Title

    +

    Introduction paragraph.

    +

    Section 1

    +

    + Text with + bold + and + a link + . +

    +
      +
    • +

      First point

      +
    • +
    • +

      Second point

      +
    • +
    +
    +
    A notable quote
    +
    +  const x = 42;
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h1.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h1.html new file mode 100644 index 0000000000..ac06cdc123 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h1.html @@ -0,0 +1 @@ +

    Heading 1

    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h2.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h2.html new file mode 100644 index 0000000000..92e9734754 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h2.html @@ -0,0 +1 @@ +

    Heading 2

    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h3.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h3.html new file mode 100644 index 0000000000..df25998db1 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h3.html @@ -0,0 +1 @@ +

    Heading 3

    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h4.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h4.html new file mode 100644 index 0000000000..430144bc54 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h4.html @@ -0,0 +1 @@ +

    Heading 4

    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h5.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h5.html new file mode 100644 index 0000000000..02e7e8fda2 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h5.html @@ -0,0 +1 @@ +
    Heading 5
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h6.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h6.html new file mode 100644 index 0000000000..6e76905810 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/h6.html @@ -0,0 +1 @@ +
    Heading 6
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/styled.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/styled.html new file mode 100644 index 0000000000..7f14fdb711 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/styled.html @@ -0,0 +1,4 @@ +

    + Bold + Heading +

    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/toggleable.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/toggleable.html new file mode 100644 index 0000000000..ecec05b566 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/heading/toggleable.html @@ -0,0 +1,6 @@ +
    + +

    Toggle Heading

    +
    +

    Child content

    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/withCaption.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/withCaption.html new file mode 100644 index 0000000000..3ecba73103 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/image/withCaption.html @@ -0,0 +1,8 @@ +
    + Example Image +
    This is a caption
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/link/withCode.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/link/withCode.html new file mode 100644 index 0000000000..bbfc4c8578 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/link/withCode.html @@ -0,0 +1,10 @@ +

    + See the + docs + for + config +

    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/lists/numberedListStart.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/lists/numberedListStart.html new file mode 100644 index 0000000000..35535f7db4 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/lists/numberedListStart.html @@ -0,0 +1,8 @@ +
      +
    1. +

      Item 5

      +
    2. +
    3. +

      Item 6

      +
    4. +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/paragraph/multiple.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/paragraph/multiple.html new file mode 100644 index 0000000000..a183a01cd8 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/paragraph/multiple.html @@ -0,0 +1,3 @@ +

    First paragraph

    +

    Second paragraph

    +

    Third paragraph

    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/quote/basic.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/quote/basic.html new file mode 100644 index 0000000000..53c51228f1 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/quote/basic.html @@ -0,0 +1 @@ +
    This is a quote
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/quote/multiple.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/quote/multiple.html new file mode 100644 index 0000000000..80b8a40ae5 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/quote/multiple.html @@ -0,0 +1,2 @@ +
    First quote
    +
    Second quote
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/quote/nested.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/quote/nested.html new file mode 100644 index 0000000000..3e74d08d92 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/quote/nested.html @@ -0,0 +1,2 @@ +
    Parent quote
    +

    Nested paragraph

    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/quote/styled.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/quote/styled.html new file mode 100644 index 0000000000..7f80b7fc7c --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/quote/styled.html @@ -0,0 +1,6 @@ +
    + Bold + and + italic + quote +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/quote/withLink.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/quote/withLink.html new file mode 100644 index 0000000000..41559f4506 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/quote/withLink.html @@ -0,0 +1,8 @@ +
    + Quote with + a link +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/backgroundColor.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/backgroundColor.html new file mode 100644 index 0000000000..66f327e85d --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/backgroundColor.html @@ -0,0 +1,8 @@ +

    + Highlighted text +

    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/bold.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/bold.html new file mode 100644 index 0000000000..e57a879f6e --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/bold.html @@ -0,0 +1,3 @@ +

    + Bold text +

    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/boldItalicStrike.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/boldItalicStrike.html new file mode 100644 index 0000000000..d7506fe610 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/boldItalicStrike.html @@ -0,0 +1,7 @@ +

    + + + All styles + + +

    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/code.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/code.html new file mode 100644 index 0000000000..6fe865b744 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/code.html @@ -0,0 +1,3 @@ +

    + Inline code +

    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/combined.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/combined.html new file mode 100644 index 0000000000..920576e90a --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/combined.html @@ -0,0 +1,5 @@ +

    + + Bold and italic + +

    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/italic.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/italic.html new file mode 100644 index 0000000000..fcff5726e6 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/italic.html @@ -0,0 +1,3 @@ +

    + Italic text +

    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/mixedInParagraph.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/mixedInParagraph.html new file mode 100644 index 0000000000..8be1d165ce --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/mixedInParagraph.html @@ -0,0 +1,7 @@ +

    + Normal + bold + italic + code + strike +

    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/strike.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/strike.html new file mode 100644 index 0000000000..abfabbe4e6 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/strike.html @@ -0,0 +1,3 @@ +

    + Strikethrough text +

    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/textColor.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/textColor.html new file mode 100644 index 0000000000..798166dcad --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/textColor.html @@ -0,0 +1,8 @@ +

    + Colored text +

    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/underline.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/underline.html new file mode 100644 index 0000000000..f861031c9e --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/style/underline.html @@ -0,0 +1,3 @@ +

    + Underline text +

    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/advancedExample.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/advancedExample.html new file mode 100644 index 0000000000..a4f5dfcb31 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/advancedExample.html @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + +
    +

    This row has headers

    +
    +

    + This is + RED +

    +
    +

    Text is Blue

    +
    +

    + This spans 2 columns +
    + and 2 rows +

    +
    +

    Sooo many features

    +
    +

    +
    +

    A cell

    +
    +

    Another Cell

    +
    +

    Aligned center

    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/cellTextAlignment.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/cellTextAlignment.html new file mode 100644 index 0000000000..21f5f0ab0c --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/cellTextAlignment.html @@ -0,0 +1,18 @@ + + + + + + + + + + + +
    +

    Left

    +
    +

    Center

    +
    +

    Right

    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/emptyCells.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/emptyCells.html new file mode 100644 index 0000000000..10a9fb5259 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/emptyCells.html @@ -0,0 +1,22 @@ + + + + + + + + + + + + + +
    +

    Has content

    +
    +

    +
    +

    +
    +

    Also has content

    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/hardBreakInCell.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/hardBreakInCell.html new file mode 100644 index 0000000000..a313a5323d --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/hardBreakInCell.html @@ -0,0 +1,18 @@ + + + + + + + + + +
    +

    + Line 1 +
    + Line 2 +

    +
    +

    Normal cell

    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/headerRowsAndCols.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/headerRowsAndCols.html new file mode 100644 index 0000000000..64ee4183aa --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/headerRowsAndCols.html @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + +
    +

    Corner

    +
    +

    Column Header 1

    +
    +

    Column Header 2

    +
    +

    Row Header 1

    +
    +

    Data 1

    +
    +

    Data 2

    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/linksInCells.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/linksInCells.html new file mode 100644 index 0000000000..26bc68b632 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/linksInCells.html @@ -0,0 +1,35 @@ + + + + + + + + + + + + + +
    +

    + Visit + Example +

    +
    +

    Plain cell

    +
    +

    Data

    +
    +

    + Link +

    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/singleCell.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/singleCell.html new file mode 100644 index 0000000000..ce8bea5831 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/singleCell.html @@ -0,0 +1,10 @@ + + + + + + + +
    +

    Only cell

    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/styledCellContent.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/styledCellContent.html new file mode 100644 index 0000000000..91db4da0e4 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/table/styledCellContent.html @@ -0,0 +1,30 @@ + + + + + + + + + + + + + +
    +

    + Bold +

    +
    +

    + Italic +

    +
    +

    + Strike +

    +
    +

    + Code +

    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/html/video/withCaption.html b/tests/src/unit/core/formatConversion/export/__snapshots__/html/video/withCaption.html new file mode 100644 index 0000000000..978dcc0448 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/html/video/withCaption.html @@ -0,0 +1,8 @@ +
    + +
    Video caption
    +
    \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/audio/basic.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/audio/basic.md new file mode 100644 index 0000000000..b459dbd5a9 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/audio/basic.md @@ -0,0 +1 @@ +[](https://example.com/audio.mp3) diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/audio/button.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/audio/button.md new file mode 100644 index 0000000000..7b4aa9de1b --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/audio/button.md @@ -0,0 +1 @@ +Add audio diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/audio/noName.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/audio/noName.md new file mode 100644 index 0000000000..b459dbd5a9 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/audio/noName.md @@ -0,0 +1 @@ +[](https://example.com/audio.mp3) diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/complex/document.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/complex/document.md new file mode 100644 index 0000000000..6978c659f9 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/complex/document.md @@ -0,0 +1,19 @@ +# Document Title + +Introduction paragraph. + +## Section 1 + +Text with **bold** and [a link](https://example.com). + +* First point + +* Second point + +*** + +> A notable quote + +```javascript +const x = 42; +``` diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/complex/misc.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/complex/misc.md index 4a2de0a7fc..fca446bec3 100644 --- a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/complex/misc.md +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/complex/misc.md @@ -1,4 +1,4 @@ -## **Heading ***~~2~~* +## **Heading** *~~2~~* Paragraph diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h1.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h1.md new file mode 100644 index 0000000000..bd706e91c4 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h1.md @@ -0,0 +1 @@ +# Heading 1 diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h2.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h2.md new file mode 100644 index 0000000000..cd760a44ba --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h2.md @@ -0,0 +1 @@ +## Heading 2 diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h3.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h3.md new file mode 100644 index 0000000000..607fcc43b6 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h3.md @@ -0,0 +1 @@ +### Heading 3 diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h4.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h4.md new file mode 100644 index 0000000000..9c7bd7c52e --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h4.md @@ -0,0 +1 @@ +#### Heading 4 diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h5.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h5.md new file mode 100644 index 0000000000..2410fdf2b0 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h5.md @@ -0,0 +1 @@ +##### Heading 5 diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h6.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h6.md new file mode 100644 index 0000000000..848d83e6dd --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/h6.md @@ -0,0 +1 @@ +###### Heading 6 diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/styled.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/styled.md new file mode 100644 index 0000000000..90c78848fd --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/styled.md @@ -0,0 +1 @@ +# **Bold** Heading diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/toggleable.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/toggleable.md new file mode 100644 index 0000000000..cc8cbf3aa9 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/heading/toggleable.md @@ -0,0 +1,3 @@ +## Toggle Heading + +Child content diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/image/withCaption.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/image/withCaption.md new file mode 100644 index 0000000000..d68bd090a4 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/image/withCaption.md @@ -0,0 +1,3 @@ +![Example Image](https://example.com/image.png) + +This is a caption diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/link/withCode.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/link/withCode.md new file mode 100644 index 0000000000..090ae185e1 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/link/withCode.md @@ -0,0 +1 @@ +See the [docs](https://example.com) for `config` diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/lists/numberedListStart.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/lists/numberedListStart.md new file mode 100644 index 0000000000..b924b4b407 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/lists/numberedListStart.md @@ -0,0 +1,3 @@ +5. Item 5 + +6. Item 6 diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/paragraph/multiple.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/paragraph/multiple.md new file mode 100644 index 0000000000..8fadfa1d86 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/paragraph/multiple.md @@ -0,0 +1,5 @@ +First paragraph + +Second paragraph + +Third paragraph diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/quote/basic.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/quote/basic.md new file mode 100644 index 0000000000..83d6a8096d --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/quote/basic.md @@ -0,0 +1 @@ +> This is a quote diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/quote/multiple.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/quote/multiple.md new file mode 100644 index 0000000000..c2610d0ba7 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/quote/multiple.md @@ -0,0 +1,3 @@ +> First quote + +> Second quote diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/quote/nested.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/quote/nested.md new file mode 100644 index 0000000000..41c50517f2 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/quote/nested.md @@ -0,0 +1,3 @@ +> Parent quote + +Nested paragraph diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/quote/styled.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/quote/styled.md new file mode 100644 index 0000000000..71e0af0173 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/quote/styled.md @@ -0,0 +1 @@ +> **Bold** and *italic* quote diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/quote/withLink.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/quote/withLink.md new file mode 100644 index 0000000000..8510d4defd --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/quote/withLink.md @@ -0,0 +1 @@ +> Quote with [a link](https://www.example.com) diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/backgroundColor.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/backgroundColor.md new file mode 100644 index 0000000000..3ba8964656 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/backgroundColor.md @@ -0,0 +1 @@ +Highlighted text diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/bold.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/bold.md new file mode 100644 index 0000000000..df2474d633 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/bold.md @@ -0,0 +1 @@ +**Bold text** diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/boldItalicStrike.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/boldItalicStrike.md new file mode 100644 index 0000000000..1af450cf5e --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/boldItalicStrike.md @@ -0,0 +1 @@ +***~~All styles~~*** diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/code.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/code.md new file mode 100644 index 0000000000..aa4775ec76 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/code.md @@ -0,0 +1 @@ +`Inline code` diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/combined.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/combined.md new file mode 100644 index 0000000000..b011bd3c15 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/combined.md @@ -0,0 +1 @@ +***Bold and italic*** diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/italic.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/italic.md new file mode 100644 index 0000000000..c6c83dc114 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/italic.md @@ -0,0 +1 @@ +*Italic text* diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/mixedInParagraph.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/mixedInParagraph.md new file mode 100644 index 0000000000..76bd55f326 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/mixedInParagraph.md @@ -0,0 +1 @@ +Normal **bold** *italic* `code `~~strike~~ diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/strike.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/strike.md new file mode 100644 index 0000000000..afe555a038 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/strike.md @@ -0,0 +1 @@ +~~Strikethrough text~~ diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/textColor.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/textColor.md new file mode 100644 index 0000000000..28f332d788 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/textColor.md @@ -0,0 +1 @@ +Colored text diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/underline.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/underline.md new file mode 100644 index 0000000000..2ccc77398c --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/style/underline.md @@ -0,0 +1 @@ +Underline text diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/advancedExample.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/advancedExample.md new file mode 100644 index 0000000000..53599a4feb --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/advancedExample.md @@ -0,0 +1,6 @@ +| This row has headers | This is **RED** | Text is Blue | +| -------------------------------- | --------------- | ------------------ | +| This spans 2 columns\ +and 2 rows | | Sooo many features | +| | | | +| A cell | Another Cell | Aligned center | diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/cellTextAlignment.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/cellTextAlignment.md new file mode 100644 index 0000000000..d3d4211640 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/cellTextAlignment.md @@ -0,0 +1,3 @@ +| | | | +| ---------- | ---------- | ---------- | +| Left | Center | Right | diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/emptyCells.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/emptyCells.md new file mode 100644 index 0000000000..ba81ea2ca9 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/emptyCells.md @@ -0,0 +1,4 @@ +| | | +| ----------- | ---------------- | +| Has content | | +| | Also has content | diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/hardBreakInCell.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/hardBreakInCell.md new file mode 100644 index 0000000000..d9ffaf65a3 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/hardBreakInCell.md @@ -0,0 +1,4 @@ +| | | +| -------------- | ----------- | +| Line 1\ +Line 2 | Normal cell | diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/headerRowsAndCols.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/headerRowsAndCols.md new file mode 100644 index 0000000000..1c29ad9f94 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/headerRowsAndCols.md @@ -0,0 +1,3 @@ +| Corner | Column Header 1 | Column Header 2 | +| ------------ | --------------- | --------------- | +| Row Header 1 | Data 1 | Data 2 | diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/linksInCells.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/linksInCells.md new file mode 100644 index 0000000000..7815cfe199 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/linksInCells.md @@ -0,0 +1,4 @@ +| | | +| ------------------------------------ | ---------------------------- | +| Visit [Example](https://example.com) | Plain cell | +| Data | [Link](https://example2.com) | diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/singleCell.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/singleCell.md new file mode 100644 index 0000000000..3be705c3e8 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/singleCell.md @@ -0,0 +1,3 @@ +| | +| ---------- | +| Only cell | diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/styledCellContent.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/styledCellContent.md new file mode 100644 index 0000000000..ff5eff81a2 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/table/styledCellContent.md @@ -0,0 +1,4 @@ +| | | +| ---------- | ---------- | +| **Bold** | *Italic* | +| ~~Strike~~ | `Code` | diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/video/withCaption.md b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/video/withCaption.md new file mode 100644 index 0000000000..3f50812477 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/markdown/video/withCaption.md @@ -0,0 +1,3 @@ +![](https://example.com/video.mp4) + +Video caption diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/audio/basic.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/audio/basic.json new file mode 100644 index 0000000000..32e28f6803 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/audio/basic.json @@ -0,0 +1,20 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "caption": "", + "name": "example", + "showPreview": true, + "url": "https://example.com/audio.mp3", + }, + "type": "audio", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/audio/button.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/audio/button.json new file mode 100644 index 0000000000..2149eef7c8 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/audio/button.json @@ -0,0 +1,20 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "caption": "", + "name": "", + "showPreview": true, + "url": "", + }, + "type": "audio", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/audio/noName.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/audio/noName.json new file mode 100644 index 0000000000..a9b3396ea4 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/audio/noName.json @@ -0,0 +1,20 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "caption": "", + "name": "", + "showPreview": true, + "url": "https://example.com/audio.mp3", + }, + "type": "audio", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/complex/document.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/complex/document.json new file mode 100644 index 0000000000..e46f405d12 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/complex/document.json @@ -0,0 +1,222 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "isToggleable": false, + "level": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "Document Title", + "type": "text", + }, + ], + "type": "heading", + }, + ], + "type": "blockContainer", + }, + { + "attrs": { + "id": "2", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "Introduction paragraph.", + "type": "text", + }, + ], + "type": "paragraph", + }, + ], + "type": "blockContainer", + }, + { + "attrs": { + "id": "3", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "isToggleable": false, + "level": 2, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "Section 1", + "type": "text", + }, + ], + "type": "heading", + }, + ], + "type": "blockContainer", + }, + { + "attrs": { + "id": "4", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "Text with ", + "type": "text", + }, + { + "marks": [ + { + "type": "bold", + }, + ], + "text": "bold", + "type": "text", + }, + { + "text": " and ", + "type": "text", + }, + { + "marks": [ + { + "attrs": { + "class": null, + "href": "https://example.com", + "rel": "noopener noreferrer nofollow", + "target": "_blank", + }, + "type": "link", + }, + ], + "text": "a link", + "type": "text", + }, + { + "text": ".", + "type": "text", + }, + ], + "type": "paragraph", + }, + ], + "type": "blockContainer", + }, + { + "attrs": { + "id": "5", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "First point", + "type": "text", + }, + ], + "type": "bulletListItem", + }, + ], + "type": "blockContainer", + }, + { + "attrs": { + "id": "6", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "Second point", + "type": "text", + }, + ], + "type": "bulletListItem", + }, + ], + "type": "blockContainer", + }, + { + "attrs": { + "id": "7", + }, + "content": [ + { + "type": "divider", + }, + ], + "type": "blockContainer", + }, + { + "attrs": { + "id": "8", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textColor": "default", + }, + "content": [ + { + "text": "A notable quote", + "type": "text", + }, + ], + "type": "quote", + }, + ], + "type": "blockContainer", + }, + { + "attrs": { + "id": "9", + }, + "content": [ + { + "attrs": { + "language": "javascript", + }, + "content": [ + { + "text": "const x = 42;", + "type": "text", + }, + ], + "type": "codeBlock", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h1.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h1.json new file mode 100644 index 0000000000..d147b23ade --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h1.json @@ -0,0 +1,26 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "isToggleable": false, + "level": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "Heading 1", + "type": "text", + }, + ], + "type": "heading", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h2.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h2.json new file mode 100644 index 0000000000..f9f92e7081 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h2.json @@ -0,0 +1,26 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "isToggleable": false, + "level": 2, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "Heading 2", + "type": "text", + }, + ], + "type": "heading", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h3.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h3.json new file mode 100644 index 0000000000..6399a58563 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h3.json @@ -0,0 +1,26 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "isToggleable": false, + "level": 3, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "Heading 3", + "type": "text", + }, + ], + "type": "heading", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h4.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h4.json new file mode 100644 index 0000000000..c23a0c4809 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h4.json @@ -0,0 +1,26 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "isToggleable": false, + "level": 4, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "Heading 4", + "type": "text", + }, + ], + "type": "heading", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h5.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h5.json new file mode 100644 index 0000000000..0867b7796f --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h5.json @@ -0,0 +1,26 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "isToggleable": false, + "level": 5, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "Heading 5", + "type": "text", + }, + ], + "type": "heading", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h6.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h6.json new file mode 100644 index 0000000000..b5eddefddc --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/h6.json @@ -0,0 +1,26 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "isToggleable": false, + "level": 6, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "Heading 6", + "type": "text", + }, + ], + "type": "heading", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/styled.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/styled.json new file mode 100644 index 0000000000..ff107bc15f --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/styled.json @@ -0,0 +1,35 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "isToggleable": false, + "level": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "marks": [ + { + "type": "bold", + }, + ], + "text": "Bold ", + "type": "text", + }, + { + "text": "Heading", + "type": "text", + }, + ], + "type": "heading", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/toggleable.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/toggleable.json new file mode 100644 index 0000000000..7bb4c57f86 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/heading/toggleable.json @@ -0,0 +1,53 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "isToggleable": true, + "level": 2, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "Toggle Heading", + "type": "text", + }, + ], + "type": "heading", + }, + { + "content": [ + { + "attrs": { + "id": "2", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "Child content", + "type": "text", + }, + ], + "type": "paragraph", + }, + ], + "type": "blockContainer", + }, + ], + "type": "blockGroup", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/image/withCaption.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/image/withCaption.json new file mode 100644 index 0000000000..5016f16a10 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/image/withCaption.json @@ -0,0 +1,22 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "caption": "This is a caption", + "name": "Example Image", + "previewWidth": undefined, + "showPreview": true, + "textAlignment": "left", + "url": "https://example.com/image.png", + }, + "type": "image", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/link/withCode.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/link/withCode.json new file mode 100644 index 0000000000..d4947afd93 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/link/withCode.json @@ -0,0 +1,52 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "See the ", + "type": "text", + }, + { + "marks": [ + { + "attrs": { + "class": null, + "href": "https://example.com", + "rel": "noopener noreferrer nofollow", + "target": "_blank", + }, + "type": "link", + }, + ], + "text": "docs", + "type": "text", + }, + { + "text": " for ", + "type": "text", + }, + { + "marks": [ + { + "type": "code", + }, + ], + "text": "config", + "type": "text", + }, + ], + "type": "paragraph", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/lists/numberedListStart.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/lists/numberedListStart.json new file mode 100644 index 0000000000..387b4fa073 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/lists/numberedListStart.json @@ -0,0 +1,48 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "start": 5, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "Item 5", + "type": "text", + }, + ], + "type": "numberedListItem", + }, + ], + "type": "blockContainer", + }, + { + "attrs": { + "id": "2", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "start": undefined, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "Item 6", + "type": "text", + }, + ], + "type": "numberedListItem", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/paragraph/multiple.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/paragraph/multiple.json new file mode 100644 index 0000000000..affba0772c --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/paragraph/multiple.json @@ -0,0 +1,68 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "First paragraph", + "type": "text", + }, + ], + "type": "paragraph", + }, + ], + "type": "blockContainer", + }, + { + "attrs": { + "id": "2", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "Second paragraph", + "type": "text", + }, + ], + "type": "paragraph", + }, + ], + "type": "blockContainer", + }, + { + "attrs": { + "id": "3", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "Third paragraph", + "type": "text", + }, + ], + "type": "paragraph", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/quote/basic.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/quote/basic.json new file mode 100644 index 0000000000..9234a8f05f --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/quote/basic.json @@ -0,0 +1,23 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textColor": "default", + }, + "content": [ + { + "text": "This is a quote", + "type": "text", + }, + ], + "type": "quote", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/quote/multiple.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/quote/multiple.json new file mode 100644 index 0000000000..458e24879a --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/quote/multiple.json @@ -0,0 +1,44 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textColor": "default", + }, + "content": [ + { + "text": "First quote", + "type": "text", + }, + ], + "type": "quote", + }, + ], + "type": "blockContainer", + }, + { + "attrs": { + "id": "2", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textColor": "default", + }, + "content": [ + { + "text": "Second quote", + "type": "text", + }, + ], + "type": "quote", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/quote/nested.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/quote/nested.json new file mode 100644 index 0000000000..b3c46ed220 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/quote/nested.json @@ -0,0 +1,50 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textColor": "default", + }, + "content": [ + { + "text": "Parent quote", + "type": "text", + }, + ], + "type": "quote", + }, + { + "content": [ + { + "attrs": { + "id": "2", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "Nested paragraph", + "type": "text", + }, + ], + "type": "paragraph", + }, + ], + "type": "blockContainer", + }, + ], + "type": "blockGroup", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/quote/styled.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/quote/styled.json new file mode 100644 index 0000000000..a482c2ad58 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/quote/styled.json @@ -0,0 +1,45 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textColor": "default", + }, + "content": [ + { + "marks": [ + { + "type": "bold", + }, + ], + "text": "Bold ", + "type": "text", + }, + { + "text": "and ", + "type": "text", + }, + { + "marks": [ + { + "type": "italic", + }, + ], + "text": "italic", + "type": "text", + }, + { + "text": " quote", + "type": "text", + }, + ], + "type": "quote", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/quote/withLink.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/quote/withLink.json new file mode 100644 index 0000000000..73eff04e34 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/quote/withLink.json @@ -0,0 +1,38 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textColor": "default", + }, + "content": [ + { + "text": "Quote with ", + "type": "text", + }, + { + "marks": [ + { + "attrs": { + "class": null, + "href": "https://www.example.com", + "rel": "noopener noreferrer nofollow", + "target": "_blank", + }, + "type": "link", + }, + ], + "text": "a link", + "type": "text", + }, + ], + "type": "quote", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/backgroundColor.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/backgroundColor.json new file mode 100644 index 0000000000..e07954b219 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/backgroundColor.json @@ -0,0 +1,32 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "marks": [ + { + "attrs": { + "stringValue": "blue", + }, + "type": "backgroundColor", + }, + ], + "text": "Highlighted text", + "type": "text", + }, + ], + "type": "paragraph", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/bold.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/bold.json new file mode 100644 index 0000000000..b6d5b7a208 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/bold.json @@ -0,0 +1,29 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "marks": [ + { + "type": "bold", + }, + ], + "text": "Bold text", + "type": "text", + }, + ], + "type": "paragraph", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/boldItalicStrike.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/boldItalicStrike.json new file mode 100644 index 0000000000..7c5f05d763 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/boldItalicStrike.json @@ -0,0 +1,35 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "marks": [ + { + "type": "bold", + }, + { + "type": "italic", + }, + { + "type": "strike", + }, + ], + "text": "All styles", + "type": "text", + }, + ], + "type": "paragraph", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/code.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/code.json new file mode 100644 index 0000000000..2cf0463e03 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/code.json @@ -0,0 +1,29 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "marks": [ + { + "type": "code", + }, + ], + "text": "Inline code", + "type": "text", + }, + ], + "type": "paragraph", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/combined.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/combined.json new file mode 100644 index 0000000000..5b91fb8abb --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/combined.json @@ -0,0 +1,32 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "marks": [ + { + "type": "bold", + }, + { + "type": "italic", + }, + ], + "text": "Bold and italic", + "type": "text", + }, + ], + "type": "paragraph", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/italic.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/italic.json new file mode 100644 index 0000000000..7d22809e81 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/italic.json @@ -0,0 +1,29 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "marks": [ + { + "type": "italic", + }, + ], + "text": "Italic text", + "type": "text", + }, + ], + "type": "paragraph", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/mixedInParagraph.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/mixedInParagraph.json new file mode 100644 index 0000000000..d72aa1d3bb --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/mixedInParagraph.json @@ -0,0 +1,60 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "text": "Normal ", + "type": "text", + }, + { + "marks": [ + { + "type": "bold", + }, + ], + "text": "bold ", + "type": "text", + }, + { + "marks": [ + { + "type": "italic", + }, + ], + "text": "italic ", + "type": "text", + }, + { + "marks": [ + { + "type": "code", + }, + ], + "text": "code ", + "type": "text", + }, + { + "marks": [ + { + "type": "strike", + }, + ], + "text": "strike", + "type": "text", + }, + ], + "type": "paragraph", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/strike.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/strike.json new file mode 100644 index 0000000000..756569ade9 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/strike.json @@ -0,0 +1,29 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "marks": [ + { + "type": "strike", + }, + ], + "text": "Strikethrough text", + "type": "text", + }, + ], + "type": "paragraph", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/textColor.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/textColor.json new file mode 100644 index 0000000000..7bca812fd7 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/textColor.json @@ -0,0 +1,32 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "marks": [ + { + "attrs": { + "stringValue": "red", + }, + "type": "textColor", + }, + ], + "text": "Colored text", + "type": "text", + }, + ], + "type": "paragraph", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/underline.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/underline.json new file mode 100644 index 0000000000..348c2ae742 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/style/underline.json @@ -0,0 +1,29 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "marks": [ + { + "type": "underline", + }, + ], + "text": "Underline text", + "type": "text", + }, + ], + "type": "paragraph", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/advancedExample.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/advancedExample.json new file mode 100644 index 0000000000..1fdde23a6b --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/advancedExample.json @@ -0,0 +1,265 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": [ + 199, + ], + "rowspan": 1, + "textAlignment": "center", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "This row has headers", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableHeader", + }, + { + "attrs": { + "backgroundColor": "red", + "colspan": 1, + "colwidth": [ + 148, + ], + "rowspan": 1, + "textAlignment": "center", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "This is ", + "type": "text", + }, + { + "marks": [ + { + "type": "bold", + }, + ], + "text": "RED", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableHeader", + }, + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": [ + 201, + ], + "rowspan": 1, + "textAlignment": "center", + "textColor": "blue", + }, + "content": [ + { + "content": [ + { + "text": "Text is Blue", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableHeader", + }, + ], + "type": "tableRow", + }, + { + "content": [ + { + "attrs": { + "backgroundColor": "yellow", + "colspan": 2, + "colwidth": [ + 199, + 148, + ], + "rowspan": 2, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "This spans 2 columns", + "type": "text", + }, + { + "type": "hardBreak", + }, + { + "text": "and 2 rows", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + { + "attrs": { + "backgroundColor": "gray", + "colspan": 1, + "colwidth": [ + 201, + ], + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "Sooo many features", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + ], + "type": "tableRow", + }, + { + "content": [ + { + "attrs": { + "backgroundColor": "gray", + "colspan": 1, + "colwidth": [ + 201, + ], + "rowspan": 1, + "textAlignment": "left", + "textColor": "purple", + }, + "content": [ + { + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + ], + "type": "tableRow", + }, + { + "content": [ + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": [ + 199, + ], + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "A cell", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": [ + 148, + ], + "rowspan": 1, + "textAlignment": "right", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "Another Cell", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": [ + 201, + ], + "rowspan": 1, + "textAlignment": "center", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "Aligned center", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + ], + "type": "tableRow", + }, + ], + "type": "table", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/cellTextAlignment.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/cellTextAlignment.json new file mode 100644 index 0000000000..c7ded0cfc6 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/cellTextAlignment.json @@ -0,0 +1,89 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "Left", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "center", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "Center", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "right", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "Right", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + ], + "type": "tableRow", + }, + ], + "type": "table", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/emptyCells.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/emptyCells.json new file mode 100644 index 0000000000..05bb8aa0af --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/emptyCells.json @@ -0,0 +1,104 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "Has content", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + ], + "type": "tableRow", + }, + { + "content": [ + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "Also has content", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + ], + "type": "tableRow", + }, + ], + "type": "table", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/hardBreakInCell.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/hardBreakInCell.json new file mode 100644 index 0000000000..121834448d --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/hardBreakInCell.json @@ -0,0 +1,74 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "Line 1", + "type": "text", + }, + { + "type": "hardBreak", + }, + { + "text": "Line 2", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "Normal cell", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + ], + "type": "tableRow", + }, + ], + "type": "table", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/headerRowsAndCols.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/headerRowsAndCols.json new file mode 100644 index 0000000000..606ec05c49 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/headerRowsAndCols.json @@ -0,0 +1,160 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "Corner", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableHeader", + }, + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "Column Header 1", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableHeader", + }, + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "Column Header 2", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableHeader", + }, + ], + "type": "tableRow", + }, + { + "content": [ + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "Row Header 1", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableHeader", + }, + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "Data 1", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "Data 2", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + ], + "type": "tableRow", + }, + ], + "type": "table", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/linksInCells.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/linksInCells.json new file mode 100644 index 0000000000..109bc712a8 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/linksInCells.json @@ -0,0 +1,142 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "Visit ", + "type": "text", + }, + { + "marks": [ + { + "attrs": { + "class": null, + "href": "https://example.com", + "rel": "noopener noreferrer nofollow", + "target": "_blank", + }, + "type": "link", + }, + ], + "text": "Example", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "Plain cell", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + ], + "type": "tableRow", + }, + { + "content": [ + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "Data", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "marks": [ + { + "attrs": { + "class": null, + "href": "https://example2.com", + "rel": "noopener noreferrer nofollow", + "target": "_blank", + }, + "type": "link", + }, + ], + "text": "Link", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + ], + "type": "tableRow", + }, + ], + "type": "table", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/singleCell.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/singleCell.json new file mode 100644 index 0000000000..dd4628d177 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/singleCell.json @@ -0,0 +1,45 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "text": "Only cell", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + ], + "type": "tableRow", + }, + ], + "type": "table", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/styledCellContent.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/styledCellContent.json new file mode 100644 index 0000000000..3727fa1cf0 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/table/styledCellContent.json @@ -0,0 +1,136 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "marks": [ + { + "type": "bold", + }, + ], + "text": "Bold", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "marks": [ + { + "type": "italic", + }, + ], + "text": "Italic", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + ], + "type": "tableRow", + }, + { + "content": [ + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "marks": [ + { + "type": "strike", + }, + ], + "text": "Strike", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + { + "attrs": { + "backgroundColor": "default", + "colspan": 1, + "colwidth": null, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "content": [ + { + "content": [ + { + "marks": [ + { + "type": "code", + }, + ], + "text": "Code", + "type": "text", + }, + ], + "type": "tableParagraph", + }, + ], + "type": "tableCell", + }, + ], + "type": "tableRow", + }, + ], + "type": "table", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/video/withCaption.json b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/video/withCaption.json new file mode 100644 index 0000000000..6d6c134eb0 --- /dev/null +++ b/tests/src/unit/core/formatConversion/export/__snapshots__/nodes/video/withCaption.json @@ -0,0 +1,22 @@ +[ + { + "attrs": { + "id": "1", + }, + "content": [ + { + "attrs": { + "backgroundColor": "default", + "caption": "Video caption", + "name": "Example Video", + "previewWidth": undefined, + "showPreview": true, + "textAlignment": "left", + "url": "https://example.com/video.mp4", + }, + "type": "video", + }, + ], + "type": "blockContainer", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/export/exportTestInstances.ts b/tests/src/unit/core/formatConversion/export/exportTestInstances.ts index de46704117..f3677e1fe9 100644 --- a/tests/src/unit/core/formatConversion/export/exportTestInstances.ts +++ b/tests/src/unit/core/formatConversion/export/exportTestInstances.ts @@ -289,12 +289,15 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, executeTest: testExportBlockNoteHTML, }, + // Heading levels { testCase: { - name: "divider/basic", + name: "heading/h1", content: [ { - type: "divider", + type: "heading", + props: { level: 1 }, + content: "Heading 1", }, ], }, @@ -302,10 +305,12 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { testCase: { - name: "pageBreak/basic", + name: "heading/h2", content: [ { - type: "pageBreak", + type: "heading", + props: { level: 2 }, + content: "Heading 2", }, ], }, @@ -313,10 +318,12 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { testCase: { - name: "file/button", + name: "heading/h3", content: [ { - type: "file", + type: "heading", + props: { level: 3 }, + content: "Heading 3", }, ], }, @@ -324,15 +331,12 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { testCase: { - name: "file/basic", + name: "heading/h4", content: [ { - type: "file", - props: { - name: "example", - url: "exampleURL", - caption: "Caption", - }, + type: "heading", + props: { level: 4 }, + content: "Heading 4", }, ], }, @@ -340,14 +344,12 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { testCase: { - name: "file/noName", + name: "heading/h5", content: [ { - type: "file", - props: { - url: "exampleURL", - caption: "Caption", - }, + type: "heading", + props: { level: 5 }, + content: "Heading 5", }, ], }, @@ -355,14 +357,12 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { testCase: { - name: "file/noCaption", + name: "heading/h6", content: [ { - type: "file", - props: { - name: "example", - url: "exampleURL", - }, + type: "heading", + props: { level: 6 }, + content: "Heading 6", }, ], }, @@ -370,23 +370,40 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { testCase: { - name: "file/nested", + name: "heading/styled", content: [ { - type: "file", - props: { - name: "example", - url: "exampleURL", - caption: "Caption", - }, + type: "heading", + props: { level: 1 }, + content: [ + { + type: "text", + text: "Bold ", + styles: { bold: true }, + }, + { + type: "text", + text: "Heading", + styles: {}, + }, + ], + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "heading/toggleable", + content: [ + { + type: "heading", + props: { level: 2, isToggleable: true }, + content: "Toggle Heading", children: [ { - type: "file", - props: { - name: "example", - url: "exampleURL", - caption: "Caption", - }, + type: "paragraph", + content: "Child content", }, ], }, @@ -394,12 +411,14 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, executeTest: testExportBlockNoteHTML, }, + // Quote / Blockquote { testCase: { - name: "image/button", + name: "quote/basic", content: [ { - type: "image", + type: "quote", + content: "This is a quote", }, ], }, @@ -407,16 +426,32 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { testCase: { - name: "image/basic", + name: "quote/styled", content: [ { - type: "image", - props: { - name: "example", - url: "exampleURL", - caption: "Caption", - previewWidth: 256, - }, + type: "quote", + content: [ + { + type: "text", + text: "Bold ", + styles: { bold: true }, + }, + { + type: "text", + text: "and ", + styles: {}, + }, + { + type: "text", + text: "italic", + styles: { italic: true }, + }, + { + type: "text", + text: " quote", + styles: {}, + }, + ], }, ], }, @@ -424,15 +459,22 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { testCase: { - name: "image/noName", + name: "quote/withLink", content: [ { - type: "image", - props: { - url: "exampleURL", - caption: "Caption", - previewWidth: 256, - }, + type: "quote", + content: [ + { + type: "text", + text: "Quote with ", + styles: {}, + }, + { + type: "link", + href: "https://www.example.com", + content: "a link", + }, + ], }, ], }, @@ -440,15 +482,17 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { testCase: { - name: "image/noCaption", + name: "quote/nested", content: [ { - type: "image", - props: { - name: "example", - url: "exampleURL", - previewWidth: 256, - }, + type: "quote", + content: "Parent quote", + children: [ + { + type: "paragraph", + content: "Nested paragraph", + }, + ], }, ], }, @@ -456,16 +500,30 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { testCase: { - name: "image/noPreview", + name: "quote/multiple", content: [ { - type: "image", + type: "quote", + content: "First quote", + }, + { + type: "quote", + content: "Second quote", + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + // Audio + { + testCase: { + name: "audio/basic", + content: [ + { + type: "audio", props: { + url: "https://example.com/audio.mp3", name: "example", - url: "exampleURL", - caption: "Caption", - showPreview: false, - previewWidth: 256, }, }, ], @@ -474,23 +532,41 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { testCase: { - name: "image/nested", + name: "audio/button", content: [ { - type: "image", + type: "audio", + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "audio/noName", + content: [ + { + type: "audio", props: { - url: "exampleURL", - caption: "Caption", - previewWidth: 256, + url: "https://example.com/audio.mp3", }, - children: [ + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + // Individual styles + { + testCase: { + name: "style/bold", + content: [ + { + type: "paragraph", + content: [ { - type: "image", - props: { - url: "exampleURL", - caption: "Caption", - previewWidth: 256, - }, + type: "text", + text: "Bold text", + styles: { bold: true }, }, ], }, @@ -500,100 +576,1150 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { testCase: { - name: "table/basic", + name: "style/italic", content: [ { - type: "table", - content: { - type: "tableContent", - rows: [ - { - cells: [ - { - type: "tableCell", - content: ["Table Cell"], - props: { - backgroundColor: "default", - colspan: 1, - rowspan: 1, - textAlignment: "left", - textColor: "default", - }, - }, - { - type: "tableCell", - content: ["Table Cell"], - props: { - backgroundColor: "default", - colspan: 1, - rowspan: 1, - textAlignment: "left", - textColor: "default", - }, - }, - { - type: "tableCell", - content: ["Table Cell"], - props: { - backgroundColor: "default", - colspan: 1, - rowspan: 1, - textAlignment: "left", - textColor: "default", - }, - }, - ], - }, - { - cells: [ - { - type: "tableCell", - content: ["Table Cell"], - props: { - backgroundColor: "default", - colspan: 1, - rowspan: 1, - textAlignment: "left", - textColor: "default", - }, - }, - { - type: "tableCell", - content: ["Table Cell"], - props: { - backgroundColor: "default", - colspan: 1, - rowspan: 1, - textAlignment: "left", - textColor: "default", - }, - }, - { - type: "tableCell", - content: ["Table Cell"], - props: { - backgroundColor: "default", - colspan: 1, - rowspan: 1, - textAlignment: "left", - textColor: "default", - }, - }, - ], - }, - { - cells: [ - { - type: "tableCell", + type: "paragraph", + content: [ + { + type: "text", + text: "Italic text", + styles: { italic: true }, + }, + ], + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "style/underline", + content: [ + { + type: "paragraph", + content: [ + { + type: "text", + text: "Underline text", + styles: { underline: true }, + }, + ], + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "style/strike", + content: [ + { + type: "paragraph", + content: [ + { + type: "text", + text: "Strikethrough text", + styles: { strike: true }, + }, + ], + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "style/code", + content: [ + { + type: "paragraph", + content: [ + { + type: "text", + text: "Inline code", + styles: { code: true }, + }, + ], + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "style/textColor", + content: [ + { + type: "paragraph", + content: [ + { + type: "text", + text: "Colored text", + styles: { textColor: "red" }, + }, + ], + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "style/backgroundColor", + content: [ + { + type: "paragraph", + content: [ + { + type: "text", + text: "Highlighted text", + styles: { backgroundColor: "blue" }, + }, + ], + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "style/combined", + content: [ + { + type: "paragraph", + content: [ + { + type: "text", + text: "Bold and italic", + styles: { bold: true, italic: true }, + }, + ], + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "style/boldItalicStrike", + content: [ + { + type: "paragraph", + content: [ + { + type: "text", + text: "All styles", + styles: { bold: true, italic: true, strike: true }, + }, + ], + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "style/mixedInParagraph", + content: [ + { + type: "paragraph", + content: [ + { + type: "text", + text: "Normal ", + styles: {}, + }, + { + type: "text", + text: "bold ", + styles: { bold: true }, + }, + { + type: "text", + text: "italic ", + styles: { italic: true }, + }, + { + type: "text", + text: "code ", + styles: { code: true }, + }, + { + type: "text", + text: "strike", + styles: { strike: true }, + }, + ], + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + // Numbered list with custom start + { + testCase: { + name: "lists/numberedListStart", + content: [ + { + type: "numberedListItem", + props: { start: 5 }, + content: "Item 5", + }, + { + type: "numberedListItem", + content: "Item 6", + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + // Multiple paragraphs + { + testCase: { + name: "paragraph/multiple", + content: [ + { + type: "paragraph", + content: "First paragraph", + }, + { + type: "paragraph", + content: "Second paragraph", + }, + { + type: "paragraph", + content: "Third paragraph", + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + // Mixed block types document + { + testCase: { + name: "complex/document", + content: [ + { + type: "heading", + props: { level: 1 }, + content: "Document Title", + }, + { + type: "paragraph", + content: "Introduction paragraph.", + }, + { + type: "heading", + props: { level: 2 }, + content: "Section 1", + }, + { + type: "paragraph", + content: [ + { + type: "text", + text: "Text with ", + styles: {}, + }, + { + type: "text", + text: "bold", + styles: { bold: true }, + }, + { + type: "text", + text: " and ", + styles: {}, + }, + { + type: "link", + href: "https://example.com", + content: "a link", + }, + { + type: "text", + text: ".", + styles: {}, + }, + ], + }, + { + type: "bulletListItem", + content: "First point", + }, + { + type: "bulletListItem", + content: "Second point", + }, + { + type: "divider", + }, + { + type: "quote", + content: "A notable quote", + }, + { + type: "codeBlock", + props: { language: "javascript" }, + content: "const x = 42;", + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + // Link with inline code + { + testCase: { + name: "link/withCode", + content: [ + { + type: "paragraph", + content: [ + { + type: "text", + text: "See the ", + styles: {}, + }, + { + type: "link", + href: "https://example.com", + content: "docs", + }, + { + type: "text", + text: " for ", + styles: {}, + }, + { + type: "text", + text: "config", + styles: { code: true }, + }, + ], + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + // Image with caption + { + testCase: { + name: "image/withCaption", + content: [ + { + type: "image", + props: { + url: "https://example.com/image.png", + name: "Example Image", + caption: "This is a caption", + }, + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + // Video with caption + { + testCase: { + name: "video/withCaption", + content: [ + { + type: "video", + props: { + url: "https://example.com/video.mp4", + name: "Example Video", + caption: "Video caption", + }, + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "divider/basic", + content: [ + { + type: "divider", + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "pageBreak/basic", + content: [ + { + type: "pageBreak", + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "file/button", + content: [ + { + type: "file", + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "file/basic", + content: [ + { + type: "file", + props: { + name: "example", + url: "exampleURL", + caption: "Caption", + }, + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "file/noName", + content: [ + { + type: "file", + props: { + url: "exampleURL", + caption: "Caption", + }, + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "file/noCaption", + content: [ + { + type: "file", + props: { + name: "example", + url: "exampleURL", + }, + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "file/nested", + content: [ + { + type: "file", + props: { + name: "example", + url: "exampleURL", + caption: "Caption", + }, + children: [ + { + type: "file", + props: { + name: "example", + url: "exampleURL", + caption: "Caption", + }, + }, + ], + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "image/button", + content: [ + { + type: "image", + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "image/basic", + content: [ + { + type: "image", + props: { + name: "example", + url: "exampleURL", + caption: "Caption", + previewWidth: 256, + }, + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "image/noName", + content: [ + { + type: "image", + props: { + url: "exampleURL", + caption: "Caption", + previewWidth: 256, + }, + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "image/noCaption", + content: [ + { + type: "image", + props: { + name: "example", + url: "exampleURL", + previewWidth: 256, + }, + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "image/noPreview", + content: [ + { + type: "image", + props: { + name: "example", + url: "exampleURL", + caption: "Caption", + showPreview: false, + previewWidth: 256, + }, + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "image/nested", + content: [ + { + type: "image", + props: { + url: "exampleURL", + caption: "Caption", + previewWidth: 256, + }, + children: [ + { + type: "image", + props: { + url: "exampleURL", + caption: "Caption", + previewWidth: 256, + }, + }, + ], + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "table/basic", + content: [ + { + type: "table", + content: { + type: "tableContent", + rows: [ + { + cells: [ + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + ], + }, + { + cells: [ + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + ], + }, + { + cells: [ + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + ], + }, + ], + }, + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "table/allColWidths", + content: [ + { + type: "table", + content: { + type: "tableContent", + columnWidths: [100, 200, 300], + rows: [ + { + cells: [ + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + ], + }, + { + cells: [ + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + ], + }, + { + cells: [ + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + ], + }, + ], + }, + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "table/mixedColWidths", + content: [ + { + type: "table", + content: { + type: "tableContent", + columnWidths: [100, undefined, 300], + rows: [ + { + cells: [ + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + ], + }, + { + cells: [ + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + ], + }, + { + cells: [ + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + ], + }, + ], + }, + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "table/mixedCellColors", + content: [ + { + type: "table", + content: { + type: "tableContent", + columnWidths: [100, undefined, 300], + rows: [ + { + cells: [ + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "red", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "blue", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "blue", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "yellow", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "red", + }, + }, + ], + }, + { + cells: [ + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + ], + }, + { + cells: [ + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "default", + }, + }, + ], + }, + ], + }, + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + { + testCase: { + name: "table/mixedRowspansAndColspans", + content: [ + { + type: "table", + content: { + type: "tableContent", + columnWidths: [100, 200, 300], + rows: [ + { + cells: [ + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "red", + colspan: 2, + rowspan: 1, + textAlignment: "left", + textColor: "blue", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "yellow", + colspan: 1, + rowspan: 1, + textAlignment: "left", + textColor: "red", + }, + }, + ], + }, + { + cells: [ + { + type: "tableCell", content: ["Table Cell"], props: { backgroundColor: "default", colspan: 1, + rowspan: 2, + textAlignment: "left", + textColor: "default", + }, + }, + { + type: "tableCell", + content: ["Table Cell"], + props: { + backgroundColor: "default", + colspan: 2, rowspan: 1, textAlignment: "left", textColor: "default", }, }, + ], + }, + { + cells: [ { type: "tableCell", content: ["Table Cell"], @@ -627,13 +1753,13 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { testCase: { - name: "table/allColWidths", + name: "table/headerRows", content: [ { type: "table", content: { + headerRows: 1, type: "tableContent", - columnWidths: [100, 200, 300], rows: [ { cells: [ @@ -755,13 +1881,13 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { testCase: { - name: "table/mixedColWidths", + name: "table/headerCols", content: [ { type: "table", content: { + headerCols: 1, type: "tableContent", - columnWidths: [100, undefined, 300], rows: [ { cells: [ @@ -881,58 +2007,23 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, executeTest: testExportBlockNoteHTML, }, + // Advanced table: header rows + header cols together { testCase: { - name: "table/mixedCellColors", + name: "table/headerRowsAndCols", content: [ { type: "table", content: { type: "tableContent", - columnWidths: [100, undefined, 300], + headerRows: 1, + headerCols: 1, rows: [ { cells: [ { type: "tableCell", - content: ["Table Cell"], - props: { - backgroundColor: "red", - colspan: 1, - rowspan: 1, - textAlignment: "left", - textColor: "blue", - }, - }, - { - type: "tableCell", - content: ["Table Cell"], - props: { - backgroundColor: "blue", - colspan: 1, - rowspan: 1, - textAlignment: "left", - textColor: "default", - }, - }, - { - type: "tableCell", - content: ["Table Cell"], - props: { - backgroundColor: "yellow", - colspan: 1, - rowspan: 1, - textAlignment: "left", - textColor: "red", - }, - }, - ], - }, - { - cells: [ - { - type: "tableCell", - content: ["Table Cell"], + content: ["Corner"], props: { backgroundColor: "default", colspan: 1, @@ -943,7 +2034,7 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { type: "tableCell", - content: ["Table Cell"], + content: ["Column Header 1"], props: { backgroundColor: "default", colspan: 1, @@ -954,7 +2045,7 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { type: "tableCell", - content: ["Table Cell"], + content: ["Column Header 2"], props: { backgroundColor: "default", colspan: 1, @@ -969,7 +2060,7 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< cells: [ { type: "tableCell", - content: ["Table Cell"], + content: ["Row Header 1"], props: { backgroundColor: "default", colspan: 1, @@ -980,7 +2071,7 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { type: "tableCell", - content: ["Table Cell"], + content: ["Data 1"], props: { backgroundColor: "default", colspan: 1, @@ -991,7 +2082,7 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { type: "tableCell", - content: ["Table Cell"], + content: ["Data 2"], props: { backgroundColor: "default", colspan: 1, @@ -1009,61 +2100,47 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, executeTest: testExportBlockNoteHTML, }, + // Advanced table: styled content in cells { testCase: { - name: "table/mixedRowspansAndColspans", + name: "table/styledCellContent", content: [ { type: "table", content: { type: "tableContent", - columnWidths: [100, 200, 300], rows: [ { cells: [ { type: "tableCell", - content: ["Table Cell"], - props: { - backgroundColor: "red", - colspan: 2, - rowspan: 1, - textAlignment: "left", - textColor: "blue", - }, - }, - { - type: "tableCell", - content: ["Table Cell"], - props: { - backgroundColor: "yellow", - colspan: 1, - rowspan: 1, - textAlignment: "left", - textColor: "red", - }, - }, - ], - }, - { - cells: [ - { - type: "tableCell", - content: ["Table Cell"], + content: [ + { + type: "text", + text: "Bold", + styles: { bold: true }, + }, + ], props: { backgroundColor: "default", colspan: 1, - rowspan: 2, + rowspan: 1, textAlignment: "left", textColor: "default", }, }, { type: "tableCell", - content: ["Table Cell"], + content: [ + { + type: "text", + text: "Italic", + styles: { italic: true }, + }, + ], props: { backgroundColor: "default", - colspan: 2, + colspan: 1, rowspan: 1, textAlignment: "left", textColor: "default", @@ -1075,7 +2152,13 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< cells: [ { type: "tableCell", - content: ["Table Cell"], + content: [ + { + type: "text", + text: "Strike", + styles: { strike: true }, + }, + ], props: { backgroundColor: "default", colspan: 1, @@ -1086,7 +2169,13 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { type: "tableCell", - content: ["Table Cell"], + content: [ + { + type: "text", + text: "Code", + styles: { code: true }, + }, + ], props: { backgroundColor: "default", colspan: 1, @@ -1104,21 +2193,32 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, executeTest: testExportBlockNoteHTML, }, + // Advanced table: links in cells { testCase: { - name: "table/headerRows", + name: "table/linksInCells", content: [ { type: "table", content: { - headerRows: 1, type: "tableContent", rows: [ { cells: [ { type: "tableCell", - content: ["Table Cell"], + content: [ + { + type: "text", + text: "Visit ", + styles: {}, + }, + { + type: "link", + href: "https://example.com", + content: "Example", + }, + ], props: { backgroundColor: "default", colspan: 1, @@ -1129,7 +2229,7 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { type: "tableCell", - content: ["Table Cell"], + content: ["Plain cell"], props: { backgroundColor: "default", colspan: 1, @@ -1138,9 +2238,13 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< textColor: "default", }, }, + ], + }, + { + cells: [ { type: "tableCell", - content: ["Table Cell"], + content: ["Data"], props: { backgroundColor: "default", colspan: 1, @@ -1149,13 +2253,15 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< textColor: "default", }, }, - ], - }, - { - cells: [ { type: "tableCell", - content: ["Table Cell"], + content: [ + { + type: "link", + href: "https://example2.com", + content: "Link", + }, + ], props: { backgroundColor: "default", colspan: 1, @@ -1164,9 +2270,30 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< textColor: "default", }, }, + ], + }, + ], + }, + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + // Advanced table: empty cells + { + testCase: { + name: "table/emptyCells", + content: [ + { + type: "table", + content: { + type: "tableContent", + rows: [ + { + cells: [ { type: "tableCell", - content: ["Table Cell"], + content: ["Has content"], props: { backgroundColor: "default", colspan: 1, @@ -1177,7 +2304,7 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { type: "tableCell", - content: ["Table Cell"], + content: [], props: { backgroundColor: "default", colspan: 1, @@ -1192,7 +2319,7 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< cells: [ { type: "tableCell", - content: ["Table Cell"], + content: [], props: { backgroundColor: "default", colspan: 1, @@ -1203,7 +2330,7 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { type: "tableCell", - content: ["Table Cell"], + content: ["Also has content"], props: { backgroundColor: "default", colspan: 1, @@ -1212,9 +2339,30 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< textColor: "default", }, }, + ], + }, + ], + }, + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + // Advanced table: single cell + { + testCase: { + name: "table/singleCell", + content: [ + { + type: "table", + content: { + type: "tableContent", + rows: [ + { + cells: [ { type: "tableCell", - content: ["Table Cell"], + content: ["Only cell"], props: { backgroundColor: "default", colspan: 1, @@ -1232,49 +2380,103 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, executeTest: testExportBlockNoteHTML, }, + // Advanced table: from the advanced-tables example (large merged cells) { testCase: { - name: "table/headerCols", + name: "table/advancedExample", content: [ { type: "table", content: { - headerCols: 1, type: "tableContent", + columnWidths: [199, 148, 201], + headerRows: 1, rows: [ { cells: [ { type: "tableCell", - content: ["Table Cell"], + content: ["This row has headers"], props: { + colspan: 1, + rowspan: 1, backgroundColor: "default", + textColor: "default", + textAlignment: "center", + }, + }, + { + type: "tableCell", + content: [ + { + type: "text", + text: "This is ", + styles: {}, + }, + { + type: "text", + text: "RED", + styles: { bold: true }, + }, + ], + props: { colspan: 1, rowspan: 1, - textAlignment: "left", + backgroundColor: "red", textColor: "default", + textAlignment: "center", }, }, { type: "tableCell", - content: ["Table Cell"], + content: ["Text is Blue"], props: { - backgroundColor: "default", colspan: 1, rowspan: 1, + backgroundColor: "default", + textColor: "blue", + textAlignment: "center", + }, + }, + ], + }, + { + cells: [ + { + type: "tableCell", + content: ["This spans 2 columns\nand 2 rows"], + props: { + colspan: 2, + rowspan: 2, + backgroundColor: "yellow", + textColor: "default", textAlignment: "left", + }, + }, + { + type: "tableCell", + content: ["Sooo many features"], + props: { + colspan: 1, + rowspan: 1, + backgroundColor: "gray", textColor: "default", + textAlignment: "left", }, }, + ], + }, + { + cells: [ { type: "tableCell", - content: ["Table Cell"], + content: [], props: { - backgroundColor: "default", colspan: 1, rowspan: 1, + backgroundColor: "gray", + textColor: "purple", textAlignment: "left", - textColor: "default", }, }, ], @@ -1283,18 +2485,67 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< cells: [ { type: "tableCell", - content: ["Table Cell"], + content: ["A cell"], props: { - backgroundColor: "default", colspan: 1, rowspan: 1, + backgroundColor: "default", + textColor: "default", textAlignment: "left", + }, + }, + { + type: "tableCell", + content: ["Another Cell"], + props: { + colspan: 1, + rowspan: 1, + backgroundColor: "default", textColor: "default", + textAlignment: "right", }, }, { type: "tableCell", - content: ["Table Cell"], + content: ["Aligned center"], + props: { + colspan: 1, + rowspan: 1, + backgroundColor: "default", + textColor: "default", + textAlignment: "center", + }, + }, + ], + }, + ], + }, + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + // Advanced table: hard breaks in cells + { + testCase: { + name: "table/hardBreakInCell", + content: [ + { + type: "table", + content: { + type: "tableContent", + rows: [ + { + cells: [ + { + type: "tableCell", + content: [ + { + type: "text", + text: "Line 1\nLine 2", + styles: {}, + }, + ], props: { backgroundColor: "default", colspan: 1, @@ -1305,7 +2556,7 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { type: "tableCell", - content: ["Table Cell"], + content: ["Normal cell"], props: { backgroundColor: "default", colspan: 1, @@ -1316,11 +2567,28 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, ], }, + ], + }, + }, + ], + }, + executeTest: testExportBlockNoteHTML, + }, + // Advanced table: mixed text alignment per cell + { + testCase: { + name: "table/cellTextAlignment", + content: [ + { + type: "table", + content: { + type: "tableContent", + rows: [ { cells: [ { type: "tableCell", - content: ["Table Cell"], + content: ["Left"], props: { backgroundColor: "default", colspan: 1, @@ -1331,23 +2599,23 @@ export const exportTestInstancesBlockNoteHTML: TestInstance< }, { type: "tableCell", - content: ["Table Cell"], + content: ["Center"], props: { backgroundColor: "default", colspan: 1, rowspan: 1, - textAlignment: "left", + textAlignment: "center", textColor: "default", }, }, { type: "tableCell", - content: ["Table Cell"], + content: ["Right"], props: { backgroundColor: "default", colspan: 1, rowspan: 1, - textAlignment: "left", + textAlignment: "right", textColor: "default", }, }, diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/bold.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/bold.json new file mode 100644 index 0000000000..0569abc8a9 --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/bold.json @@ -0,0 +1,21 @@ +[ + { + "children": [], + "content": [ + { + "styles": { + "bold": true, + }, + "text": "Bold text", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/boldItalic.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/boldItalic.json new file mode 100644 index 0000000000..f6b0ca8045 --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/boldItalic.json @@ -0,0 +1,22 @@ +[ + { + "children": [], + "content": [ + { + "styles": { + "bold": true, + "italic": true, + }, + "text": "Bold and italic", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/bulletList.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/bulletList.json new file mode 100644 index 0000000000..b5999d3ec9 --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/bulletList.json @@ -0,0 +1,36 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Item 1", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Item 2", + "type": "text", + }, + ], + "id": "2", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/checkList.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/checkList.json new file mode 100644 index 0000000000..3f344bf122 --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/checkList.json @@ -0,0 +1,38 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Unchecked", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "checked": false, + "textAlignment": "left", + "textColor": "default", + }, + "type": "checkListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Checked", + "type": "text", + }, + ], + "id": "2", + "props": { + "backgroundColor": "default", + "checked": true, + "textAlignment": "left", + "textColor": "default", + }, + "type": "checkListItem", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/codeBlock.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/codeBlock.json new file mode 100644 index 0000000000..63ebc503a5 --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/codeBlock.json @@ -0,0 +1,17 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "const x = 42;", + "type": "text", + }, + ], + "id": "1", + "props": { + "language": "javascript", + }, + "type": "codeBlock", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/complexDocument.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/complexDocument.json new file mode 100644 index 0000000000..570c7e98d5 --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/complexDocument.json @@ -0,0 +1,106 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Title", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "isToggleable": false, + "level": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "heading", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Paragraph with ", + "type": "text", + }, + { + "styles": { + "bold": true, + }, + "text": "bold", + "type": "text", + }, + { + "styles": {}, + "text": " text.", + "type": "text", + }, + ], + "id": "2", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Bullet 1", + "type": "text", + }, + ], + "id": "3", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Bullet 2", + "type": "text", + }, + ], + "id": "4", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, + { + "children": [], + "content": undefined, + "id": "5", + "props": {}, + "type": "divider", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "print('hello')", + "type": "text", + }, + ], + "id": "6", + "props": { + "language": "python", + }, + "type": "codeBlock", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/divider.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/divider.json new file mode 100644 index 0000000000..e944763aea --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/divider.json @@ -0,0 +1,43 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Before", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, + { + "children": [], + "content": undefined, + "id": "2", + "props": {}, + "type": "divider", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "After", + "type": "text", + }, + ], + "id": "3", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/hardBreak.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/hardBreak.json new file mode 100644 index 0000000000..5626739e88 --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/hardBreak.json @@ -0,0 +1,20 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Line 1 + Line 2", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/headingLevels.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/headingLevels.json new file mode 100644 index 0000000000..f1ce124a58 --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/headingLevels.json @@ -0,0 +1,59 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Heading 1", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "isToggleable": false, + "level": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "heading", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Heading 2", + "type": "text", + }, + ], + "id": "2", + "props": { + "backgroundColor": "default", + "isToggleable": false, + "level": 2, + "textAlignment": "left", + "textColor": "default", + }, + "type": "heading", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Heading 3", + "type": "text", + }, + ], + "id": "3", + "props": { + "backgroundColor": "default", + "isToggleable": false, + "level": 3, + "textAlignment": "left", + "textColor": "default", + }, + "type": "heading", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/image.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/image.json new file mode 100644 index 0000000000..c9a6bddd61 --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/image.json @@ -0,0 +1,16 @@ +[ + { + "children": [], + "content": undefined, + "id": "1", + "props": { + "backgroundColor": "default", + "caption": "", + "name": "Example", + "showPreview": true, + "textAlignment": "left", + "url": "https://example.com/image.png", + }, + "type": "image", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/inlineCode.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/inlineCode.json new file mode 100644 index 0000000000..6ddd0e59ee --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/inlineCode.json @@ -0,0 +1,21 @@ +[ + { + "children": [], + "content": [ + { + "styles": { + "code": true, + }, + "text": "Code text", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/italic.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/italic.json new file mode 100644 index 0000000000..01ec89cd69 --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/italic.json @@ -0,0 +1,21 @@ +[ + { + "children": [], + "content": [ + { + "styles": { + "italic": true, + }, + "text": "Italic text", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/link.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/link.json new file mode 100644 index 0000000000..fabbb2daa3 --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/link.json @@ -0,0 +1,35 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Text ", + "type": "text", + }, + { + "content": [ + { + "styles": {}, + "text": "Link", + "type": "text", + }, + ], + "href": "https://example.com", + "type": "link", + }, + { + "styles": {}, + "text": " more text", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/mixedStyles.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/mixedStyles.json new file mode 100644 index 0000000000..7085f4c8b8 --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/mixedStyles.json @@ -0,0 +1,50 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Normal ", + "type": "text", + }, + { + "styles": { + "bold": true, + }, + "text": "bold", + "type": "text", + }, + { + "styles": {}, + "text": " ", + "type": "text", + }, + { + "styles": { + "italic": true, + }, + "text": "italic", + "type": "text", + }, + { + "styles": {}, + "text": " ", + "type": "text", + }, + { + "styles": { + "strike": true, + }, + "text": "strike", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/multipleParagraphs.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/multipleParagraphs.json new file mode 100644 index 0000000000..fc70b307a0 --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/multipleParagraphs.json @@ -0,0 +1,36 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "First paragraph", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Second paragraph", + "type": "text", + }, + ], + "id": "2", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/nestedLists.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/nestedLists.json new file mode 100644 index 0000000000..90bb306cb8 --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/nestedLists.json @@ -0,0 +1,54 @@ +[ + { + "children": [ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Child 1", + "type": "text", + }, + ], + "id": "2", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "numberedListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Child 2", + "type": "text", + }, + ], + "id": "3", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "numberedListItem", + }, + ], + "content": [ + { + "styles": {}, + "text": "Parent", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/numberedList.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/numberedList.json new file mode 100644 index 0000000000..f2dfd7912e --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/numberedList.json @@ -0,0 +1,36 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Item 1", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "numberedListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Item 2", + "type": "text", + }, + ], + "id": "2", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "numberedListItem", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/paragraph.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/paragraph.json new file mode 100644 index 0000000000..575bc9876a --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/paragraph.json @@ -0,0 +1,19 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Simple paragraph", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/quote.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/quote.json new file mode 100644 index 0000000000..7a2b3b4601 --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/quote.json @@ -0,0 +1,18 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "A quote", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textColor": "default", + }, + "type": "quote", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/strike.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/strike.json new file mode 100644 index 0000000000..7f504a4b3f --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/strike.json @@ -0,0 +1,21 @@ +[ + { + "children": [], + "content": [ + { + "styles": { + "strike": true, + }, + "text": "Strikethrough text", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/table.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/table.json new file mode 100644 index 0000000000..c96eefda40 --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/table.json @@ -0,0 +1,123 @@ +[ + { + "children": [], + "content": { + "columnWidths": [ + undefined, + undefined, + ], + "headerCols": undefined, + "headerRows": 1, + "rows": [ + { + "cells": [ + { + "content": [], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + ], + }, + { + "cells": [ + { + "content": [ + { + "styles": {}, + "text": "Header 1", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": {}, + "text": "Header 2", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + ], + }, + { + "cells": [ + { + "content": [ + { + "styles": {}, + "text": "Cell 1", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": {}, + "text": "Cell 2", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + ], + }, + ], + "type": "tableContent", + }, + "id": "1", + "props": { + "textColor": "default", + }, + "type": "table", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/video.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/video.json new file mode 100644 index 0000000000..d7fdf700aa --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/video.json @@ -0,0 +1,16 @@ +[ + { + "children": [], + "content": undefined, + "id": "1", + "props": { + "backgroundColor": "default", + "caption": "", + "name": "", + "showPreview": true, + "textAlignment": "left", + "url": "https://example.com/video.mp4", + }, + "type": "video", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/exportParseEqualityTestInstances.ts b/tests/src/unit/core/formatConversion/exportParseEquality/exportParseEqualityTestInstances.ts index 57120eb518..8e0c87c313 100644 --- a/tests/src/unit/core/formatConversion/exportParseEquality/exportParseEqualityTestInstances.ts +++ b/tests/src/unit/core/formatConversion/exportParseEquality/exportParseEqualityTestInstances.ts @@ -2,6 +2,7 @@ import { ExportParseEqualityTestCase } from "../../../shared/formatConversion/ex import { testExportParseEqualityBlockNoteHTML, testExportParseEqualityHTML, + testExportParseEqualityMarkdown, } from "../../../shared/formatConversion/exportParseEquality/exportParseEqualityTestExecutors.js"; import { TestInstance } from "../../../types.js"; import { @@ -371,3 +372,456 @@ export const exportParseEqualityTestInstancesHTML: TestInstance< executeTest: testExportParseEqualityHTML, }, ]; + +// Markdown round-trip tests: blocks → markdown → blocks +// Markdown is a lossy format (no colors, underline, alignment), so these tests +// use snapshot matching to capture the expected round-trip result rather than +// strict equality with the input. This is critical for verifying that the +// custom markdown parser/serializer produces the same round-trip results. +export const exportParseEqualityTestInstancesMarkdown: TestInstance< + ExportParseEqualityTestCase< + TestBlockSchema, + TestInlineContentSchema, + TestStyleSchema + >, + TestBlockSchema, + TestInlineContentSchema, + TestStyleSchema +>[] = [ + { + testCase: { + name: "markdown/paragraph", + content: [ + { + type: "paragraph", + content: "Simple paragraph", + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/multipleParagraphs", + content: [ + { + type: "paragraph", + content: "First paragraph", + }, + { + type: "paragraph", + content: "Second paragraph", + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/headingLevels", + content: [ + { + type: "heading", + props: { level: 1 }, + content: "Heading 1", + }, + { + type: "heading", + props: { level: 2 }, + content: "Heading 2", + }, + { + type: "heading", + props: { level: 3 }, + content: "Heading 3", + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/bulletList", + content: [ + { + type: "bulletListItem", + content: "Item 1", + }, + { + type: "bulletListItem", + content: "Item 2", + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/numberedList", + content: [ + { + type: "numberedListItem", + content: "Item 1", + }, + { + type: "numberedListItem", + content: "Item 2", + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/checkList", + content: [ + { + type: "checkListItem", + content: "Unchecked", + }, + { + type: "checkListItem", + props: { checked: true }, + content: "Checked", + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/nestedLists", + content: [ + { + type: "bulletListItem", + content: "Parent", + children: [ + { + type: "numberedListItem", + content: "Child 1", + }, + { + type: "numberedListItem", + content: "Child 2", + }, + ], + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/codeBlock", + content: [ + { + type: "codeBlock", + props: { language: "javascript" }, + content: "const x = 42;", + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/divider", + content: [ + { + type: "paragraph", + content: "Before", + }, + { + type: "divider", + }, + { + type: "paragraph", + content: "After", + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/bold", + content: [ + { + type: "paragraph", + content: [ + { + type: "text", + text: "Bold text", + styles: { bold: true }, + }, + ], + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/italic", + content: [ + { + type: "paragraph", + content: [ + { + type: "text", + text: "Italic text", + styles: { italic: true }, + }, + ], + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/strike", + content: [ + { + type: "paragraph", + content: [ + { + type: "text", + text: "Strikethrough text", + styles: { strike: true }, + }, + ], + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/inlineCode", + content: [ + { + type: "paragraph", + content: [ + { + type: "text", + text: "Code text", + styles: { code: true }, + }, + ], + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/boldItalic", + content: [ + { + type: "paragraph", + content: [ + { + type: "text", + text: "Bold and italic", + styles: { bold: true, italic: true }, + }, + ], + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/link", + content: [ + { + type: "paragraph", + content: [ + { + type: "text", + text: "Text ", + styles: {}, + }, + { + type: "link", + content: "Link", + href: "https://example.com", + }, + { + type: "text", + text: " more text", + styles: {}, + }, + ], + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/image", + content: [ + { + type: "image", + props: { + url: "https://example.com/image.png", + name: "Example", + }, + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/video", + content: [ + { + type: "video", + props: { + url: "https://example.com/video.mp4", + name: "Example", + }, + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/table", + content: [ + { + type: "table", + content: { + type: "tableContent", + rows: [ + { + cells: ["Header 1", "Header 2"], + }, + { + cells: ["Cell 1", "Cell 2"], + }, + ], + }, + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/quote", + content: [ + { + type: "quote", + content: "A quote", + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/hardBreak", + content: [ + { + type: "paragraph", + content: [ + { + type: "text", + text: "Line 1\nLine 2", + styles: {}, + }, + ], + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/mixedStyles", + content: [ + { + type: "paragraph", + content: [ + { + type: "text", + text: "Normal ", + styles: {}, + }, + { + type: "text", + text: "bold ", + styles: { bold: true }, + }, + { + type: "text", + text: "italic ", + styles: { italic: true }, + }, + { + type: "text", + text: "strike", + styles: { strike: true }, + }, + ], + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/complexDocument", + content: [ + { + type: "heading", + props: { level: 1 }, + content: "Title", + }, + { + type: "paragraph", + content: [ + { + type: "text", + text: "Paragraph with ", + styles: {}, + }, + { + type: "text", + text: "bold", + styles: { bold: true }, + }, + { + type: "text", + text: " text.", + styles: {}, + }, + ], + }, + { + type: "bulletListItem", + content: "Bullet 1", + }, + { + type: "bulletListItem", + content: "Bullet 2", + }, + { + type: "divider", + }, + { + type: "codeBlock", + props: { language: "python" }, + content: "print('hello')", + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, +]; diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/runTests.test.ts b/tests/src/unit/core/formatConversion/exportParseEquality/runTests.test.ts index fc7f33f3c8..bcbad6fea9 100644 --- a/tests/src/unit/core/formatConversion/exportParseEquality/runTests.test.ts +++ b/tests/src/unit/core/formatConversion/exportParseEquality/runTests.test.ts @@ -5,6 +5,7 @@ import { testSchema } from "../../testSchema.js"; import { exportParseEqualityTestInstancesBlockNoteHTML, exportParseEqualityTestInstancesHTML, + exportParseEqualityTestInstancesMarkdown, } from "./exportParseEqualityTestInstances.js"; // Tests for verifying that exporting blocks to another format, then importing @@ -36,3 +37,16 @@ describe("Export/parse equality tests (HTML)", () => { }); } }); + +describe("Export/parse equality tests (Markdown)", () => { + const getEditor = createTestEditor(testSchema); + + for (const { + testCase, + executeTest, + } of exportParseEqualityTestInstancesMarkdown) { + it(`${testCase.name}`, async () => { + await executeTest(getEditor(), testCase); + }); + } +}); diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/adjacentFormattedRuns.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/adjacentFormattedRuns.json new file mode 100644 index 0000000000..24ea75b41d --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/adjacentFormattedRuns.json @@ -0,0 +1,35 @@ +[ + { + "children": [], + "content": [ + { + "styles": { + "bold": true, + }, + "text": "bold", + "type": "text", + }, + { + "styles": { + "italic": true, + }, + "text": "italic", + "type": "text", + }, + { + "styles": { + "strike": true, + }, + "text": "strike", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/adjacentLinks.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/adjacentLinks.json new file mode 100644 index 0000000000..bdaf169421 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/adjacentLinks.json @@ -0,0 +1,36 @@ +[ + { + "children": [], + "content": [ + { + "content": [ + { + "styles": {}, + "text": "Link1", + "type": "text", + }, + ], + "href": "https://example1.com", + "type": "link", + }, + { + "content": [ + { + "styles": {}, + "text": "Link2", + "type": "text", + }, + ], + "href": "https://example2.com", + "type": "link", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/backslashEscapes.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/backslashEscapes.json new file mode 100644 index 0000000000..fbdb14c852 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/backslashEscapes.json @@ -0,0 +1,19 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "*not bold* [not a link] ~not strike~", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/blockquoteMultiline.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/blockquoteMultiline.json new file mode 100644 index 0000000000..abe3612e64 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/blockquoteMultiline.json @@ -0,0 +1,18 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Line one Line two Line three", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textColor": "default", + }, + "type": "quote", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/blockquoteWithCode.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/blockquoteWithCode.json new file mode 100644 index 0000000000..b4a209b413 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/blockquoteWithCode.json @@ -0,0 +1,30 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Quote with ", + "type": "text", + }, + { + "styles": { + "code": true, + }, + "text": "inline code", + "type": "text", + }, + { + "styles": {}, + "text": " inside", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textColor": "default", + }, + "type": "quote", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/blockquoteWithLink.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/blockquoteWithLink.json new file mode 100644 index 0000000000..8cd0e17218 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/blockquoteWithLink.json @@ -0,0 +1,34 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Quote with ", + "type": "text", + }, + { + "content": [ + { + "styles": {}, + "text": "a link", + "type": "text", + }, + ], + "href": "https://example.com", + "type": "link", + }, + { + "styles": {}, + "text": " inside", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textColor": "default", + }, + "type": "quote", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/boldOnly.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/boldOnly.json new file mode 100644 index 0000000000..0569abc8a9 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/boldOnly.json @@ -0,0 +1,21 @@ +[ + { + "children": [], + "content": [ + { + "styles": { + "bold": true, + }, + "text": "Bold text", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/boldUnderscore.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/boldUnderscore.json new file mode 100644 index 0000000000..fc81db25f4 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/boldUnderscore.json @@ -0,0 +1,21 @@ +[ + { + "children": [], + "content": [ + { + "styles": { + "bold": true, + }, + "text": "Bold with underscores", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/checkListBasic.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/checkListBasic.json new file mode 100644 index 0000000000..62136bf1c9 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/checkListBasic.json @@ -0,0 +1,56 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Unchecked item", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "checked": false, + "textAlignment": "left", + "textColor": "default", + }, + "type": "checkListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Checked item", + "type": "text", + }, + ], + "id": "2", + "props": { + "backgroundColor": "default", + "checked": true, + "textAlignment": "left", + "textColor": "default", + }, + "type": "checkListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Another unchecked", + "type": "text", + }, + ], + "id": "3", + "props": { + "backgroundColor": "default", + "checked": false, + "textAlignment": "left", + "textColor": "default", + }, + "type": "checkListItem", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/checkListMixed.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/checkListMixed.json new file mode 100644 index 0000000000..4084a42458 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/checkListMixed.json @@ -0,0 +1,55 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Regular bullet", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Check item", + "type": "text", + }, + ], + "id": "2", + "props": { + "backgroundColor": "default", + "checked": false, + "textAlignment": "left", + "textColor": "default", + }, + "type": "checkListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Checked item", + "type": "text", + }, + ], + "id": "3", + "props": { + "backgroundColor": "default", + "checked": true, + "textAlignment": "left", + "textColor": "default", + }, + "type": "checkListItem", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/checkListNested.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/checkListNested.json new file mode 100644 index 0000000000..ff5c84bf29 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/checkListNested.json @@ -0,0 +1,57 @@ +[ + { + "children": [ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Child checked", + "type": "text", + }, + ], + "id": "2", + "props": { + "backgroundColor": "default", + "checked": true, + "textAlignment": "left", + "textColor": "default", + }, + "type": "checkListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Child unchecked", + "type": "text", + }, + ], + "id": "3", + "props": { + "backgroundColor": "default", + "checked": false, + "textAlignment": "left", + "textColor": "default", + }, + "type": "checkListItem", + }, + ], + "content": [ + { + "styles": {}, + "text": "Parent item", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "checked": false, + "textAlignment": "left", + "textColor": "default", + }, + "type": "checkListItem", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/codeBlockBasic.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/codeBlockBasic.json new file mode 100644 index 0000000000..cf59869a6f --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/codeBlockBasic.json @@ -0,0 +1,17 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "console.log('Hello');", + "type": "text", + }, + ], + "id": "1", + "props": { + "language": "text", + }, + "type": "codeBlock", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/codeBlockPython.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/codeBlockPython.json new file mode 100644 index 0000000000..78cd6179bf --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/codeBlockPython.json @@ -0,0 +1,18 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "def hello(): + print("Hello, world!")", + "type": "text", + }, + ], + "id": "1", + "props": { + "language": "python", + }, + "type": "codeBlock", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/codeBlockTildes.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/codeBlockTildes.json new file mode 100644 index 0000000000..1a656bd726 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/codeBlockTildes.json @@ -0,0 +1,17 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "code with tildes", + "type": "text", + }, + ], + "id": "1", + "props": { + "language": "text", + }, + "type": "codeBlock", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/codeBlockWithLanguage.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/codeBlockWithLanguage.json new file mode 100644 index 0000000000..90eb554680 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/codeBlockWithLanguage.json @@ -0,0 +1,18 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "const x = 42; +console.log(x);", + "type": "text", + }, + ], + "id": "1", + "props": { + "language": "javascript", + }, + "type": "codeBlock", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/codeBlockWithSpecialChars.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/codeBlockWithSpecialChars.json new file mode 100644 index 0000000000..bdf7b585f8 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/codeBlockWithSpecialChars.json @@ -0,0 +1,19 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "
    +

    Hello **not bold**

    +
    ", + "type": "text", + }, + ], + "id": "1", + "props": { + "language": "html", + }, + "type": "codeBlock", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/complexDocument.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/complexDocument.json new file mode 100644 index 0000000000..972d1d02df --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/complexDocument.json @@ -0,0 +1,442 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Main Title", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "isToggleable": false, + "level": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "heading", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "An introduction paragraph with ", + "type": "text", + }, + { + "styles": { + "bold": true, + }, + "text": "bold", + "type": "text", + }, + { + "styles": {}, + "text": " and ", + "type": "text", + }, + { + "styles": { + "italic": true, + }, + "text": "italic", + "type": "text", + }, + { + "styles": {}, + "text": " text.", + "type": "text", + }, + ], + "id": "2", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Section 1", + "type": "text", + }, + ], + "id": "3", + "props": { + "backgroundColor": "default", + "isToggleable": false, + "level": 2, + "textAlignment": "left", + "textColor": "default", + }, + "type": "heading", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "First bullet point", + "type": "text", + }, + ], + "id": "4", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, + { + "children": [ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Nested point", + "type": "text", + }, + ], + "id": "6", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, + ], + "content": [ + { + "styles": {}, + "text": "Second bullet point", + "type": "text", + }, + ], + "id": "5", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "A notable quote", + "type": "text", + }, + ], + "id": "7", + "props": { + "backgroundColor": "default", + "textColor": "default", + }, + "type": "quote", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Code Example", + "type": "text", + }, + ], + "id": "8", + "props": { + "backgroundColor": "default", + "isToggleable": false, + "level": 3, + "textAlignment": "left", + "textColor": "default", + }, + "type": "heading", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "function hello() { + return "world"; +}", + "type": "text", + }, + ], + "id": "9", + "props": { + "language": "javascript", + }, + "type": "codeBlock", + }, + { + "children": [], + "content": undefined, + "id": "10", + "props": {}, + "type": "divider", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Section 2", + "type": "text", + }, + ], + "id": "11", + "props": { + "backgroundColor": "default", + "isToggleable": false, + "level": 2, + "textAlignment": "left", + "textColor": "default", + }, + "type": "heading", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Step one", + "type": "text", + }, + ], + "id": "12", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "numberedListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Step two", + "type": "text", + }, + ], + "id": "13", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "numberedListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Step three", + "type": "text", + }, + ], + "id": "14", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "numberedListItem", + }, + { + "children": [], + "content": { + "columnWidths": [ + undefined, + undefined, + ], + "headerCols": undefined, + "headerRows": 1, + "rows": [ + { + "cells": [ + { + "content": [ + { + "styles": {}, + "text": "Feature", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": {}, + "text": "Status", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + ], + }, + { + "cells": [ + { + "content": [ + { + "styles": {}, + "text": "Bold", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": {}, + "text": "Done", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + ], + }, + { + "cells": [ + { + "content": [ + { + "styles": {}, + "text": "Italic", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": {}, + "text": "Done", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + ], + }, + ], + "type": "tableContent", + }, + "id": "15", + "props": { + "textColor": "default", + }, + "type": "table", + }, + { + "children": [], + "content": undefined, + "id": "16", + "props": { + "backgroundColor": "default", + "caption": "", + "name": "Image", + "showPreview": true, + "textAlignment": "left", + "url": "https://example.com/image.png", + }, + "type": "image", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Final paragraph with ", + "type": "text", + }, + { + "content": [ + { + "styles": {}, + "text": "a link", + "type": "text", + }, + ], + "href": "https://example.com", + "type": "link", + }, + { + "styles": {}, + "text": ".", + "type": "text", + }, + ], + "id": "17", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/deeplyNestedLists.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/deeplyNestedLists.json new file mode 100644 index 0000000000..535c584553 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/deeplyNestedLists.json @@ -0,0 +1,73 @@ +[ + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Level 4", + "type": "text", + }, + ], + "id": "4", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, + ], + "content": [ + { + "styles": {}, + "text": "Level 3", + "type": "text", + }, + ], + "id": "3", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, + ], + "content": [ + { + "styles": {}, + "text": "Level 2", + "type": "text", + }, + ], + "id": "2", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, + ], + "content": [ + { + "styles": {}, + "text": "Level 1", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/emptyString.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/emptyString.json new file mode 100644 index 0000000000..45f0949abe --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/emptyString.json @@ -0,0 +1,13 @@ +[ + { + "children": [], + "content": [], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/hardBreakBackslash.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/hardBreakBackslash.json new file mode 100644 index 0000000000..e4ed206bcf --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/hardBreakBackslash.json @@ -0,0 +1,20 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Line one + Line two", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/hardBreakMultiple.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/hardBreakMultiple.json new file mode 100644 index 0000000000..359a3b1cbc --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/hardBreakMultiple.json @@ -0,0 +1,21 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Line one + Line two + Line three", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH1.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH1.json new file mode 100644 index 0000000000..27be2c1d52 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH1.json @@ -0,0 +1,21 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Heading 1", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "isToggleable": false, + "level": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "heading", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH2.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH2.json new file mode 100644 index 0000000000..2cb042a21b --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH2.json @@ -0,0 +1,21 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Heading 2", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "isToggleable": false, + "level": 2, + "textAlignment": "left", + "textColor": "default", + }, + "type": "heading", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH3.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH3.json new file mode 100644 index 0000000000..3e79e93fa0 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH3.json @@ -0,0 +1,21 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Heading 3", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "isToggleable": false, + "level": 3, + "textAlignment": "left", + "textColor": "default", + }, + "type": "heading", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH4.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH4.json new file mode 100644 index 0000000000..b5dffeaff8 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH4.json @@ -0,0 +1,21 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Heading 4", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "isToggleable": false, + "level": 4, + "textAlignment": "left", + "textColor": "default", + }, + "type": "heading", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH5.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH5.json new file mode 100644 index 0000000000..9f69aff1a8 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH5.json @@ -0,0 +1,21 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Heading 5", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "isToggleable": false, + "level": 5, + "textAlignment": "left", + "textColor": "default", + }, + "type": "heading", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH6.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH6.json new file mode 100644 index 0000000000..c01c6b3437 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingH6.json @@ -0,0 +1,21 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Heading 6", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "isToggleable": false, + "level": 6, + "textAlignment": "left", + "textColor": "default", + }, + "type": "heading", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingThenCode.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingThenCode.json new file mode 100644 index 0000000000..d32797e0b3 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingThenCode.json @@ -0,0 +1,36 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Code Section", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "isToggleable": false, + "level": 2, + "textAlignment": "left", + "textColor": "default", + }, + "type": "heading", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "x = 42", + "type": "text", + }, + ], + "id": "2", + "props": { + "language": "python", + }, + "type": "codeBlock", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingWithInlineStyles.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingWithInlineStyles.json new file mode 100644 index 0000000000..61518fb6d7 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/headingWithInlineStyles.json @@ -0,0 +1,52 @@ +[ + { + "children": [], + "content": [ + { + "styles": { + "bold": true, + }, + "text": "Bold", + "type": "text", + }, + { + "styles": {}, + "text": " ", + "type": "text", + }, + { + "styles": { + "italic": true, + }, + "text": "Italic", + "type": "text", + }, + { + "styles": {}, + "text": " ", + "type": "text", + }, + { + "styles": { + "strike": true, + }, + "text": "Strike", + "type": "text", + }, + { + "styles": {}, + "text": " Heading", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "isToggleable": false, + "level": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "heading", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/horizontalRuleAsterisks.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/horizontalRuleAsterisks.json new file mode 100644 index 0000000000..6d2e457fd8 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/horizontalRuleAsterisks.json @@ -0,0 +1,43 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Paragraph above", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, + { + "children": [], + "content": undefined, + "id": "2", + "props": {}, + "type": "divider", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Paragraph below", + "type": "text", + }, + ], + "id": "3", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/horizontalRuleDashes.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/horizontalRuleDashes.json new file mode 100644 index 0000000000..6d2e457fd8 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/horizontalRuleDashes.json @@ -0,0 +1,43 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Paragraph above", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, + { + "children": [], + "content": undefined, + "id": "2", + "props": {}, + "type": "divider", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Paragraph below", + "type": "text", + }, + ], + "id": "3", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/horizontalRuleUnderscores.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/horizontalRuleUnderscores.json new file mode 100644 index 0000000000..6d2e457fd8 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/horizontalRuleUnderscores.json @@ -0,0 +1,43 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Paragraph above", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, + { + "children": [], + "content": undefined, + "id": "2", + "props": {}, + "type": "divider", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Paragraph below", + "type": "text", + }, + ], + "id": "3", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/imageWithAlt.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/imageWithAlt.json new file mode 100644 index 0000000000..7f7ff95459 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/imageWithAlt.json @@ -0,0 +1,16 @@ +[ + { + "children": [], + "content": undefined, + "id": "1", + "props": { + "backgroundColor": "default", + "caption": "", + "name": "Alt text for image", + "showPreview": true, + "textAlignment": "left", + "url": "https://example.com/photo.jpg", + }, + "type": "image", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/inlineCode.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/inlineCode.json new file mode 100644 index 0000000000..7e02a1406d --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/inlineCode.json @@ -0,0 +1,31 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "This has ", + "type": "text", + }, + { + "styles": { + "code": true, + }, + "text": "inline code", + "type": "text", + }, + { + "styles": {}, + "text": " in it", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/inlineCodeWithSpecialChars.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/inlineCodeWithSpecialChars.json new file mode 100644 index 0000000000..8b77e45fc7 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/inlineCodeWithSpecialChars.json @@ -0,0 +1,31 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Use ", + "type": "text", + }, + { + "styles": { + "code": true, + }, + "text": "const x = 42;", + "type": "text", + }, + { + "styles": {}, + "text": " to declare", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/inlineImage.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/inlineImage.json new file mode 100644 index 0000000000..35a0bc23d1 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/inlineImage.json @@ -0,0 +1,19 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Text before text after", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/italicOnly.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/italicOnly.json new file mode 100644 index 0000000000..01ec89cd69 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/italicOnly.json @@ -0,0 +1,21 @@ +[ + { + "children": [], + "content": [ + { + "styles": { + "italic": true, + }, + "text": "Italic text", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/italicUnderscore.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/italicUnderscore.json new file mode 100644 index 0000000000..3e39b28872 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/italicUnderscore.json @@ -0,0 +1,21 @@ +[ + { + "children": [], + "content": [ + { + "styles": { + "italic": true, + }, + "text": "Italic with underscores", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/linkAndText.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/linkAndText.json new file mode 100644 index 0000000000..4e6bd86d51 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/linkAndText.json @@ -0,0 +1,35 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Before ", + "type": "text", + }, + { + "content": [ + { + "styles": {}, + "text": "Link", + "type": "text", + }, + ], + "href": "https://example.com", + "type": "link", + }, + { + "styles": {}, + "text": " after", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/linkBasic.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/linkBasic.json new file mode 100644 index 0000000000..2d0b7cff77 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/linkBasic.json @@ -0,0 +1,25 @@ +[ + { + "children": [], + "content": [ + { + "content": [ + { + "styles": {}, + "text": "Example", + "type": "text", + }, + ], + "href": "https://example.com", + "type": "link", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/linkInParagraph.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/linkInParagraph.json new file mode 100644 index 0000000000..0509ca27e9 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/linkInParagraph.json @@ -0,0 +1,35 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Check out ", + "type": "text", + }, + { + "content": [ + { + "styles": {}, + "text": "this link", + "type": "text", + }, + ], + "href": "https://example.com", + "type": "link", + }, + { + "styles": {}, + "text": " for more info.", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/linkWithStyledContent.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/linkWithStyledContent.json new file mode 100644 index 0000000000..17d11b4dde --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/linkWithStyledContent.json @@ -0,0 +1,27 @@ +[ + { + "children": [], + "content": [ + { + "content": [ + { + "styles": { + "bold": true, + }, + "text": "Bold link", + "type": "text", + }, + ], + "href": "https://example.com", + "type": "link", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/listWithStyledItems.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/listWithStyledItems.json new file mode 100644 index 0000000000..95ff31e2d2 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/listWithStyledItems.json @@ -0,0 +1,83 @@ +[ + { + "children": [], + "content": [ + { + "styles": { + "bold": true, + }, + "text": "Bold item", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, + { + "children": [], + "content": [ + { + "styles": { + "italic": true, + }, + "text": "Italic item", + "type": "text", + }, + ], + "id": "2", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, + { + "children": [], + "content": [ + { + "styles": { + "strike": true, + }, + "text": "Strikethrough item", + "type": "text", + }, + ], + "id": "3", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Item with ", + "type": "text", + }, + { + "styles": { + "code": true, + }, + "text": "code", + "type": "text", + }, + ], + "id": "4", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/mixedInlineContent.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/mixedInlineContent.json new file mode 100644 index 0000000000..71ff4f8a21 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/mixedInlineContent.json @@ -0,0 +1,78 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Normal ", + "type": "text", + }, + { + "styles": { + "bold": true, + }, + "text": "bold", + "type": "text", + }, + { + "styles": {}, + "text": " ", + "type": "text", + }, + { + "styles": { + "italic": true, + }, + "text": "italic", + "type": "text", + }, + { + "styles": {}, + "text": " ", + "type": "text", + }, + { + "styles": { + "strike": true, + }, + "text": "strike", + "type": "text", + }, + { + "styles": {}, + "text": " ", + "type": "text", + }, + { + "styles": { + "code": true, + }, + "text": "code", + "type": "text", + }, + { + "styles": {}, + "text": " ", + "type": "text", + }, + { + "content": [ + { + "styles": {}, + "text": "link", + "type": "text", + }, + ], + "href": "https://example.com", + "type": "link", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/mixedListTypes.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/mixedListTypes.json new file mode 100644 index 0000000000..a877f90459 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/mixedListTypes.json @@ -0,0 +1,90 @@ +[ + { + "children": [ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Numbered child", + "type": "text", + }, + ], + "id": "2", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "numberedListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Another numbered", + "type": "text", + }, + ], + "id": "3", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "numberedListItem", + }, + ], + "content": [ + { + "styles": {}, + "text": "Bullet item", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, + { + "children": [ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Check child", + "type": "text", + }, + ], + "id": "5", + "props": { + "backgroundColor": "default", + "checked": false, + "textAlignment": "left", + "textColor": "default", + }, + "type": "checkListItem", + }, + ], + "content": [ + { + "styles": {}, + "text": "Another bullet", + "type": "text", + }, + ], + "id": "4", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/multipleImages.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/multipleImages.json new file mode 100644 index 0000000000..11f15e765d --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/multipleImages.json @@ -0,0 +1,30 @@ +[ + { + "children": [], + "content": undefined, + "id": "1", + "props": { + "backgroundColor": "default", + "caption": "", + "name": "First", + "showPreview": true, + "textAlignment": "left", + "url": "https://example.com/first.png", + }, + "type": "image", + }, + { + "children": [], + "content": undefined, + "id": "2", + "props": { + "backgroundColor": "default", + "caption": "", + "name": "Second", + "showPreview": true, + "textAlignment": "left", + "url": "https://example.com/second.png", + }, + "type": "image", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/multipleParagraphs.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/multipleParagraphs.json new file mode 100644 index 0000000000..de4db270fe --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/multipleParagraphs.json @@ -0,0 +1,53 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "First paragraph", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Second paragraph", + "type": "text", + }, + ], + "id": "2", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Third paragraph", + "type": "text", + }, + ], + "id": "3", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/nestedBulletLists.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/nestedBulletLists.json new file mode 100644 index 0000000000..d2d96963e9 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/nestedBulletLists.json @@ -0,0 +1,89 @@ +[ + { + "children": [ + { + "children": [ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Deep nested", + "type": "text", + }, + ], + "id": "3", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, + ], + "content": [ + { + "styles": {}, + "text": "Nested 1", + "type": "text", + }, + ], + "id": "2", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Nested 2", + "type": "text", + }, + ], + "id": "4", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, + ], + "content": [ + { + "styles": {}, + "text": "Item 1", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Item 2", + "type": "text", + }, + ], + "id": "5", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/nestedEmphasis.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/nestedEmphasis.json new file mode 100644 index 0000000000..2e5c63d508 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/nestedEmphasis.json @@ -0,0 +1,22 @@ +[ + { + "children": [], + "content": [ + { + "styles": { + "bold": true, + "italic": true, + }, + "text": "bold and italic", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/nestedEmphasisComplex.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/nestedEmphasisComplex.json new file mode 100644 index 0000000000..717e251a84 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/nestedEmphasisComplex.json @@ -0,0 +1,36 @@ +[ + { + "children": [], + "content": [ + { + "styles": { + "bold": true, + }, + "text": "bold ", + "type": "text", + }, + { + "styles": { + "bold": true, + "italic": true, + }, + "text": "bold and italic", + "type": "text", + }, + { + "styles": { + "bold": true, + }, + "text": " bold", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/nestedOrderedLists.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/nestedOrderedLists.json new file mode 100644 index 0000000000..301593b562 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/nestedOrderedLists.json @@ -0,0 +1,71 @@ +[ + { + "children": [ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Sub first", + "type": "text", + }, + ], + "id": "2", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "numberedListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Sub second", + "type": "text", + }, + ], + "id": "3", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "numberedListItem", + }, + ], + "content": [ + { + "styles": {}, + "text": "First", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "numberedListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Second", + "type": "text", + }, + ], + "id": "4", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "numberedListItem", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/onlyWhitespace.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/onlyWhitespace.json new file mode 100644 index 0000000000..45f0949abe --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/onlyWhitespace.json @@ -0,0 +1,13 @@ +[ + { + "children": [], + "content": [], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/orderedListStart.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/orderedListStart.json new file mode 100644 index 0000000000..35d2df68a3 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/orderedListStart.json @@ -0,0 +1,54 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Third item", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "start": 3, + "textAlignment": "left", + "textColor": "default", + }, + "type": "numberedListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Fourth item", + "type": "text", + }, + ], + "id": "2", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "numberedListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Fifth item", + "type": "text", + }, + ], + "id": "3", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "numberedListItem", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/paragraphContinuation.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/paragraphContinuation.json new file mode 100644 index 0000000000..6f7169f109 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/paragraphContinuation.json @@ -0,0 +1,19 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Line one still same paragraph", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/setextH1.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/setextH1.json new file mode 100644 index 0000000000..27be2c1d52 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/setextH1.json @@ -0,0 +1,21 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Heading 1", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "isToggleable": false, + "level": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "heading", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/setextH2.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/setextH2.json new file mode 100644 index 0000000000..2cb042a21b --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/setextH2.json @@ -0,0 +1,21 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Heading 2", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "isToggleable": false, + "level": 2, + "textAlignment": "left", + "textColor": "default", + }, + "type": "heading", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/strikethroughOnly.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/strikethroughOnly.json new file mode 100644 index 0000000000..7f504a4b3f --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/strikethroughOnly.json @@ -0,0 +1,21 @@ +[ + { + "children": [], + "content": [ + { + "styles": { + "strike": true, + }, + "text": "Strikethrough text", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableAlignment.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableAlignment.json new file mode 100644 index 0000000000..35d1354f9f --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableAlignment.json @@ -0,0 +1,132 @@ +[ + { + "children": [], + "content": { + "columnWidths": [ + undefined, + undefined, + undefined, + ], + "headerCols": undefined, + "headerRows": 1, + "rows": [ + { + "cells": [ + { + "content": [ + { + "styles": {}, + "text": "Left", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": {}, + "text": "Center", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": {}, + "text": "Right", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + ], + }, + { + "cells": [ + { + "content": [ + { + "styles": {}, + "text": "L", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": {}, + "text": "C", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": {}, + "text": "R", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + ], + }, + ], + "type": "tableContent", + }, + "id": "1", + "props": { + "textColor": "default", + }, + "type": "table", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableBasic.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableBasic.json new file mode 100644 index 0000000000..7a5d236441 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableBasic.json @@ -0,0 +1,135 @@ +[ + { + "children": [], + "content": { + "columnWidths": [ + undefined, + undefined, + ], + "headerCols": undefined, + "headerRows": 1, + "rows": [ + { + "cells": [ + { + "content": [ + { + "styles": {}, + "text": "Header 1", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": {}, + "text": "Header 2", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + ], + }, + { + "cells": [ + { + "content": [ + { + "styles": {}, + "text": "Cell 1", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": {}, + "text": "Cell 2", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + ], + }, + { + "cells": [ + { + "content": [ + { + "styles": {}, + "text": "Cell 3", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": {}, + "text": "Cell 4", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + ], + }, + ], + "type": "tableContent", + }, + "id": "1", + "props": { + "textColor": "default", + }, + "type": "table", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableFollowedByParagraph.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableFollowedByParagraph.json new file mode 100644 index 0000000000..36896f03df --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableFollowedByParagraph.json @@ -0,0 +1,114 @@ +[ + { + "children": [], + "content": { + "columnWidths": [ + undefined, + undefined, + ], + "headerCols": undefined, + "headerRows": 1, + "rows": [ + { + "cells": [ + { + "content": [ + { + "styles": {}, + "text": "Col 1", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": {}, + "text": "Col 2", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + ], + }, + { + "cells": [ + { + "content": [ + { + "styles": {}, + "text": "A", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": {}, + "text": "B", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + ], + }, + ], + "type": "tableContent", + }, + "id": "1", + "props": { + "textColor": "default", + }, + "type": "table", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Paragraph after table", + "type": "text", + }, + ], + "id": "2", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableThreeColumns.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableThreeColumns.json new file mode 100644 index 0000000000..e235519185 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableThreeColumns.json @@ -0,0 +1,132 @@ +[ + { + "children": [], + "content": { + "columnWidths": [ + undefined, + undefined, + undefined, + ], + "headerCols": undefined, + "headerRows": 1, + "rows": [ + { + "cells": [ + { + "content": [ + { + "styles": {}, + "text": "A", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": {}, + "text": "B", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": {}, + "text": "C", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + ], + }, + { + "cells": [ + { + "content": [ + { + "styles": {}, + "text": "1", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": {}, + "text": "2", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": {}, + "text": "3", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + ], + }, + ], + "type": "tableContent", + }, + "id": "1", + "props": { + "textColor": "default", + }, + "type": "table", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableWithInlineFormatting.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableWithInlineFormatting.json new file mode 100644 index 0000000000..7d0cb2bfe3 --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableWithInlineFormatting.json @@ -0,0 +1,141 @@ +[ + { + "children": [], + "content": { + "columnWidths": [ + undefined, + undefined, + ], + "headerCols": undefined, + "headerRows": 1, + "rows": [ + { + "cells": [ + { + "content": [ + { + "styles": {}, + "text": "Header", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": {}, + "text": "Styled", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + ], + }, + { + "cells": [ + { + "content": [ + { + "styles": {}, + "text": "Normal", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": { + "bold": true, + }, + "text": "Bold", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + ], + }, + { + "cells": [ + { + "content": [ + { + "styles": { + "italic": true, + }, + "text": "Italic", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": { + "strike": true, + }, + "text": "Strike", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + ], + }, + ], + "type": "tableContent", + }, + "id": "1", + "props": { + "textColor": "default", + }, + "type": "table", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableWithLinks.json b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableWithLinks.json new file mode 100644 index 0000000000..7ac6eab9fb --- /dev/null +++ b/tests/src/unit/core/formatConversion/parse/__snapshots__/markdown/tableWithLinks.json @@ -0,0 +1,103 @@ +[ + { + "children": [], + "content": { + "columnWidths": [ + undefined, + undefined, + ], + "headerCols": undefined, + "headerRows": 1, + "rows": [ + { + "cells": [ + { + "content": [ + { + "styles": {}, + "text": "Name", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "styles": {}, + "text": "Link", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + ], + }, + { + "cells": [ + { + "content": [ + { + "styles": {}, + "text": "Example", + "type": "text", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + { + "content": [ + { + "content": [ + { + "styles": {}, + "text": "Click", + "type": "text", + }, + ], + "href": "https://example.com", + "type": "link", + }, + ], + "props": { + "backgroundColor": "default", + "colspan": 1, + "rowspan": 1, + "textAlignment": "left", + "textColor": "default", + }, + "type": "tableCell", + }, + ], + }, + ], + "type": "tableContent", + }, + "id": "1", + "props": { + "textColor": "default", + }, + "type": "table", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/parse/parseTestInstances.ts b/tests/src/unit/core/formatConversion/parse/parseTestInstances.ts index 266d87a68a..83ddcf10aa 100644 --- a/tests/src/unit/core/formatConversion/parse/parseTestInstances.ts +++ b/tests/src/unit/core/formatConversion/parse/parseTestInstances.ts @@ -1279,4 +1279,595 @@ Regular paragraph`, }, executeTest: testParseMarkdown, }, + // Individual heading levels + { + testCase: { + name: "headingH1", + content: `# Heading 1`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "headingH2", + content: `## Heading 2`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "headingH3", + content: `### Heading 3`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "headingH4", + content: `#### Heading 4`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "headingH5", + content: `##### Heading 5`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "headingH6", + content: `###### Heading 6`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "headingWithInlineStyles", + content: `# **Bold** *Italic* ~~Strike~~ Heading`, + }, + executeTest: testParseMarkdown, + }, + // Setext headings + { + testCase: { + name: "setextH1", + content: `Heading 1 +===`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "setextH2", + content: `Heading 2 +---`, + }, + executeTest: testParseMarkdown, + }, + // Code blocks + { + testCase: { + name: "codeBlockBasic", + content: `\`\`\` +console.log('Hello'); +\`\`\``, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "codeBlockWithLanguage", + content: `\`\`\`javascript +const x = 42; +console.log(x); +\`\`\``, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "codeBlockPython", + content: `\`\`\`python +def hello(): + print("Hello, world!") +\`\`\``, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "codeBlockWithSpecialChars", + content: `\`\`\`html +
    +

    Hello **not bold**

    +
    +\`\`\``, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "codeBlockTildes", + content: `~~~ +code with tildes +~~~`, + }, + executeTest: testParseMarkdown, + }, + // Horizontal rules + { + testCase: { + name: "horizontalRuleDashes", + content: `Paragraph above + +--- + +Paragraph below`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "horizontalRuleAsterisks", + content: `Paragraph above + +*** + +Paragraph below`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "horizontalRuleUnderscores", + content: `Paragraph above + +___ + +Paragraph below`, + }, + executeTest: testParseMarkdown, + }, + // Inline code + { + testCase: { + name: "inlineCode", + content: `This has \`inline code\` in it`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "inlineCodeWithSpecialChars", + content: `Use \`const x = 42;\` to declare`, + }, + executeTest: testParseMarkdown, + }, + // Links + { + testCase: { + name: "linkBasic", + content: `[Example](https://example.com)`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "linkInParagraph", + content: `Check out [this link](https://example.com) for more info.`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "linkWithStyledContent", + content: `[**Bold link**](https://example.com)`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "adjacentLinks", + content: `[Link1](https://example1.com)[Link2](https://example2.com)`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "linkAndText", + content: `Before [Link](https://example.com) after`, + }, + executeTest: testParseMarkdown, + }, + // Tables + { + testCase: { + name: "tableBasic", + content: `| Header 1 | Header 2 | +| -------- | -------- | +| Cell 1 | Cell 2 | +| Cell 3 | Cell 4 |`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "tableThreeColumns", + content: `| A | B | C | +| - | - | - | +| 1 | 2 | 3 |`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "tableWithInlineFormatting", + content: `| Header | Styled | +| ------ | ------ | +| Normal | **Bold** | +| *Italic* | ~~Strike~~ |`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "tableWithLinks", + content: `| Name | Link | +| ---- | ---- | +| Example | [Click](https://example.com) |`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "tableAlignment", + content: `| Left | Center | Right | +| :--- | :----: | ----: | +| L | C | R |`, + }, + executeTest: testParseMarkdown, + }, + // Task lists / check lists + { + testCase: { + name: "checkListBasic", + content: `- [ ] Unchecked item +- [x] Checked item +- [ ] Another unchecked`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "checkListMixed", + content: `- Regular bullet +- [ ] Check item +- [x] Checked item`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "checkListNested", + content: `- [ ] Parent item + - [x] Child checked + - [ ] Child unchecked`, + }, + executeTest: testParseMarkdown, + }, + // Ordered list with start number + { + testCase: { + name: "orderedListStart", + content: `3. Third item +4. Fourth item +5. Fifth item`, + }, + executeTest: testParseMarkdown, + }, + // Hard breaks + { + testCase: { + name: "hardBreakBackslash", + content: `Line one\\ +Line two`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "hardBreakMultiple", + content: `Line one\\ +Line two\\ +Line three`, + }, + executeTest: testParseMarkdown, + }, + // Backslash escapes + { + testCase: { + name: "backslashEscapes", + content: `\\*not bold\\* \\[not a link\\] \\~not strike\\~`, + }, + executeTest: testParseMarkdown, + }, + // Nested emphasis + { + testCase: { + name: "nestedEmphasis", + content: `***bold and italic***`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "nestedEmphasisComplex", + content: `**bold *bold and italic* bold**`, + }, + executeTest: testParseMarkdown, + }, + // Individual styles + { + testCase: { + name: "boldOnly", + content: `**Bold text**`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "italicOnly", + content: `*Italic text*`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "strikethroughOnly", + content: `~~Strikethrough text~~`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "boldUnderscore", + content: `__Bold with underscores__`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "italicUnderscore", + content: `_Italic with underscores_`, + }, + executeTest: testParseMarkdown, + }, + // Mixed inline content + { + testCase: { + name: "mixedInlineContent", + content: `Normal **bold** *italic* ~~strike~~ \`code\` [link](https://example.com)`, + }, + executeTest: testParseMarkdown, + }, + // Multiple paragraphs + { + testCase: { + name: "multipleParagraphs", + content: `First paragraph + +Second paragraph + +Third paragraph`, + }, + executeTest: testParseMarkdown, + }, + // Empty content + { + testCase: { + name: "emptyString", + content: ``, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "onlyWhitespace", + content: ` + + `, + }, + executeTest: testParseMarkdown, + }, + // Paragraph continuation (lines without blank line) + { + testCase: { + name: "paragraphContinuation", + content: `Line one +still same paragraph`, + }, + executeTest: testParseMarkdown, + }, + // Nested lists - complex + { + testCase: { + name: "nestedBulletLists", + content: `- Item 1 + - Nested 1 + - Deep nested + - Nested 2 +- Item 2`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "nestedOrderedLists", + content: `1. First + 1. Sub first + 2. Sub second +2. Second`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "mixedListTypes", + content: `- Bullet item + 1. Numbered child + 2. Another numbered +- Another bullet + - [ ] Check child`, + }, + executeTest: testParseMarkdown, + }, + // Blockquote with multiple blocks + { + testCase: { + name: "blockquoteMultiline", + content: `> Line one +> Line two +> Line three`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "blockquoteWithCode", + content: `> Quote with \`inline code\` inside`, + }, + executeTest: testParseMarkdown, + }, + { + testCase: { + name: "blockquoteWithLink", + content: `> Quote with [a link](https://example.com) inside`, + }, + executeTest: testParseMarkdown, + }, + // Complex document + { + testCase: { + name: "complexDocument", + content: `# Main Title + +An introduction paragraph with **bold** and *italic* text. + +## Section 1 + +- First bullet point +- Second bullet point + - Nested point + +> A notable quote + +### Code Example + +\`\`\`javascript +function hello() { + return "world"; +} +\`\`\` + +--- + +## Section 2 + +1. Step one +2. Step two +3. Step three + +| Feature | Status | +| ------- | ------ | +| Bold | Done | +| Italic | Done | + +![Image](https://example.com/image.png) + +Final paragraph with [a link](https://example.com).`, + }, + executeTest: testParseMarkdown, + }, + // Image with alt text + { + testCase: { + name: "imageWithAlt", + content: `![Alt text for image](https://example.com/photo.jpg)`, + }, + executeTest: testParseMarkdown, + }, + // Multiple images + { + testCase: { + name: "multipleImages", + content: `![First](https://example.com/first.png) + +![Second](https://example.com/second.png)`, + }, + executeTest: testParseMarkdown, + }, + // Inline image within text (should be handled) + { + testCase: { + name: "inlineImage", + content: `Text before ![inline](https://example.com/img.png) text after`, + }, + executeTest: testParseMarkdown, + }, + // Code block immediately after heading + { + testCase: { + name: "headingThenCode", + content: `## Code Section + +\`\`\`python +x = 42 +\`\`\``, + }, + executeTest: testParseMarkdown, + }, + // List with styled items + { + testCase: { + name: "listWithStyledItems", + content: `- **Bold item** +- *Italic item* +- ~~Strikethrough item~~ +- Item with \`code\``, + }, + executeTest: testParseMarkdown, + }, + // Deeply nested lists + { + testCase: { + name: "deeplyNestedLists", + content: `- Level 1 + - Level 2 + - Level 3 + - Level 4`, + }, + executeTest: testParseMarkdown, + }, + // Table followed by paragraph + { + testCase: { + name: "tableFollowedByParagraph", + content: `| Col 1 | Col 2 | +| ----- | ----- | +| A | B | + +Paragraph after table`, + }, + executeTest: testParseMarkdown, + }, + // Paragraphs with various inline formatting + { + testCase: { + name: "adjacentFormattedRuns", + content: `**bold***italic*~~strike~~`, + }, + executeTest: testParseMarkdown, + }, ]; diff --git a/tests/src/unit/shared/formatConversion/exportParseEquality/exportParseEqualityTestExecutors.ts b/tests/src/unit/shared/formatConversion/exportParseEquality/exportParseEqualityTestExecutors.ts index a42f7c7c4b..0606d7dd85 100644 --- a/tests/src/unit/shared/formatConversion/exportParseEquality/exportParseEqualityTestExecutors.ts +++ b/tests/src/unit/shared/formatConversion/exportParseEquality/exportParseEqualityTestExecutors.ts @@ -63,6 +63,34 @@ export const testExportParseEqualityHTML = async < ); }; +export const testExportParseEqualityMarkdown = async < + B extends BlockSchema, + I extends InlineContentSchema, + S extends StyleSchema, +>( + editor: BlockNoteEditor, + testCase: ExportParseEqualityTestCase, +) => { + (window as any).__TEST_OPTIONS.mockID = 0; + + addIdsToBlocks(testCase.content); + + const exported = await editor.blocksToMarkdownLossy(testCase.content); + + // Reset mock ID as we don't expect block IDs to be preserved in this + // conversion. + (window as any).__TEST_OPTIONS.mockID = 0; + + // Markdown is lossy (colors, underline, alignment are dropped), so we use + // snapshot matching to capture the expected round-trip result rather than + // strict equality with the input. + await expect( + await editor.tryParseMarkdownToBlocks(exported), + ).toMatchFileSnapshot( + `./__snapshots__/markdown/${testCase.name}.json`, + ); +}; + export const testExportParseEqualityNodes = async < B extends BlockSchema, I extends InlineContentSchema, From f9da2df5fcbf730c2eb940af231c8af7c623b78f Mon Sep 17 00:00:00 2001 From: Nick the Sick Date: Fri, 3 Apr 2026 10:52:24 +0200 Subject: [PATCH 2/4] fix: resolve lint errors in custom markdown parser/serializer Add curly braces to single-line if/while statements and convert inner function declaration to arrow function to fix no-inner-declarations error. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../api/exporters/markdown/htmlToMarkdown.ts | 28 ++++----- .../api/parsers/markdown/markdownToHtml.ts | 60 +++++++++---------- 2 files changed, 44 insertions(+), 44 deletions(-) diff --git a/packages/core/src/api/exporters/markdown/htmlToMarkdown.ts b/packages/core/src/api/exporters/markdown/htmlToMarkdown.ts index be663a9b70..a18e75312e 100644 --- a/packages/core/src/api/exporters/markdown/htmlToMarkdown.ts +++ b/packages/core/src/api/exporters/markdown/htmlToMarkdown.ts @@ -143,7 +143,7 @@ function serializeBlockquote(el: HTMLElement, ctx: SerializeContext): string { function serializeCodeBlock(el: HTMLElement, ctx: SerializeContext): string { const codeEl = el.querySelector("code"); - if (!codeEl) return ""; + if (!codeEl) {return "";} const language = codeEl.getAttribute("data-language") || @@ -283,7 +283,7 @@ function serializeListItem( if (details) { const summary = details.querySelector("summary"); for (const child of Array.from(details.children)) { - if (child === summary) continue; + if (child === summary) {continue;} const childTag = child.tagName.toLowerCase(); if (childTag === "p") { const content = serializeInlineContent(child as HTMLElement); @@ -299,8 +299,8 @@ function serializeListItem( const childTag = child.tagName.toLowerCase(); // Skip the first content element and checkbox - if (child === firstContentEl || (child as HTMLElement) === checkbox) continue; - if (childTag === "input") continue; + if (child === firstContentEl || (child as HTMLElement) === checkbox) {continue;} + if (childTag === "input") {continue;} // Nested lists and other block content if (childTag === "ul" || childTag === "ol") { @@ -322,10 +322,10 @@ function getFirstContentElement( checkbox: HTMLInputElement | null ): HTMLElement | null { for (const child of Array.from(li.children)) { - if (child === checkbox) continue; - if (child.tagName.toLowerCase() === "input") continue; + if (child === checkbox) {continue;} + if (child.tagName.toLowerCase() === "input") {continue;} const tag = child.tagName.toLowerCase(); - if (tag === "p" || tag === "span") return child as HTMLElement; + if (tag === "p" || tag === "span") {return child as HTMLElement;} } return null; } @@ -350,13 +350,13 @@ function serializeTable(el: HTMLElement, ctx: SerializeContext): string { const grid: (string | null)[][] = []; trElements.forEach((tr, rowIdx) => { - if (!grid[rowIdx]) grid[rowIdx] = []; + if (!grid[rowIdx]) {grid[rowIdx] = [];} const cellElements = tr.querySelectorAll("th, td"); let gridCol = 0; cellElements.forEach((cell) => { // Find next empty column in this row - while (grid[rowIdx][gridCol] !== undefined) gridCol++; + while (grid[rowIdx][gridCol] !== undefined) {gridCol++;} if (rowIdx === 0 && cell.tagName.toLowerCase() === "th") { hasHeader = true; @@ -370,7 +370,7 @@ function serializeTable(el: HTMLElement, ctx: SerializeContext): string { for (let r = 0; r < rowspan; r++) { for (let c = 0; c < colspan; c++) { const ri = rowIdx + r; - if (!grid[ri]) grid[ri] = []; + if (!grid[ri]) {grid[ri] = [];} grid[ri][gridCol + c] = r === 0 && c === 0 ? content : ""; } } @@ -393,7 +393,7 @@ function serializeTable(el: HTMLElement, ctx: SerializeContext): string { rows.push(row); } - if (rows.length === 0) return ""; + if (rows.length === 0) {return "";} // Determine column widths const colWidths: number[] = []; @@ -475,7 +475,7 @@ function serializeVideo(el: HTMLElement, ctx: SerializeContext): string { function serializeAudio(el: HTMLElement, ctx: SerializeContext): string { const src = el.getAttribute("src") || ""; - if (!src) return ""; + if (!src) {return "";} // Audio has no visible representation in markdown; output as link with empty text return ctx.indent + `[](${src})\n\n`; } @@ -521,14 +521,14 @@ function serializeFigure(el: HTMLElement, ctx: SerializeContext): string { function serializeBlockLink(el: HTMLElement, ctx: SerializeContext): string { const href = el.getAttribute("href") || ""; const text = el.textContent?.trim() || ""; - if (!href) return ctx.indent + text + "\n\n"; + if (!href) {return ctx.indent + text + "\n\n";} return ctx.indent + `[${text}](${href})\n\n`; } function serializeDetails(el: HTMLElement, ctx: SerializeContext): string { // Toggle heading or toggle list item const summary = el.querySelector("summary"); - if (!summary) return serializeChildren(el, ctx); + if (!summary) {return serializeChildren(el, ctx);} // Check if summary contains a heading const heading = summary.querySelector("h1, h2, h3, h4, h5, h6"); diff --git a/packages/core/src/api/parsers/markdown/markdownToHtml.ts b/packages/core/src/api/parsers/markdown/markdownToHtml.ts index b4ad425f09..4b7307f9dd 100644 --- a/packages/core/src/api/parsers/markdown/markdownToHtml.ts +++ b/packages/core/src/api/parsers/markdown/markdownToHtml.ts @@ -208,13 +208,13 @@ function parseImage( // ![alt](url) or ![alt](url "title") const altStart = start + 2; // after ![ const altEnd = text.indexOf("]", altStart); - if (altEnd === -1) return null; + if (altEnd === -1) {return null;} - if (text[altEnd + 1] !== "(") return null; + if (text[altEnd + 1] !== "(") {return null;} const urlStart = altEnd + 2; const parenEnd = findClosingParen(text, urlStart - 1); - if (parenEnd === -1) return null; + if (parenEnd === -1) {return null;} const alt = text.substring(altStart, altEnd); let urlContent = text.substring(urlStart, parenEnd).trim(); @@ -250,13 +250,13 @@ function parseLink( // [text](url) const textStart = start + 1; const textEnd = findClosingBracket(text, start); - if (textEnd === -1) return null; + if (textEnd === -1) {return null;} - if (text[textEnd + 1] !== "(") return null; + if (text[textEnd + 1] !== "(") {return null;} const urlStart = textEnd + 2; const parenEnd = findClosingParen(text, textEnd + 1); - if (parenEnd === -1) return null; + if (parenEnd === -1) {return null;} const linkText = text.substring(textStart, textEnd); const url = text.substring(urlStart, parenEnd).trim(); @@ -274,10 +274,10 @@ function findClosingBracket(text: string, openPos: number): number { i++; // skip escaped continue; } - if (text[i] === "[") depth++; + if (text[i] === "[") {depth++;} if (text[i] === "]") { depth--; - if (depth === 0) return i; + if (depth === 0) {return i;} } } return -1; @@ -290,10 +290,10 @@ function findClosingParen(text: string, openPos: number): number { i++; continue; } - if (text[i] === "(") depth++; + if (text[i] === "(") {depth++;} if (text[i] === ")") { depth--; - if (depth === 0) return i; + if (depth === 0) {return i;} } } return -1; @@ -309,10 +309,10 @@ function parseDelimited( const len = delimiter.length; const afterOpen = start + len; - if (afterOpen >= text.length) return null; + if (afterOpen >= text.length) {return null;} // Opening delimiter must not be followed by whitespace - if (text[afterOpen] === " " || text[afterOpen] === "\t") return null; + if (text[afterOpen] === " " || text[afterOpen] === "\t") {return null;} // Find closing delimiter let j = afterOpen; @@ -580,11 +580,11 @@ function tokenize(markdown: string): Token[] { const minChildIndent = indent + 1; // Helper to check if a line belongs to this list item - function belongsToItem(lineStr: string): boolean { - if (lineStr.trim() === "") return true; // blank lines checked separately + const belongsToItem = (lineStr: string): boolean => { + if (lineStr.trim() === "") {return true;} // blank lines checked separately const lineInd = lineStr.match(/^\s*/)![0].length; // Lines at contentIndent are continuation text - if (lineInd >= contentIndent) return true; + if (lineInd >= contentIndent) {return true;} // Lines between marker and content column that start a sub-list if ( lineInd >= minChildIndent && @@ -615,7 +615,7 @@ function tokenize(markdown: string): Token[] { break; } - if (!belongsToItem(cur)) break; + if (!belongsToItem(cur)) {break;} // Strip indent: for lines at contentIndent+, strip contentIndent chars; // for sub-list lines between minChildIndent and contentIndent, strip minChildIndent @@ -652,14 +652,14 @@ function tokenize(markdown: string): Token[] { while (i < lines.length) { const nextLine = lines[i]; // Stop paragraph on blank line - if (nextLine.trim() === "") break; + if (nextLine.trim() === "") {break;} // Stop on block-level element - if (/^(#{1,6})\s/.test(nextLine)) break; - if (/^(`{3,}|~{3,})/.test(nextLine)) break; - if (/^\s{0,3}>/.test(nextLine)) break; - if (/^(\s{0,3})([-*_])\s*(\2\s*){2,}$/.test(nextLine)) break; - if (/^\s*([-*+]|\d+[.)])\s+/.test(nextLine)) break; - if (/^\s*\|(.+\|)+\s*$/.test(nextLine)) break; + if (/^(#{1,6})\s/.test(nextLine)) {break;} + if (/^(`{3,}|~{3,})/.test(nextLine)) {break;} + if (/^\s{0,3}>/.test(nextLine)) {break;} + if (/^(\s{0,3})([-*_])\s*(\2\s*){2,}$/.test(nextLine)) {break;} + if (/^\s*([-*+]|\d+[.)])\s+/.test(nextLine)) {break;} + if (/^\s*\|(.+\|)+\s*$/.test(nextLine)) {break;} // Check if next-next line is setext marker if ( i + 1 < lines.length && @@ -686,16 +686,16 @@ function tryParseTable( start: number ): { token: TableToken; nextLine: number } | null { // A table needs at least a header row and a separator row - if (start + 1 >= lines.length) return null; + if (start + 1 >= lines.length) {return null;} const headerLine = lines[start]; const separatorLine = lines[start + 1]; // Check separator line format: | --- | --- | or | :--- | ---: | - if (!/^\s*\|(\s*:?-+:?\s*\|)+\s*$/.test(separatorLine)) return null; + if (!/^\s*\|(\s*:?-+:?\s*\|)+\s*$/.test(separatorLine)) {return null;} // Check header line format: | ... | ... | - if (!/^\s*\|(.+\|)+\s*$/.test(headerLine)) return null; + if (!/^\s*\|(.+\|)+\s*$/.test(headerLine)) {return null;} const headers = parsePipeCells(headerLine); const alignments = parseAlignments(separatorLine); @@ -704,7 +704,7 @@ function tryParseTable( let i = start + 2; while (i < lines.length) { const line = lines[i]; - if (!/^\s*\|(.+\|)+\s*$/.test(line)) break; + if (!/^\s*\|(.+\|)+\s*$/.test(line)) {break;} rows.push(parsePipeCells(line)); i++; } @@ -757,9 +757,9 @@ function parseAlignments( const trimmed = cell.trim(); const left = trimmed.startsWith(":"); const right = trimmed.endsWith(":"); - if (left && right) return "center"; - if (right) return "right"; - if (left) return "left"; + if (left && right) {return "center";} + if (right) {return "right";} + if (left) {return "left";} return null; }); } From 837e36797b92c5c55e341bbc9b94264871b23358 Mon Sep 17 00:00:00 2001 From: Nick the Sick Date: Fri, 3 Apr 2026 12:12:04 +0200 Subject: [PATCH 3/4] fix: ensure custom markdown serializer works in Node.js/JSDOM environments Use document.createElement instead of DOMParser and numeric constants instead of Node.TEXT_NODE/ELEMENT_NODE for server-side compatibility. Update snapshots to match new serializer output. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../api/exporters/markdown/htmlToMarkdown.ts | 21 +-- .../api/parsers/markdown/markdownToHtml.ts | 30 +++- .../ServerBlockNoteEditor.test.ts.snap | 9 +- .../markdown/markdown/deeplyNestedLists.json | 144 ++++++++++++++++++ .../markdown/specialCharEscaping.json | 124 +++++++++++++++ .../exportParseEqualityTestInstances.ts | 103 +++++++++++++ 6 files changed, 417 insertions(+), 14 deletions(-) create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/deeplyNestedLists.json create mode 100644 tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/specialCharEscaping.json diff --git a/packages/core/src/api/exporters/markdown/htmlToMarkdown.ts b/packages/core/src/api/exporters/markdown/htmlToMarkdown.ts index a18e75312e..ca9266ef84 100644 --- a/packages/core/src/api/exporters/markdown/htmlToMarkdown.ts +++ b/packages/core/src/api/exporters/markdown/htmlToMarkdown.ts @@ -10,9 +10,12 @@ * Convert an HTML string (from BlockNote's external HTML exporter) to markdown. */ export function htmlToMarkdown(html: string): string { - const parser = new DOMParser(); - const doc = parser.parseFromString(html, "text/html"); - const result = serializeChildren(doc.body, { indent: "", inList: false }); + // Use a temporary element to parse HTML. This works in both browser and + // server (JSDOM) environments, unlike `new DOMParser()` which may not be + // globally available in Node.js. + const container = document.createElement("div"); + container.innerHTML = html; + const result = serializeChildren(container, { indent: "", inList: false }); return result.trim() + "\n"; } @@ -36,11 +39,11 @@ function serializeChildren(node: Node, ctx: SerializeContext): string { } function serializeNode(node: Node, ctx: SerializeContext): string { - if (node.nodeType === Node.TEXT_NODE) { + if (node.nodeType === 3 /* Node.TEXT_NODE */) { return node.textContent || ""; } - if (node.nodeType !== Node.ELEMENT_NODE) { + if (node.nodeType !== 1 /* Node.ELEMENT_NODE */) { return ""; } @@ -172,9 +175,9 @@ function serializeCodeBlock(el: HTMLElement, ctx: SerializeContext): string { function extractCodeContent(el: Element): string { let result = ""; for (const child of Array.from(el.childNodes)) { - if (child.nodeType === Node.TEXT_NODE) { + if (child.nodeType === 3 /* Node.TEXT_NODE */) { result += child.textContent || ""; - } else if (child.nodeType === Node.ELEMENT_NODE) { + } else if (child.nodeType === 1 /* Node.ELEMENT_NODE */) { const tag = (child as HTMLElement).tagName.toLowerCase(); if (tag === "br") { result += "\n"; @@ -553,9 +556,9 @@ function serializeInlineContent(el: Element): string { let result = ""; for (const child of Array.from(el.childNodes)) { - if (child.nodeType === Node.TEXT_NODE) { + if (child.nodeType === 3 /* Node.TEXT_NODE */) { result += child.textContent || ""; - } else if (child.nodeType === Node.ELEMENT_NODE) { + } else if (child.nodeType === 1 /* Node.ELEMENT_NODE */) { const childEl = child as HTMLElement; const tag = childEl.tagName.toLowerCase(); diff --git a/packages/core/src/api/parsers/markdown/markdownToHtml.ts b/packages/core/src/api/parsers/markdown/markdownToHtml.ts index 4b7307f9dd..54c346d764 100644 --- a/packages/core/src/api/parsers/markdown/markdownToHtml.ts +++ b/packages/core/src/api/parsers/markdown/markdownToHtml.ts @@ -16,6 +16,27 @@ function escapeHtml(str: string): string { .replace(/"/g, """); } +// ─── Helpers ───────────────────────────────────────────────────────────────── + +function isAlphanumeric(char: string | undefined): boolean { + if (!char) { + return false; + } + return /\w/.test(char); +} + +/** + * Returns true when an underscore delimiter at position `i` is "intraword", + * meaning the characters on both sides are alphanumeric (e.g. `snake_case`). + * In that case the underscore should NOT be treated as emphasis per CommonMark. + */ +function isIntraword(text: string, i: number, delimLen: number): boolean { + const before = i > 0 ? text[i - 1] : undefined; + const after = + i + delimLen < text.length ? text[i + delimLen] : undefined; + return isAlphanumeric(before) && isAlphanumeric(after); +} + // ─── Inline Parser ─────────────────────────────────────────────────────────── /** @@ -88,7 +109,10 @@ function parseInline(text: string): string { // Bold+Italic ***text*** or ___text___ if ( (text[i] === "*" && text[i + 1] === "*" && text[i + 2] === "*") || - (text[i] === "_" && text[i + 1] === "_" && text[i + 2] === "_") + (text[i] === "_" && + text[i + 1] === "_" && + text[i + 2] === "_" && + !isIntraword(text, i, 3)) ) { const delimiter = text.substring(i, i + 3); const tripleResult = parseDelimited( @@ -108,7 +132,7 @@ function parseInline(text: string): string { // Bold **text** or __text__ if ( (text[i] === "*" && text[i + 1] === "*") || - (text[i] === "_" && text[i + 1] === "_") + (text[i] === "_" && text[i + 1] === "_" && !isIntraword(text, i, 2)) ) { const delimiter = text.substring(i, i + 2); const boldResult = parseDelimited( @@ -126,7 +150,7 @@ function parseInline(text: string): string { } // Italic *text* or _text_ - if (text[i] === "*" || text[i] === "_") { + if (text[i] === "*" || (text[i] === "_" && !isIntraword(text, i, 1))) { const delimiter = text[i]; const italicResult = parseDelimited( text, diff --git a/packages/server-util/src/context/__snapshots__/ServerBlockNoteEditor.test.ts.snap b/packages/server-util/src/context/__snapshots__/ServerBlockNoteEditor.test.ts.snap index 1aec369bd8..db260108b3 100644 --- a/packages/server-util/src/context/__snapshots__/ServerBlockNoteEditor.test.ts.snap +++ b/packages/server-util/src/context/__snapshots__/ServerBlockNoteEditor.test.ts.snap @@ -129,7 +129,7 @@ exports[`Test ServerBlockNoteEditor > converts to and from HTML (blocksToHTMLLos `; exports[`Test ServerBlockNoteEditor > converts to and from markdown (blocksToMarkdownLossy) 1`] = ` -"## **Heading ***~~2~~* +"## **Heading** *~~2~~* Paragraph @@ -154,7 +154,12 @@ exports[`Test ServerBlockNoteEditor > converts to and from markdown (blocksToMar "styles": { "bold": true, }, - "text": "Heading ", + "text": "Heading", + "type": "text", + }, + { + "styles": {}, + "text": " ", "type": "text", }, { diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/deeplyNestedLists.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/deeplyNestedLists.json new file mode 100644 index 0000000000..f81fde4a33 --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/deeplyNestedLists.json @@ -0,0 +1,144 @@ +[ + { + "children": [ + { + "children": [ + { + "children": [ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Level 4 numbered", + "type": "text", + }, + ], + "id": "4", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "numberedListItem", + }, + ], + "content": [ + { + "styles": {}, + "text": "Level 3 bullet", + "type": "text", + }, + ], + "id": "3", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, + ], + "content": [ + { + "styles": {}, + "text": "Level 2 numbered", + "type": "text", + }, + ], + "id": "2", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "numberedListItem", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Level 2 sibling", + "type": "text", + }, + ], + "id": "5", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "numberedListItem", + }, + ], + "content": [ + { + "styles": {}, + "text": "Level 1 bullet", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, + { + "children": [ + { + "children": [ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Deep checklist item", + "type": "text", + }, + ], + "id": "8", + "props": { + "backgroundColor": "default", + "checked": false, + "textAlignment": "left", + "textColor": "default", + }, + "type": "checkListItem", + }, + ], + "content": [ + { + "styles": {}, + "text": "Child of second bullet", + "type": "text", + }, + ], + "id": "7", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, + ], + "content": [ + { + "styles": {}, + "text": "Another top-level bullet", + "type": "text", + }, + ], + "id": "6", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "bulletListItem", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/specialCharEscaping.json b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/specialCharEscaping.json new file mode 100644 index 0000000000..0ede1c2000 --- /dev/null +++ b/tests/src/unit/core/formatConversion/exportParseEquality/__snapshots__/markdown/markdown/specialCharEscaping.json @@ -0,0 +1,124 @@ +[ + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Literal ", + "type": "text", + }, + { + "styles": { + "italic": true, + }, + "text": "asterisks", + "type": "text", + }, + { + "styles": {}, + "text": " and ", + "type": "text", + }, + { + "styles": { + "bold": true, + }, + "text": "double asterisks", + "type": "text", + }, + ], + "id": "1", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Backticks ` in plain text and ", + "type": "text", + }, + { + "styles": { + "code": true, + }, + "text": "double", + "type": "text", + }, + ], + "id": "2", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Underscores ", + "type": "text", + }, + { + "styles": { + "italic": true, + }, + "text": "here", + "type": "text", + }, + { + "styles": {}, + "text": " and ~tildes~ and [brackets]", + "type": "text", + }, + ], + "id": "3", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "Pipes | and backslash \ and #hash at start", + "type": "text", + }, + ], + "id": "4", + "props": { + "backgroundColor": "default", + "textAlignment": "left", + "textColor": "default", + }, + "type": "paragraph", + }, + { + "children": [], + "content": [ + { + "styles": {}, + "text": "const x = `template ${literal}`; +const y = '```triple backticks```';", + "type": "text", + }, + ], + "id": "5", + "props": { + "language": "text", + }, + "type": "codeBlock", + }, +] \ No newline at end of file diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/exportParseEqualityTestInstances.ts b/tests/src/unit/core/formatConversion/exportParseEquality/exportParseEqualityTestInstances.ts index 8e0c87c313..0d52d05638 100644 --- a/tests/src/unit/core/formatConversion/exportParseEquality/exportParseEqualityTestInstances.ts +++ b/tests/src/unit/core/formatConversion/exportParseEquality/exportParseEqualityTestInstances.ts @@ -824,4 +824,107 @@ export const exportParseEqualityTestInstancesMarkdown: TestInstance< }, executeTest: testExportParseEqualityMarkdown, }, + { + testCase: { + name: "markdown/deeplyNestedLists", + content: [ + { + type: "bulletListItem", + content: "Level 1 bullet", + children: [ + { + type: "numberedListItem", + content: "Level 2 numbered", + children: [ + { + type: "bulletListItem", + content: "Level 3 bullet", + children: [ + { + type: "numberedListItem", + content: "Level 4 numbered", + }, + ], + }, + ], + }, + { + type: "numberedListItem", + content: "Level 2 sibling", + }, + ], + }, + { + type: "bulletListItem", + content: "Another top-level bullet", + children: [ + { + type: "bulletListItem", + content: "Child of second bullet", + children: [ + { + type: "checkListItem", + content: "Deep checklist item", + }, + ], + }, + ], + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, + { + testCase: { + name: "markdown/specialCharEscaping", + content: [ + { + type: "paragraph", + content: [ + { + type: "text", + text: "Literal *asterisks* and **double asterisks**", + styles: {}, + }, + ], + }, + { + type: "paragraph", + content: [ + { + type: "text", + text: "Backticks ` in plain text and `` double ``", + styles: {}, + }, + ], + }, + { + type: "paragraph", + content: [ + { + type: "text", + text: "Underscores _here_ and ~tildes~ and [brackets]", + styles: {}, + }, + ], + }, + { + type: "paragraph", + content: [ + { + type: "text", + text: "Pipes | and backslash \\ and #hash at start", + styles: {}, + }, + ], + }, + { + type: "codeBlock", + props: { language: "" }, + content: "const x = `template ${literal}`;\nconst y = '```triple backticks```';", + }, + ], + }, + executeTest: testExportParseEqualityMarkdown, + }, ]; From 7c3e17db3648263287a42473ea4ade17fe58aa7b Mon Sep 17 00:00:00 2001 From: Nick the Sick Date: Fri, 3 Apr 2026 14:06:06 +0200 Subject: [PATCH 4/4] fix: suppress no-template-curly-in-string lint warning in test data Co-Authored-By: Claude Opus 4.6 (1M context) --- .../exportParseEquality/exportParseEqualityTestInstances.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/src/unit/core/formatConversion/exportParseEquality/exportParseEqualityTestInstances.ts b/tests/src/unit/core/formatConversion/exportParseEquality/exportParseEqualityTestInstances.ts index 0d52d05638..b6cf5672fa 100644 --- a/tests/src/unit/core/formatConversion/exportParseEquality/exportParseEqualityTestInstances.ts +++ b/tests/src/unit/core/formatConversion/exportParseEquality/exportParseEqualityTestInstances.ts @@ -921,6 +921,7 @@ export const exportParseEqualityTestInstancesMarkdown: TestInstance< { type: "codeBlock", props: { language: "" }, + // eslint-disable-next-line no-template-curly-in-string content: "const x = `template ${literal}`;\nconst y = '```triple backticks```';", }, ],