@blocknote/core 0.29.1 → 0.30.1

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 (182) hide show
  1. package/README.md +125 -0
  2. package/dist/blocknote.cjs +9 -9
  3. package/dist/blocknote.cjs.map +1 -1
  4. package/dist/blocknote.js +1501 -1359
  5. package/dist/blocknote.js.map +1 -1
  6. package/dist/comments.cjs.map +1 -1
  7. package/dist/comments.js.map +1 -1
  8. package/dist/locales.cjs +1 -1
  9. package/dist/locales.cjs.map +1 -1
  10. package/dist/locales.js +751 -9
  11. package/dist/locales.js.map +1 -1
  12. package/dist/style.css +1 -1
  13. package/dist/tsconfig.tsbuildinfo +1 -1
  14. package/dist/webpack-stats.json +1 -1
  15. package/package.json +7 -8
  16. package/src/api/README.md +1 -1
  17. package/src/api/blockManipulation/commands/insertBlocks/__snapshots__/insertBlocks.test.ts.snap +0 -7
  18. package/src/api/blockManipulation/commands/insertBlocks/insertBlocks.test.ts +19 -14
  19. package/src/api/blockManipulation/commands/insertBlocks/insertBlocks.ts +5 -5
  20. package/src/api/blockManipulation/commands/mergeBlocks/__snapshots__/mergeBlocks.test.ts.snap +0 -5
  21. package/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.test.ts +3 -3
  22. package/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.ts +12 -12
  23. package/src/api/blockManipulation/commands/moveBlocks/__snapshots__/moveBlocks.test.ts.snap +0 -20
  24. package/src/api/blockManipulation/commands/moveBlocks/moveBlocks.test.ts +14 -14
  25. package/src/api/blockManipulation/commands/moveBlocks/moveBlocks.ts +16 -16
  26. package/src/api/blockManipulation/commands/nestBlock/nestBlock.ts +8 -8
  27. package/src/api/blockManipulation/commands/replaceBlocks/__snapshots__/replaceBlocks.test.ts.snap +0 -12
  28. package/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.test.ts +12 -12
  29. package/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.ts +7 -7
  30. package/src/api/blockManipulation/commands/splitBlock/__snapshots__/splitBlock.test.ts.snap +0 -6
  31. package/src/api/blockManipulation/commands/splitBlock/splitBlock.test.ts +10 -10
  32. package/src/api/blockManipulation/commands/splitBlock/splitBlock.ts +2 -2
  33. package/src/api/blockManipulation/commands/updateBlock/__snapshots__/updateBlock.test.ts.snap +0 -17
  34. package/src/api/blockManipulation/commands/updateBlock/updateBlock.test.ts +42 -42
  35. package/src/api/blockManipulation/commands/updateBlock/updateBlock.ts +18 -18
  36. package/src/api/blockManipulation/getBlock/getBlock.ts +9 -9
  37. package/src/api/blockManipulation/insertContentAt.ts +1 -1
  38. package/src/api/blockManipulation/selections/selection.ts +11 -11
  39. package/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.test.ts +7 -7
  40. package/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.ts +6 -6
  41. package/src/api/blockManipulation/tables/tables.test.ts +106 -106
  42. package/src/api/blockManipulation/tables/tables.ts +35 -35
  43. package/src/api/clipboard/fromClipboard/fileDropExtension.ts +2 -2
  44. package/src/api/clipboard/fromClipboard/handleFileInsertion.ts +9 -9
  45. package/src/api/clipboard/fromClipboard/handleVSCodePaste.ts +3 -3
  46. package/src/api/clipboard/fromClipboard/pasteExtension.ts +21 -3
  47. package/src/api/clipboard/toClipboard/copyExtension.ts +22 -22
  48. package/src/api/exporters/html/externalHTMLExporter.ts +6 -6
  49. package/src/api/exporters/html/internalHTMLSerializer.ts +3 -3
  50. package/src/api/exporters/html/util/serializeBlocksExternalHTML.ts +16 -16
  51. package/src/api/exporters/html/util/serializeBlocksInternalHTML.ts +14 -14
  52. package/src/api/exporters/markdown/markdownExporter.ts +3 -3
  53. package/src/api/exporters/markdown/util/addSpacesToCheckboxesRehypePlugin.ts +3 -3
  54. package/src/api/getBlockInfoFromPos.ts +6 -6
  55. package/src/api/nodeConversions/blockToNode.ts +26 -26
  56. package/src/api/nodeConversions/fragmentToBlocks.ts +1 -1
  57. package/src/api/nodeConversions/nodeToBlock.ts +37 -33
  58. package/src/api/nodeUtil.test.ts +16 -16
  59. package/src/api/nodeUtil.ts +10 -10
  60. package/src/api/parsers/html/parseHTML.ts +1 -1
  61. package/src/api/parsers/html/util/nestedLists.ts +2 -2
  62. package/src/api/parsers/markdown/parseMarkdown.ts +1 -1
  63. package/src/api/pmUtil.ts +4 -4
  64. package/src/api/positionMapping.test.ts +3 -3
  65. package/src/api/positionMapping.ts +5 -5
  66. package/src/blocks/AudioBlockContent/AudioBlockContent.ts +9 -4
  67. package/src/blocks/CodeBlockContent/CodeBlockContent.ts +40 -26
  68. package/src/blocks/FileBlockContent/FileBlockContent.ts +7 -2
  69. package/src/blocks/FileBlockContent/helpers/parse/parseFigureElement.ts +2 -2
  70. package/src/blocks/FileBlockContent/helpers/render/createAddFileButton.ts +5 -5
  71. package/src/blocks/FileBlockContent/helpers/render/createFileBlockWrapper.ts +2 -2
  72. package/src/blocks/FileBlockContent/helpers/render/createFileNameWithIcon.ts +1 -1
  73. package/src/blocks/FileBlockContent/helpers/render/createResizableFileBlockWrapper.ts +15 -8
  74. package/src/blocks/FileBlockContent/helpers/toExternalHTML/createFigureWithCaption.ts +1 -1
  75. package/src/blocks/FileBlockContent/helpers/toExternalHTML/createLinkWithCaption.ts +1 -1
  76. package/src/blocks/FileBlockContent/uploadToTmpFilesDotOrg_DEV_ONLY.ts +2 -2
  77. package/src/blocks/HeadingBlockContent/HeadingBlockContent.ts +9 -6
  78. package/src/blocks/ImageBlockContent/ImageBlockContent.ts +14 -6
  79. package/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +13 -29
  80. package/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts +24 -13
  81. package/src/blocks/ListItemBlockContent/ListItemKeyboardShortcuts.ts +1 -1
  82. package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.ts +1 -1
  83. package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +13 -30
  84. package/src/blocks/ListItemBlockContent/getListItemContent.ts +115 -0
  85. package/src/blocks/PageBreakBlockContent/PageBreakBlockContent.ts +1 -1
  86. package/src/blocks/PageBreakBlockContent/getPageBreakSlashMenuItems.ts +3 -3
  87. package/src/blocks/PageBreakBlockContent/schema.ts +2 -2
  88. package/src/blocks/ParagraphBlockContent/ParagraphBlockContent.ts +9 -5
  89. package/src/blocks/QuoteBlockContent/QuoteBlockContent.ts +10 -5
  90. package/src/blocks/README.md +1 -1
  91. package/src/blocks/TableBlockContent/TableBlockContent.ts +76 -19
  92. package/src/blocks/TableBlockContent/TableExtension.ts +3 -3
  93. package/src/blocks/VideoBlockContent/VideoBlockContent.ts +14 -6
  94. package/src/blocks/defaultBlockHelpers.ts +24 -8
  95. package/src/blocks/defaultBlockTypeGuards.ts +16 -16
  96. package/src/blocks/defaultBlocks.ts +3 -3
  97. package/src/comments/threadstore/DefaultThreadStoreAuth.ts +3 -3
  98. package/src/comments/threadstore/ThreadStore.ts +1 -1
  99. package/src/comments/threadstore/TipTapThreadStore.ts +10 -10
  100. package/src/comments/threadstore/yjs/RESTYjsThreadStore.ts +4 -4
  101. package/src/comments/threadstore/yjs/YjsThreadStore.test.ts +2 -2
  102. package/src/comments/threadstore/yjs/YjsThreadStore.ts +14 -14
  103. package/src/comments/threadstore/yjs/YjsThreadStoreBase.ts +1 -1
  104. package/src/comments/threadstore/yjs/yjsHelpers.ts +6 -6
  105. package/src/editor/Block.css +10 -1
  106. package/src/editor/BlockNoteEditor.test.ts +3 -3
  107. package/src/editor/BlockNoteEditor.ts +110 -61
  108. package/src/editor/BlockNoteExtensions.ts +24 -15
  109. package/src/editor/BlockNoteSchema.ts +4 -4
  110. package/src/editor/BlockNoteTipTapEditor.ts +10 -10
  111. package/src/editor/README.md +1 -1
  112. package/src/editor/cursorPositionTypes.ts +1 -1
  113. package/src/editor/editor.css +15 -3
  114. package/src/editor/selectionTypes.ts +1 -1
  115. package/src/editor/transformPasted.ts +2 -2
  116. package/src/exporter/Exporter.ts +5 -5
  117. package/src/exporter/mapping.ts +7 -7
  118. package/src/extensions/BackgroundColor/BackgroundColorMark.ts +1 -1
  119. package/src/extensions/Collaboration/CursorPlugin.ts +152 -0
  120. package/src/extensions/Collaboration/SyncPlugin.ts +15 -0
  121. package/src/extensions/Collaboration/UndoPlugin.ts +14 -0
  122. package/src/extensions/Comments/CommentsPlugin.ts +9 -9
  123. package/src/extensions/Comments/userstore/UserStore.ts +2 -2
  124. package/src/extensions/FilePanel/FilePanelPlugin.ts +37 -28
  125. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +6 -8
  126. package/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts +29 -26
  127. package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +11 -11
  128. package/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.ts +4 -4
  129. package/src/extensions/Placeholder/PlaceholderPlugin.ts +10 -10
  130. package/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.ts +2 -2
  131. package/src/extensions/README.md +1 -1
  132. package/src/extensions/SideMenu/MultipleNodeSelection.ts +1 -1
  133. package/src/extensions/SideMenu/SideMenuPlugin.ts +31 -31
  134. package/src/extensions/SideMenu/dragging.ts +8 -8
  135. package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +17 -17
  136. package/src/extensions/SuggestionMenu/getDefaultEmojiPickerItems.ts +2 -2
  137. package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +12 -12
  138. package/src/extensions/TableHandles/TableHandlesPlugin.ts +54 -53
  139. package/src/extensions/TrailingNode/TrailingNodeExtension.ts +1 -1
  140. package/src/extensions/UniqueID/UniqueID.ts +6 -6
  141. package/src/extensions/getDraggableBlockFromElement.ts +1 -1
  142. package/src/fonts/inter.css +18 -9
  143. package/src/i18n/locales/index.ts +2 -0
  144. package/src/i18n/locales/ru.ts +2 -2
  145. package/src/i18n/locales/sk.ts +355 -0
  146. package/src/i18n/locales/zh-tw.ts +390 -0
  147. package/src/locales.ts +1 -1
  148. package/src/pm-nodes/BlockContainer.ts +7 -6
  149. package/src/pm-nodes/BlockGroup.ts +1 -1
  150. package/src/pm-nodes/Doc.ts +4 -4
  151. package/src/schema/README.md +1 -1
  152. package/src/schema/blocks/createSpec.ts +15 -15
  153. package/src/schema/blocks/internal.ts +17 -18
  154. package/src/schema/blocks/types.ts +27 -26
  155. package/src/schema/inlineContent/createSpec.ts +16 -20
  156. package/src/schema/inlineContent/internal.ts +9 -9
  157. package/src/schema/inlineContent/types.ts +26 -26
  158. package/src/schema/propTypes.ts +8 -8
  159. package/src/schema/styles/createSpec.ts +2 -2
  160. package/src/schema/styles/internal.ts +7 -7
  161. package/src/schema/styles/types.ts +2 -2
  162. package/src/util/EventEmitter.ts +4 -4
  163. package/src/util/README.md +1 -1
  164. package/src/util/combineByGroup.ts +1 -1
  165. package/src/util/table.ts +33 -30
  166. package/types/src/api/blockManipulation/setupTestEnv.d.ts +8 -4
  167. package/types/src/blocks/ImageBlockContent/ImageBlockContent.d.ts +8 -4
  168. package/types/src/blocks/ListItemBlockContent/getListItemContent.d.ts +28 -0
  169. package/types/src/blocks/VideoBlockContent/VideoBlockContent.d.ts +8 -4
  170. package/types/src/blocks/defaultBlockHelpers.d.ts +1 -0
  171. package/types/src/blocks/defaultBlocks.d.ts +16 -8
  172. package/types/src/editor/BlockNoteEditor.d.ts +21 -2
  173. package/types/src/extensions/Collaboration/CursorPlugin.d.ts +31 -0
  174. package/types/src/extensions/Collaboration/SyncPlugin.d.ts +7 -0
  175. package/types/src/extensions/Collaboration/UndoPlugin.d.ts +6 -0
  176. package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +1 -1
  177. package/types/src/i18n/locales/index.d.ts +2 -0
  178. package/types/src/i18n/locales/sk.d.ts +313 -0
  179. package/types/src/i18n/locales/zh-tw.d.ts +2 -0
  180. package/types/src/schema/blocks/types.d.ts +2 -1
  181. package/src/extensions/Collaboration/createCollaborationExtensions.ts +0 -147
  182. package/types/src/extensions/Collaboration/createCollaborationExtensions.d.ts +0 -17
