@blocknote/core 0.13.2 → 0.13.4

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 (163) hide show
  1. package/dist/blocknote.js +5730 -2891
  2. package/dist/blocknote.js.map +1 -1
  3. package/dist/blocknote.umd.cjs +7 -7
  4. package/dist/blocknote.umd.cjs.map +1 -1
  5. package/dist/style.css +1 -1
  6. package/dist/webpack-stats.json +1 -1
  7. package/package.json +2 -2
  8. package/src/api/exporters/html/__snapshots__/file/basic/external.html +1 -0
  9. package/src/api/exporters/html/__snapshots__/file/basic/internal.html +1 -0
  10. package/src/api/exporters/html/__snapshots__/file/button/external.html +1 -0
  11. package/src/api/exporters/html/__snapshots__/file/button/internal.html +1 -0
  12. package/src/api/exporters/html/__snapshots__/file/nested/external.html +1 -0
  13. package/src/api/exporters/html/__snapshots__/file/nested/internal.html +1 -0
  14. package/src/api/exporters/html/__snapshots__/file/noCaption/external.html +1 -0
  15. package/src/api/exporters/html/__snapshots__/file/noCaption/internal.html +1 -0
  16. package/src/api/exporters/html/__snapshots__/file/noName/external.html +1 -0
  17. package/src/api/exporters/html/__snapshots__/file/noName/internal.html +1 -0
  18. package/src/api/exporters/html/__snapshots__/image/basic/external.html +1 -1
  19. package/src/api/exporters/html/__snapshots__/image/basic/internal.html +1 -1
  20. package/src/api/exporters/html/__snapshots__/image/button/external.html +1 -1
  21. package/src/api/exporters/html/__snapshots__/image/button/internal.html +1 -1
  22. package/src/api/exporters/html/__snapshots__/image/nested/external.html +1 -1
  23. package/src/api/exporters/html/__snapshots__/image/nested/internal.html +1 -1
  24. package/src/api/exporters/html/__snapshots__/image/noCaption/external.html +1 -0
  25. package/src/api/exporters/html/__snapshots__/image/noCaption/internal.html +1 -0
  26. package/src/api/exporters/html/__snapshots__/image/noName/external.html +1 -0
  27. package/src/api/exporters/html/__snapshots__/image/noName/internal.html +1 -0
  28. package/src/api/exporters/html/__snapshots__/image/noPreview/external.html +1 -0
  29. package/src/api/exporters/html/__snapshots__/image/noPreview/internal.html +1 -0
  30. package/src/api/exporters/html/__snapshots__/lists/basic/external.html +1 -0
  31. package/src/api/exporters/html/__snapshots__/lists/basic/internal.html +1 -0
  32. package/src/api/exporters/html/__snapshots__/lists/nested/external.html +1 -0
  33. package/src/api/exporters/html/__snapshots__/lists/nested/internal.html +1 -0
  34. package/src/api/exporters/html/__snapshots__/simpleFile/basic/external.html +1 -0
  35. package/src/api/exporters/html/__snapshots__/simpleFile/basic/internal.html +1 -0
  36. package/src/api/exporters/html/__snapshots__/simpleFile/button/external.html +1 -0
  37. package/src/api/exporters/html/__snapshots__/simpleFile/button/internal.html +1 -0
  38. package/src/api/exporters/html/__snapshots__/simpleFile/nested/external.html +1 -0
  39. package/src/api/exporters/html/__snapshots__/simpleFile/nested/internal.html +1 -0
  40. package/src/api/exporters/html/__snapshots__/simpleImage/basic/external.html +1 -1
  41. package/src/api/exporters/html/__snapshots__/simpleImage/basic/internal.html +1 -1
  42. package/src/api/exporters/html/__snapshots__/simpleImage/button/external.html +1 -1
  43. package/src/api/exporters/html/__snapshots__/simpleImage/button/internal.html +1 -1
  44. package/src/api/exporters/html/__snapshots__/simpleImage/nested/external.html +1 -1
  45. package/src/api/exporters/html/__snapshots__/simpleImage/nested/internal.html +1 -1
  46. package/src/api/exporters/html/__snapshots__/simpleImage/noCaption/external.html +1 -0
  47. package/src/api/exporters/html/__snapshots__/simpleImage/noCaption/internal.html +1 -0
  48. package/src/api/exporters/html/__snapshots__/simpleImage/noName/external.html +1 -0
  49. package/src/api/exporters/html/__snapshots__/simpleImage/noName/internal.html +1 -0
  50. package/src/api/exporters/html/__snapshots__/simpleImage/noPreview/external.html +1 -0
  51. package/src/api/exporters/html/__snapshots__/simpleImage/noPreview/internal.html +1 -0
  52. package/src/api/exporters/html/externalHTMLExporter.ts +4 -3
  53. package/src/api/exporters/html/util/simplifyBlocksRehypePlugin.ts +1 -1
  54. package/src/api/exporters/markdown/__snapshots__/file/basic/markdown.md +3 -0
  55. package/src/api/exporters/markdown/__snapshots__/file/button/markdown.md +1 -0
  56. package/src/api/exporters/markdown/__snapshots__/file/nested/markdown.md +7 -0
  57. package/src/api/exporters/markdown/__snapshots__/file/noCaption/markdown.md +1 -0
  58. package/src/api/exporters/markdown/__snapshots__/file/noName/markdown.md +3 -0
  59. package/src/api/exporters/markdown/__snapshots__/image/basic/markdown.md +1 -1
  60. package/src/api/exporters/markdown/__snapshots__/image/button/markdown.md +1 -1
  61. package/src/api/exporters/markdown/__snapshots__/image/nested/markdown.md +2 -2
  62. package/src/api/exporters/markdown/__snapshots__/image/noCaption/markdown.md +1 -0
  63. package/src/api/exporters/markdown/__snapshots__/image/noName/markdown.md +3 -0
  64. package/src/api/exporters/markdown/__snapshots__/image/noPreview/markdown.md +3 -0
  65. package/src/api/exporters/markdown/__snapshots__/lists/basic/markdown.md +8 -0
  66. package/src/api/exporters/markdown/__snapshots__/lists/nested/markdown.md +10 -0
  67. package/src/api/exporters/markdown/__snapshots__/simpleFile/basic/markdown.md +3 -0
  68. package/src/api/exporters/markdown/__snapshots__/simpleFile/button/markdown.md +1 -0
  69. package/src/api/exporters/markdown/__snapshots__/simpleFile/nested/markdown.md +7 -0
  70. package/src/api/exporters/markdown/__snapshots__/simpleImage/basic/markdown.md +3 -1
  71. package/src/api/exporters/markdown/__snapshots__/simpleImage/button/markdown.md +1 -0
  72. package/src/api/exporters/markdown/__snapshots__/simpleImage/nested/markdown.md +6 -2
  73. package/src/api/exporters/markdown/__snapshots__/simpleImage/noCaption/markdown.md +1 -0
  74. package/src/api/exporters/markdown/__snapshots__/simpleImage/noName/markdown.md +3 -0
  75. package/src/api/exporters/markdown/__snapshots__/simpleImage/noPreview/markdown.md +3 -0
  76. package/src/api/exporters/markdown/markdownExporter.ts +2 -0
  77. package/src/api/exporters/markdown/util/addSpacesToCheckboxesRehypePlugin.ts +42 -0
  78. package/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap +262 -4
  79. package/src/api/parsers/html/__snapshots__/paste/list-test.json +74 -2
  80. package/src/api/parsers/html/__snapshots__/paste/parse-basic-block-types.json +3 -1
  81. package/src/api/parsers/html/__snapshots__/paste/parse-fake-image-caption.json +3 -1
  82. package/src/api/parsers/html/__snapshots__/paste/parse-mixed-nested-lists.json +135 -10
  83. package/src/api/parsers/html/__snapshots__/paste/parse-nested-lists-with-paragraphs.json +132 -7
  84. package/src/api/parsers/html/__snapshots__/paste/parse-nested-lists.json +111 -3
  85. package/src/api/parsers/html/parseHTML.test.ts +166 -95
  86. package/src/api/testUtil/cases/customBlocks.ts +82 -33
  87. package/src/api/testUtil/cases/customInlineContent.ts +1 -1
  88. package/src/api/testUtil/cases/customStyles.ts +1 -1
  89. package/src/api/testUtil/cases/defaultSchema.ts +185 -4
  90. package/src/blocks/AudioBlockContent/AudioBlockContent.ts +163 -0
  91. package/src/blocks/AudioBlockContent/audioBlockHelpers.ts +5 -0
  92. package/src/blocks/FileBlockContent/FileBlockContent.ts +120 -0
  93. package/src/blocks/FileBlockContent/fileBlockHelpers.ts +377 -0
  94. package/src/blocks/ImageBlockContent/ImageBlockContent.ts +134 -354
  95. package/src/blocks/ImageBlockContent/imageBlockHelpers.ts +6 -0
  96. package/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +3 -0
  97. package/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts +266 -0
  98. package/src/blocks/ListItemBlockContent/ListItemKeyboardShortcuts.ts +2 -1
  99. package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +1 -0
  100. package/src/blocks/VideoBlockContent/VideoBlockContent.ts +181 -0
  101. package/src/blocks/VideoBlockContent/videoBlockHelpers.ts +6 -0
  102. package/src/blocks/defaultBlockTypeGuards.ts +53 -1
  103. package/src/blocks/defaultBlocks.ts +11 -2
  104. package/src/editor/Block.css +89 -27
  105. package/src/editor/BlockNoteEditor.ts +24 -10
  106. package/src/editor/BlockNoteSchema.ts +12 -3
  107. package/src/editor/transformPasted.ts +2 -1
  108. package/src/extensions/{ImagePanel/ImageToolbarPlugin.ts → FilePanel/FilePanelPlugin.ts} +22 -25
  109. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +14 -2
  110. package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +72 -2
  111. package/src/extensions/TableHandles/TableHandlesPlugin.ts +27 -27
  112. package/src/extensions/TextAlignment/TextAlignmentExtension.ts +7 -1
  113. package/src/i18n/locales/en.ts +117 -11
  114. package/src/i18n/locales/fr.ts +118 -11
  115. package/src/i18n/locales/index.ts +8 -2
  116. package/src/i18n/locales/is.ts +295 -0
  117. package/src/i18n/locales/ja.ts +323 -0
  118. package/src/i18n/locales/ko.ts +307 -0
  119. package/src/i18n/locales/nl.ts +108 -8
  120. package/src/i18n/locales/pl.ts +287 -0
  121. package/src/i18n/locales/pt.ts +295 -0
  122. package/src/i18n/locales/vi.ts +295 -0
  123. package/src/i18n/locales/zh.ts +123 -8
  124. package/src/index.ts +9 -2
  125. package/src/pm-nodes/BlockContainer.ts +18 -6
  126. package/src/schema/blocks/createSpec.ts +1 -0
  127. package/src/schema/blocks/internal.ts +10 -0
  128. package/src/schema/blocks/types.ts +40 -5
  129. package/src/util/string.ts +12 -0
  130. package/types/src/api/exporters/markdown/util/addSpacesToCheckboxesRehypePlugin.d.ts +7 -0
  131. package/types/src/api/testUtil/cases/customBlocks.d.ts +272 -54
  132. package/types/src/api/testUtil/cases/customInlineContent.d.ts +222 -16
  133. package/types/src/api/testUtil/cases/customStyles.d.ts +222 -16
  134. package/types/src/blocks/AudioBlockContent/AudioBlockContent.d.ts +101 -0
  135. package/types/src/blocks/AudioBlockContent/audioBlockHelpers.d.ts +3 -0
  136. package/types/src/blocks/FileBlockContent/FileBlockContent.d.ts +93 -0
  137. package/types/src/blocks/FileBlockContent/fileBlockHelpers.d.ts +30 -0
  138. package/types/src/blocks/ImageBlockContent/ImageBlockContent.d.ts +50 -14
  139. package/types/src/blocks/ImageBlockContent/imageBlockHelpers.d.ts +4 -0
  140. package/types/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.d.ts +55 -0
  141. package/types/src/blocks/VideoBlockContent/VideoBlockContent.d.ts +129 -0
  142. package/types/src/blocks/VideoBlockContent/videoBlockHelpers.d.ts +4 -0
  143. package/types/src/blocks/defaultBlockTypeGuards.d.ts +6 -1
  144. package/types/src/blocks/defaultBlocks.d.ts +444 -32
  145. package/types/src/editor/BlockNoteEditor.d.ts +12 -5
  146. package/types/src/extensions/{ImagePanel/ImageToolbarPlugin.d.ts → FilePanel/FilePanelPlugin.d.ts} +9 -12
  147. package/types/src/extensions/TableHandles/TableHandlesPlugin.d.ts +1 -1
  148. package/types/src/i18n/locales/en.d.ts +56 -7
  149. package/types/src/i18n/locales/fr.d.ts +2 -184
  150. package/types/src/i18n/locales/index.d.ts +7 -1
  151. package/types/src/i18n/locales/is.d.ts +2 -0
  152. package/types/src/i18n/locales/ja.d.ts +2 -0
  153. package/types/src/i18n/locales/ko.d.ts +2 -0
  154. package/types/src/i18n/locales/pl.d.ts +2 -0
  155. package/types/src/i18n/locales/pt.d.ts +2 -0
  156. package/types/src/i18n/locales/vi.d.ts +2 -0
  157. package/types/src/index.d.ts +8 -2
  158. package/types/src/pm-nodes/BlockContainer.d.ts +1 -1
  159. package/types/src/schema/blocks/internal.d.ts +1 -1
  160. package/types/src/schema/blocks/types.d.ts +25 -1
  161. package/types/src/util/string.d.ts +1 -0
  162. /package/src/blocks/{ImageBlockContent → FileBlockContent}/uploadToTmpFilesDotOrg_DEV_ONLY.ts +0 -0
  163. /package/types/src/blocks/{ImageBlockContent → FileBlockContent}/uploadToTmpFilesDotOrg_DEV_ONLY.d.ts +0 -0
