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..ca9266ef84 --- /dev/null +++ b/packages/core/src/api/exporters/markdown/htmlToMarkdown.ts @@ -0,0 +1,681 @@ +/** + * 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 { + // 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"; +} + +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 === 3 /* Node.TEXT_NODE */) { + return node.textContent || ""; + } + + if (node.nodeType !== 1 /* 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 === 3 /* Node.TEXT_NODE */) {
+ result += child.textContent || "";
+ } else if (child.nodeType === 1 /* 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 + `\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 + `\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 + `\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 + `\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 === 3 /* Node.TEXT_NODE */) {
+ result += child.textContent || "";
+ } else if (child.nodeType === 1 /* 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 += ``;
+ break;
+ }
+ case "video": {
+ const src =
+ childEl.getAttribute("src") ||
+ childEl.getAttribute("data-url") ||
+ "";
+ const name =
+ childEl.getAttribute("data-name") ||
+ childEl.getAttribute("title") ||
+ "";
+ result += ``;
+ 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: ``,
- };
- }
- });
- };
-}
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`] = `
-"
-
-
-"
+"
-
-
-
-
+
"
`;
exports[`Lift nested lists > Lifts multiple bullet lists with content in between 1`] = `
-"
-
+
+
-
-"
+"
-
-
-
-
+
"
`;
exports[`Lift nested lists > Lifts nested bullet lists 1`] = `
-"
-
+
+
-
-"
+"
-
-
+
"
`;
exports[`Lift nested lists > Lifts nested bullet lists with content after nested list 1`] = `
-"
-
+
-
-"
+"
-
-
+
"
`;
exports[`Lift nested lists > Lifts nested bullet lists without li 1`] = `
-"
-
+
Bullet List Item 1
-
-"
+"
-
-
+ Bullet List Item 1
+
"
`;
exports[`Lift nested lists > Lifts nested mixed lists 1`] = `
-"
-
+
+
-
-"
+"
-
-
+
"
`;
exports[`Lift nested lists > Lifts nested numbered lists 1`] = `
-"
-
+
-
-"
+"
-
-
+
"
`;
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..54c346d764
--- /dev/null
+++ b/packages/core/src/api/parsers/markdown/markdownToHtml.ts
@@ -0,0 +1,969 @@
+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, """);
+}
+
+// ─── 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 ───────────────────────────────────────────────────────────
+
+/**
+ * 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 
+ 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] === "_" &&
+ !isIntraword(text, i, 3))
+ ) {
+ 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] === "_" && !isIntraword(text, i, 2))
+ ) {
+ 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] === "_" && !isIntraword(text, i, 1))) {
+ 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 {
+ //  or 
+ 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: ``,
+ 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
+ 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;}
+ // 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 "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 += `
${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 += `| ${parseInline(table.headers[c])} | `; + } + html += "
|---|
| ${parseInline(cell)} | `; + } + html += "
`.
- /** @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/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/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 @@
+
+
+
+
+
+
+
+
+
+
+
\ 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 @@
+
+
+
+
+
+
+
+
+
+
+ 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 @@
+
+
+
\ 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 @@
+
+
+ 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 @@
+
+ -
+
Item 5
+
+ -
+
Item 6
+
+
\ 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 @@
+
+
+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 @@
+
+
+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/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/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/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/__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..b6cf5672fa 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,560 @@ 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,
+ },
+ {
+ 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: "" },
+ // eslint-disable-next-line no-template-curly-in-string
+ content: "const x = `template ${literal}`;\nconst y = '```triple backticks```';",
+ },
+ ],
+ },
+ 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 |
+
+
+
+Final paragraph with [a link](https://example.com).`,
+ },
+ executeTest: testParseMarkdown,
+ },
+ // Image with alt text
+ {
+ testCase: {
+ name: "imageWithAlt",
+ content: ``,
+ },
+ executeTest: testParseMarkdown,
+ },
+ // Multiple images
+ {
+ testCase: {
+ name: "multipleImages",
+ content: `
+
+`,
+ },
+ executeTest: testParseMarkdown,
+ },
+ // Inline image within text (should be handled)
+ {
+ testCase: {
+ name: "inlineImage",
+ content: `Text before  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,