@blocknote/core 0.29.1 → 0.30.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 (69) 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 +1479 -1339
  5. package/dist/blocknote.js.map +1 -1
  6. package/dist/locales.cjs +1 -1
  7. package/dist/locales.cjs.map +1 -1
  8. package/dist/locales.js +751 -9
  9. package/dist/locales.js.map +1 -1
  10. package/dist/style.css +1 -1
  11. package/dist/tsconfig.tsbuildinfo +1 -1
  12. package/dist/webpack-stats.json +1 -1
  13. package/package.json +3 -6
  14. package/src/api/blockManipulation/commands/insertBlocks/__snapshots__/insertBlocks.test.ts.snap +0 -7
  15. package/src/api/blockManipulation/commands/mergeBlocks/__snapshots__/mergeBlocks.test.ts.snap +0 -5
  16. package/src/api/blockManipulation/commands/moveBlocks/__snapshots__/moveBlocks.test.ts.snap +0 -20
  17. package/src/api/blockManipulation/commands/replaceBlocks/__snapshots__/replaceBlocks.test.ts.snap +0 -12
  18. package/src/api/blockManipulation/commands/splitBlock/__snapshots__/splitBlock.test.ts.snap +0 -6
  19. package/src/api/blockManipulation/commands/updateBlock/__snapshots__/updateBlock.test.ts.snap +0 -17
  20. package/src/api/clipboard/fromClipboard/pasteExtension.ts +19 -1
  21. package/src/blocks/AudioBlockContent/AudioBlockContent.ts +5 -0
  22. package/src/blocks/CodeBlockContent/CodeBlockContent.ts +32 -18
  23. package/src/blocks/FileBlockContent/FileBlockContent.ts +5 -0
  24. package/src/blocks/FileBlockContent/helpers/render/createResizableFileBlockWrapper.ts +9 -2
  25. package/src/blocks/HeadingBlockContent/HeadingBlockContent.ts +3 -0
  26. package/src/blocks/ImageBlockContent/ImageBlockContent.ts +10 -2
  27. package/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +9 -25
  28. package/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts +14 -3
  29. package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +9 -26
  30. package/src/blocks/ListItemBlockContent/getListItemContent.ts +115 -0
  31. package/src/blocks/ParagraphBlockContent/ParagraphBlockContent.ts +6 -2
  32. package/src/blocks/QuoteBlockContent/QuoteBlockContent.ts +6 -1
  33. package/src/blocks/TableBlockContent/TableBlockContent.ts +71 -14
  34. package/src/blocks/VideoBlockContent/VideoBlockContent.ts +10 -2
  35. package/src/blocks/defaultBlockHelpers.ts +16 -0
  36. package/src/editor/Block.css +2 -1
  37. package/src/editor/BlockNoteEditor.ts +103 -60
  38. package/src/editor/BlockNoteExtensions.ts +14 -5
  39. package/src/extensions/Collaboration/CursorPlugin.ts +152 -0
  40. package/src/extensions/Collaboration/SyncPlugin.ts +15 -0
  41. package/src/extensions/Collaboration/UndoPlugin.ts +14 -0
  42. package/src/extensions/FilePanel/FilePanelPlugin.ts +31 -22
  43. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +2 -4
  44. package/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts +3 -0
  45. package/src/i18n/locales/index.ts +2 -0
  46. package/src/i18n/locales/ru.ts +2 -2
  47. package/src/i18n/locales/sk.ts +355 -0
  48. package/src/i18n/locales/zh-tw.ts +390 -0
  49. package/src/pm-nodes/BlockContainer.ts +7 -6
  50. package/src/schema/blocks/createSpec.ts +1 -1
  51. package/src/schema/blocks/internal.ts +0 -1
  52. package/src/schema/blocks/types.ts +2 -1
  53. package/types/src/api/blockManipulation/setupTestEnv.d.ts +8 -4
  54. package/types/src/blocks/ImageBlockContent/ImageBlockContent.d.ts +8 -4
  55. package/types/src/blocks/ListItemBlockContent/getListItemContent.d.ts +28 -0
  56. package/types/src/blocks/VideoBlockContent/VideoBlockContent.d.ts +8 -4
  57. package/types/src/blocks/defaultBlockHelpers.d.ts +1 -0
  58. package/types/src/blocks/defaultBlocks.d.ts +16 -8
  59. package/types/src/editor/BlockNoteEditor.d.ts +18 -1
  60. package/types/src/extensions/Collaboration/CursorPlugin.d.ts +31 -0
  61. package/types/src/extensions/Collaboration/SyncPlugin.d.ts +7 -0
  62. package/types/src/extensions/Collaboration/UndoPlugin.d.ts +6 -0
  63. package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +1 -1
  64. package/types/src/i18n/locales/index.d.ts +2 -0
  65. package/types/src/i18n/locales/sk.d.ts +313 -0
  66. package/types/src/i18n/locales/zh-tw.d.ts +2 -0
  67. package/types/src/schema/blocks/types.d.ts +2 -1
  68. package/src/extensions/Collaboration/createCollaborationExtensions.ts +0 -147
  69. package/types/src/extensions/Collaboration/createCollaborationExtensions.d.ts +0 -17
