@blocknote/core 0.38.0 → 0.39.1-capitol

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (239) hide show
  1. package/dist/BlockNoteSchema-Bsa_tSAC.cjs +11 -0
  2. package/dist/BlockNoteSchema-Bsa_tSAC.cjs.map +1 -0
  3. package/dist/BlockNoteSchema-CZez1nQf.js +4244 -0
  4. package/dist/BlockNoteSchema-CZez1nQf.js.map +1 -0
  5. package/dist/blocknote.cjs +4 -12
  6. package/dist/blocknote.cjs.map +1 -1
  7. package/dist/blocknote.js +3401 -7305
  8. package/dist/blocknote.js.map +1 -1
  9. package/dist/blocks.cjs +2 -0
  10. package/dist/blocks.cjs.map +1 -0
  11. package/dist/blocks.js +71 -0
  12. package/dist/blocks.js.map +1 -0
  13. package/dist/style.css +1 -1
  14. package/dist/webpack-stats.json +1 -1
  15. package/package.json +19 -17
  16. package/src/api/blockManipulation/commands/insertBlocks/insertBlocks.ts +1 -1
  17. package/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.ts +2 -2
  18. package/src/api/blockManipulation/commands/splitBlock/splitBlock.ts +34 -25
  19. package/src/api/blockManipulation/setupTestEnv.ts +0 -1
  20. package/src/api/clipboard/fromClipboard/handleFileInsertion.ts +6 -10
  21. package/src/api/clipboard/fromClipboard/pasteExtension.ts +1 -1
  22. package/src/api/clipboard/toClipboard/copyExtension.ts +1 -1
  23. package/src/api/exporters/html/util/serializeBlocksExternalHTML.ts +128 -28
  24. package/src/api/exporters/html/util/serializeBlocksInternalHTML.ts +101 -41
  25. package/src/api/pmUtil.ts +1 -1
  26. package/src/api/positionMapping.test.ts +58 -15
  27. package/src/api/positionMapping.ts +2 -4
  28. package/src/blocks/Audio/block.ts +174 -0
  29. package/src/blocks/BlockNoteSchema.ts +59 -0
  30. package/src/blocks/Code/block.ts +299 -0
  31. package/src/blocks/File/block.ts +98 -0
  32. package/src/blocks/{FileBlockContent → File}/helpers/render/createAddFileButton.ts +5 -2
  33. package/src/blocks/{FileBlockContent → File}/helpers/render/createFileBlockWrapper.ts +15 -6
  34. package/src/blocks/{FileBlockContent → File}/helpers/render/createFileNameWithIcon.ts +15 -2
  35. package/src/blocks/{FileBlockContent → File}/helpers/render/createResizableFileBlockWrapper.ts +21 -2
  36. package/src/blocks/Heading/block.ts +138 -0
  37. package/src/blocks/Image/block.ts +190 -0
  38. package/src/blocks/ListItem/BulletListItem/block.ts +116 -0
  39. package/src/blocks/ListItem/CheckListItem/block.ts +175 -0
  40. package/src/blocks/ListItem/NumberedListItem/IndexingPlugin.ts +173 -0
  41. package/src/blocks/ListItem/NumberedListItem/block.ts +133 -0
  42. package/src/blocks/ListItem/ToggleListItem/block.ts +78 -0
  43. package/src/blocks/PageBreak/block.ts +72 -0
  44. package/src/blocks/{PageBreakBlockContent → PageBreak}/getPageBreakSlashMenuItems.ts +9 -7
  45. package/src/blocks/Paragraph/block.ts +80 -0
  46. package/src/blocks/Quote/block.ts +90 -0
  47. package/src/blocks/{TableBlockContent/TableBlockContent.ts → Table/block.ts} +169 -51
  48. package/src/blocks/ToggleWrapper/createToggleWrapper.ts +1 -1
  49. package/src/blocks/Video/block.ts +143 -0
  50. package/src/blocks/defaultBlockHelpers.ts +2 -2
  51. package/src/blocks/defaultBlockTypeGuards.ts +143 -174
  52. package/src/blocks/defaultBlocks.ts +107 -35
  53. package/src/blocks/defaultProps.ts +145 -4
  54. package/src/blocks/index.ts +26 -0
  55. package/src/blocks/utils/listItemEnterHandler.ts +42 -0
  56. package/src/editor/Block.css +54 -18
  57. package/src/editor/BlockNoteEditor.ts +256 -211
  58. package/src/editor/BlockNoteExtension.ts +92 -0
  59. package/src/editor/BlockNoteExtensions.ts +18 -17
  60. package/src/editor/defaultColors.ts +2 -2
  61. package/src/exporter/Exporter.ts +1 -1
  62. package/src/exporter/mapping.ts +1 -1
  63. package/src/extensions/BackgroundColor/BackgroundColorExtension.ts +3 -20
  64. package/src/extensions/BackgroundColor/BackgroundColorMark.ts +6 -8
  65. package/src/extensions/BlockChange/BlockChangePlugin.ts +2 -1
  66. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor-forked.json +2 -2
  67. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor.json +2 -2
  68. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-forked.html +1 -1
  69. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap.html +1 -1
  70. package/src/extensions/Collaboration/schemaMigration/SchemaMigrationPlugin.ts +52 -0
  71. package/src/extensions/Collaboration/schemaMigration/migrationRules/index.ts +4 -0
  72. package/src/extensions/Collaboration/schemaMigration/migrationRules/migrationRule.ts +4 -0
  73. package/src/extensions/Collaboration/schemaMigration/migrationRules/moveColorAttributes.ts +78 -0
  74. package/src/extensions/Comments/CommentsPlugin.ts +1 -1
  75. package/src/extensions/FilePanel/FilePanelPlugin.ts +5 -10
  76. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +1 -1
  77. package/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts +4 -3
  78. package/src/extensions/Placeholder/PlaceholderPlugin.ts +6 -6
  79. package/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.ts +1 -23
  80. package/src/extensions/SideMenu/SideMenuPlugin.ts +1 -3
  81. package/src/extensions/SideMenu/dragging.ts +2 -2
  82. package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +4 -7
  83. package/src/extensions/SuggestionMenu/getDefaultEmojiPickerItems.ts +6 -2
  84. package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +24 -17
  85. package/src/extensions/TableHandles/TableHandlesPlugin.ts +8 -8
  86. package/src/extensions/TextAlignment/TextAlignmentExtension.ts +5 -11
  87. package/src/extensions/TextColor/TextColorExtension.ts +3 -17
  88. package/src/extensions/TextColor/TextColorMark.ts +4 -9
  89. package/src/extensions/UniqueID/UniqueID.ts +6 -13
  90. package/src/index.ts +2 -28
  91. package/src/schema/blocks/createSpec.ts +342 -169
  92. package/src/schema/blocks/internal.ts +77 -138
  93. package/src/schema/blocks/types.ts +264 -94
  94. package/src/schema/index.ts +1 -0
  95. package/src/schema/inlineContent/createSpec.ts +99 -21
  96. package/src/schema/inlineContent/internal.ts +16 -7
  97. package/src/schema/inlineContent/types.ts +24 -2
  98. package/src/schema/propTypes.ts +15 -9
  99. package/src/schema/schema.ts +209 -0
  100. package/src/schema/styles/createSpec.ts +79 -31
  101. package/src/schema/styles/internal.ts +61 -2
  102. package/src/schema/styles/types.ts +17 -3
  103. package/src/util/topo-sort.test.ts +125 -0
  104. package/src/util/topo-sort.ts +160 -0
  105. package/types/src/api/blockManipulation/commands/splitBlock/splitBlock.d.ts +2 -1
  106. package/types/src/api/blockManipulation/selections/selection.d.ts +1 -1
  107. package/types/src/api/blockManipulation/setupTestEnv.d.ts +29 -543
  108. package/types/src/api/exporters/html/util/serializeBlocksExternalHTML.d.ts +1 -1
  109. package/types/src/api/exporters/html/util/serializeBlocksInternalHTML.d.ts +1 -1
  110. package/types/src/api/exporters/markdown/util/convertVideoToMarkdownRehypePlugin.d.ts +2 -0
  111. package/types/src/api/exporters/markdown/util/removeUnderlinesRehypePlugin.d.ts +6 -0
  112. package/types/src/api/pmUtil.d.ts +1 -1
  113. package/types/src/blocks/Audio/block.d.ts +58 -0
  114. package/types/src/blocks/BlockNoteSchema.d.ts +18 -0
  115. package/types/src/blocks/{CodeBlockContent/CodeBlockContent.d.ts → Code/block.d.ts} +25 -26
  116. package/types/src/blocks/Code/shiki.d.ts +4 -0
  117. package/types/src/blocks/Divider/block.d.ts +3 -0
  118. package/types/src/blocks/File/block.d.ts +37 -0
  119. package/types/src/blocks/File/helpers/render/createAddFileButton.d.ts +6 -0
  120. package/types/src/blocks/File/helpers/render/createFileBlockWrapper.d.ts +25 -0
  121. package/types/src/blocks/{FileBlockContent → File}/helpers/render/createFileNameWithIcon.d.ts +6 -2
  122. package/types/src/blocks/File/helpers/render/createResizableFileBlockWrapper.d.ts +31 -0
  123. package/types/src/blocks/Heading/block.d.ts +71 -0
  124. package/types/src/blocks/Image/block.d.ts +102 -0
  125. package/types/src/blocks/ListItem/BulletListItem/block.d.ts +25 -0
  126. package/types/src/blocks/ListItem/CheckListItem/block.d.ts +33 -0
  127. package/types/src/blocks/ListItem/NumberedListItem/IndexingPlugin.d.ts +8 -0
  128. package/types/src/blocks/ListItem/NumberedListItem/block.d.ts +33 -0
  129. package/types/src/blocks/ListItem/ToggleListItem/block.d.ts +25 -0
  130. package/types/src/blocks/PageBreak/block.d.ts +11 -0
  131. package/types/src/blocks/{PageBreakBlockContent → PageBreak}/getPageBreakSlashMenuItems.d.ts +4 -2
  132. package/types/src/blocks/Paragraph/block.d.ts +25 -0
  133. package/types/src/blocks/Quote/block.d.ts +17 -0
  134. package/types/src/blocks/Table/block.d.ts +21 -0
  135. package/types/src/blocks/Video/block.d.ts +67 -0
  136. package/types/src/blocks/defaultBlockHelpers.d.ts +1 -1
  137. package/types/src/blocks/defaultBlockTypeGuards.d.ts +15 -36
  138. package/types/src/blocks/defaultBlocks.d.ts +221 -1060
  139. package/types/src/blocks/defaultProps.d.ts +17 -1
  140. package/types/src/blocks/index.d.ts +24 -0
  141. package/types/src/blocks/utils/listItemEnterHandler.d.ts +2 -0
  142. package/types/src/editor/BlockNoteEditor.d.ts +36 -67
  143. package/types/src/editor/BlockNoteExtension.d.ts +67 -0
  144. package/types/src/editor/BlockNoteExtensions.d.ts +1 -1
  145. package/types/src/editor/defaultColors.d.ts +8 -76
  146. package/types/src/editor/managers/BlockManager.d.ts +114 -0
  147. package/types/src/editor/managers/CollaborationManager.d.ts +115 -0
  148. package/types/src/editor/managers/EventManager.d.ts +58 -0
  149. package/types/src/editor/managers/ExportManager.d.ts +64 -0
  150. package/types/src/editor/managers/ExtensionManager.d.ts +68 -0
  151. package/types/src/editor/managers/SelectionManager.d.ts +54 -0
  152. package/types/src/editor/managers/StateManager.d.ts +115 -0
  153. package/types/src/editor/managers/StyleManager.d.ts +48 -0
  154. package/types/src/editor/managers/index.d.ts +8 -0
  155. package/types/src/exporter/Exporter.d.ts +1 -1
  156. package/types/src/exporter/mapping.d.ts +1 -1
  157. package/types/src/extensions/BackgroundColor/BackgroundColorMark.d.ts +4 -1
  158. package/types/src/extensions/Collaboration/schemaMigration/SchemaMigrationPlugin.d.ts +7 -0
  159. package/types/src/extensions/Collaboration/schemaMigration/migrationRules/index.d.ts +3 -0
  160. package/types/src/extensions/Collaboration/schemaMigration/migrationRules/migrationRule.d.ts +3 -0
  161. package/types/src/extensions/Collaboration/schemaMigration/migrationRules/moveColorAttributes.d.ts +2 -0
  162. package/types/src/extensions/Comments/CommentsPlugin.d.ts +1 -1
  163. package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +4 -4
  164. package/types/src/extensions/TextColor/TextColorMark.d.ts +4 -1
  165. package/types/src/index.d.ts +2 -25
  166. package/types/src/schema/blocks/createSpec.d.ts +16 -36
  167. package/types/src/schema/blocks/internal.d.ts +11 -33
  168. package/types/src/schema/blocks/types.d.ts +181 -57
  169. package/types/src/schema/index.d.ts +1 -0
  170. package/types/src/schema/inlineContent/createSpec.d.ts +36 -2
  171. package/types/src/schema/inlineContent/internal.d.ts +7 -15
  172. package/types/src/schema/inlineContent/types.d.ts +15 -1
  173. package/types/src/schema/propTypes.d.ts +4 -4
  174. package/types/src/schema/schema.d.ts +40 -0
  175. package/types/src/schema/styles/createSpec.d.ts +6 -4
  176. package/types/src/schema/styles/internal.d.ts +6 -3
  177. package/types/src/schema/styles/types.d.ts +11 -2
  178. package/types/src/util/topo-sort.d.ts +18 -0
  179. package/types/src/util/topo-sort.test.d.ts +1 -0
  180. package/dist/tsconfig.tsbuildinfo +0 -1
  181. package/src/blocks/AudioBlockContent/AudioBlockContent.ts +0 -144
  182. package/src/blocks/CodeBlockContent/CodeBlockContent.ts +0 -445
  183. package/src/blocks/FileBlockContent/FileBlockContent.ts +0 -100
  184. package/src/blocks/HeadingBlockContent/HeadingBlockContent.ts +0 -159
  185. package/src/blocks/ImageBlockContent/ImageBlockContent.ts +0 -159
  186. package/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +0 -134
  187. package/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts +0 -299
  188. package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.ts +0 -86
  189. package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +0 -172
  190. package/src/blocks/ListItemBlockContent/ToggleListItemBlockContent/ToggleListItemBlockContent.ts +0 -104
  191. package/src/blocks/PageBreakBlockContent/PageBreakBlockContent.ts +0 -49
  192. package/src/blocks/PageBreakBlockContent/schema.ts +0 -40
  193. package/src/blocks/ParagraphBlockContent/ParagraphBlockContent.ts +0 -78
  194. package/src/blocks/QuoteBlockContent/QuoteBlockContent.ts +0 -121
  195. package/src/blocks/VideoBlockContent/VideoBlockContent.ts +0 -158
  196. package/src/editor/BlockNoteSchema.ts +0 -107
  197. package/src/editor/BlockNoteTipTapEditor.ts +0 -335
  198. package/types/src/blocks/AudioBlockContent/AudioBlockContent.d.ts +0 -99
  199. package/types/src/blocks/FileBlockContent/FileBlockContent.d.ts +0 -90
  200. package/types/src/blocks/FileBlockContent/helpers/render/createAddFileButton.d.ts +0 -6
  201. package/types/src/blocks/FileBlockContent/helpers/render/createFileBlockWrapper.d.ts +0 -9
  202. package/types/src/blocks/FileBlockContent/helpers/render/createResizableFileBlockWrapper.d.ts +0 -9
  203. package/types/src/blocks/HeadingBlockContent/HeadingBlockContent.d.ts +0 -67
  204. package/types/src/blocks/ImageBlockContent/ImageBlockContent.d.ts +0 -131
  205. package/types/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.d.ts +0 -46
  206. package/types/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.d.ts +0 -55
  207. package/types/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.d.ts +0 -2
  208. package/types/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.d.ts +0 -58
  209. package/types/src/blocks/ListItemBlockContent/ToggleListItemBlockContent/ToggleListItemBlockContent.d.ts +0 -46
  210. package/types/src/blocks/PageBreakBlockContent/PageBreakBlockContent.d.ts +0 -31
  211. package/types/src/blocks/PageBreakBlockContent/schema.d.ts +0 -86
  212. package/types/src/blocks/ParagraphBlockContent/ParagraphBlockContent.d.ts +0 -52
  213. package/types/src/blocks/QuoteBlockContent/QuoteBlockContent.d.ts +0 -52
  214. package/types/src/blocks/TableBlockContent/TableBlockContent.d.ts +0 -39
  215. package/types/src/blocks/VideoBlockContent/VideoBlockContent.d.ts +0 -131
  216. package/types/src/editor/BlockNoteSchema.d.ts +0 -34
  217. package/types/src/editor/BlockNoteTipTapEditor.d.ts +0 -43
  218. /package/src/blocks/{AudioBlockContent → Audio}/parseAudioElement.ts +0 -0
  219. /package/src/blocks/{FileBlockContent → File}/helpers/parse/parseEmbedElement.ts +0 -0
  220. /package/src/blocks/{FileBlockContent → File}/helpers/parse/parseFigureElement.ts +0 -0
  221. /package/src/blocks/{FileBlockContent → File}/helpers/toExternalHTML/createFigureWithCaption.ts +0 -0
  222. /package/src/blocks/{FileBlockContent → File}/helpers/toExternalHTML/createLinkWithCaption.ts +0 -0
  223. /package/src/blocks/{FileBlockContent → File/helpers}/uploadToTmpFilesDotOrg_DEV_ONLY.ts +0 -0
  224. /package/src/blocks/{ImageBlockContent → Image}/parseImageElement.ts +0 -0
  225. /package/src/blocks/{ListItemBlockContent → ListItem}/ListItemKeyboardShortcuts.ts +0 -0
  226. /package/src/blocks/{ListItemBlockContent → ListItem}/getListItemContent.ts +0 -0
  227. /package/src/blocks/{TableBlockContent → Table}/TableExtension.ts +0 -0
  228. /package/src/blocks/{VideoBlockContent → Video}/parseVideoElement.ts +0 -0
  229. /package/types/src/blocks/{AudioBlockContent → Audio}/parseAudioElement.d.ts +0 -0
  230. /package/types/src/blocks/{FileBlockContent → File}/helpers/parse/parseEmbedElement.d.ts +0 -0
  231. /package/types/src/blocks/{FileBlockContent → File}/helpers/parse/parseFigureElement.d.ts +0 -0
  232. /package/types/src/blocks/{FileBlockContent → File}/helpers/toExternalHTML/createFigureWithCaption.d.ts +0 -0
  233. /package/types/src/blocks/{FileBlockContent → File}/helpers/toExternalHTML/createLinkWithCaption.d.ts +0 -0
  234. /package/types/src/blocks/{FileBlockContent → File/helpers}/uploadToTmpFilesDotOrg_DEV_ONLY.d.ts +0 -0
  235. /package/types/src/blocks/{ImageBlockContent → Image}/parseImageElement.d.ts +0 -0
  236. /package/types/src/blocks/{ListItemBlockContent → ListItem}/ListItemKeyboardShortcuts.d.ts +0 -0
  237. /package/types/src/blocks/{ListItemBlockContent → ListItem}/getListItemContent.d.ts +0 -0
  238. /package/types/src/blocks/{TableBlockContent → Table}/TableExtension.d.ts +0 -0
  239. /package/types/src/blocks/{VideoBlockContent → Video}/parseVideoElement.d.ts +0 -0
