@blocknote/core 0.30.1 → 0.31.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (120) hide show
  1. package/dist/blocknote.cjs +9 -9
  2. package/dist/blocknote.cjs.map +1 -1
  3. package/dist/blocknote.js +2793 -2213
  4. package/dist/blocknote.js.map +1 -1
  5. package/dist/{en-D4taoCs4.cjs → en-BXVKCwYt.cjs} +2 -2
  6. package/dist/en-BXVKCwYt.cjs.map +1 -0
  7. package/dist/{en-B7ycW7c8.js → en-qGo6sk9V.js} +2 -3
  8. package/dist/en-qGo6sk9V.js.map +1 -0
  9. package/dist/locales.cjs +1 -1
  10. package/dist/locales.cjs.map +1 -1
  11. package/dist/locales.js +20 -39
  12. package/dist/locales.js.map +1 -1
  13. package/dist/style.css +1 -1
  14. package/dist/tsconfig.tsbuildinfo +1 -1
  15. package/dist/webpack-stats.json +1 -1
  16. package/package.json +5 -6
  17. package/src/api/blockManipulation/commands/insertBlocks/insertBlocks.ts +2 -3
  18. package/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.ts +1 -1
  19. package/src/api/blockManipulation/commands/updateBlock/__snapshots__/updateBlock.test.ts.snap +2816 -0
  20. package/src/api/blockManipulation/commands/updateBlock/updateBlock.test.ts +158 -0
  21. package/src/api/blockManipulation/commands/updateBlock/updateBlock.ts +87 -17
  22. package/src/api/blockManipulation/selections/selection.ts +48 -1
  23. package/src/api/blockManipulation/selections/{textCursorPosition/textCursorPosition.ts → textCursorPosition.ts} +7 -7
  24. package/src/api/getBlockInfoFromPos.ts +1 -1
  25. package/src/api/nodeConversions/blockToNode.ts +5 -2
  26. package/src/api/nodeConversions/nodeToBlock.ts +203 -8
  27. package/src/api/pmUtil.ts +3 -3
  28. package/src/blocks/CodeBlockContent/CodeBlockContent.ts +6 -6
  29. package/src/blocks/FileBlockContent/helpers/render/createAddFileButton.ts +1 -1
  30. package/src/blocks/TableBlockContent/TableBlockContent.ts +32 -2
  31. package/src/editor/Block.css +27 -1
  32. package/src/editor/BlockNoteEditor.test.ts +7 -0
  33. package/src/editor/BlockNoteEditor.ts +124 -39
  34. package/src/editor/BlockNoteExtension.ts +26 -0
  35. package/src/editor/BlockNoteExtensions.ts +28 -12
  36. package/src/editor/BlockNoteTipTapEditor.ts +23 -2
  37. package/src/extensions/Collaboration/CursorPlugin.ts +13 -7
  38. package/src/extensions/Collaboration/ForkYDocPlugin.test.ts +166 -0
  39. package/src/extensions/Collaboration/ForkYDocPlugin.ts +174 -0
  40. package/src/extensions/Collaboration/SyncPlugin.ts +7 -4
  41. package/src/extensions/Collaboration/UndoPlugin.ts +7 -4
  42. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor-forked.json +30 -0
  43. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor.json +30 -0
  44. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-forked.html +1 -0
  45. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap.html +1 -0
  46. package/src/extensions/Comments/CommentsPlugin.ts +79 -70
  47. package/src/extensions/FilePanel/FilePanelPlugin.ts +54 -49
  48. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +60 -26
  49. package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +26 -21
  50. package/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.ts +49 -42
  51. package/src/extensions/Placeholder/PlaceholderPlugin.ts +115 -108
  52. package/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.ts +183 -170
  53. package/src/extensions/ShowSelection/ShowSelectionPlugin.ts +26 -19
  54. package/src/extensions/SideMenu/SideMenuPlugin.ts +23 -18
  55. package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +172 -168
  56. package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +4 -4
  57. package/src/extensions/Suggestions/SuggestionMarks.ts +175 -0
  58. package/src/extensions/TableHandles/TableHandlesPlugin.ts +157 -150
  59. package/src/i18n/locales/ar.ts +0 -1
  60. package/src/i18n/locales/de.ts +0 -1
  61. package/src/i18n/locales/en.ts +0 -1
  62. package/src/i18n/locales/es.ts +0 -1
  63. package/src/i18n/locales/fr.ts +0 -1
  64. package/src/i18n/locales/hr.ts +0 -1
  65. package/src/i18n/locales/is.ts +0 -1
  66. package/src/i18n/locales/it.ts +0 -1
  67. package/src/i18n/locales/ja.ts +0 -1
  68. package/src/i18n/locales/ko.ts +0 -1
  69. package/src/i18n/locales/nl.ts +0 -1
  70. package/src/i18n/locales/no.ts +0 -1
  71. package/src/i18n/locales/pl.ts +0 -1
  72. package/src/i18n/locales/pt.ts +0 -1
  73. package/src/i18n/locales/ru.ts +0 -1
  74. package/src/i18n/locales/sk.ts +0 -1
  75. package/src/i18n/locales/uk.ts +0 -1
  76. package/src/i18n/locales/vi.ts +0 -1
  77. package/src/i18n/locales/zh-tw.ts +0 -1
  78. package/src/i18n/locales/zh.ts +0 -1
  79. package/src/index.ts +18 -8
  80. package/src/pm-nodes/BlockContainer.ts +1 -1
  81. package/src/pm-nodes/BlockGroup.ts +1 -1
  82. package/src/pm-nodes/Doc.ts +1 -0
  83. package/types/src/api/blockManipulation/commands/insertBlocks/insertBlocks.d.ts +1 -1
  84. package/types/src/api/blockManipulation/commands/updateBlock/updateBlock.d.ts +3 -1
  85. package/types/src/api/blockManipulation/selections/selection.d.ts +10 -0
  86. package/types/src/api/blockManipulation/selections/{textCursorPosition/textCursorPosition.d.ts → textCursorPosition.d.ts} +2 -2
  87. package/types/src/api/nodeConversions/nodeToBlock.d.ts +39 -2
  88. package/types/src/api/pmUtil.d.ts +3 -3
  89. package/types/src/blocks/TableBlockContent/TableBlockContent.d.ts +9 -1
  90. package/types/src/editor/BlockNoteEditor.d.ts +62 -10
  91. package/types/src/editor/BlockNoteExtension.d.ts +9 -0
  92. package/types/src/editor/BlockNoteExtensions.d.ts +2 -2
  93. package/types/src/editor/BlockNoteTipTapEditor.d.ts +2 -2
  94. package/types/src/extensions/Collaboration/CursorPlugin.d.ts +3 -3
  95. package/types/src/extensions/Collaboration/ForkYDocPlugin.d.ts +41 -0
  96. package/types/src/extensions/Collaboration/SyncPlugin.d.ts +3 -3
  97. package/types/src/extensions/Collaboration/UndoPlugin.d.ts +3 -3
  98. package/types/src/extensions/Comments/CommentsPlugin.d.ts +3 -4
  99. package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +4 -4
  100. package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +6 -5
  101. package/types/src/extensions/LinkToolbar/LinkToolbarPlugin.d.ts +4 -4
  102. package/types/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.d.ts +3 -3
  103. package/types/src/extensions/Placeholder/PlaceholderPlugin.d.ts +3 -3
  104. package/types/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.d.ts +3 -3
  105. package/types/src/extensions/ShowSelection/ShowSelectionPlugin.d.ts +3 -3
  106. package/types/src/extensions/SideMenu/SideMenuPlugin.d.ts +4 -4
  107. package/types/src/extensions/SuggestionMenu/SuggestionPlugin.d.ts +3 -4
  108. package/types/src/extensions/Suggestions/SuggestionMarks.d.ts +4 -0
  109. package/types/src/extensions/TableHandles/TableHandlesPlugin.d.ts +6 -6
  110. package/types/src/i18n/locales/en.d.ts +0 -1
  111. package/types/src/i18n/locales/sk.d.ts +0 -1
  112. package/types/src/index.d.ts +15 -8
  113. package/dist/en-B7ycW7c8.js.map +0 -1
  114. package/dist/en-D4taoCs4.cjs.map +0 -1
  115. package/src/api/blockManipulation/selections/__snapshots__/selection.test.ts.snap +0 -844
  116. package/src/api/blockManipulation/selections/selection.test.ts +0 -72
  117. package/src/api/blockManipulation/selections/textCursorPosition/__snapshots__/textCursorPosition.test.ts.snap +0 -316
  118. package/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.test.ts +0 -74
  119. package/types/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.test.d.ts +0 -1
  120. /package/types/src/{api/blockManipulation/selections/selection.test.d.ts → extensions/Collaboration/ForkYDocPlugin.test.d.ts} +0 -0
