@blocknote/core 0.30.1 → 0.31.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 (139) hide show
  1. package/dist/blocknote.cjs +9 -9
  2. package/dist/blocknote.cjs.map +1 -1
  3. package/dist/blocknote.js +2754 -2230
  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/webpack-stats.json +1 -1
  15. package/package.json +4 -5
  16. package/src/api/blockManipulation/commands/insertBlocks/insertBlocks.ts +2 -3
  17. package/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.ts +1 -1
  18. package/src/api/blockManipulation/commands/updateBlock/__snapshots__/updateBlock.test.ts.snap +2816 -0
  19. package/src/api/blockManipulation/commands/updateBlock/updateBlock.test.ts +158 -0
  20. package/src/api/blockManipulation/commands/updateBlock/updateBlock.ts +87 -17
  21. package/src/api/blockManipulation/selections/selection.ts +48 -1
  22. package/src/api/blockManipulation/selections/{textCursorPosition/textCursorPosition.ts → textCursorPosition.ts} +7 -7
  23. package/src/api/getBlockInfoFromPos.ts +1 -1
  24. package/src/api/nodeConversions/blockToNode.ts +5 -2
  25. package/src/api/nodeConversions/nodeToBlock.ts +203 -8
  26. package/src/api/pmUtil.ts +3 -3
  27. package/src/blocks/CodeBlockContent/CodeBlockContent.ts +6 -6
  28. package/src/blocks/FileBlockContent/helpers/render/createAddFileButton.ts +1 -1
  29. package/src/blocks/TableBlockContent/TableBlockContent.ts +32 -2
  30. package/src/editor/Block.css +27 -1
  31. package/src/editor/BlockNoteEditor.test.ts +7 -0
  32. package/src/editor/BlockNoteEditor.ts +88 -37
  33. package/src/editor/BlockNoteExtension.ts +26 -0
  34. package/src/editor/BlockNoteExtensions.ts +28 -12
  35. package/src/editor/BlockNoteTipTapEditor.ts +23 -2
  36. package/src/extensions/Collaboration/CursorPlugin.ts +13 -7
  37. package/src/extensions/Collaboration/ForkYDocPlugin.test.ts +166 -0
  38. package/src/extensions/Collaboration/ForkYDocPlugin.ts +174 -0
  39. package/src/extensions/Collaboration/SyncPlugin.ts +7 -4
  40. package/src/extensions/Collaboration/UndoPlugin.ts +7 -4
  41. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor-forked.json +30 -0
  42. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor.json +30 -0
  43. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-forked.html +1 -0
  44. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap.html +1 -0
  45. package/src/extensions/Comments/CommentsPlugin.ts +75 -70
  46. package/src/extensions/FilePanel/FilePanelPlugin.ts +50 -49
  47. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +56 -26
  48. package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +22 -21
  49. package/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.ts +45 -42
  50. package/src/extensions/Placeholder/PlaceholderPlugin.ts +111 -108
  51. package/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.ts +179 -170
  52. package/src/extensions/ShowSelection/ShowSelectionPlugin.ts +22 -19
  53. package/src/extensions/SideMenu/SideMenuPlugin.ts +19 -18
  54. package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +168 -168
  55. package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +4 -4
  56. package/src/extensions/Suggestions/SuggestionMarks.ts +175 -0
  57. package/src/extensions/TableHandles/TableHandlesPlugin.ts +153 -150
  58. package/src/i18n/locales/ar.ts +0 -1
  59. package/src/i18n/locales/de.ts +0 -1
  60. package/src/i18n/locales/en.ts +0 -1
  61. package/src/i18n/locales/es.ts +0 -1
  62. package/src/i18n/locales/fr.ts +0 -1
  63. package/src/i18n/locales/hr.ts +0 -1
  64. package/src/i18n/locales/is.ts +0 -1
  65. package/src/i18n/locales/it.ts +0 -1
  66. package/src/i18n/locales/ja.ts +0 -1
  67. package/src/i18n/locales/ko.ts +0 -1
  68. package/src/i18n/locales/nl.ts +0 -1
  69. package/src/i18n/locales/no.ts +0 -1
  70. package/src/i18n/locales/pl.ts +0 -1
  71. package/src/i18n/locales/pt.ts +0 -1
  72. package/src/i18n/locales/ru.ts +0 -1
  73. package/src/i18n/locales/sk.ts +0 -1
  74. package/src/i18n/locales/uk.ts +0 -1
  75. package/src/i18n/locales/vi.ts +0 -1
  76. package/src/i18n/locales/zh-tw.ts +0 -1
  77. package/src/i18n/locales/zh.ts +0 -1
  78. package/src/index.ts +18 -8
  79. package/src/pm-nodes/BlockContainer.ts +1 -1
  80. package/src/pm-nodes/BlockGroup.ts +1 -1
  81. package/src/pm-nodes/Doc.ts +1 -0
  82. package/types/src/api/blockManipulation/commands/insertBlocks/insertBlocks.d.ts +1 -1
  83. package/types/src/api/blockManipulation/commands/removeBlocks/removeBlocks.d.ts +4 -0
  84. package/types/src/api/blockManipulation/commands/removeBlocks/removeBlocks.test.d.ts +1 -0
  85. package/types/src/api/blockManipulation/commands/updateBlock/updateBlock.d.ts +3 -1
  86. package/types/src/api/blockManipulation/selections/selection.d.ts +10 -0
  87. package/types/src/api/blockManipulation/selections/textCursorPosition.d.ts +5 -0
  88. package/types/src/api/blockManipulation/transactions.test.d.ts +0 -0
  89. package/types/src/api/clipboard/clipboardExternal.test.d.ts +1 -0
  90. package/types/src/api/clipboard/clipboardInternal.test.d.ts +1 -0
  91. package/types/src/api/clipboard/testUtil.d.ts +541 -0
  92. package/types/src/api/exporters/html/htmlConversion.test.d.ts +1 -0
  93. package/types/src/api/exporters/markdown/markdownExporter.test.d.ts +1 -0
  94. package/types/src/api/nodeConversions/nodeConversions.test.d.ts +1 -0
  95. package/types/src/api/nodeConversions/nodeToBlock.d.ts +39 -2
  96. package/types/src/api/parsers/html/parseHTML.test.d.ts +1 -0
  97. package/types/src/api/parsers/markdown/parseMarkdown.test.d.ts +1 -0
  98. package/types/src/api/pmUtil.d.ts +3 -3
  99. package/types/src/api/testUtil/cases/customBlocks.d.ts +670 -0
  100. package/types/src/api/testUtil/cases/customInlineContent.d.ts +558 -0
  101. package/types/src/api/testUtil/cases/customStyles.d.ts +552 -0
  102. package/types/src/api/testUtil/cases/defaultSchema.d.ts +4 -0
  103. package/types/src/api/testUtil/index.d.ts +14 -0
  104. package/types/src/api/testUtil/partialBlockTestUtil.d.ts +9 -0
  105. package/types/src/api/testUtil/paste.d.ts +2 -0
  106. package/types/src/blocks/CodeBlockContent/defaultSupportedLanguages.d.ts +6 -0
  107. package/types/src/blocks/TableBlockContent/TableBlockContent.d.ts +9 -1
  108. package/types/src/editor/BlockNoteEditor.d.ts +55 -9
  109. package/types/src/editor/BlockNoteExtension.d.ts +9 -0
  110. package/types/src/editor/BlockNoteExtensions.d.ts +2 -2
  111. package/types/src/editor/BlockNoteTipTapEditor.d.ts +2 -2
  112. package/types/src/extensions/Collaboration/CursorPlugin.d.ts +3 -3
  113. package/types/src/extensions/Collaboration/ForkYDocPlugin.d.ts +41 -0
  114. package/types/src/extensions/Collaboration/ForkYDocPlugin.test.d.ts +1 -0
  115. package/types/src/extensions/Collaboration/SyncPlugin.d.ts +3 -3
  116. package/types/src/extensions/Collaboration/UndoPlugin.d.ts +3 -3
  117. package/types/src/extensions/Collaboration/createCollaborationExtensions.d.ts +17 -0
  118. package/types/src/extensions/Comments/CommentsPlugin.d.ts +2 -4
  119. package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +3 -4
  120. package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +5 -5
  121. package/types/src/extensions/LinkToolbar/LinkToolbarPlugin.d.ts +3 -4
  122. package/types/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.d.ts +2 -3
  123. package/types/src/extensions/Placeholder/PlaceholderPlugin.d.ts +2 -3
  124. package/types/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.d.ts +2 -3
  125. package/types/src/extensions/ShowSelection/ShowSelectionPlugin.d.ts +2 -3
  126. package/types/src/extensions/SideMenu/SideMenuPlugin.d.ts +3 -4
  127. package/types/src/extensions/SuggestionMenu/SuggestionPlugin.d.ts +2 -4
  128. package/types/src/extensions/Suggestions/SuggestionMarks.d.ts +4 -0
  129. package/types/src/extensions/TableHandles/TableHandlesPlugin.d.ts +5 -6
  130. package/types/src/i18n/locales/en.d.ts +0 -1
  131. package/types/src/i18n/locales/sk.d.ts +0 -1
  132. package/types/src/index.d.ts +15 -8
  133. package/dist/en-B7ycW7c8.js.map +0 -1
  134. package/dist/en-D4taoCs4.cjs.map +0 -1
  135. package/dist/tsconfig.tsbuildinfo +0 -1
  136. package/src/api/blockManipulation/selections/__snapshots__/selection.test.ts.snap +0 -844
  137. package/src/api/blockManipulation/selections/selection.test.ts +0 -72
  138. package/src/api/blockManipulation/selections/textCursorPosition/__snapshots__/textCursorPosition.test.ts.snap +0 -316
  139. package/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.test.ts +0 -74
