@blocknote/core 0.19.2 → 0.21.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 (220) hide show
  1. package/dist/blocknote.js +2035 -1758
  2. package/dist/blocknote.js.map +1 -1
  3. package/dist/blocknote.umd.cjs +6 -6
  4. package/dist/blocknote.umd.cjs.map +1 -1
  5. package/dist/src/api/blockManipulation/commands/insertBlocks/insertBlocks.js +6 -3
  6. package/dist/src/api/blockManipulation/commands/insertBlocks/insertBlocks.js.map +1 -1
  7. package/dist/src/api/blockManipulation/commands/moveBlocks/moveBlocks.js +219 -0
  8. package/dist/src/api/blockManipulation/commands/moveBlocks/moveBlocks.js.map +1 -0
  9. package/dist/src/api/blockManipulation/commands/moveBlocks/moveBlocks.test.js +175 -0
  10. package/dist/src/api/blockManipulation/commands/moveBlocks/moveBlocks.test.js.map +1 -0
  11. package/dist/src/api/blockManipulation/commands/splitBlock/splitBlock.test.js +4 -1
  12. package/dist/src/api/blockManipulation/commands/splitBlock/splitBlock.test.js.map +1 -1
  13. package/dist/src/api/blockManipulation/commands/updateBlock/updateBlock.js +6 -3
  14. package/dist/src/api/blockManipulation/commands/updateBlock/updateBlock.js.map +1 -1
  15. package/dist/src/api/blockManipulation/getBlock/getBlock.js +56 -0
  16. package/dist/src/api/blockManipulation/getBlock/getBlock.js.map +1 -0
  17. package/dist/src/api/blockManipulation/selections/selection.js +149 -0
  18. package/dist/src/api/blockManipulation/selections/selection.js.map +1 -0
  19. package/dist/src/api/blockManipulation/selections/selection.test.js +39 -0
  20. package/dist/src/api/blockManipulation/selections/selection.test.js.map +1 -0
  21. package/dist/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.js +3 -0
  22. package/dist/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.js.map +1 -1
  23. package/dist/src/api/clipboard/clipboard.test.js +6 -3
  24. package/dist/src/api/clipboard/clipboard.test.js.map +1 -1
  25. package/dist/src/api/clipboard/fromClipboard/handleFileInsertion.js +1 -1
  26. package/dist/src/api/clipboard/fromClipboard/handleFileInsertion.js.map +1 -1
  27. package/dist/src/api/clipboard/fromClipboard/handleVSCodePaste.js +2 -3
  28. package/dist/src/api/clipboard/fromClipboard/handleVSCodePaste.js.map +1 -1
  29. package/dist/src/api/clipboard/fromClipboard/pasteExtension.js +5 -5
  30. package/dist/src/api/clipboard/fromClipboard/pasteExtension.js.map +1 -1
  31. package/dist/src/api/clipboard/toClipboard/copyExtension.js +4 -2
  32. package/dist/src/api/clipboard/toClipboard/copyExtension.js.map +1 -1
  33. package/dist/src/api/nodeUtil.js +1 -1
  34. package/dist/src/api/nodeUtil.js.map +1 -1
  35. package/dist/src/api/parsers/markdown/parseMarkdown.test.js +4 -1
  36. package/dist/src/api/parsers/markdown/parseMarkdown.test.js.map +1 -1
  37. package/dist/src/blocks/AudioBlockContent/AudioBlockContent.js +14 -7
  38. package/dist/src/blocks/AudioBlockContent/AudioBlockContent.js.map +1 -1
  39. package/dist/src/blocks/AudioBlockContent/{audioBlockHelpers.js → parseAudioElement.js} +1 -1
  40. package/dist/src/blocks/AudioBlockContent/parseAudioElement.js.map +1 -0
  41. package/dist/src/blocks/FileBlockContent/FileBlockContent.js +5 -4
  42. package/dist/src/blocks/FileBlockContent/FileBlockContent.js.map +1 -1
  43. package/dist/src/blocks/FileBlockContent/helpers/parse/parseEmbedElement.js +5 -0
  44. package/dist/src/blocks/FileBlockContent/helpers/parse/parseEmbedElement.js.map +1 -0
  45. package/dist/src/blocks/FileBlockContent/helpers/parse/parseFigureElement.js +10 -0
  46. package/dist/src/blocks/FileBlockContent/helpers/parse/parseFigureElement.js.map +1 -0
  47. package/dist/src/blocks/FileBlockContent/helpers/render/createAddFileButton.js +39 -0
  48. package/dist/src/blocks/FileBlockContent/helpers/render/createAddFileButton.js.map +1 -0
  49. package/dist/src/blocks/FileBlockContent/helpers/render/createFileBlockWrapper.js +51 -0
  50. package/dist/src/blocks/FileBlockContent/helpers/render/createFileBlockWrapper.js.map +1 -0
  51. package/dist/src/blocks/FileBlockContent/helpers/render/createFileNameWithIcon.js +17 -0
  52. package/dist/src/blocks/FileBlockContent/helpers/render/createFileNameWithIcon.js.map +1 -0
  53. package/dist/src/blocks/FileBlockContent/helpers/render/createResizableFileBlockWrapper.js +147 -0
  54. package/dist/src/blocks/FileBlockContent/helpers/render/createResizableFileBlockWrapper.js.map +1 -0
  55. package/dist/src/blocks/FileBlockContent/helpers/toExternalHTML/createFigureWithCaption.js +9 -0
  56. package/dist/src/blocks/FileBlockContent/helpers/toExternalHTML/createFigureWithCaption.js.map +1 -0
  57. package/dist/src/blocks/FileBlockContent/helpers/toExternalHTML/createLinkWithCaption.js +11 -0
  58. package/dist/src/blocks/FileBlockContent/helpers/toExternalHTML/createLinkWithCaption.js.map +1 -0
  59. package/dist/src/blocks/ImageBlockContent/ImageBlockContent.js +17 -9
  60. package/dist/src/blocks/ImageBlockContent/ImageBlockContent.js.map +1 -1
  61. package/dist/src/blocks/ImageBlockContent/{imageBlockHelpers.js → parseImageElement.js} +1 -1
  62. package/dist/src/blocks/ImageBlockContent/parseImageElement.js.map +1 -0
  63. package/dist/src/blocks/TableBlockContent/TableExtension.js +8 -1
  64. package/dist/src/blocks/TableBlockContent/TableExtension.js.map +1 -1
  65. package/dist/src/blocks/VideoBlockContent/VideoBlockContent.js +18 -7
  66. package/dist/src/blocks/VideoBlockContent/VideoBlockContent.js.map +1 -1
  67. package/dist/src/blocks/VideoBlockContent/{videoBlockHelpers.js → parseVideoElement.js} +1 -1
  68. package/dist/src/blocks/VideoBlockContent/parseVideoElement.js.map +1 -0
  69. package/dist/src/editor/BlockNoteEditor.js +64 -62
  70. package/dist/src/editor/BlockNoteEditor.js.map +1 -1
  71. package/dist/src/editor/BlockNoteExtensions.js +5 -8
  72. package/dist/src/editor/BlockNoteExtensions.js.map +1 -1
  73. package/dist/src/extensions/FormattingToolbar/FormattingToolbarPlugin.js +4 -2
  74. package/dist/src/extensions/FormattingToolbar/FormattingToolbarPlugin.js.map +1 -1
  75. package/dist/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.js +10 -8
  76. package/dist/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.js.map +1 -1
  77. package/dist/src/extensions/LinkToolbar/LinkToolbarPlugin.js +7 -3
  78. package/dist/src/extensions/LinkToolbar/LinkToolbarPlugin.js.map +1 -1
  79. package/dist/src/extensions/LinkToolbar/protocols.js +14 -0
  80. package/dist/src/extensions/LinkToolbar/protocols.js.map +1 -0
  81. package/dist/src/extensions/Placeholder/PlaceholderPlugin.js +19 -13
  82. package/dist/src/extensions/Placeholder/PlaceholderPlugin.js.map +1 -1
  83. package/dist/src/extensions/SideMenu/SideMenuPlugin.js +5 -1
  84. package/dist/src/extensions/SideMenu/SideMenuPlugin.js.map +1 -1
  85. package/dist/src/extensions/SideMenu/dragging.js +8 -1
  86. package/dist/src/extensions/SideMenu/dragging.js.map +1 -1
  87. package/dist/src/extensions/SuggestionMenu/SuggestionPlugin.js +3 -3
  88. package/dist/src/extensions/SuggestionMenu/SuggestionPlugin.js.map +1 -1
  89. package/dist/src/extensions/TableHandles/TableHandlesPlugin.js +37 -11
  90. package/dist/src/extensions/TableHandles/TableHandlesPlugin.js.map +1 -1
  91. package/dist/src/i18n/locales/ru.js +1 -1
  92. package/dist/src/index.js +9 -2
  93. package/dist/src/index.js.map +1 -1
  94. package/dist/src/schema/inlineContent/createSpec.js +1 -1
  95. package/dist/src/schema/inlineContent/createSpec.js.map +1 -1
  96. package/dist/style.css +1 -1
  97. package/dist/tsconfig.tsbuildinfo +1 -1
  98. package/dist/webpack-stats.json +1 -1
  99. package/package.json +3 -3
  100. package/src/api/blockManipulation/commands/insertBlocks/insertBlocks.ts +6 -6
  101. package/src/api/blockManipulation/commands/moveBlocks/__snapshots__/moveBlocks.test.ts.snap +9506 -0
  102. package/src/api/blockManipulation/commands/moveBlocks/moveBlocks.test.ts +295 -0
  103. package/src/api/blockManipulation/commands/moveBlocks/moveBlocks.ts +336 -0
  104. package/src/api/blockManipulation/commands/splitBlock/splitBlock.test.ts +5 -1
  105. package/src/api/blockManipulation/commands/updateBlock/updateBlock.ts +11 -3
  106. package/src/api/blockManipulation/getBlock/getBlock.ts +141 -0
  107. package/src/api/blockManipulation/selections/__snapshots__/selection.test.ts.snap +660 -0
  108. package/src/api/blockManipulation/selections/selection.test.ts +56 -0
  109. package/src/api/blockManipulation/selections/selection.ts +244 -0
  110. package/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.ts +4 -0
  111. package/src/api/clipboard/__snapshots__/tableAllCells.html +1 -1
  112. package/src/api/clipboard/__snapshots__/tableCell.html +1 -1
  113. package/src/api/clipboard/__snapshots__/tableRow.html +1 -1
  114. package/src/api/clipboard/clipboard.test.ts +7 -3
  115. package/src/api/clipboard/fromClipboard/handleFileInsertion.ts +1 -1
  116. package/src/api/clipboard/fromClipboard/handleVSCodePaste.ts +7 -14
  117. package/src/api/clipboard/fromClipboard/pasteExtension.ts +6 -6
  118. package/src/api/clipboard/toClipboard/copyExtension.ts +7 -2
  119. package/src/api/exporters/html/__snapshots__/file/basic/internal.html +1 -1
  120. package/src/api/exporters/html/__snapshots__/file/nested/internal.html +1 -1
  121. package/src/api/exporters/html/__snapshots__/file/noCaption/internal.html +1 -1
  122. package/src/api/exporters/html/__snapshots__/file/noName/internal.html +1 -1
  123. package/src/api/exporters/html/__snapshots__/image/basic/internal.html +1 -1
  124. package/src/api/exporters/html/__snapshots__/image/nested/internal.html +1 -1
  125. package/src/api/exporters/html/__snapshots__/image/noCaption/internal.html +1 -1
  126. package/src/api/exporters/html/__snapshots__/image/noName/internal.html +1 -1
  127. package/src/api/exporters/html/__snapshots__/image/noPreview/internal.html +1 -1
  128. package/src/api/exporters/html/__snapshots__/simpleImage/basic/external.html +1 -1
  129. package/src/api/exporters/html/__snapshots__/simpleImage/basic/internal.html +1 -1
  130. package/src/api/exporters/html/__snapshots__/simpleImage/nested/external.html +1 -1
  131. package/src/api/exporters/html/__snapshots__/simpleImage/nested/internal.html +1 -1
  132. package/src/api/exporters/html/__snapshots__/simpleImage/noCaption/external.html +1 -1
  133. package/src/api/exporters/html/__snapshots__/simpleImage/noCaption/internal.html +1 -1
  134. package/src/api/exporters/html/__snapshots__/simpleImage/noName/external.html +1 -1
  135. package/src/api/exporters/html/__snapshots__/simpleImage/noName/internal.html +1 -1
  136. package/src/api/exporters/html/__snapshots__/simpleImage/noPreview/external.html +1 -1
  137. package/src/api/exporters/html/__snapshots__/simpleImage/noPreview/internal.html +1 -1
  138. package/src/api/exporters/markdown/__snapshots__/simpleImage/basic/markdown.md +1 -1
  139. package/src/api/exporters/markdown/__snapshots__/simpleImage/nested/markdown.md +2 -2
  140. package/src/api/exporters/markdown/__snapshots__/simpleImage/noCaption/markdown.md +1 -1
  141. package/src/api/exporters/markdown/__snapshots__/simpleImage/noName/markdown.md +1 -1
  142. package/src/api/nodeUtil.ts +2 -2
  143. package/src/api/parsers/markdown/parseMarkdown.test.ts +5 -7
  144. package/src/blocks/AudioBlockContent/AudioBlockContent.ts +13 -14
  145. package/src/blocks/FileBlockContent/FileBlockContent.ts +5 -12
  146. package/src/blocks/FileBlockContent/helpers/parse/parseEmbedElement.ts +5 -0
  147. package/src/blocks/FileBlockContent/helpers/parse/parseFigureElement.ts +16 -0
  148. package/src/blocks/FileBlockContent/helpers/render/createAddFileButton.ts +63 -0
  149. package/src/blocks/FileBlockContent/helpers/render/createFileBlockWrapper.ts +80 -0
  150. package/src/blocks/FileBlockContent/helpers/render/createFileNameWithIcon.ts +24 -0
  151. package/src/blocks/FileBlockContent/helpers/render/createResizableFileBlockWrapper.ts +204 -0
  152. package/src/blocks/FileBlockContent/helpers/toExternalHTML/createFigureWithCaption.ts +13 -0
  153. package/src/blocks/FileBlockContent/helpers/toExternalHTML/createLinkWithCaption.ts +15 -0
  154. package/src/blocks/ImageBlockContent/ImageBlockContent.ts +20 -28
  155. package/src/blocks/TableBlockContent/TableExtension.ts +12 -1
  156. package/src/blocks/VideoBlockContent/VideoBlockContent.ts +20 -27
  157. package/src/editor/Block.css +35 -51
  158. package/src/editor/BlockNoteEditor.ts +101 -92
  159. package/src/editor/BlockNoteExtensions.ts +9 -8
  160. package/src/editor/editor.css +1 -0
  161. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +4 -2
  162. package/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts +11 -8
  163. package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +11 -4
  164. package/src/extensions/LinkToolbar/protocols.ts +13 -0
  165. package/src/extensions/Placeholder/PlaceholderPlugin.ts +29 -21
  166. package/src/extensions/SideMenu/SideMenuPlugin.ts +5 -1
  167. package/src/extensions/SideMenu/dragging.ts +8 -1
  168. package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +3 -6
  169. package/src/extensions/TableHandles/TableHandlesPlugin.ts +49 -12
  170. package/src/i18n/locales/ru.ts +1 -1
  171. package/src/index.ts +9 -2
  172. package/src/schema/inlineContent/createSpec.ts +2 -2
  173. package/types/src/api/blockManipulation/commands/moveBlocks/moveBlocks.d.ts +15 -0
  174. package/types/src/api/blockManipulation/getBlock/getBlock.d.ts +7 -0
  175. package/types/src/api/blockManipulation/selections/selection.d.ts +5 -0
  176. package/types/src/api/blockManipulation/selections/selection.test.d.ts +1 -0
  177. package/types/src/api/clipboard/fromClipboard/handleVSCodePaste.d.ts +2 -3
  178. package/types/src/api/clipboard/fromClipboard/pasteExtension.d.ts +1 -3
  179. package/types/src/api/nodeUtil.d.ts +1 -1
  180. package/types/src/blocks/AudioBlockContent/AudioBlockContent.d.ts +2 -5
  181. package/types/src/blocks/FileBlockContent/FileBlockContent.d.ts +2 -5
  182. package/types/src/blocks/FileBlockContent/helpers/parse/parseEmbedElement.d.ts +3 -0
  183. package/types/src/blocks/FileBlockContent/helpers/parse/parseFigureElement.d.ts +4 -0
  184. package/types/src/blocks/FileBlockContent/helpers/render/createAddFileButton.d.ts +6 -0
  185. package/types/src/blocks/FileBlockContent/helpers/render/createFileBlockWrapper.d.ts +9 -0
  186. package/types/src/blocks/FileBlockContent/helpers/render/createFileNameWithIcon.d.ts +6 -0
  187. package/types/src/blocks/FileBlockContent/helpers/render/createResizableFileBlockWrapper.d.ts +9 -0
  188. package/types/src/blocks/FileBlockContent/helpers/toExternalHTML/createFigureWithCaption.d.ts +3 -0
  189. package/types/src/blocks/FileBlockContent/helpers/toExternalHTML/createLinkWithCaption.d.ts +3 -0
  190. package/types/src/blocks/ImageBlockContent/ImageBlockContent.d.ts +2 -5
  191. package/types/src/blocks/VideoBlockContent/VideoBlockContent.d.ts +2 -5
  192. package/types/src/editor/BlockNoteEditor.d.ts +60 -14
  193. package/types/src/editor/BlockNoteExtensions.d.ts +1 -0
  194. package/types/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.d.ts +1 -0
  195. package/types/src/extensions/LinkToolbar/protocols.d.ts +2 -0
  196. package/types/src/index.d.ts +9 -2
  197. package/types/src/pm-nodes/BlockContainer.d.ts +2 -2
  198. package/types/src/pm-nodes/BlockGroup.d.ts +2 -2
  199. package/dist/src/api/blockManipulation/commands/moveBlock/moveBlock.js +0 -116
  200. package/dist/src/api/blockManipulation/commands/moveBlock/moveBlock.js.map +0 -1
  201. package/dist/src/api/blockManipulation/commands/moveBlock/moveBlock.test.js +0 -110
  202. package/dist/src/api/blockManipulation/commands/moveBlock/moveBlock.test.js.map +0 -1
  203. package/dist/src/blocks/AudioBlockContent/audioBlockHelpers.js.map +0 -1
  204. package/dist/src/blocks/FileBlockContent/fileBlockHelpers.js +0 -317
  205. package/dist/src/blocks/FileBlockContent/fileBlockHelpers.js.map +0 -1
  206. package/dist/src/blocks/ImageBlockContent/imageBlockHelpers.js.map +0 -1
  207. package/dist/src/blocks/VideoBlockContent/videoBlockHelpers.js.map +0 -1
  208. package/src/api/blockManipulation/commands/moveBlock/__snapshots__/moveBlock.test.ts.snap +0 -3799
  209. package/src/api/blockManipulation/commands/moveBlock/moveBlock.test.ts +0 -196
  210. package/src/api/blockManipulation/commands/moveBlock/moveBlock.ts +0 -176
  211. package/src/blocks/FileBlockContent/fileBlockHelpers.ts +0 -456
  212. package/types/src/api/blockManipulation/commands/moveBlock/moveBlock.d.ts +0 -5
  213. package/types/src/blocks/FileBlockContent/fileBlockHelpers.d.ts +0 -41
  214. /package/src/blocks/AudioBlockContent/{audioBlockHelpers.ts → parseAudioElement.ts} +0 -0
  215. /package/src/blocks/ImageBlockContent/{imageBlockHelpers.ts → parseImageElement.ts} +0 -0
  216. /package/src/blocks/VideoBlockContent/{videoBlockHelpers.ts → parseVideoElement.ts} +0 -0
  217. /package/types/src/api/blockManipulation/commands/{moveBlock/moveBlock.test.d.ts → moveBlocks/moveBlocks.test.d.ts} +0 -0
  218. /package/types/src/blocks/AudioBlockContent/{audioBlockHelpers.d.ts → parseAudioElement.d.ts} +0 -0
  219. /package/types/src/blocks/ImageBlockContent/{imageBlockHelpers.d.ts → parseImageElement.d.ts} +0 -0
  220. /package/types/src/blocks/VideoBlockContent/{videoBlockHelpers.d.ts → parseVideoElement.d.ts} +0 -0
