@blocknote/core 0.42.3 → 0.44.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 (200) hide show
  1. package/dist/BlockNoteExtension-BWw0r8Gy.cjs +2 -0
  2. package/dist/BlockNoteExtension-BWw0r8Gy.cjs.map +1 -0
  3. package/dist/BlockNoteExtension-C2X7LW-V.js +25 -0
  4. package/dist/BlockNoteExtension-C2X7LW-V.js.map +1 -0
  5. package/dist/BlockNoteSchema-B4gm-Qco.cjs +2 -0
  6. package/dist/BlockNoteSchema-B4gm-Qco.cjs.map +1 -0
  7. package/dist/BlockNoteSchema-C-l154WP.js +270 -0
  8. package/dist/BlockNoteSchema-C-l154WP.js.map +1 -0
  9. package/dist/EventEmitter-CLwfmbqG.cjs +2 -0
  10. package/dist/EventEmitter-CLwfmbqG.cjs.map +1 -0
  11. package/dist/EventEmitter-CjSwpTbz.js +27 -0
  12. package/dist/EventEmitter-CjSwpTbz.js.map +1 -0
  13. package/dist/ShowSelection-BW37oJ6h.cjs +2 -0
  14. package/dist/ShowSelection-BW37oJ6h.cjs.map +1 -0
  15. package/dist/ShowSelection-Dz-NEase.js +43 -0
  16. package/dist/ShowSelection-Dz-NEase.js.map +1 -0
  17. package/dist/TrailingNode-B_zPMWxw.js +2098 -0
  18. package/dist/TrailingNode-B_zPMWxw.js.map +1 -0
  19. package/dist/TrailingNode-CRHrgOnK.cjs +2 -0
  20. package/dist/TrailingNode-CRHrgOnK.cjs.map +1 -0
  21. package/dist/{blockToNode-DIfPWLH8.js → blockToNode-DBNbhwwC.js} +33 -33
  22. package/dist/blockToNode-DBNbhwwC.js.map +1 -0
  23. package/dist/blockToNode-w7H99R6p.cjs.map +1 -1
  24. package/dist/blocknote.cjs +4 -4
  25. package/dist/blocknote.cjs.map +1 -1
  26. package/dist/blocknote.js +2496 -5686
  27. package/dist/blocknote.js.map +1 -1
  28. package/dist/blocks.cjs +1 -1
  29. package/dist/blocks.js +71 -70
  30. package/dist/blocks.js.map +1 -1
  31. package/dist/comments.cjs +1 -1
  32. package/dist/comments.cjs.map +1 -1
  33. package/dist/comments.js +451 -137
  34. package/dist/comments.js.map +1 -1
  35. package/dist/defaultBlocks-DLJ4Q1_J.cjs +6 -0
  36. package/dist/defaultBlocks-DLJ4Q1_J.cjs.map +1 -0
  37. package/dist/{BlockNoteSchema-Bi-eeHal.js → defaultBlocks-DgA_mtQV.js} +974 -1027
  38. package/dist/defaultBlocks-DgA_mtQV.js.map +1 -0
  39. package/dist/extensions.cjs +2 -0
  40. package/dist/extensions.cjs.map +1 -0
  41. package/dist/extensions.js +57 -0
  42. package/dist/extensions.js.map +1 -0
  43. package/dist/tsconfig.tsbuildinfo +1 -1
  44. package/dist/webpack-stats.json +1 -1
  45. package/dist/yjs.js +1 -1
  46. package/package.json +9 -3
  47. package/src/api/nodeConversions/blockToNode.ts +1 -1
  48. package/src/api/nodeConversions/nodeToBlock.ts +1 -1
  49. package/src/blocks/Code/block.ts +4 -4
  50. package/src/blocks/Divider/block.ts +2 -2
  51. package/src/blocks/File/helpers/render/createAddFileButton.ts +7 -5
  52. package/src/blocks/Heading/block.ts +23 -20
  53. package/src/blocks/ListItem/BulletListItem/block.ts +2 -2
  54. package/src/blocks/ListItem/CheckListItem/block.ts +2 -2
  55. package/src/blocks/ListItem/NumberedListItem/block.ts +3 -3
  56. package/src/blocks/ListItem/ToggleListItem/block.ts +2 -2
  57. package/src/blocks/PageBreak/getPageBreakSlashMenuItems.ts +2 -2
  58. package/src/blocks/Paragraph/block.ts +2 -2
  59. package/src/blocks/Quote/block.ts +2 -2
  60. package/src/blocks/Table/block.ts +4 -3
  61. package/src/blocks/ToggleWrapper/createToggleWrapper.ts +2 -1
  62. package/src/comments/extension.ts +353 -0
  63. package/src/comments/index.ts +2 -1
  64. package/src/comments/types.ts +8 -0
  65. package/src/{extensions/Comments → comments}/userstore/UserStore.ts +2 -2
  66. package/src/editor/BlockNoteEditor.test.ts +2 -23
  67. package/src/editor/BlockNoteEditor.ts +60 -453
  68. package/src/editor/BlockNoteExtension.test.ts +103 -0
  69. package/src/editor/BlockNoteExtension.ts +174 -56
  70. package/src/editor/managers/EventManager.ts +64 -35
  71. package/src/editor/managers/ExtensionManager/extensions.ts +214 -0
  72. package/src/editor/managers/ExtensionManager/index.ts +514 -0
  73. package/src/editor/managers/ExtensionManager/symbol.ts +6 -0
  74. package/src/editor/managers/SelectionManager.ts +5 -1
  75. package/src/editor/managers/StateManager.ts +29 -17
  76. package/src/editor/managers/index.ts +1 -5
  77. package/src/extensions/BlockChange/{BlockChangePlugin.ts → BlockChange.ts} +27 -29
  78. package/src/extensions/Collaboration/{ForkYDocPlugin.test.ts → ForkYDoc.test.ts} +6 -5
  79. package/src/extensions/Collaboration/ForkYDoc.ts +158 -0
  80. package/src/extensions/Collaboration/YCursorPlugin.ts +183 -0
  81. package/src/extensions/Collaboration/YSync.ts +16 -0
  82. package/src/extensions/Collaboration/YUndo.ts +12 -0
  83. package/src/extensions/Collaboration/schemaMigration/SchemaMigration.ts +59 -0
  84. package/src/extensions/DropCursor/DropCursor.ts +26 -0
  85. package/src/extensions/FilePanel/FilePanel.ts +41 -0
  86. package/src/extensions/FormattingToolbar/FormattingToolbar.ts +119 -0
  87. package/src/extensions/History/History.ts +11 -0
  88. package/src/extensions/LinkToolbar/LinkToolbar.ts +121 -0
  89. package/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboard.ts +74 -0
  90. package/src/extensions/Placeholder/Placeholder.ts +148 -0
  91. package/src/extensions/PreviousBlockType/{PreviousBlockTypePlugin.ts → PreviousBlockType.ts} +9 -13
  92. package/src/extensions/ShowSelection/{ShowSelectionPlugin.ts → ShowSelection.ts} +27 -33
  93. package/src/extensions/SideMenu/{SideMenuPlugin.ts → SideMenu.ts} +63 -83
  94. package/src/extensions/SuggestionMenu/{SuggestionPlugin.ts → SuggestionMenu.ts} +71 -77
  95. package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +29 -44
  96. package/src/extensions/TableHandles/{TableHandlesPlugin.ts → TableHandles.ts} +416 -437
  97. package/src/extensions/TrailingNode/{TrailingNodeExtension.ts → TrailingNode.ts} +8 -17
  98. package/src/extensions/index.ts +24 -0
  99. package/src/extensions/{BackgroundColor → tiptap-extensions/BackgroundColor}/BackgroundColorExtension.ts +1 -1
  100. package/src/extensions/{KeyboardShortcuts → tiptap-extensions/KeyboardShortcuts}/KeyboardShortcutsExtension.ts +21 -16
  101. package/src/extensions/{TextColor → tiptap-extensions/TextColor}/TextColorExtension.ts +1 -1
  102. package/src/extensions/tiptap-extensions/index.ts +31 -0
  103. package/src/index.ts +1 -13
  104. package/src/schema/blocks/createSpec.ts +14 -11
  105. package/src/schema/blocks/internal.ts +2 -2
  106. package/src/schema/blocks/types.ts +8 -5
  107. package/src/schema/schema.ts +11 -36
  108. package/src/util/topo-sort.ts +46 -0
  109. package/types/src/comments/extension.d.ts +70 -0
  110. package/types/src/comments/index.d.ts +2 -1
  111. package/types/src/comments/types.d.ts +8 -0
  112. package/types/src/{extensions/Comments → comments}/userstore/UserStore.d.ts +2 -2
  113. package/types/src/editor/BlockNoteEditor.d.ts +34 -105
  114. package/types/src/editor/BlockNoteExtension.d.ts +87 -22
  115. package/types/src/editor/managers/EventManager.d.ts +25 -16
  116. package/types/src/editor/managers/ExtensionManager/extensions.d.ts +8 -0
  117. package/types/src/editor/managers/ExtensionManager/index.d.ts +83 -0
  118. package/types/src/editor/managers/ExtensionManager/symbol.d.ts +5 -0
  119. package/types/src/editor/managers/StateManager.d.ts +1 -12
  120. package/types/src/editor/managers/index.d.ts +1 -2
  121. package/types/src/extensions/BlockChange/BlockChange.d.ts +16 -0
  122. package/types/src/extensions/Collaboration/ForkYDoc.d.ts +34 -0
  123. package/types/src/extensions/Collaboration/ForkYDoc.test.d.ts +1 -0
  124. package/types/src/extensions/Collaboration/YCursorPlugin.d.ts +24 -0
  125. package/types/src/extensions/Collaboration/YSync.d.ts +8 -0
  126. package/types/src/extensions/Collaboration/YUndo.d.ts +12 -0
  127. package/types/src/extensions/Collaboration/schemaMigration/SchemaMigration.d.ts +8 -0
  128. package/types/src/extensions/DropCursor/DropCursor.d.ts +5 -0
  129. package/types/src/extensions/FilePanel/FilePanel.d.ts +11 -0
  130. package/types/src/extensions/FormattingToolbar/FormattingToolbar.d.ts +9 -0
  131. package/types/src/extensions/History/History.d.ts +6 -0
  132. package/types/src/extensions/LinkToolbar/LinkToolbar.d.ts +24 -0
  133. package/types/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboard.d.ts +5 -0
  134. package/types/src/extensions/Placeholder/Placeholder.d.ts +6 -0
  135. package/types/src/extensions/PreviousBlockType/{PreviousBlockTypePlugin.d.ts → PreviousBlockType.d.ts} +9 -5
  136. package/types/src/extensions/ShowSelection/ShowSelection.d.ts +21 -0
  137. package/types/src/extensions/SideMenu/{SideMenuPlugin.d.ts → SideMenu.d.ts} +11 -15
  138. package/types/src/extensions/SuggestionMenu/SuggestionMenu.d.ts +54 -0
  139. package/types/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.d.ts +1 -1
  140. package/types/src/extensions/TableHandles/{TableHandlesPlugin.d.ts → TableHandles.d.ts} +28 -31
  141. package/types/src/extensions/TrailingNode/TrailingNode.d.ts +8 -0
  142. package/types/src/extensions/index.d.ts +24 -0
  143. package/types/src/extensions/{KeyboardShortcuts → tiptap-extensions/KeyboardShortcuts}/KeyboardShortcutsExtension.d.ts +1 -1
  144. package/types/src/extensions/tiptap-extensions/index.d.ts +11 -0
  145. package/types/src/index.d.ts +1 -13
  146. package/types/src/schema/blocks/createSpec.d.ts +4 -4
  147. package/types/src/schema/blocks/internal.d.ts +2 -2
  148. package/types/src/schema/blocks/types.d.ts +5 -5
  149. package/types/src/util/topo-sort.d.ts +8 -0
  150. package/dist/BlockNoteSchema-Bi-eeHal.js.map +0 -1
  151. package/dist/BlockNoteSchema-DjDaA2C3.cjs +0 -6
  152. package/dist/BlockNoteSchema-DjDaA2C3.cjs.map +0 -1
  153. package/dist/blockToNode-DIfPWLH8.js.map +0 -1
  154. package/src/comments/models/User.ts +0 -8
  155. package/src/editor/BlockNoteExtensions.ts +0 -325
  156. package/src/editor/managers/CollaborationManager.ts +0 -212
  157. package/src/editor/managers/ExtensionManager.ts +0 -130
  158. package/src/extensions/Collaboration/CursorPlugin.ts +0 -189
  159. package/src/extensions/Collaboration/ForkYDocPlugin.ts +0 -192
  160. package/src/extensions/Collaboration/SyncPlugin.ts +0 -18
  161. package/src/extensions/Collaboration/UndoPlugin.ts +0 -18
  162. package/src/extensions/Collaboration/schemaMigration/SchemaMigrationPlugin.ts +0 -59
  163. package/src/extensions/Comments/CommentsPlugin.ts +0 -392
  164. package/src/extensions/FilePanel/FilePanelPlugin.ts +0 -206
  165. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +0 -363
  166. package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +0 -380
  167. package/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.ts +0 -75
  168. package/src/extensions/Placeholder/PlaceholderPlugin.ts +0 -147
  169. package/types/src/comments/models/User.d.ts +0 -8
  170. package/types/src/editor/BlockNoteExtensions.d.ts +0 -43
  171. package/types/src/editor/managers/CollaborationManager.d.ts +0 -115
  172. package/types/src/editor/managers/ExtensionManager.d.ts +0 -68
  173. package/types/src/extensions/BlockChange/BlockChangePlugin.d.ts +0 -15
  174. package/types/src/extensions/Collaboration/CursorPlugin.d.ts +0 -37
  175. package/types/src/extensions/Collaboration/ForkYDocPlugin.d.ts +0 -41
  176. package/types/src/extensions/Collaboration/SyncPlugin.d.ts +0 -7
  177. package/types/src/extensions/Collaboration/UndoPlugin.d.ts +0 -9
  178. package/types/src/extensions/Collaboration/schemaMigration/SchemaMigrationPlugin.d.ts +0 -7
  179. package/types/src/extensions/Comments/CommentsPlugin.d.ts +0 -66
  180. package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +0 -31
  181. package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +0 -41
  182. package/types/src/extensions/LinkToolbar/LinkToolbarPlugin.d.ts +0 -42
  183. package/types/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.d.ts +0 -5
  184. package/types/src/extensions/Placeholder/PlaceholderPlugin.d.ts +0 -6
  185. package/types/src/extensions/ShowSelection/ShowSelectionPlugin.d.ts +0 -15
  186. package/types/src/extensions/SuggestionMenu/SuggestionPlugin.d.ts +0 -31
  187. package/types/src/extensions/TrailingNode/TrailingNodeExtension.d.ts +0 -13
  188. /package/src/{extensions/Comments/CommentMark.ts → comments/mark.ts} +0 -0
  189. /package/src/extensions/{HardBreak → tiptap-extensions/HardBreak}/HardBreak.ts +0 -0
  190. /package/src/extensions/{Suggestions → tiptap-extensions/Suggestions}/SuggestionMarks.ts +0 -0
  191. /package/src/extensions/{TextAlignment → tiptap-extensions/TextAlignment}/TextAlignmentExtension.ts +0 -0
  192. /package/src/extensions/{UniqueID → tiptap-extensions/UniqueID}/UniqueID.ts +0 -0
  193. /package/types/src/{extensions/Comments/CommentMark.d.ts → comments/mark.d.ts} +0 -0
  194. /package/types/src/{extensions/Collaboration/ForkYDocPlugin.test.d.ts → editor/BlockNoteExtension.test.d.ts} +0 -0
  195. /package/types/src/extensions/{BackgroundColor → tiptap-extensions/BackgroundColor}/BackgroundColorExtension.d.ts +0 -0
  196. /package/types/src/extensions/{HardBreak → tiptap-extensions/HardBreak}/HardBreak.d.ts +0 -0
  197. /package/types/src/extensions/{Suggestions → tiptap-extensions/Suggestions}/SuggestionMarks.d.ts +0 -0
  198. /package/types/src/extensions/{TextAlignment → tiptap-extensions/TextAlignment}/TextAlignmentExtension.d.ts +0 -0
  199. /package/types/src/extensions/{TextColor → tiptap-extensions/TextColor}/TextColorExtension.d.ts +0 -0
  200. /package/types/src/extensions/{UniqueID → tiptap-extensions/UniqueID}/UniqueID.d.ts +0 -0