@@ -13,15 +13,9 @@ BASIC STYLES
13
13
  flex-direction: column;
14
14
  }
15
15
 
16
- /*Ensures block content inside React node views spans editor width*/
17
- .bn-react-node-view-renderer {
18
- display: flex;
19
- flex-grow: 1;
20
- }
21
-
22
16
  .bn-block-content {
17
+ display: flex;
23
18
  padding: 3px 0;
24
- flex-grow: 1;
25
19
  transition: font-size 0.2s;
26
20
  width: 100%;
27
21
  /*
@@ -36,6 +30,13 @@ BASIC STYLES
36
30
  /*margin: 0px;*/
37
31
  }
38
32
 
33
+ .bn-block-content.ProseMirror-selectednode > *,
34
+ /* Case for node view renderers */
35
+ .ProseMirror-selectednode > .bn-block-content > * {
36
+ border-radius: 4px;
37
+ outline: 4px solid rgb(100, 160, 255);
38
+ }
39
+
39
40
  /*
40
41
  NESTED BLOCKS
41
42
  */
@@ -184,6 +185,28 @@ NESTED BLOCKS
184
185
  gap: 1.2em;
185
186
  }
186
187
 
188
+ /* Checked */
189
+ .bn-block-content[data-content-type="checkListItem"] > div {
190
+ display: flex;
191
+ }
192
+
193
+ .bn-block-content[data-content-type="checkListItem"] > div > div > input {
194
+ margin: 0 1.2em 0 0;
195
+ cursor: pointer;
196
+ }
197
+
198
+ .bn-block-content[data-content-type="checkListItem"][data-checked="true"] .bn-inline-content {
199
+ text-decoration: line-through;
200
+ }
201
+
202
+ .bn-block-content[data-text-alignment="center"] {
203
+ justify-content: center;
204
+ }
205
+
206
+ .bn-block-content[data-text-alignment="right"] {
207
+ justify-content: flex-end;
208
+ }
209
+
187
210
  /* No list nesting */