@@ -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,31 @@ 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
14
  private enabled = false;
15
15
 
16
16
  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]);
17
+ super();
18
+ this.addProsemirrorPlugin(
19
+ new Plugin({
20
+ key: PLUGIN_KEY,
21
+ props: {
22
+ decorations: (state) => {
23
+ const { doc, selection } = state;
24
+
25
+ if (!this.enabled) {
26
+ return DecorationSet.empty;
27
+ }
28
+
29
+ const dec = Decoration.inline(selection.from, selection.to, {
30
+ "data-show-selection": "true",
31
+ });
32
+
33
+ return DecorationSet.create(doc, [dec]);
34
+ },
32
35
  },
33
- },
34
- });
36
+ }),
37
+ );
35
38
  }
36
39
 
37
40
  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,30 @@ 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
612
  public view: SideMenuView<BSchema, I, S> | undefined;
613
- public readonly plugin: Plugin;
614
613
 
615
614
  constructor(
616
615
  private readonly editor: BlockNoteEditor<BSchema, I, S>,
617
616
  sideMenuDetection: "viewport" | "editor",
618
617
  ) {
619
618
  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
- });
619
+ this.addProsemirrorPlugin(
620
+ 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
+ }),
634
+ );
634
635
  }
635
636
 
636
637
  public onUpdate(callback: (state: SideMenuState<BSchema, I, S>) => void) {
@@ -2,15 +2,15 @@ import { findParentNode } from "@tiptap/core";
2
2
  import { EditorState, Plugin, PluginKey } from "prosemirror-state";
3
3
  import { Decoration, DecorationSet, EditorView } from "prosemirror-view";
4
4
 
5
+ import { trackPosition } from "../../api/positionMapping.js";
5
6
  import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
7
+ import { BlockNoteExtension } from "../../editor/BlockNoteExtension.js";
6
8
  import { UiElementPosition } from "../../extensions-shared/UiElementPosition.js";
7
9
  import {
8
10
  BlockSchema,
9
11
  InlineContentSchema,
10
12
  StyleSchema,
11
13
  } from "../../schema/index.js";
12
- import { EventEmitter } from "../../util/EventEmitter.js";
13
- import { trackPosition } from "../../api/positionMapping.js";
14
14
 
15
15
  const findBlock = findParentNode((node) => node.type.name === "blockContainer");
16
16
 
@@ -166,190 +166,190 @@ export class SuggestionMenuProseMirrorPlugin<
166
166
  BSchema extends BlockSchema,
167
167
  I extends InlineContentSchema,
168
168
  S extends StyleSchema,
169
- > extends EventEmitter<any> {
169
+ > extends BlockNoteExtension {
170
170
  private view: SuggestionMenuView<BSchema, I, S> | undefined;
171
- public readonly plugin: Plugin;
172
-
173
171
  private triggerCharacters: string[] = [];
174
172
 
175
173
  constructor(editor: BlockNoteEditor<BSchema, I, S>) {
176
174
  super();
177
175
  const triggerCharacters = this.triggerCharacters;
178
- this.plugin = new Plugin({
179
- key: suggestionMenuPluginKey,
180
-
181
- view: () => {
182
- this.view = new SuggestionMenuView<BSchema, I, S>(
183
- editor,
184
- (triggerCharacter, state) => {
185
- this.emit(`update ${triggerCharacter}`, state);
186
- },
187
- );
188
- return this.view;
189
- },
190
-
191
- state: {
192
- // Initialize the plugin's internal state.
193
- init(): SuggestionPluginState {
194
- return undefined;
176
+ this.addProsemirrorPlugin(
177
+ new Plugin({
178
+ key: suggestionMenuPluginKey,
179
+
180
+ view: () => {
181
+ this.view = new SuggestionMenuView<BSchema, I, S>(
182
+ editor,
183
+ (triggerCharacter, state) => {
184
+ this.emit(`update ${triggerCharacter}`, state);
185
+ },
186
+ );
187
+ return this.view;
195
188
  },
196
189
 
197
- // Apply changes to the plugin state from an editor transaction.
198
- apply: (
199
- transaction,
200
- prev,
201
- _oldState,
202
- newState,
203
- ): SuggestionPluginState => {
204
- // TODO: More clearly define which transactions should be ignored.
205
- if (transaction.getMeta("orderedListIndexing") !== undefined) {
206
- return prev;
207
- }
208
-
209
- // Ignore transactions in code blocks.
210
- if (transaction.selection.$from.parent.type.spec.code) {
211
- return prev;
212
- }
213
-
214
- // Either contains the trigger character if the menu should be shown,
215
- // or null if it should be hidden.
216
- const suggestionPluginTransactionMeta: {
217
- triggerCharacter: string;
218
- deleteTriggerCharacter?: boolean;
219
- ignoreQueryLength?: boolean;
220
- } | null = transaction.getMeta(suggestionMenuPluginKey);
221
-
222
- if (
223
- typeof suggestionPluginTransactionMeta === "object" &&
224
- suggestionPluginTransactionMeta !== null
225
- ) {
226
- if (prev) {
227
- // Close the previous menu if it exists
228
- this.closeMenu();
229
- }
230
- const trackedPosition = trackPosition(
231
- editor,
232
- newState.selection.from -
233
- // Need to account for the trigger char that was inserted, so we offset the position by the length of the trigger character.
234
- suggestionPluginTransactionMeta.triggerCharacter.length,
235
- );
236
- return {
237
- triggerCharacter:
238
- suggestionPluginTransactionMeta.triggerCharacter,
239
- deleteTriggerCharacter:
240
- suggestionPluginTransactionMeta.deleteTriggerCharacter !==
241
- false,
242
- // When reading the queryStartPos, we offset the result by the length of the trigger character, to make it easy on the caller
243
- queryStartPos: () =>
244
- trackedPosition() +
245
- suggestionPluginTransactionMeta.triggerCharacter.length,
246
- query: "",
247
- decorationId: `id_${Math.floor(Math.random() * 0xffffffff)}`,
248
- ignoreQueryLength:
249
- suggestionPluginTransactionMeta?.ignoreQueryLength,
250
- };
251
- }
252
-
253
- // Checks if the menu is hidden, in which case it doesn't need to be hidden or updated.
254
- if (prev === undefined) {
255
- return prev;
256
- }
257
-
258
- // Checks if the menu should be hidden.
259
- if (
260
- // Highlighting text should hide the menu.
261
- newState.selection.from !== newState.selection.to ||
262
- // Transactions with plugin metadata should hide the menu.
263
- suggestionPluginTransactionMeta === null ||
264
- // Certain mouse events should hide the menu.
265
- // TODO: Change to global mousedown listener.
266
- transaction.getMeta("focus") ||
267
- transaction.getMeta("blur") ||
268
- transaction.getMeta("pointer") ||
269
- // Moving the caret before the character which triggered the menu should hide it.
270
- (prev.triggerCharacter !== undefined &&
271
- newState.selection.from < prev.queryStartPos()) ||
272
- // Moving the caret to a new block should hide the menu.
273
- !newState.selection.$from.sameParent(
274
- newState.doc.resolve(prev.queryStartPos()),
275
- )
276
- ) {
190
+ state: {
191
+ // Initialize the plugin's internal state.
192
+ init(): SuggestionPluginState {
277
193
  return undefined;
278
- }
194
+ },
279
195
 
280
- const next = { ...prev };
196
+ // Apply changes to the plugin state from an editor transaction.
197
+ apply: (
198
+ transaction,
199
+ prev,
200
+ _oldState,
201
+ newState,
202
+ ): SuggestionPluginState => {
203
+ // TODO: More clearly define which transactions should be ignored.
204
+ if (transaction.getMeta("orderedListIndexing") !== undefined) {
205
+ return prev;
206
+ }
281
207
 
282
- // Updates the current query.
283
- next.query = newState.doc.textBetween(
284
- prev.queryStartPos(),
285
- newState.selection.from,
286
- );
208
+ // Ignore transactions in code blocks.
209
+ if (transaction.selection.$from.parent.type.spec.code) {
210
+ return prev;
211
+ }
287
212
 
288
- return next;
289
- },
290
- },
291
-
292
- props: {
293
- handleTextInput(view, _from, _to, text) {
294
- if (triggerCharacters.includes(text)) {
295
- view.dispatch(view.state.tr.insertText(text));
296
- view.dispatch(
297
- view.state.tr
298
- .setMeta(suggestionMenuPluginKey, {
299
- triggerCharacter: text,
300
- })
301
- .scrollIntoView(),
213
+ // Either contains the trigger character if the menu should be shown,
214
+ // or null if it should be hidden.
215
+ const suggestionPluginTransactionMeta: {
216
+ triggerCharacter: string;
217
+ deleteTriggerCharacter?: boolean;
218
+ ignoreQueryLength?: boolean;
219
+ } | null = transaction.getMeta(suggestionMenuPluginKey);
220
+
221
+ if (
222
+ typeof suggestionPluginTransactionMeta === "object" &&
223
+ suggestionPluginTransactionMeta !== null
224
+ ) {
225
+ if (prev) {
226
+ // Close the previous menu if it exists
227
+ this.closeMenu();
228
+ }
229
+ const trackedPosition = trackPosition(
230
+ editor,
231
+ newState.selection.from -
232
+ // Need to account for the trigger char that was inserted, so we offset the position by the length of the trigger character.
233
+ suggestionPluginTransactionMeta.triggerCharacter.length,
234
+ );
235
+ return {
236
+ triggerCharacter:
237
+ suggestionPluginTransactionMeta.triggerCharacter,
238
+ deleteTriggerCharacter:
239
+ suggestionPluginTransactionMeta.deleteTriggerCharacter !==
240
+ false,
241
+ // When reading the queryStartPos, we offset the result by the length of the trigger character, to make it easy on the caller
242
+ queryStartPos: () =>
243
+ trackedPosition() +
244
+ suggestionPluginTransactionMeta.triggerCharacter.length,
245
+ query: "",
246
+ decorationId: `id_${Math.floor(Math.random() * 0xffffffff)}`,
247
+ ignoreQueryLength:
248
+ suggestionPluginTransactionMeta?.ignoreQueryLength,
249
+ };
250
+ }
251
+
252
+ // Checks if the menu is hidden, in which case it doesn't need to be hidden or updated.
253
+ if (prev === undefined) {
254
+ return prev;
255
+ }
256
+
257
+ // Checks if the menu should be hidden.
258
+ if (
259
+ // Highlighting text should hide the menu.
260
+ newState.selection.from !== newState.selection.to ||
261
+ // Transactions with plugin metadata should hide the menu.
262
+ suggestionPluginTransactionMeta === null ||
263
+ // Certain mouse events should hide the menu.
264
+ // TODO: Change to global mousedown listener.
265
+ transaction.getMeta("focus") ||
266
+ transaction.getMeta("blur") ||
267
+ transaction.getMeta("pointer") ||
268
+ // Moving the caret before the character which triggered the menu should hide it.
269
+ (prev.triggerCharacter !== undefined &&
270
+ newState.selection.from < prev.queryStartPos()) ||
271
+ // Moving the caret to a new block should hide the menu.
272
+ !newState.selection.$from.sameParent(
273
+ newState.doc.resolve(prev.queryStartPos()),
274
+ )
275
+ ) {
276
+ return undefined;
277
+ }
278
+
279
+ const next = { ...prev };
280
+
281
+ // Updates the current query.
282
+ next.query = newState.doc.textBetween(
283
+ prev.queryStartPos(),
284
+ newState.selection.from,
302
285
  );
303
286
 
304
- return true;
305
- }
306
- return false;
287
+ return next;
288
+ },
307
289
  },
308
290
 
309
- // Setup decorator on the currently active suggestion.
310
- decorations(state) {
311
- const suggestionPluginState: SuggestionPluginState = (
312
- this as Plugin
313
- ).getState(state);
314
-
315
- if (suggestionPluginState === undefined) {
316
- return null;
317
- }
318
-
319
- // If the menu was opened programmatically by another extension, it may not use a trigger character. In this
320
- // case, the decoration is set on the whole block instead, as the decoration range would otherwise be empty.
321
- if (!suggestionPluginState.deleteTriggerCharacter) {
322
- const blockNode = findBlock(state.selection);
323
- if (blockNode) {
324
- return DecorationSet.create(state.doc, [
325
- Decoration.node(
326
- blockNode.pos,
327
- blockNode.pos + blockNode.node.nodeSize,
328
- {
329
- nodeName: "span",
330
- class: "bn-suggestion-decorator",
331
- "data-decoration-id": suggestionPluginState.decorationId,
332
- },
333
- ),
334
- ]);
291
+ props: {
292
+ handleTextInput(view, _from, _to, text) {
293
+ if (triggerCharacters.includes(text)) {
294
+ view.dispatch(view.state.tr.insertText(text));
295
+ view.dispatch(
296
+ view.state.tr
297
+ .setMeta(suggestionMenuPluginKey, {
298
+ triggerCharacter: text,
299
+ })
300
+ .scrollIntoView(),
301
+ );
302
+
303
+ return true;
304
+ }
305
+ return false;
306
+ },
307
+
308
+ // Setup decorator on the currently active suggestion.
309
+ decorations(state) {
310
+ const suggestionPluginState: SuggestionPluginState = (
311
+ this as Plugin
312
+ ).getState(state);
313
+
314
+ if (suggestionPluginState === undefined) {
315
+ return null;
335
316
  }
336
- }
337
- // Creates an inline decoration around the trigger character.
338
- return DecorationSet.create(state.doc, [
339
- Decoration.inline(
340
- suggestionPluginState.queryStartPos() -
341
- suggestionPluginState.triggerCharacter!.length,
342
- suggestionPluginState.queryStartPos(),
343
- {
344
- nodeName: "span",
345
- class: "bn-suggestion-decorator",
346
- "data-decoration-id": suggestionPluginState.decorationId,
347
- },
348
- ),
349
- ]);
317
+
318
+ // If the menu was opened programmatically by another extension, it may not use a trigger character. In this
319
+ // case, the decoration is set on the whole block instead, as the decoration range would otherwise be empty.
320
+ if (!suggestionPluginState.deleteTriggerCharacter) {
321
+ const blockNode = findBlock(state.selection);
322
+ if (blockNode) {
323
+ return DecorationSet.create(state.doc, [
324
+ Decoration.node(
325
+ blockNode.pos,
326
+ blockNode.pos + blockNode.node.nodeSize,
327
+ {
328
+ nodeName: "span",
329
+ class: "bn-suggestion-decorator",
330
+ "data-decoration-id": suggestionPluginState.decorationId,
331
+ },
332
+ ),
333
+ ]);
334
+ }
335
+ }
336
+ // Creates an inline decoration around the trigger character.
337
+ return DecorationSet.create(state.doc, [
338
+ Decoration.inline(
339
+ suggestionPluginState.queryStartPos() -
340
+ suggestionPluginState.triggerCharacter!.length,
341
+ suggestionPluginState.queryStartPos(),
342
+ {
343
+ nodeName: "span",
344
+ class: "bn-suggestion-decorator",
345
+ "data-decoration-id": suggestionPluginState.decorationId,
346
+ },
347
+ ),
348
+ ]);
349
+ },
350
350
  },
351
- },
352
- });
351
+ }),
352
+ );
353
353
  }
354
354
 
355
355
  public onUpdate(
@@ -235,7 +235,7 @@ export function getDefaultSlashMenuItems<
235
235
 
236
236
  // Immediately open the file toolbar
237
237
  editor.transact((tr) =>
238
- tr.setMeta(editor.filePanel!.plugin, {
238
+ tr.setMeta(editor.filePanel!.plugins[0], {
239
239
  block: insertedBlock,
240
240
  }),
241
241
  );
@@ -254,7 +254,7 @@ export function getDefaultSlashMenuItems<
254
254
 
255
255
  // Immediately open the file toolbar
256
256
  editor.transact((tr) =>
257
- tr.setMeta(editor.filePanel!.plugin, {
257
+ tr.setMeta(editor.filePanel!.plugins[0], {
258
258
  block: insertedBlock,
259
259
  }),
260
260
  );
@@ -273,7 +273,7 @@ export function getDefaultSlashMenuItems<
273
273
 
274
274
  // Immediately open the file toolbar
275
275
  editor.transact((tr) =>
276
- tr.setMeta(editor.filePanel!.plugin, {
276
+ tr.setMeta(editor.filePanel!.plugins[0], {
277
277
  block: insertedBlock,
278
278
  }),
279
279
  );
@@ -292,7 +292,7 @@ export function getDefaultSlashMenuItems<
292
292
 
293
293
  // Immediately open the file toolbar
294
294
  editor.transact((tr) =>
295
- tr.setMeta(editor.filePanel!.plugin, {
295
+ tr.setMeta(editor.filePanel!.plugins[0], {
296
296
  block: insertedBlock,
297
297
  }),
298
298
  );