@blocknote/core 0.41.1 → 0.42.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 (81) hide show
  1. package/dist/BlockNoteSchema-Bi-eeHal.js +3288 -0
  2. package/dist/BlockNoteSchema-Bi-eeHal.js.map +1 -0
  3. package/dist/BlockNoteSchema-DjDaA2C3.cjs +6 -0
  4. package/dist/BlockNoteSchema-DjDaA2C3.cjs.map +1 -0
  5. package/dist/blockToNode-DIfPWLH8.js +1140 -0
  6. package/dist/blockToNode-DIfPWLH8.js.map +1 -0
  7. package/dist/blockToNode-w7H99R6p.cjs +7 -0
  8. package/dist/blockToNode-w7H99R6p.cjs.map +1 -0
  9. package/dist/blocknote.cjs +4 -4
  10. package/dist/blocknote.cjs.map +1 -1
  11. package/dist/blocknote.js +1413 -1366
  12. package/dist/blocknote.js.map +1 -1
  13. package/dist/blocks.cjs +1 -1
  14. package/dist/blocks.js +1 -1
  15. package/dist/comments.cjs +1 -1
  16. package/dist/comments.cjs.map +1 -1
  17. package/dist/comments.js +49 -49
  18. package/dist/comments.js.map +1 -1
  19. package/dist/en-Cl87Uuyf.cjs.map +1 -1
  20. package/dist/en-njEqD7AG.js.map +1 -1
  21. package/dist/locales.cjs.map +1 -1
  22. package/dist/locales.js.map +1 -1
  23. package/dist/tsconfig.tsbuildinfo +1 -0
  24. package/dist/webpack-stats.json +1 -1
  25. package/dist/yjs.cjs +2 -0
  26. package/dist/yjs.cjs.map +1 -0
  27. package/dist/yjs.js +44 -0
  28. package/dist/yjs.js.map +1 -0
  29. package/package.json +30 -25
  30. package/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.ts +19 -6
  31. package/src/api/blockManipulation/commands/replaceBlocks/util/fixColumnList.ts +173 -0
  32. package/src/api/nodeConversions/nodeToBlock.ts +17 -14
  33. package/src/blocks/Code/block.ts +1 -0
  34. package/src/blocks/ListItem/NumberedListItem/IndexingPlugin.ts +2 -1
  35. package/src/blocks/defaultBlockTypeGuards.ts +7 -18
  36. package/src/comments/threadstore/yjs/YjsThreadStoreBase.ts +3 -1
  37. package/src/editor/BlockNoteEditor.test.ts +70 -1
  38. package/src/editor/BlockNoteEditor.ts +55 -4
  39. package/src/editor/managers/ExportManager.ts +1 -1
  40. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor-forked.json +2 -2
  41. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor.json +2 -2
  42. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-forked.html +1 -1
  43. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap.html +1 -1
  44. package/src/extensions/Collaboration/schemaMigration/SchemaMigrationPlugin.ts +10 -3
  45. package/src/extensions/Collaboration/schemaMigration/migrationRules/moveColorAttributes.test.ts +130 -0
  46. package/src/extensions/Collaboration/schemaMigration/migrationRules/moveColorAttributes.ts +34 -21
  47. package/src/extensions/Comments/CommentsPlugin.ts +37 -7
  48. package/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts +30 -128
  49. package/src/extensions/SideMenu/SideMenuPlugin.ts +2 -0
  50. package/src/index.ts +1 -0
  51. package/src/schema/inlineContent/createSpec.ts +3 -0
  52. package/src/schema/inlineContent/types.ts +1 -0
  53. package/src/schema/schema.ts +49 -6
  54. package/src/schema/styles/createSpec.ts +6 -0
  55. package/src/schema/styles/types.ts +1 -0
  56. package/src/yjs/index.ts +1 -0
  57. package/src/yjs/utils.test.ts +1023 -0
  58. package/src/yjs/utils.ts +150 -0
  59. package/types/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.d.ts +1 -1
  60. package/types/src/api/blockManipulation/commands/replaceBlocks/util/fixColumnList.d.ts +32 -0
  61. package/types/src/api/nodeConversions/nodeToBlock.d.ts +1 -1
  62. package/types/src/editor/BlockNoteEditor.d.ts +6 -1
  63. package/types/src/editor/managers/ExportManager.d.ts +1 -1
  64. package/types/src/extensions/Collaboration/schemaMigration/migrationRules/moveColorAttributes.test.d.ts +1 -0
  65. package/types/src/extensions/Comments/CommentsPlugin.d.ts +1 -1
  66. package/types/src/index.d.ts +1 -0
  67. package/types/src/schema/inlineContent/createSpec.d.ts +1 -0
  68. package/types/src/schema/inlineContent/types.d.ts +1 -0
  69. package/types/src/schema/styles/createSpec.d.ts +1 -0
  70. package/types/src/schema/styles/types.d.ts +1 -0
  71. package/types/src/yjs/index.d.ts +1 -0
  72. package/types/src/yjs/utils.d.ts +55 -0
  73. package/types/src/yjs/utils.test.d.ts +1 -0
  74. package/dist/BlockNoteSchema-COA0fsXW.cjs +0 -11
  75. package/dist/BlockNoteSchema-COA0fsXW.cjs.map +0 -1
  76. package/dist/BlockNoteSchema-CYRHak18.js +0 -4375
  77. package/dist/BlockNoteSchema-CYRHak18.js.map +0 -1
  78. package/src/extensions/BackgroundColor/BackgroundColorMark.ts +0 -46
  79. package/src/extensions/TextColor/TextColorMark.ts +0 -38
  80. package/types/src/extensions/BackgroundColor/BackgroundColorMark.d.ts +0 -10
  81. package/types/src/extensions/TextColor/TextColorMark.d.ts +0 -10