@@ -0,0 +1,103 @@
1
+ import { expect, it } from "vitest";
2
+ import { createExtension, ExtensionOptions } from "./BlockNoteExtension.js";
3
+ import { BlockNoteEditor } from "./BlockNoteEditor.js";
4
+
5
+ const editor = BlockNoteEditor.create();
6
+ /**
7
+ * @vitest-environment jsdom
8
+ */
9
+ it("creates an extension factory", () => {
10
+ const extension = createExtension(() => {
11
+ return {
12
+ key: "test",
13
+ prosemirrorPlugins: [],
14
+ } as const;
15
+ });
16
+
17
+ const extInstance = extension()({ editor });
18
+ expect(extInstance.key).toBe("test");
19
+ expect(extInstance.prosemirrorPlugins).toEqual([]);
20
+ });
21
+
22
+ it("creates an extension factory with options", () => {
23
+ const extension = createExtension((opts: ExtensionOptions<{ x: number }>) => {
24
+ expect(opts.options.x).toBe(1);
25
+ return {
26
+ key: "test",
27
+ prosemirrorPlugins: [],
28
+ } as const;
29
+ });
30
+
31
+ const extInstance = extension({ x: 1 })({ editor });
32
+ expect(extInstance.key).toBe("test");
33
+ expect(extInstance.prosemirrorPlugins).toEqual([]);
34
+ });
35
+
36
+ it("creates an extension factory with undefined options", () => {
37
+ const extension = createExtension(
38
+ (opts: ExtensionOptions<{ x: number } | undefined>) => {
39
+ expect(opts.options).toBe(undefined);
40
+ return {
41
+ key: "test",
42
+ prosemirrorPlugins: [],
43
+ } as const;
44
+ },
45
+ );
46
+
47
+ const extInstance = extension()({ editor });
48
+ expect(extInstance.key).toBe("test");
49
+ expect(extInstance.prosemirrorPlugins).toEqual([]);
50
+ });
51
+
52
+ it("creates an extension factory from an object", () => {
53
+ const extension = createExtension({
54
+ key: "test",
55
+ prosemirrorPlugins: [],
56
+ } as const);
57
+
58
+ const extInstance = extension({ editor });
59
+ expect(extInstance.key).toBe("test");
60
+ expect(extInstance.prosemirrorPlugins).toEqual([]);
61
+ });
62
+
63
+ it("allows arbitrary properties on a no-options extension", () => {
64
+ const extension = createExtension(() => {
65
+ return {
66
+ key: "test",
67
+ prosemirrorPlugins: [],
68
+ arbitraryProperty: "arbitraryValue",
69
+ arbitraryMethod: () => {
70
+ return "arbitraryValue";
71
+ },
72
+ } as const;
73
+ });
74
+
75
+ const extInstance = extension()({ editor });
76
+ expect(extInstance.arbitraryProperty).toBe("arbitraryValue");
77
+ expect(extInstance.arbitraryMethod()).toBe("arbitraryValue");
78
+ // @ts-expect-error - this method takes no arguments
79
+ extInstance.arbitraryMethod(90);
80
+ // @ts-expect-error - this property is not defined
81
+ extInstance.nonExistentProperty = "newArbitraryValue";
82
+ });
83
+
84
+ it("allows arbitrary properties on an extension with options", () => {
85
+ const extension = createExtension((opts: ExtensionOptions<{ x: number }>) => {
86
+ expect(opts.options.x).toBe(1);
87
+ return {
88
+ key: "test",
89
+ prosemirrorPlugins: [],
90
+ arbitraryProperty: "arbitraryValue",
91
+ arbitraryMethod: () => {
92
+ return "arbitraryValue";
93
+ },
94
+ } as const;
95
+ });
96
+
97
+ const extInstance = extension({ x: 1 })({ editor });
98
+ expect(extInstance.arbitraryProperty).toBe("arbitraryValue");
99
+ // @ts-expect-error - this method takes no arguments
100
+ extInstance.arbitraryMethod(90);
101
+ // @ts-expect-error - this property is not defined
102
+ extInstance.nonExistentProperty = "newArbitraryValue";
103
+ });
@@ -1,42 +1,57 @@
1
- import { Plugin } from "prosemirror-state";
2
- import { EventEmitter } from "../util/EventEmitter.js";
3
-
4
- import { AnyExtension } from "@tiptap/core";
5
- import {
6
- BlockSchema,
7
- InlineContentSchema,
8
- PartialBlockNoDefaults,
9
- StyleSchema,
10
- } from "../schema/index.js";
11
- import { BlockNoteEditor } from "./BlockNoteEditor.js";
12
-
13
- export abstract class BlockNoteExtension<
14
- TEvent extends Record<string, any> = any,
15
- > extends EventEmitter<TEvent> {
16
- public static key(): string {
17
- throw new Error("You must implement the key method in your extension");
18
- }
1
+ import { Store, StoreOptions } from "@tanstack/store";
2
+ import { type AnyExtension } from "@tiptap/core";
3
+ import type { Plugin as ProsemirrorPlugin } from "prosemirror-state";
4
+ import type { PartialBlockNoDefaults } from "../schema/index.js";
5
+ import type { BlockNoteEditor } from "./BlockNoteEditor.js";
6
+ import { originalFactorySymbol } from "./managers/ExtensionManager/symbol.js";
19
7
 
20
- protected addProsemirrorPlugin(plugin: Plugin) {
21
- this.plugins.push(plugin);
22
- }
8
+ /**
9
+ * This function is called when the extension is destroyed.
10
+ */
11
+ type OnDestroy = () => void;
23
12
 
24
- public readonly plugins: Plugin[] = [];
25
- public get priority(): number | undefined {
26
- return undefined;
27
- }
13
+ /**
14
+ * Describes a BlockNote extension.
15
+ */
16
+ export interface Extension<State = any, Key extends string = string> {
17
+ /**
18
+ * The unique identifier for the extension.
19
+ */
20
+ readonly key: Key;
28
21
 
29
- // eslint-disable-next-line
30
- constructor(..._args: any[]) {
31
- super();
32
- // Allow subclasses to have constructors with parameters
33
- // without this, we can't easily implement BlockNoteEditor.extension(MyExtension) pattern
34
- }
22
+ /**
23
+ * Triggered when the extension is mounted to the editor.
24
+ */
25
+ readonly mount?: (ctx: {
26
+ /**
27
+ * The DOM element that the editor is mounted to.
28
+ */
29
+ dom: HTMLElement;
30
+ /**
31
+ * The root document of the {@link document} that the editor is mounted to.
32
+ */
33
+ root: Document | ShadowRoot;
34
+ /**
35
+ * An {@link AbortSignal} that will be aborted when the extension is destroyed.
36
+ */
37
+ signal: AbortSignal;
38
+ }) => void | OnDestroy;
39
+
40
+ /**
41
+ * The store for the extension.
42
+ */
43
+ readonly store?: Store<State>;
44
+
45
+ /**
46
+ * Declares what {@link Extension}s that this extension depends on.
47
+ */
48
+ readonly runsBefore?: ReadonlyArray<string>;
35
49
 
36
50
  /**
37
- * Input rules for the block
51
+ * Input rules for a block: An input rule is what is used to replace text in a block when a regular expression match is found.
52
+ * As an example, typing `#` in a paragraph block will trigger an input rule to replace the text with a heading block.
38
53
  */
39
- public inputRules?: InputRule[];
54
+ readonly inputRules?: ReadonlyArray<InputRule>;
40
55
 
41
56
  /**
42
57
  * A mapping of a keyboard shortcut to a function that will be called when the shortcut is pressed
@@ -60,17 +75,27 @@ export abstract class BlockNoteExtension<
60
75
  * }
61
76
  * ```
62
77
  */
63
- public keyboardShortcuts?: Record<
78
+ readonly keyboardShortcuts?: Record<
64
79
  string,
65
- (ctx: {
66
- editor: BlockNoteEditor<BlockSchema, InlineContentSchema, StyleSchema>;
67
- }) => boolean
80
+ (ctx: { editor: BlockNoteEditor<any, any, any> }) => boolean
68
81
  >;
69
82
 
70
- public tiptapExtensions?: AnyExtension[];
83
+ /**
84
+ * Add additional prosemirror plugins to the editor.
85
+ */
86
+ readonly prosemirrorPlugins?: ReadonlyArray<ProsemirrorPlugin>;
87
+
88
+ /**
89
+ * Add additional tiptap extensions to the editor.
90
+ */
91
+ readonly tiptapExtensions?: ReadonlyArray<AnyExtension>;
71
92
  }
72
93
 
73
- export type InputRule = {
94
+ /**
95
+ * An input rule is what is used to replace text in a block when a regular expression match is found.
96
+ * As an example, typing `#` in a paragraph block will trigger an input rule to replace the text with a heading block.
97
+ */
98
+ type InputRule = {
74
99
  /**
75
100
  * The regex to match when to trigger the input rule
76
101
  */
@@ -97,22 +122,115 @@ export type InputRule = {
97
122
  };
98
123
 
99
124
  /**
100
- * This creates an instance of a BlockNoteExtension that can be used to add to a schema.
101
- * It is a bit of a hack, but it works.
125
+ * These are the arguments that are passed to an {@link ExtensionFactoryInstance}.
126
+ */
127
+ export interface ExtensionOptions<
128
+ Options extends Record<string, any> | undefined =
129
+ | Record<string, any>
130
+ | undefined,
131
+ > {
132
+ options: Options;
133
+ editor: BlockNoteEditor<any, any, any>;
134
+ }
135
+
136
+ // a type that maps the extension key to the return type of the extension factory
137
+ export type ExtensionMap<T extends ReadonlyArray<ExtensionFactoryInstance>> = {
138
+ [K in T[number] extends ExtensionFactoryInstance<infer Ext>
139
+ ? Ext["key"]
140
+ : never]: T[number] extends ExtensionFactoryInstance<infer Ext>
141
+ ? Ext
142
+ : never;
143
+ };
144
+
145
+ /**
146
+ * This is a type that represents the function which will actually create the extension.
147
+ * It requires the editor instance to be passed in, but will already have the options applied automatically.
148
+ *
149
+ * @note Only the BlockNoteEditor should instantiate this function, not the user. Look at {@link createExtension} for user-facing functions.
102
150
  */
103
- export function createBlockNoteExtension(
104
- options: Partial<
105
- Pick<
106
- BlockNoteExtension,
107
- "inputRules" | "keyboardShortcuts" | "plugins" | "tiptapExtensions"
108
- >
109
- > & { key: string },
110
- ) {
111
- const x = Object.create(BlockNoteExtension.prototype);
112
- x.key = options.key;
113
- x.inputRules = options.inputRules;
114
- x.keyboardShortcuts = options.keyboardShortcuts;
115
- x.plugins = options.plugins ?? [];
116
- x.tiptapExtensions = options.tiptapExtensions;
117
- return x as BlockNoteExtension;
151
+ export type ExtensionFactoryInstance<
152
+ Ext extends Extension<any, any> = Extension<any, any>,
153
+ > = (ctx: Omit<ExtensionOptions<any>, "options">) => Ext;
154
+
155
+ /**
156
+ * This is the return type of the {@link createExtension} function.
157
+ * It is a function that can be invoked with the extension's options to create a new extension factory.
158
+ */
159
+ export type ExtensionFactory<
160
+ State = any,
161
+ Key extends string = string,
162
+ Factory extends (ctx: any) => Extension<State, Key> = (
163
+ ctx: ExtensionOptions<any>,
164
+ ) => Extension<State, Key>,
165
+ > =
166
+ Parameters<Factory>[0] extends ExtensionOptions<infer Options>
167
+ ? undefined extends Options
168
+ ? (
169
+ options?: Exclude<Options, undefined>,
170
+ ) => ExtensionFactoryInstance<ReturnType<Factory>>
171
+ : (options: Options) => ExtensionFactoryInstance<ReturnType<Factory>>
172
+ : () => ExtensionFactoryInstance<ReturnType<Factory>>;
173
+
174
+ /**
175
+ * Constructs a BlockNote {@link ExtensionFactory} from a factory function or object
176
+ */
177
+ // This overload is for `createExtension({ key: "test", ... })`
178
+ export function createExtension<
179
+ const State = any,
180
+ const Key extends string = string,
181
+ const Ext extends Extension<State, Key> = Extension<State, Key>,
182
+ >(factory: Ext): ExtensionFactoryInstance<Ext>;
183
+ // This overload is for `createExtension(({editor, options}) => ({ key: "test", ... }))`
184
+ export function createExtension<
185
+ const State = any,
186
+ const Options extends Record<string, any> | undefined = any,
187
+ const Key extends string = string,
188
+ const Factory extends (ctx: any) => Extension<State, Key> = (
189
+ ctx: ExtensionOptions<Options>,
190
+ ) => Extension<State, Key>,
191
+ >(factory: Factory): ExtensionFactory<State, Key, Factory>;
192
+ // This overload is for both of the above overloads as it is the implementation of the function
193
+ export function createExtension<
194
+ const State = any,
195
+ const Options extends Record<string, any> | undefined = any,
196
+ const Key extends string = string,
197
+ const Factory extends
198
+ | Extension<State, Key>
199
+ | ((ctx: any) => Extension<State, Key>) = (
200
+ ctx: ExtensionOptions<Options>,
201
+ ) => Extension<State, Key>,
202
+ >(
203
+ factory: Factory,
204
+ ): Factory extends Extension<State, Key>
205
+ ? ExtensionFactoryInstance<Factory>
206
+ : Factory extends (ctx: any) => Extension<State, Key>
207
+ ? ExtensionFactory<State, Key, Factory>
208
+ : never {
209
+ if (typeof factory === "object" && "key" in factory) {
210
+ return function factoryFn() {
211
+ (factory as any)[originalFactorySymbol] = factoryFn;
212
+ return factory;
213
+ } as any;
214
+ }
215
+
216
+ if (typeof factory !== "function") {
217
+ throw new Error("factory must be a function");
218
+ }
219
+
220
+ return function factoryFn(options: Options) {
221
+ return (ctx: { editor: BlockNoteEditor<any, any, any> }) => {
222
+ const extension = factory({ editor: ctx.editor, options });
223
+ // We stick a symbol onto the extension to allow us to retrieve the original factory for comparison later.
224
+ // This enables us to do things like: `editor.getExtension(YSync).prosemirrorPlugins`
225
+ (extension as any)[originalFactorySymbol] = factoryFn;
226
+ return extension;
227
+ };
228
+ } as any;
229
+ }
230
+
231
+ export function createStore<T = any>(
232
+ initialState: T,
233
+ options?: StoreOptions<T>,
234
+ ): Store<T> {
235
+ return new Store(initialState, options);
118
236
  }
@@ -5,6 +5,11 @@ import {
5
5
  } from "../../api/getBlocksChangedByTransaction.js";
6
6
  import { Transaction } from "prosemirror-state";
7
7
  import { EventEmitter } from "../../util/EventEmitter.js";
8
+ import {
9
+ BlockSchema,
10
+ InlineContentSchema,
11
+ StyleSchema,
12
+ } from "../../schema/index.js";
8
13
 
9
14
  /**
10
15
  * A function that can be used to unsubscribe from an event.
@@ -14,37 +19,33 @@ export type Unsubscribe = () => void;
14
19
  /**
15
20
  * EventManager is a class which manages the events of the editor
16
21
  */
17
- export class EventManager<Editor extends BlockNoteEditor> extends EventEmitter<{
22
+ export class EventManager<
23
+ BSchema extends BlockSchema,
24
+ I extends InlineContentSchema,
25
+ S extends StyleSchema,
26
+ > extends EventEmitter<{
18
27
  onChange: [
19
- editor: Editor,
20
28
  ctx: {
21
- getChanges(): BlocksChanged<
22
- Editor["schema"]["blockSchema"],
23
- Editor["schema"]["inlineContentSchema"],
24
- Editor["schema"]["styleSchema"]
25
- >;
29
+ editor: BlockNoteEditor<BSchema, I, S>;
30
+ transaction: Transaction;
31
+ appendedTransactions: Transaction[];
26
32
  },
27
33
  ];
28
- onSelectionChange: [ctx: { editor: Editor; transaction: Transaction }];
29
- onMount: [ctx: { editor: Editor }];
30
- onUnmount: [ctx: { editor: Editor }];
34
+ onSelectionChange: [
35
+ ctx: { editor: BlockNoteEditor<BSchema, I, S>; transaction: Transaction },
36
+ ];
37
+ onMount: [ctx: { editor: BlockNoteEditor<BSchema, I, S> }];
38
+ onUnmount: [ctx: { editor: BlockNoteEditor<BSchema, I, S> }];
31
39
  }> {
32
- constructor(private editor: Editor) {
40
+ constructor(private editor: BlockNoteEditor<BSchema, I, S>) {
33
41
  super();
34
42
  // We register tiptap events only once the editor is finished initializing
35
43
  // otherwise we would be trying to register events on a tiptap editor which does not exist yet
36
- editor.onCreate(() => {
44
+ editor.on("create", () => {
37
45
  editor._tiptapEditor.on(
38
46
  "update",
39
47
  ({ transaction, appendedTransactions }) => {
40
- this.emit("onChange", editor, {
41
- getChanges() {
42
- return getBlocksChangedByTransaction(
43
- transaction,
44
- appendedTransactions,
45
- );
46
- },
47
- });
48
+ this.emit("onChange", { editor, transaction, appendedTransactions });
48
49
  },
49
50
  );
50
51
  editor._tiptapEditor.on("selectionUpdate", ({ transaction }) => {
@@ -64,20 +65,41 @@ export class EventManager<Editor extends BlockNoteEditor> extends EventEmitter<{
64
65
  */
65
66
  public onChange(
66
67
  callback: (
67
- editor: Editor,
68
+ editor: BlockNoteEditor<BSchema, I, S>,
68
69
  ctx: {
69
- getChanges(): BlocksChanged<
70
- Editor["schema"]["blockSchema"],
71
- Editor["schema"]["inlineContentSchema"],
72
- Editor["schema"]["styleSchema"]
73
- >;
70
+ getChanges(): BlocksChanged<BSchema, I, S>;
74
71
  },
75
72
  ) => void,
73
+ /**
74
+ * If true, the callback will be triggered when the changes are caused by a remote user
75
+ * @default true
76
+ */
77
+ includeUpdatesFromRemote = true,
76
78
  ): Unsubscribe {
77
- this.on("onChange", callback);
79
+ const cb = ({
80
+ transaction,
81
+ appendedTransactions,
82
+ }: {
83
+ transaction: Transaction;
84
+ appendedTransactions: Transaction[];
85
+ }) => {
86
+ if (!includeUpdatesFromRemote && isRemoteTransaction(transaction)) {
87
+ // don't trigger the callback if the changes are caused by a remote user
88
+ return;
89
+ }
90
+ callback(this.editor, {
91
+ getChanges() {
92
+ return getBlocksChangedByTransaction(
93
+ transaction,
94
+ appendedTransactions,
95
+ );
96
+ },
97
+ });
98
+ };
99
+ this.on("onChange", cb);
78
100
 
79
101
  return () => {
80
- this.off("onChange", callback);
102
+ this.off("onChange", cb);
81
103
  };
82
104
  }
83
105
 
@@ -85,7 +107,7 @@ export class EventManager<Editor extends BlockNoteEditor> extends EventEmitter<{
85
107
  * Register a callback that will be called when the selection changes.
86
108
  */
87
109
  public onSelectionChange(
88
- callback: (editor: Editor) => void,
110
+ callback: (editor: BlockNoteEditor<BSchema, I, S>) => void,
89
111
  /**
90
112
  * If true, the callback will be triggered when the selection changes due to a yjs sync (i.e.: other user was typing)
91
113
  */
@@ -93,11 +115,10 @@ export class EventManager<Editor extends BlockNoteEditor> extends EventEmitter<{
93
115
  ): Unsubscribe {
94
116
  const cb = (e: { transaction: Transaction }) => {
95
117
  if (
96
- e.transaction.getMeta("$y-sync") &&
97
- !includeSelectionChangedByRemote
118
+ !includeSelectionChangedByRemote &&
119
+ isRemoteTransaction(e.transaction)
98
120
  ) {
99
- // selection changed because of a yjs sync (i.e.: other user was typing)
100
- // we don't want to trigger the callback in this case
121
+ // don't trigger the callback if the selection changed because of a remote user
101
122
  return;
102
123
  }
103
124
  callback(this.editor);
@@ -113,7 +134,9 @@ export class EventManager<Editor extends BlockNoteEditor> extends EventEmitter<{
113
134
  /**
114
135
  * Register a callback that will be called when the editor is mounted.
115
136
  */
116
- public onMount(callback: (ctx: { editor: Editor }) => void): Unsubscribe {
137
+ public onMount(
138
+ callback: (ctx: { editor: BlockNoteEditor<BSchema, I, S> }) => void,
139
+ ): Unsubscribe {
117
140
  this.on("onMount", callback);
118
141
 
119
142
  return () => {
@@ -124,7 +147,9 @@ export class EventManager<Editor extends BlockNoteEditor> extends EventEmitter<{
124
147
  /**
125
148
  * Register a callback that will be called when the editor is unmounted.
126
149
  */
127
- public onUnmount(callback: (ctx: { editor: Editor }) => void): Unsubscribe {
150
+ public onUnmount(
151
+ callback: (ctx: { editor: BlockNoteEditor<BSchema, I, S> }) => void,
152
+ ): Unsubscribe {
128
153
  this.on("onUnmount", callback);
129
154
 
130
155
  return () => {
@@ -132,3 +157,7 @@ export class EventManager<Editor extends BlockNoteEditor> extends EventEmitter<{
132
157
  };
133
158
  }
134
159
  }
160
+
161
+ function isRemoteTransaction(transaction: Transaction): boolean {
162
+ return !!transaction.getMeta("y-sync$");
163
+ }