@blocknote/core 0.16.0 → 0.17.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 (248) hide show
  1. package/dist/blocknote.js +3292 -2755
  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/webpack-stats.json +1 -1
  6. package/package.json +5 -2
  7. package/src/api/blockManipulation/commands/insertBlocks/__snapshots__/insertBlocks.test.ts.snap +3087 -0
  8. package/src/api/blockManipulation/commands/insertBlocks/insertBlocks.test.ts +132 -0
  9. package/src/api/blockManipulation/commands/insertBlocks/insertBlocks.ts +71 -0
  10. package/src/api/blockManipulation/commands/mergeBlocks/__snapshots__/mergeBlocks.test.ts.snap +2276 -0
  11. package/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.test.ts +131 -0
  12. package/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.ts +103 -0
  13. package/src/api/blockManipulation/commands/moveBlock/__snapshots__/moveBlock.test.ts.snap +3767 -0
  14. package/src/api/blockManipulation/commands/moveBlock/moveBlock.test.ts +192 -0
  15. package/src/api/blockManipulation/commands/moveBlock/moveBlock.ts +178 -0
  16. package/src/api/blockManipulation/commands/removeBlocks/__snapshots__/removeBlocks.test.ts.snap +1136 -0
  17. package/src/api/blockManipulation/commands/removeBlocks/removeBlocks.test.ts +34 -0
  18. package/src/api/blockManipulation/commands/removeBlocks/removeBlocks.ts +100 -0
  19. package/src/api/blockManipulation/commands/replaceBlocks/__snapshots__/replaceBlocks.test.ts.snap +4931 -0
  20. package/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.test.ts +222 -0
  21. package/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.ts +70 -0
  22. package/src/api/blockManipulation/commands/splitBlock/__snapshots__/splitBlock.test.ts.snap +2924 -0
  23. package/src/api/blockManipulation/commands/splitBlock/splitBlock.test.ts +136 -0
  24. package/src/api/blockManipulation/commands/splitBlock/splitBlock.ts +48 -0
  25. package/src/api/blockManipulation/commands/updateBlock/__snapshots__/updateBlock.test.ts.snap +8376 -0
  26. package/src/api/blockManipulation/commands/updateBlock/updateBlock.test.ts +300 -0
  27. package/src/api/blockManipulation/commands/updateBlock/updateBlock.ts +199 -0
  28. package/src/api/blockManipulation/insertContentAt.ts +96 -0
  29. package/src/api/blockManipulation/selections/textCursorPosition/__snapshots__/textCursorPosition.test.ts.snap +316 -0
  30. package/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.test.ts +53 -0
  31. package/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.ts +130 -0
  32. package/src/api/blockManipulation/setupTestEnv.ts +179 -0
  33. package/src/api/clipboard/__snapshots__/tableAllCells.html +1 -1
  34. package/src/api/clipboard/clipboard.test.ts +5 -6
  35. package/src/api/clipboard/fromClipboard/fileDropExtension.ts +8 -4
  36. package/src/api/clipboard/fromClipboard/handleFileInsertion.ts +11 -6
  37. package/src/api/clipboard/fromClipboard/pasteExtension.ts +8 -4
  38. package/src/api/clipboard/toClipboard/copyExtension.ts +113 -61
  39. package/src/api/exporters/html/__snapshots__/complex/misc/external.html +1 -1
  40. package/src/api/exporters/html/__snapshots__/lists/basic/external.html +1 -1
  41. package/src/api/exporters/html/__snapshots__/lists/nested/external.html +1 -1
  42. package/src/api/exporters/html/externalHTMLExporter.ts +42 -94
  43. package/src/api/exporters/html/htmlConversion.test.ts +19 -13
  44. package/src/api/exporters/html/internalHTMLSerializer.ts +21 -72
  45. package/src/api/exporters/html/util/serializeBlocksExternalHTML.ts +263 -0
  46. package/src/api/exporters/html/util/serializeBlocksInternalHTML.ts +158 -0
  47. package/src/api/exporters/markdown/markdownExporter.test.ts +10 -10
  48. package/src/api/exporters/markdown/markdownExporter.ts +11 -7
  49. package/src/api/exporters/markdown/util/addSpacesToCheckboxesRehypePlugin.ts +2 -2
  50. package/src/api/getBlockInfoFromPos.ts +172 -90
  51. package/src/api/nodeConversions/blockToNode.ts +257 -0
  52. package/src/api/nodeConversions/fragmentToBlocks.ts +60 -0
  53. package/src/api/nodeConversions/nodeConversions.test.ts +9 -8
  54. package/src/api/nodeConversions/{nodeConversions.ts → nodeToBlock.ts} +20 -262
  55. package/src/api/parsers/html/parseHTML.test.ts +2 -2
  56. package/src/api/parsers/html/parseHTML.ts +8 -4
  57. package/src/api/parsers/html/util/nestedLists.test.ts +2 -2
  58. package/src/api/parsers/markdown/parseMarkdown.test.ts +2 -2
  59. package/src/api/parsers/markdown/parseMarkdown.ts +8 -4
  60. package/src/api/testUtil/cases/customBlocks.ts +11 -11
  61. package/src/api/testUtil/cases/customInlineContent.ts +6 -6
  62. package/src/api/testUtil/cases/customStyles.ts +6 -6
  63. package/src/api/testUtil/cases/defaultSchema.ts +4 -4
  64. package/src/api/testUtil/index.ts +6 -6
  65. package/src/api/testUtil/partialBlockTestUtil.ts +5 -5
  66. package/src/blocks/AudioBlockContent/AudioBlockContent.ts +5 -5
  67. package/src/blocks/FileBlockContent/FileBlockContent.ts +4 -4
  68. package/src/blocks/FileBlockContent/fileBlockHelpers.ts +2 -2
  69. package/src/blocks/HeadingBlockContent/HeadingBlockContent.ts +61 -39
  70. package/src/blocks/ImageBlockContent/ImageBlockContent.ts +5 -5
  71. package/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +30 -18
  72. package/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts +67 -33
  73. package/src/blocks/ListItemBlockContent/ListItemKeyboardShortcuts.ts +23 -19
  74. package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListIndexingPlugin.ts +22 -24
  75. package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +31 -19
  76. package/src/blocks/ParagraphBlockContent/ParagraphBlockContent.ts +16 -11
  77. package/src/blocks/TableBlockContent/TableBlockContent.ts +4 -4
  78. package/src/blocks/VideoBlockContent/VideoBlockContent.ts +5 -5
  79. package/src/blocks/defaultBlockHelpers.ts +4 -4
  80. package/src/blocks/defaultBlockTypeGuards.ts +5 -5
  81. package/src/blocks/defaultBlocks.ts +13 -13
  82. package/src/blocks/defaultProps.ts +1 -1
  83. package/src/editor/BlockNoteEditor.test.ts +14 -7
  84. package/src/editor/BlockNoteEditor.ts +82 -149
  85. package/src/editor/BlockNoteExtensions.ts +15 -11
  86. package/src/editor/BlockNoteSchema.ts +7 -7
  87. package/src/editor/BlockNoteTipTapEditor.ts +5 -3
  88. package/src/editor/cursorPositionTypes.ts +7 -2
  89. package/src/editor/selectionTypes.ts +6 -2
  90. package/src/extensions/BackgroundColor/BackgroundColorExtension.ts +1 -1
  91. package/src/extensions/BackgroundColor/BackgroundColorMark.ts +1 -1
  92. package/src/extensions/FilePanel/FilePanelPlugin.ts +4 -4
  93. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +8 -4
  94. package/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts +334 -0
  95. package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +9 -4
  96. package/src/extensions/{NonEditableBlocks/NonEditableBlockPlugin.ts → NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.ts} +2 -2
  97. package/src/extensions/Placeholder/PlaceholderPlugin.ts +1 -1
  98. package/src/extensions/SideMenu/SideMenuPlugin.ts +72 -401
  99. package/src/extensions/SideMenu/dragging.ts +251 -0
  100. package/src/extensions/SuggestionMenu/DefaultSuggestionItem.ts +1 -1
  101. package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +8 -4
  102. package/src/extensions/SuggestionMenu/getDefaultEmojiPickerItems.ts +8 -4
  103. package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +19 -15
  104. package/src/extensions/TableHandles/TableHandlesPlugin.ts +11 -7
  105. package/src/extensions/TextColor/TextColorExtension.ts +1 -1
  106. package/src/extensions/TextColor/TextColorMark.ts +1 -1
  107. package/src/i18n/dictionary.ts +1 -1
  108. package/src/i18n/locales/ar.ts +1 -1
  109. package/src/i18n/locales/fr.ts +1 -1
  110. package/src/i18n/locales/hr.ts +308 -0
  111. package/src/i18n/locales/index.ts +15 -14
  112. package/src/i18n/locales/is.ts +1 -1
  113. package/src/i18n/locales/ja.ts +1 -1
  114. package/src/i18n/locales/ko.ts +1 -1
  115. package/src/i18n/locales/nl.ts +1 -1
  116. package/src/i18n/locales/pl.ts +1 -1
  117. package/src/i18n/locales/pt.ts +1 -1
  118. package/src/i18n/locales/ru.ts +1 -1
  119. package/src/i18n/locales/vi.ts +1 -1
  120. package/src/i18n/locales/zh.ts +1 -1
  121. package/src/index.ts +45 -44
  122. package/src/pm-nodes/BlockContainer.ts +3 -647
  123. package/src/pm-nodes/BlockGroup.ts +2 -2
  124. package/src/pm-nodes/index.ts +3 -3
  125. package/src/schema/blocks/createSpec.ts +8 -7
  126. package/src/schema/blocks/internal.ts +9 -9
  127. package/src/schema/blocks/types.ts +4 -4
  128. package/src/schema/index.ts +10 -10
  129. package/src/schema/inlineContent/createSpec.ts +9 -10
  130. package/src/schema/inlineContent/internal.ts +3 -3
  131. package/src/schema/inlineContent/types.ts +2 -2
  132. package/src/schema/styles/createSpec.ts +4 -3
  133. package/src/schema/styles/internal.ts +1 -1
  134. package/types/src/api/blockManipulation/commands/insertBlocks/insertBlocks.d.ts +4 -0
  135. package/types/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.d.ts +7 -0
  136. package/types/src/api/blockManipulation/commands/mergeBlocks/mergeBlocks.test.d.ts +1 -0
  137. package/types/src/api/blockManipulation/commands/moveBlock/moveBlock.d.ts +5 -0
  138. package/types/src/api/blockManipulation/commands/moveBlock/moveBlock.test.d.ts +1 -0
  139. package/types/src/api/blockManipulation/commands/removeBlocks/removeBlocks.d.ts +7 -0
  140. package/types/src/api/blockManipulation/commands/removeBlocks/removeBlocks.test.d.ts +1 -0
  141. package/types/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.d.ts +7 -0
  142. package/types/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.test.d.ts +1 -0
  143. package/types/src/api/blockManipulation/commands/splitBlock/splitBlock.d.ts +5 -0
  144. package/types/src/api/blockManipulation/commands/splitBlock/splitBlock.test.d.ts +1 -0
  145. package/types/src/api/blockManipulation/commands/updateBlock/updateBlock.d.ts +11 -0
  146. package/types/src/api/blockManipulation/commands/updateBlock/updateBlock.test.d.ts +1 -0
  147. package/types/src/api/blockManipulation/insertContentAt.d.ts +6 -0
  148. package/types/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.d.ts +5 -0
  149. package/types/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.test.d.ts +1 -0
  150. package/types/src/api/blockManipulation/setupTestEnv.d.ts +492 -0
  151. package/types/src/api/clipboard/fromClipboard/fileDropExtension.d.ts +3 -3
  152. package/types/src/api/clipboard/fromClipboard/handleFileInsertion.d.ts +1 -1
  153. package/types/src/api/clipboard/fromClipboard/pasteExtension.d.ts +2 -2
  154. package/types/src/api/clipboard/toClipboard/copyExtension.d.ts +5 -5
  155. package/types/src/api/exporters/html/externalHTMLExporter.d.ts +7 -9
  156. package/types/src/api/exporters/html/internalHTMLSerializer.d.ts +6 -10
  157. package/types/src/api/exporters/html/util/serializeBlocksExternalHTML.d.ts +10 -0
  158. package/types/src/api/exporters/html/util/serializeBlocksInternalHTML.d.ts +11 -0
  159. package/types/src/api/exporters/markdown/markdownExporter.d.ts +3 -3
  160. package/types/src/api/getBlockInfoFromPos.d.ts +63 -20
  161. package/types/src/api/nodeConversions/blockToNode.d.ts +15 -0
  162. package/types/src/api/nodeConversions/fragmentToBlocks.d.ts +7 -0
  163. package/types/src/api/nodeConversions/nodeToBlock.d.ts +16 -0
  164. package/types/src/api/parsers/html/parseHTML.d.ts +2 -2
  165. package/types/src/api/parsers/markdown/parseMarkdown.d.ts +2 -2
  166. package/types/src/api/testUtil/cases/customBlocks.d.ts +39 -39
  167. package/types/src/api/testUtil/cases/customInlineContent.d.ts +35 -35
  168. package/types/src/api/testUtil/cases/customStyles.d.ts +35 -35
  169. package/types/src/api/testUtil/cases/defaultSchema.d.ts +2 -2
  170. package/types/src/api/testUtil/index.d.ts +6 -6
  171. package/types/src/api/testUtil/partialBlockTestUtil.d.ts +4 -4
  172. package/types/src/blocks/AudioBlockContent/AudioBlockContent.d.ts +4 -4
  173. package/types/src/blocks/FileBlockContent/FileBlockContent.d.ts +4 -4
  174. package/types/src/blocks/FileBlockContent/fileBlockHelpers.d.ts +2 -2
  175. package/types/src/blocks/HeadingBlockContent/HeadingBlockContent.d.ts +2 -2
  176. package/types/src/blocks/ImageBlockContent/ImageBlockContent.d.ts +4 -4
  177. package/types/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.d.ts +2 -2
  178. package/types/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.d.ts +2 -2
  179. package/types/src/blocks/ListItemBlockContent/ListItemKeyboardShortcuts.d.ts +2 -2
  180. package/types/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.d.ts +2 -2
  181. package/types/src/blocks/ParagraphBlockContent/ParagraphBlockContent.d.ts +2 -2
  182. package/types/src/blocks/TableBlockContent/TableBlockContent.d.ts +2 -2
  183. package/types/src/blocks/VideoBlockContent/VideoBlockContent.d.ts +4 -4
  184. package/types/src/blocks/defaultBlockHelpers.d.ts +3 -3
  185. package/types/src/blocks/defaultBlockTypeGuards.d.ts +4 -4
  186. package/types/src/blocks/defaultBlocks.d.ts +38 -38
  187. package/types/src/blocks/defaultProps.d.ts +1 -1
  188. package/types/src/editor/BlockNoteEditor.d.ts +28 -16
  189. package/types/src/editor/BlockNoteExtensions.d.ts +3 -3
  190. package/types/src/editor/BlockNoteSchema.d.ts +4 -4
  191. package/types/src/editor/BlockNoteTipTapEditor.d.ts +2 -2
  192. package/types/src/editor/cursorPositionTypes.d.ts +3 -2
  193. package/types/src/editor/selectionTypes.d.ts +2 -2
  194. package/types/src/extensions/BackgroundColor/BackgroundColorMark.d.ts +1 -1
  195. package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +4 -4
  196. package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +4 -4
  197. package/types/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.d.ts +5 -0
  198. package/types/src/extensions/LinkToolbar/LinkToolbarPlugin.d.ts +4 -4
  199. package/types/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.d.ts +2 -0
  200. package/types/src/extensions/Placeholder/PlaceholderPlugin.d.ts +1 -1
  201. package/types/src/extensions/SideMenu/SideMenuPlugin.d.ts +12 -28
  202. package/types/src/extensions/SideMenu/dragging.d.ts +17 -0
  203. package/types/src/extensions/SuggestionMenu/DefaultSuggestionItem.d.ts +1 -1
  204. package/types/src/extensions/SuggestionMenu/SuggestionPlugin.d.ts +4 -4
  205. package/types/src/extensions/SuggestionMenu/getDefaultEmojiPickerItems.d.ts +3 -3
  206. package/types/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.d.ts +4 -4
  207. package/types/src/extensions/TableHandles/TableHandlesPlugin.d.ts +4 -4
  208. package/types/src/extensions/TextColor/TextColorMark.d.ts +1 -1
  209. package/types/src/i18n/dictionary.d.ts +1 -1
  210. package/types/src/i18n/locales/ar.d.ts +1 -1
  211. package/types/src/i18n/locales/fr.d.ts +1 -1
  212. package/types/src/i18n/locales/hr.d.ts +239 -0
  213. package/types/src/i18n/locales/index.d.ts +15 -14
  214. package/types/src/i18n/locales/is.d.ts +1 -1
  215. package/types/src/i18n/locales/ja.d.ts +1 -1
  216. package/types/src/i18n/locales/ko.d.ts +1 -1
  217. package/types/src/i18n/locales/nl.d.ts +1 -1
  218. package/types/src/i18n/locales/pl.d.ts +1 -1
  219. package/types/src/i18n/locales/pt.d.ts +1 -1
  220. package/types/src/i18n/locales/ru.d.ts +1 -1
  221. package/types/src/i18n/locales/vi.d.ts +1 -1
  222. package/types/src/i18n/locales/zh.d.ts +1 -1
  223. package/types/src/index.d.ts +45 -44
  224. package/types/src/pm-nodes/BlockContainer.d.ts +2 -16
  225. package/types/src/pm-nodes/BlockGroup.d.ts +1 -1
  226. package/types/src/pm-nodes/index.d.ts +3 -3
  227. package/types/src/schema/blocks/createSpec.d.ts +5 -5
  228. package/types/src/schema/blocks/internal.d.ts +5 -5
  229. package/types/src/schema/blocks/types.d.ts +4 -4
  230. package/types/src/schema/index.d.ts +10 -10
  231. package/types/src/schema/inlineContent/createSpec.d.ts +3 -3
  232. package/types/src/schema/inlineContent/internal.d.ts +2 -2
  233. package/types/src/schema/inlineContent/types.d.ts +2 -2
  234. package/types/src/schema/styles/createSpec.d.ts +1 -1
  235. package/types/src/schema/styles/internal.d.ts +1 -1
  236. package/src/api/blockManipulation/__snapshots__/blockManipulation.test.ts.snap +0 -714
  237. package/src/api/blockManipulation/blockManipulation.test.ts +0 -292
  238. package/src/api/blockManipulation/blockManipulation.ts +0 -350
  239. package/src/api/exporters/html/util/sharedHTMLConversion.ts +0 -130
  240. package/src/api/exporters/html/util/simplifyBlocksRehypePlugin.ts +0 -218
  241. package/src/api/getCurrentBlockContentType.ts +0 -14
  242. package/types/src/api/blockManipulation/blockManipulation.d.ts +0 -14
  243. package/types/src/api/exporters/html/util/sharedHTMLConversion.d.ts +0 -9
  244. package/types/src/api/exporters/html/util/simplifyBlocksRehypePlugin.d.ts +0 -16
  245. package/types/src/api/getCurrentBlockContentType.d.ts +0 -2
  246. package/types/src/api/nodeConversions/nodeConversions.d.ts +0 -24
  247. package/types/src/extensions/NonEditableBlocks/NonEditableBlockPlugin.d.ts +0 -2
  248. /package/types/src/api/blockManipulation/{blockManipulation.test.d.ts → commands/insertBlocks/insertBlocks.test.d.ts} +0 -0