@@ -101,7 +101,8 @@ import {
101
101
  } from "@tiptap/pm/state";
102
102
  import { dropCursor } from "prosemirror-dropcursor";
103
103
  import { EditorView } from "prosemirror-view";
104
- import { ySyncPluginKey } from "y-prosemirror";
104
+ import { undoCommand, redoCommand, ySyncPluginKey } from "y-prosemirror";
105
+ import { undo, redo } from "@tiptap/pm/history";
105
106
  import { createInternalHTMLSerializer } from "../api/exporters/html/internalHTMLSerializer.js";
106
107
  import { inlineContentToNodes } from "../api/nodeConversions/blockToNode.js";
107
108
  import { nodeToBlock } from "../api/nodeConversions/nodeToBlock.js";
@@ -114,27 +115,29 @@ import { CodeBlockOptions } from "../blocks/CodeBlockContent/CodeBlockContent.js
114
115
  import type { ThreadStore, User } from "../comments/index.js";
115
116
  import "../style.css";
116
117
  import { EventEmitter } from "../util/EventEmitter.js";
118
+ import { CursorPlugin } from "../extensions/Collaboration/CursorPlugin.js";
117
119
 
118
120
  export type BlockNoteExtensionFactory = (
119
- editor: BlockNoteEditor<any, any, any>
121
+ editor: BlockNoteEditor<any, any, any>,
120
122
  ) => BlockNoteExtension;
121
123
 
122
124
  export type BlockNoteExtension =
123
125
  | AnyExtension
124
126
  | {
125
127
  plugin: Plugin;
128
+ priority?: number;
126
129
  };
127
130
 
128
131
  export type BlockCache<
129
132
  BSchema extends BlockSchema = any,
130
133
  ISchema extends InlineContentSchema = any,
131
- SSchema extends StyleSchema = any
134
+ SSchema extends StyleSchema = any,
132
135
  > = WeakMap<Node, Block<BSchema, ISchema, SSchema>>;
133
136
 
134
137
  export type BlockNoteEditorOptions<
135
138
  BSchema extends BlockSchema,
136
139
  ISchema extends InlineContentSchema,