@@ -64,7 +64,7 @@ type CodeBlockConfigOptions = {
64
64
 
65
65
  export const shikiParserSymbol = Symbol.for("blocknote.shikiParser");
66
66
  export const shikiHighlighterPromiseSymbol = Symbol.for(
67
- "blocknote.shikiHighlighterPromise"
67
+ "blocknote.shikiHighlighterPromise",
68
68
  );
69
69
  export const defaultCodeBlockPropSchema = {
70
70
  language: {
@@ -126,7 +126,10 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
126
126
  return null;
127
127
  }
128
128
 
129
- return getLanguageId(options.editor.settings.codeBlock, language);
129
+ return (
130
+ getLanguageId(options.editor.settings.codeBlock, language) ??
131
+ language
132
+ );
130
133
  },
131
134
  renderHTML: (attributes) => {
132
135
  return attributes.language
@@ -141,10 +144,12 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
141
144
  },
142
145
  parseHTML() {
143
146
  return [
147
+ // Parse from internal HTML.
144
148
  {
145
149
  tag: "div[data-content-type=" + this.name + "]",
146
- contentElement: "code",
150
+ contentElement: ".bn-inline-content",
147
151
  },
152
+ // Parse from external HTML.
148
153
  {
149
154
  tag: "pre",
150
155
  contentElement: "code",
@@ -161,7 +166,7 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
161
166
  {
162
167
  ...(this.options.domAttributes?.inlineContent || {}),
163
168
  ...HTMLAttributes,
164
- }
169
+ },
165
170
  );
166
171
 
167
172
  dom.removeChild(contentDOM);
@@ -187,7 +192,7 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
187
192
  ...(this.options.domAttributes?.blockContent || {}),
188
193
  ...HTMLAttributes,
189
194
  },
190
- this.options.domAttributes?.inlineContent || {}
195
+ this.options.domAttributes?.inlineContent || {},
191
196
  );