@@ -1,10 +1,8 @@
1
1
  import { Extension } from "@tiptap/core";
2
2
 
3
3
  import { TextSelection } from "prosemirror-state";
4
- import { ReplaceAroundStep } from "prosemirror-transform";
5
4
  import {
6
5
  getBottomNestedBlockInfo,
7
- getParentBlockInfo,
8
6
  getPrevBlockInfo,
9
7
  mergeBlocksCommand,
10
8
  } from "../../api/blockManipulation/commands/mergeBlocks/mergeBlocks.js";
@@ -13,6 +11,7 @@ import { splitBlockCommand } from "../../api/blockManipulation/commands/splitBlo
13
11
  import { updateBlockCommand } from "../../api/blockManipulation/commands/updateBlock/updateBlock.js";
14
12
  import { getBlockInfoFromSelection } from "../../api/getBlockInfoFromPos.js";
15
13
  import { BlockNoteEditor } from "../../editor/BlockNoteEditor.js";
14
+ import { fixColumnList } from "../../api/blockManipulation/commands/replaceBlocks/util/fixColumnList.js";
16
15
 
17
16
  export const KeyboardShortcutsExtension = Extension.create<{
18
17
  editor: BlockNoteEditor<any, any, any>;
@@ -98,7 +97,7 @@ export const KeyboardShortcutsExtension = Extension.create<{
98
97
  return false;
99
98
  }),
100
99
  () =>
