@blocknote/core 0.11.2 → 0.12.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 (138) hide show
  1. package/README.md +13 -17
  2. package/dist/blocknote.js +1662 -1447
  3. package/dist/blocknote.js.map +1 -1
  4. package/dist/blocknote.umd.cjs +6 -6
  5. package/dist/blocknote.umd.cjs.map +1 -1
  6. package/dist/style.css +1 -1
  7. package/dist/webpack-stats.json +1 -1
  8. package/package.json +7 -3
  9. package/src/api/blockManipulation/blockManipulation.test.ts +19 -15
  10. package/src/api/blockManipulation/blockManipulation.ts +107 -17
  11. package/src/api/exporters/html/externalHTMLExporter.ts +3 -7
  12. package/src/api/exporters/html/htmlConversion.test.ts +6 -3
  13. package/src/api/exporters/html/internalHTMLSerializer.ts +3 -7
  14. package/src/api/exporters/html/util/sharedHTMLConversion.ts +3 -3
  15. package/src/api/exporters/markdown/markdownExporter.test.ts +7 -3
  16. package/src/api/exporters/markdown/markdownExporter.ts +2 -6
  17. package/src/api/getCurrentBlockContentType.ts +14 -0
  18. package/src/api/nodeConversions/nodeConversions.test.ts +14 -7
  19. package/src/api/nodeConversions/nodeConversions.ts +1 -2
  20. package/src/api/parsers/html/parseHTML.test.ts +5 -1
  21. package/src/api/parsers/html/parseHTML.ts +2 -6
  22. package/src/api/parsers/html/util/nestedLists.ts +11 -1
  23. package/src/api/parsers/markdown/parseMarkdown.test.ts +3 -0
  24. package/src/api/parsers/markdown/parseMarkdown.ts +2 -6
  25. package/src/api/testUtil/cases/customBlocks.ts +18 -16
  26. package/src/api/testUtil/cases/customInlineContent.ts +12 -13
  27. package/src/api/testUtil/cases/customStyles.ts +12 -10
  28. package/src/api/testUtil/index.ts +4 -2
  29. package/src/api/testUtil/partialBlockTestUtil.ts +2 -6
  30. package/src/blocks/HeadingBlockContent/HeadingBlockContent.ts +50 -21
  31. package/src/blocks/ImageBlockContent/ImageBlockContent.ts +1 -2
  32. package/src/blocks/ImageBlockContent/uploadToTmpFilesDotOrg_DEV_ONLY.ts +8 -1
  33. package/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +18 -5
  34. package/src/blocks/ListItemBlockContent/ListItemKeyboardShortcuts.ts +7 -1
  35. package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +18 -5
  36. package/src/blocks/ParagraphBlockContent/ParagraphBlockContent.ts +14 -5
  37. package/src/blocks/defaultBlockHelpers.ts +3 -3
  38. package/src/blocks/defaultBlockTypeGuards.ts +84 -0
  39. package/src/blocks/defaultBlocks.ts +29 -3
  40. package/src/editor/Block.css +2 -31
  41. package/src/editor/BlockNoteEditor.ts +223 -267
  42. package/src/editor/BlockNoteExtensions.ts +5 -2
  43. package/src/editor/BlockNoteSchema.ts +98 -0
  44. package/src/editor/BlockNoteTipTapEditor.ts +162 -0
  45. package/src/editor/cursorPositionTypes.ts +2 -6
  46. package/src/editor/editor.css +0 -1
  47. package/src/editor/selectionTypes.ts +2 -6
  48. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +22 -29
  49. package/src/extensions/{ImageToolbar → ImagePanel}/ImageToolbarPlugin.ts +54 -60
  50. package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +330 -0
  51. package/src/extensions/Placeholder/PlaceholderExtension.ts +81 -88
  52. package/src/extensions/SideMenu/SideMenuPlugin.ts +55 -56
  53. package/src/extensions/SuggestionMenu/DefaultSuggestionItem.ts +8 -0
  54. package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +353 -0
  55. package/src/extensions/{SlashMenu/defaultSlashMenuItems.ts → SuggestionMenu/getDefaultSlashMenuItems.ts} +119 -89
  56. package/src/extensions/TableHandles/TableHandlesPlugin.ts +62 -45
  57. package/src/extensions-shared/UiElementPosition.ts +4 -0
  58. package/src/index.ts +8 -8
  59. package/src/pm-nodes/BlockContainer.ts +5 -5
  60. package/src/schema/blocks/types.ts +15 -15
  61. package/src/schema/inlineContent/createSpec.ts +2 -2
  62. package/src/schema/inlineContent/types.ts +1 -1
  63. package/src/util/browser.ts +6 -4
  64. package/src/util/typescript.ts +7 -4
  65. package/types/src/api/blockManipulation/blockManipulation.d.ts +6 -1
  66. package/types/src/api/exporters/html/externalHTMLExporter.d.ts +2 -1
  67. package/types/src/api/exporters/html/internalHTMLSerializer.d.ts +2 -1
  68. package/types/src/api/exporters/markdown/markdownExporter.d.ts +2 -1
  69. package/types/src/api/getCurrentBlockContentType.d.ts +2 -0
  70. package/types/src/api/nodeConversions/nodeConversions.d.ts +2 -1
  71. package/types/src/api/parsers/html/parseHTML.d.ts +2 -1
  72. package/types/src/api/parsers/markdown/parseMarkdown.d.ts +2 -1
  73. package/types/src/api/testUtil/cases/customBlocks.d.ts +72 -13
  74. package/types/src/api/testUtil/cases/customInlineContent.d.ts +281 -6
  75. package/types/src/api/testUtil/cases/customStyles.d.ts +247 -13
  76. package/types/src/api/testUtil/index.d.ts +4 -2
  77. package/types/src/api/testUtil/partialBlockTestUtil.d.ts +2 -1
  78. package/types/src/blocks/ImageBlockContent/uploadToTmpFilesDotOrg_DEV_ONLY.d.ts +6 -1
  79. package/types/src/blocks/defaultBlockHelpers.d.ts +2 -2
  80. package/types/src/blocks/defaultBlockTypeGuards.d.ts +24 -0
  81. package/types/src/blocks/defaultBlocks.d.ts +21 -15
  82. package/types/src/editor/BlockNoteEditor.d.ts +51 -56
  83. package/types/src/editor/BlockNoteExtensions.d.ts +1 -0
  84. package/types/src/editor/BlockNoteSchema.d.ts +34 -0
  85. package/types/src/editor/BlockNoteTipTapEditor.d.ts +28 -0
  86. package/types/src/editor/cursorPositionTypes.d.ts +2 -1
  87. package/types/src/editor/selectionTypes.d.ts +2 -1
  88. package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +5 -6
  89. package/types/src/extensions/ImagePanel/ImageToolbarPlugin.d.ts +32 -0
  90. package/types/src/extensions/LinkToolbar/LinkToolbarPlugin.d.ts +40 -0
  91. package/types/src/extensions/Placeholder/PlaceholderExtension.d.ts +2 -15
  92. package/types/src/extensions/SideMenu/SideMenuPlugin.d.ts +8 -7
  93. package/types/src/extensions/SuggestionMenu/DefaultSuggestionItem.d.ts +8 -0
  94. package/types/src/extensions/SuggestionMenu/SuggestionPlugin.d.ts +31 -0
  95. package/types/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.d.ts +10 -0
  96. package/types/src/extensions/TableHandles/TableHandlesPlugin.d.ts +7 -7
  97. package/types/src/extensions-shared/UiElementPosition.d.ts +4 -0
  98. package/types/src/index.d.ts +8 -8
  99. package/types/src/pm-nodes/BlockContainer.d.ts +3 -2
  100. package/types/src/pm-nodes/BlockGroup.d.ts +1 -1
  101. package/types/src/schema/blocks/types.d.ts +15 -15
  102. package/types/src/schema/inlineContent/types.d.ts +1 -1
  103. package/types/src/util/browser.d.ts +1 -0
  104. package/types/src/util/typescript.d.ts +1 -0
  105. package/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.ts +0 -335
  106. package/src/extensions/SlashMenu/BaseSlashMenuItem.ts +0 -12
  107. package/src/extensions/SlashMenu/SlashMenuPlugin.ts +0 -53
  108. package/src/extensions-shared/BaseUiElementTypes.ts +0 -8
  109. package/src/extensions-shared/README.md +0 -3
  110. package/src/extensions-shared/suggestion/SuggestionItem.ts +0 -3
  111. package/src/extensions-shared/suggestion/SuggestionPlugin.ts +0 -448
  112. package/types/src/extensions/HyperlinkToolbar/HyperlinkToolbarPlugin.d.ts +0 -38
  113. package/types/src/extensions/ImageToolbar/ImageToolbarPlugin.d.ts +0 -31
  114. package/types/src/extensions/SlashMenu/BaseSlashMenuItem.d.ts +0 -7
  115. package/types/src/extensions/SlashMenu/SlashMenuPlugin.d.ts +0 -13
  116. package/types/src/extensions/SlashMenu/defaultSlashMenuItems.d.ts +0 -3
  117. package/types/src/extensions-shared/BaseUiElementTypes.d.ts +0 -7
  118. package/types/src/extensions-shared/suggestion/SuggestionItem.d.ts +0 -3
  119. package/types/src/extensions-shared/suggestion/SuggestionPlugin.d.ts +0 -36
  120. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-100.woff +0 -0
  121. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-100.woff2 +0 -0
  122. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-200.woff +0 -0
  123. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-200.woff2 +0 -0
  124. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-300.woff +0 -0
  125. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-300.woff2 +0 -0
  126. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-500.woff +0 -0
  127. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-500.woff2 +0 -0
  128. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-600.woff +0 -0
  129. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-600.woff2 +0 -0
  130. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-700.woff +0 -0
  131. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-700.woff2 +0 -0
  132. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-800.woff +0 -0
  133. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-800.woff2 +0 -0
  134. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-900.woff +0 -0
  135. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-900.woff2 +0 -0
  136. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-regular.woff +0 -0
  137. /package/src/{assets → fonts}/inter-v12-latin/inter-v12-latin-regular.woff2 +0 -0
  138. /package/src/{assets/fonts-inter.css → fonts/inter.css} +0 -0