192
197
  const handleLanguageChange = (event: Event) => {
193
198
  const language = (event.target as HTMLSelectElement).value;
@@ -200,7 +205,7 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
200
205
  };
201
206
 
202
207
  Object.entries(
203
- options.editor.settings.codeBlock.supportedLanguages
208
+ options.editor.settings.codeBlock.supportedLanguages,
204
209
  ).forEach(([id, { name }]) => {
205
210
  const option = document.createElement("option");
206
211
 
@@ -251,7 +256,7 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
251
256
  if (process.env.NODE_ENV === "development" && !hasWarned) {
252
257
  // eslint-disable-next-line no-console
253
258
  console.log(
254
- "For syntax highlighting of code blocks, you must provide a highlighter function"
259
+ "For syntax highlighting of code blocks, you must provide a `codeBlock.createHighlighter` function",
255
260
  );
256
261
  hasWarned = true;
257
262
  }
@@ -265,18 +270,25 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
265
270
  return globalThisForShiki[shikiHighlighterPromiseSymbol].then(
266
271
  (createdHighlighter) => {
267
272
  highlighter = createdHighlighter;
268
- }
273
+ },
269
274
  );
270
275
  }
271
-
272
- const language = parserOptions.language;
276
+ const language = getLanguageId(
277
+ options.editor.settings.codeBlock,
278
+ parserOptions.language!,
279
+ );
273
280
 