188
211
  .bn-block-outer[data-prev-type="bulletListItem"]
189
212
  > .bn-block
@@ -235,49 +258,75 @@ NESTED BLOCKS
235
258
  content: "▪";
236
259
  }
237
260
 
238
- /* IMAGES */
261
+ /* FILES */
262
+
263
+ /* Add block button & default preview */
264
+ [data-file-block] .bn-file-block-content-wrapper:has(.bn-add-file-button),
265
+ [data-file-block] .bn-file-block-content-wrapper:has(.bn-file-default-preview) {
266
+ width: 100%;
267
+ }
239
268
 
240
- [data-content-type="image"] .bn-image-block-content-wrapper {
269
+ [data-file-block] .bn-file-block-content-wrapper {
270
+ cursor: pointer;
241
271
  display: flex;
242
272
  flex-direction: column;
243
- justify-content: center;
273
+ justify-content: stretch;
244
274
  user-select: none;
245
- width: 100%;
246
275
  }
247
276
 
248
- [data-content-type="image"] .bn-add-image-button {
249
- display: flex;
250
- flex-direction: row;
277
+ [data-file-block] .bn-add-file-button {
251
278
  align-items: center;
252
- gap: 8px;
253
- background-color: whitesmoke;
279
+ background-color: rgb(242, 241, 238);
254
280
  border-radius: 4px;
281
+ color: rgb(125, 121, 122);
255
282
  cursor: pointer;
283
+ display: flex;
284
+ flex-direction: row;
285
+ gap: 10px;
256
286
  padding: 12px;
257
287
  width: 100%;
258
288
  }
259
289
 
260
- [data-content-type="image"] .bn-add-image-button:hover {
261
- background-color: gainsboro;
290
+ [data-file-block] .bn-add-file-button:hover {
291
+ background-color: rgb(225, 225, 225);
262
292
  }
263
293
 
264
- [data-content-type="image"] .bn-add-image-button-icon {
265
- background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M20 5H4V19L13.2923 9.70649C13.6828 9.31595 14.3159 9.31591 14.7065 9.70641L20 15.0104V5ZM2 3.9934C2 3.44476 2.45531 3 2.9918 3H21.0082C21.556 3 22 3.44495 22 3.9934V20.0066C22 20.5552 21.5447 21 21.0082 21H2.9918C2.44405 21 2 20.5551 2 20.0066V3.9934ZM8 11C6.89543 11 6 10.1046 6 9C6 7.89543 6.89543 7 8 7C9.10457 7 10 7.89543 10 9C10 10.1046 9.10457 11 8 11Z'%3E%3C/path%3E%3C/svg%3E");
294
+ [data-file-block] .bn-add-file-button-icon {
266
295
  width: 24px;
267
296
  height: 24px;
268
297
  }
269
298
 
270
- [data-content-type="image"] .bn-add-image-button-text {
271
- color: black;
299
+ [data-file-block] .bn-add-file-button .bn-add-file-button-text {
300
+ font-size: 0.9rem;
272
301
  }
273
302
 
274
- [data-content-type="image"] .bn-image-and-caption-wrapper {
303
+ [data-file-block] .bn-file-and-caption-wrapper {
275
304
  display: flex;
276
305
  flex-direction: column;
277
306
  border-radius: 4px;
278
307
  }
279
308
 
280
- [data-content-type="image"] .bn-image-wrapper {
309
+ [data-file-block] .bn-file-default-preview {
310
+ align-items: center;
311
+ border-radius: 4px;
312
+ display: flex;
313
+ flex-direction: row;
314
+ gap: 4px;
315
+ padding: 4px;
316
+ width: 100%;
317
+ }
318
+
319
+ [data-file-block] .bn-file-default-preview:hover,
320
+ .ProseMirror-selectednode .bn-file-default-preview {
321
+ background-color: rgb(225, 225, 225);
322
+ }
323
+
324
+ [data-file-block] .bn-file-default-preview-icon {
325
+ width: 24px;
326
+ height: 24px;
327
+ }
328
+
329
+ [data-file-block] .bn-visual-media-wrapper {
281
330
  display: flex;
282
331
  flex-direction: row;
283
332
  align-items: center;
@@ -285,12 +334,12 @@ NESTED BLOCKS
285
334
  width: fit-content;
286
335
  }
287
336
 
288
- [data-content-type="image"] .bn-image {
337
+ [data-file-block] .bn-visual-media {
289
338
  border-radius: 4px;
290
339
  max-width: 100%;
291
340
  }
292
341
 
293
- [data-content-type="image"] .bn-image-resize-handle {
342
+ [data-file-block] .bn-visual-media-resize-handle {
294
343
  position: absolute;
295
344
  width: 8px;
296
345
  height: 30px;
@@ -300,8 +349,17 @@ NESTED BLOCKS
300
349
  cursor: ew-resize;
301
350
  }
302
351
 
303
- [data-content-type="image"] .caption {
352
+ [data-content-type="audio"] > .bn-file-block-content-wrapper, .bn-audio {
353
+ width: 100%;
354
+ }
355
+
356
+ [data-file-block] .bn-file-caption {
304
357
  font-size: 0.8em;
358
+ padding-block: 4px;
359
+ }
360
+
361
+ [data-file-block] .bn-file-caption:empty {
362
+ padding-block: 0;
305
363
  }
306
364
 
307
365
  /* PLACEHOLDERS*/
@@ -392,17 +450,21 @@ NESTED BLOCKS
392
450
 
393
451
  /* TEXT ALIGNMENT */
394
452
  [data-text-alignment="left"] {
453
+ justify-content: flex-start;
395
454
  text-align: left;
396
455
  }
397
456
 
398
457
  [data-text-alignment="center"] {
458
+ justify-content: center;
399
459
  text-align: center;
400
460
  }
401
461
 
402
462
  [data-text-alignment="right"] {
463
+ justify-content: flex-end;
403
464
  text-align: right;
404
465
  }
405
466
 
406
467
  [data-text-alignment="justify"] {
468
+ justify-content: flex-start;
407
469
  text-align: justify;
408
470
  }
@@ -26,8 +26,8 @@ import {
26
26
  DefaultStyleSchema,
27
27
  PartialBlock,
28
28
  } from "../blocks/defaultBlocks";
29
+ import { FilePanelProsemirrorPlugin } from "../extensions/FilePanel/FilePanelPlugin";
29
30
  import { FormattingToolbarProsemirrorPlugin } from "../extensions/FormattingToolbar/FormattingToolbarPlugin";
30
- import { ImagePanelProsemirrorPlugin } from "../extensions/ImagePanel/ImageToolbarPlugin";
31
31
  import { LinkToolbarProsemirrorPlugin } from "../extensions/LinkToolbar/LinkToolbarPlugin";
32
32
  import { SideMenuProsemirrorPlugin } from "../extensions/SideMenu/SideMenuPlugin";
33
33
  import { SuggestionMenuProseMirrorPlugin } from "../extensions/SuggestionMenu/SuggestionPlugin";
@@ -113,9 +113,16 @@ export type BlockNoteEditorOptions<
113
113
  /**
114
114
  * A custom function to handle file uploads.
115
115
  * @param file The file that should be uploaded.
116
- * @returns The URL of the uploaded file.
116
+ * @returns The URL of the uploaded file OR an object containing props that should be set on the file block (such as an id)
117
117
  */
118
- uploadFile: (file: File) => Promise<string>;
118
+ uploadFile: (file: File) => Promise<string | Record<string, any>>;
119
+
120
+ /**
121
+ * Resolve a URL of a file block to one that can be displayed or downloaded. This can be used for creating authenticated URL or
122
+ * implementing custom protocols / schemes
123
+ * @returns The URL that's
124
+ */
125
+ resolveFileUrl: (url: string) => Promise<string>;
119
126
 
120
127
  /**
121
128
  * When enabled, allows for collaboration between multiple users.
@@ -187,13 +194,21 @@ export class BlockNoteEditor<
187
194
  ISchema,
188
195
  SSchema
189
196
  >;
190
- public readonly imagePanel?: ImagePanelProsemirrorPlugin<ISchema, SSchema>;
197
+ public readonly filePanel?: FilePanelProsemirrorPlugin<
198
+ BSchema,
199
+ ISchema,
200
+ SSchema
201
+ >;
191
202
  public readonly tableHandles?: TableHandlesProsemirrorPlugin<
192
203
  ISchema,
193
204
  SSchema
194
205
  >;
195
206
 
196
- public readonly uploadFile: ((file: File) => Promise<string>) | undefined;
207
+ public readonly uploadFile:
208
+ | ((file: File) => Promise<string | Record<string, any>>)
209
+ | undefined;
210
+
211
+ public readonly resolveFileUrl: (url: string) => Promise<string>;
197
212
 
198
213
  public static create<
199
214
  BSchema extends BlockSchema = DefaultBlockSchema,
@@ -254,10 +269,8 @@ export class BlockNoteEditor<
254
269
  this.linkToolbar = new LinkToolbarProsemirrorPlugin(this);
255
270
  this.sideMenu = new SideMenuProsemirrorPlugin(this);
256
271
  this.suggestionMenus = new SuggestionMenuProseMirrorPlugin(this);
257
- if (checkDefaultBlockTypeInSchema("image", this)) {
258
- // Type guards only work on `const`s? Not working for `this`
259
- this.imagePanel = new ImagePanelProsemirrorPlugin(this as any);
260
- }
272
+ this.filePanel = new FilePanelProsemirrorPlugin(this as any);
273
+
261
274
  if (checkDefaultBlockTypeInSchema("table", this)) {
262
275
  this.tableHandles = new TableHandlesProsemirrorPlugin(this as any);
263
276
  }
@@ -282,7 +295,7 @@ export class BlockNoteEditor<
282
295
  this.linkToolbar.plugin,
283
296
  this.sideMenu.plugin,
284
297
  this.suggestionMenus.plugin,
285
- ...(this.imagePanel ? [this.imagePanel.plugin] : []),
298
+ ...(this.filePanel ? [this.filePanel.plugin] : []),
286
299
  ...(this.tableHandles ? [this.tableHandles.plugin] : []),
287
300
  PlaceholderPlugin(this, newOptions.placeholders),
288
301
  ];
@@ -291,6 +304,7 @@ export class BlockNoteEditor<
291
304
  extensions.push(blockNoteUIExtension);
292
305
 
293
306
  this.uploadFile = newOptions.uploadFile;
307
+ this.resolveFileUrl = newOptions.resolveFileUrl || (async (url) => url);
294
308
 
295
309
  if (newOptions.collaboration && newOptions.initialContent) {
296
310
  console.warn(
@@ -23,6 +23,15 @@ import type {
23
23
  } from "../schema/blocks/types";
24
24
  import type { BlockNoteEditor } from "./BlockNoteEditor";
25
25
 
26
+ function removeUndefined<T extends Record<string, any> | undefined>(obj: T): T {
27
+ if (!obj) {
28
+ return obj;
29
+ }
30
+ return Object.fromEntries(
31
+ Object.entries(obj).filter(([, value]) => value !== undefined)
32
+ ) as T;
33
+ }
34
+
26
35
  export class BlockNoteSchema<
27
36
  BSchema extends BlockSchema,
28
37
  ISchema extends InlineContentSchema,
@@ -84,10 +93,10 @@ export class BlockNoteSchema<
84
93
  inlineContentSpecs?: InlineContentSpecs;
85
94
  styleSpecs?: StyleSpecs;
86
95
  }) {
87
- this.blockSpecs = opts?.blockSpecs || defaultBlockSpecs;
96
+ this.blockSpecs = removeUndefined(opts?.blockSpecs) || defaultBlockSpecs;
88
97
  this.inlineContentSpecs =
89
- opts?.inlineContentSpecs || defaultInlineContentSpecs;
90
- this.styleSpecs = opts?.styleSpecs || defaultStyleSpecs;
98
+ removeUndefined(opts?.inlineContentSpecs) || defaultInlineContentSpecs;
99
+ this.styleSpecs = removeUndefined(opts?.styleSpecs) || defaultStyleSpecs;
91
100
 
92
101
  this.blockSchema = getBlockSchemaFromSpecs(this.blockSpecs) as any;
93
102
  this.inlineContentSchema = getInlineContentSchemaFromSpecs(
@@ -40,7 +40,8 @@ export function transformPasted(slice: Slice, view: EditorView) {
40
40
 
41
41
  if (
42
42
  nestedChild.type.name === "bulletListItem" ||
43
- nestedChild.type.name === "numberedListItem"
43
+ nestedChild.type.name === "numberedListItem" ||
44
+ nestedChild.type.name === "checkListItem"
44
45
  ) {
45
46
  content.push(f.child(i + 1));
46
47
  f = removeChild(f, i + 1);
@@ -1,30 +1,29 @@
1
1
  import { EditorState, Plugin, PluginKey, PluginView } from "prosemirror-state";
2
2
  import { EditorView } from "prosemirror-view";
3
3
 
4
- import { DefaultBlockSchema } from "../../blocks/defaultBlocks";
5
4
  import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
6
5
  import { UiElementPosition } from "../../extensions-shared/UiElementPosition";
7
6
  import type {
8
7
  BlockFromConfig,
8
+ BlockSchema,
9
+ FileBlockConfig,
9
10
  InlineContentSchema,
10
11
  StyleSchema,
11
12
  } from "../../schema";
12
13
  import { EventEmitter } from "../../util/EventEmitter";
13
14
 
14
- export type ImagePanelState<
15
+ export type FilePanelState<
15
16
  I extends InlineContentSchema,
16
17
  S extends StyleSchema
17
18
  > = UiElementPosition & {
18
19
  // TODO: This typing is not quite right (children should be from BSchema)
19
- block: BlockFromConfig<DefaultBlockSchema["image"], I, S>;
20
+ block: BlockFromConfig<FileBlockConfig, I, S>;
20
21
  };
21
22
 
22
- export class ImagePanelView<
23
- I extends InlineContentSchema,
24
- S extends StyleSchema
25
- > implements PluginView
23
+ export class FilePanelView<I extends InlineContentSchema, S extends StyleSchema>
24
+ implements PluginView
26
25
  {
27
- public state?: ImagePanelState<I, S>;
26
+ public state?: FilePanelState<I, S>;
28
27
  public emitUpdate: () => void;
29
28
 
30
29
  public prevWasEditable: boolean | null = null;
@@ -32,11 +31,11 @@ export class ImagePanelView<
32
31
  constructor(
33
32
  private readonly pluginKey: PluginKey,
34
33
  private readonly pmView: EditorView,
35
- emitUpdate: (state: ImagePanelState<I, S>) => void
34
+ emitUpdate: (state: FilePanelState<I, S>) => void
36
35
  ) {
37
36
  this.emitUpdate = () => {
38
37
  if (!this.state) {
39
- throw new Error("Attempting to update uninitialized image panel");
38
+ throw new Error("Attempting to update uninitialized file panel");
40
39
  }
41
40
 
42
41
  emitUpdate(this.state);
@@ -77,7 +76,7 @@ export class ImagePanelView<
77
76
 
78
77
  update(view: EditorView, prevState: EditorState) {
79
78
  const pluginState: {
80
- block: BlockFromConfig<DefaultBlockSchema["image"], I, S>;
79
+ block: BlockFromConfig<FileBlockConfig, I, S>;
81
80
  } = this.pluginKey.getState(view.state);
82
81
 
83
82
  if (!this.state?.show && pluginState.block) {
@@ -124,27 +123,26 @@ export class ImagePanelView<
124
123
  }
125
124
  }
126
125
 
127
- const imagePanelPluginKey = new PluginKey("ImagePanelPlugin");
126
+ const filePanelPluginKey = new PluginKey("FilePanelPlugin");
128
127
 
129
- export class ImagePanelProsemirrorPlugin<
128
+ export class FilePanelProsemirrorPlugin<
129
+ B extends BlockSchema,
130
130
  I extends InlineContentSchema,
131
131
  S extends StyleSchema
132
132
  > extends EventEmitter<any> {
133
- private view: ImagePanelView<I, S> | undefined;
133
+ private view: FilePanelView<I, S> | undefined;
134
134
  public readonly plugin: Plugin;
135
135
 
136
- constructor(
137
- _editor: BlockNoteEditor<{ image: DefaultBlockSchema["image"] }, I, S>
138
- ) {
136
+ constructor(_editor: BlockNoteEditor<B, I, S>) {
139
137
  super();
140
138
  this.plugin = new Plugin<{
141
- block: BlockFromConfig<DefaultBlockSchema["image"], I, S> | undefined;
139
+ block: BlockFromConfig<FileBlockConfig, I, S> | undefined;
142
140
  }>({
143
- key: imagePanelPluginKey,
141
+ key: filePanelPluginKey,
144
142
  view: (editorView) => {
145
- this.view = new ImagePanelView(
143
+ this.view = new FilePanelView(
146
144
  // editor,
147
- imagePanelPluginKey,
145
+ filePanelPluginKey,
148
146
  editorView,
149
147
  (state) => {
150
148
  this.emit("update", state);
@@ -168,9 +166,8 @@ export class ImagePanelProsemirrorPlugin<
168
166
  };
169
167
  },
170
168
  apply: (transaction) => {
171
- const block:
172
- | BlockFromConfig<DefaultBlockSchema["image"], I, S>
173
- | undefined = transaction.getMeta(imagePanelPluginKey)?.block;
169
+ const block: BlockFromConfig<FileBlockConfig, I, S> | undefined =
170
+ transaction.getMeta(filePanelPluginKey)?.block;
174
171
 
175
172
  return {
176
173
  block,
@@ -184,7 +181,7 @@ export class ImagePanelProsemirrorPlugin<
184
181
  return this.view?.state?.show || false;
185
182
  }
186
183
 
187
- public onUpdate(callback: (state: ImagePanelState<I, S>) => void) {
184
+ public onUpdate(callback: (state: FilePanelState<I, S>) => void) {
188
185
  return this.on("update", callback);
189
186
  }
190
187
 
@@ -1,4 +1,4 @@
1
- import { isNodeSelection, posToDOMRect } from "@tiptap/core";
1
+ import { isNodeSelection, isTextSelection, posToDOMRect } from "@tiptap/core";
2
2
  import { EditorState, Plugin, PluginKey, PluginView } from "prosemirror-state";
3
3
  import { EditorView } from "prosemirror-view";
4
4
 
@@ -22,7 +22,19 @@ export class FormattingToolbarView implements PluginView {
22
22
  state: EditorState;
23
23
  from: number;
24
24
  to: number;
25
- }) => boolean = ({ state }) => !state.selection.empty;
25
+ }) => boolean = ({ state, from, to, view }) => {
26
+ const { doc, selection } = state;
27
+ const { empty } = selection;
28
+
29
+ // Sometime check for `empty` is not enough.
30
+ // Doubleclick an empty paragraph returns a node size of 2.
31
+ // So we check also for an empty text size.
32
+ const isEmptyTextBlock =
33
+ !doc.textBetween(from, to).length && isTextSelection(state.selection);
34
+
35
+ // check view.hasFocus so that the toolbar doesn't show up when the editor is not focused or when for example a code block is focused
36
+ return !(!view.hasFocus() || empty || isEmptyTextBlock);
37
+ };
26
38
 
27
39
  constructor(
28
40
  private readonly editor: BlockNoteEditor<
@@ -144,6 +144,19 @@ export function getDefaultSlashMenuItems<
144
144
  });
145
145
  }
146
146
 
147
+ if (checkDefaultBlockTypeInSchema("checkListItem", editor)) {
148
+ items.push({
149
+ onItemClick: () => {
150
+ insertOrUpdateBlock(editor, {
151
+ type: "checkListItem",
152
+ });
153
+ },
154
+ badge: formatKeyboardShortcut("Mod-Shift-9"),
155
+ key: "check_list",
156
+ ...editor.dictionary.slash_menu.check_list,
157
+ });
158
+ }
159
+
147
160
  if (checkDefaultBlockTypeInSchema("paragraph", editor)) {
148
161
  items.push({
149
162
  onItemClick: () => {
@@ -188,9 +201,9 @@ export function getDefaultSlashMenuItems<
188
201
  type: "image",
189
202
  });
190
203
 
191
- // Immediately open the image toolbar
204
+ // Immediately open the file toolbar
192
205
  editor.prosemirrorView.dispatch(
193
- editor._tiptapEditor.state.tr.setMeta(editor.imagePanel!.plugin, {
206
+ editor._tiptapEditor.state.tr.setMeta(editor.filePanel!.plugin, {
194
207
  block: insertedBlock,
195
208
  })
196
209
  );
@@ -200,6 +213,63 @@ export function getDefaultSlashMenuItems<
200
213
  });
201
214
  }
202
215
 
216
+ if (checkDefaultBlockTypeInSchema("video", editor)) {
217
+ items.push({
218
+ onItemClick: () => {
219
+ const insertedBlock = insertOrUpdateBlock(editor, {
220
+ type: "video",
221
+ });
222
+
223
+ // Immediately open the file toolbar
224
+ editor.prosemirrorView.dispatch(
225
+ editor._tiptapEditor.state.tr.setMeta(editor.filePanel!.plugin, {
226
+ block: insertedBlock,
227
+ })
228
+ );
229
+ },
230
+ key: "video",
231
+ ...editor.dictionary.slash_menu.video,
232
+ });
233
+ }
234
+
235
+ if (checkDefaultBlockTypeInSchema("audio", editor)) {
236
+ items.push({
237
+ onItemClick: () => {
238
+ const insertedBlock = insertOrUpdateBlock(editor, {
239
+ type: "audio",
240
+ });
241
+
242
+ // Immediately open the file toolbar
243
+ editor.prosemirrorView.dispatch(
244
+ editor._tiptapEditor.state.tr.setMeta(editor.filePanel!.plugin, {
245
+ block: insertedBlock,
246
+ })
247
+ );
248
+ },
249
+ key: "audio",
250
+ ...editor.dictionary.slash_menu.audio,
251
+ });
252
+ }
253
+
254
+ if (checkDefaultBlockTypeInSchema("file", editor)) {
255
+ items.push({
256
+ onItemClick: () => {
257
+ const insertedBlock = insertOrUpdateBlock(editor, {
258
+ type: "file",
259
+ });
260
+
261
+ // Immediately open the file toolbar
262
+ editor.prosemirrorView.dispatch(
263
+ editor._tiptapEditor.state.tr.setMeta(editor.filePanel!.plugin, {
264
+ block: insertedBlock,
265
+ })
266
+ );
267
+ },
268
+ key: "image",
269
+ ...editor.dictionary.slash_menu.file,
270
+ });
271
+ }
272
+
203
273
  return items;
204
274
  }
205
275
 
@@ -7,9 +7,9 @@ import {
7
7
  BlockFromConfigNoChildren,
8
8
  BlockSchemaWithBlock,
9
9
  InlineContentSchema,
10
- SpecificBlock,
11
10
  StyleSchema,
12
11
  } from "../../schema";
12
+ import { checkBlockIsDefaultType } from "../../blocks/defaultBlockTypeGuards";
13
13
  import { EventEmitter } from "../../util/EventEmitter";
14
14
  import { getDraggableBlockFromCoords } from "../SideMenu/SideMenuPlugin";
15
15
 
@@ -83,7 +83,6 @@ function hideElementsWithClassNames(classNames: string[]) {
83
83
  }
84
84
 
85
85
  export class TableHandlesView<
86
- BSchema extends BlockSchemaWithBlock<"table", DefaultBlockSchema["table"]>,
87
86
  I extends InlineContentSchema,
88
87
  S extends StyleSchema
89
88
  > implements PluginView
@@ -146,28 +145,15 @@ export class TableHandlesView<
146
145
 
147
146
  const blockEl = getDraggableBlockFromCoords(cellRect, this.pmView);
148
147
  if (!blockEl) {
149
- throw new Error(
150
- "Found table cell element, but could not find surrounding blockContent element."
151
- );
152
- }
153
- this.tableId = blockEl.id;
154
-
155
- if (
156
- this.state !== undefined &&
157
- this.state.show &&
158
- this.tableId === blockEl.id &&
159
- this.state.rowIndex === rowIndex &&
160
- this.state.colIndex === colIndex
161
- ) {
162
148
  return;
163
149
  }
164
150
 
165
- let block: Block<any, any, any> | undefined = undefined;
151
+ let tableBlock: Block<any, any, any> | undefined = undefined;
166
152
 
167
153
  // Copied from `getBlock`. We don't use `getBlock` since we also need the PM
168
154
  // node for the table, so we would effectively be doing the same work twice.
169
155
  this.editor._tiptapEditor.state.doc.descendants((node, pos) => {
170
- if (typeof block !== "undefined") {
156
+ if (typeof tableBlock !== "undefined") {
171
157
  return false;
172
158
  }
173
159
 
@@ -175,24 +161,44 @@ export class TableHandlesView<
175
161
  return true;
176
162
  }
177
163
 
178
- block = nodeToBlock(
164
+ const block = nodeToBlock(
179
165
  node,
180
166
  this.editor.schema.blockSchema,
181
167
  this.editor.schema.inlineContentSchema,
182
168
  this.editor.schema.styleSchema,
183
169
  this.editor.blockCache
184
170
  );
185
- this.tablePos = pos + 1;
171
+
172
+ if (checkBlockIsDefaultType("table", block, this.editor)) {
173
+ this.tablePos = pos + 1;
174
+ tableBlock = block;
175
+ }
186
176
 
187
177
  return false;
188
178
  });
189
179
 
180
+ if (!tableBlock) {
181
+ return;
182
+ }
183
+
184
+ this.tableId = blockEl.id;
185
+
186
+ if (
187
+ this.state !== undefined &&
188
+ this.state.show &&
189
+ this.tableId === blockEl.id &&
190
+ this.state.rowIndex === rowIndex &&
191
+ this.state.colIndex === colIndex
192
+ ) {
193
+ return;
194
+ }
195
+
190
196
  this.state = {
191
197
  show: true,
192
198
  referencePosCell: cellRect,
193
199
  referencePosTable: tableRect,
194
200
 
195
- block: block! as SpecificBlock<BSchema, "table", I, S>,
201
+ block: tableBlock,
196
202
  colIndex: colIndex,
197
203
  rowIndex: rowIndex,
198
204
 
@@ -365,13 +371,7 @@ export class TableHandlesProsemirrorPlugin<
365
371
  I extends InlineContentSchema,
366
372
  S extends StyleSchema
367
373
  > extends EventEmitter<any> {
368
- private view:
369
- | TableHandlesView<
370
- BlockSchemaWithBlock<"table", DefaultBlockSchema["table"]>,
371
- I,
372
- S
373
- >
374
- | undefined;
374
+ private view: TableHandlesView<I, S> | undefined;
375
375
  public readonly plugin: Plugin;
376
376
 
377
377
  constructor(