@@ -1,10 +1,10 @@
1
- import { Editor, EditorOptions, Extension } from "@tiptap/core";
1
+ import { EditorOptions, Extension } from "@tiptap/core";
2
2
  import { Node } from "prosemirror-model";
3
3
  // import "./blocknote.css";
4
- import { Editor as TiptapEditor } from "@tiptap/core/dist/packages/core/src/Editor";
5
4
  import * as Y from "yjs";
6
5
  import {
7
6
  insertBlocks,
7
+ insertContentAt,
8
8
  removeBlocks,
9
9
  replaceBlocks,
10
10
  updateBlock,
@@ -13,52 +13,40 @@ import { createExternalHTMLExporter } from "../api/exporters/html/externalHTMLEx
13
13
  import { blocksToMarkdown } from "../api/exporters/markdown/markdownExporter";
14
14
  import { getBlockInfoFromPos } from "../api/getBlockInfoFromPos";
15
15
  import {
16
- blockToNode,
16
+ inlineContentToNodes,
17
17
  nodeToBlock,
18
18
  } from "../api/nodeConversions/nodeConversions";
19
19
  import { getNodeById } from "../api/nodeUtil";
20
20
  import { HTMLToBlocks } from "../api/parsers/html/parseHTML";
21
21
  import { markdownToBlocks } from "../api/parsers/markdown/parseMarkdown";
22
22
  import {
23
+ Block,
23
24
  DefaultBlockSchema,
24
25
  DefaultInlineContentSchema,
25
26
  DefaultStyleSchema,
26
- defaultBlockSchema,
27
- defaultBlockSpecs,
28
- defaultInlineContentSpecs,
29
- defaultStyleSpecs,
27
+ PartialBlock,
30
28
  } from "../blocks/defaultBlocks";
31
29
  import { FormattingToolbarProsemirrorPlugin } from "../extensions/FormattingToolbar/FormattingToolbarPlugin";
32
- import { HyperlinkToolbarProsemirrorPlugin } from "../extensions/HyperlinkToolbar/HyperlinkToolbarPlugin";
33
- import { ImageToolbarProsemirrorPlugin } from "../extensions/ImageToolbar/ImageToolbarPlugin";
30
+ import { LinkToolbarProsemirrorPlugin } from "../extensions/LinkToolbar/LinkToolbarPlugin";
34
31
  import { SideMenuProsemirrorPlugin } from "../extensions/SideMenu/SideMenuPlugin";
35
- import { BaseSlashMenuItem } from "../extensions/SlashMenu/BaseSlashMenuItem";
36
- import { SlashMenuProsemirrorPlugin } from "../extensions/SlashMenu/SlashMenuPlugin";
37
- import { getDefaultSlashMenuItems } from "../extensions/SlashMenu/defaultSlashMenuItems";
32
+ import { SuggestionMenuProseMirrorPlugin } from "../extensions/SuggestionMenu/SuggestionPlugin";
33
+ import { ImagePanelProsemirrorPlugin } from "../extensions/ImagePanel/ImageToolbarPlugin";
38
34
  import { TableHandlesProsemirrorPlugin } from "../extensions/TableHandles/TableHandlesPlugin";
39
35
  import { UniqueID } from "../extensions/UniqueID/UniqueID";
40
36
  import {
41
- Block,
42
37
  BlockIdentifier,
43
38
  BlockNoteDOMAttributes,
44
39
  BlockSchema,
45
- BlockSchemaFromSpecs,
46
- BlockSchemaWithBlock,
47
40
  BlockSpecs,
48
41
  InlineContentSchema,
49
- InlineContentSchemaFromSpecs,
50
42
  InlineContentSpecs,
51
- PartialBlock,
43
+ PartialInlineContent,
52
44
  StyleSchema,
53
- StyleSchemaFromSpecs,
54
45
  StyleSpecs,
55
46
  Styles,
56
- getBlockSchemaFromSpecs,
57
- getInlineContentSchemaFromSpecs,
58
- getStyleSchemaFromSpecs,
59
47
  } from "../schema";
60
48
  import { mergeCSSClasses } from "../util/browser";
61
- import { UnreachableCaseError } from "../util/typescript";
49
+ import { NoInfer, UnreachableCaseError } from "../util/typescript";
62
50
 
63
51
  import { getBlockNoteExtensions } from "./BlockNoteExtensions";
64
52
  import { TextCursorPosition } from "./cursorPositionTypes";
@@ -66,78 +54,41 @@ import { TextCursorPosition } from "./cursorPositionTypes";
66
54
  import { Selection } from "./selectionTypes";
67
55
  import { transformPasted } from "./transformPasted";
68
56
 
57
+ import { checkDefaultBlockTypeInSchema } from "../blocks/defaultBlockTypeGuards";
58
+ import { BlockNoteSchema } from "./BlockNoteSchema";
59
+ import {
60
+ BlockNoteTipTapEditor,
61
+ BlockNoteTipTapEditorOptions,
62
+ } from "./BlockNoteTipTapEditor";
63
+
69
64
  // CSS
70
65
  import "./Block.css";
71
66
  import "./editor.css";
72
67
 
73
68
  export type BlockNoteEditorOptions<
74
- BSpecs extends BlockSpecs,
75
- ISpecs extends InlineContentSpecs,
76
- SSpecs extends StyleSpecs
69
+ BSchema extends BlockSchema,
70
+ ISchema extends InlineContentSchema,
71
+ SSchema extends StyleSchema
77
72
  > = {
78
73
  // TODO: Figure out if enableBlockNoteExtensions/disableHistoryExtension are needed and document them.
79
74
  enableBlockNoteExtensions: boolean;
80
- /**
81
- *
82
- * (couldn't fix any type, see https://github.com/TypeCellOS/BlockNote/pull/191#discussion_r1210708771)
83
- *
84
- * @default defaultSlashMenuItems from `./extensions/SlashMenu`
85
- */
86
- slashMenuItems: BaseSlashMenuItem<any, any, any>[];
87
75
 
88
- /**
89
- * The HTML element that should be used as the parent element for the editor.
90
- *
91
- * @default: undefined, the editor is not attached to the DOM
92
- */
93
- parentElement: HTMLElement;
76
+ placeholders: Record<string | "default", string>;
77
+
94
78
  /**
95
79
  * An object containing attributes that should be added to HTML elements of the editor.
96
80
  *
97
81
  * @example { editor: { class: "my-editor-class" } }
98
82
  */
99
83
  domAttributes: Partial<BlockNoteDOMAttributes>;
100
- /**
101
- * A callback function that runs when the editor is ready to be used.
102
- */
103
- onEditorReady: (
104
- editor: BlockNoteEditor<
105
- BlockSchemaFromSpecs<BSpecs>,
106
- InlineContentSchemaFromSpecs<ISpecs>,
107
- StyleSchemaFromSpecs<SSpecs>
108
- >
109
- ) => void;
110
- /**
111
- * A callback function that runs whenever the editor's contents change.
112
- */
113
- onEditorContentChange: (
114
- editor: BlockNoteEditor<
115
- BlockSchemaFromSpecs<BSpecs>,
116
- InlineContentSchemaFromSpecs<ISpecs>,
117
- StyleSchemaFromSpecs<SSpecs>
118
- >
119
- ) => void;
120
- /**
121
- * A callback function that runs whenever the text cursor position changes.
122
- */
123
- onTextCursorPositionChange: (
124
- editor: BlockNoteEditor<
125
- BlockSchemaFromSpecs<BSpecs>,
126
- InlineContentSchemaFromSpecs<ISpecs>,
127
- StyleSchemaFromSpecs<SSpecs>
128
- >
129
- ) => void;
130
- /**
131
- * Locks the editor from being editable by the user if set to `false`.
132
- */
133
- editable: boolean;
84
+
134
85
  /**
135
86
  * The content that should be in the editor when it's created, represented as an array of partial block objects.
136
87
  */
137
88
  initialContent: PartialBlock<
138
- BlockSchemaFromSpecs<BSpecs>,
139
- InlineContentSchemaFromSpecs<ISpecs>,
140
- StyleSchemaFromSpecs<SSpecs>
89
+ NoInfer<BSchema>,
90
+ NoInfer<ISchema>,
91
+ NoInfer<SSchema>
141
92
  >[];
142
93
  /**
143
94
  * Use default BlockNote font and reset the styles of <p> <li> <h1> elements etc., that are used in BlockNote.
@@ -146,14 +97,7 @@ export type BlockNoteEditorOptions<
146
97
  */
147
98
  defaultStyles: boolean;
148
99
 
149
- /**
150
- * A list of block types that should be available in the editor.
151
- */
152
- blockSpecs: BSpecs;
153
-
154
- styleSpecs: SSpecs;
155
-
156
- inlineContentSpecs: ISpecs;
100
+ schema: BlockNoteSchema<BSchema, ISchema, SSchema>;
157
101
 
158
102
  /**
159
103
  * A custom function to handle file uploads.
@@ -202,110 +146,109 @@ export class BlockNoteEditor<
202
146
  ISchema extends InlineContentSchema = DefaultInlineContentSchema,
203
147
  SSchema extends StyleSchema = DefaultStyleSchema
204
148
  > {
205
- public readonly _tiptapEditor: TiptapEditor & { contentComponent: any };
149
+ public readonly _tiptapEditor: BlockNoteTipTapEditor & {
150
+ contentComponent: any;
151
+ };
206
152
  public blockCache = new WeakMap<Node, Block<any, any, any>>();
207
- public readonly blockSchema: BSchema;
208
- public readonly inlineContentSchema: ISchema;
209
- public readonly styleSchema: SSchema;
153
+ public readonly schema: BlockNoteSchema<BSchema, ISchema, SSchema>;
210
154
 
211
155
  public readonly blockImplementations: BlockSpecs;
212
156
  public readonly inlineContentImplementations: InlineContentSpecs;
213
157
  public readonly styleImplementations: StyleSpecs;
214
158
 
215
- public ready = false;
216
-
217
- public readonly sideMenu: SideMenuProsemirrorPlugin<
159
+ public readonly formattingToolbar: FormattingToolbarProsemirrorPlugin;
160
+ public readonly linkToolbar: LinkToolbarProsemirrorPlugin<
218
161
  BSchema,
219
162
  ISchema,
220
163
  SSchema
221
164
  >;
222
- public readonly formattingToolbar: FormattingToolbarProsemirrorPlugin;
223
- public readonly slashMenu: SlashMenuProsemirrorPlugin<
165
+ public readonly sideMenu: SideMenuProsemirrorPlugin<
224
166
  BSchema,
225
167
  ISchema,
226
- SSchema,
227
- any
168
+ SSchema
228
169
  >;
229
- public readonly hyperlinkToolbar: HyperlinkToolbarProsemirrorPlugin<
170
+ public readonly suggestionMenus: SuggestionMenuProseMirrorPlugin<
230
171
  BSchema,
231
172
  ISchema,
232
173
  SSchema
233
174
  >;
234
- public readonly imageToolbar: ImageToolbarProsemirrorPlugin<
235
- BSchema,
175
+ public readonly imagePanel?: ImagePanelProsemirrorPlugin<ISchema, SSchema>;
176
+ public readonly tableHandles?: TableHandlesProsemirrorPlugin<
236
177
  ISchema,
237
178
  SSchema
238
179
  >;
239
- public readonly tableHandles:
240
- | TableHandlesProsemirrorPlugin<
241
- BSchema extends BlockSchemaWithBlock<
242
- "table",
243
- DefaultBlockSchema["table"]
244
- >
245
- ? BSchema
246
- : any,
247
- ISchema,
248
- SSchema
249
- >
250
- | undefined;
251
180
 
252
181
  public readonly uploadFile: ((file: File) => Promise<string>) | undefined;
253
182
 
254
183
  public static create<
255
- BSpecs extends BlockSpecs = typeof defaultBlockSpecs,
256
- ISpecs extends InlineContentSpecs = typeof defaultInlineContentSpecs,
257
- SSpecs extends StyleSpecs = typeof defaultStyleSpecs
258
- >(options: Partial<BlockNoteEditorOptions<BSpecs, ISpecs, SSpecs>> = {}) {
259
- return new BlockNoteEditor(options) as BlockNoteEditor<
260
- BlockSchemaFromSpecs<BSpecs>,
261
- InlineContentSchemaFromSpecs<ISpecs>,
262
- StyleSchemaFromSpecs<SSpecs>
263
- >;
184
+ BSchema extends BlockSchema = DefaultBlockSchema,
185
+ ISchema extends InlineContentSchema = DefaultInlineContentSchema,
186
+ SSchema extends StyleSchema = DefaultStyleSchema
187
+ >(options: Partial<BlockNoteEditorOptions<BSchema, ISchema, SSchema>> = {}) {
188
+ return new BlockNoteEditor<BSchema, ISchema, SSchema>(options);
264
189
  }
265
190
 
266
191
  private constructor(
267
192
  private readonly options: Partial<BlockNoteEditorOptions<any, any, any>>
268
193
  ) {
194
+ const anyOpts = options as any;
195
+ if (anyOpts.onEditorContentChange) {
196
+ throw new Error(
197
+ "onEditorContentChange initialization option is deprecated, use <BlockNoteView onChange={...} />, the useEditorChange(...) hook, or editor.onChange(...)"
198
+ );
199
+ }
200
+
201
+ if (anyOpts.onTextCursorPositionChange) {
202
+ throw new Error(
203
+ "onTextCursorPositionChange initialization option is deprecated, use <BlockNoteView onSelectionChange={...} />, the useEditorSelectionChange(...) hook, or editor.onSelectionChange(...)"
204
+ );
205
+ }
206
+
207
+ if (anyOpts.onEditorReady) {
208
+ throw new Error(
209
+ "onEditorReady is deprecated. Editor is immediately ready for use after creation."
210
+ );
211
+ }
212
+
213
+ if (anyOpts.editable) {
214
+ throw new Error(
215
+ "editable initialization option is deprecated, use <BlockNoteView editable={true/false} />, or alternatively editor.isEditable = true/false"
216
+ );
217
+ }
218
+
269
219
  // apply defaults
270
220
  const newOptions = {
271
221
  defaultStyles: true,
272
- blockSpecs: options.blockSpecs || defaultBlockSpecs,
273
- styleSpecs: options.styleSpecs || defaultStyleSpecs,
274
- inlineContentSpecs:
275
- options.inlineContentSpecs || defaultInlineContentSpecs,
222
+ schema: options.schema || BlockNoteSchema.create(),
276
223
  ...options,
277
224
  };
278
225
 
279
- this.blockSchema = getBlockSchemaFromSpecs(newOptions.blockSpecs);
280
- this.inlineContentSchema = getInlineContentSchemaFromSpecs(
281
- newOptions.inlineContentSpecs
282
- );
283
- this.styleSchema = getStyleSchemaFromSpecs(newOptions.styleSpecs);
284
- this.blockImplementations = newOptions.blockSpecs;
285
- this.inlineContentImplementations = newOptions.inlineContentSpecs;
286
- this.styleImplementations = newOptions.styleSpecs;
226
+ // @ts-ignore
227
+ this.schema = newOptions.schema;
228
+ this.blockImplementations = newOptions.schema.blockSpecs;
229
+ this.inlineContentImplementations = newOptions.schema.inlineContentSpecs;
230
+ this.styleImplementations = newOptions.schema.styleSpecs;
287
231
 
288
- this.sideMenu = new SideMenuProsemirrorPlugin(this);
289
232
  this.formattingToolbar = new FormattingToolbarProsemirrorPlugin(this);
290
- this.slashMenu = new SlashMenuProsemirrorPlugin(
291
- this,
292
- newOptions.slashMenuItems ||
293
- (getDefaultSlashMenuItems(this.blockSchema) as any)
294
- );
295
- this.hyperlinkToolbar = new HyperlinkToolbarProsemirrorPlugin(this);
296
- this.imageToolbar = new ImageToolbarProsemirrorPlugin(this);
297
-
298
- if (this.blockSchema.table === defaultBlockSchema.table) {
233
+ this.linkToolbar = new LinkToolbarProsemirrorPlugin(this);
234
+ this.sideMenu = new SideMenuProsemirrorPlugin(this);
235
+ this.suggestionMenus = new SuggestionMenuProseMirrorPlugin(this);
236
+ if (checkDefaultBlockTypeInSchema("image", this)) {
237
+ // Type guards only work on `const`s? Not working for `this`
238
+ this.imagePanel = new ImagePanelProsemirrorPlugin(this as any);
239
+ }
240
+ if (checkDefaultBlockTypeInSchema("table", this)) {
299
241
  this.tableHandles = new TableHandlesProsemirrorPlugin(this as any);
300
242
  }
301
243
 
302
244
  const extensions = getBlockNoteExtensions({
303
245
  editor: this,
246
+ placeholders: newOptions.placeholders,
304
247
  domAttributes: newOptions.domAttributes || {},
305
- blockSchema: this.blockSchema,
306
- blockSpecs: newOptions.blockSpecs,
307
- styleSpecs: newOptions.styleSpecs,
308
- inlineContentSpecs: newOptions.inlineContentSpecs,
248
+ blockSchema: this.schema.blockSchema,
249
+ blockSpecs: this.schema.blockSpecs,
250
+ styleSpecs: this.schema.styleSpecs,
251
+ inlineContentSpecs: this.schema.inlineContentSpecs,
309
252
  collaboration: newOptions.collaboration,
310
253
  });
311
254
 
@@ -314,11 +257,11 @@ export class BlockNoteEditor<
314
257
 
315
258
  addProseMirrorPlugins: () => {
316
259
  return [
317
- this.sideMenu.plugin,
318
260
  this.formattingToolbar.plugin,
319
- this.slashMenu.plugin,
320
- this.hyperlinkToolbar.plugin,
321
- this.imageToolbar.plugin,
261
+ this.linkToolbar.plugin,
262
+ this.sideMenu.plugin,
263
+ this.suggestionMenus.plugin,
264
+ ...(this.imagePanel ? [this.imagePanel.plugin] : []),
322
265
  ...(this.tableHandles ? [this.tableHandles.plugin] : []),
323
266
  ];
324
267
  },
@@ -336,95 +279,30 @@ export class BlockNoteEditor<
336
279
  const initialContent =
337
280
  newOptions.initialContent ||
338
281
  (options.collaboration
339
- ? undefined
282
+ ? [
283
+ {
284
+ type: "paragraph",
285
+ id: "initialBlockId",
286
+ },
287
+ ]
340
288
  : [
341
289
  {
342
290
  type: "paragraph",
343
291
  id: UniqueID.options.generateID(),
344
292
  },
345
293
  ]);
346
- const styleSchema = this.styleSchema;
347
294
 
348
- const tiptapOptions: Partial<EditorOptions> = {
295
+ if (!Array.isArray(initialContent) || initialContent.length === 0) {
296
+ throw new Error(
297
+ "initialContent must be a non-empty array of blocks, received: " +
298
+ initialContent
299
+ );
300
+ }
301
+
302
+ const tiptapOptions: BlockNoteTipTapEditorOptions = {
349
303
  ...blockNoteTipTapOptions,
350
304
  ...newOptions._tiptapOptions,
351
- onBeforeCreate(editor) {
352
- newOptions._tiptapOptions?.onBeforeCreate?.(editor);
353
- // We always set the initial content to a single paragraph block. This
354
- // allows us to easily replace it with the actual initial content once
355
- // the TipTap editor is initialized.
356
- const schema = editor.editor.schema;
357
-
358
- // This is a hack to make "initial content detection" by y-prosemirror (and also tiptap isEmpty)
359
- // properly detect whether or not the document has changed.
360
- // We change the doc.createAndFill function to make sure the initial block id is set, instead of null
361
- let cache: any;
362
- const oldCreateAndFill = schema.nodes.doc.createAndFill;
363
- (schema.nodes.doc as any).createAndFill = (...args: any) => {
364
- if (cache) {
365
- return cache;
366
- }
367
- const ret = oldCreateAndFill.apply(schema.nodes.doc, args);
368
-
369
- // create a copy that we can mutate (otherwise, assigning attrs is not safe and corrupts the pm state)
370
- const jsonNode = JSON.parse(JSON.stringify(ret!.toJSON()));
371
- jsonNode.content[0].content[0].attrs.id = "initialBlockId";
372
-
373
- cache = Node.fromJSON(schema, jsonNode);
374
- return cache;
375
- };
376
-
377
- const root = schema.node(
378
- "doc",
379
- undefined,
380
- schema.node("blockGroup", undefined, [
381
- blockToNode(
382
- { id: "initialBlockId", type: "paragraph" },
383
- schema,
384
- styleSchema
385
- ),
386
- ])
387
- );
388
- editor.editor.options.content = root.toJSON();
389
- },
390
- onCreate: (editor) => {
391
- newOptions._tiptapOptions?.onCreate?.(editor);
392
- // We need to wait for the TipTap editor to init before we can set the
393
- // initial content, as the schema may contain custom blocks which need
394
- // it to render.
395
- if (initialContent !== undefined) {
396
- this.replaceBlocks(this.topLevelBlocks, initialContent as any);
397
- }
398
-
399
- newOptions.onEditorReady?.(this);
400
- this.ready = true;
401
- },
402
- onUpdate: (editor) => {
403
- newOptions._tiptapOptions?.onUpdate?.(editor);
404
- // This seems to be necessary due to a bug in TipTap:
405
- // https://github.com/ueberdosis/tiptap/issues/2583
406
- if (!this.ready) {
407
- return;
408
- }
409
-
410
- newOptions.onEditorContentChange?.(this);
411
- },
412
- onSelectionUpdate: (editor) => {
413
- newOptions._tiptapOptions?.onSelectionUpdate?.(editor);
414
- // This seems to be necessary due to a bug in TipTap:
415
- // https://github.com/ueberdosis/tiptap/issues/2583
416
- if (!this.ready) {
417
- return;
418
- }
419
-
420
- newOptions.onTextCursorPositionChange?.(this);
421
- },
422
- editable:
423
- options.editable !== undefined
424
- ? options.editable
425
- : newOptions._tiptapOptions?.editable !== undefined
426
- ? newOptions._tiptapOptions?.editable
427
- : true,
305
+ content: initialContent,
428
306
  extensions:
429
307
  newOptions.enableBlockNoteExtensions === false
430
308
  ? newOptions._tiptapOptions?.extensions || []
@@ -435,7 +313,6 @@ export class BlockNoteEditor<
435
313
  ...newOptions._tiptapOptions?.editorProps?.attributes,
436
314
  ...newOptions.domAttributes?.editor,
437
315
  class: mergeCSSClasses(
438
- "bn-root",
439
316
  "bn-editor",
440
317
  newOptions.defaultStyles ? "bn-default-styles" : "",
441
318
  newOptions.domAttributes?.editor?.class || ""
@@ -445,15 +322,23 @@ export class BlockNoteEditor<
445
322
  },
446
323
  };
447
324
 
448
- if (newOptions.parentElement) {
449
- tiptapOptions.element = newOptions.parentElement;
450
- }
451
-
452
- this._tiptapEditor = new Editor(tiptapOptions) as Editor & {
325
+ this._tiptapEditor = new BlockNoteTipTapEditor(
326
+ tiptapOptions,
327
+ this.schema.styleSchema
328
+ ) as BlockNoteTipTapEditor & {
453
329
  contentComponent: any;
454
330
  };
455
331
  }
456
332
 
333
+ /**
334
+ * Mount the editor to a parent DOM element. Call mount(undefined) to clean up
335
+ *
336
+ * @warning Not needed for React, use BlockNoteView to take care of this
337
+ */
338
+ public mount(parentElement?: HTMLElement | null) {
339
+ this._tiptapEditor.mount(parentElement);
340
+ }
341
+
457
342
  public get prosemirrorView() {
458
343
  return this._tiptapEditor.view;
459
344
  }
@@ -470,20 +355,27 @@ export class BlockNoteEditor<
470
355
  this._tiptapEditor.view.focus();
471
356
  }
472
357
 
358
+ /**
359
+ * @deprecated, use `editor.document` instead
360
+ */
361
+ public get topLevelBlocks(): Block<BSchema, ISchema, SSchema>[] {
362
+ return this.topLevelBlocks;
363
+ }
364
+
473
365
  /**
474
366
  * Gets a snapshot of all top-level (non-nested) blocks in the editor.
475
367
  * @returns A snapshot of all top-level (non-nested) blocks in the editor.
476
368
  */
477
- public get topLevelBlocks(): Block<BSchema, ISchema, SSchema>[] {
369
+ public get document(): Block<BSchema, ISchema, SSchema>[] {
478
370
  const blocks: Block<BSchema, ISchema, SSchema>[] = [];
479
371
 
480
372
  this._tiptapEditor.state.doc.firstChild!.descendants((node) => {
481
373
  blocks.push(
482
374
  nodeToBlock(
483
375
  node,
484
- this.blockSchema,
485
- this.inlineContentSchema,
486
- this.styleSchema,
376
+ this.schema.blockSchema,
377
+ this.schema.inlineContentSchema,
378
+ this.schema.styleSchema,
487
379
  this.blockCache
488
380
  )
489
381
  );
@@ -519,9 +411,9 @@ export class BlockNoteEditor<
519
411
 
520
412
  newBlock = nodeToBlock(
521
413
  node,
522
- this.blockSchema,
523
- this.inlineContentSchema,
524
- this.styleSchema,
414
+ this.schema.blockSchema,
415
+ this.schema.inlineContentSchema,
416
+ this.schema.styleSchema,
525
417
  this.blockCache
526
418
  );
527
419
 
@@ -540,7 +432,7 @@ export class BlockNoteEditor<
540
432
  callback: (block: Block<BSchema, ISchema, SSchema>) => boolean,
541
433
  reverse = false
542
434
  ): void {
543
- const blocks = this.topLevelBlocks.slice();
435
+ const blocks = this.document.slice();
544
436
 
545
437
  if (reverse) {
546
438
  blocks.reverse();
@@ -623,9 +515,9 @@ export class BlockNoteEditor<
623
515
  return {
624
516
  block: nodeToBlock(
625
517
  node,
626
- this.blockSchema,
627
- this.inlineContentSchema,
628
- this.styleSchema,
518
+ this.schema.blockSchema,
519
+ this.schema.inlineContentSchema,
520
+ this.schema.styleSchema,
629
521
  this.blockCache
630
522
  ),
631
523
  prevBlock:
@@ -633,9 +525,9 @@ export class BlockNoteEditor<
633
525
  ? undefined
634
526
  : nodeToBlock(
635
527
  prevNode,
636
- this.blockSchema,
637
- this.inlineContentSchema,
638
- this.styleSchema,
528
+ this.schema.blockSchema,
529
+ this.schema.inlineContentSchema,
530
+ this.schema.styleSchema,
639
531
  this.blockCache
640
532
  ),
641
533
  nextBlock:
@@ -643,9 +535,9 @@ export class BlockNoteEditor<
643
535
  ? undefined
644
536
  : nodeToBlock(
645
537
  nextNode,
646
- this.blockSchema,
647
- this.inlineContentSchema,
648
- this.styleSchema,
538
+ this.schema.blockSchema,
539
+ this.schema.inlineContentSchema,
540
+ this.schema.styleSchema,
649
541
  this.blockCache
650
542
  ),
651
543
  };
@@ -670,7 +562,7 @@ export class BlockNoteEditor<
670
562
  )!;
671
563
 
672
564
  const contentType: "none" | "inline" | "table" =
673
- this.blockSchema[contentNode.type.name]!.content;
565
+ this.schema.blockSchema[contentNode.type.name]!.content;
674
566
 
675
567
  if (contentType === "none") {
676
568
  this._tiptapEditor.commands.setNodeSelection(startPos);
@@ -734,9 +626,9 @@ export class BlockNoteEditor<
734
626
  blocks.push(
735
627
  nodeToBlock(
736
628
  this._tiptapEditor.state.doc.resolve(pos).node(),
737
- this.blockSchema,
738
- this.inlineContentSchema,
739
- this.styleSchema,
629
+ this.schema.blockSchema,
630
+ this.schema.inlineContentSchema,
631
+ this.schema.styleSchema,
740
632
  this.blockCache
741
633
  )
742
634
  );
@@ -760,7 +652,9 @@ export class BlockNoteEditor<
760
652
  * @param editable True to make the editor editable, or false to lock it.
761
653
  */
762
654
  public set isEditable(editable: boolean) {
763
- this._tiptapEditor.setEditable(editable);
655
+ if (this._tiptapEditor.options.editable !== editable) {
656
+ this._tiptapEditor.setEditable(editable);
657
+ }
764
658
  }
765
659
 
766
660
  /**
@@ -815,6 +709,28 @@ export class BlockNoteEditor<
815
709
  return replaceBlocks(blocksToRemove, blocksToInsert, this);
816
710
  }
817
711
 
712
+ /**
713
+ * Insert a piece of content at the current cursor position.
714
+ *
715
+ * @param content can be a string, or array of partial inline content elements
716
+ */
717
+ public insertInlineContent(content: PartialInlineContent<ISchema, SSchema>) {
718
+ const nodes = inlineContentToNodes(
719
+ content,
720
+ this._tiptapEditor.schema,
721
+ this.schema.styleSchema
722
+ );
723
+
724
+ insertContentAt(
725
+ {
726
+ from: this._tiptapEditor.state.selection.from,
727
+ to: this._tiptapEditor.state.selection.to,
728
+ },
729
+ nodes,
730
+ this
731
+ );
732
+ }
733
+
818
734
  /**
819
735
  * Gets the active text styles at the text cursor position or at the end of the current selection if it's active.
820
736
  */
@@ -823,7 +739,7 @@ export class BlockNoteEditor<
823
739
  const marks = this._tiptapEditor.state.selection.$to.marks();
824
740
 
825
741
  for (const mark of marks) {
826
- const config = this.styleSchema[mark.type.name];
742
+ const config = this.schema.styleSchema[mark.type.name];
827
743
  if (!config) {
828
744
  console.warn("mark not found in styleschema", mark.type.name);
829
745
  continue;
@@ -846,7 +762,7 @@ export class BlockNoteEditor<
846
762
  this._tiptapEditor.view.focus();
847
763
 
848
764
  for (const [style, value] of Object.entries(styles)) {
849
- const config = this.styleSchema[style];
765
+ const config = this.schema.styleSchema[style];
850
766
  if (!config) {
851
767
  throw new Error(`style ${style} not found in styleSchema`);
852
768
  }
@@ -880,7 +796,7 @@ export class BlockNoteEditor<
880
796
  this._tiptapEditor.view.focus();
881
797
 
882
798
  for (const [style, value] of Object.entries(styles)) {
883
- const config = this.styleSchema[style];
799
+ const config = this.schema.styleSchema[style];
884
800
  if (!config) {
885
801
  throw new Error(`style ${style} not found in styleSchema`);
886
802
  }
@@ -982,7 +898,7 @@ export class BlockNoteEditor<
982
898
  * @returns The blocks, serialized as an HTML string.
983
899
  */
984
900
  public async blocksToHTMLLossy(
985
- blocks = this.topLevelBlocks
901
+ blocks: Block<BSchema, ISchema, SSchema>[] = this.document
986
902
  ): Promise<string> {
987
903
  const exporter = createExternalHTMLExporter(
988
904
  this._tiptapEditor.schema,
@@ -1003,9 +919,9 @@ export class BlockNoteEditor<
1003
919
  ): Promise<Block<BSchema, ISchema, SSchema>[]> {
1004
920
  return HTMLToBlocks(
1005
921
  html,
1006
- this.blockSchema,
1007
- this.inlineContentSchema,
1008
- this.styleSchema,
922
+ this.schema.blockSchema,
923
+ this.schema.inlineContentSchema,
924
+ this.schema.styleSchema,
1009
925
  this._tiptapEditor.schema
1010
926
  );
1011
927
  }
@@ -1017,7 +933,7 @@ export class BlockNoteEditor<
1017
933
  * @returns The blocks, serialized as a Markdown string.
1018
934
  */
1019
935
  public async blocksToMarkdownLossy(
1020
- blocks: Block<BSchema, ISchema, SSchema>[] = this.topLevelBlocks
936
+ blocks: Block<BSchema, ISchema, SSchema>[] = this.document
1021
937
  ): Promise<string> {
1022
938
  return blocksToMarkdown(blocks, this._tiptapEditor.schema, this);
1023
939
  }
@@ -1034,9 +950,9 @@ export class BlockNoteEditor<
1034
950
  ): Promise<Block<BSchema, ISchema, SSchema>[]> {
1035
951
  return markdownToBlocks(
1036
952
  markdown,
1037
- this.blockSchema,
1038
- this.inlineContentSchema,
1039
- this.styleSchema,
953
+ this.schema.blockSchema,
954
+ this.schema.inlineContentSchema,
955
+ this.schema.styleSchema,
1040
956
  this._tiptapEditor.schema
1041
957
  );
1042
958
  }
@@ -1052,4 +968,44 @@ export class BlockNoteEditor<
1052
968
  }
1053
969
  this._tiptapEditor.commands.updateUser(user);
1054
970
  }
971
+
972
+ /**
973
+ * A callback function that runs whenever the editor's contents change.
974
+ *
975
+ * @param callback The callback to execute.
976
+ * @returns A function to remove the callback.
977
+ */
978
+ public onChange(
979
+ callback: (editor: BlockNoteEditor<BSchema, ISchema, SSchema>) => void
980
+ ) {
981
+ const cb = () => {
982
+ callback(this);
983
+ };
984
+
985
+ this._tiptapEditor.on("update", cb);
986
+
987
+ return () => {
988
+ this._tiptapEditor.off("update", cb);
989
+ };
990
+ }
991
+
992
+ /**
993
+ * A callback function that runs whenever the text cursor position or selection changes.
994
+ *
995
+ * @param callback The callback to execute.
996
+ * @returns A function to remove the callback.
997
+ */
998
+ public onSelectionChange(
999
+ callback: (editor: BlockNoteEditor<BSchema, ISchema, SSchema>) => void
1000
+ ) {
1001
+ const cb = () => {
1002
+ callback(this);
1003
+ };
1004
+
1005
+ this._tiptapEditor.on("selectionUpdate", cb);
1006
+
1007
+ return () => {
1008
+ this._tiptapEditor.off("selectionUpdate", cb);
1009
+ };
1010
+ }
1055
1011
  }