274
281
  if (
275
- language &&
276
- language !== "text" &&
277
- !highlighter.getLoadedLanguages().includes(language) &&
278
- language in options.editor.settings.codeBlock.supportedLanguages
282
+ !language ||
283
+ language === "text" ||
284
+ language === "none" ||
285
+ language === "plaintext" ||
286
+ language === "txt"
279
287
  ) {
288
+ return [];
289
+ }
290
+
291
+ if (!highlighter.getLoadedLanguages().includes(language)) {
280
292
  return highlighter.loadLanguage(language);
281
293
  }
282
294
 
@@ -308,10 +320,9 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
308
320
  const $start = state.doc.resolve(range.from);
309
321
  const languageName = match[1].trim();
310
322
  const attributes = {
311
- language: getLanguageId(
312
- options.editor.settings.codeBlock,
313
- languageName
314
- ),
323
+ language:
324
+ getLanguageId(options.editor.settings.codeBlock, languageName) ??
325
+ languageName,
315
326
  };
316
327
 
317
328
  if (
@@ -320,7 +331,7 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
320
331
  .canReplaceWith(
321
332
  $start.index(-1),
322
333
  $start.indexAfter(-1),
323
- this.type
334
+ this.type,
324
335
  )
325
336
  ) {
326
337
  return null;
@@ -407,7 +418,7 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
407
418
  $from.pos - $from.parentOffset + $from.parent.nodeSize,
408
419
  {
409
420
  type: "paragraph",
410
- }
421
+ },
411
422
  )