137
- SSchema extends StyleSchema
140
+ SSchema extends StyleSchema,
138
141
  > = {
139
142
  /**
140
143
  * Whether changes to blocks (like indentation, creating lists, changing headings) should be animated or not. Defaults to `true`.
@@ -357,7 +360,7 @@ export type BlockNoteEditorOptions<
357
360
  */
358
361
  uploadFile: (
359
362
  file: File,
360
- blockId?: string
363
+ blockId?: string,
361
364
  ) => Promise<string | Record<string, any>>;
362
365
 
363
366
  /**
@@ -389,7 +392,7 @@ const blockNoteTipTapOptions = {
389
392
  export class BlockNoteEditor<
390
393
  BSchema extends BlockSchema = DefaultBlockSchema,
391
394
  ISchema extends InlineContentSchema = DefaultInlineContentSchema,
392
- SSchema extends StyleSchema = DefaultStyleSchema
395
+ SSchema extends StyleSchema = DefaultStyleSchema,
393
396
  > extends EventEmitter<{
394
397
  create: void;
395
398
  }> {
@@ -412,11 +415,10 @@ export class BlockNoteEditor<
412
415
  */
413
416
  public readonly headless: boolean = false;
414
417
 
415
- public readonly _tiptapEditor:
416
- | Omit<BlockNoteTipTapEditor, "view"> & {
417
- view: EditorView | undefined;
418
- contentComponent: any;
419
- } = undefined as any; // TODO: Type should actually reflect that it can be `undefined` in headless mode
418
+ public readonly _tiptapEditor: Omit<BlockNoteTipTapEditor, "view"> & {
419
+ view: EditorView | undefined;
420
+ contentComponent: any;
421
+ } = undefined as any; // TODO: Type should actually reflect that it can be `undefined` in headless mode
420
422
 
421
423
  /**
422
424
  * Used by React to store a reference to an `ElementRenderer` helper utility to make sure we can render React elements
@@ -471,6 +473,8 @@ export class BlockNoteEditor<
471
473
 
472
474
  private readonly showSelectionPlugin: ShowSelectionPlugin;
473
475
 
476
+ private readonly cursorPlugin: CursorPlugin;
477
+
474
478
  /**
475
479
  * The `uploadFile` method is what the editor uses when files need to be uploaded (for example when selecting an image to upload).
476
480
  * This method should set when creating the editor as this is application-specific.
@@ -505,37 +509,37 @@ export class BlockNoteEditor<
505
509
  public static create<
506
510
  BSchema extends BlockSchema = DefaultBlockSchema,
507
511
  ISchema extends InlineContentSchema = DefaultInlineContentSchema,
508
- SSchema extends StyleSchema = DefaultStyleSchema
512
+ SSchema extends StyleSchema = DefaultStyleSchema,
509
513
  >(options: Partial<BlockNoteEditorOptions<BSchema, ISchema, SSchema>> = {}) {
510
514
  return new BlockNoteEditor<BSchema, ISchema, SSchema>(options);
511
515
  }
512
516
 
513
517
  protected constructor(
514
- protected readonly options: Partial<BlockNoteEditorOptions<any, any, any>>
518
+ protected readonly options: Partial<BlockNoteEditorOptions<any, any, any>>,
515
519
  ) {
516
520
  super();
517
521
  const anyOpts = options as any;
518
522
  if (anyOpts.onEditorContentChange) {
519
523
  throw new Error(
520
- "onEditorContentChange initialization option is deprecated, use <BlockNoteView onChange={...} />, the useEditorChange(...) hook, or editor.onChange(...)"
524
+ "onEditorContentChange initialization option is deprecated, use <BlockNoteView onChange={...} />, the useEditorChange(...) hook, or editor.onChange(...)",
521
525
  );
522
526
  }
523
527
 
524
528
  if (anyOpts.onTextCursorPositionChange) {
525
529
  throw new Error(
526
- "onTextCursorPositionChange initialization option is deprecated, use <BlockNoteView onSelectionChange={...} />, the useEditorSelectionChange(...) hook, or editor.onSelectionChange(...)"
530
+ "onTextCursorPositionChange initialization option is deprecated, use <BlockNoteView onSelectionChange={...} />, the useEditorSelectionChange(...) hook, or editor.onSelectionChange(...)",
527
531
  );
528
532
  }
529
533
 
530
534
  if (anyOpts.onEditorReady) {
531
535
  throw new Error(
532
- "onEditorReady is deprecated. Editor is immediately ready for use after creation."
536
+ "onEditorReady is deprecated. Editor is immediately ready for use after creation.",
533
537
  );
534
538
  }
535
539
 
536
540
  if (anyOpts.editable) {
537
541
  throw new Error(
538
- "editable initialization option is deprecated, use <BlockNoteView editable={true/false} />, or alternatively editor.isEditable = true/false"
542
+ "editable initialization option is deprecated, use <BlockNoteView editable={true/false} />, or alternatively editor.isEditable = true/false",
539
543
  );
540
544
  }
541
545
 
@@ -621,18 +625,19 @@ export class BlockNoteEditor<
621
625
  this.tableHandles = this.extensions["tableHandles"] as any;
622
626
  this.comments = this.extensions["comments"] as any;
623
627
  this.showSelectionPlugin = this.extensions["showSelection"] as any;
628
+ this.cursorPlugin = this.extensions["yCursorPlugin"] as any;
624
629
 
625
630
  if (newOptions.uploadFile) {
626
631
  const uploadFile = newOptions.uploadFile;
627
- this.uploadFile = async (file, block) => {
632
+ this.uploadFile = async (file, blockId) => {
628
633
  this.onUploadStartCallbacks.forEach((callback) =>
629
- callback.apply(this, [block])
634
+ callback.apply(this, [blockId]),
630
635
  );
631
636
  try {
632
- return await uploadFile(file, block);
637
+ return await uploadFile(file, blockId);
633
638
  } finally {
634
639
  this.onUploadEndCallbacks.forEach((callback) =>
635
- callback.apply(this, [block])
640
+ callback.apply(this, [blockId]),
636
641
  );
637
642
  }
638
643
  };
@@ -642,13 +647,13 @@ export class BlockNoteEditor<
642
647
  this.headless = newOptions._headless;
643
648
 
644
649
  const collaborationEnabled =
645
- "collaboration" in this.extensions ||
650
+ "ySyncPlugin" in this.extensions ||
646
651
  "liveblocksExtension" in this.extensions;
647
652
 
648
653
  if (collaborationEnabled && newOptions.initialContent) {
649
654
  // eslint-disable-next-line no-console
650
655
  console.warn(
651
- "When using Collaboration, initialContent might cause conflicts, because changes should come from the collaboration provider"
656
+ "When using Collaboration, initialContent might cause conflicts, because changes should come from the collaboration provider",
652
657
  );
653
658
  }
654
659
 
@@ -671,7 +676,7 @@ export class BlockNoteEditor<
671
676
  if (!Array.isArray(initialContent) || initialContent.length === 0) {
672
677
  throw new Error(
673
678
  "initialContent must be a non-empty array of blocks, received: " +
674
- initialContent
679
+ initialContent,
675
680
  );
676
681
  }
677
682
 
@@ -688,13 +693,14 @@ export class BlockNoteEditor<
688
693
 
689
694
  if (!ext.plugin) {
690
695
  throw new Error(
691
- "Extension should either be a TipTap extension or a ProseMirror plugin in a plugin property"
696
+ "Extension should either be a TipTap extension or a ProseMirror plugin in a plugin property",
692
697
  );
693
698
  }
694
699
 
695
700
  // "blocknote" extensions (prosemirror plugins)
696
701
  return Extension.create({
697
702
  name: key,
703
+ priority: ext.priority,
698
704
  addProseMirrorPlugins: () => [ext.plugin],
699
705
  });
700
706
  }),
@@ -716,7 +722,7 @@ export class BlockNoteEditor<
716
722
  class: mergeCSSClasses(
717
723
  "bn-editor",
718
724
  newOptions.defaultStyles ? "bn-default-styles" : "",
719
- newOptions.domAttributes?.editor?.class || ""
725
+ newOptions.domAttributes?.editor?.class || "",
720
726
  ),
721
727
  },
722
728
  transformPasted,
@@ -726,7 +732,7 @@ export class BlockNoteEditor<
726
732
  if (!this.headless) {
727
733
  this._tiptapEditor = BlockNoteTipTapEditor.create(
728
734
  tiptapOptions,
729
- this.schema.styleSchema
735
+ this.schema.styleSchema,
730
736
  ) as BlockNoteTipTapEditor & {
731
737
  view: any;
732
738
  contentComponent: any;
@@ -761,7 +767,7 @@ export class BlockNoteEditor<
761
767
  public exec(command: Command) {
762
768
  if (this.activeTransaction) {
763
769
  throw new Error(
764
- "`exec` should not be called within a `transact` call, move the `exec` call outside of the `transact` call"
770
+ "`exec` should not be called within a `transact` call, move the `exec` call outside of the `transact` call",
765
771
  );
766
772
  }
767
773
  const state = this._tiptapEditor.state;
@@ -786,7 +792,7 @@ export class BlockNoteEditor<
786
792
  public canExec(command: Command): boolean {
787
793
  if (this.activeTransaction) {
788
794
  throw new Error(
789
- "`canExec` should not be called within a `transact` call, move the `canExec` call outside of the `transact` call"
795
+ "`canExec` should not be called within a `transact` call, move the `canExec` call outside of the `transact` call",
790
796
  );
791
797
  }
792
798
  const state = this._tiptapEditor.state;
@@ -820,8 +826,8 @@ export class BlockNoteEditor<
820
826
  * The current active transaction, this will automatically be dispatched to the editor when the callback is complete
821
827
  * If another `transact` call is made within the callback, it will be passed the same transaction as the parent call.
822
828
  */
823
- tr: Transaction
824
- ) => T
829
+ tr: Transaction,
830
+ ) => T,
825
831
  ): T {
826
832
  if (this.activeTransaction) {
827
833
  // Already in a transaction, so we can just callback immediately
@@ -866,13 +872,28 @@ export class BlockNoteEditor<
866
872
  */
867
873
  public mount = (
868
874
  parentElement?: HTMLElement | null,
869
- contentComponent?: any
875
+ contentComponent?: any,
870
876
  ) => {
871
877
  this._tiptapEditor.mount(this, parentElement, contentComponent);
872
878
  };
873
879
 
880
+ /**
881
+ * Get the underlying prosemirror state
882
+ * @note Prefer using `editor.transact` to read the current editor state, as that will ensure the state is up to date
883
+ * @see https://prosemirror.net/docs/ref/#state.EditorState
884
+ */
885
+ public get prosemirrorState() {
886
+ if (this.activeTransaction) {
887
+ throw new Error(
888
+ "`prosemirrorState` should not be called within a `transact` call, move the `prosemirrorState` call outside of the `transact` call or use `editor.transact` to read the current editor state",
889
+ );
890
+ }
891
+ return this._tiptapEditor.state;
892
+ }
893
+
874
894
  /**
875
895
  * Get the underlying prosemirror view
896
+ * @see https://prosemirror.net/docs/ref/#view.EditorView
876
897
  */
877
898
  public get prosemirrorView() {
878
899
  return this._tiptapEditor.view;
@@ -945,7 +966,7 @@ export class BlockNoteEditor<
945
966
  * matching block was found.
946
967
  */
947
968
  public getBlock(
948
- blockIdentifier: BlockIdentifier
969
+ blockIdentifier: BlockIdentifier,
949
970
  ): Block<BSchema, ISchema, SSchema> | undefined {
950
971
  return this.transact((tr) => getBlock(tr.doc, blockIdentifier));
951
972
  }
@@ -960,7 +981,7 @@ export class BlockNoteEditor<
960
981
  * in the document.
961
982
  */
962
983
  public getPrevBlock(
963
- blockIdentifier: BlockIdentifier
984
+ blockIdentifier: BlockIdentifier,
964
985
  ): Block<BSchema, ISchema, SSchema> | undefined {
965
986
  return this.transact((tr) => getPrevBlock(tr.doc, blockIdentifier));
966
987
  }
@@ -974,7 +995,7 @@ export class BlockNoteEditor<
974
995
  * the document.
975
996
  */
976
997
  public getNextBlock(
977
- blockIdentifier: BlockIdentifier
998
+ blockIdentifier: BlockIdentifier,
978
999
  ): Block<BSchema, ISchema, SSchema> | undefined {
979
1000
  return this.transact((tr) => getNextBlock(tr.doc, blockIdentifier));
980
1001
  }
@@ -987,7 +1008,7 @@ export class BlockNoteEditor<
987
1008
  * if no matching block was found, or the block isn't nested.
988
1009
  */
989
1010
  public getParentBlock(
990
- blockIdentifier: BlockIdentifier
1011
+ blockIdentifier: BlockIdentifier,
991
1012
  ): Block<BSchema, ISchema, SSchema> | undefined {
992
1013
  return this.transact((tr) => getParentBlock(tr.doc, blockIdentifier));
993
1014
  }
@@ -999,7 +1020,7 @@ export class BlockNoteEditor<
999
1020
  */
1000
1021
  public forEachBlock(
1001
1022
  callback: (block: Block<BSchema, ISchema, SSchema>) => boolean,
1002
- reverse = false
1023
+ reverse = false,
1003
1024
  ): void {
1004
1025
  const blocks = this.document.slice();
1005
1026
 
@@ -1008,7 +1029,7 @@ export class BlockNoteEditor<
1008
1029
  }
1009
1030
 
1010
1031
  function traverseBlockArray(
1011
- blockArray: Block<BSchema, ISchema, SSchema>[]
1032
+ blockArray: Block<BSchema, ISchema, SSchema>[],
1012
1033
  ): boolean {
1013
1034
  for (const block of blockArray) {
1014
1035
  if (callback(block) === false) {
@@ -1034,7 +1055,7 @@ export class BlockNoteEditor<
1034
1055
  * Executes a callback whenever the editor's contents change.
1035
1056
  * @param callback The callback to execute.
1036
1057
  *
1037
- * @deprecated use `onChange` instead
1058
+ * @deprecated use {@link BlockNoteEditor.onChange} instead
1038
1059
  */
1039
1060
  public onEditorContentChange(callback: () => void) {
1040
1061
  this._tiptapEditor.on("update", callback);
@@ -1070,10 +1091,10 @@ export class BlockNoteEditor<
1070
1091
  */
1071
1092
  public setTextCursorPosition(
1072
1093
  targetBlock: BlockIdentifier,
1073
- placement: "start" | "end" = "start"
1094
+ placement: "start" | "end" = "start",
1074
1095
  ) {
1075
1096
  return this.transact((tr) =>
1076
- setTextCursorPosition(tr, targetBlock, placement)
1097
+ setTextCursorPosition(tr, targetBlock, placement),
1077
1098
  );
1078
1099
  }
1079
1100
 
@@ -1137,10 +1158,10 @@ export class BlockNoteEditor<
1137
1158
  public insertBlocks(
1138
1159
  blocksToInsert: PartialBlock<BSchema, ISchema, SSchema>[],
1139
1160
  referenceBlock: BlockIdentifier,
1140
- placement: "before" | "after" = "before"
1161
+ placement: "before" | "after" = "before",
1141
1162
  ) {
1142
1163
  return this.transact((tr) =>
1143
- insertBlocks(tr, blocksToInsert, referenceBlock, placement)
1164
+ insertBlocks(tr, blocksToInsert, referenceBlock, placement),
1144
1165
  );
1145
1166
  }
1146
1167
 
@@ -1153,7 +1174,7 @@ export class BlockNoteEditor<
1153
1174
  */
1154
1175
  public updateBlock(
1155
1176
  blockToUpdate: BlockIdentifier,
1156
- update: PartialBlock<BSchema, ISchema, SSchema>
1177
+ update: PartialBlock<BSchema, ISchema, SSchema>,
1157
1178
  ) {
1158
1179
  return this.transact((tr) => updateBlock(tr, blockToUpdate, update));
1159
1180
  }
@@ -1164,7 +1185,7 @@ export class BlockNoteEditor<
1164
1185
  */
1165
1186
  public removeBlocks(blocksToRemove: BlockIdentifier[]) {
1166
1187
  return this.transact(
1167
- (tr) => removeAndInsertBlocks(tr, blocksToRemove, []).removedBlocks
1188
+ (tr) => removeAndInsertBlocks(tr, blocksToRemove, []).removedBlocks,
1168
1189
  );
1169
1190
  }
1170
1191
 
@@ -1177,13 +1198,34 @@ export class BlockNoteEditor<
1177
1198
  */
1178
1199
  public replaceBlocks(
1179
1200
  blocksToRemove: BlockIdentifier[],
1180
- blocksToInsert: PartialBlock<BSchema, ISchema, SSchema>[]
1201
+ blocksToInsert: PartialBlock<BSchema, ISchema, SSchema>[],
1181
1202
  ) {
1182
1203
  return this.transact((tr) =>
1183
- removeAndInsertBlocks(tr, blocksToRemove, blocksToInsert)
1204
+ removeAndInsertBlocks(tr, blocksToRemove, blocksToInsert),
1184
1205
  );
1185
1206
  }
1186
1207
 
1208
+ /**
1209
+ * Undo the last action.
1210
+ */
1211
+ public undo() {
1212
+ if (this.options.collaboration) {
1213
+ return this.exec(undoCommand);
1214
+ }
1215
+
1216
+ return this.exec(undo);
1217
+ }
1218
+
1219
+ /**
1220
+ * Redo the last action.
1221
+ */
1222
+ public redo() {
1223
+ if (this.options.collaboration) {
1224
+ return this.exec(redoCommand);
1225
+ }
1226
+ return this.exec(redo);
1227
+ }
1228
+
1187
1229
  /**
1188
1230
  * Insert a piece of content at the current cursor position.
1189
1231
  *
@@ -1199,7 +1241,7 @@ export class BlockNoteEditor<
1199
1241
  from: tr.selection.from,
1200
1242
  to: tr.selection.to,
1201
1243
  },
1202
- nodes
1244
+ nodes,
1203
1245
  );
1204
1246
  });
1205
1247
  }
@@ -1323,7 +1365,7 @@ export class BlockNoteEditor<
1323
1365
  tr.setSelection(TextSelection.create(tr.doc, to)).addMark(
1324
1366
  from,
1325
1367
  to,
1326
- mark
1368
+ mark,
1327
1369
  );
1328
1370
  }
1329
1371
  });
@@ -1383,7 +1425,7 @@ export class BlockNoteEditor<
1383
1425
  * @returns The blocks, serialized as an HTML string.
1384
1426
  */
1385
1427
  public async blocksToHTMLLossy(
1386
- blocks: PartialBlock<BSchema, ISchema, SSchema>[] = this.document
1428
+ blocks: PartialBlock<BSchema, ISchema, SSchema>[] = this.document,
1387
1429
  ): Promise<string> {
1388
1430
  const exporter = createExternalHTMLExporter(this.pmSchema, this);
1389
1431
  return exporter.exportBlocks(blocks, {});
@@ -1399,7 +1441,7 @@ export class BlockNoteEditor<
1399
1441
  * @returns The blocks, serialized as an HTML string.
1400
1442
  */
1401
1443
  public async blocksToFullHTML(
1402
- blocks: PartialBlock<BSchema, ISchema, SSchema>[]
1444
+ blocks: PartialBlock<BSchema, ISchema, SSchema>[],
1403
1445
  ): Promise<string> {
1404
1446
  const exporter = createInternalHTMLSerializer(this.pmSchema, this);
1405
1447
  return exporter.serializeBlocks(blocks, {});
@@ -1412,7 +1454,7 @@ export class BlockNoteEditor<
1412
1454
  * @returns The blocks parsed from the HTML string.
1413
1455
  */
1414
1456
  public async tryParseHTMLToBlocks(
1415
- html: string
1457
+ html: string,
1416
1458
  ): Promise<Block<BSchema, ISchema, SSchema>[]> {
1417
1459
  return HTMLToBlocks(html, this.pmSchema);
1418
1460
  }
@@ -1424,7 +1466,7 @@ export class BlockNoteEditor<
1424
1466
  * @returns The blocks, serialized as a Markdown string.
1425
1467
  */
1426
1468
  public async blocksToMarkdownLossy(
1427
- blocks: PartialBlock<BSchema, ISchema, SSchema>[] = this.document
1469
+ blocks: PartialBlock<BSchema, ISchema, SSchema>[] = this.document,
1428
1470
  ): Promise<string> {
1429
1471
  return blocksToMarkdown(blocks, this.pmSchema, this, {});
1430
1472
  }
@@ -1437,7 +1479,7 @@ export class BlockNoteEditor<
1437
1479
  * @returns The blocks parsed from the Markdown string.
1438
1480
  */
1439
1481
  public async tryParseMarkdownToBlocks(
1440
- markdown: string
1482
+ markdown: string,
1441
1483
  ): Promise<Block<BSchema, ISchema, SSchema>[]> {
1442
1484
  return markdownToBlocks(markdown, this.pmSchema);
1443
1485
  }
@@ -1448,10 +1490,11 @@ export class BlockNoteEditor<
1448
1490
  public updateCollaborationUserInfo(user: { name: string; color: string }) {
1449
1491
  if (!this.options.collaboration) {
1450
1492
  throw new Error(
1451
- "Cannot update collaboration user info when collaboration is disabled."
1493
+ "Cannot update collaboration user info when collaboration is disabled.",
1452
1494
  );
1453
1495
  }
1454
- this._tiptapEditor.commands.updateUser(user);
1496
+
1497
+ this.cursorPlugin.updateUser(user);
1455
1498
  }
1456
1499
 
1457
1500
  /**
@@ -1468,8 +1511,8 @@ export class BlockNoteEditor<
1468
1511
  * Returns the blocks that were inserted, updated, or deleted by the change that occurred.
1469
1512
  */
1470
1513
  getChanges(): BlocksChanged<BSchema, ISchema, SSchema>;
1471
- }
1472
- ) => void
1514
+ },
1515
+ ) => void,
1473
1516
  ) {
1474
1517
  if (this.headless) {
1475
1518
  // Note: would be nice if this is possible in headless mode as well
@@ -1504,7 +1547,7 @@ export class BlockNoteEditor<
1504
1547
  */
1505
1548
  public onSelectionChange(
1506
1549
  callback: (editor: BlockNoteEditor<BSchema, ISchema, SSchema>) => void,
1507
- includeSelectionChangedByRemote?: boolean
1550
+ includeSelectionChangedByRemote?: boolean,
1508
1551
  ) {
1509
1552
  if (this.headless) {
1510
1553
  return;
@@ -1581,7 +1624,7 @@ export class BlockNoteEditor<
1581
1624
  pluginState?: {
1582
1625
  deleteTriggerCharacter?: boolean;
1583
1626
  ignoreQueryLength?: boolean;
1584
- }
1627
+ },
1585
1628
  ) {
1586
1629
  if (!this.prosemirrorView) {
1587
1630
  return;
@@ -10,7 +10,9 @@ import { createDropFileExtension } from "../api/clipboard/fromClipboard/fileDrop
10
10
  import { createPasteFromClipboardExtension } from "../api/clipboard/fromClipboard/pasteExtension.js";
11
11
  import { createCopyToClipboardExtension } from "../api/clipboard/toClipboard/copyExtension.js";
12
12
  import { BackgroundColorExtension } from "../extensions/BackgroundColor/BackgroundColorExtension.js";
13
- import { createCollaborationExtensions } from "../extensions/Collaboration/createCollaborationExtensions.js";
13
+ import { CursorPlugin } from "../extensions/Collaboration/CursorPlugin.js";
14
+ import { UndoPlugin } from "../extensions/Collaboration/UndoPlugin.js";
15
+ import { SyncPlugin } from "../extensions/Collaboration/SyncPlugin.js";
14
16
  import { CommentMark } from "../extensions/Comments/CommentMark.js";
15
17
  import { CommentsPlugin } from "../extensions/Comments/CommentsPlugin.js";
16
18
  import type { ThreadStore } from "../comments/index.js";
@@ -106,6 +108,15 @@ export const getBlockNoteExtensions = <
106
108
  ret[ext.name] = ext;
107
109
  }
108
110
 
111
+ if (opts.collaboration) {
112
+ ret["ySyncPlugin"] = new SyncPlugin(opts.collaboration.fragment);
113
+ ret["yUndoPlugin"] = new UndoPlugin();
114
+
115
+ if (opts.collaboration.provider?.awareness) {
116
+ ret["yCursorPlugin"] = new CursorPlugin(opts.collaboration);
117
+ }
118
+ }
119
+
109
120
  // Note: this is pretty hardcoded and will break when user provides plugins with same keys.
110
121
  // Define name on plugins instead and not make this a map?
111
122
  ret["formattingToolbar"] = new FormattingToolbarProsemirrorPlugin(
@@ -285,10 +296,8 @@ const getTipTapExtensions = <
285
296
 
286
297
  LINKIFY_INITIALIZED = true;
287
298
 
288
- if (opts.collaboration) {
289
- tiptapExtensions.push(...createCollaborationExtensions(opts.collaboration));
290
- } else {
291
- // disable history extension when collaboration is enabled as Yjs takes care of undo / redo
299
+ if (!opts.collaboration) {
300
+ // disable history extension when collaboration is enabled as y-prosemirror takes care of undo / redo
292
301
  tiptapExtensions.push(History);
293
302
  }
294
303