@blocknote/core 0.40.0 → 0.41.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 (102) hide show
  1. package/dist/BlockNoteSchema-COA0fsXW.cjs +11 -0
  2. package/dist/BlockNoteSchema-COA0fsXW.cjs.map +1 -0
  3. package/dist/{BlockNoteSchema-oR047ACf.js → BlockNoteSchema-CYRHak18.js} +681 -581
  4. package/dist/BlockNoteSchema-CYRHak18.js.map +1 -0
  5. package/dist/blocknote.cjs +4 -4
  6. package/dist/blocknote.cjs.map +1 -1
  7. package/dist/blocknote.js +3663 -2817
  8. package/dist/blocknote.js.map +1 -1
  9. package/dist/blocks.cjs +1 -1
  10. package/dist/blocks.js +51 -49
  11. package/dist/en-Cl87Uuyf.cjs +2 -0
  12. package/dist/en-Cl87Uuyf.cjs.map +1 -0
  13. package/dist/{en-Bq3Es3Np.js → en-njEqD7AG.js} +9 -3
  14. package/dist/en-njEqD7AG.js.map +1 -0
  15. package/dist/locales.cjs +1 -1
  16. package/dist/locales.cjs.map +1 -1
  17. package/dist/locales.js +122 -2
  18. package/dist/locales.js.map +1 -1
  19. package/dist/style.css +1 -1
  20. package/dist/tsconfig.tsbuildinfo +1 -1
  21. package/dist/webpack-stats.json +1 -1
  22. package/package.json +4 -3
  23. package/src/api/exporters/html/externalHTMLExporter.ts +1 -1
  24. package/src/api/exporters/html/util/serializeBlocksInternalHTML.ts +2 -1
  25. package/src/api/exporters/markdown/markdownExporter.ts +3 -1
  26. package/src/api/exporters/markdown/util/convertVideoToMarkdownRehypePlugin.ts +19 -0
  27. package/src/api/parsers/markdown/parseMarkdown.ts +31 -0
  28. package/src/blocks/Code/block.ts +14 -14
  29. package/src/blocks/Divider/block.ts +49 -0
  30. package/src/blocks/ListItem/BulletListItem/block.ts +9 -1
  31. package/src/blocks/ListItem/CheckListItem/block.ts +1 -0
  32. package/src/blocks/ListItem/NumberedListItem/block.ts +9 -1
  33. package/src/blocks/Table/block.ts +56 -1
  34. package/src/blocks/ToggleWrapper/createToggleWrapper.ts +1 -1
  35. package/src/blocks/defaultBlocks.ts +16 -14
  36. package/src/blocks/index.ts +1 -0
  37. package/src/editor/Block.css +14 -20
  38. package/src/editor/BlockNoteEditor.test.ts +40 -0
  39. package/src/editor/BlockNoteEditor.ts +248 -465
  40. package/src/editor/BlockNoteExtensions.ts +3 -1
  41. package/src/editor/managers/BlockManager.ts +251 -0
  42. package/src/editor/managers/CollaborationManager.ts +212 -0
  43. package/src/editor/managers/EventManager.ts +134 -0
  44. package/src/editor/managers/ExportManager.ts +137 -0
  45. package/src/editor/managers/ExtensionManager.ts +130 -0
  46. package/src/editor/managers/SelectionManager.ts +114 -0
  47. package/src/editor/managers/StateManager.ts +238 -0
  48. package/src/editor/managers/StyleManager.ts +182 -0
  49. package/src/editor/managers/index.ts +11 -0
  50. package/src/extensions/Comments/CommentsPlugin.ts +7 -4
  51. package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +10 -0
  52. package/src/i18n/locales/ar.ts +6 -0
  53. package/src/i18n/locales/de.ts +6 -0
  54. package/src/i18n/locales/en.ts +6 -0
  55. package/src/i18n/locales/es.ts +6 -0
  56. package/src/i18n/locales/fr.ts +6 -0
  57. package/src/i18n/locales/he.ts +6 -0
  58. package/src/i18n/locales/hr.ts +6 -0
  59. package/src/i18n/locales/is.ts +6 -0
  60. package/src/i18n/locales/it.ts +6 -0
  61. package/src/i18n/locales/ja.ts +6 -0
  62. package/src/i18n/locales/ko.ts +6 -0
  63. package/src/i18n/locales/nl.ts +6 -0
  64. package/src/i18n/locales/no.ts +6 -0
  65. package/src/i18n/locales/pl.ts +6 -0
  66. package/src/i18n/locales/pt.ts +6 -0
  67. package/src/i18n/locales/ru.ts +6 -0
  68. package/src/i18n/locales/sk.ts +6 -0
  69. package/src/i18n/locales/uk.ts +6 -0
  70. package/src/i18n/locales/vi.ts +6 -0
  71. package/src/i18n/locales/zh-tw.ts +6 -0
  72. package/src/i18n/locales/zh.ts +6 -0
  73. package/src/schema/blocks/createSpec.ts +1 -0
  74. package/src/util/string.ts +21 -0
  75. package/types/src/api/exporters/markdown/util/convertVideoToMarkdownRehypePlugin.d.ts +2 -0
  76. package/types/src/blocks/Divider/block.d.ts +3 -0
  77. package/types/src/blocks/Heading/block.d.ts +3 -3
  78. package/types/src/blocks/defaultBlocks.d.ts +2 -1
  79. package/types/src/blocks/index.d.ts +1 -0
  80. package/types/src/editor/BlockNoteEditor.d.ts +68 -47
  81. package/types/src/editor/BlockNoteExtensions.d.ts +2 -1
  82. package/types/src/editor/managers/BlockManager.d.ts +114 -0
  83. package/types/src/editor/managers/CollaborationManager.d.ts +115 -0
  84. package/types/src/editor/managers/EventManager.d.ts +58 -0
  85. package/types/src/editor/managers/ExportManager.d.ts +64 -0
  86. package/types/src/editor/managers/ExtensionManager.d.ts +68 -0
  87. package/types/src/editor/managers/SelectionManager.d.ts +54 -0
  88. package/types/src/editor/managers/StateManager.d.ts +115 -0
  89. package/types/src/editor/managers/StyleManager.d.ts +48 -0
  90. package/types/src/editor/managers/index.d.ts +8 -0
  91. package/types/src/extensions/Comments/CommentsPlugin.d.ts +4 -3
  92. package/types/src/i18n/locales/en.d.ts +6 -0
  93. package/types/src/i18n/locales/sk.d.ts +6 -0
  94. package/types/src/util/string.d.ts +1 -0
  95. package/dist/BlockNoteSchema-DmZ6UQfY.cjs +0 -11
  96. package/dist/BlockNoteSchema-DmZ6UQfY.cjs.map +0 -1
  97. package/dist/BlockNoteSchema-oR047ACf.js.map +0 -1
  98. package/dist/en-Bq3Es3Np.js.map +0 -1
  99. package/dist/en-D3B48eJ7.cjs +0 -2
  100. package/dist/en-D3B48eJ7.cjs.map +0 -1
  101. /package/src/api/exporters/markdown/{removeUnderlinesRehypePlugin.ts → util/removeUnderlinesRehypePlugin.ts} +0 -0
  102. /package/types/src/api/exporters/markdown/{removeUnderlinesRehypePlugin.d.ts → util/removeUnderlinesRehypePlugin.d.ts} +0 -0
