@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
@@ -0,0 +1,174 @@
1
+ import * as Y from "yjs";
2
+
3
+ import {
4
+ yCursorPluginKey,
5
+ ySyncPluginKey,
6
+ yUndoPluginKey,
7
+ } from "y-prosemirror";
8
+ import { CursorPlugin } from "./CursorPlugin.js";
9
+ import { SyncPlugin } from "./SyncPlugin.js";
10
+ import { UndoPlugin } from "./UndoPlugin.js";
11
+
12
+ import {
13
+ BlockNoteEditor,
14
+ BlockNoteEditorOptions,
15
+ } from "../../editor/BlockNoteEditor.js";
16
+ import { BlockNoteExtension } from "../../editor/BlockNoteExtension.js";
17
+
18
+ export class ForkYDocPlugin extends BlockNoteExtension<{
19
+ forked: boolean;
20
+ }> {
21
+ public static key() {
22
+ return "ForkYDocPlugin";
23
+ }
24
+
25
+ private editor: BlockNoteEditor<any, any, any>;
26
+ private collaboration: BlockNoteEditorOptions<any, any, any>["collaboration"];
27
+
28
+ constructor({
29
+ editor,
30
+ collaboration,
31
+ }: {
32
+ editor: BlockNoteEditor<any, any, any>;
33
+ collaboration: BlockNoteEditorOptions<any, any, any>["collaboration"];
34
+ }) {
35
+ super(editor);
36
+ this.editor = editor;
37
+ this.collaboration = collaboration;
38
+ }
39
+
40
+ /**
41
+ * To find a fragment in another ydoc, we need to search for it.
42
+ */
43
+ private findTypeInOtherYdoc<T extends Y.AbstractType<any>>(
44
+ ytype: T,
45
+ otherYdoc: Y.Doc,
46
+ ): T {
47
+ const ydoc = ytype.doc!;
48
+ if (ytype._item === null) {
49
+ /**
50
+ * If is a root type, we need to find the root key in the original ydoc
51
+ * and use it to get the type in the other ydoc.
52
+ */
53
+ const rootKey = Array.from(ydoc.share.keys()).find(
54
+ (key) => ydoc.share.get(key) === ytype,
55
+ );
56
+ if (rootKey == null) {
57
+ throw new Error("type does not exist in other ydoc");
58
+ }
59
+ return otherYdoc.get(rootKey, ytype.constructor as new () => T) as T;
60
+ } else {
61
+ /**
62
+ * If it is a sub type, we use the item id to find the history type.
63
+ */
64
+ const ytypeItem = ytype._item;
65
+ const otherStructs =
66
+ otherYdoc.store.clients.get(ytypeItem.id.client) ?? [];
67
+ const itemIndex = Y.findIndexSS(otherStructs, ytypeItem.id.clock);
68
+ const otherItem = otherStructs[itemIndex] as Y.Item;
69
+ const otherContent = otherItem.content as Y.ContentType;
70
+ return otherContent.type as T;
71
+ }
72
+ }
73
+
74
+ /**
75
+ * Whether the editor is editing a forked document,
76
+ * preserving a reference to the original document and the forked document.
77
+ */
78
+ public get isForkedFromRemote() {
79
+ return this.forkedState !== undefined;
80
+ }
81
+
82
+ /**
83
+ * Stores whether the editor is editing a forked document,
84
+ * preserving a reference to the original document and the forked document.
85
+ */
86
+ private forkedState:
87
+ | {
88
+ originalFragment: Y.XmlFragment;
89
+ forkedFragment: Y.XmlFragment;
90
+ }
91
+ | undefined;
92
+
93
+ /**
94
+ * Fork the Y.js document from syncing to the remote,
95
+ * allowing modifications to the document without affecting the remote.
96
+ * These changes can later be rolled back or applied to the remote.
97
+ */
98
+ public fork() {
99
+ if (this.isForkedFromRemote) {
100
+ return;
101
+ }
102
+
103
+ const originalFragment = this.collaboration.fragment;
104
+
105
+ if (!originalFragment) {
106
+ throw new Error("No fragment to fork from");
107
+ }
108
+
109
+ const doc = new Y.Doc();
110
+ // Copy the original document to a new Yjs document
111
+ Y.applyUpdate(doc, Y.encodeStateAsUpdate(originalFragment.doc!));
112
+
113
+ // Find the forked fragment in the new Yjs document
114
+ const forkedFragment = this.findTypeInOtherYdoc(originalFragment, doc);
115
+
116
+ this.forkedState = {
117
+ originalFragment,
118
+ forkedFragment,
119
+ };
120
+
121
+ // Need to reset all the yjs plugins
122
+ this.editor._tiptapEditor.unregisterPlugin([
123
+ yCursorPluginKey,
124
+ yUndoPluginKey,
125
+ ySyncPluginKey,
126
+ ]);
127
+ // Register them again, based on the new forked fragment
128
+ this.editor._tiptapEditor.registerPlugin(
129
+ new SyncPlugin(forkedFragment).plugins[0],
130
+ );
131
+ this.editor._tiptapEditor.registerPlugin(new UndoPlugin().plugins[0]);
132
+ // No need to register the cursor plugin again, it's a local fork
133
+ this.emit("forked", true);
134
+ }
135
+
136
+ /**
137
+ * Resume syncing the Y.js document to the remote
138
+ * If `keepChanges` is true, any changes that have been made to the forked document will be applied to the original document.
139
+ * Otherwise, the original document will be restored and the changes will be discarded.
140
+ */
141
+ public merge({ keepChanges }: { keepChanges: boolean }) {
142
+ if (!this.forkedState) {
143
+ return;
144
+ }
145
+ // Remove the forked fragment's plugins
146
+ this.editor._tiptapEditor.unregisterPlugin(ySyncPluginKey);
147
+ this.editor._tiptapEditor.unregisterPlugin(yUndoPluginKey);
148
+
149
+ const { originalFragment, forkedFragment } = this.forkedState;
150
+ if (keepChanges) {
151
+ // Apply any changes that have been made to the fork, onto the original doc
152
+ const update = Y.encodeStateAsUpdate(forkedFragment.doc!);
153
+ Y.applyUpdate(originalFragment.doc!, update);
154
+ }
155
+ this.editor.extensions["ySyncPlugin"] = new SyncPlugin(originalFragment);
156
+ this.editor.extensions["yCursorPlugin"] = new CursorPlugin(
157
+ this.collaboration!,
158
+ );
159
+ this.editor.extensions["yUndoPlugin"] = new UndoPlugin();
160
+ // Register the plugins again, based on the original fragment
161
+ this.editor._tiptapEditor.registerPlugin(
162
+ this.editor.extensions["ySyncPlugin"].plugins[0],
163
+ );
164
+ this.editor._tiptapEditor.registerPlugin(
165
+ this.editor.extensions["yCursorPlugin"].plugins[0],
166
+ );
167
+ this.editor._tiptapEditor.registerPlugin(
168
+ this.editor.extensions["yUndoPlugin"].plugins[0],
169
+ );
170
+ // Reset the forked state
171
+ this.forkedState = undefined;
172
+ this.emit("forked", false);
173
+ }
174
+ }
@@ -1,12 +1,15 @@
1
- import { Plugin } from "prosemirror-state";
2
1
  import { ySyncPlugin } from "y-prosemirror";