@@ -1,6 +1,7 @@
1
1
  import { findChildren } from "@tiptap/core";
2
2
  import { Plugin, PluginKey } from "prosemirror-state";
3
3
  import { Decoration, DecorationSet } from "prosemirror-view";
4
+ import { BlockNoteExtension } from "../../editor/BlockNoteExtension.js";
4
5
 
5
6
  const PLUGIN_KEY = new PluginKey(`previous-blocks`);
6
7
 
@@ -23,199 +24,211 @@ const nodeAttributes: Record<string, string> = {
23
24
  *
24
25
  * Solution: When attributes change on a node, this plugin sets a data-* attribute with the "previous" value. This way we can still use CSS transitions. (See block.module.css)
25
26
  */
26
- export class PreviousBlockTypePlugin {
27
- public readonly plugin: Plugin;
27
+ export class PreviousBlockTypePlugin extends BlockNoteExtension {
28
+ public static key() {
29
+ return "previousBlockType";
30
+ }
31
+
28
32
  constructor() {
33
+ super();
29
34
  let timeout: ReturnType<typeof setTimeout>;
30
- this.plugin = new Plugin({
31
- key: PLUGIN_KEY,
32
- view(_editorView) {
33
- return {
34
- update: async (view, _prevState) => {
35
- if (this.key?.getState(view.state).updatedBlocks.size > 0) {
36
- // use setTimeout 0 to clear the decorations so that at least
37
- // for one DOM-render the decorations have been applied
38
- timeout = setTimeout(() => {
39
- view.dispatch(
40
- view.state.tr.setMeta(PLUGIN_KEY, { clearUpdate: true }),
41
- );
42
- }, 0);
43
- }
44
- },
45
- destroy: () => {
46
- if (timeout) {
47
- clearTimeout(timeout);
48
- }
49
- },
50
- };
51
- },
52
- state: {
53
- init() {
35
+ this.addProsemirrorPlugin(
36
+ new Plugin({
37
+ key: PLUGIN_KEY,
38
+ view(_editorView) {
54
39
  return {
55
- // Block attributes, by block ID, from just before the previous transaction.
56
- prevTransactionOldBlockAttrs: {} as any,
57
- // Block attributes, by block ID, from just before the current transaction.
58
- currentTransactionOldBlockAttrs: {} as any,
59
- // Set of IDs of blocks whose attributes changed from the current transaction.
60
- updatedBlocks: new Set<string>(),
40
+ update: async (view, _prevState) => {
41
+ if (this.key?.getState(view.state).updatedBlocks.size > 0) {
42
+ // use setTimeout 0 to clear the decorations so that at least
43
+ // for one DOM-render the decorations have been applied
44
+ timeout = setTimeout(() => {
45
+ view.dispatch(
46
+ view.state.tr.setMeta(PLUGIN_KEY, { clearUpdate: true }),
47
+ );
48
+ }, 0);
49
+ }
50
+ },
51
+ destroy: () => {
52
+ if (timeout) {
53
+ clearTimeout(timeout);
54
+ }
55
+ },
61
56
  };
62
57
  },
58
+ state: {
59
+ init() {
60
+ return {
61
+ // Block attributes, by block ID, from just before the previous transaction.
62
+ prevTransactionOldBlockAttrs: {} as any,
63
+ // Block attributes, by block ID, from just before the current transaction.
64
+ currentTransactionOldBlockAttrs: {} as any,
65
+ // Set of IDs of blocks whose attributes changed from the current transaction.
66
+ updatedBlocks: new Set<string>(),
67
+ };
68
+ },
63
69
 
64
- apply(transaction, prev, oldState, newState) {
65
- prev.currentTransactionOldBlockAttrs = {};
66
- prev.updatedBlocks.clear();
70
+ apply(transaction, prev, oldState, newState) {
71
+ prev.currentTransactionOldBlockAttrs = {};
72
+ prev.updatedBlocks.clear();
67
73
 
68
- if (!transaction.docChanged || oldState.doc.eq(newState.doc)) {
69
- return prev;
70
- }
71
-
72
- // TODO: Instead of iterating through the entire document, only check nodes affected by the transactions. Will
73
- // also probably require checking nodes affected by the previous transaction too.
74
- // We didn't get this to work yet:
75
- // const transform = combineTransactionSteps(oldState.doc, [transaction]);
76
- // // const { mapping } = transform;
77
- // const changes = getChangedRanges(transform);
78
- //
79
- // changes.forEach(({ oldRange, newRange }) => {
80
- // const oldNodes = findChildrenInRange(
81
- // oldState.doc,
82
- // oldRange,
83
- // (node) => node.attrs.id
84
- // );
85
- //
86
- // const newNodes = findChildrenInRange(
87
- // newState.doc,
88
- // newRange,
89
- // (node) => node.attrs.id
90
- // );
91
-
92
- const currentTransactionOriginalOldBlockAttrs = {} as any;
93
-
94
- const oldNodes = findChildren(oldState.doc, (node) => node.attrs.id);
95
- const oldNodesById = new Map(
96
- oldNodes.map((node) => [node.node.attrs.id, node]),
97
- );
98
- const newNodes = findChildren(newState.doc, (node) => node.attrs.id);
99
-
100
- // Traverses all block containers in the new editor state.
101
- for (const node of newNodes) {
102
- const oldNode = oldNodesById.get(node.node.attrs.id);
103
-
104
- const oldContentNode = oldNode?.node.firstChild;
105
- const newContentNode = node.node.firstChild;
106
-
107
- if (oldNode && oldContentNode && newContentNode) {
108
- const newAttrs = {
109
- index: newContentNode.attrs.index,
110
- level: newContentNode.attrs.level,
111
- type: newContentNode.type.name,
112
- depth: newState.doc.resolve(node.pos).depth,
113
- };
114
-
115
- let oldAttrs = {
116
- index: oldContentNode.attrs.index,
117
- level: oldContentNode.attrs.level,
118
- type: oldContentNode.type.name,
119
- depth: oldState.doc.resolve(oldNode.pos).depth,
120
- };
121
-
122
- currentTransactionOriginalOldBlockAttrs[node.node.attrs.id] =
123
- oldAttrs;
124
-
125
- // Whenever a transaction is appended by the OrderedListItemIndexPlugin, it's given the metadata:
126
- // { "orderedListIndexing": true }
127
- // These appended transactions happen immediately after any transaction which causes ordered list item
128
- // indices to require updating, including those which trigger animations. Therefore, these animations are
129
- // immediately overridden when the PreviousBlockTypePlugin processes the appended transaction, despite only
130
- // the listItemIndex attribute changing. To solve this, oldAttrs must be edited for transactions with the
131
- // "orderedListIndexing" metadata, so the correct animation can be re-triggered.
132
- if (transaction.getMeta("numberedListIndexing")) {
133
- // If the block existed before the transaction, gets the attributes from before the previous transaction
134
- // (i.e. the transaction that caused list item indices to need updating).
135
- if (node.node.attrs.id in prev.prevTransactionOldBlockAttrs) {
136
- oldAttrs =
137
- prev.prevTransactionOldBlockAttrs[node.node.attrs.id];
138
- }
74
+ if (!transaction.docChanged || oldState.doc.eq(newState.doc)) {
75
+ return prev;
76
+ }
77
+
78
+ // TODO: Instead of iterating through the entire document, only check nodes affected by the transactions. Will
79
+ // also probably require checking nodes affected by the previous transaction too.
80
+ // We didn't get this to work yet:
81
+ // const transform = combineTransactionSteps(oldState.doc, [transaction]);
82
+ // // const { mapping } = transform;
83
+ // const changes = getChangedRanges(transform);
84
+ //
85
+ // changes.forEach(({ oldRange, newRange }) => {
86
+ // const oldNodes = findChildrenInRange(
87
+ // oldState.doc,
88
+ // oldRange,
89
+ // (node) => node.attrs.id
90
+ // );
91
+ //
92
+ // const newNodes = findChildrenInRange(
93
+ // newState.doc,
94
+ // newRange,
95
+ // (node) => node.attrs.id
96
+ // );
139
97
 
140
- // Stops list item indices themselves being animated (looks smoother), unless the block's content type is
141
- // changing from a numbered list item to something else.
142
- if (newAttrs.type === "numberedListItem") {
143
- oldAttrs.index = newAttrs.index;
98
+ const currentTransactionOriginalOldBlockAttrs = {} as any;
99
+
100
+ const oldNodes = findChildren(
101
+ oldState.doc,
102
+ (node) => node.attrs.id,
103
+ );
104
+ const oldNodesById = new Map(
105
+ oldNodes.map((node) => [node.node.attrs.id, node]),
106
+ );
107
+ const newNodes = findChildren(
108
+ newState.doc,
109
+ (node) => node.attrs.id,
110
+ );
111
+
112
+ // Traverses all block containers in the new editor state.
113
+ for (const node of newNodes) {
114
+ const oldNode = oldNodesById.get(node.node.attrs.id);
115
+
116
+ const oldContentNode = oldNode?.node.firstChild;
117
+ const newContentNode = node.node.firstChild;
118
+
119
+ if (oldNode && oldContentNode && newContentNode) {
120
+ const newAttrs = {
121
+ index: newContentNode.attrs.index,
122
+ level: newContentNode.attrs.level,
123
+ type: newContentNode.type.name,
124
+ depth: newState.doc.resolve(node.pos).depth,
125
+ };
126
+
127
+ let oldAttrs = {
128
+ index: oldContentNode.attrs.index,
129
+ level: oldContentNode.attrs.level,
130
+ type: oldContentNode.type.name,
131
+ depth: oldState.doc.resolve(oldNode.pos).depth,
132
+ };
133
+
134
+ currentTransactionOriginalOldBlockAttrs[node.node.attrs.id] =
135
+ oldAttrs;
136
+
137
+ // Whenever a transaction is appended by the OrderedListItemIndexPlugin, it's given the metadata:
138
+ // { "orderedListIndexing": true }
139
+ // These appended transactions happen immediately after any transaction which causes ordered list item
140
+ // indices to require updating, including those which trigger animations. Therefore, these animations are
141
+ // immediately overridden when the PreviousBlockTypePlugin processes the appended transaction, despite only
142
+ // the listItemIndex attribute changing. To solve this, oldAttrs must be edited for transactions with the
143
+ // "orderedListIndexing" metadata, so the correct animation can be re-triggered.
144
+ if (transaction.getMeta("numberedListIndexing")) {
145
+ // If the block existed before the transaction, gets the attributes from before the previous transaction
146
+ // (i.e. the transaction that caused list item indices to need updating).
147
+ if (node.node.attrs.id in prev.prevTransactionOldBlockAttrs) {
148
+ oldAttrs =
149
+ prev.prevTransactionOldBlockAttrs[node.node.attrs.id];
150
+ }
151
+
152
+ // Stops list item indices themselves being animated (looks smoother), unless the block's content type is
153
+ // changing from a numbered list item to something else.
154
+ if (newAttrs.type === "numberedListItem") {
155
+ oldAttrs.index = newAttrs.index;
156
+ }
144
157
  }
145
- }
146
158
 
147
- prev.currentTransactionOldBlockAttrs[node.node.attrs.id] =
148
- oldAttrs;
149
-
150
- // TODO: faster deep equal?
151
- if (JSON.stringify(oldAttrs) !== JSON.stringify(newAttrs)) {
152
- (oldAttrs as any)["depth-change"] =
153
- oldAttrs.depth - newAttrs.depth;
154
-
155
- // for debugging:
156
- // console.log(
157
- // "id:",
158
- // node.node.attrs.id,
159
- // "previousBlockTypePlugin changes detected, oldAttrs",
160
- // oldAttrs,
161
- // "new",
162
- // newAttrs
163
- // );
164
-
165
- prev.updatedBlocks.add(node.node.attrs.id);
159
+ prev.currentTransactionOldBlockAttrs[node.node.attrs.id] =
160
+ oldAttrs;
161
+
162
+ // TODO: faster deep equal?
163
+ if (JSON.stringify(oldAttrs) !== JSON.stringify(newAttrs)) {
164
+ (oldAttrs as any)["depth-change"] =
165
+ oldAttrs.depth - newAttrs.depth;
166
+
167
+ // for debugging:
168
+ // console.log(
169
+ // "id:",
170
+ // node.node.attrs.id,
171
+ // "previousBlockTypePlugin changes detected, oldAttrs",
172
+ // oldAttrs,
173
+ // "new",
174
+ // newAttrs
175
+ // );
176
+
177
+ prev.updatedBlocks.add(node.node.attrs.id);
178
+ }
166
179
  }
167
180
  }
168
- }
169
181
 
170
- prev.prevTransactionOldBlockAttrs =
171
- currentTransactionOriginalOldBlockAttrs;
182
+ prev.prevTransactionOldBlockAttrs =
183
+ currentTransactionOriginalOldBlockAttrs;
172
184
 
173
- return prev;
185
+ return prev;
186
+ },
174
187
  },
175
- },
176
- props: {
177
- decorations(state) {
178
- const pluginState = (this as Plugin).getState(state);
179
- if (pluginState.updatedBlocks.size === 0) {
180
- return undefined;
181
- }
182
-
183
- const decorations: Decoration[] = [];
184
-
185
- state.doc.descendants((node, pos) => {
186
- if (!node.attrs.id) {
187
- return;
188
+ props: {
189
+ decorations(state) {
190
+ const pluginState = (this as Plugin).getState(state);
191
+ if (pluginState.updatedBlocks.size === 0) {
192
+ return undefined;
188
193
  }
189
194
 
190
- if (!pluginState.updatedBlocks.has(node.attrs.id)) {
191
- return;
192
- }
195
+ const decorations: Decoration[] = [];
193
196
 
194
- const prevAttrs =
195
- pluginState.currentTransactionOldBlockAttrs[node.attrs.id];
196
- const decorationAttrs: any = {};
197
+ state.doc.descendants((node, pos) => {
198
+ if (!node.attrs.id) {
199
+ return;
200
+ }
197
201
 
198
- for (const [nodeAttr, val] of Object.entries(prevAttrs)) {
199
- decorationAttrs["data-prev-" + nodeAttributes[nodeAttr]] =
200
- val || "none";
201
- }
202
+ if (!pluginState.updatedBlocks.has(node.attrs.id)) {
203
+ return;
204
+ }
202
205
 
203
- // for debugging:
204
- // console.log(
205
- // "previousBlockTypePlugin committing decorations",
206
- // decorationAttrs
207
- // );
206
+ const prevAttrs =
207
+ pluginState.currentTransactionOldBlockAttrs[node.attrs.id];
208
+ const decorationAttrs: any = {};
208
209
 
209
- const decoration = Decoration.node(pos, pos + node.nodeSize, {
210
- ...decorationAttrs,
211
- });
210
+ for (const [nodeAttr, val] of Object.entries(prevAttrs)) {
211
+ decorationAttrs["data-prev-" + nodeAttributes[nodeAttr]] =
212
+ val || "none";
213
+ }
214
+
215
+ // for debugging:
216
+ // console.log(
217
+ // "previousBlockTypePlugin committing decorations",
218
+ // decorationAttrs
219
+ // );
212
220
 
213
- decorations.push(decoration);
214
- });
221
+ const decoration = Decoration.node(pos, pos + node.nodeSize, {
222
+ ...decorationAttrs,
223
+ });
215
224
 
216
- return DecorationSet.create(state.doc, decorations);
225
+ decorations.push(decoration);
226
+ });
227
+
228
+ return DecorationSet.create(state.doc, decorations);
229
+ },
217
230
  },
218
- },
219
- });
231
+ }),
232
+ );
220
233
  }
221
234
  }
@@ -1,6 +1,7 @@
1
1
  import { Plugin, PluginKey } from "prosemirror-state";
2
2
  import { Decoration, DecorationSet } from "prosemirror-view";
3
3
  import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
4
+ import { BlockNoteExtension } from "../../editor/BlockNoteExtension.js";
4
5
 
5
6
  const PLUGIN_KEY = new PluginKey(`blocknote-show-selection`);
6
7
 
@@ -9,29 +10,35 @@ const PLUGIN_KEY = new PluginKey(`blocknote-show-selection`);
9
10
  * This can be used to highlight the current selection in the UI even when the
10
11
  * text editor is not focused.
11
12
  */
12
- export class ShowSelectionPlugin {
13
- public readonly plugin: Plugin;
13
+ export class ShowSelectionPlugin extends BlockNoteExtension {
14
+ public static key() {
15
+ return "showSelection";
16
+ }
17
+
14
18
  private enabled = false;
15
19
 
16
20
  public constructor(private readonly editor: BlockNoteEditor<any, any, any>) {
17
- this.plugin = new Plugin({
18
- key: PLUGIN_KEY,
19
- props: {
20
- decorations: (state) => {
21
- const { doc, selection } = state;
22
-
23
- if (!this.enabled) {
24
- return DecorationSet.empty;
25
- }
26
-
27
- const dec = Decoration.inline(selection.from, selection.to, {
28
- "data-show-selection": "true",
29
- });
30
-
31
- return DecorationSet.create(doc, [dec]);
21
+ super();
22
+ this.addProsemirrorPlugin(
23
+ new Plugin({
24
+ key: PLUGIN_KEY,
25
+ props: {
26
+ decorations: (state) => {
27
+ const { doc, selection } = state;
28
+
29
+ if (!this.enabled) {
30
+ return DecorationSet.empty;
31
+ }
32
+
33
+ const dec = Decoration.inline(selection.from, selection.to, {
34
+ "data-show-selection": "true",
35
+ });
36
+
37
+ return DecorationSet.create(doc, [dec]);
38
+ },
32
39
  },
33
- },
34
- });
40
+ }),
41
+ );
35
42
  }
36
43
 
37
44
  public setEnabled(enabled: boolean) {
@@ -3,20 +3,20 @@ import {
3
3
  EditorState,
4
4
  Plugin,
5
5
  PluginKey,
6
- TextSelection,
7
6
  PluginView,
7
+ TextSelection,
8
8
  } from "@tiptap/pm/state";
9
9
  import { EditorView } from "@tiptap/pm/view";
10
10
 
11
11
  import { Block } from "../../blocks/defaultBlocks.js";
12
12
  import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
13
+ import { BlockNoteExtension } from "../../editor/BlockNoteExtension.js";
13
14
  import { UiElementPosition } from "../../extensions-shared/UiElementPosition.js";
14
15
  import {
15
16
  BlockSchema,
16
17
  InlineContentSchema,
17
18
  StyleSchema,
18
19
  } from "../../schema/index.js";
19
- import { EventEmitter } from "../../util/EventEmitter.js";
20
20
  import { initializeESMDependencies } from "../../util/esmDependencies.js";
21
21
  import { getDraggableBlockFromElement } from "../getDraggableBlockFromElement.js";
22
22
  import { dragStart, unsetDragImage } from "./dragging.js";
@@ -608,29 +608,34 @@ export class SideMenuProsemirrorPlugin<
608
608
  BSchema extends BlockSchema,
609
609
  I extends InlineContentSchema,
610
610
  S extends StyleSchema,
611
- > extends EventEmitter<any> {
611
+ > extends BlockNoteExtension {
612
+ public static key() {
613
+ return "sideMenu";
614
+ }
615
+
612
616
  public view: SideMenuView<BSchema, I, S> | undefined;
613
- public readonly plugin: Plugin;
614
617
 
615
618
  constructor(
616
619
  private readonly editor: BlockNoteEditor<BSchema, I, S>,
617
620
  sideMenuDetection: "viewport" | "editor",
618
621
  ) {
619
622
  super();
620
- this.plugin = new Plugin({
621
- key: sideMenuPluginKey,
622
- view: (editorView) => {
623
- this.view = new SideMenuView(
624
- editor,
625
- sideMenuDetection,
626
- editorView,
627
- (state) => {
628
- this.emit("update", state);
629
- },
630
- );
631
- return this.view;
632
- },
633
- });
623
+ this.addProsemirrorPlugin(
624
+ new Plugin({
625
+ key: sideMenuPluginKey,
626
+ view: (editorView) => {
627
+ this.view = new SideMenuView(
628
+ editor,
629
+ sideMenuDetection,
630
+ editorView,
631
+ (state) => {
632
+ this.emit("update", state);
633
+ },
634
+ );
635
+ return this.view;
636
+ },
637
+ }),
638
+ );
634
639
  }
635
640
 
636
641
  public onUpdate(callback: (state: SideMenuState<BSchema, I, S>) => void) {