@@ -6,91 +6,38 @@ import {
6
6
  FocusPosition,
7
7
  getSchema,
8
8
  InputRule,
9
- isNodeSelection,
10
9
  Mark,
11
- posToDOMRect,
12
10
  Editor as TiptapEditor,
13
11
  Node as TipTapNode,
14
12
  } from "@tiptap/core";
15
- import { redo, undo } from "@tiptap/pm/history";
16
- import {
17
- TextSelection,
18
- type Command,
19
- type Plugin,
20
- type Transaction,
21
- } from "@tiptap/pm/state";
13
+ import { type Command, type Plugin, type Transaction } from "@tiptap/pm/state";
22
14
  import { dropCursor } from "prosemirror-dropcursor";
23
15
  import { Node, Schema } from "prosemirror-model";
24
- import { redoCommand, undoCommand, ySyncPluginKey } from "y-prosemirror";
25
16
  import * as Y from "yjs";
26
17
 
27
- import { insertBlocks } from "../api/blockManipulation/commands/insertBlocks/insertBlocks.js";
28
- import {
29
- moveBlocksDown,
30
- moveBlocksUp,
31
- } from "../api/blockManipulation/commands/moveBlocks/moveBlocks.js";
32
- import {
33
- canNestBlock,
34
- canUnnestBlock,
35
- nestBlock,
36
- unnestBlock,
37
- } from "../api/blockManipulation/commands/nestBlock/nestBlock.js";
38
- import { removeAndInsertBlocks } from "../api/blockManipulation/commands/replaceBlocks/replaceBlocks.js";
39
- import {
40
- updateBlock,
41
- updateBlockTr,
42
- } from "../api/blockManipulation/commands/updateBlock/updateBlock.js";
43
- import {
44
- getBlock,
45
- getNextBlock,
46
- getParentBlock,
47
- getPrevBlock,
48
- } from "../api/blockManipulation/getBlock/getBlock.js";
49
- import { insertContentAt } from "../api/blockManipulation/insertContentAt.js";
50
- import {
51
- getSelection,
52
- getSelectionCutBlocks,
53
- setSelection,
54
- } from "../api/blockManipulation/selections/selection.js";
55
- import {
56
- getTextCursorPosition,
57
- setTextCursorPosition,
58
- } from "../api/blockManipulation/selections/textCursorPosition.js";
59
- import { createExternalHTMLExporter } from "../api/exporters/html/externalHTMLExporter.js";
60
- import { createInternalHTMLSerializer } from "../api/exporters/html/internalHTMLSerializer.js";
61
- import { blocksToMarkdown } from "../api/exporters/markdown/markdownExporter.js";
62
- import { getBlockInfoFromTransaction } from "../api/getBlockInfoFromPos.js";
63
- import {
64
- BlocksChanged,
65
- getBlocksChangedByTransaction,
66
- } from "../api/getBlocksChangedByTransaction.js";
67
- import {
68
- blockToNode,
69
- inlineContentToNodes,
70
- } from "../api/nodeConversions/blockToNode.js";
71
- import { docToBlocks } from "../api/nodeConversions/nodeToBlock.js";
72
- import { HTMLToBlocks } from "../api/parsers/html/parseHTML.js";
73
- import {
74
- markdownToBlocks,
75
- markdownToHTML,
76
- } from "../api/parsers/markdown/parseMarkdown.js";
18
+ import type { BlocksChanged } from "../api/getBlocksChangedByTransaction.js";
77
19
  import { editorHasBlockWithType } from "../blocks/defaultBlockTypeGuards.js";
20
+ import {
21
+ Block,
22
+ BlockNoteSchema,
23
+ DefaultBlockSchema,
24
+ DefaultInlineContentSchema,
25
+ DefaultStyleSchema,
26
+ PartialBlock,
27
+ } from "../blocks/index.js";
78
28
  import type { ThreadStore, User } from "../comments/index.js";
79
- import { BlockChangePlugin } from "../extensions/BlockChange/BlockChangePlugin.js";
80
- import type { CursorPlugin } from "../extensions/Collaboration/CursorPlugin.js";
81
- import type { ForkYDocPlugin } from "../extensions/Collaboration/ForkYDocPlugin.js";
82
29
  import type { CommentsPlugin } from "../extensions/Comments/CommentsPlugin.js";
83
- import { FilePanelProsemirrorPlugin } from "../extensions/FilePanel/FilePanelPlugin.js";
84
- import { FormattingToolbarProsemirrorPlugin } from "../extensions/FormattingToolbar/FormattingToolbarPlugin.js";
85
- import { LinkToolbarProsemirrorPlugin } from "../extensions/LinkToolbar/LinkToolbarPlugin.js";
86
- import { ShowSelectionPlugin } from "../extensions/ShowSelection/ShowSelectionPlugin.js";
87
- import { SideMenuProsemirrorPlugin } from "../extensions/SideMenu/SideMenuPlugin.js";
88
- import { SuggestionMenuProseMirrorPlugin } from "../extensions/SuggestionMenu/SuggestionPlugin.js";
89
- import { TableHandlesProsemirrorPlugin } from "../extensions/TableHandles/TableHandlesPlugin.js";
30
+ import type { FilePanelProsemirrorPlugin } from "../extensions/FilePanel/FilePanelPlugin.js";
31
+ import type { FormattingToolbarProsemirrorPlugin } from "../extensions/FormattingToolbar/FormattingToolbarPlugin.js";
32
+ import type { LinkToolbarProsemirrorPlugin } from "../extensions/LinkToolbar/LinkToolbarPlugin.js";
33
+ import type { ShowSelectionPlugin } from "../extensions/ShowSelection/ShowSelectionPlugin.js";
34
+ import type { SideMenuProsemirrorPlugin } from "../extensions/SideMenu/SideMenuPlugin.js";
35
+ import type { SuggestionMenuProseMirrorPlugin } from "../extensions/SuggestionMenu/SuggestionPlugin.js";
36
+ import type { TableHandlesProsemirrorPlugin } from "../extensions/TableHandles/TableHandlesPlugin.js";
90
37
  import { UniqueID } from "../extensions/UniqueID/UniqueID.js";
91
- import { Dictionary } from "../i18n/dictionary.js";
38
+ import type { Dictionary } from "../i18n/dictionary.js";
92
39
  import { en } from "../i18n/locales/index.js";