412
423
  .run();
413
424
 
@@ -419,13 +430,16 @@ const CodeBlockContent = createStronglyTypedTiptapNode({
419
430
 
420
431
  export const CodeBlock = createBlockSpecFromStronglyTypedTiptapNode(
421
432
  CodeBlockContent,
422
- defaultCodeBlockPropSchema
433
+ defaultCodeBlockPropSchema,
423
434
  );
424
435
 
425
- function getLanguageId(options: CodeBlockOptions, languageName: string) {
426
- return (
427
- Object.entries(options.supportedLanguages).find(([id, { aliases }]) => {
436
+ function getLanguageId(
437
+ options: CodeBlockOptions,
438
+ languageName: string,
439
+ ): string | undefined {
440
+ return Object.entries(options.supportedLanguages).find(
441
+ ([id, { aliases }]) => {
428
442
  return aliases?.includes(languageName) || id === languageName;
429
- })?.[0] || languageName
430
- );
443
+ },
444
+ )?.[0];
431
445
  }
@@ -36,13 +36,18 @@ export const fileBlockConfig = {
36
36
 
37
37
  export const fileRender = (
38
38
  block: BlockFromConfig<typeof fileBlockConfig, any, any>,
39
- editor: BlockNoteEditor<any, any, any>
39
+ editor: BlockNoteEditor<any, any, any>,
40
40
  ) => {
41
41
  return createFileBlockWrapper(block, editor);
42
42
  };
43
43
 
44
44
  export const fileParse = (element: HTMLElement) => {
45
45
  if (element.tagName === "EMBED") {
46
+ // Ignore if parent figure has already been parsed.
47
+ if (element.closest("figure")) {
48
+ return undefined;
49
+ }
50
+
46
51
  return parseEmbedElement(element as HTMLEmbedElement);
47
52
  }
48
53
 
@@ -64,7 +69,7 @@ export const fileParse = (element: HTMLElement) => {
64
69
  };
65
70
 
66
71
  export const fileToExternalHTML = (
67
- block: BlockFromConfig<typeof fileBlockConfig, any, any>
72
+ block: BlockFromConfig<typeof fileBlockConfig, any, any>,
68
73
  ) => {
69
74
  if (!block.props.url) {
70
75
  const div = document.createElement("p");
@@ -1,9 +1,9 @@
1
1
  export const parseFigureElement = (
2
2
  figureElement: HTMLElement,
3
- targetTag: string
3
+ targetTag: string,
4
4
  ) => {
5
5
  const targetElement = figureElement.querySelector(
6
- targetTag
6
+ targetTag,
7
7
  ) as HTMLElement | null;
8
8
  if (!targetElement) {
9
9
  return undefined;
@@ -5,7 +5,7 @@ export const createAddFileButton = (
5
5
  block: BlockFromConfig<FileBlockConfig, any, any>,
6
6
  editor: BlockNoteEditor<any, any, any>,
7
7
  buttonText?: string,
8
- buttonIcon?: HTMLElement
8
+ buttonIcon?: HTMLElement,
9
9
  ) => {
10
10
  const addFileButton = document.createElement("div");
11
11
  addFileButton.className = "bn-add-file-button";
@@ -35,13 +35,13 @@ export const createAddFileButton = (
35
35
  editor.transact((tr) =>
36
36
  tr.setMeta(editor.filePanel!.plugin, {
37
37
  block: block,
38
- })
38
+ }),
39
39
  );
40
40
  };
41
41
  addFileButton.addEventListener(
42
42
  "mousedown",
43
43
  addFileButtonMouseDownHandler,
44
- true
44
+ true,
45
45
  );
46
46
  addFileButton.addEventListener("click", addFileButtonClickHandler, true);
47
47
 
@@ -51,12 +51,12 @@ export const createAddFileButton = (
51
51
  addFileButton.removeEventListener(
52
52
  "mousedown",
53
53
  addFileButtonMouseDownHandler,
54
- true
54
+ true,
55
55
  );
56
56
  addFileButton.removeEventListener(
57
57
  "click",
58
58
  addFileButtonClickHandler,
59
- true
59
+ true,
60
60
  );
61
61
  },
62
62
  };
@@ -16,7 +16,7 @@ export const createFileBlockWrapper = (
16
16
  >,
17
17
  element?: { dom: HTMLElement; destroy?: () => void },
18
18
  buttonText?: string,
19
- buttonIcon?: HTMLElement
19
+ buttonIcon?: HTMLElement,
20
20
  ) => {
21
21
  const wrapper = document.createElement("div");
22
22
  wrapper.className = "bn-file-block-content-wrapper";
@@ -28,7 +28,7 @@ export const createFileBlockWrapper = (
28
28
  block,
29
29
  editor,
30
30
  buttonText,
31
- buttonIcon
31
+ buttonIcon,
32
32
  );
33
33
  wrapper.appendChild(addFileButton.dom);
34
34
 
@@ -3,7 +3,7 @@ import { BlockFromConfig, FileBlockConfig } from "../../../../schema/index.js";
3
3
  export const FILE_ICON_SVG = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M3 8L9.00319 2H19.9978C20.5513 2 21 2.45531 21 2.9918V21.0082C21 21.556 20.5551 22 20.0066 22H3.9934C3.44476 22 3 21.5501 3 20.9932V8ZM10 4V9H5V20H19V4H10Z"></path></svg>`;
4
4
 
5
5
  export const createFileNameWithIcon = (
6
- block: BlockFromConfig<FileBlockConfig, any, any>
6
+ block: BlockFromConfig<FileBlockConfig, any, any>,
7
7
  ): { dom: HTMLElement; destroy?: () => void } => {
8
8
  const file = document.createElement("div");
9
9
  file.className = "bn-file-name-with-icon";
@@ -8,18 +8,22 @@ export const createResizableFileBlockWrapper = (
8
8
  element: { dom: HTMLElement; destroy?: () => void },
9
9
  resizeHandlesContainerElement: HTMLElement,
10
10
  buttonText: string,
11
- buttonIcon: HTMLElement
11
+ buttonIcon: HTMLElement,
12
12
  ): { dom: HTMLElement; destroy: () => void } => {
13
13
  const { dom, destroy } = createFileBlockWrapper(
14
14
  block,
15
15
  editor,
16
16
  element,
17
17
  buttonText,
18
- buttonIcon
18
+ buttonIcon,
19
19
  );
20
20
  const wrapper = dom;
21
21
  if (block.props.url && block.props.showPreview) {
22
- wrapper.style.width = `${block.props.previewWidth}px`;
22
+ if (block.props.previewWidth) {
23
+ wrapper.style.width = `${block.props.previewWidth}px`;
24
+ } else {
25
+ wrapper.style.width = "fit-content";
26
+ }
23
27
  }
24
28
 
25
29
  const leftResizeHandle = document.createElement("div");
@@ -87,7 +91,10 @@ export const createResizableFileBlockWrapper = (
87
91
 
88
92
  // Ensures the element is not wider than the editor and not narrower than a
89
93
  // predetermined minimum width.
90
- width = Math.max(newWidth, minWidth);
94
+ width = Math.min(
95
+ Math.max(newWidth, minWidth),
96
+ editor.domElement?.firstElementChild?.clientWidth || Number.MAX_VALUE,
97
+ );
91
98
  wrapper.style.width = `${width}px`;
92
99
  };
93
100
  // Stops mouse movements from resizing the element and updates the block's
@@ -176,11 +183,11 @@ export const createResizableFileBlockWrapper = (
176
183
  wrapper.addEventListener("mouseleave", wrapperMouseLeaveHandler);
177
184
  leftResizeHandle.addEventListener(
178
185
  "mousedown",
179
- leftResizeHandleMouseDownHandler
186
+ leftResizeHandleMouseDownHandler,
180
187
  );
181
188
  rightResizeHandle.addEventListener(
182
189
  "mousedown",
183
- rightResizeHandleMouseDownHandler
190
+ rightResizeHandleMouseDownHandler,
184
191
  );
185
192
 
186
193
  return {
@@ -193,11 +200,11 @@ export const createResizableFileBlockWrapper = (
193
200
  wrapper.removeEventListener("mouseleave", wrapperMouseLeaveHandler);
194
201
  leftResizeHandle.removeEventListener(
195
202
  "mousedown",
196
- leftResizeHandleMouseDownHandler
203
+ leftResizeHandleMouseDownHandler,
197
204
  );
198
205
  rightResizeHandle.removeEventListener(
199
206
  "mousedown",
200
- rightResizeHandleMouseDownHandler
207
+ rightResizeHandleMouseDownHandler,
201
208
  );
202
209
  },
203
210
  };
@@ -1,6 +1,6 @@
1
1
  export const createFigureWithCaption = (
2
2
  element: HTMLElement,
3
- caption: string
3
+ caption: string,
4
4
  ) => {
5
5
  const figure = document.createElement("figure");
6
6
  const captionElement = document.createElement("figcaption");
@@ -1,6 +1,6 @@
1
1
  export const createLinkWithCaption = (
2
2
  element: HTMLElement,
3
- caption: string
3
+ caption: string,
4
4
  ) => {
5
5
  const wrapper = document.createElement("div");
6
6
  const fileCaption = document.createElement("p");
@@ -4,7 +4,7 @@
4
4
  * @warning This function should only be used for development purposes, replace with your own backend!
5
5
  */
6
6
  export const uploadToTmpFilesDotOrg_DEV_ONLY = async (
7
- file: File
7
+ file: File,
8
8
  ): Promise<string> => {
9
9
  const body = new FormData();
10
10
  body.append("file", file);
@@ -15,6 +15,6 @@ export const uploadToTmpFilesDotOrg_DEV_ONLY = async (
15
15
  });
16
16
  return (await ret.json()).data.url.replace(
17
17
  "tmpfiles.org/",
18
- "tmpfiles.org/dl/"
18
+ "tmpfiles.org/dl/",
19
19
  );
20
20
  };
@@ -46,7 +46,7 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
46
46
  props: {
47
47
  level: level as any,
48
48
  },
49
- })
49
+ }),
50
50
  )
51
51
  // Removes the "#" character(s) used to set the heading.
52
52
  .deleteRange({ from: range.from, to: range.to })
@@ -75,7 +75,7 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
75
75
  props: {
76
76
  level: 1 as any,
77
77
  },
78
- })
78
+ }),
79
79
  );
80
80
  },
81
81
  "Mod-Alt-2": () => {
@@ -93,7 +93,7 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
93
93
  props: {
94
94
  level: 2 as any,
95
95
  },
96
- })
96
+ }),
97
97
  );
