@blocknote/core 0.38.0 → 0.39.0

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 (225) hide show
  1. package/dist/BlockNoteSchema-DmZ6UQfY.cjs +11 -0
  2. package/dist/BlockNoteSchema-DmZ6UQfY.cjs.map +1 -0
  3. package/dist/BlockNoteSchema-oR047ACf.js +4275 -0
  4. package/dist/BlockNoteSchema-oR047ACf.js.map +1 -0
  5. package/dist/blocknote.cjs +4 -12
  6. package/dist/blocknote.cjs.map +1 -1
  7. package/dist/blocknote.js +3296 -7187
  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/tsconfig.tsbuildinfo +1 -1
  15. package/dist/webpack-stats.json +1 -1
  16. package/package.json +19 -16
  17. package/src/api/blockManipulation/commands/insertBlocks/insertBlocks.ts +1 -1
  18. package/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.ts +2 -2
  19. package/src/api/blockManipulation/commands/splitBlock/splitBlock.ts +34 -25
  20. package/src/api/blockManipulation/setupTestEnv.ts +0 -1
  21. package/src/api/clipboard/fromClipboard/handleFileInsertion.ts +4 -8
  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 +303 -0
  31. package/src/blocks/Code/shiki.ts +73 -0
  32. package/src/blocks/File/block.ts +98 -0
  33. package/src/blocks/{FileBlockContent → File}/helpers/render/createAddFileButton.ts +5 -2
  34. package/src/blocks/{FileBlockContent → File}/helpers/render/createFileBlockWrapper.ts +15 -6
  35. package/src/blocks/{FileBlockContent → File}/helpers/render/createFileNameWithIcon.ts +15 -2
  36. package/src/blocks/{FileBlockContent → File}/helpers/render/createResizableFileBlockWrapper.ts +21 -2
  37. package/src/blocks/Heading/block.ts +138 -0
  38. package/src/blocks/Image/block.ts +190 -0
  39. package/src/blocks/ListItem/BulletListItem/block.ts +116 -0
  40. package/src/blocks/ListItem/CheckListItem/block.ts +175 -0
  41. package/src/blocks/ListItem/NumberedListItem/IndexingPlugin.ts +173 -0
  42. package/src/blocks/ListItem/NumberedListItem/block.ts +133 -0
  43. package/src/blocks/ListItem/ToggleListItem/block.ts +78 -0
  44. package/src/blocks/PageBreak/block.ts +72 -0
  45. package/src/blocks/{PageBreakBlockContent → PageBreak}/getPageBreakSlashMenuItems.ts +9 -7
  46. package/src/blocks/Paragraph/block.ts +80 -0
  47. package/src/blocks/Quote/block.ts +90 -0
  48. package/src/blocks/{TableBlockContent/TableBlockContent.ts → Table/block.ts} +169 -51
  49. package/src/blocks/ToggleWrapper/createToggleWrapper.ts +1 -1
  50. package/src/blocks/Video/block.ts +143 -0
  51. package/src/blocks/defaultBlockHelpers.ts +2 -2
  52. package/src/blocks/defaultBlockTypeGuards.ts +143 -174
  53. package/src/blocks/defaultBlocks.ts +107 -35
  54. package/src/blocks/defaultProps.ts +145 -4
  55. package/src/blocks/index.ts +26 -0
  56. package/src/blocks/utils/listItemEnterHandler.ts +42 -0
  57. package/src/editor/Block.css +54 -18
  58. package/src/editor/BlockNoteEditor.ts +251 -210
  59. package/src/editor/BlockNoteExtension.ts +92 -0
  60. package/src/editor/BlockNoteExtensions.ts +18 -17
  61. package/src/editor/defaultColors.ts +2 -2
  62. package/src/exporter/Exporter.ts +1 -1
  63. package/src/exporter/mapping.ts +1 -1
  64. package/src/extensions/BackgroundColor/BackgroundColorExtension.ts +3 -20
  65. package/src/extensions/BackgroundColor/BackgroundColorMark.ts +6 -8
  66. package/src/extensions/BlockChange/BlockChangePlugin.ts +2 -1
  67. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor-forked.json +2 -2
  68. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor.json +2 -2
  69. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-forked.html +1 -1
  70. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap.html +1 -1
  71. package/src/extensions/Collaboration/schemaMigration/SchemaMigrationPlugin.ts +52 -0
  72. package/src/extensions/Collaboration/schemaMigration/migrationRules/index.ts +4 -0
  73. package/src/extensions/Collaboration/schemaMigration/migrationRules/migrationRule.ts +4 -0
  74. package/src/extensions/Collaboration/schemaMigration/migrationRules/moveColorAttributes.ts +78 -0
  75. package/src/extensions/Comments/CommentsPlugin.ts +1 -1
  76. package/src/extensions/FilePanel/FilePanelPlugin.ts +5 -10
  77. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +1 -1
  78. package/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts +4 -3
  79. package/src/extensions/Placeholder/PlaceholderPlugin.ts +2 -2
  80. package/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.ts +1 -23
  81. package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +0 -5
  82. package/src/extensions/SuggestionMenu/getDefaultEmojiPickerItems.ts +6 -2
  83. package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +24 -17
  84. package/src/extensions/TableHandles/TableHandlesPlugin.ts +2 -2
  85. package/src/extensions/TextAlignment/TextAlignmentExtension.ts +5 -11
  86. package/src/extensions/TextColor/TextColorExtension.ts +3 -17
  87. package/src/extensions/TextColor/TextColorMark.ts +4 -9
  88. package/src/extensions/UniqueID/UniqueID.ts +6 -13
  89. package/src/index.ts +2 -28
  90. package/src/schema/blocks/createSpec.ts +342 -169
  91. package/src/schema/blocks/internal.ts +77 -138
  92. package/src/schema/blocks/types.ts +264 -94
  93. package/src/schema/index.ts +1 -0
  94. package/src/schema/inlineContent/createSpec.ts +99 -21
  95. package/src/schema/inlineContent/internal.ts +16 -7
  96. package/src/schema/inlineContent/types.ts +24 -2
  97. package/src/schema/propTypes.ts +15 -9
  98. package/src/schema/schema.ts +209 -0
  99. package/src/schema/styles/createSpec.ts +79 -31
  100. package/src/schema/styles/internal.ts +61 -2
  101. package/src/schema/styles/types.ts +17 -3
  102. package/src/util/topo-sort.test.ts +125 -0
  103. package/src/util/topo-sort.ts +160 -0
  104. package/types/src/api/blockManipulation/commands/splitBlock/splitBlock.d.ts +2 -1
  105. package/types/src/api/blockManipulation/selections/selection.d.ts +1 -1
  106. package/types/src/api/blockManipulation/setupTestEnv.d.ts +29 -543
  107. package/types/src/api/exporters/html/util/serializeBlocksExternalHTML.d.ts +1 -1
  108. package/types/src/api/exporters/html/util/serializeBlocksInternalHTML.d.ts +1 -1
  109. package/types/src/api/pmUtil.d.ts +1 -1
  110. package/types/src/blocks/Audio/block.d.ts +58 -0
  111. package/types/src/blocks/BlockNoteSchema.d.ts +18 -0
  112. package/types/src/blocks/{CodeBlockContent/CodeBlockContent.d.ts → Code/block.d.ts} +25 -26
  113. package/types/src/blocks/Code/shiki.d.ts +4 -0
  114. package/types/src/blocks/File/block.d.ts +37 -0
  115. package/types/src/blocks/File/helpers/render/createAddFileButton.d.ts +6 -0
  116. package/types/src/blocks/File/helpers/render/createFileBlockWrapper.d.ts +25 -0
  117. package/types/src/blocks/{FileBlockContent → File}/helpers/render/createFileNameWithIcon.d.ts +6 -2
  118. package/types/src/blocks/File/helpers/render/createResizableFileBlockWrapper.d.ts +31 -0
  119. package/types/src/blocks/Heading/block.d.ts +71 -0
  120. package/types/src/blocks/Image/block.d.ts +102 -0
  121. package/types/src/blocks/ListItem/BulletListItem/block.d.ts +25 -0
  122. package/types/src/blocks/ListItem/CheckListItem/block.d.ts +33 -0
  123. package/types/src/blocks/ListItem/NumberedListItem/IndexingPlugin.d.ts +8 -0
  124. package/types/src/blocks/ListItem/NumberedListItem/block.d.ts +33 -0
  125. package/types/src/blocks/ListItem/ToggleListItem/block.d.ts +25 -0
  126. package/types/src/blocks/PageBreak/block.d.ts +11 -0
  127. package/types/src/blocks/{PageBreakBlockContent → PageBreak}/getPageBreakSlashMenuItems.d.ts +4 -2
  128. package/types/src/blocks/Paragraph/block.d.ts +25 -0
  129. package/types/src/blocks/Quote/block.d.ts +17 -0
  130. package/types/src/blocks/Table/block.d.ts +21 -0
  131. package/types/src/blocks/Video/block.d.ts +67 -0
  132. package/types/src/blocks/defaultBlockHelpers.d.ts +1 -1
  133. package/types/src/blocks/defaultBlockTypeGuards.d.ts +15 -36
  134. package/types/src/blocks/defaultBlocks.d.ts +221 -1060
  135. package/types/src/blocks/defaultProps.d.ts +17 -1
  136. package/types/src/blocks/index.d.ts +24 -0
  137. package/types/src/blocks/utils/listItemEnterHandler.d.ts +2 -0
  138. package/types/src/editor/BlockNoteEditor.d.ts +33 -66
  139. package/types/src/editor/BlockNoteExtension.d.ts +67 -0
  140. package/types/src/editor/BlockNoteExtensions.d.ts +1 -1
  141. package/types/src/editor/defaultColors.d.ts +8 -76
  142. package/types/src/exporter/Exporter.d.ts +1 -1
  143. package/types/src/exporter/mapping.d.ts +1 -1
  144. package/types/src/extensions/BackgroundColor/BackgroundColorMark.d.ts +4 -1
  145. package/types/src/extensions/Collaboration/schemaMigration/SchemaMigrationPlugin.d.ts +7 -0
  146. package/types/src/extensions/Collaboration/schemaMigration/migrationRules/index.d.ts +3 -0
  147. package/types/src/extensions/Collaboration/schemaMigration/migrationRules/migrationRule.d.ts +3 -0
  148. package/types/src/extensions/Collaboration/schemaMigration/migrationRules/moveColorAttributes.d.ts +2 -0
  149. package/types/src/extensions/Comments/CommentsPlugin.d.ts +1 -1
  150. package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +4 -4
  151. package/types/src/extensions/TextColor/TextColorMark.d.ts +4 -1
  152. package/types/src/index.d.ts +2 -25
  153. package/types/src/schema/blocks/createSpec.d.ts +16 -36
  154. package/types/src/schema/blocks/internal.d.ts +11 -33
  155. package/types/src/schema/blocks/types.d.ts +181 -57
  156. package/types/src/schema/index.d.ts +1 -0
  157. package/types/src/schema/inlineContent/createSpec.d.ts +36 -2
  158. package/types/src/schema/inlineContent/internal.d.ts +7 -15
  159. package/types/src/schema/inlineContent/types.d.ts +15 -1
  160. package/types/src/schema/propTypes.d.ts +4 -4
  161. package/types/src/schema/schema.d.ts +40 -0
  162. package/types/src/schema/styles/createSpec.d.ts +6 -4
  163. package/types/src/schema/styles/internal.d.ts +6 -3
  164. package/types/src/schema/styles/types.d.ts +11 -2
  165. package/types/src/util/topo-sort.d.ts +18 -0
  166. package/types/src/util/topo-sort.test.d.ts +1 -0
  167. package/src/blocks/AudioBlockContent/AudioBlockContent.ts +0 -144
  168. package/src/blocks/CodeBlockContent/CodeBlockContent.ts +0 -445
  169. package/src/blocks/FileBlockContent/FileBlockContent.ts +0 -100
  170. package/src/blocks/HeadingBlockContent/HeadingBlockContent.ts +0 -159
  171. package/src/blocks/ImageBlockContent/ImageBlockContent.ts +0 -159
  172. package/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +0 -134
  173. package/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts +0 -299
  174. package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.ts +0 -86
  175. package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +0 -172
  176. package/src/blocks/ListItemBlockContent/ToggleListItemBlockContent/ToggleListItemBlockContent.ts +0 -104
  177. package/src/blocks/PageBreakBlockContent/PageBreakBlockContent.ts +0 -49
  178. package/src/blocks/PageBreakBlockContent/schema.ts +0 -40
  179. package/src/blocks/ParagraphBlockContent/ParagraphBlockContent.ts +0 -78
  180. package/src/blocks/QuoteBlockContent/QuoteBlockContent.ts +0 -121
  181. package/src/blocks/VideoBlockContent/VideoBlockContent.ts +0 -158
  182. package/src/editor/BlockNoteSchema.ts +0 -107
  183. package/src/editor/BlockNoteTipTapEditor.ts +0 -335
  184. package/types/src/blocks/AudioBlockContent/AudioBlockContent.d.ts +0 -99
  185. package/types/src/blocks/FileBlockContent/FileBlockContent.d.ts +0 -90
  186. package/types/src/blocks/FileBlockContent/helpers/render/createAddFileButton.d.ts +0 -6
  187. package/types/src/blocks/FileBlockContent/helpers/render/createFileBlockWrapper.d.ts +0 -9
  188. package/types/src/blocks/FileBlockContent/helpers/render/createResizableFileBlockWrapper.d.ts +0 -9
  189. package/types/src/blocks/HeadingBlockContent/HeadingBlockContent.d.ts +0 -67
  190. package/types/src/blocks/ImageBlockContent/ImageBlockContent.d.ts +0 -131
  191. package/types/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.d.ts +0 -46
  192. package/types/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.d.ts +0 -55
  193. package/types/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.d.ts +0 -2
  194. package/types/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.d.ts +0 -58
  195. package/types/src/blocks/ListItemBlockContent/ToggleListItemBlockContent/ToggleListItemBlockContent.d.ts +0 -46
  196. package/types/src/blocks/PageBreakBlockContent/PageBreakBlockContent.d.ts +0 -31
  197. package/types/src/blocks/PageBreakBlockContent/schema.d.ts +0 -86
  198. package/types/src/blocks/ParagraphBlockContent/ParagraphBlockContent.d.ts +0 -52
  199. package/types/src/blocks/QuoteBlockContent/QuoteBlockContent.d.ts +0 -52
  200. package/types/src/blocks/TableBlockContent/TableBlockContent.d.ts +0 -39
  201. package/types/src/blocks/VideoBlockContent/VideoBlockContent.d.ts +0 -131
  202. package/types/src/editor/BlockNoteSchema.d.ts +0 -34
  203. package/types/src/editor/BlockNoteTipTapEditor.d.ts +0 -43
  204. /package/src/blocks/{AudioBlockContent → Audio}/parseAudioElement.ts +0 -0
  205. /package/src/blocks/{FileBlockContent → File}/helpers/parse/parseEmbedElement.ts +0 -0
  206. /package/src/blocks/{FileBlockContent → File}/helpers/parse/parseFigureElement.ts +0 -0
  207. /package/src/blocks/{FileBlockContent → File}/helpers/toExternalHTML/createFigureWithCaption.ts +0 -0
  208. /package/src/blocks/{FileBlockContent → File}/helpers/toExternalHTML/createLinkWithCaption.ts +0 -0
  209. /package/src/blocks/{FileBlockContent → File/helpers}/uploadToTmpFilesDotOrg_DEV_ONLY.ts +0 -0
  210. /package/src/blocks/{ImageBlockContent → Image}/parseImageElement.ts +0 -0
  211. /package/src/blocks/{ListItemBlockContent → ListItem}/ListItemKeyboardShortcuts.ts +0 -0
  212. /package/src/blocks/{ListItemBlockContent → ListItem}/getListItemContent.ts +0 -0
  213. /package/src/blocks/{TableBlockContent → Table}/TableExtension.ts +0 -0
  214. /package/src/blocks/{VideoBlockContent → Video}/parseVideoElement.ts +0 -0
  215. /package/types/src/blocks/{AudioBlockContent → Audio}/parseAudioElement.d.ts +0 -0
  216. /package/types/src/blocks/{FileBlockContent → File}/helpers/parse/parseEmbedElement.d.ts +0 -0
  217. /package/types/src/blocks/{FileBlockContent → File}/helpers/parse/parseFigureElement.d.ts +0 -0
  218. /package/types/src/blocks/{FileBlockContent → File}/helpers/toExternalHTML/createFigureWithCaption.d.ts +0 -0
  219. /package/types/src/blocks/{FileBlockContent → File}/helpers/toExternalHTML/createLinkWithCaption.d.ts +0 -0
  220. /package/types/src/blocks/{FileBlockContent → File/helpers}/uploadToTmpFilesDotOrg_DEV_ONLY.d.ts +0 -0
  221. /package/types/src/blocks/{ImageBlockContent → Image}/parseImageElement.d.ts +0 -0
  222. /package/types/src/blocks/{ListItemBlockContent → ListItem}/ListItemKeyboardShortcuts.d.ts +0 -0
  223. /package/types/src/blocks/{ListItemBlockContent → ListItem}/getListItemContent.d.ts +0 -0
  224. /package/types/src/blocks/{TableBlockContent → Table}/TableExtension.d.ts +0 -0
  225. /package/types/src/blocks/{VideoBlockContent → Video}/parseVideoElement.d.ts +0 -0