@@ -1,13 +1,15 @@
1
1
  import { EditorOptions, createDocument } from "@tiptap/core";
2
2
  // import "./blocknote.css";
3
3
  import { Editor as TiptapEditor } from "@tiptap/core";
4
+
4
5
  import { Node } from "@tiptap/pm/model";
6
+
5
7
  import { EditorView } from "@tiptap/pm/view";
6
8
 
7
9
  import { EditorState, Transaction } from "@tiptap/pm/state";
8
- import { blockToNode } from "../api/nodeConversions/nodeConversions";
9
- import { PartialBlock } from "../blocks/defaultBlocks";
10
- import { StyleSchema } from "../schema";
10
+ import { blockToNode } from "../api/nodeConversions/blockToNode.js";
11
+ import { PartialBlock } from "../blocks/defaultBlocks.js";
12
+ import { StyleSchema } from "../schema/index.js";
11
13
 
12
14
  export type BlockNoteTipTapEditorOptions = Partial<
13
15
  Omit<EditorOptions, "content">
@@ -1,5 +1,9 @@
1
- import { Block } from "../blocks/defaultBlocks";
2
- import { BlockSchema, InlineContentSchema, StyleSchema } from "../schema";
1
+ import { Block } from "../blocks/defaultBlocks.js";
2
+ import {
3
+ BlockSchema,
4
+ InlineContentSchema,
5
+ StyleSchema,
6
+ } from "../schema/index.js";
3
7
 