98
98
  },
99
99
  "Mod-Alt-3": () => {
@@ -111,16 +111,19 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
111
111
  props: {
112
112
  level: 3 as any,
113
113
  },
114
- })
114
+ }),
115
115
  );
116
116
  },
117
117
  };
118
118
  },
119
119
  parseHTML() {
120
120
  return [
121
+ // Parse from internal HTML.
121
122
  {
122
123
  tag: "div[data-content-type=" + this.name + "]",
124
+ contentElement: ".bn-inline-content",
123
125
  },
126
+ // Parse from external HTML.
124
127
  {
125
128
  tag: "h1",
126
129
  attrs: { level: 1 },
@@ -147,12 +150,12 @@ const HeadingBlockContent = createStronglyTypedTiptapNode({
147
150
  ...(this.options.domAttributes?.blockContent || {}),
148
151
  ...HTMLAttributes,
149
152
  },
150
- this.options.domAttributes?.inlineContent || {}
153
+ this.options.domAttributes?.inlineContent || {},
151
154
  );
152
155
  },
153
156
  });
154
157
 
155
158
  export const Heading = createBlockSpecFromStronglyTypedTiptapNode(
156
159
  HeadingBlockContent,
157
- headingPropSchema
160
+ headingPropSchema,
158
161
  );