@@ -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,
@@ -0,0 +1,174 @@
1
+ import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
2
+ import {
3
+ BlockFromConfig,
4
+ createBlockConfig,
5
+ createBlockSpec,
6
+ } from "../../schema/index.js";
7
+ import { defaultProps, parseDefaultProps } from "../defaultProps.js";
8
+ import { parseFigureElement } from "../File/helpers/parse/parseFigureElement.js";
9
+ import { createFileBlockWrapper } from "../File/helpers/render/createFileBlockWrapper.js";
10
+ import { createFigureWithCaption } from "../File/helpers/toExternalHTML/createFigureWithCaption.js";
11
+ import { createLinkWithCaption } from "../File/helpers/toExternalHTML/createLinkWithCaption.js";
12
+ import { parseAudioElement } from "./parseAudioElement.js";
13
+
14
+ export const FILE_AUDIO_ICON_SVG =
15
+ '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M2 16.0001H5.88889L11.1834 20.3319C11.2727 20.405 11.3846 20.4449 11.5 20.4449C11.7761 20.4449 12 20.2211 12 19.9449V4.05519C12 3.93977 11.9601 3.8279 11.887 3.73857C11.7121 3.52485 11.3971 3.49335 11.1834 3.66821L5.88889 8.00007H2C1.44772 8.00007 1 8.44778 1 9.00007V15.0001C1 15.5524 1.44772 16.0001 2 16.0001ZM23 12C23 15.292 21.5539 18.2463 19.2622 20.2622L17.8445 18.8444C19.7758 17.1937 21 14.7398 21 12C21 9.26016 19.7758 6.80629 17.8445 5.15557L19.2622 3.73779C21.5539 5.75368 23 8.70795 23 12ZM18 12C18 10.0883 17.106 8.38548 15.7133 7.28673L14.2842 8.71584C15.3213 9.43855 16 10.64 16 12C16 13.36 15.3213 14.5614 14.2842 15.2841L15.7133 16.7132C17.106 15.6145 18 13.9116 18 12Z"></path></svg>';
16
+
17
+ export interface AudioOptions {
18
+ icon?: string;
19
+ }
20
+
21
+ export type AudioBlockConfig = ReturnType<typeof createAudioBlockConfig>;
22
+
23
+ export const createAudioBlockConfig = createBlockConfig(
24
+ (_ctx: AudioOptions) =>
25
+ ({
26
+ type: "audio" as const,
27
+ propSchema: {
28
+ backgroundColor: defaultProps.backgroundColor,
29
+ // File name.
30
+ name: {
31
+ default: "" as const,
32
+ },
33
+ // File url.
34
+ url: {
35
+ default: "" as const,
36
+ },
37
+ // File caption.
38
+ caption: {
39
+ default: "" as const,
40
+ },
41
+
42
+ showPreview: {
43
+ default: true,
44
+ },
45
+ },
46
+ content: "none",
47
+ }) as const,
48
+ );
49
+
50
+ export const audioParse =
51
+ (_config: AudioOptions = {}) =>
52
+ (element: HTMLElement) => {
53
+ if (element.tagName === "AUDIO") {
54
+ // Ignore if parent figure has already been parsed.
55
+ if (element.closest("figure")) {
56
+ return undefined;
57
+ }
58
+
59
+ const { backgroundColor } = parseDefaultProps(element);
60
+
61
+ return {
62
+ ...parseAudioElement(element as HTMLAudioElement),
63
+ backgroundColor,
64
+ };
65
+ }
66
+
67
+ if (element.tagName === "FIGURE") {
68
+ const parsedFigure = parseFigureElement(element, "audio");
69
+ if (!parsedFigure) {
70
+ return undefined;
71
+ }
72
+
73
+ const { targetElement, caption } = parsedFigure;
74
+
75
+ const { backgroundColor } = parseDefaultProps(element);
76
+
77
+ return {
78
+ ...parseAudioElement(targetElement as HTMLAudioElement),
79
+ backgroundColor,
80
+ caption,
81
+ };
82
+ }
83
+
84
+ return undefined;
85
+ };
86
+
87
+ export const audioRender =
88
+ (config: AudioOptions = {}) =>
89
+ (
90
+ block: BlockFromConfig<ReturnType<typeof createAudioBlockConfig>, any, any>,
91
+ editor: BlockNoteEditor<
92
+ Record<"audio", ReturnType<typeof createAudioBlockConfig>>,
93
+ any,
94
+ any
95
+ >,
96
+ ) => {
97
+ const icon = document.createElement("div");
98
+ icon.innerHTML = config.icon ?? FILE_AUDIO_ICON_SVG;
99
+
100
+ const audio = document.createElement("audio");
101
+ audio.className = "bn-audio";
102
+ if (editor.resolveFileUrl) {
103
+ editor.resolveFileUrl(block.props.url).then((downloadUrl) => {
104
+ audio.src = downloadUrl;
105
+ });
106
+ } else {
107
+ audio.src = block.props.url;
108
+ }
109
+ audio.controls = true;
110
+ audio.contentEditable = "false";
111
+ audio.draggable = false;
112
+
113
+ return createFileBlockWrapper(
114
+ block,
115
+ editor,
116
+ { dom: audio },
117
+ icon.firstElementChild as HTMLElement,
118
+ );
119
+ };
120
+
121
+ export const audioToExternalHTML =
122
+ (_config: AudioOptions = {}) =>
123
+ (
124
+ block: BlockFromConfig<ReturnType<typeof createAudioBlockConfig>, any, any>,
125
+ _editor: BlockNoteEditor<
126
+ Record<"audio", ReturnType<typeof createAudioBlockConfig>>,
127
+ any,
128
+ any
129
+ >,
130
+ ) => {
131
+ if (!block.props.url) {
132
+ const div = document.createElement("p");
133
+ div.textContent = "Add audio";
134
+
135
+ return {
136
+ dom: div,
137
+ };
138
+ }
139
+
140
+ let audio;
141
+ if (block.props.showPreview) {
142
+ audio = document.createElement("audio");
143
+ audio.src = block.props.url;
144
+ } else {
145
+ audio = document.createElement("a");
146
+ audio.href = block.props.url;
147
+ audio.textContent = block.props.name || block.props.url;
148
+ }
149
+
150
+ if (block.props.caption) {
151
+ if (block.props.showPreview) {
152
+ return createFigureWithCaption(audio, block.props.caption);
153
+ } else {
154
+ return createLinkWithCaption(audio, block.props.caption);
155
+ }
156
+ }
157
+
158
+ return {
159
+ dom: audio,
160
+ };
161
+ };
162
+
163
+ export const createAudioBlockSpec = createBlockSpec(
164
+ createAudioBlockConfig,
165
+ (config) => ({
166
+ meta: {
167
+ fileBlockAccept: ["audio/*"],
168
+ },
169
+ parse: audioParse(config),
170
+ render: audioRender(config),
171
+ toExternalHTML: audioToExternalHTML(config),
172
+ runsBefore: ["file"],
173
+ }),
174
+ );
@@ -0,0 +1,59 @@
1
+ import {
2
+ BlockSchema,
3
+ BlockSchemaFromSpecs,
4
+ BlockSpecs,
5
+ CustomBlockNoteSchema,
6
+ InlineContentSchema,
7
+ InlineContentSchemaFromSpecs,
8
+ InlineContentSpecs,
9
+ StyleSchema,
10
+ StyleSchemaFromSpecs,
11
+ StyleSpecs,
12
+ } from "../schema/index.js";
13
+ import {
14
+ defaultBlockSpecs,
15
+ defaultInlineContentSpecs,
16
+ defaultStyleSpecs,
17
+ } from "./defaultBlocks.js";
18
+
19
+ export class BlockNoteSchema<
20
+ BSchema extends BlockSchema,
21
+ ISchema extends InlineContentSchema,
22
+ SSchema extends StyleSchema,
23
+ > extends CustomBlockNoteSchema<BSchema, ISchema, SSchema> {
24
+ public static create<
25
+ BSpecs extends BlockSpecs | undefined = undefined,
26
+ ISpecs extends InlineContentSpecs | undefined = undefined,
27
+ SSpecs extends StyleSpecs | undefined = undefined,
28
+ >(options?: {
29
+ /**
30
+ * A list of custom block types that should be available in the editor.
31
+ */
32
+ blockSpecs?: BSpecs;
33
+ /**
34
+ * A list of custom InlineContent types that should be available in the editor.
35
+ */
36
+ inlineContentSpecs?: ISpecs;
37
+ /**
38
+ * A list of custom Styles that should be available in the editor.
39
+ */
40
+ styleSpecs?: SSpecs;
41
+ }): BlockNoteSchema<
42
+ BSpecs extends undefined
43
+ ? BlockSchemaFromSpecs<typeof defaultBlockSpecs>
44
+ : BlockSchemaFromSpecs<NonNullable<BSpecs>>,
45
+ ISpecs extends undefined
46
+ ? InlineContentSchemaFromSpecs<typeof defaultInlineContentSpecs>
47
+ : InlineContentSchemaFromSpecs<NonNullable<ISpecs>>,
48
+ SSpecs extends undefined
49
+ ? StyleSchemaFromSpecs<typeof defaultStyleSpecs>
50
+ : StyleSchemaFromSpecs<NonNullable<SSpecs>>
51
+ > {
52
+ return new BlockNoteSchema<any, any, any>({
53
+ blockSpecs: options?.blockSpecs ?? defaultBlockSpecs,
54
+ inlineContentSpecs:
55
+ options?.inlineContentSpecs ?? defaultInlineContentSpecs,
56
+ styleSpecs: options?.styleSpecs ?? defaultStyleSpecs,
57
+ });
58
+ }
59
+ }