93
- import {
40
+ import type {
94
41
  BlockIdentifier,
95
42
  BlockNoteDOMAttributes,
96
43
  BlockSchema,
@@ -103,26 +50,29 @@ import {
103
50
  StyleSchema,
104
51
  StyleSpecs,
105
52
  } from "../schema/index.js";
106
- import "../style.css";
107
53
  import { mergeCSSClasses } from "../util/browser.js";
108
54
  import { EventEmitter } from "../util/EventEmitter.js";
109
- import { NoInfer, UnreachableCaseError } from "../util/typescript.js";
55
+ import type { NoInfer } from "../util/typescript.js";
110
56
  import { BlockNoteExtension } from "./BlockNoteExtension.js";
111
57
  import { getBlockNoteExtensions } from "./BlockNoteExtensions.js";
112
- import { TextCursorPosition } from "./cursorPositionTypes.js";
113
- import { Selection } from "./selectionTypes.js";
114
- import { transformPasted } from "./transformPasted.js";
115
-
116
- // TODO eventually we will want to de-couple this from the editor instance, for now it provides a default schema to use
58
+ import type { TextCursorPosition } from "./cursorPositionTypes.js";
117
59
  import {
118
- Block,
119
- BlockNoteSchema,
120
- DefaultBlockSchema,
121
- DefaultInlineContentSchema,
122
- DefaultStyleSchema,
123
- PartialBlock,
124
- } from "../blocks/index.js";
60
+ BlockManager,
61
+ CollaborationManager,
62
+ type CollaborationOptions,
63
+ EventManager,
64
+ ExportManager,
65
+ ExtensionManager,
66
+ SelectionManager,
67
+ StateManager,
68
+ StyleManager,
69
+ } from "./managers/index.js";
70
+ import type { Selection } from "./selectionTypes.js";
71
+ import { transformPasted } from "./transformPasted.js";
125
72
 
73
+ import { updateBlockTr } from "../api/blockManipulation/commands/updateBlock/updateBlock.js";
74
+ import { getBlockInfoFromTransaction } from "../api/getBlockInfoFromPos.js";
75
+ import { blockToNode } from "../api/nodeConversions/blockToNode.js";
126
76
  import "../style.css";
127
77
 
128
78
  /**
@@ -497,41 +447,62 @@ export class BlockNoteEditor<
497
447
  /**
498
448
  * The schema of the editor. The schema defines which Blocks, InlineContent, and Styles are available in the editor.
499
449
  */
500
- public readonly schema: CustomBlockNoteSchema<BSchema, ISchema, SSchema>;
450
+ public readonly schema: BlockNoteSchema<BSchema, ISchema, SSchema>;
501
451
 
502
452
  public readonly blockImplementations: BlockSpecs;
503
453
  public readonly inlineContentImplementations: InlineContentSpecs;
504
454
  public readonly styleImplementations: StyleSpecs;
505
455
 
506
- public readonly formattingToolbar: FormattingToolbarProsemirrorPlugin;
507
- public readonly linkToolbar: LinkToolbarProsemirrorPlugin<
508
- BSchema,
509
- ISchema,
510
- SSchema
511
- >;
512
- public readonly sideMenu: SideMenuProsemirrorPlugin<
456
+ public get formattingToolbar(): FormattingToolbarProsemirrorPlugin {
457
+ return this._extensionManager.formattingToolbar;
458
+ }
459
+
460
+ public get linkToolbar(): LinkToolbarProsemirrorPlugin<
513
461
  BSchema,
514
462
  ISchema,
515
463
  SSchema
516
- >;
517
- public readonly suggestionMenus: SuggestionMenuProseMirrorPlugin<
464
+ > {
465
+ return this._extensionManager.linkToolbar;
466
+ }
467
+
468
+ public get sideMenu(): SideMenuProsemirrorPlugin<BSchema, ISchema, SSchema> {
469
+ return this._extensionManager.sideMenu;
470
+ }
471
+
472
+ public get suggestionMenus(): SuggestionMenuProseMirrorPlugin<
518
473
  BSchema,
519
474
  ISchema,
520
475
  SSchema
521
- >;
522
- public readonly filePanel?: FilePanelProsemirrorPlugin<ISchema, SSchema>;
523
- public readonly tableHandles?: TableHandlesProsemirrorPlugin<
524
- ISchema,
525
- SSchema
526
- >;
527
- public readonly comments?: CommentsPlugin;
476
+ > {
477
+ return this._extensionManager.suggestionMenus;
478
+ }
528
479
 
529
- private readonly showSelectionPlugin: ShowSelectionPlugin;
480
+ public get filePanel():
481
+ | FilePanelProsemirrorPlugin<ISchema, SSchema>
482
+ | undefined {
483
+ return this._extensionManager.filePanel;
484
+ }
485
+
486
+ public get tableHandles():
487
+ | TableHandlesProsemirrorPlugin<ISchema, SSchema>
488
+ | undefined {
489
+ return this._extensionManager.tableHandles;
490
+ }
491
+
492
+ public get comments(): CommentsPlugin | undefined {
493
+ return this._collaborationManager?.comments;
494
+ }
495
+
496
+ public get showSelectionPlugin(): ShowSelectionPlugin {
497
+ return this._extensionManager.showSelectionPlugin;
498
+ }
530
499
 
531
500
  /**
532
501
  * The plugin for forking a document, only defined if in collaboration mode
533
502
  */
534
- public readonly forkYDocPlugin?: ForkYDocPlugin;
503
+ public get forkYDocPlugin() {
504
+ return this._collaborationManager?.forkYDocPlugin;
505
+ }
535
506
  /**
536
507
  * The `uploadFile` method is what the editor uses when files need to be uploaded (for example when selecting an image to upload).
537
508
  * This method should set when creating the editor as this is application-specific.
@@ -635,35 +606,59 @@ export class BlockNoteEditor<
635
606
  },
636
607
  };
637
608
 
609
+ // Initialize CollaborationManager if collaboration is enabled or if comments are configured
610
+ if (newOptions.collaboration || newOptions.comments) {
611
+ const collaborationOptions: CollaborationOptions = {
612
+ // Use collaboration options if available, otherwise provide defaults
613
+ fragment: newOptions.collaboration?.fragment || new Y.XmlFragment(),
614
+ user: newOptions.collaboration?.user || {
615
+ name: "User",
616
+ color: "#FF0000",
617
+ },
618
+ provider: newOptions.collaboration?.provider || null,
619
+ renderCursor: newOptions.collaboration?.renderCursor,
620
+ showCursorLabels: newOptions.collaboration?.showCursorLabels,
621
+ comments: newOptions.comments,
622
+ resolveUsers: newOptions.resolveUsers,
623
+ };
624
+ this._collaborationManager = new CollaborationManager(
625
+ this as any,
626
+ collaborationOptions,
627
+ );
628
+ } else {
629
+ this._collaborationManager = undefined;
630
+ }
631
+
638
632
  if (newOptions.comments && !newOptions.resolveUsers) {
639
633
  throw new Error("resolveUsers is required when using comments");
640
634
  }
641
635
 
642
- this.resolveUsers = newOptions.resolveUsers;
643
-
636
+ // @ts-ignore
644
637
  this.schema = newOptions.schema;
645
638
  this.blockImplementations = newOptions.schema.blockSpecs;
646
639
  this.inlineContentImplementations = newOptions.schema.inlineContentSpecs;
647
640
  this.styleImplementations = newOptions.schema.styleSpecs;
648
641
 
649
- this.extensions = getBlockNoteExtensions({
650
- editor: this,
651
- domAttributes: newOptions.domAttributes || {},
652
- blockSpecs: this.schema.blockSpecs,
653
- styleSpecs: this.schema.styleSpecs,
654
- inlineContentSpecs: this.schema.inlineContentSpecs,
655
- collaboration: newOptions.collaboration,
656
- trailingBlock: newOptions.trailingBlock,
657
- disableExtensions: newOptions.disableExtensions,
658
- setIdAttribute: newOptions.setIdAttribute,
659
- animations: newOptions.animations ?? true,
660
- tableHandles: editorHasBlockWithType(this, "table"),
661
- dropCursor: this.options.dropCursor ?? dropCursor,
662
- placeholders: newOptions.placeholders,
663
- tabBehavior: newOptions.tabBehavior,
664
- comments: newOptions.comments,
665
- pasteHandler: newOptions.pasteHandler,
666
- });
642
+ this.extensions = {
643
+ ...getBlockNoteExtensions({
644
+ editor: this,
645
+ domAttributes: newOptions.domAttributes || {},
646
+ blockSpecs: this.schema.blockSpecs,
647
+ styleSpecs: this.schema.styleSpecs,
648
+ inlineContentSpecs: this.schema.inlineContentSpecs,
649
+ collaboration: newOptions.collaboration,
650
+ trailingBlock: newOptions.trailingBlock,
651
+ disableExtensions: newOptions.disableExtensions,
652
+ setIdAttribute: newOptions.setIdAttribute,
653
+ animations: newOptions.animations ?? true,
654
+ tableHandles: editorHasBlockWithType(this, "table"),
655
+ dropCursor: this.options.dropCursor ?? dropCursor,
656
+ placeholders: newOptions.placeholders,
657
+ tabBehavior: newOptions.tabBehavior,
658
+ pasteHandler: newOptions.pasteHandler,
659
+ }),
660
+ ...this._collaborationManager?.initExtensions(),
661
+ } as any;
667
662
 
668
663
  // add extensions from _tiptapOptions
669
664
  (newOptions._tiptapOptions?.extensions || []).forEach((ext) => {
@@ -716,16 +711,6 @@ export class BlockNoteEditor<
716
711
  })();
717
712
  });
718
713
 
719
- this.formattingToolbar = this.extensions["formattingToolbar"] as any;
720
- this.linkToolbar = this.extensions["linkToolbar"] as any;
721
- this.sideMenu = this.extensions["sideMenu"] as any;
722
- this.suggestionMenus = this.extensions["suggestionMenus"] as any;
723
- this.filePanel = this.extensions["filePanel"] as any;
724
- this.tableHandles = this.extensions["tableHandles"] as any;
725
- this.comments = this.extensions["comments"] as any;
726
- this.showSelectionPlugin = this.extensions["showSelection"] as any;
727
- this.forkYDocPlugin = this.extensions["forkYDocPlugin"] as any;
728
-
729
714
  if (newOptions.uploadFile) {
730
715
  const uploadFile = newOptions.uploadFile;
731
716
  this.uploadFile = async (file, blockId) => {
@@ -933,13 +918,37 @@ export class BlockNoteEditor<
933
918
  }
934
919
 
935
920
  this.pmSchema.cached.blockNoteEditor = this;
921
+
922
+ // Initialize managers
923
+ this._blockManager = new BlockManager(this as any);
924
+
925
+ this._eventManager = new EventManager(this as any);
926
+ this._exportManager = new ExportManager(this as any);
927
+ this._extensionManager = new ExtensionManager(this as any);
928
+ this._selectionManager = new SelectionManager(this as any);
929
+ this._stateManager = new StateManager(
930
+ this as any,
931
+ collaborationEnabled
932
+ ? {
933
+ undo: this._collaborationManager?.getUndoCommand(),
934
+ redo: this._collaborationManager?.getRedoCommand(),
935
+ }
936
+ : undefined,
937
+ );
938
+ this._styleManager = new StyleManager(this as any);
939
+
936
940
  this.emit("create");
937
941
  }
938
942
 
939
- /**
940
- * Stores the currently active transaction, which is the accumulated transaction from all {@link dispatch} calls during a {@link transact} calls
941
- */
942
- private activeTransaction: Transaction | null = null;
943
+ // Manager instances
944
+ private readonly _blockManager: BlockManager<any, any, any>;
945
+ private readonly _collaborationManager?: CollaborationManager;
946
+ private readonly _eventManager: EventManager<any>;
947
+ private readonly _exportManager: ExportManager<any, any, any>;
948
+ private readonly _extensionManager: ExtensionManager;
949
+ private readonly _selectionManager: SelectionManager<any, any, any>;
950
+ private readonly _stateManager: StateManager;
951
+ private readonly _styleManager: StyleManager<any, any, any>;
943
952
 
944
953
  /**
945
954
  * Execute a prosemirror command. This is mostly for backwards compatibility with older code.
@@ -954,16 +963,7 @@ export class BlockNoteEditor<
954
963
  * ```
955
964
  */
956
965
  public exec(command: Command) {
957
- if (this.activeTransaction) {
958
- throw new Error(
959
- "`exec` should not be called within a `transact` call, move the `exec` call outside of the `transact` call",
960
- );
961
- }
962
- const state = this._tiptapEditor.state;
963
- const view = this._tiptapEditor.view;
964
- const dispatch = (tr: Transaction) => view.dispatch(tr);
965
-
966
- return command(state, dispatch, view);
966
+ return this._stateManager.exec(command);
967
967
  }
968
968
 
969
969
  /**
@@ -979,15 +979,7 @@ export class BlockNoteEditor<
979
979
  * ```
980
980
  */
981
981
  public canExec(command: Command): boolean {
982
- if (this.activeTransaction) {
983
- throw new Error(
984
- "`canExec` should not be called within a `transact` call, move the `canExec` call outside of the `transact` call",
985
- );
986
- }
987
- const state = this._tiptapEditor.state;
988
- const view = this._tiptapEditor.view;
989
-
990
- return command(state, undefined, view);
982
+ return this._stateManager.canExec(command);
991
983
  }
992
984
 
993
985
  /**
@@ -1018,40 +1010,7 @@ export class BlockNoteEditor<
1018
1010
  tr: Transaction,
1019
1011
  ) => T,
1020
1012
  ): T {
1021
- if (this.activeTransaction) {
1022
- // Already in a transaction, so we can just callback immediately
1023
- return callback(this.activeTransaction);
1024
- }
1025
-
1026
- try {
1027
- // Enter transaction mode, by setting a starting transaction
1028
- this.activeTransaction = this._tiptapEditor.state.tr;
1029
-
1030
- // Capture all dispatch'd transactions
1031
- const result = callback(this.activeTransaction);
1032
-
1033
- // Any transactions captured by the `dispatch` call will be stored in `this.activeTransaction`
1034
- const activeTr = this.activeTransaction;
1035
-
1036
- this.activeTransaction = null;
1037
- if (
1038
- activeTr &&
1039
- // Only dispatch if the transaction was actually modified in some way
1040
- (activeTr.docChanged ||
1041
- activeTr.selectionSet ||
1042
- activeTr.scrolledIntoView ||
1043
- activeTr.storedMarksSet ||
1044
- !activeTr.isGeneric)
1045
- ) {
1046
- // Dispatch the transaction if it was modified
1047
- this._tiptapEditor.view.dispatch(activeTr);
1048
- }
1049
-
1050
- return result;
1051
- } finally {
1052
- // We wrap this in a finally block to ensure we don't disable future transactions just because of an error in the callback
1053
- this.activeTransaction = null;
1054
- }
1013
+ return this._stateManager.transact(callback);
1055
1014
  }
1056
1015
 
1057
1016
  // TO DISCUSS
@@ -1067,12 +1026,9 @@ export class BlockNoteEditor<
1067
1026
  ext: { new (...args: any[]): T } & typeof BlockNoteExtension,
1068
1027
  key = ext.key(),
1069
1028
  ): T {
1070
- const extension = this.extensions[key] as T;
1071
- if (!extension) {
1072
- throw new Error(`Extension ${key} not found`);
1073
- }
1074
- return extension;
1029
+ return this._extensionManager.extension(ext, key);
1075
1030
  }
1031
+
1076
1032
  /**
1077
1033
  * Mount the editor to a DOM element.
1078
1034
  *
@@ -1096,12 +1052,7 @@ export class BlockNoteEditor<
1096
1052
  * @see https://prosemirror.net/docs/ref/#state.EditorState
1097
1053
  */
1098
1054
  public get prosemirrorState() {
1099
- if (this.activeTransaction) {
1100
- throw new Error(
1101
- "`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",
1102
- );
1103
- }
1104
- return this._tiptapEditor.state;
1055
+ return this._stateManager.prosemirrorState;
1105
1056
  }
1106
1057
 
1107
1058
  /**
@@ -1109,7 +1060,7 @@ export class BlockNoteEditor<
1109
1060
  * @see https://prosemirror.net/docs/ref/#view.EditorView
1110
1061
  */
1111
1062
  public get prosemirrorView() {
1112
- return this._tiptapEditor.view;
1063
+ return this._stateManager.prosemirrorView;
1113
1064
  }
1114
1065
 
1115
1066
  public get domElement() {
@@ -1165,9 +1116,7 @@ export class BlockNoteEditor<
1165
1116
  * @returns A snapshot of all top-level (non-nested) blocks in the editor.
1166
1117
  */
1167
1118
  public get document(): Block<BSchema, ISchema, SSchema>[] {
1168
- return this.transact((tr) => {
1169
- return docToBlocks(tr.doc, this.pmSchema);
1170
- });
1119
+ return this._blockManager.document;
1171
1120
  }
1172
1121
 
1173
1122
  /**
@@ -1180,7 +1129,7 @@ export class BlockNoteEditor<
1180
1129
  public getBlock(
1181
1130
  blockIdentifier: BlockIdentifier,
1182
1131
  ): Block<BSchema, ISchema, SSchema> | undefined {
1183
- return this.transact((tr) => getBlock(tr.doc, blockIdentifier));
1132
+ return this._blockManager.getBlock(blockIdentifier);
1184
1133
  }
1185
1134
 
1186
1135
  /**
@@ -1195,7 +1144,7 @@ export class BlockNoteEditor<
1195
1144
  public getPrevBlock(
1196
1145
  blockIdentifier: BlockIdentifier,
1197
1146
  ): Block<BSchema, ISchema, SSchema> | undefined {
1198
- return this.transact((tr) => getPrevBlock(tr.doc, blockIdentifier));
1147
+ return this._blockManager.getPrevBlock(blockIdentifier);
1199
1148
  }
1200
1149
 
1201
1150
  /**
@@ -1209,7 +1158,7 @@ export class BlockNoteEditor<
1209
1158
  public getNextBlock(
1210
1159
  blockIdentifier: BlockIdentifier,
1211
1160
  ): Block<BSchema, ISchema, SSchema> | undefined {
1212
- return this.transact((tr) => getNextBlock(tr.doc, blockIdentifier));
1161
+ return this._blockManager.getNextBlock(blockIdentifier);
1213
1162
  }
1214
1163
 
1215
1164
  /**
@@ -1222,7 +1171,7 @@ export class BlockNoteEditor<
1222
1171
  public getParentBlock(
1223
1172
  blockIdentifier: BlockIdentifier,
1224
1173
  ): Block<BSchema, ISchema, SSchema> | undefined {
1225
- return this.transact((tr) => getParentBlock(tr.doc, blockIdentifier));
1174
+ return this._blockManager.getParentBlock(blockIdentifier);
1226
1175
  }
1227
1176
 
1228
1177
  /**
@@ -1234,33 +1183,7 @@ export class BlockNoteEditor<
1234
1183
  callback: (block: Block<BSchema, ISchema, SSchema>) => boolean,
1235
1184
  reverse = false,
1236
1185
  ): void {
1237
- const blocks = this.document.slice();
1238
-
1239
- if (reverse) {
1240
- blocks.reverse();
1241
- }
1242
-
1243
- function traverseBlockArray(
1244
- blockArray: Block<BSchema, ISchema, SSchema>[],
1245
- ): boolean {
1246
- for (const block of blockArray) {
1247
- if (callback(block) === false) {
1248
- return false;
1249
- }
1250
-
1251
- const children = reverse
1252
- ? block.children.slice().reverse()
1253
- : block.children;
1254
-
1255
- if (!traverseBlockArray(children)) {
1256
- return false;
1257
- }
1258
- }
1259
-
1260
- return true;
1261
- }
1262
-
1263
- traverseBlockArray(blocks);
1186
+ this._blockManager.forEachBlock(callback, reverse);
1264
1187
  }
1265
1188
 
1266
1189
  /**
@@ -1292,7 +1215,7 @@ export class BlockNoteEditor<
1292
1215
  ISchema,
1293
1216
  SSchema
1294
1217
  > {
1295
- return this.transact((tr) => getTextCursorPosition(tr));
1218
+ return this._selectionManager.getTextCursorPosition();
1296
1219
  }
1297
1220
 
1298
1221
  /**
@@ -1305,9 +1228,7 @@ export class BlockNoteEditor<
1305
1228
  targetBlock: BlockIdentifier,
1306
1229
  placement: "start" | "end" = "start",
1307
1230
  ) {
1308
- return this.transact((tr) =>
1309
- setTextCursorPosition(tr, targetBlock, placement),
1310
- );
1231
+ return this._selectionManager.setTextCursorPosition(targetBlock, placement);
1311
1232
  }
1312
1233
 
1313
1234
  /**
@@ -1317,7 +1238,7 @@ export class BlockNoteEditor<
1317
1238
  * If the selection starts / ends halfway through a block, the returned data will contain the entire block.
1318
1239
  */
1319
1240
  public getSelection(): Selection<BSchema, ISchema, SSchema> | undefined {
1320
- return this.transact((tr) => getSelection(tr));
1241
+ return this._selectionManager.getSelection();
1321
1242
  }
1322
1243
 
1323
1244
  /**
@@ -1328,7 +1249,7 @@ export class BlockNoteEditor<
1328
1249
  * only the part of the block that is included in the selection.
1329
1250
  */
1330
1251
  public getSelectionCutBlocks() {
1331
- return this.transact((tr) => getSelectionCutBlocks(tr));
1252
+ return this._selectionManager.getSelectionCutBlocks();
1332
1253
  }
1333
1254
 
1334
1255
  /**
@@ -1337,7 +1258,7 @@ export class BlockNoteEditor<
1337
1258
  * @param endBlock The identifier of the block that should be the end of the selection.
1338
1259
  */
1339
1260
  public setSelection(startBlock: BlockIdentifier, endBlock: BlockIdentifier) {
1340
- return this.transact((tr) => setSelection(tr, startBlock, endBlock));
1261
+ return this._selectionManager.setSelection(startBlock, endBlock);
1341
1262
  }
1342
1263
 
1343
1264
  /**
@@ -1345,9 +1266,7 @@ export class BlockNoteEditor<
1345
1266
  * @returns True if the editor is editable, false otherwise.
1346
1267
  */
1347
1268
  public get isEditable(): boolean {
1348
- return this._tiptapEditor.isEditable === undefined
1349
- ? true
1350
- : this._tiptapEditor.isEditable;
1269
+ return this._stateManager.isEditable;
1351
1270
  }
1352
1271
 
1353
1272
  /**
@@ -1355,9 +1274,7 @@ export class BlockNoteEditor<
1355
1274
  * @param editable True to make the editor editable, or false to lock it.
1356
1275
  */
1357
1276
  public set isEditable(editable: boolean) {
1358
- if (this._tiptapEditor.options.editable !== editable) {
1359
- this._tiptapEditor.setEditable(editable);
1360
- }
1277
+ this._stateManager.isEditable = editable;
1361
1278
  }
1362
1279
 
1363
1280
  /**
@@ -1373,8 +1290,10 @@ export class BlockNoteEditor<
1373
1290
  referenceBlock: BlockIdentifier,
1374
1291
  placement: "before" | "after" = "before",
1375
1292
  ) {
1376
- return this.transact((tr) =>
1377
- insertBlocks(tr, blocksToInsert, referenceBlock, placement),
1293
+ return this._blockManager.insertBlocks(
1294
+ blocksToInsert,
1295
+ referenceBlock,
1296
+ placement,
1378
1297
  );
1379
1298
  }
1380
1299
 
@@ -1389,7 +1308,7 @@ export class BlockNoteEditor<
1389
1308
  blockToUpdate: BlockIdentifier,
1390
1309
  update: PartialBlock<BSchema, ISchema, SSchema>,
1391
1310
  ) {
1392
- return this.transact((tr) => updateBlock(tr, blockToUpdate, update));
1311
+ return this._blockManager.updateBlock(blockToUpdate, update);
1393
1312
  }
1394
1313
 
1395
1314
  /**
@@ -1397,9 +1316,7 @@ export class BlockNoteEditor<
1397
1316
  * @param blocksToRemove An array of identifiers for existing blocks that should be removed.
1398
1317
  */
1399
1318
  public removeBlocks(blocksToRemove: BlockIdentifier[]) {
1400
- return this.transact(
1401
- (tr) => removeAndInsertBlocks(tr, blocksToRemove, []).removedBlocks,
1402
- );
1319
+ return this._blockManager.removeBlocks(blocksToRemove);
1403
1320
  }
1404
1321
 
1405
1322
  /**
@@ -1413,30 +1330,21 @@ export class BlockNoteEditor<
1413
1330
  blocksToRemove: BlockIdentifier[],
1414
1331
  blocksToInsert: PartialBlock<BSchema, ISchema, SSchema>[],
1415
1332
  ) {
1416
- return this.transact((tr) =>
1417
- removeAndInsertBlocks(tr, blocksToRemove, blocksToInsert),
1418
- );
1333
+ return this._blockManager.replaceBlocks(blocksToRemove, blocksToInsert);
1419
1334
  }
1420
1335
 
1421
1336
  /**
1422
1337
  * Undo the last action.
1423
1338
  */
1424
1339
  public undo() {
1425
- if (this.options.collaboration) {
1426
- return this.exec(undoCommand);
1427
- }
1428
-
1429
- return this.exec(undo);
1340
+ return this._stateManager.undo();
1430
1341
  }
1431
1342
 
1432
1343
  /**
1433
1344
  * Redo the last action.
1434
1345
  */
1435
1346
  public redo() {
1436
- if (this.options.collaboration) {
1437
- return this.exec(redoCommand);
1438
- }
1439
- return this.exec(redo);
1347
+ return this._stateManager.redo();
1440
1348
  }
1441
1349
 
1442
1350
  /**
@@ -1448,55 +1356,14 @@ export class BlockNoteEditor<
1448
1356
  content: PartialInlineContent<ISchema, SSchema>,
1449
1357
  { updateSelection = false }: { updateSelection?: boolean } = {},
1450
1358
  ) {
1451
- const nodes = inlineContentToNodes(content, this.pmSchema);
1452
-
1453
- this.transact((tr) => {
1454
- insertContentAt(
1455
- tr,
1456
- {
1457
- from: tr.selection.from,
1458
- to: tr.selection.to,
1459
- },
1460
- nodes,
1461
- {
1462
- updateSelection,
1463
- },
1464
- );
1465
- });
1359
+ this._styleManager.insertInlineContent(content, { updateSelection });
1466
1360
  }
1467
1361
 
1468
1362
  /**
1469
1363
  * Gets the active text styles at the text cursor position or at the end of the current selection if it's active.
1470
1364
  */
1471
- public getActiveStyles() {
1472
- return this.transact((tr) => {
1473
- const styles: Styles<SSchema> = {};
1474
- const marks = tr.selection.$to.marks();
1475
-
1476
- for (const mark of marks) {
1477
- const config = this.schema.styleSchema[mark.type.name];
1478
- if (!config) {
1479
- if (
1480
- // Links are not considered styles in blocknote
1481
- mark.type.name !== "link" &&
1482
- // "blocknoteIgnore" tagged marks (such as comments) are also not considered BlockNote "styles"
1483
- !mark.type.spec.blocknoteIgnore
1484
- ) {
1485
- // eslint-disable-next-line no-console
1486
- console.warn("mark not found in styleschema", mark.type.name);
1487
- }
1488
-
1489
- continue;
1490
- }
1491
- if (config.propSchema === "boolean") {
1492
- (styles as any)[config.type] = true;
1493
- } else {
1494
- (styles as any)[config.type] = mark.attrs.stringValue;
1495
- }
1496
- }
1497
-
1498
- return styles;
1499
- });
1365
+ public getActiveStyles(): Styles<SSchema> {
1366
+ return this._styleManager.getActiveStyles();
1500
1367
  }
1501
1368
 
1502
1369
  /**
@@ -1504,19 +1371,7 @@ export class BlockNoteEditor<
1504
1371
  * @param styles The styles to add.
1505
1372
  */
1506
1373
  public addStyles(styles: Styles<SSchema>) {
1507
- for (const [style, value] of Object.entries(styles)) {
1508
- const config = this.schema.styleSchema[style];
1509
- if (!config) {
1510
- throw new Error(`style ${style} not found in styleSchema`);
1511
- }
1512
- if (config.propSchema === "boolean") {
1513
- this._tiptapEditor.commands.setMark(style);
1514
- } else if (config.propSchema === "string") {
1515
- this._tiptapEditor.commands.setMark(style, { stringValue: value });
1516
- } else {
1517
- throw new UnreachableCaseError(config.propSchema);
1518
- }
1519
- }
1374
+ this._styleManager.addStyles(styles);
1520
1375
  }
1521
1376
 
1522
1377
  /**
@@ -1524,9 +1379,7 @@ export class BlockNoteEditor<
1524
1379
  * @param styles The styles to remove.
1525
1380
  */
1526
1381
  public removeStyles(styles: Styles<SSchema>) {
1527
- for (const style of Object.keys(styles)) {
1528
- this._tiptapEditor.commands.unsetMark(style);
1529
- }
1382
+ this._styleManager.removeStyles(styles);
1530
1383
  }
1531
1384
 
1532
1385
  /**
@@ -1534,35 +1387,21 @@ export class BlockNoteEditor<
1534
1387
  * @param styles The styles to toggle.
1535
1388
  */
1536
1389
  public toggleStyles(styles: Styles<SSchema>) {
1537
- for (const [style, value] of Object.entries(styles)) {
1538
- const config = this.schema.styleSchema[style];
1539
- if (!config) {
1540
- throw new Error(`style ${style} not found in styleSchema`);
1541
- }
1542
- if (config.propSchema === "boolean") {
1543
- this._tiptapEditor.commands.toggleMark(style);
1544
- } else if (config.propSchema === "string") {
1545
- this._tiptapEditor.commands.toggleMark(style, { stringValue: value });
1546
- } else {
1547
- throw new UnreachableCaseError(config.propSchema);
1548
- }
1549
- }
1390
+ this._styleManager.toggleStyles(styles);
1550
1391
  }
1551
1392
 
1552
1393
  /**
1553
1394
  * Gets the currently selected text.
1554
1395
  */
1555
1396
  public getSelectedText() {
1556
- return this.transact((tr) => {
1557
- return tr.doc.textBetween(tr.selection.from, tr.selection.to);
1558
- });
1397
+ return this._styleManager.getSelectedText();
1559
1398
  }
1560
1399
 
1561
1400
  /**
1562
1401
  * Gets the URL of the last link in the current selection, or `undefined` if there are no links in the selection.
1563
1402
  */
1564
1403
  public getSelectedLinkUrl() {
1565
- return this._tiptapEditor.getAttributes("link").href as string | undefined;
1404
+ return this._styleManager.getSelectedLinkUrl();
1566
1405
  }
1567
1406
 
1568
1407
  /**
@@ -1571,51 +1410,35 @@ export class BlockNoteEditor<
1571
1410
  * @param text The text to display the link with.
1572
1411
  */
1573
1412
  public createLink(url: string, text?: string) {
1574
- if (url === "") {
1575
- return;
1576
- }
1577
- const mark = this.pmSchema.mark("link", { href: url });
1578
- this.transact((tr) => {
1579
- const { from, to } = tr.selection;
1580
-
1581
- if (text) {
1582
- tr.insertText(text, from, to).addMark(from, from + text.length, mark);
1583
- } else {
1584
- tr.setSelection(TextSelection.create(tr.doc, to)).addMark(
1585
- from,
1586
- to,
1587
- mark,
1588
- );
1589
- }
1590
- });
1413
+ this._styleManager.createLink(url, text);
1591
1414
  }
1592
1415
 
1593
1416
  /**
1594
1417
  * Checks if the block containing the text cursor can be nested.
1595
1418
  */
1596
1419
  public canNestBlock() {
1597
- return canNestBlock(this);
1420
+ return this._blockManager.canNestBlock();
1598
1421
  }
1599
1422
 
1600
1423
  /**
1601
1424
  * Nests the block containing the text cursor into the block above it.
1602
1425
  */
1603
1426
  public nestBlock() {
1604
- nestBlock(this);
1427
+ this._blockManager.nestBlock();
1605
1428
  }
1606
1429
 
1607
1430
  /**
1608
1431
  * Checks if the block containing the text cursor is nested.
1609
1432
  */
1610
1433
  public canUnnestBlock() {
1611
- return canUnnestBlock(this);
1434
+ return this._blockManager.canUnnestBlock();
1612
1435
  }
1613
1436
 
1614
1437
  /**
1615
1438
  * Lifts the block containing the text cursor out of its parent.
1616
1439
  */
1617
1440
  public unnestBlock() {
1618
- unnestBlock(this);
1441
+ this._blockManager.unnestBlock();
1619
1442
  }
1620
1443
 
1621
1444
  /**
@@ -1624,7 +1447,7 @@ export class BlockNoteEditor<
1624
1447
  * current blocks share a common parent, moves them out of & before it.
1625
1448
  */
1626
1449
  public moveBlocksUp() {
1627
- return moveBlocksUp(this);
1450
+ return this._blockManager.moveBlocksUp();
1628
1451
  }
1629
1452
 
1630
1453
  /**
@@ -1633,7 +1456,7 @@ export class BlockNoteEditor<
1633
1456
  * current blocks share a common parent, moves them out of & after it.
1634
1457
  */
1635
1458
  public moveBlocksDown() {
1636
- return moveBlocksDown(this);
1459
+ return this._blockManager.moveBlocksDown();
1637
1460
  }
1638
1461
 
1639
1462
  /**
@@ -1646,8 +1469,7 @@ export class BlockNoteEditor<
1646
1469
  public blocksToHTMLLossy(
1647
1470
  blocks: PartialBlock<BSchema, ISchema, SSchema>[] = this.document,
1648
1471
  ): string {
1649
- const exporter = createExternalHTMLExporter(this.pmSchema, this);
1650
- return exporter.exportBlocks(blocks, {});
1472
+ return this._exportManager.blocksToHTMLLossy(blocks);
1651
1473
  }
1652
1474
 
1653
1475
  /**
@@ -1662,8 +1484,7 @@ export class BlockNoteEditor<
1662
1484
  public blocksToFullHTML(
1663
1485
  blocks: PartialBlock<BSchema, ISchema, SSchema>[],
1664
1486
  ): string {
1665
- const exporter = createInternalHTMLSerializer(this.pmSchema, this);
1666
- return exporter.serializeBlocks(blocks, {});
1487
+ return this._exportManager.blocksToFullHTML(blocks);
1667
1488
  }
1668
1489
  /**
1669
1490
  * Parses blocks from an HTML string. Tries to create `Block` objects out of any HTML block-level elements, and
@@ -1675,7 +1496,7 @@ export class BlockNoteEditor<
1675
1496
  public tryParseHTMLToBlocks(
1676
1497
  html: string,
1677
1498
  ): Block<BSchema, ISchema, SSchema>[] {
1678
- return HTMLToBlocks(html, this.pmSchema);
1499
+ return this._exportManager.tryParseHTMLToBlocks(html);
1679
1500
  }
1680
1501
 
1681
1502
  /**
@@ -1687,7 +1508,7 @@ export class BlockNoteEditor<
1687
1508
  public blocksToMarkdownLossy(
1688
1509
  blocks: PartialBlock<BSchema, ISchema, SSchema>[] = this.document,
1689
1510
  ): string {
1690
- return blocksToMarkdown(blocks, this.pmSchema, this, {});
1511
+ return this._exportManager.blocksToMarkdownLossy(blocks);
1691
1512
  }
1692
1513
 
1693
1514
  /**
@@ -1697,43 +1518,23 @@ export class BlockNoteEditor<
1697
1518
  * @param markdown The Markdown string to parse blocks from.
1698
1519
  * @returns The blocks parsed from the Markdown string.
1699
1520
  */
1700
- public async tryParseMarkdownToBlocks(
1521
+ public tryParseMarkdownToBlocks(
1701
1522
  markdown: string,
1702
- ): Promise<Block<BSchema, ISchema, SSchema>[]> {
1703
- return markdownToBlocks(markdown, this.pmSchema);
1523
+ ): Block<BSchema, ISchema, SSchema>[] {
1524
+ return this._exportManager.tryParseMarkdownToBlocks(markdown);
1704
1525
  }
1705
1526
 
1706
1527
  /**
1707
1528
  * Updates the user info for the current user that's shown to other collaborators.
1708
1529
  */
1709
1530
  public updateCollaborationUserInfo(user: { name: string; color: string }) {
1710
- if (!this.options.collaboration) {
1531
+ if (!this._collaborationManager) {
1711
1532
  throw new Error(
1712
1533
  "Cannot update collaboration user info when collaboration is disabled.",
1713
1534
  );
1714
1535
  }
1715
1536
 
1716
- (this.extensions["yCursorPlugin"] as CursorPlugin).updateUser(user);
1717
- }
1718
-
1719
- /**
1720
- * Registers a callback which will be called before any change is applied to the editor, allowing you to cancel the change.
1721
- */
1722
- public onBeforeChange(
1723
- /**
1724
- * If the callback returns `false`, the change will be canceled & not applied to the editor.
1725
- */
1726
- callback: (
1727
- editor: BlockNoteEditor<BSchema, ISchema, SSchema>,
1728
- context: {
1729
- getChanges: () => BlocksChanged<BSchema, ISchema, SSchema>;
1730
- tr: Transaction;
1731
- },
1732
- ) => boolean | void,
1733
- ): () => void {
1734
- return (this.extensions["blockChange"] as BlockChangePlugin).subscribe(
1735
- (context) => callback(this, context),
1736
- );
1537
+ this._collaborationManager.updateUserInfo(user);
1737
1538
  }
1738
1539
 
1739
1540
  /**
@@ -1753,24 +1554,7 @@ export class BlockNoteEditor<
1753
1554
  },
1754
1555
  ) => void,
1755
1556
  ) {
1756
- const cb = ({
1757
- transaction,
1758
- appendedTransactions,
1759
- }: {
1760
- transaction: Transaction;
1761
- appendedTransactions: Transaction[];
1762
- }) => {
1763
- callback(this, {
1764
- getChanges: () =>
1765
- getBlocksChangedByTransaction(transaction, appendedTransactions),
1766
- });
1767
- };
1768
-
1769
- this._tiptapEditor.on("update", cb);
1770
-
1771
- return () => {
1772
- this._tiptapEditor.off("update", cb);
1773
- };
1557
+ return this._eventManager.onChange(callback);
1774
1558
  }
1775
1559
 
1776
1560
  /**
@@ -1783,29 +1567,19 @@ export class BlockNoteEditor<
1783
1567
  callback: (editor: BlockNoteEditor<BSchema, ISchema, SSchema>) => void,
1784
1568
  includeSelectionChangedByRemote?: boolean,
1785
1569
  ) {
1786
- const cb = (e: { transaction: Transaction }) => {
1787
- if (
1788
- e.transaction.getMeta(ySyncPluginKey) &&
1789
- !includeSelectionChangedByRemote
1790
- ) {
1791
- // selection changed because of a yjs sync (i.e.: other user was typing)
1792
- // we don't want to trigger the callback in this case
1793
- return;
1794
- }
1795
- callback(this);
1796
- };
1797
-
1798
- this._tiptapEditor.on("selectionUpdate", cb);
1799
-
1800
- return () => {
1801
- this._tiptapEditor.off("selectionUpdate", cb);
1802
- };
1570
+ return this._eventManager.onSelectionChange(
1571
+ callback,
1572
+ includeSelectionChangedByRemote,
1573
+ );
1803
1574
  }
1804
1575
 
1805
1576
  /**
1806
1577
  * A callback function that runs when the editor has been initialized.
1807
1578
  *
1808
1579
  * This can be useful for plugins to initialize themselves after the editor has been initialized.
1580
+ *
1581
+ * @param callback The callback to execute.
1582
+ * @returns A function to remove the callback.
1809
1583
  */
1810
1584
  public onCreate(callback: () => void) {
1811
1585
  this.on("create", callback);
@@ -1815,26 +1589,44 @@ export class BlockNoteEditor<
1815
1589
  };
1816
1590
  }
1817
1591
 
1818
- public getSelectionBoundingBox() {
1819
- if (!this.prosemirrorView) {
1820
- return undefined;
1821
- }
1822
-
1823
- const { selection } = this.prosemirrorState;
1824
-
1825
- // support for CellSelections
1826
- const { ranges } = selection;
1827
- const from = Math.min(...ranges.map((range) => range.$from.pos));
1828
- const to = Math.max(...ranges.map((range) => range.$to.pos));
1592
+ /**
1593
+ * A callback function that runs when the editor has been mounted.
1594
+ *
1595
+ * This can be useful for plugins to initialize themselves after the editor has been mounted.
1596
+ *
1597
+ * @param callback The callback to execute.
1598
+ * @returns A function to remove the callback.
1599
+ */
1600
+ public onMount(
1601
+ callback: (ctx: {
1602
+ editor: BlockNoteEditor<BSchema, ISchema, SSchema>;
1603
+ }) => void,
1604
+ ) {
1605
+ this._eventManager.onMount(callback);
1606
+ }
1829
1607
 
1830
- if (isNodeSelection(selection)) {
1831
- const node = this.prosemirrorView.nodeDOM(from) as HTMLElement;
1832
- if (node) {
1833
- return node.getBoundingClientRect();
1834
- }
1835
- }
1608
+ /**
1609
+ * A callback function that runs when the editor has been unmounted.
1610
+ *
1611
+ * This can be useful for plugins to clean up themselves after the editor has been unmounted.
1612
+ *
1613
+ * @param callback The callback to execute.
1614
+ * @returns A function to remove the callback.
1615
+ */
1616
+ public onUnmount(
1617
+ callback: (ctx: {
1618
+ editor: BlockNoteEditor<BSchema, ISchema, SSchema>;
1619
+ }) => void,
1620
+ ) {
1621
+ this._eventManager.onUnmount(callback);
1622
+ }
1836
1623
 
1837
- return posToDOMRect(this.prosemirrorView, from, to);
1624
+ /**
1625
+ * Gets the bounding box of the current selection.
1626
+ * @returns The bounding box of the current selection.
1627
+ */
1628
+ public getSelectionBoundingBox() {
1629
+ return this._selectionManager.getSelectionBoundingBox();
1838
1630
  }
1839
1631
 
1840
1632
  public get isEmpty() {
@@ -1892,15 +1684,7 @@ export class BlockNoteEditor<
1892
1684
  * @param raw Whether to paste the HTML as is, or to convert it to BlockNote HTML.
1893
1685
  */
1894
1686
  public pasteHTML(html: string, raw = false) {
1895
- let htmlToPaste = html;
1896
- if (!raw) {
1897
- const blocks = this.tryParseHTMLToBlocks(html);
1898
- htmlToPaste = this.blocksToFullHTML(blocks);
1899
- }
1900
- if (!htmlToPaste) {
1901
- return;
1902
- }
1903
- this.prosemirrorView?.pasteHTML(htmlToPaste);
1687
+ this._exportManager.pasteHTML(html, raw);
1904
1688
  }
1905
1689
 
1906
1690
  /**
@@ -1908,7 +1692,7 @@ export class BlockNoteEditor<
1908
1692
  * @param text The text to paste.
1909
1693
  */
1910
1694
  public pasteText(text: string) {
1911
- return this.prosemirrorView?.pasteText(text);
1695
+ return this._exportManager.pasteText(text);
1912
1696
  }
1913
1697
 
1914
1698
  /**
@@ -1916,7 +1700,6 @@ export class BlockNoteEditor<
1916
1700
  * @param markdown The markdown to paste.
1917
1701
  */
1918
1702
  public pasteMarkdown(markdown: string) {
1919
- const html = markdownToHTML(markdown);
1920
- return this.pasteHTML(html);
1703
+ return this._exportManager.pasteMarkdown(markdown);
1921
1704
  }
1922
1705
  }