3
2
  import type * as Y from "yjs";
3
+ import { BlockNoteExtension } from "../../editor/BlockNoteExtension.js";
4
4
 
5
- export class SyncPlugin {
6
- public plugin: Plugin;
5
+ export class SyncPlugin extends BlockNoteExtension {
6
+ public static key() {
7
+ return "ySyncPlugin";
8
+ }
7
9
 
8
10
  constructor(fragment: Y.XmlFragment) {
9
- this.plugin = ySyncPlugin(fragment);
11
+ super();
12
+ this.addProsemirrorPlugin(ySyncPlugin(fragment));
10
13
  }
11
14
 
12
15
  public get priority() {
@@ -1,11 +1,14 @@
1
- import { Plugin } from "prosemirror-state";
2
1
  import { yUndoPlugin } from "y-prosemirror";
2
+ import { BlockNoteExtension } from "../../editor/BlockNoteExtension.js";
3
3
 
4
- export class UndoPlugin {
5
- public plugin: Plugin;
4
+ export class UndoPlugin extends BlockNoteExtension {
5
+ public static key() {
6
+ return "yUndoPlugin";
7
+ }
6
8
 
7
9
  constructor() {
8
- this.plugin = yUndoPlugin();
10
+ super();
11
+ this.addProsemirrorPlugin(yUndoPlugin());
9
12
  }
10
13
 
11
14
  public get priority() {
@@ -0,0 +1,30 @@
1
+ [
2
+ {
3
+ "children": [],
4
+ "content": [
5
+ {
6
+ "styles": {},
7
+ "text": "Hello World",
8
+ "type": "text",
9
+ },
10
+ ],
11
+ "id": "2",
12
+ "props": {
13
+ "backgroundColor": "default",
14
+ "textAlignment": "left",
15
+ "textColor": "default",
16
+ },
17
+ "type": "paragraph",
18
+ },
19
+ {
20
+ "children": [],
21
+ "content": [],
22
+ "id": "3",
23
+ "props": {
24
+ "backgroundColor": "default",
25
+ "textAlignment": "left",
26
+ "textColor": "default",
27
+ },
28
+ "type": "paragraph",
29
+ },
30
+ ]
@@ -0,0 +1,30 @@
1
+ [
2
+ {
3
+ "children": [],
4
+ "content": [
5
+ {
6
+ "styles": {},
7
+ "text": "Hello",
8
+ "type": "text",
9
+ },
10
+ ],
11
+ "id": "0",
12
+ "props": {
13
+ "backgroundColor": "default",
14
+ "textAlignment": "left",
15
+ "textColor": "default",
16
+ },
17
+ "type": "paragraph",
18
+ },
19
+ {
20
+ "children": [],
21
+ "content": [],
22
+ "id": "1",
23
+ "props": {
24
+ "backgroundColor": "default",
25
+ "textAlignment": "left",
26
+ "textColor": "default",
27
+ },
28
+ "type": "paragraph",
29
+ },
30
+ ]
@@ -0,0 +1 @@
1
+ <blockgroup><blockcontainer backgroundColor="default" id="2" textColor="default"><paragraph textAlignment="left">Hello World</paragraph></blockcontainer><blockcontainer backgroundColor="default" id="3" textColor="default"><paragraph textAlignment="left"></paragraph></blockcontainer></blockgroup>
@@ -0,0 +1 @@
1
+ <blockgroup><blockcontainer backgroundColor="default" id="0" textColor="default"><paragraph textAlignment="left">Hello</paragraph></blockcontainer><blockcontainer backgroundColor="default" id="1" textColor="default"><paragraph textAlignment="left"></paragraph></blockcontainer></blockgroup>
@@ -9,7 +9,7 @@ import type {
9
9
  User,
10
10
  } from "../../comments/index.js";
11
11
  import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
12
- import { EventEmitter } from "../../util/EventEmitter.js";
12
+ import { BlockNoteExtension } from "../../editor/BlockNoteExtension.js";
13
13
  import { UserStore } from "./userstore/UserStore.js";
14
14
 
15
15
  const PLUGIN_KEY = new PluginKey(`blocknote-comments`);
@@ -56,8 +56,11 @@ function getUpdatedThreadPositions(doc: Node, markType: string) {
56
56
  return threadPositions;
57
57
  }
58
58
 
59
- export class CommentsPlugin extends EventEmitter<any> {
60
- public readonly plugin: Plugin;
59
+ export class CommentsPlugin extends BlockNoteExtension {
60
+ public static key() {
61
+ return "comments";
62
+ }
63
+
61
64
  public readonly userStore: UserStore<User>;
62
65
 
63
66
  /**
@@ -103,6 +106,7 @@ export class CommentsPlugin extends EventEmitter<any> {
103
106
  const trimmedTo = Math.min(
104
107
  pos + node.nodeSize,
105
108
  tr.doc.content.size - 1,
109
+ tr.doc.content.size - 1,
106
110
  );
107
111
  tr.removeMark(trimmedFrom, trimmedTo, mark);
108
112
  tr.addMark(
@@ -156,86 +160,91 @@ export class CommentsPlugin extends EventEmitter<any> {
156
160
  // eslint-disable-next-line @typescript-eslint/no-this-alias
157
161
  const self = this;
158
162
 
159
- this.plugin = new Plugin<CommentsPluginState>({
160
- key: PLUGIN_KEY,
161
- state: {
162
- init() {
163
- return {
164
- decorations: DecorationSet.empty,
165
- };
166
- },
167
- apply(tr, state) {
168
- const action = tr.getMeta(PLUGIN_KEY);
169
-
170
- if (!tr.docChanged && !action) {
171
- return state;
172
- }
173
-
174
- // only update threadPositions if the doc changed
175
- const threadPositions = tr.docChanged
176
- ? getUpdatedThreadPositions(tr.doc, self.markType)
177
- : self.threadPositions;
163
+ this.addProsemirrorPlugin(
164
+ new Plugin<CommentsPluginState>({
165
+ key: PLUGIN_KEY,
166
+ state: {
167
+ init() {
168
+ return {
169
+ decorations: DecorationSet.empty,
170
+ };
171
+ },
172
+ apply(tr, state) {
173
+ const action = tr.getMeta(PLUGIN_KEY);
174
+
175
+ if (!tr.docChanged && !action) {
176
+ return state;
177
+ }
178
178
 
179
- if (threadPositions.size > 0 || self.threadPositions.size > 0) {
180
- // small optimization; don't emit event if threadPositions before / after were both empty
181
- self.threadPositions = threadPositions;
182
- self.emitStateUpdate();
183
- }
179
+ // only update threadPositions if the doc changed
180
+ const threadPositions = tr.docChanged
181
+ ? getUpdatedThreadPositions(tr.doc, self.markType)
182
+ : self.threadPositions;
184
183
 
185
- // update decorations if doc or selected thread changed
186
- const decorations = [];
184
+ if (threadPositions.size > 0 || self.threadPositions.size > 0) {
185
+ // small optimization; don't emit event if threadPositions before / after were both empty
186
+ self.threadPositions = threadPositions;
187
+ self.emitStateUpdate();
188
+ }
187
189
 
188
- if (self.selectedThreadId) {
189
- const selectedThreadPosition = threadPositions.get(
190
- self.selectedThreadId,
191
- );
190
+ // update decorations if doc or selected thread changed
191
+ const decorations = [];
192
192
 
193
- if (selectedThreadPosition) {
194
- decorations.push(
195
- Decoration.inline(
196
- selectedThreadPosition.from,
197
- selectedThreadPosition.to,
198
- {
199
- class: "bn-thread-mark-selected",
200
- },
201
- ),
193
+ if (self.selectedThreadId) {
194
+ const selectedThreadPosition = threadPositions.get(
195
+ self.selectedThreadId,
202
196
  );
197
+
198
+ if (selectedThreadPosition) {
199
+ decorations.push(
200
+ Decoration.inline(
201
+ selectedThreadPosition.from,
202
+ selectedThreadPosition.to,
203
+ {
204
+ class: "bn-thread-mark-selected",
205
+ },
206
+ ),
207
+ );
208
+ }
203
209
  }
204
- }
205
210
 
206
- return {
207
- decorations: DecorationSet.create(tr.doc, decorations),
208
- };
209
- },
210
- },
211
- props: {
212
- decorations(state) {
213
- return PLUGIN_KEY.getState(state)?.decorations ?? DecorationSet.empty;
211
+ return {
212
+ decorations: DecorationSet.create(tr.doc, decorations),
213
+ };
214
+ },
214
215
  },
215
- /**
216
- * Handle click on a thread mark and mark it as selected
217
- */
218
- handleClick: (view, pos, event) => {
219
- if (event.button !== 0) {
220
- return;
221
- }
216
+ props: {
217
+ decorations(state) {
218
+ return (
219
+ PLUGIN_KEY.getState(state)?.decorations ?? DecorationSet.empty
220
+ );
221
+ },
222
+ /**
223
+ * Handle click on a thread mark and mark it as selected
224
+ */
225
+ handleClick: (view, pos, event) => {
226
+ if (event.button !== 0) {
227
+ return;
228
+ }
222
229
 
223
- const node = view.state.doc.nodeAt(pos);
230
+ const node = view.state.doc.nodeAt(pos);
224
231
 
225
- if (!node) {
226
- self.selectThread(undefined);
227
- return;
228
- }
232
+ if (!node) {
233
+ self.selectThread(undefined);
234
+ return;
235
+ }
229
236
 
230
- const commentMark = node.marks.find(
231
- (mark) => mark.type.name === markType && mark.attrs.orphan !== true,
232
- );
237
+ const commentMark = node.marks.find(
238
+ (mark) =>
239
+ mark.type.name === markType && mark.attrs.orphan !== true,
240
+ );
233
241
 
234
- const threadId = commentMark?.attrs.threadId as string | undefined;
235
- self.selectThread(threadId, false);
242
+ const threadId = commentMark?.attrs.threadId as string | undefined;
243
+ self.selectThread(threadId, false);
244
+ },
236
245
  },
237
- },
238
- });
246
+ }),
247
+ );
239
248
  }
240
249
 
241
250
  /**
@@ -1,7 +1,9 @@
1
1
  import { EditorState, Plugin, PluginKey, PluginView } from "prosemirror-state";
2
2
  import { EditorView } from "prosemirror-view";
3
3
 
4
+ import { ySyncPluginKey } from "y-prosemirror";
4
5
  import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
6
+ import { BlockNoteExtension } from "../../editor/BlockNoteExtension.js";
5
7
  import { UiElementPosition } from "../../extensions-shared/UiElementPosition.js";
6
8
  import type {
7
9
  BlockFromConfig,
@@ -9,8 +11,6 @@ import type {
9
11
  InlineContentSchema,
10
12
  StyleSchema,
11
13
  } from "../../schema/index.js";
12
- import { EventEmitter } from "../../util/EventEmitter.js";
13
- import { ySyncPluginKey } from "y-prosemirror";
14
14
 
15
15
  export type FilePanelState<
16
16
  I extends InlineContentSchema,
@@ -138,60 +138,65 @@ const filePanelPluginKey = new PluginKey<FilePanelState<any, any>>(
138
138
  export class FilePanelProsemirrorPlugin<
139
139
  I extends InlineContentSchema,
140
140
  S extends StyleSchema,
141
- > extends EventEmitter<any> {
141
+ > extends BlockNoteExtension {
142
+ public static key() {
143
+ return "filePanel";
144
+ }
145
+
142
146
  private view: FilePanelView<I, S> | undefined;
143
- public readonly plugin: Plugin;
144
147
 
145
148
  constructor(editor: BlockNoteEditor<Record<string, FileBlockConfig>, I, S>) {
146
149
  super();
147
- this.plugin = new Plugin<{
148
- block: BlockFromConfig<FileBlockConfig, I, S> | undefined;
149
- }>({
150
- key: filePanelPluginKey,
151
- view: (editorView) => {
152
- this.view = new FilePanelView<I, S>(
153
- editor,
154
- filePanelPluginKey,
155
- editorView,
156
- (state) => {
157
- this.emit("update", state);
158
- },
159
- );
160
- return this.view;
161
- },
162
- props: {
163
- handleKeyDown: (_view, event: KeyboardEvent) => {
164
- if (event.key === "Escape" && this.shown) {
165
- this.view?.closeMenu();
166
- return true;
167
- }
168
- return false;
150
+ this.addProsemirrorPlugin(
151
+ new Plugin<{
152
+ block: BlockFromConfig<FileBlockConfig, I, S> | undefined;
153
+ }>({
154
+ key: filePanelPluginKey,
155
+ view: (editorView) => {
156
+ this.view = new FilePanelView<I, S>(
157
+ editor,
158
+ filePanelPluginKey,
159
+ editorView,
160
+ (state) => {
161
+ this.emit("update", state);
162
+ },
163
+ );
164
+ return this.view;
169
165
  },
170
- },
171
- state: {
172
- init: () => {
173
- return {
174
- block: undefined,
175
- };
166
+ props: {
167
+ handleKeyDown: (_view, event: KeyboardEvent) => {
168
+ if (event.key === "Escape" && this.shown) {
169
+ this.view?.closeMenu();
170
+ return true;
171
+ }
172
+ return false;
173
+ },
176
174
  },
177
- apply: (transaction, prev) => {
178
- const state: FilePanelState<I, S> | undefined =
179
- transaction.getMeta(filePanelPluginKey);
180
-
181
- if (state) {
182
- return state;
183
- }
184
-
185
- if (
186
- !transaction.getMeta(ySyncPluginKey) &&
187
- (transaction.selectionSet || transaction.docChanged)
188
- ) {
189
- return { block: undefined };
190
- }
191
- return prev;
175
+ state: {
176
+ init: () => {
177
+ return {
178
+ block: undefined,
179
+ };
180
+ },
181
+ apply: (transaction, prev) => {
182
+ const state: FilePanelState<I, S> | undefined =
183
+ transaction.getMeta(filePanelPluginKey);
184
+
185
+ if (state) {
186
+ return state;
187
+ }
188
+
189
+ if (
190
+ !transaction.getMeta(ySyncPluginKey) &&
191
+ (transaction.selectionSet || transaction.docChanged)
192
+ ) {
193
+ return { block: undefined };
194
+ }
195
+ return prev;
196
+ },
192
197
  },
193
- },
194
- });
198
+ }),
199
+ );
195
200
  }
196
201
 
197
202
  public get shown() {