@@ -7,16 +7,11 @@ import {
7
7
  PropSchema,
8
8
  } from "../../schema/index.js";
9
9
  import { defaultProps } from "../defaultProps.js";
10
-
11
- import {
12
- createFigureWithCaption,
13
- createFileAndCaptionWrapper,
14
- createFileBlockWrapper,
15
- createLinkWithCaption,
16
- createResizeHandlesWrapper,
17
- parseFigureElement,
18
- } from "../FileBlockContent/fileBlockHelpers.js";
19
- import { parseVideoElement } from "./videoBlockHelpers.js";
10
+ import { parseFigureElement } from "../FileBlockContent/helpers/parse/parseFigureElement.js";
11
+ import { createFigureWithCaption } from "../FileBlockContent/helpers/toExternalHTML/createFigureWithCaption.js";
12
+ import { createLinkWithCaption } from "../FileBlockContent/helpers/toExternalHTML/createLinkWithCaption.js";
13
+ import { createResizableFileBlockWrapper } from "../FileBlockContent/helpers/render/createResizableFileBlockWrapper.js";
14
+ import { parseVideoElement } from "./parseVideoElement.js";
20
15
 
21
16
  export const FILE_VIDEO_ICON_SVG =
22
17
  '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor"><path d="M2 3.9934C2 3.44476 2.45531 3 2.9918 3H21.0082C21.556 3 22 3.44495 22 3.9934V20.0066C22 20.5552 21.5447 21 21.0082 21H2.9918C2.44405 21 2 20.5551 2 20.0066V3.9934ZM8 5V19H16V5H8ZM4 5V7H6V5H4ZM18 5V7H20V5H18ZM4 9V11H6V9H4ZM18 9V11H20V9H18ZM4 13V15H6V13H4ZM18 13V15H20V13H18ZM4 17V19H6V17H4ZM18 17V19H20V17H18Z"></path></svg>';