101
- commands.command(({ state, dispatch }) => {
100
+ commands.command(({ state, tr, dispatch }) => {
102
101
  // when at the start of a first block in a column
103
102
  const blockInfo = getBlockInfoFromSelection(state);
104
103
  if (!blockInfo.isBlockContainer) {
@@ -106,151 +105,54 @@ export const KeyboardShortcutsExtension = Extension.create<{
106
105
  }
107
106
 
108
107
  const selectionAtBlockStart =
109
- state.selection.from === blockInfo.blockContent.beforePos + 1;
110
-
108
+ tr.selection.from === blockInfo.blockContent.beforePos + 1;
111
109
  if (!selectionAtBlockStart) {
112
110
  return false;
113
111
  }
114
112
 
115
- const prevBlockInfo = getPrevBlockInfo(
116
- state.doc,
117
- blockInfo.bnBlock.beforePos,
118
- );
113
+ const $pos = tr.doc.resolve(blockInfo.bnBlock.beforePos);
119
114
 
120
- if (prevBlockInfo) {
115
+ const prevBlock = $pos.nodeBefore;
116
+ if (prevBlock) {
121
117
  // should be no previous block
122
118
  return false;
123
119
  }
124
120
 
125
- const parentBlockInfo = getParentBlockInfo(
126
- state.doc,
127
- blockInfo.bnBlock.beforePos,
128
- );
129
-
130
- if (parentBlockInfo?.blockNoteType !== "column") {
121
+ const parentBlock = $pos.node();
122
+ if (parentBlock.type.name !== "column") {
131
123
  return false;
132
124
  }
133
125
 
134
- const column = parentBlockInfo;
135
-
136
- const columnList = getParentBlockInfo(
137
- state.doc,
138
- column.bnBlock.beforePos,
139
- );
140
- if (columnList?.blockNoteType !== "columnList") {
141
- throw new Error("parent of column is not a column list");
142
- }
143
-
144
- const shouldRemoveColumn =
145
- column.childContainer!.node.childCount === 1;
146
-
147
- const shouldRemoveColumnList =
148
- shouldRemoveColumn &&
149
- columnList.childContainer!.node.childCount === 2;
150
-
151
- const isFirstColumn =
152
- columnList.childContainer!.node.firstChild ===
153
- column.bnBlock.node;
126
+ const $blockPos = tr.doc.resolve(blockInfo.bnBlock.beforePos);
127
+ const $columnPos = tr.doc.resolve($blockPos.before());
128
+ const columnListPos = $columnPos.before();
154
129
 
155
130
  if (dispatch) {
156
- const blockToMove = state.doc.slice(
131
+ const fragment = tr.doc.slice(
157
132
  blockInfo.bnBlock.beforePos,
158
133
  blockInfo.bnBlock.afterPos,
159
- false,
160
- );
134
+ ).content;
161
135
 
162
- /*
163
- There are 3 different cases:
164
- a) remove entire column list (if no columns would be remaining)
165
- b) remove just a column (if no blocks inside a column would be remaining)
166
- c) keep columns (if there are blocks remaining inside a column)
167
-
168
- Each of these 3 cases has 2 sub-cases, depending on whether the backspace happens at the start of the first (most-left) column,
169
- or at the start of a non-first column.
170
- */
171
- if (shouldRemoveColumnList) {
172
- if (isFirstColumn) {
173
- state.tr.step(
174
- new ReplaceAroundStep(
175
- // replace entire column list
176
- columnList.bnBlock.beforePos,
177
- columnList.bnBlock.afterPos,
178
- // select content of remaining column:
179
- column.bnBlock.afterPos + 1,
180
- columnList.bnBlock.afterPos - 2,
181
- blockToMove,
182
- blockToMove.size, // append existing content to blockToMove
183
- false,
184
- ),
185
- );
186
- const pos = state.tr.doc.resolve(column.bnBlock.beforePos);
187
- state.tr.setSelection(TextSelection.between(pos, pos));
188
- } else {
189
- // replaces the column list with the blockToMove slice, prepended with the content of the remaining column
190
- state.tr.step(
191
- new ReplaceAroundStep(
192
- // replace entire column list
193
- columnList.bnBlock.beforePos,
194
- columnList.bnBlock.afterPos,
195
- // select content of existing column:
196
- columnList.bnBlock.beforePos + 2,
197
- column.bnBlock.beforePos - 1,
198
- blockToMove,
199
- 0, // prepend existing content to blockToMove
200
- false,
201
- ),
202
- );
203
- const pos = state.tr.doc.resolve(
204
- state.tr.mapping.map(column.bnBlock.beforePos - 1),
205
- );
206
- state.tr.setSelection(TextSelection.between(pos, pos));
207
- }
208
- } else if (shouldRemoveColumn) {
209
- if (isFirstColumn) {
210
- // delete column
211
- state.tr.delete(
212
- column.bnBlock.beforePos,
213
- column.bnBlock.afterPos,
214
- );
215
-
216
- // move before columnlist
217
- state.tr.insert(
218
- columnList.bnBlock.beforePos,
219
- blockToMove.content,
220
- );
136
+ tr.delete(
137
+ blockInfo.bnBlock.beforePos,
138
+ blockInfo.bnBlock.afterPos,
139
+ );
221
140
 
222
- const pos = state.tr.doc.resolve(
223
- columnList.bnBlock.beforePos,
224
- );
225
- state.tr.setSelection(TextSelection.between(pos, pos));
226
- } else {
227
- // just delete the </column><column> closing and opening tags to merge the columns
228
- state.tr.delete(
229
- column.bnBlock.beforePos - 1,
230
- column.bnBlock.beforePos + 1,
231
- );
232
- }
141
+ if ($columnPos.index() === 0) {
142
+ // Fix `columnList` and insert the block before it.
143
+ fixColumnList(tr, columnListPos);
144
+ tr.insert(columnListPos, fragment);
145
+ tr.setSelection(
146
+ TextSelection.near(tr.doc.resolve(columnListPos)),
147
+ );
233
148
  } else {
234
- // delete block
235
- state.tr.delete(
236
- blockInfo.bnBlock.beforePos,
237
- blockInfo.bnBlock.afterPos,
149
+ // Insert the block at the end of the first column and fix
150
+ // `columnList`.
151
+ tr.insert($columnPos.pos - 1, fragment);
152
+ tr.setSelection(
153
+ TextSelection.near(tr.doc.resolve($columnPos.pos - 1)),
238
154
  );
239
- if (isFirstColumn) {
240
- // move before columnlist
241
- state.tr.insert(
242
- columnList.bnBlock.beforePos - 1,
243
- blockToMove.content,
244
- );
245
- } else {
246
- // append block to previous column
247
- state.tr.insert(
248
- column.bnBlock.beforePos - 1,
249
- blockToMove.content,
250
- );
251
- }
252
- const pos = state.tr.doc.resolve(column.bnBlock.beforePos - 1);
253
- state.tr.setSelection(TextSelection.between(pos, pos));
155
+ fixColumnList(tr, columnListPos);
254
156
  }
255
157
  }
256
158
 
@@ -738,6 +738,8 @@ export class SideMenuProsemirrorPlugin<
738
738
  if (this.view) {
739
739
  this.view.isDragOrigin = false;
740
740
  }
741
+
742
+ this.editor.blur();
741
743
  };
742
744
  /**
743
745
  * Freezes the side menu. When frozen, the side menu will stay
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from "./api/blockManipulation/commands/insertBlocks/insertBlocks.js";
2
2
  export * from "./api/blockManipulation/commands/replaceBlocks/replaceBlocks.js";
3
3
  export * from "./api/blockManipulation/commands/updateBlock/updateBlock.js";
4
+ export * from "./api/blockManipulation/commands/replaceBlocks/util/fixColumnList.js";
4
5
  export * from "./api/exporters/html/externalHTMLExporter.js";
5
6
  export * from "./api/exporters/html/internalHTMLSerializer.js";
6
7
  export * from "./api/getBlockInfoFromPos.js";
@@ -81,6 +81,8 @@ export type CustomInlineContentImplementation<
81
81
  contentDOM?: HTMLElement;
82
82
  }
83
83
  | undefined;
84
+
85
+ runsBefore?: string[];
84
86
  };
85
87
 
86
88
  export function getInlineContentParseRules<C extends CustomInlineContentConfig>(
@@ -220,6 +222,7 @@ export function createInlineContentSpec<
220
222
  node,
221
223
  inlineContentConfig.propSchema,
222
224
  {
225
+ ...inlineContentImplementation,
223
226
  toExternalHTML: inlineContentImplementation.toExternalHTML,
224
227
  render(inlineContent, updateInlineContent, editor) {
225
228
  const output = inlineContentImplementation.render(
@@ -43,6 +43,7 @@ export type InlineContentImplementation<T extends InlineContentConfig> =
43
43
  ignoreMutation?: (mutation: ViewMutationRecord) => boolean;
44
44
  destroy?: () => void;
45
45
  };
46
+ runsBefore?: string[];
46
47
  };
47
48
 
48
49
  export type InlineContentSchemaWithInlineContent<
@@ -84,8 +84,12 @@ export class CustomBlockNoteSchema<
84
84
  const defaultSet = new Set<string>();
85
85
  dag.set("default", defaultSet);
86
86
 
87
- for (const [key, specDef] of Object.entries(this.opts.blockSpecs)) {
88
- if (specDef.implementation.runsBefore) {
87
+ for (const [key, specDef] of Object.entries({
88
+ ...this.opts.blockSpecs,
89
+ ...this.opts.inlineContentSpecs,
90
+ ...this.opts.styleSpecs,
91
+ })) {
92
+ if (specDef.implementation?.runsBefore) {
89
93
  dag.set(key, new Set(specDef.implementation.runsBefore));
90
94
  } else {
91
95
  defaultSet.add(key);
@@ -130,6 +134,45 @@ export class CustomBlockNoteSchema<
130
134
  : never;
131
135
  };
132
136
 
137
+ const inlineContentSpecs = Object.fromEntries(
138
+ Object.entries(this.opts.inlineContentSpecs).map(
139
+ ([key, inlineContentSpec]) => {
140
+ // Case for text and links.
141
+ if (typeof inlineContentSpec.config !== "object") {
142
+ return [key, inlineContentSpec];
143
+ }
144
+
145
+ return [
146
+ key,
147
+ {
148
+ ...inlineContentSpec,
149
+ implementation: {
150
+ ...inlineContentSpec.implementation,
151
+ node: inlineContentSpec.implementation?.node.extend({
152
+ priority: getPriority(key),
153
+ }),
154
+ },
155
+ },
156
+ ];
157
+ },
158
+ ),
159
+ ) as InlineContentSpecs;
160
+
161
+ const styleSpecs = Object.fromEntries(
162
+ Object.entries(this.opts.styleSpecs).map(([key, styleSpec]) => [
163
+ key,
164
+ {
165
+ ...styleSpec,
166
+ implementation: {
167
+ ...styleSpec.implementation,
168
+ mark: styleSpec.implementation?.mark.extend({
169
+ priority: getPriority(key),
170
+ }),
171
+ },
172
+ },
173
+ ]),
174
+ ) as StyleSpecs;
175
+
133
176
  return {
134
177
  blockSpecs,
135
178
  blockSchema: Object.fromEntries(
@@ -137,12 +180,12 @@ export class CustomBlockNoteSchema<
137
180
  return [key, blockDef.config];
138
181
  }),
139
182
  ) as any,
140
- inlineContentSpecs: removeUndefined(this.opts.inlineContentSpecs),
141
- styleSpecs: removeUndefined(this.opts.styleSpecs),
183
+ inlineContentSpecs: removeUndefined(inlineContentSpecs),
184
+ styleSpecs: removeUndefined(styleSpecs),
142
185
  inlineContentSchema: getInlineContentSchemaFromSpecs(
143
- this.opts.inlineContentSpecs,
186
+ inlineContentSpecs,
144
187
  ) as any,
145
- styleSchema: getStyleSchemaFromSpecs(this.opts.styleSpecs) as any,
188
+ styleSchema: getStyleSchemaFromSpecs(styleSpecs) as any,
146
189
  };
147
190
  }
148
191
 
@@ -22,6 +22,7 @@ export type CustomStyleImplementation<T extends StyleConfig> = {
22
22
  parse?: (
23
23
  element: HTMLElement,
24
24
  ) => (T["propSchema"] extends "boolean" ? true : string) | undefined;
25
+ runsBefore?: string[];
25
26
  };
26
27
 
27
28
  export function getStyleParseRules<T extends StyleConfig>(
@@ -46,6 +47,10 @@ export function getStyleParseRules<T extends StyleConfig>(
46
47
  if (customParseFunction) {
47
48
  rules.push({
48
49
  tag: "*",
50
+ // By default, styles can overlap each other, so the rules should not
51
+ // completely consume the element they parse (which can have multiple
52
+ // styles).
53
+ consuming: false,
49
54
  getAttrs(node: string | HTMLElement) {
50
55
  if (typeof node === "string") {
51
56
  return false;
@@ -107,6 +112,7 @@ export function createStyleSpec<const T extends StyleConfig>(
107
112
  });
108
113
 
109
114
  return createInternalStyleSpec(styleConfig, {
115
+ ...styleImplementation,
110
116
  mark,
111
117
  render: (value) => {
112
118
  const renderResult = styleImplementation.render(value as any);
@@ -28,6 +28,7 @@ export type StyleImplementation<T extends StyleConfig> = {
28
28
  dom: HTMLElement;
29
29
  contentDOM?: HTMLElement;
30
30
  };
31
+ runsBefore?: string[];
31
32
  };
32
33
 
33
34
  // Container for both the config and implementation of a Style,
@@ -0,0 +1 @@
1
+ export * from "./utils.js";