@@ -1,8 +1,9 @@
1
- import { DOMSerializer, Fragment } from "prosemirror-model";
1
+ import { DOMSerializer, Fragment, Node } from "prosemirror-model";
2
2
 
3
3
  import { PartialBlock } from "../../../../blocks/defaultBlocks.js";
4
4
  import type { BlockNoteEditor } from "../../../../editor/BlockNoteEditor.js";
5
5
  import {
6
+ BlockImplementation,
6
7
  BlockSchema,
7
8
  InlineContentSchema,
8
9
  StyleSchema,
@@ -12,6 +13,7 @@ import {
12
13
  inlineContentToNodes,
13
14
  tableContentToNodes,
14
15
  } from "../../../nodeConversions/blockToNode.js";
16
+ import { nodeToCustomInlineContent } from "../../../nodeConversions/nodeToBlock.js";
15
17
 
16
18
  function addAttributesAndRemoveClasses(element: HTMLElement) {
17
19
  // Removes all BlockNote specific class names.
@@ -37,7 +39,7 @@ export function serializeInlineContentExternalHTML<
37
39
  serializer: DOMSerializer,
38
40
  options?: { document?: Document },
39
41
  ) {
40
- let nodes: any;
42
+ let nodes: Node[];
41
43
 
42
44
  // TODO: reuse function from nodeconversions?
43
45
  if (!blockContent) {
@@ -52,16 +54,104 @@ export function serializeInlineContentExternalHTML<
52
54
  throw new UnreachableCaseError(blockContent.type);
53
55
  }
54
56
 
55
- // We call the prosemirror serializer here because it handles Marks and Inline Content nodes nicely.
56
- // If we'd want to support custom serialization or externalHTML for Inline Content, we'd have to implement
57
- // a custom serializer here.
58
- const dom = serializer.serializeFragment(Fragment.from(nodes), options);
57
+ // Check if any of the nodes are custom inline content with toExternalHTML
58
+ const doc = options?.document ?? document;
59
+ const fragment = doc.createDocumentFragment();
60
+
61
+ for (const node of nodes) {
62
+ // Check if this is a custom inline content node with toExternalHTML
63
+ if (
64
+ node.type.name !== "text" &&
65
+ editor.schema.inlineContentSchema[node.type.name]
66
+ ) {
67
+ const inlineContentImplementation =
68
+ editor.schema.inlineContentSpecs[node.type.name].implementation;
69
+
70
+ if (inlineContentImplementation) {
71
+ // Convert the node to inline content format
72
+ const inlineContent = nodeToCustomInlineContent(
73
+ node,
74
+ editor.schema.inlineContentSchema,
75
+ editor.schema.styleSchema,
76
+ );
77
+
78
+ // Use the custom toExternalHTML method or fallback to `render`
79
+ const output = inlineContentImplementation.toExternalHTML
80
+ ? inlineContentImplementation.toExternalHTML(
81
+ inlineContent as any,
82
+ editor as any,
83
+ )
84
+ : inlineContentImplementation.render.call(
85
+ {
86
+ renderType: "dom",
87
+ props: undefined,
88
+ },
89
+ inlineContent as any,
90
+ () => {
91
+ // No-op
92
+ },
93
+ editor as any,
94
+ );
95
+
96
+ if (output) {
97
+ fragment.appendChild(output.dom);
98
+
99
+ // If contentDOM exists, render the inline content into it
100
+ if (output.contentDOM) {
101
+ const contentFragment = serializer.serializeFragment(
102
+ node.content,
103
+ options,
104
+ );
105
+ output.contentDOM.dataset.editable = "";
106
+ output.contentDOM.appendChild(contentFragment);
107
+ }
108
+ continue;
109
+ }
110
+ }
111
+ } else if (node.type.name === "text") {
112
+ // We serialize text nodes manually as we need to serialize the styles/
113
+ // marks using `styleSpec.implementation.render`. When left up to
114
+ // ProseMirror, it'll use `toDOM` which is incorrect.
115
+ let dom: globalThis.Node | Text = document.createTextNode(
116
+ node.textContent,
117
+ );
118
+ // Reverse the order of marks to maintain the correct priority.
119
+ for (const mark of node.marks.toReversed()) {
120
+ if (mark.type.name in editor.schema.styleSpecs) {
121
+ const newDom = (
122
+ editor.schema.styleSpecs[mark.type.name].implementation
123
+ .toExternalHTML ??
124
+ editor.schema.styleSpecs[mark.type.name].implementation.render
125
+ )(mark.attrs["stringValue"], editor);
126
+ newDom.contentDOM!.appendChild(dom);
127
+ dom = newDom.dom;
128
+ } else {
129
+ const domOutputSpec = mark.type.spec.toDOM!(mark, true);
130
+ const newDom = DOMSerializer.renderSpec(document, domOutputSpec);
131
+ newDom.contentDOM!.appendChild(dom);
132
+ dom = newDom.dom;
133
+ }
134
+ }
135
+
136
+ fragment.appendChild(dom);
137
+ } else {
138
+ // Fall back to default serialization for this node
139
+ const nodeFragment = serializer.serializeFragment(
140
+ Fragment.from([node]),
141
+ options,
142
+ );
143
+ fragment.appendChild(nodeFragment);
144
+ }
145
+ }
59
146
 
60
- if (dom.nodeType === 1 /* Node.ELEMENT_NODE */) {
61
- addAttributesAndRemoveClasses(dom as HTMLElement);
147
+ if (
148
+ fragment.childNodes.length === 1 &&
149
+ fragment.firstChild?.nodeType === 1 /* Node.ELEMENT_NODE */
150
+ ) {
151
+ addAttributesAndRemoveClasses(fragment.firstChild as HTMLElement);
62
152
  }
63
153
 
64
- return dom;
154
+ return fragment;
65
155
  }
66
156
 
67
157
  /**
@@ -85,16 +175,13 @@ function serializeBlock<
85
175
  const doc = options?.document ?? document;
86
176
  const BC_NODE = editor.pmSchema.nodes["blockContainer"];
87
177
 
88
- let props = block.props;
89
178
  // set default props in case we were passed a partial block
90
- if (!block.props) {
91
- props = {};
92
- for (const [name, spec] of Object.entries(
93
- editor.schema.blockSchema[block.type as any].propSchema,
94
- )) {
95
- if (spec.default !== undefined) {
96
- (props as any)[name] = spec.default;
97
- }
179
+ const props = block.props || {};
180
+ for (const [name, spec] of Object.entries(
181
+ editor.schema.blockSchema[block.type as any].propSchema,
182
+ )) {
183
+ if (!(name in props) && spec.default !== undefined) {
184
+ (props as any)[name] = spec.default;
98
185
  }
99
186
  }
100
187
 
@@ -112,15 +199,26 @@ function serializeBlock<
112
199
  // we should change toExternalHTML so that this is not necessary
113
200
  const attrs = Array.from(bc.dom.attributes);
114
201
 
115
- const ret = editor.blockImplementations[
116
- block.type as any
117
- ].implementation.toExternalHTML({ ...block, props } as any, editor as any);
202
+ const blockImplementation = editor.blockImplementations[block.type as any]
203
+ .implementation as BlockImplementation;
204
+ const ret =
205
+ blockImplementation.toExternalHTML?.call(
206
+ {},
207
+ { ...block, props } as any,
208
+ editor as any,
209
+ ) ||
210
+ blockImplementation.render.call(
211
+ {},
212
+ { ...block, props } as any,
213
+ editor as any,
214
+ );
118
215
 
119
216
  const elementFragment = doc.createDocumentFragment();
120
- if (ret.dom.classList.contains("bn-block-content")) {
217
+
218
+ if ((ret.dom as HTMLElement).classList.contains("bn-block-content")) {
121
219
  const blockContentDataAttributes = [
122
220
  ...attrs,
123
- ...Array.from(ret.dom.attributes),
221
+ ...Array.from((ret.dom as HTMLElement).attributes),
124
222
  ].filter(
125
223
  (attr) =>
126
224
  attr.name.startsWith("data") &&
@@ -129,7 +227,6 @@ function serializeBlock<
129
227
  attr.name !== "data-node-view-wrapper" &&
130
228
  attr.name !== "data-node-type" &&
131
229
  attr.name !== "data-id" &&
132
- attr.name !== "data-index" &&
133
230
  attr.name !== "data-editable",
134
231
  );
135
232
 
@@ -166,14 +263,17 @@ function serializeBlock<
166
263
  if (fragment.lastChild?.nodeName !== listType) {
167
264
  const list = doc.createElement(listType);
168
265
 
169
- if (listType === "OL" && props?.start && props?.start !== 1) {
266
+ if (
267
+ listType === "OL" &&
268
+ "start" in props &&
269
+ props.start &&
270
+ props?.start !== 1
271
+ ) {
170
272
  list.setAttribute("start", props.start + "");
171
273
  }
172
274
  fragment.append(list);
173
275
  }
174
- const li = doc.createElement("li");
175
- li.append(elementFragment);
176
- fragment.lastChild!.appendChild(li);
276
+ fragment.lastChild!.appendChild(elementFragment);
177
277
  } else {
178
278
  fragment.append(elementFragment);
179
279
  }
@@ -1,4 +1,4 @@
1
- import { DOMSerializer, Fragment } from "prosemirror-model";
1
+ import { DOMSerializer, Fragment, Node } from "prosemirror-model";
2
2
 
3
3
  import { PartialBlock } from "../../../../blocks/defaultBlocks.js";
4
4
  import type { BlockNoteEditor } from "../../../../editor/BlockNoteEditor.js";
@@ -13,6 +13,7 @@ import {
13
13
  tableContentToNodes,
14
14
  } from "../../../nodeConversions/blockToNode.js";
15
15
 
16
+ import { nodeToCustomInlineContent } from "../../../nodeConversions/nodeToBlock.js";
16
17
  export function serializeInlineContentInternalHTML<
17
18
  BSchema extends BlockSchema,
18
19
  I extends InlineContentSchema,
@@ -24,7 +25,7 @@ export function serializeInlineContentInternalHTML<
24
25
  blockType?: string,
25
26
  options?: { document?: Document },
26
27
  ) {
27
- let nodes: any;
28
+ let nodes: Node[];
28
29
 
29
30
  // TODO: reuse function from nodeconversions?
30
31
  if (!blockContent) {
@@ -39,12 +40,90 @@ export function serializeInlineContentInternalHTML<
39
40
  throw new UnreachableCaseError(blockContent.type);
40
41
  }
41
42
 
42
- // We call the prosemirror serializer here because it handles Marks and Inline Content nodes nicely.
43
- // If we'd want to support custom serialization or externalHTML for Inline Content, we'd have to implement
44
- // a custom serializer here.
45
- const dom = serializer.serializeFragment(Fragment.from(nodes), options);
43
+ // Check if any of the nodes are custom inline content with toExternalHTML
44
+ const doc = options?.document ?? document;
45
+ const fragment = doc.createDocumentFragment();
46
+
47
+ for (const node of nodes) {
48
+ // Check if this is a custom inline content node with toExternalHTML
49
+ if (
50
+ node.type.name !== "text" &&
51
+ editor.schema.inlineContentSchema[node.type.name]
52
+ ) {
53
+ const inlineContentImplementation =
54
+ editor.schema.inlineContentSpecs[node.type.name].implementation;
55
+
56
+ if (inlineContentImplementation) {
57
+ // Convert the node to inline content format
58
+ const inlineContent = nodeToCustomInlineContent(
59
+ node,
60
+ editor.schema.inlineContentSchema,
61
+ editor.schema.styleSchema,
62
+ );
63
+
64
+ // Use the custom toExternalHTML method
65
+ const output = inlineContentImplementation.render.call(
66
+ {
67
+ renderType: "dom",
68
+ props: undefined,
69
+ },
70
+ inlineContent as any,
71
+ () => {
72
+ // No-op
73
+ },
74
+ editor as any,
75
+ );
76
+
77
+ if (output) {
78
+ fragment.appendChild(output.dom);
79
+
80
+ // If contentDOM exists, render the inline content into it
81
+ if (output.contentDOM) {
82
+ const contentFragment = serializer.serializeFragment(
83
+ node.content,
84
+ options,
85
+ );
86
+ output.contentDOM.dataset.editable = "";
87
+ output.contentDOM.appendChild(contentFragment);
88
+ }
89
+ continue;
90
+ }
91
+ }
92
+ } else if (node.type.name === "text") {
93
+ // We serialize text nodes manually as we need to serialize the styles/
94
+ // marks using `styleSpec.implementation.render`. When left up to
95
+ // ProseMirror, it'll use `toDOM` which is incorrect.
96
+ let dom: globalThis.Node | Text = document.createTextNode(
97
+ node.textContent,
98
+ );
99
+ // Reverse the order of marks to maintain the correct priority.
100
+ for (const mark of node.marks.toReversed()) {
101
+ if (mark.type.name in editor.schema.styleSpecs) {
102
+ const newDom = editor.schema.styleSpecs[
103
+ mark.type.name
104
+ ].implementation.render(mark.attrs["stringValue"], editor);
105
+ newDom.contentDOM!.appendChild(dom);
106
+ dom = newDom.dom;
107
+ } else {
108
+ const domOutputSpec = mark.type.spec.toDOM!(mark, true);
109
+ const newDom = DOMSerializer.renderSpec(document, domOutputSpec);
110
+ newDom.contentDOM!.appendChild(dom);
111
+ dom = newDom.dom;
112
+ }
113
+ }
46
114
 
47
- return dom;
115
+ fragment.appendChild(dom);
116
+ } else {
117
+ // Fall back to default serialization for this node
118
+ const nodeFragment = serializer.serializeFragment(
119
+ Fragment.from([node]),
120
+ options,
121
+ );
122
+ fragment.appendChild(nodeFragment);
123
+ }
124
+ }
125
+
126
+ return fragment;
48
127
  }
49
128
 
50
129
  function serializeBlock<
@@ -55,36 +134,29 @@ function serializeBlock<
55
134
  editor: BlockNoteEditor<BSchema, I, S>,
56
135
  block: PartialBlock<BSchema, I, S>,
57
136
  serializer: DOMSerializer,
58
- listIndex: number,
59
137
  options?: { document?: Document },
60
138
  ) {
61
139
  const BC_NODE = editor.pmSchema.nodes["blockContainer"];
62
140
 
63
- let props = block.props;
64
141
  // set default props in case we were passed a partial block
65
- if (!block.props) {
66
- props = {};
67
- for (const [name, spec] of Object.entries(
68
- editor.schema.blockSchema[block.type as any].propSchema,
69
- )) {
70
- if (spec.default !== undefined) {
71
- (props as any)[name] = spec.default;
72
- }
142
+ const props = block.props || {};
143
+ for (const [name, spec] of Object.entries(
144
+ editor.schema.blockSchema[block.type as any].propSchema,
145
+ )) {
146
+ if (!(name in props) && spec.default !== undefined) {
147
+ (props as any)[name] = spec.default;
73
148
  }
74
149
  }
75
150
 
76
151
  const impl = editor.blockImplementations[block.type as any].implementation;
77
- const ret = impl.toInternalHTML({ ...block, props } as any, editor as any);
78
-
79
- if (block.type === "numberedListItem") {
80
- // This is a workaround to make sure there's a list index set.
81
- // Normally, this is set on the internal prosemirror nodes by the NumberedListIndexingPlugin,
82
- // but:
83
- // - (a) this information is not available on the Blocks passed to the serializer. (we only have access to BlockNote Blocks)
84
- // - (b) the NumberedListIndexingPlugin might not even have run, because we can manually call blocksToFullHTML
85
- // with blocks that are not part of the active document
86
- ret.dom.setAttribute("data-index", listIndex.toString());
87
- }
152
+ const ret = impl.render.call(
153
+ {
154
+ renderType: "dom",
155
+ props: undefined,
156
+ },
157
+ { ...block, props } as any,
158
+ editor as any,
159
+ );
88
160
 
89
161
  if (ret.contentDOM && block.content) {
90
162
  const ic = serializeInlineContentInternalHTML(
@@ -147,20 +219,8 @@ function serializeBlocks<
147
219
  const doc = options?.document ?? document;
148
220
  const fragment = doc.createDocumentFragment();
149
221
 
150
- let listIndex = 0;
151
222
  for (const block of blocks) {
152
- if (block.type === "numberedListItem") {
153
- listIndex++;
154
- } else {
155
- listIndex = 0;
156
- }
157
- const blockDOM = serializeBlock(
158
- editor,
159
- block,
160
- serializer,
161
- listIndex,
162
- options,
163
- );
223
+ const blockDOM = serializeBlock(editor, block, serializer, options);
164
224
  fragment.appendChild(blockDOM);
165
225
  }
166
226
 
package/src/api/pmUtil.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import type { Node, Schema } from "prosemirror-model";
2
2
  import { Transform } from "prosemirror-transform";
3
3
  import type { BlockNoteEditor } from "../editor/BlockNoteEditor.js";
4
- import { BlockNoteSchema } from "../editor/BlockNoteSchema.js";
4
+ import { BlockNoteSchema } from "../blocks/BlockNoteSchema.js";
5
5
  import type { BlockSchema } from "../schema/blocks/types.js";
6
6
  import type { InlineContentSchema } from "../schema/inlineContent/types.js";
7
7
  import type { StyleSchema } from "../schema/styles/types.js";
@@ -17,7 +17,21 @@ describe("PositionStorage with local editor", () => {
17
17
  expect.any(Function),
18
18
  );
19
19
 
20
- editor.mount(undefined);
20
+ editor._tiptapEditor.destroy();
21
+ });
22
+
23
+ it("should register transaction handler on creation & mount", () => {
24
+ const editor = BlockNoteEditor.create();
25
+ // editor.mount(document.createElement("div"));
26
+
27
+ editor._tiptapEditor.on = vi.fn();
28
+ trackPosition(editor, 0);
29
+
30
+ expect(editor._tiptapEditor.on).toHaveBeenCalledWith(
31
+ "transaction",
32
+ expect.any(Function),
33
+ );
34
+
21
35
  editor._tiptapEditor.destroy();
22
36
  });
23
37
  });
@@ -31,7 +45,6 @@ describe("PositionStorage with local editor", () => {
31
45
 
32
46
  expect(getPos()).toBe(10);
33
47
 
34
- editor.mount(undefined);
35
48
  editor._tiptapEditor.destroy();
36
49
  });
37
50
 
@@ -43,7 +56,6 @@ describe("PositionStorage with local editor", () => {
43
56
 
44
57
  expect(getPos()).toBe(10);
45
58
 
46
- editor.mount(undefined);
47
59
  editor._tiptapEditor.destroy();
48
60
  });
49
61
  });
@@ -89,7 +101,49 @@ describe("PositionStorage with local editor", () => {
89
101
  // Position should be updated according to mapping
90
102
  expect(getPos()).toBe(14);
91
103
 
92
- editor.mount(undefined);
104
+ editor._tiptapEditor.destroy();
105
+ });
106
+
107
+ it("should update mapping for local transactions before the position (unmounted)", () => {
108
+ const editor = BlockNoteEditor.create();
109
+
110
+ // Set initial content
111
+ editor.insertBlocks(
112
+ [
113
+ {
114
+ id: "1",
115
+ type: "paragraph",
116
+ content: [
117
+ {
118
+ type: "text",
119
+ text: "Hello World",
120
+ styles: {},
121
+ },
122
+ ],
123
+ },
124
+ ],
125
+ editor.document[0],
126
+ "before",
127
+ );
128
+
129
+ // Start tracking
130
+ const getPos = trackPosition(editor, 10);
131
+
132
+ // Move the cursor to the start of the document
133
+ editor.setTextCursorPosition(editor.document[0], "start");
134
+
135
+ // Insert text at the start of the document
136
+ editor.insertInlineContent([
137
+ {
138
+ type: "text",
139
+ text: "Test",
140
+ styles: {},
141
+ },
142
+ ]);
143
+
144
+ // Position should be updated according to mapping
145
+ expect(getPos()).toBe(14);
146
+
93
147
  editor._tiptapEditor.destroy();
94
148
  });
95
149
 
@@ -133,7 +187,6 @@ describe("PositionStorage with local editor", () => {
133
187
  // Position should not be updated
134
188
  expect(getPos()).toBe(10);
135
189
 
136
- editor.mount(undefined);
137
190
  editor._tiptapEditor.destroy();
138
191
  });
139
192
 
@@ -164,7 +217,6 @@ describe("PositionStorage with local editor", () => {
164
217
  expect(getPosAfterPos()).toBe(9); // 4 + 5 ("Test " length)
165
218
  expect(getPosAfterRightPos()).toBe(9); // 4 + 5 ("Test " length)
166
219
 
167
- editor.mount(undefined);
168
220
  editor._tiptapEditor.destroy();
169
221
  });
170
222
 
@@ -200,7 +252,6 @@ describe("PositionStorage with local editor", () => {
200
252
  expect(getPosAfterPos()).toBe(9); // 4 + 5 ("Test " length)
201
253
  expect(getPosAfterRightPos()).toBe(9); // 4 + 5 ("Test " length)
202
254
 
203
- editor.mount(undefined);
204
255
  editor._tiptapEditor.destroy();
205
256
  });
206
257
  });
@@ -289,9 +340,7 @@ describe("PositionStorage with remote editor", () => {
289
340
 
290
341
  ydoc.destroy();
291
342
  remoteYdoc.destroy();
292
- localEditor.mount(undefined);
293
343
  localEditor._tiptapEditor.destroy();
294
- remoteEditor.mount(undefined);
295
344
  remoteEditor._tiptapEditor.destroy();
296
345
  });
297
346
 
@@ -356,9 +405,7 @@ describe("PositionStorage with remote editor", () => {
356
405
 
357
406
  ydoc.destroy();
358
407
  remoteYdoc.destroy();
359
- localEditor.mount(undefined);
360
408
  localEditor._tiptapEditor.destroy();
361
- remoteEditor.mount(undefined);
362
409
  remoteEditor._tiptapEditor.destroy();
363
410
  });
364
411
 
@@ -419,9 +466,7 @@ describe("PositionStorage with remote editor", () => {
419
466
 
420
467
  ydoc.destroy();
421
468
  remoteYdoc.destroy();
422
- localEditor.mount(undefined);
423
469
  localEditor._tiptapEditor.destroy();
424
- remoteEditor.mount(undefined);
425
470
  remoteEditor._tiptapEditor.destroy();
426
471
  });
427
472
 
@@ -482,9 +527,7 @@ describe("PositionStorage with remote editor", () => {
482
527
 
483
528
  ydoc.destroy();
484
529
  remoteYdoc.destroy();
485
- localEditor.mount(undefined);
486
530
  localEditor._tiptapEditor.destroy();
487
- remoteEditor.mount(undefined);
488
531
  remoteEditor._tiptapEditor.destroy();
489
532
  });
490
533
  });
@@ -61,9 +61,7 @@ export function trackPosition(
61
61
  */
62
62
  side: "left" | "right" = "left",
63
63
  ): () => number {
64
- const ySyncPluginState = ySyncPluginKey.getState(
65
- editor._tiptapEditor.state,
66
- ) as {
64
+ const ySyncPluginState = ySyncPluginKey.getState(editor.prosemirrorState) as {
67
65
  doc: Y.Doc;
68
66
  binding: ProsemirrorBinding;
69
67
  };
@@ -95,7 +93,7 @@ export function trackPosition(
95
93
 
96
94
  return () => {
97
95
  const curYSyncPluginState = ySyncPluginKey.getState(
98
- editor._tiptapEditor.state,
96
+ editor.prosemirrorState,
99
97
  ) as typeof ySyncPluginState;
100
98
  const pos = relativePositionToAbsolutePosition(
101
99
  curYSyncPluginState.doc,