@@ -61,31 +56,29 @@ export const videoRender = (
61
56
  const icon = document.createElement("div");
62
57
  icon.innerHTML = FILE_VIDEO_ICON_SVG;
63
58
 
59
+ const videoWrapper = document.createElement("div");
60
+ videoWrapper.className = "bn-visual-media-wrapper";
61
+
64
62
  const video = document.createElement("video");
65
63
  video.className = "bn-visual-media";
66
- video.src = block.props.url;
64
+ if (editor.resolveFileUrl) {
65
+ editor.resolveFileUrl(block.props.url).then((downloadUrl) => {
66
+ video.src = downloadUrl;
67
+ });
68
+ } else {
69
+ video.src = block.props.url;
70
+ }
67
71
  video.controls = true;
68
72
  video.contentEditable = "false";
69
73
  video.draggable = false;
70
- video.width = Math.min(
71
- block.props.previewWidth,
72
- editor.domElement.firstElementChild!.clientWidth
73
- );
74
-
75
- const file = createResizeHandlesWrapper(
76
- block,
77
- editor,
78
- video,
79
- () => video.width,
80
- (width) => (video.width = width)
81
- );
82
-
83
- const element = createFileAndCaptionWrapper(block, file.dom);
74
+ video.width = block.props.previewWidth;
75
+ videoWrapper.appendChild(video);
84
76
 
85
- return createFileBlockWrapper(
77
+ return createResizableFileBlockWrapper(
86
78
  block,
87
79
  editor,
88
- element,
80
+ { dom: videoWrapper },
81
+ videoWrapper,
89
82
  editor.dictionary.file_blocks.video.add_button_text,
90
83
  icon.firstElementChild as HTMLElement
91
84
  );
@@ -310,85 +310,66 @@ NESTED BLOCKS
310
310
 
311
311
  /* FILES */
312
312
 
313
- /* Add block button & default preview */
314
- [data-file-block] .bn-file-block-content-wrapper:has(.bn-add-file-button),
315
- [data-file-block] .bn-file-block-content-wrapper:has(.bn-file-default-preview) {
316
- width: 100%;
317
- }
318
-
313
+ /* Element that wraps content for all file blocks */
319
314
  [data-file-block] .bn-file-block-content-wrapper {
320
315
  cursor: pointer;
321
316
  display: flex;
322
317
  flex-direction: column;
323
- justify-content: stretch;
324
318
  user-select: none;
325
319
  }
326
320
 
321
+ /* Add block button & default element (name with icon) */
322
+ [data-file-block] .bn-file-block-content-wrapper:has(.bn-add-file-button),
323
+ [data-file-block] .bn-file-block-content-wrapper:has(.bn-file-name-with-icon) {
324
+ width: 100%;
325
+ }
326
+
327
327
  [data-file-block] .bn-add-file-button {
328
328
  align-items: center;
329
329
  background-color: rgb(242, 241, 238);
330
330
  border-radius: 4px;
331
331
  color: rgb(125, 121, 122);
332
332
  display: flex;
333
- flex-direction: row;
334
333
  gap: 10px;
335
334
  padding: 12px;
336
- width: 100%;
337
335
  }
338
336
 
339
- .bn-editor[contenteditable="true"] [data-file-block] .bn-add-file-button:hover {
337
+ .bn-editor[contenteditable="true"] [data-file-block] .bn-add-file-button:hover,
338
+ [data-file-block] .bn-file-name-with-icon:hover,
339
+ .ProseMirror-selectednode .bn-file-name-with-icon{
340
340
  background-color: rgb(225, 225, 225);
341
341
  }
342
342
 
343
- [data-file-block] .bn-add-file-button-icon {
343
+ [data-file-block] .bn-add-file-button-icon,
344
+ [data-file-block] .bn-file-icon {
344
345
  width: 24px;
345
346
  height: 24px;
346
347
  }
347
348
 
348
- [data-file-block] .bn-add-file-button .bn-add-file-button-text {
349
+ [data-file-block] .bn-add-file-button-text {
349
350
  font-size: 0.9rem;
350
351
  }
351
352
 
352
- [data-file-block] .bn-file-and-caption-wrapper {
353
- display: flex;
354
- flex-direction: column;
355
- border-radius: 4px;
356
- }
357
-
358
- [data-file-block] .bn-file-default-preview {
359
- align-items: center;
353
+ [data-file-block] .bn-file-name-with-icon {
360
354
  border-radius: 4px;
361
355
  display: flex;
362
- flex-direction: row;
363
356
  gap: 4px;
364
357
  padding: 4px;
365
- width: 100%;
366
- }
367
-
368
- [data-file-block] .bn-file-default-preview:hover,
369
- .ProseMirror-selectednode .bn-file-default-preview {
370
- background-color: rgb(225, 225, 225);
371
- }
372
-
373
- [data-file-block] .bn-file-default-preview-icon {
374
- width: 24px;
375
- height: 24px;
376
358
  }
377
359
 
378
- [data-file-block] .bn-visual-media-wrapper {
379
- display: flex;
380
- flex-direction: row;
381
- align-items: center;
382
- position: relative;
383
- width: fit-content;
360
+ /* File captions */
361
+ [data-file-block] .bn-file-caption {
362
+ font-size: 0.8em;
363
+ padding-block: 4px;
364
+ word-break: break-word;
384
365
  }
385
366
 
386
- [data-file-block] .bn-visual-media {
387
- border-radius: 4px;
388
- max-width: 100%;
367
+ [data-file-block] .bn-file-caption:empty {
368
+ padding-block: 0;
389
369
  }
390
370
 
391
- [data-file-block] .bn-visual-media-resize-handle {
371
+ /* Resize handles */
372
+ [data-file-block] .bn-resize-handle {
392
373
  position: absolute;
393
374
  width: 8px;
394
375
  height: 30px;
@@ -398,19 +379,22 @@ NESTED BLOCKS
398
379
  cursor: ew-resize;
399
380
  }
400
381
 
401
- [data-content-type="audio"] > .bn-file-block-content-wrapper,
402
- .bn-audio {
403
- width: 100%;
382
+ /* Visual media file blocks, e.g. images & videos */
383
+ [data-file-block] .bn-visual-media-wrapper {
384
+ display: flex;
385
+ align-items: center;
386
+ position: relative;
404
387
  }
405
388
 
406
- [data-file-block] .bn-file-caption {
407
- font-size: 0.8em;
408
- padding-block: 4px;
409
- word-break: break-word;
389
+ [data-file-block] .bn-visual-media {
390
+ border-radius: 4px;
391
+ width: 100%;
410
392
  }
411
393
 
412
- [data-file-block] .bn-file-caption:empty {
413
- padding-block: 0;
394
+ /* Block-specific styles */
395
+ [data-content-type="audio"] > .bn-file-block-content-wrapper,
396
+ .bn-audio {
397
+ width: 100%;
414
398
  }
415
399
 
416
400
  /* PLACEHOLDERS*/
@@ -9,11 +9,17 @@ import {
9
9
  import { Node, Schema } from "prosemirror-model";
10
10
  // import "./blocknote.css";
11
11
  import * as Y from "yjs";
12
+ import {
13
+ getBlock,
14
+ getNextBlock,
15
+ getParentBlock,
16
+ getPrevBlock,
17
+ } from "../api/blockManipulation/getBlock/getBlock.js";
12
18
  import { insertBlocks } from "../api/blockManipulation/commands/insertBlocks/insertBlocks.js";
13
19
  import {
14
- moveBlockDown,
15
- moveBlockUp,
16
- } from "../api/blockManipulation/commands/moveBlock/moveBlock.js";
20
+ moveBlocksDown,
21
+ moveBlocksUp,
22
+ } from "../api/blockManipulation/commands/moveBlocks/moveBlocks.js";
17
23
  import {
18
24
  canNestBlock,
19
25
  canUnnestBlock,
@@ -28,6 +34,10 @@ import {
28
34
  getTextCursorPosition,
29
35
  setTextCursorPosition,
30
36
  } from "../api/blockManipulation/selections/textCursorPosition/textCursorPosition.js";
37
+ import {
38
+ getSelection,
39
+ setSelection,
40
+ } from "../api/blockManipulation/selections/selection.js";
31
41
  import { createExternalHTMLExporter } from "../api/exporters/html/externalHTMLExporter.js";
32
42
  import { blocksToMarkdown } from "../api/exporters/markdown/markdownExporter.js";
33
43
  import { HTMLToBlocks } from "../api/parsers/html/parseHTML.js";
@@ -83,6 +93,7 @@ import { createInternalHTMLSerializer } from "../api/exporters/html/internalHTML
83
93
  import { inlineContentToNodes } from "../api/nodeConversions/blockToNode.js";
84
94
  import { nodeToBlock } from "../api/nodeConversions/nodeToBlock.js";
85
95
  import "../style.css";
96
+ import { EditorView } from "prosemirror-view";
86
97
 
87
98
  export type BlockNoteExtension =
88
99
  | AnyExtension
@@ -219,6 +230,21 @@ export type BlockNoteEditorOptions<
219
230
  setIdAttribute?: boolean;
220
231
 
221
232
  dropCursor?: (opts: any) => Plugin;
233
+
234
+ /**
235
+ Select desired behavior when pressing `Tab` (or `Shift-Tab`). Specifically,
236
+ what should happen when a user has selected multiple blocks while a toolbar
237
+ is open:
238
+ - `"prefer-navigate-ui"`: Change focus to the toolbar. The user needs to
239
+ first press `Escape` to close the toolbar, and can then indent multiple
240
+ blocks. Better for keyboard accessibility.
241
+ - `"prefer-indent"`: Regardless of whether toolbars are open, indent the
242
+ selection of blocks. In this case, it's not possible to navigate toolbars
243
+ with the keyboard.
244
+
245
+ @default "prefer-navigate-ui"
246
+ */
247
+ tabBehavior: "prefer-navigate-ui" | "prefer-indent";
222
248
  };
223
249
 
224
250
  const blockNoteTipTapOptions = {
@@ -249,7 +275,8 @@ export class BlockNoteEditor<
249
275
  public readonly headless: boolean = false;
250
276
 
251
277
  public readonly _tiptapEditor:
252
- | BlockNoteTipTapEditor & {
278
+ | Omit<BlockNoteTipTapEditor, "view"> & {
279
+ view: EditorView | undefined;
253
280
  contentComponent: any;
254
281
  } = undefined as any; // TODO: Type should actually reflect that it can be `undefined` in headless mode
255
282
 
@@ -319,7 +346,7 @@ export class BlockNoteEditor<
319
346
  private onUploadStartCallbacks: ((blockId?: string) => void)[] = [];
320
347
  private onUploadEndCallbacks: ((blockId?: string) => void)[] = [];
321
348
 
322
- public readonly resolveFileUrl: (url: string) => Promise<string>;
349
+ public readonly resolveFileUrl?: (url: string) => Promise<string>;
323
350
 
324
351
  public get pmSchema() {
325
352
  return this._pmSchema;
@@ -395,6 +422,7 @@ export class BlockNoteEditor<
395
422
  tableHandles: checkDefaultBlockTypeInSchema("table", this),
396
423
  dropCursor: this.options.dropCursor ?? dropCursor,
397
424
  placeholders: newOptions.placeholders,
425
+ tabBehavior: newOptions.tabBehavior,
398
426
  });
399
427
 
400
428
  // add extensions from _tiptapOptions
@@ -430,7 +458,7 @@ export class BlockNoteEditor<
430
458
  };
431
459
  }
432
460
 
433
- this.resolveFileUrl = newOptions.resolveFileUrl || (async (url) => url);
461
+ this.resolveFileUrl = newOptions.resolveFileUrl;
434
462
  this.headless = newOptions._headless;
435
463
 
436
464
  if (newOptions.collaboration && newOptions.initialContent) {
@@ -516,6 +544,7 @@ export class BlockNoteEditor<
516
544
  tiptapOptions,
517
545
  this.schema.styleSchema
518
546
  ) as BlockNoteTipTapEditor & {
547
+ view: any;
519
548
  contentComponent: any;
520
549
  };
521
550
  this._pmSchema = this._tiptapEditor.schema;
@@ -544,15 +573,15 @@ export class BlockNoteEditor<
544
573
  }
545
574
 
546
575
  public get domElement() {
547
- return this._tiptapEditor.view.dom as HTMLDivElement;
576
+ return this.prosemirrorView?.dom as HTMLDivElement | undefined;
548
577
  }
549
578
 
550
579
  public isFocused() {
551
- return this._tiptapEditor.view.hasFocus();
580
+ return this.prosemirrorView?.hasFocus() || false;
552
581
  }
553
582
 
554
583
  public focus() {
555
- this._tiptapEditor.view.focus();
584
+ this.prosemirrorView?.focus();
556
585
  }
557
586
 
558
587
  public onUploadStart(callback: (blockId?: string) => void) {
@@ -610,39 +639,57 @@ export class BlockNoteEditor<
610
639
 
611
640
  /**
612
641
  * Gets a snapshot of an existing block from the editor.
613
- * @param blockIdentifier The identifier of an existing block that should be retrieved.
614
- * @returns The block that matches the identifier, or `undefined` if no matching block was found.
642
+ * @param blockIdentifier The identifier of an existing block that should be
643
+ * retrieved.
644
+ * @returns The block that matches the identifier, or `undefined` if no
645
+ * matching block was found.
615
646
  */
616
647
  public getBlock(
617
648
  blockIdentifier: BlockIdentifier
618
649
  ): Block<BSchema, ISchema, SSchema> | undefined {
619
- const id =
620
- typeof blockIdentifier === "string"
621
- ? blockIdentifier
622
- : blockIdentifier.id;
623
- let newBlock: Block<BSchema, ISchema, SSchema> | undefined = undefined;
624
-
625
- this._tiptapEditor.state.doc.firstChild!.descendants((node) => {
626
- if (typeof newBlock !== "undefined") {
627
- return false;
628
- }
629
-
630
- if (node.type.name !== "blockContainer" || node.attrs.id !== id) {
631
- return true;
632
- }
650
+ return getBlock(this, blockIdentifier);
651
+ }
633
652
 
634
- newBlock = nodeToBlock(
635
- node,
636
- this.schema.blockSchema,
637
- this.schema.inlineContentSchema,
638
- this.schema.styleSchema,
639
- this.blockCache
640
- );
653
+ /**
654
+ * Gets a snapshot of the previous sibling of an existing block from the
655
+ * editor.
656
+ * @param blockIdentifier The identifier of an existing block for which the
657
+ * previous sibling should be retrieved.
658
+ * @returns The previous sibling of the block that matches the identifier.
659
+ * `undefined` if no matching block was found, or it's the first child/block
660
+ * in the document.
661
+ */
662
+ public getPrevBlock(
663
+ blockIdentifier: BlockIdentifier
664
+ ): Block<BSchema, ISchema, SSchema> | undefined {
665
+ return getPrevBlock(this, blockIdentifier);
666
+ }
641
667
 
642
- return false;
643
- });
668
+ /**
669
+ * Gets a snapshot of the next sibling of an existing block from the editor.
670
+ * @param blockIdentifier The identifier of an existing block for which the
671
+ * next sibling should be retrieved.
672
+ * @returns The next sibling of the block that matches the identifier.
673
+ * `undefined` if no matching block was found, or it's the last child/block in
674
+ * the document.
675
+ */
676
+ public getNextBlock(
677
+ blockIdentifier: BlockIdentifier
678
+ ): Block<BSchema, ISchema, SSchema> | undefined {
679
+ return getNextBlock(this, blockIdentifier);
680
+ }
644
681
 
645
- return newBlock;
682
+ /**
683
+ * Gets a snapshot of the parent of an existing block from the editor.
684
+ * @param blockIdentifier The identifier of an existing block for which the
685
+ * parent should be retrieved.
686
+ * @returns The parent of the block that matches the identifier. `undefined`
687
+ * if no matching block was found, or the block isn't nested.
688
+ */
689
+ public getParentBlock(
690
+ blockIdentifier: BlockIdentifier
691
+ ): Block<BSchema, ISchema, SSchema> | undefined {
692
+ return getParentBlock(this, blockIdentifier);
646
693
  }
647
694
 
648
695
  /**
@@ -728,53 +775,11 @@ export class BlockNoteEditor<
728
775
  * Gets a snapshot of the current selection.
729
776
  */
730
777
  public getSelection(): Selection<BSchema, ISchema, SSchema> | undefined {
731
- // Either the TipTap selection is empty, or it's a node selection. In either
732
- // case, it only spans one block, so we return undefined.
733
- if (
734
- this._tiptapEditor.state.selection.from ===
735
- this._tiptapEditor.state.selection.to ||
736
- "node" in this._tiptapEditor.state.selection
737
- ) {
738
- return undefined;
739
- }
740
-
741
- const blocks: Block<BSchema, ISchema, SSchema>[] = [];
742
-
743
- // TODO: This adds all child blocks to the same array. Needs to find min
744
- // depth and only add blocks at that depth.
745
- this._tiptapEditor.state.doc.descendants((node, pos) => {
746
- if (node.type.spec.group !== "blockContent") {
747
- return true;
748
- }
749
-
750
- // Fixed the block pos and size
751
- // all block is wrapped with a blockContent wrapper
752
- // and blockContent wrapper pos = inner block pos - 1
753
- // blockContent wrapper end = inner block pos + nodeSize + 1
754
- // need to add 1 to start and -1 to end
755
- const end = pos + node.nodeSize - 1;
756
- const start = pos + 1;
757
- if (
758
- end <= this._tiptapEditor.state.selection.from ||
759
- start >= this._tiptapEditor.state.selection.to
760
- ) {
761
- return true;
762
- }
763
-
764
- blocks.push(
765
- nodeToBlock(
766
- this._tiptapEditor.state.doc.resolve(pos).node(),
767
- this.schema.blockSchema,
768
- this.schema.inlineContentSchema,
769
- this.schema.styleSchema,
770
- this.blockCache
771
- )
772
- );
773
-
774
- return false;
775
- });
778
+ return getSelection(this);
779
+ }
776
780
 
777
- return { blocks: blocks };
781
+ public setSelection(startBlock: BlockIdentifier, endBlock: BlockIdentifier) {
782
+ setSelection(this, startBlock, endBlock);
778
783
  }
779
784
 
780
785
  /**
@@ -1032,21 +1037,21 @@ export class BlockNoteEditor<
1032
1037
  }
1033
1038
 
1034
1039
  /**
1035
- * Moves the block containing the text cursor up. If the previous block has
1036
- * children, moves it to the end of its children. If there is no previous
1037
- * block, but the current block is nested, moves it out of & before its parent.
1040
+ * Moves the selected blocks up. If the previous block has children, moves
1041
+ * them to the end of its children. If there is no previous block, but the
1042
+ * current blocks share a common parent, moves them out of & before it.
1038
1043
  */
1039
- public moveBlockUp() {
1040
- moveBlockUp(this);
1044
+ public moveBlocksUp() {
1045
+ moveBlocksUp(this);
1041
1046
  }
1042
1047
 
1043
1048
  /**
1044
- * Moves the block containing the text cursor down. If the next block has
1045
- * children, moves it to the start of its children. If there is no next block,
1046
- * but the current block is nested, moves it out of & after its parent.
1049
+ * Moves the selected blocks down. If the next block has children, moves
1050
+ * them to the start of its children. If there is no next block, but the
1051
+ * current blocks share a common parent, moves them out of & after it.
1047
1052
  */
1048
- public moveBlockDown() {
1049
- moveBlockDown(this);
1053
+ public moveBlocksDown() {
1054
+ moveBlocksDown(this);
1050
1055
  }
1051
1056
 
1052
1057
  /**
@@ -1196,7 +1201,11 @@ export class BlockNoteEditor<
1196
1201
  ignoreQueryLength?: boolean;
1197
1202
  }
1198
1203
  ) {
1199
- const tr = this.prosemirrorView.state.tr;
1204
+ const tr = this.prosemirrorView?.state.tr;
1205
+ if (!tr) {
1206
+ return;
1207
+ }
1208
+
1200
1209
  const transaction =
1201
1210
  pluginState && pluginState.deleteTriggerCharacter
1202
1211
  ? tr.insertText(triggerCharacter)
@@ -19,6 +19,10 @@ import { FilePanelProsemirrorPlugin } from "../extensions/FilePanel/FilePanelPlu
19
19
  import { FormattingToolbarProsemirrorPlugin } from "../extensions/FormattingToolbar/FormattingToolbarPlugin.js";
20
20
  import { KeyboardShortcutsExtension } from "../extensions/KeyboardShortcuts/KeyboardShortcutsExtension.js";
21
21
  import { LinkToolbarProsemirrorPlugin } from "../extensions/LinkToolbar/LinkToolbarPlugin.js";
22
+ import {
23
+ DEFAULT_LINK_PROTOCOL,
24
+ VALID_LINK_PROTOCOLS,
25
+ } from "../extensions/LinkToolbar/protocols.js";
22
26
  import { NodeSelectionKeyboardPlugin } from "../extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.js";
23
27
  import { PlaceholderPlugin } from "../extensions/Placeholder/PlaceholderPlugin.js";
24
28
  import { PreviousBlockTypePlugin } from "../extensions/PreviousBlockType/PreviousBlockTypePlugin.js";
@@ -67,6 +71,7 @@ type ExtensionOptions<
67
71
  tableHandles: boolean;
68
72
  dropCursor: (opts: any) => Plugin;
69
73
  placeholders: Record<string | "default", string>;
74
+ tabBehavior?: "prefer-navigate-ui" | "prefer-indent";
70
75
  };
71
76
 
72
77
  /**
@@ -158,14 +163,9 @@ const getTipTapExtensions = <
158
163
  // marks:
159
164
  Link.extend({
160
165
  inclusive: false,
161
- addKeyboardShortcuts() {
162
- return {
163
- "Mod-k": () => {
164
- this.editor.commands.toggleLink({ href: "" });
165
- return true;
166
- },
167
- };
168
- },
166
+ }).configure({
167
+ defaultProtocol: DEFAULT_LINK_PROTOCOL,
168
+ protocols: VALID_LINK_PROTOCOLS,
169
169
  }),
170
170
  ...Object.values(opts.styleSpecs).map((styleSpec) => {
171
171
  return styleSpec.implementation.mark;
@@ -200,6 +200,7 @@ const getTipTapExtensions = <
200
200
  }),
201
201
  KeyboardShortcutsExtension.configure({
202
202
  editor: opts.editor,
203
+ tabBehavior: opts.tabBehavior,
203
204
  }),
204
205
  BlockGroup.configure({
205
206
  domAttributes: opts.domAttributes,
@@ -86,6 +86,7 @@ Tippy popups that are appended to document.body directly
86
86
  pointer-events: none;
87
87
  position: relative;
88
88
  word-break: normal;
89
+ white-space: nowrap !important;
89
90
  }
90
91
 
91
92
  /* Render the username above the caret */
@@ -139,9 +139,11 @@ export class FormattingToolbarView implements PluginView {
139
139
  // Wrapping in a setTimeout gives enough time to wait for the blur event to
140
140
  // occur before updating the toolbar.
141
141
  const { state, composing } = view;
142
- const { doc, selection } = state;
142
+ const { selection } = state;
143
143
  const isSame =
144
- oldState && oldState.doc.eq(doc) && oldState.selection.eq(selection);
144
+ oldState &&
145
+ oldState.selection.from === state.selection.from &&
146
+ oldState.selection.to === state.selection.to;
145
147
 
146
148
  if (composing || isSame) {
147
149
  return;
@@ -16,6 +16,7 @@ import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
16
16
 
17
17
  export const KeyboardShortcutsExtension = Extension.create<{
18
18
  editor: BlockNoteEditor<any, any, any>;
19
+ tabBehavior: "prefer-navigate-ui" | "prefer-indent";
19
20
  }>({
20
21
  priority: 50,
21
22
 
@@ -479,9 +480,10 @@ export const KeyboardShortcutsExtension = Extension.create<{
479
480
  // editor since the browser will try to use tab for keyboard navigation.
480
481
  Tab: () => {
481
482
  if (
482
- this.options.editor.formattingToolbar?.shown ||
483
- this.options.editor.linkToolbar?.shown ||
484
- this.options.editor.filePanel?.shown
483
+ this.options.tabBehavior !== "prefer-indent" &&
484
+ (this.options.editor.formattingToolbar?.shown ||
485
+ this.options.editor.linkToolbar?.shown ||
486
+ this.options.editor.filePanel?.shown)
485
487
  ) {
486
488
  // don't handle tabs if a toolbar is shown, so we can tab into / out of it
487
489
  return false;
@@ -491,9 +493,10 @@ export const KeyboardShortcutsExtension = Extension.create<{
491
493
  },
492
494
  "Shift-Tab": () => {
493
495
  if (
494
- this.options.editor.formattingToolbar?.shown ||
495
- this.options.editor.linkToolbar?.shown ||
496
- this.options.editor.filePanel?.shown
496
+ this.options.tabBehavior !== "prefer-indent" &&
497
+ (this.options.editor.formattingToolbar?.shown ||
498
+ this.options.editor.linkToolbar?.shown ||
499
+ this.options.editor.filePanel?.shown)
497
500
  ) {
498
501
  // don't handle tabs if a toolbar is shown, so we can tab into / out of it
499
502
  return false;
@@ -502,11 +505,11 @@ export const KeyboardShortcutsExtension = Extension.create<{
502
505
  return true;
503
506
  },
504
507
  "Shift-Mod-ArrowUp": () => {
505
- this.options.editor.moveBlockUp();
508
+ this.options.editor.moveBlocksUp();
506
509
  return true;
507
510
  },
508
511
  "Shift-Mod-ArrowDown": () => {
509
- this.options.editor.moveBlockDown();
512
+ this.options.editor.moveBlocksDown();
510
513
  return true;
511
514
  },
512
515
  };
@@ -2,7 +2,7 @@ import { getMarkRange, posToDOMRect, Range } from "@tiptap/core";
2
2
 
3
3
  import { EditorView } from "@tiptap/pm/view";
4
4
  import { Mark } from "prosemirror-model";
5
- import { Plugin, PluginKey, PluginView } from "prosemirror-state";
5
+ import { EditorState, Plugin, PluginKey, PluginView } from "prosemirror-state";
6
6
 
7
7
  import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
8
8
  import { UiElementPosition } from "../../extensions-shared/UiElementPosition.js";
@@ -52,7 +52,7 @@ class LinkToolbarView implements PluginView {
52
52
 
53
53
  this.startMenuUpdateTimer = () => {
54
54
  this.menuUpdateTimer = setTimeout(() => {
55
- this.update();
55
+ this.update(this.pmView);
56
56
  }, 250);
57
57
  };
58
58
 
@@ -190,8 +190,15 @@ class LinkToolbarView implements PluginView {
190
190
  }
191
191
  }
192
192
 
193
- update() {
194
- if (!this.pmView.hasFocus()) {
193
+ update(view: EditorView, oldState?: EditorState) {
194
+ const { state } = view;
195
+
196
+ const isSame =
197
+ oldState &&
198
+ oldState.selection.from === state.selection.from &&
199
+ oldState.selection.to === state.selection.to;
200
+
201
+ if (isSame || !this.pmView.hasFocus()) {
195
202
  return;
196
203
  }
197
204