4
8
  export type TextCursorPosition<
5
9
  BSchema extends BlockSchema,
@@ -9,4 +13,5 @@ export type TextCursorPosition<
9
13
  block: Block<BSchema, I, S>;
10
14
  prevBlock: Block<BSchema, I, S> | undefined;
11
15
  nextBlock: Block<BSchema, I, S> | undefined;
16
+ parentBlock: Block<BSchema, I, S> | undefined;
12
17
  };
@@ -1,5 +1,9 @@
1
- import { Block } from "../blocks/defaultBlocks";
2
- import { BlockSchema, InlineContentSchema, StyleSchema } from "../schema";
1
+ import { Block } from "../blocks/defaultBlocks.js";
2
+ import {
3
+ BlockSchema,
4
+ InlineContentSchema,
5
+ StyleSchema,
6
+ } from "../schema/index.js";
3
7
 
4
8
  export type Selection<
5
9
  BSchema extends BlockSchema,
@@ -1,5 +1,5 @@
1
1
  import { Extension } from "@tiptap/core";
2
- import { defaultProps } from "../../blocks/defaultProps";
2
+ import { defaultProps } from "../../blocks/defaultProps.js";
3
3
 
4
4
  export const BackgroundColorExtension = Extension.create({
5
5
  name: "blockBackgroundColor",
@@ -1,5 +1,5 @@
1
1
  import { Mark } from "@tiptap/core";
2
- import { createStyleSpecFromTipTapMark } from "../../schema";
2
+ import { createStyleSpecFromTipTapMark } from "../../schema/index.js";
3
3
 
4
4
  const BackgroundColorMark = Mark.create({
5
5
  name: "backgroundColor",
@@ -1,15 +1,15 @@
1
1
  import { EditorState, Plugin, PluginKey, PluginView } from "prosemirror-state";
2
2
  import { EditorView } from "prosemirror-view";
3
3
 
4
- import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
5
- import { UiElementPosition } from "../../extensions-shared/UiElementPosition";
4
+ import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
5
+ import { UiElementPosition } from "../../extensions-shared/UiElementPosition.js";
6
6
  import type {
7
7
  BlockFromConfig,
8
8
  FileBlockConfig,
9
9
  InlineContentSchema,
10
10
  StyleSchema,
11
- } from "../../schema";
12
- import { EventEmitter } from "../../util/EventEmitter";
11
+ } from "../../schema/index.js";
12
+ import { EventEmitter } from "../../util/EventEmitter.js";
13
13
 
14
14
  export type FilePanelState<
15
15
  I extends InlineContentSchema,
@@ -2,10 +2,14 @@ import { isNodeSelection, isTextSelection, posToDOMRect } from "@tiptap/core";
2
2
  import { EditorState, Plugin, PluginKey, PluginView } from "prosemirror-state";
3
3
  import { EditorView } from "prosemirror-view";
4
4
 
5
- import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
6
- import { UiElementPosition } from "../../extensions-shared/UiElementPosition";
7
- import { BlockSchema, InlineContentSchema, StyleSchema } from "../../schema";
8
- import { EventEmitter } from "../../util/EventEmitter";
5
+ import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
6
+ import { UiElementPosition } from "../../extensions-shared/UiElementPosition.js";
7
+ import {
8
+ BlockSchema,
9
+ InlineContentSchema,
10
+ StyleSchema,
11
+ } from "../../schema/index.js";
12
+ import { EventEmitter } from "../../util/EventEmitter.js";
9
13
 
10
14
  export type FormattingToolbarState = UiElementPosition;
11
15
 
@@ -0,0 +1,334 @@
1
+ import { Extension } from "@tiptap/core";
2
+
3
+ import { TextSelection } from "prosemirror-state";
4
+ import {
5
+ getPrevBlockPos,
6
+ mergeBlocksCommand,
7
+ } from "../../api/blockManipulation/commands/mergeBlocks/mergeBlocks.js";
8
+ import { splitBlockCommand } from "../../api/blockManipulation/commands/splitBlock/splitBlock.js";
9
+ import { updateBlockCommand } from "../../api/blockManipulation/commands/updateBlock/updateBlock.js";
10
+ import {
11
+ getBlockInfoFromResolvedPos,
12
+ getBlockInfoFromSelection,
13
+ } from "../../api/getBlockInfoFromPos.js";
14
+ import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
15
+
16
+ export const KeyboardShortcutsExtension = Extension.create<{
17
+ editor: BlockNoteEditor<any, any, any>;
18
+ }>({
19
+ priority: 50,
20
+
21
+ // TODO: The shortcuts need a refactor. Do we want to use a command priority
22
+ // design as there is now, or clump the logic into a single function?
23
+ addKeyboardShortcuts() {
24
+ // handleBackspace is partially adapted from https://github.com/ueberdosis/tiptap/blob/ed56337470efb4fd277128ab7ef792b37cfae992/packages/core/src/extensions/keymap.ts
25
+ const handleBackspace = () =>
26
+ this.editor.commands.first(({ chain, commands }) => [
27
+ // Deletes the selection if it's not empty.
28
+ () => commands.deleteSelection(),
29
+ // Undoes an input rule if one was triggered in the last editor state change.
30
+ () => commands.undoInputRule(),
31
+ // Reverts block content type to a paragraph if the selection is at the start of the block.
32
+ () =>
33
+ commands.command(({ state }) => {
34
+ const blockInfo = getBlockInfoFromSelection(state);
35
+
36
+ const selectionAtBlockStart =
37
+ state.selection.from === blockInfo.blockContent.beforePos + 1;
38
+ const isParagraph =
39
+ blockInfo.blockContent.node.type.name === "paragraph";
40
+
41
+ if (selectionAtBlockStart && !isParagraph) {
42
+ return commands.command(
43
+ updateBlockCommand(
44
+ this.options.editor,
45
+ blockInfo.blockContainer.beforePos,
46
+ {
47
+ type: "paragraph",
48
+ props: {},
49
+ }
50
+ )
51
+ );
52
+ }
53
+
54
+ return false;
55
+ }),
56
+ // Removes a level of nesting if the block is indented if the selection is at the start of the block.
57
+ () =>
58
+ commands.command(({ state }) => {
59
+ const { blockContent } = getBlockInfoFromSelection(state);
60
+
61
+ const selectionAtBlockStart =
62
+ state.selection.from === blockContent.beforePos + 1;
63
+
64
+ if (selectionAtBlockStart) {
65
+ return commands.liftListItem("blockContainer");
66
+ }
67
+
68
+ return false;
69
+ }),
70
+ // Merges block with the previous one if it isn't indented, isn't the
71
+ // first block in the doc, and the selection is at the start of the
72
+ // block. The target block for merging must contain inline content.
73
+ () =>
74
+ commands.command(({ state }) => {
75
+ const { blockContainer, blockContent } =
76
+ getBlockInfoFromSelection(state);
77
+
78
+ const { depth } = state.doc.resolve(blockContainer.beforePos);
79
+
80
+ const selectionAtBlockStart =
81
+ state.selection.from === blockContent.beforePos + 1;
82
+ const selectionEmpty = state.selection.empty;
83
+ const blockAtDocStart = blockContainer.beforePos === 1;
84
+
85
+ const posBetweenBlocks = blockContainer.beforePos;
86
+
87
+ if (
88
+ !blockAtDocStart &&
89
+ selectionAtBlockStart &&
90
+ selectionEmpty &&
91
+ depth === 1
92
+ ) {
93
+ return chain()
94
+ .command(mergeBlocksCommand(posBetweenBlocks))
95
+ .scrollIntoView()
96
+ .run();
97
+ }
98
+
99
+ return false;
100
+ }),
101
+ // Deletes previous block if it contains no content and isn't a table,
102
+ // when the selection is empty and at the start of the block. Moves the
103
+ // current block into the deleted block's place.
104
+ () =>
105
+ commands.command(({ state }) => {
106
+ const blockInfo = getBlockInfoFromSelection(state);
107
+
108
+ const { depth } = state.doc.resolve(
109
+ blockInfo.blockContainer.beforePos
110
+ );
111
+
112
+ const selectionAtBlockStart =
113
+ state.selection.from === blockInfo.blockContent.beforePos + 1;
114
+ const selectionEmpty = state.selection.empty;
115
+ const blockAtDocStart = blockInfo.blockContainer.beforePos === 1;
116
+
117
+ if (
118
+ !blockAtDocStart &&
119
+ selectionAtBlockStart &&
120
+ selectionEmpty &&
121
+ depth === 1
122
+ ) {
123
+ const prevBlockPos = getPrevBlockPos(
124
+ state.doc,
125
+ state.doc.resolve(blockInfo.blockContainer.beforePos)
126
+ );
127
+ const prevBlockInfo = getBlockInfoFromResolvedPos(
128
+ state.doc.resolve(prevBlockPos.pos)
129
+ );
130
+
131
+ const prevBlockNotTableAndNoContent =
132
+ prevBlockInfo.blockContent.node.type.spec.content === "" ||
133
+ (prevBlockInfo.blockContent.node.type.spec.content ===
134
+ "inline*" &&
135
+ prevBlockInfo.blockContent.node.childCount === 0);
136
+
137
+ if (prevBlockNotTableAndNoContent) {
138
+ return chain()
139
+ .cut(
140
+ {
141
+ from: blockInfo.blockContainer.beforePos,
142
+ to: blockInfo.blockContainer.afterPos,
143
+ },
144
+ prevBlockInfo.blockContainer.afterPos
145
+ )
146
+ .deleteRange({
147
+ from: prevBlockInfo.blockContainer.beforePos,
148
+ to: prevBlockInfo.blockContainer.afterPos,
149
+ })
150
+ .run();
151
+ }
152
+ }
153
+
154
+ return false;
155
+ }),
156
+ ]);
157
+
158
+ const handleDelete = () =>
159
+ this.editor.commands.first(({ commands }) => [
160
+ // Deletes the selection if it's not empty.
161
+ () => commands.deleteSelection(),
162
+ // Merges block with the next one (at the same nesting level or lower),
163
+ // if one exists, the block has no children, and the selection is at the
164
+ // end of the block.
165
+ () =>
166
+ commands.command(({ state }) => {
167
+ // TODO: Change this to not rely on offsets & schema assumptions
168
+ const { blockContainer, blockContent, blockGroup } =
169
+ getBlockInfoFromSelection(state);
170
+
171
+ const { depth } = state.doc.resolve(blockContainer.beforePos);
172
+ const blockAtDocEnd =
173
+ blockContainer.afterPos === state.doc.nodeSize - 3;
174
+ const selectionAtBlockEnd =
175
+ state.selection.from === blockContent.afterPos - 1;
176
+ const selectionEmpty = state.selection.empty;
177
+ const hasChildBlocks = blockGroup !== undefined;
178
+
179
+ if (
180
+ !blockAtDocEnd &&
181
+ selectionAtBlockEnd &&
182
+ selectionEmpty &&
183
+ !hasChildBlocks
184
+ ) {
185
+ let oldDepth = depth;
186
+ let newPos = blockContainer.afterPos + 1;
187
+ let newDepth = state.doc.resolve(newPos).depth;
188
+
189
+ while (newDepth < oldDepth) {
190
+ oldDepth = newDepth;
191
+ newPos += 2;
192
+ newDepth = state.doc.resolve(newPos).depth;
193
+ }
194
+
195
+ return commands.command(mergeBlocksCommand(newPos - 1));
196
+ }
197
+
198
+ return false;
199
+ }),
200
+ ]);
201
+
202
+ const handleEnter = () =>
203
+ this.editor.commands.first(({ commands }) => [
204
+ // Removes a level of nesting if the block is empty & indented, while the selection is also empty & at the start
205
+ // of the block.
206
+ () =>
207
+ commands.command(({ state }) => {
208
+ const { blockContent, blockContainer } =
209
+ getBlockInfoFromSelection(state);
210
+
211
+ const { depth } = state.doc.resolve(blockContainer.beforePos);
212
+
213
+ const selectionAtBlockStart =
214
+ state.selection.$anchor.parentOffset === 0;
215
+ const selectionEmpty =
216
+ state.selection.anchor === state.selection.head;
217
+ const blockEmpty = blockContent.node.childCount === 0;
218
+ const blockIndented = depth > 1;
219
+
220
+ if (
221
+ selectionAtBlockStart &&
222
+ selectionEmpty &&
223
+ blockEmpty &&
224
+ blockIndented
225
+ ) {
226
+ return commands.liftListItem("blockContainer");
227
+ }
228
+
229
+ return false;
230
+ }),
231
+ // Creates a new block and moves the selection to it if the current one is empty, while the selection is also
232
+ // empty & at the start of the block.
233
+ () =>
234
+ commands.command(({ state, dispatch }) => {
235
+ const { blockContainer, blockContent } =
236
+ getBlockInfoFromSelection(state);
237
+
238
+ const selectionAtBlockStart =
239
+ state.selection.$anchor.parentOffset === 0;
240
+ const selectionEmpty =
241
+ state.selection.anchor === state.selection.head;
242
+ const blockEmpty = blockContent.node.childCount === 0;
243
+
244
+ if (selectionAtBlockStart && selectionEmpty && blockEmpty) {
245
+ const newBlockInsertionPos = blockContainer.afterPos;
246
+ const newBlockContentPos = newBlockInsertionPos + 2;
247
+
248
+ if (dispatch) {
249
+ const newBlock =
250
+ state.schema.nodes["blockContainer"].createAndFill()!;
251
+
252
+ state.tr
253
+ .insert(newBlockInsertionPos, newBlock)
254
+ .scrollIntoView();
255
+ state.tr.setSelection(
256
+ new TextSelection(state.doc.resolve(newBlockContentPos))
257
+ );
258
+ }
259
+
260
+ return true;
261
+ }
262
+
263
+ return false;
264
+ }),
265
+ // Splits the current block, moving content inside that's after the cursor to a new text block below. Also
266
+ // deletes the selection beforehand, if it's not empty.
267
+ () =>
268
+ commands.command(({ state, chain }) => {
269
+ const { blockContent } = getBlockInfoFromSelection(state);
270
+
271
+ const selectionAtBlockStart =
272
+ state.selection.$anchor.parentOffset === 0;
273
+ const blockEmpty = blockContent.node.childCount === 0;
274
+
275
+ if (!blockEmpty) {
276
+ chain()
277
+ .deleteSelection()
278
+ .command(
279
+ splitBlockCommand(
280
+ state.selection.from,
281
+ selectionAtBlockStart,
282
+ selectionAtBlockStart
283
+ )
284
+ )
285
+ .run();
286
+
287
+ return true;
288
+ }
289
+
290
+ return false;
291
+ }),
292
+ ]);
293
+
294
+ return {
295
+ Backspace: handleBackspace,
296
+ Delete: handleDelete,
297
+ Enter: handleEnter,
298
+ // Always returning true for tab key presses ensures they're not captured by the browser. Otherwise, they blur the
299
+ // editor since the browser will try to use tab for keyboard navigation.
300
+ Tab: () => {
301
+ if (
302
+ this.options.editor.formattingToolbar?.shown ||
303
+ this.options.editor.linkToolbar?.shown ||
304
+ this.options.editor.filePanel?.shown
305
+ ) {
306
+ // don't handle tabs if a toolbar is shown, so we can tab into / out of it
307
+ return false;
308
+ }
309
+ this.editor.commands.sinkListItem("blockContainer");
310
+ return true;
311
+ },
312
+ "Shift-Tab": () => {
313
+ if (
314
+ this.options.editor.formattingToolbar?.shown ||
315
+ this.options.editor.linkToolbar?.shown ||
316
+ this.options.editor.filePanel?.shown
317
+ ) {
318
+ // don't handle tabs if a toolbar is shown, so we can tab into / out of it
319
+ return false;
320
+ }
321
+ this.editor.commands.liftListItem("blockContainer");
322
+ return true;
323
+ },
324
+ "Shift-Mod-ArrowUp": () => {
325
+ this.options.editor.moveBlockUp();
326
+ return true;
327
+ },
328
+ "Shift-Mod-ArrowDown": () => {
329
+ this.options.editor.moveBlockDown();
330
+ return true;
331
+ },
332
+ };
333
+ },
334
+ });
@@ -1,12 +1,17 @@
1
1
  import { getMarkRange, posToDOMRect, Range } from "@tiptap/core";
2
+
2
3
  import { EditorView } from "@tiptap/pm/view";
3
4
  import { Mark } from "prosemirror-model";
4
5
  import { Plugin, PluginKey, PluginView } from "prosemirror-state";
5
6
 
6
- import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
7
- import { UiElementPosition } from "../../extensions-shared/UiElementPosition";
8
- import { BlockSchema, InlineContentSchema, StyleSchema } from "../../schema";
9
- import { EventEmitter } from "../../util/EventEmitter";
7
+ import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
8
+ import { UiElementPosition } from "../../extensions-shared/UiElementPosition.js";
9
+ import {
10
+ BlockSchema,
11
+ InlineContentSchema,
12
+ StyleSchema,
13
+ } from "../../schema/index.js";
14
+ import { EventEmitter } from "../../util/EventEmitter.js";
10
15
 
11
16
  export type LinkToolbarState = UiElementPosition & {
12
17
  // The hovered link's URL, and the text it's displayed with in the
@@ -1,6 +1,6 @@
1
1
  import { Plugin, PluginKey, TextSelection } from "prosemirror-state";
2
2
 
3
- const PLUGIN_KEY = new PluginKey("non-editable-block");
3
+ const PLUGIN_KEY = new PluginKey("node-selection-keyboard");
4
4
  // By default, typing with a node selection active will cause ProseMirror to
5
5
  // replace the node with one that contains editable content. This plugin blocks
6
6
  // this behaviour without also blocking things like keyboard shortcuts:
@@ -15,7 +15,7 @@ const PLUGIN_KEY = new PluginKey("non-editable-block");
15
15
  // While a more elegant solution would probably process transactions instead of
16
16
  // keystrokes, this brings us most of the way to Notion's UX without much added
17
17
  // complexity.
18
- export const NonEditableBlockPlugin = () => {
18
+ export const NodeSelectionKeyboardPlugin = () => {
19
19
  return new Plugin({
20
20
  key: PLUGIN_KEY,
21
21
  props: {
@@ -1,6 +1,6 @@
1
1
  import { Plugin, PluginKey } from "prosemirror-state";
2
2
  import { Decoration, DecorationSet } from "prosemirror-view";
3
- import type { BlockNoteEditor } from "../../editor/BlockNoteEditor";
3
+ import type { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
4
4
 
5
5
  const PLUGIN_KEY = new PluginKey(`blocknote-placeholder`);
6
6