@@ -37,7 +37,8 @@ export const imagePropSchema = {
37
37
  },
38
38
  // File preview width in px.
39
39
  previewWidth: {
40
- default: 512,
40
+ default: undefined,
41
+ type: "number",
41
42
  },
42
43
  } satisfies PropSchema;
43
44
 
@@ -51,7 +52,7 @@ export const imageBlockConfig = {
51
52
 
52
53
  export const imageRender = (
53
54
  block: BlockFromConfig<typeof imageBlockConfig, any, any>,
54
- editor: BlockNoteEditor<any, any, any>
55
+ editor: BlockNoteEditor<any, any, any>,
55
56
  ) => {
56
57
  const icon = document.createElement("div");
57
58
  icon.innerHTML = FILE_IMAGE_ICON_SVG;
@@ -80,14 +81,19 @@ export const imageRender = (
80
81
  { dom: imageWrapper },
81
82
  imageWrapper,
82
83
  editor.dictionary.file_blocks.image.add_button_text,
83
- icon.firstElementChild as HTMLElement
84
+ icon.firstElementChild as HTMLElement,
84
85
  );
85
86
  };
86
87
 
87
88
  export const imageParse = (
88
- element: HTMLElement
89
+ element: HTMLElement,
89
90
  ): Partial<Props<typeof imageBlockConfig.propSchema>> | undefined => {
90
91
  if (element.tagName === "IMG") {
92
+ // Ignore if parent figure has already been parsed.
93
+ if (element.closest("figure")) {
94
+ return undefined;
95
+ }
96
+
91
97
  return parseImageElement(element as HTMLImageElement);
92
98
  }
93
99
 
@@ -109,7 +115,7 @@ export const imageParse = (
109
115
  };
110
116
 
111
117
  export const imageToExternalHTML = (
112
- block: BlockFromConfig<typeof imageBlockConfig, any, any>
118
+ block: BlockFromConfig<typeof imageBlockConfig, any, any>,
113
119
  ) => {
114
120
  if (!block.props.url) {
115
121
  const div = document.createElement("p");
@@ -125,7 +131,9 @@ export const imageToExternalHTML = (
125
131
  image = document.createElement("img");
126
132
  image.src = block.props.url;
127
133
  image.alt = block.props.name || block.props.caption || "BlockNote image";
128
- image.width = block.props.previewWidth;
134
+ if (block.props.previewWidth) {
135
+ image.width = block.props.previewWidth;
136
+ }
129
137
  } else {
130
138
  image = document.createElement("a");
131
139
  image.href = block.props.url;
@@ -8,6 +8,7 @@ import {
8
8
  } from "../../../schema/index.js";
9
9
  import { createDefaultBlockDOMOutputSpec } from "../../defaultBlockHelpers.js";
10
10
  import { defaultProps } from "../../defaultProps.js";
11
+ import { getListItemContent } from "../getListItemContent.js";
11
12
  import { handleEnter } from "../ListItemKeyboardShortcuts.js";
12
13
 
13
14
  export const bulletListItemPropSchema = {
@@ -40,7 +41,7 @@ const BulletListItemBlockContent = createStronglyTypedTiptapNode({
40
41
  updateBlockCommand(blockInfo.bnBlock.beforePos, {
41
42
  type: "bulletListItem",
42
43
  props: {},
43
- })
44
+ }),
44
45
  )
45
46
  // Removes the "-", "+", or "*" character used to set the list.
46
47
  .deleteRange({ from: range.from, to: range.to });
@@ -65,7 +66,7 @@ const BulletListItemBlockContent = createStronglyTypedTiptapNode({
65
66
  updateBlockCommand(blockInfo.bnBlock.beforePos, {
66
67
  type: "bulletListItem",
67
68
  props: {},
68
- })
69
+ }),
69
70
  );
70
71
  },
71
72
  };
@@ -73,10 +74,12 @@ const BulletListItemBlockContent = createStronglyTypedTiptapNode({
73
74
 
74
75
  parseHTML() {
75
76
  return [
76
- // Case for regular HTML list structure.
77
+ // Parse from internal HTML.
77
78
  {
78
79
  tag: "div[data-content-type=" + this.name + "]",
80
+ contentElement: ".bn-inline-content",
79
81
  },
82
+ // Parse from external HTML.
80
83
  {
81
84
  tag: "li",
82
85
  getAttrs: (element) => {
@@ -92,36 +95,17 @@ const BulletListItemBlockContent = createStronglyTypedTiptapNode({
92
95
 
93
96
  if (
94
97
  parent.tagName === "UL" ||
95
- (parent.tagName === "DIV" && parent.parentElement!.tagName === "UL")
98
+ (parent.tagName === "DIV" && parent.parentElement?.tagName === "UL")
96
99
  ) {
97
100
  return {};
98
101
  }
99
102
 
100
103
  return false;
101
104
  },
102
- node: "bulletListItem",
103
- },
104
- // Case for BlockNote list structure.
105
- {
106
- tag: "p",
107
- getAttrs: (element) => {
108
- if (typeof element === "string") {
109
- return false;
110
- }
111
-
112
- const parent = element.parentElement;
113
-
114
- if (parent === null) {
115
- return false;
116
- }
117
-
118
- if (parent.getAttribute("data-content-type") === "bulletListItem") {
119
- return {};
120
- }
121
-
122
- return false;
123
- },
124
- priority: 300,
105
+ // As `li` elements can contain multiple paragraphs, we need to merge their contents
106
+ // into a single one so that ProseMirror can parse everything correctly.
107
+ getContent: (node, schema) =>
108
+ getListItemContent(node, schema, this.name),
125
109
  node: "bulletListItem",
126
110
  },
127
111
  ];
@@ -138,12 +122,12 @@ const BulletListItemBlockContent = createStronglyTypedTiptapNode({
138
122
  ...(this.options.domAttributes?.blockContent || {}),
139
123
  ...HTMLAttributes,
140
124
  },
141
- this.options.domAttributes?.inlineContent || {}
125
+ this.options.domAttributes?.inlineContent || {},
142
126
  );
143
127
  },
144
128
  });
145
129
 
146
130
  export const BulletListItem = createBlockSpecFromStronglyTypedTiptapNode(
147
131
  BulletListItemBlockContent,
148
- bulletListItemPropSchema
132
+ bulletListItemPropSchema,
149
133
  );