@blocknote/core 0.30.1 → 0.31.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 (139) hide show
  1. package/dist/blocknote.cjs +9 -9
  2. package/dist/blocknote.cjs.map +1 -1
  3. package/dist/blocknote.js +2754 -2230
  4. package/dist/blocknote.js.map +1 -1
  5. package/dist/{en-D4taoCs4.cjs → en-BXVKCwYt.cjs} +2 -2
  6. package/dist/en-BXVKCwYt.cjs.map +1 -0
  7. package/dist/{en-B7ycW7c8.js → en-qGo6sk9V.js} +2 -3
  8. package/dist/en-qGo6sk9V.js.map +1 -0
  9. package/dist/locales.cjs +1 -1
  10. package/dist/locales.cjs.map +1 -1
  11. package/dist/locales.js +20 -39
  12. package/dist/locales.js.map +1 -1
  13. package/dist/style.css +1 -1
  14. package/dist/webpack-stats.json +1 -1
  15. package/package.json +4 -5
  16. package/src/api/blockManipulation/commands/insertBlocks/insertBlocks.ts +2 -3
  17. package/src/api/blockManipulation/commands/replaceBlocks/replaceBlocks.ts +1 -1
  18. package/src/api/blockManipulation/commands/updateBlock/__snapshots__/updateBlock.test.ts.snap +2816 -0
  19. package/src/api/blockManipulation/commands/updateBlock/updateBlock.test.ts +158 -0
  20. package/src/api/blockManipulation/commands/updateBlock/updateBlock.ts +87 -17
  21. package/src/api/blockManipulation/selections/selection.ts +48 -1
  22. package/src/api/blockManipulation/selections/{textCursorPosition/textCursorPosition.ts → textCursorPosition.ts} +7 -7
  23. package/src/api/getBlockInfoFromPos.ts +1 -1
  24. package/src/api/nodeConversions/blockToNode.ts +5 -2
  25. package/src/api/nodeConversions/nodeToBlock.ts +203 -8
  26. package/src/api/pmUtil.ts +3 -3
  27. package/src/blocks/CodeBlockContent/CodeBlockContent.ts +6 -6
  28. package/src/blocks/FileBlockContent/helpers/render/createAddFileButton.ts +1 -1
  29. package/src/blocks/TableBlockContent/TableBlockContent.ts +32 -2
  30. package/src/editor/Block.css +27 -1
  31. package/src/editor/BlockNoteEditor.test.ts +7 -0
  32. package/src/editor/BlockNoteEditor.ts +88 -37
  33. package/src/editor/BlockNoteExtension.ts +26 -0
  34. package/src/editor/BlockNoteExtensions.ts +28 -12
  35. package/src/editor/BlockNoteTipTapEditor.ts +23 -2
  36. package/src/extensions/Collaboration/CursorPlugin.ts +13 -7
  37. package/src/extensions/Collaboration/ForkYDocPlugin.test.ts +166 -0
  38. package/src/extensions/Collaboration/ForkYDocPlugin.ts +174 -0
  39. package/src/extensions/Collaboration/SyncPlugin.ts +7 -4
  40. package/src/extensions/Collaboration/UndoPlugin.ts +7 -4
  41. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor-forked.json +30 -0
  42. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-editor.json +30 -0
  43. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap-forked.html +1 -0
  44. package/src/extensions/Collaboration/__snapshots__/fork-yjs-snap.html +1 -0
  45. package/src/extensions/Comments/CommentsPlugin.ts +75 -70
  46. package/src/extensions/FilePanel/FilePanelPlugin.ts +50 -49
  47. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +56 -26
  48. package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +22 -21
  49. package/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.ts +45 -42
  50. package/src/extensions/Placeholder/PlaceholderPlugin.ts +111 -108
  51. package/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.ts +179 -170
  52. package/src/extensions/ShowSelection/ShowSelectionPlugin.ts +22 -19
  53. package/src/extensions/SideMenu/SideMenuPlugin.ts +19 -18
  54. package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +168 -168
  55. package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +4 -4
  56. package/src/extensions/Suggestions/SuggestionMarks.ts +175 -0
  57. package/src/extensions/TableHandles/TableHandlesPlugin.ts +153 -150
  58. package/src/i18n/locales/ar.ts +0 -1
  59. package/src/i18n/locales/de.ts +0 -1
  60. package/src/i18n/locales/en.ts +0 -1
  61. package/src/i18n/locales/es.ts +0 -1
  62. package/src/i18n/locales/fr.ts +0 -1
  63. package/src/i18n/locales/hr.ts +0 -1
  64. package/src/i18n/locales/is.ts +0 -1
  65. package/src/i18n/locales/it.ts +0 -1
  66. package/src/i18n/locales/ja.ts +0 -1
  67. package/src/i18n/locales/ko.ts +0 -1
  68. package/src/i18n/locales/nl.ts +0 -1
  69. package/src/i18n/locales/no.ts +0 -1
  70. package/src/i18n/locales/pl.ts +0 -1
  71. package/src/i18n/locales/pt.ts +0 -1
  72. package/src/i18n/locales/ru.ts +0 -1
  73. package/src/i18n/locales/sk.ts +0 -1
  74. package/src/i18n/locales/uk.ts +0 -1
  75. package/src/i18n/locales/vi.ts +0 -1
  76. package/src/i18n/locales/zh-tw.ts +0 -1
  77. package/src/i18n/locales/zh.ts +0 -1
  78. package/src/index.ts +18 -8
  79. package/src/pm-nodes/BlockContainer.ts +1 -1
  80. package/src/pm-nodes/BlockGroup.ts +1 -1
  81. package/src/pm-nodes/Doc.ts +1 -0
  82. package/types/src/api/blockManipulation/commands/insertBlocks/insertBlocks.d.ts +1 -1
  83. package/types/src/api/blockManipulation/commands/removeBlocks/removeBlocks.d.ts +4 -0
  84. package/types/src/api/blockManipulation/commands/removeBlocks/removeBlocks.test.d.ts +1 -0
  85. package/types/src/api/blockManipulation/commands/updateBlock/updateBlock.d.ts +3 -1
  86. package/types/src/api/blockManipulation/selections/selection.d.ts +10 -0
  87. package/types/src/api/blockManipulation/selections/textCursorPosition.d.ts +5 -0
  88. package/types/src/api/blockManipulation/transactions.test.d.ts +0 -0
  89. package/types/src/api/clipboard/clipboardExternal.test.d.ts +1 -0
  90. package/types/src/api/clipboard/clipboardInternal.test.d.ts +1 -0
  91. package/types/src/api/clipboard/testUtil.d.ts +541 -0
  92. package/types/src/api/exporters/html/htmlConversion.test.d.ts +1 -0
  93. package/types/src/api/exporters/markdown/markdownExporter.test.d.ts +1 -0
  94. package/types/src/api/nodeConversions/nodeConversions.test.d.ts +1 -0
  95. package/types/src/api/nodeConversions/nodeToBlock.d.ts +39 -2
  96. package/types/src/api/parsers/html/parseHTML.test.d.ts +1 -0
  97. package/types/src/api/parsers/markdown/parseMarkdown.test.d.ts +1 -0
  98. package/types/src/api/pmUtil.d.ts +3 -3
  99. package/types/src/api/testUtil/cases/customBlocks.d.ts +670 -0
  100. package/types/src/api/testUtil/cases/customInlineContent.d.ts +558 -0
  101. package/types/src/api/testUtil/cases/customStyles.d.ts +552 -0
  102. package/types/src/api/testUtil/cases/defaultSchema.d.ts +4 -0
  103. package/types/src/api/testUtil/index.d.ts +14 -0
  104. package/types/src/api/testUtil/partialBlockTestUtil.d.ts +9 -0
  105. package/types/src/api/testUtil/paste.d.ts +2 -0
  106. package/types/src/blocks/CodeBlockContent/defaultSupportedLanguages.d.ts +6 -0
  107. package/types/src/blocks/TableBlockContent/TableBlockContent.d.ts +9 -1
  108. package/types/src/editor/BlockNoteEditor.d.ts +55 -9
  109. package/types/src/editor/BlockNoteExtension.d.ts +9 -0
  110. package/types/src/editor/BlockNoteExtensions.d.ts +2 -2
  111. package/types/src/editor/BlockNoteTipTapEditor.d.ts +2 -2
  112. package/types/src/extensions/Collaboration/CursorPlugin.d.ts +3 -3
  113. package/types/src/extensions/Collaboration/ForkYDocPlugin.d.ts +41 -0
  114. package/types/src/extensions/Collaboration/ForkYDocPlugin.test.d.ts +1 -0
  115. package/types/src/extensions/Collaboration/SyncPlugin.d.ts +3 -3
  116. package/types/src/extensions/Collaboration/UndoPlugin.d.ts +3 -3
  117. package/types/src/extensions/Collaboration/createCollaborationExtensions.d.ts +17 -0
  118. package/types/src/extensions/Comments/CommentsPlugin.d.ts +2 -4
  119. package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +3 -4
  120. package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +5 -5
  121. package/types/src/extensions/LinkToolbar/LinkToolbarPlugin.d.ts +3 -4
  122. package/types/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.d.ts +2 -3
  123. package/types/src/extensions/Placeholder/PlaceholderPlugin.d.ts +2 -3
  124. package/types/src/extensions/PreviousBlockType/PreviousBlockTypePlugin.d.ts +2 -3
  125. package/types/src/extensions/ShowSelection/ShowSelectionPlugin.d.ts +2 -3
  126. package/types/src/extensions/SideMenu/SideMenuPlugin.d.ts +3 -4
  127. package/types/src/extensions/SuggestionMenu/SuggestionPlugin.d.ts +2 -4
  128. package/types/src/extensions/Suggestions/SuggestionMarks.d.ts +4 -0
  129. package/types/src/extensions/TableHandles/TableHandlesPlugin.d.ts +5 -6
  130. package/types/src/i18n/locales/en.d.ts +0 -1
  131. package/types/src/i18n/locales/sk.d.ts +0 -1
  132. package/types/src/index.d.ts +15 -8
  133. package/dist/en-B7ycW7c8.js.map +0 -1
  134. package/dist/en-D4taoCs4.cjs.map +0 -1
  135. package/dist/tsconfig.tsbuildinfo +0 -1
  136. package/src/api/blockManipulation/selections/__snapshots__/selection.test.ts.snap +0 -844
  137. package/src/api/blockManipulation/selections/selection.test.ts +0 -72
  138. package/src/api/blockManipulation/selections/textCursorPosition/__snapshots__/textCursorPosition.test.ts.snap +0 -316
  139. package/src/api/blockManipulation/selections/textCursorPosition/textCursorPosition.test.ts +0 -74
@@ -1,5 +1,7 @@
1
1
  import { describe, expect, it } from "vitest";
2
2
 
3
+ import { getBlockInfo } from "../../../getBlockInfoFromPos.js";
4
+ import { getNodeById } from "../../../nodeUtil.js";
3
5
  import { setupTestEnv } from "../../setupTestEnv.js";
4
6
  import { updateBlock } from "./updateBlock.js";
5
7
 
@@ -173,6 +175,162 @@ describe("Test updateBlock", () => {
173
175
  expect(getEditor().document).toMatchSnapshot();
174
176
  });
175
177
 
178
+ it("Update partial (offset start)", () => {
179
+ const info = getBlockInfo(
180
+ getNodeById("heading-with-everything", getEditor().prosemirrorState.doc)!,
181
+ );
182
+
183
+ if (!info.isBlockContainer) {
184
+ throw new Error("heading-with-everything is not a block container");
185
+ }
186
+
187
+ getEditor().transact((tr) =>
188
+ updateBlock(
189
+ tr,
190
+ "heading-with-everything",
191
+ {
192
+ content: [
193
+ {
194
+ type: "text",
195
+ text: "without styles",
196
+ styles: {},
197
+ },
198
+ ],
199
+ },
200
+ info.blockContent.beforePos + 9,
201
+ ),
202
+ );
203
+
204
+ expect(getEditor().document).toMatchSnapshot();
205
+ });
206
+
207
+ it("Update partial (offset start + end)", () => {
208
+ const info = getBlockInfo(
209
+ getNodeById("heading-with-everything", getEditor().prosemirrorState.doc)!,
210
+ );
211
+
212
+ if (!info.isBlockContainer) {
213
+ throw new Error("heading-with-everything is not a block container");
214
+ }
215
+
216
+ getEditor().transact((tr) =>
217
+ updateBlock(
218
+ tr,
219
+ "heading-with-everything",
220
+ {
221
+ content: [
222
+ {
223
+ type: "text",
224
+ text: "without styles and ",
225
+ styles: {},
226
+ },
227
+ ],
228
+ },
229
+ info.blockContent.beforePos + 9,
230
+ info.blockContent.beforePos + 9,
231
+ ),
232
+ );
233
+
234
+ expect(getEditor().document).toMatchSnapshot();
235
+ });
236
+
237
+ it("Update partial (props + offset end)", () => {
238
+ const info = getBlockInfo(
239
+ getNodeById("heading-with-everything", getEditor().prosemirrorState.doc)!,
240
+ );
241
+
242
+ if (!info.isBlockContainer) {
243
+ throw new Error("heading-with-everything is not a block container");
244
+ }
245
+
246
+ getEditor().transact((tr) => {
247
+ updateBlock(
248
+ tr,
249
+ "heading-with-everything",
250
+ {
251
+ props: {
252
+ level: 1,
253
+ },
254
+ content: [
255
+ {
256
+ type: "text",
257
+ text: "Title",
258
+ styles: {},
259
+ },
260
+ ],
261
+ },
262
+ undefined,
263
+ info.blockContent.beforePos + 8,
264
+ );
265
+ });
266
+
267
+ expect(getEditor().document).toMatchSnapshot();
268
+ });
269
+
270
+ it("Update partial (table cell)", () => {
271
+ const info = getBlockInfo(
272
+ getNodeById("table-0", getEditor().prosemirrorState.doc)!,
273
+ );
274
+
275
+ if (!info.isBlockContainer) {
276
+ throw new Error("table-0 is not a block container");
277
+ }
278
+
279
+ const cell = info.blockContent.node.resolve(2);
280
+
281
+ getEditor().transact((tr) =>
282
+ updateBlock(
283
+ tr,
284
+ "table-0",
285
+ {
286
+ type: "table",
287
+ content: {
288
+ type: "tableContent",
289
+ rows: [{ cells: ["updated cell 1"] }],
290
+ },
291
+ },
292
+ info.blockContent.beforePos + 2,
293
+ info.blockContent.beforePos + 2 + cell.node().nodeSize,
294
+ ),
295
+ );
296
+
297
+ expect(getEditor().document).toMatchSnapshot();
298
+ });
299
+
300
+ it("Update partial (table row)", () => {
301
+ const info = getBlockInfo(
302
+ getNodeById("table-0", getEditor().prosemirrorState.doc)!,
303
+ );
304
+
305
+ if (!info.isBlockContainer) {
306
+ throw new Error("table-0 is not a block container");
307
+ }
308
+
309
+ const cell = info.blockContent.node.resolve(1);
310
+
311
+ getEditor().transact((tr) =>
312
+ updateBlock(
313
+ tr,
314
+ "table-0",
315
+ {
316
+ type: "table",
317
+ content: {
318
+ type: "tableContent",
319
+ rows: [
320
+ {
321
+ cells: ["updated cell 1", "updated cell 2", "updated cell 3"],
322
+ },
323
+ ],
324
+ },
325
+ },
326
+ info.blockContent.beforePos + 1,
327
+ info.blockContent.beforePos + 1 + cell.node().nodeSize,
328
+ ),
329
+ );
330
+
331
+ expect(getEditor().document).toMatchSnapshot();
332
+ });
333
+
176
334
  it("Update children", () => {
177
335
  expect(
178
336
  getEditor().transact((tr) =>
@@ -6,7 +6,7 @@ import {
6
6
  } from "prosemirror-model";
7
7
  import type { Transaction } from "prosemirror-state";
8
8
 
9
- import { ReplaceStep } from "prosemirror-transform";
9
+ import { ReplaceStep, Transform } from "prosemirror-transform";
10
10
  import type { Block, PartialBlock } from "../../../../blocks/defaultBlocks.js";
11
11
  import type {
12
12
  BlockIdentifier,
@@ -28,6 +28,7 @@ import { nodeToBlock } from "../../../nodeConversions/nodeToBlock.js";
28
28
  import { getNodeById } from "../../../nodeUtil.js";
29
29
  import { getPmSchema } from "../../../pmUtil.js";
30
30
 
31
+ // for compatibility with tiptap. TODO: remove as we want to remove dependency on tiptap command interface
31
32
  export const updateBlockCommand = <
32
33
  BSchema extends BlockSchema,
33
34
  I extends InlineContentSchema,
@@ -50,18 +51,29 @@ export const updateBlockCommand = <
50
51
  };
51
52
  };
52
53
 
53
- const updateBlockTr = <
54
+ export function updateBlockTr<
54
55
  BSchema extends BlockSchema,
55
56
  I extends InlineContentSchema,
56
57
  S extends StyleSchema,
57
58
  >(
58
- tr: Transaction,
59
+ tr: Transform,
59
60
  posBeforeBlock: number,
60
61
  block: PartialBlock<BSchema, I, S>,
61
- ) => {
62
+ replaceFromPos?: number,
63
+ replaceToPos?: number,
64
+ ) {
62
65
  const blockInfo = getBlockInfoFromResolvedPos(tr.doc.resolve(posBeforeBlock));
63
66
 
64
67
  const pmSchema = getPmSchema(tr);
68
+
69
+ if (
70
+ replaceFromPos !== undefined &&
71
+ replaceToPos !== undefined &&
72
+ replaceFromPos > replaceToPos
73
+ ) {
74
+ throw new Error("Invalid replaceFromPos or replaceToPos");
75
+ }
76
+
65
77
  // Adds blockGroup node with child blocks if necessary.
66
78
 
67
79
  const oldNodeType = pmSchema.nodes[blockInfo.blockNoteType];
@@ -71,10 +83,32 @@ const updateBlockTr = <
71
83
  : pmSchema.nodes["blockContainer"];
72
84
 
73
85
  if (blockInfo.isBlockContainer && newNodeType.isInGroup("blockContent")) {
86
+ const replaceFromOffset =
87
+ replaceFromPos !== undefined &&
88
+ replaceFromPos > blockInfo.blockContent.beforePos &&
89
+ replaceFromPos < blockInfo.blockContent.afterPos
90
+ ? replaceFromPos - blockInfo.blockContent.beforePos - 1
91
+ : undefined;
92
+
93
+ const replaceToOffset =
94
+ replaceToPos !== undefined &&
95
+ replaceToPos > blockInfo.blockContent.beforePos &&
96
+ replaceToPos < blockInfo.blockContent.afterPos
97
+ ? replaceToPos - blockInfo.blockContent.beforePos - 1
98
+ : undefined;
99
+
74
100
  updateChildren(block, tr, blockInfo);
75
101
  // The code below determines the new content of the block.
76
102
  // or "keep" to keep as-is
77
- updateBlockContentNode(block, tr, oldNodeType, newNodeType, blockInfo);
103
+ updateBlockContentNode(
104
+ block,
105
+ tr,
106
+ oldNodeType,
107
+ newNodeType,
108
+ blockInfo,
109
+ replaceFromOffset,
110
+ replaceToOffset,
111
+ );
78
112
  } else if (!blockInfo.isBlockContainer && newNodeType.isInGroup("bnBlock")) {
79
113
  updateChildren(block, tr, blockInfo);
80
114
  // old node was a bnBlock type (like column or columnList) and new block as well
@@ -109,7 +143,7 @@ const updateBlockTr = <
109
143
  ...blockInfo.bnBlock.node.attrs,
110
144
  ...block.props,
111
145
  });
112
- };
146
+ }
113
147
 
114
148
  function updateBlockContentNode<
115
149
  BSchema extends BlockSchema,
@@ -117,7 +151,7 @@ function updateBlockContentNode<
117
151
  S extends StyleSchema,
118
152
  >(
119
153
  block: PartialBlock<BSchema, I, S>,
120
- tr: Transaction,
154
+ tr: Transform,
121
155
  oldNodeType: NodeType,
122
156
  newNodeType: NodeType,
123
157
  blockInfo: {
@@ -126,6 +160,8 @@ function updateBlockContentNode<
126
160
  | undefined;
127
161
  blockContent: { node: PMNode; beforePos: number; afterPos: number };
128
162
  },
163
+ replaceFromOffset?: number,
164
+ replaceToOffset?: number,
129
165
  ) {
130
166
  const pmSchema = getPmSchema(tr);
131
167
  let content: PMNode[] | "keep" = "keep";
@@ -172,17 +208,43 @@ function updateBlockContentNode<
172
208
  // content is being replaced or not.
173
209
  if (content === "keep") {
174
210
  // use setNodeMarkup to only update the type and attributes
175
- tr.setNodeMarkup(
176
- blockInfo.blockContent.beforePos,
177
- block.type === undefined ? undefined : pmSchema.nodes[block.type],
178
- {
179
- ...blockInfo.blockContent.node.attrs,
180
- ...block.props,
181
- },
211
+ tr.setNodeMarkup(blockInfo.blockContent.beforePos, newNodeType, {
212
+ ...blockInfo.blockContent.node.attrs,
213
+ ...block.props,
214
+ });
215
+ } else if (replaceFromOffset !== undefined || replaceToOffset !== undefined) {
216
+ // first update markup of the containing node
217
+ tr.setNodeMarkup(blockInfo.blockContent.beforePos, newNodeType, {
218
+ ...blockInfo.blockContent.node.attrs,
219
+ ...block.props,
220
+ });
221
+
222
+ const start =
223
+ blockInfo.blockContent.beforePos + 1 + (replaceFromOffset ?? 0);
224
+ const end =
225
+ blockInfo.blockContent.beforePos +
226
+ 1 +
227
+ (replaceToOffset ?? blockInfo.blockContent.node.content.size);
228
+
229
+ // for content like table cells (where the blockcontent has nested PM nodes),
230
+ // we need to figure out the correct openStart and openEnd for the slice when replacing
231
+
232
+ const contentDepth = tr.doc.resolve(blockInfo.blockContent.beforePos).depth;
233
+ const startDepth = tr.doc.resolve(start).depth;
234
+ const endDepth = tr.doc.resolve(end).depth;
235
+
236
+ tr.replace(
237
+ start,
238
+ end,
239
+ new Slice(
240
+ Fragment.from(content),
241
+ startDepth - contentDepth - 1,
242
+ endDepth - contentDepth - 1,
243
+ ),
182
244
  );
183
245
  } else {
184
246
  // use replaceWith to replace the content and the block itself
185
- // also reset the selection since replacing the block content
247
+ // also reset the selection since replacing the block content
186
248
  // sets it to the next block.
187
249
  tr.replaceWith(
188
250
  blockInfo.blockContent.beforePos,
@@ -202,7 +264,7 @@ function updateChildren<
202
264
  BSchema extends BlockSchema,
203
265
  I extends InlineContentSchema,
204
266
  S extends StyleSchema,
205
- >(block: PartialBlock<BSchema, I, S>, tr: Transaction, blockInfo: BlockInfo) {
267
+ >(block: PartialBlock<BSchema, I, S>, tr: Transform, blockInfo: BlockInfo) {
206
268
  const pmSchema = getPmSchema(tr);
207
269
  if (block.children !== undefined && block.children.length > 0) {
208
270
  const childNodes = block.children.map((child) => {
@@ -242,6 +304,8 @@ export function updateBlock<
242
304
  tr: Transaction,
243
305
  blockToUpdate: BlockIdentifier,
244
306
  update: PartialBlock<BSchema, I, S>,
307
+ replaceFromPos?: number,
308
+ replaceToPos?: number,
245
309
  ): Block<BSchema, I, S> {
246
310
  const id =
247
311
  typeof blockToUpdate === "string" ? blockToUpdate : blockToUpdate.id;
@@ -250,7 +314,13 @@ export function updateBlock<
250
314
  throw new Error(`Block with ID ${id} not found`);
251
315
  }
252
316
 
253
- updateBlockTr(tr, posInfo.posBeforeNode, update);
317
+ updateBlockTr(
318
+ tr,
319
+ posInfo.posBeforeNode,
320
+ update,
321
+ replaceFromPos,
322
+ replaceToPos,
323
+ );
254
324
 
255
325
  const blockContainerNode = tr.doc
256
326
  .resolve(posInfo.posBeforeNode + 1) // TODO: clean?
@@ -10,7 +10,10 @@ import {
10
10
  StyleSchema,
11
11
  } from "../../../schema/index.js";
12
12
  import { getBlockInfo, getNearestBlockPos } from "../../getBlockInfoFromPos.js";
13
- import { nodeToBlock } from "../../nodeConversions/nodeToBlock.js";
13
+ import {
14
+ nodeToBlock,
15
+ prosemirrorSliceToSlicedBlocks,
16
+ } from "../../nodeConversions/nodeToBlock.js";
14
17
  import { getNodeById } from "../../nodeUtil.js";
15
18
  import { getBlockNoteSchema, getPmSchema } from "../../pmUtil.js";
16
19
 
@@ -216,3 +219,47 @@ export function setSelection(
216
219
  // restriction that the start/end blocks must have content.
217
220
  tr.setSelection(TextSelection.create(tr.doc, startPos, endPos));
218
221
  }
222
+
223
+ export function getSelectionCutBlocks(tr: Transaction) {
224
+ // TODO: fix image node selection
225
+
226
+ const pmSchema = getPmSchema(tr);
227
+ let start = tr.selection.$from;
228
+ let end = tr.selection.$to;
229
+
230
+ // the selection moves below are used to make sure `prosemirrorSliceToSlicedBlocks` returns
231
+ // the correct information about whether content is cut at the start or end of a block
232
+
233
+ // if the end is at the end of a node (|</span></p>) move it forward so we include all closing tags (</span></p>|)
234
+ while (end.parentOffset >= end.parent.nodeSize - 2 && end.depth > 0) {
235
+ end = tr.doc.resolve(end.pos + 1);
236
+ }
237
+
238
+ // if the end is at the start of an empty node (</span></p><p>|) move it backwards so we drop empty start tags (</span></p>|)
239
+ while (end.parentOffset === 0 && end.depth > 0) {
240
+ end = tr.doc.resolve(end.pos - 1);
241
+ }
242
+
243
+ // if the start is at the start of a node (<p><span>|) move it backwards so we include all open tags (|<p><span>)
244
+ while (start.parentOffset === 0 && start.depth > 0) {
245
+ start = tr.doc.resolve(start.pos - 1);
246
+ }
247
+
248
+ // if the start is at the end of a node (|</p><p><span>|) move it forwards so we drop all closing tags (|<p><span>)
249
+ while (start.parentOffset >= start.parent.nodeSize - 2 && start.depth > 0) {
250
+ start = tr.doc.resolve(start.pos + 1);
251
+ }
252
+
253
+ const selectionInfo = prosemirrorSliceToSlicedBlocks(
254
+ tr.doc.slice(start.pos, end.pos, true),
255
+ pmSchema,
256
+ );
257
+
258
+ return {
259
+ _meta: {
260
+ startPos: start.pos,
261
+ endPos: end.pos,
262
+ },
263
+ ...selectionInfo,
264
+ };
265
+ }
@@ -4,21 +4,21 @@ import {
4
4
  TextSelection,
5
5
  type Transaction,
6
6
  } from "prosemirror-state";
7
- import type { TextCursorPosition } from "../../../../editor/cursorPositionTypes.js";
7
+ import type { TextCursorPosition } from "../../../editor/cursorPositionTypes.js";
8
8
  import type {
9
9
  BlockIdentifier,
10
10
  BlockSchema,
11
11
  InlineContentSchema,
12
12
  StyleSchema,
13
- } from "../../../../schema/index.js";
14
- import { UnreachableCaseError } from "../../../../util/typescript.js";
13
+ } from "../../../schema/index.js";
14
+ import { UnreachableCaseError } from "../../../util/typescript.js";
15
15
  import {
16
16
  getBlockInfo,
17
17
  getBlockInfoFromTransaction,
18
- } from "../../../getBlockInfoFromPos.js";
19
- import { nodeToBlock } from "../../../nodeConversions/nodeToBlock.js";
20
- import { getNodeById } from "../../../nodeUtil.js";
21
- import { getBlockNoteSchema, getPmSchema } from "../../../pmUtil.js";
18
+ } from "../../getBlockInfoFromPos.js";
19
+ import { nodeToBlock } from "../../nodeConversions/nodeToBlock.js";
20
+ import { getNodeById } from "../../nodeUtil.js";
21
+ import { getBlockNoteSchema, getPmSchema } from "../../pmUtil.js";
22
22
 
23
23
  export function getTextCursorPosition<
24
24
  BSchema extends BlockSchema,
@@ -126,7 +126,7 @@ export function getBlockInfoWithManualOffset(
126
126
  ): BlockInfo {
127
127
  if (!node.type.isInGroup("bnBlock")) {
128
128
  throw new Error(
129
- `Attempted to get bnBlock node at position but found node of different type ${node.type}`,
129
+ `Attempted to get bnBlock node at position but found node of different type ${node.type.name}`,
130
130
  );
131
131
  }
132
132
 
@@ -33,7 +33,7 @@ function styledTextToNodes<T extends StyleSchema>(
33
33
  ): Node[] {
34
34
  const marks: Mark[] = [];
35
35
 
36
- for (const [style, value] of Object.entries(styledText.styles)) {
36
+ for (const [style, value] of Object.entries(styledText.styles || {})) {
37
37
  const config = styleSchema[style];
38
38
  if (!config) {
39
39
  throw new Error(`style ${style} not found in styleSchema`);
@@ -51,7 +51,9 @@ function styledTextToNodes<T extends StyleSchema>(
51
51
  const parseHardBreaks = !blockType || !schema.nodes[blockType].spec.code;
52
52
 
53
53
  if (!parseHardBreaks) {
54
- return [schema.text(styledText.text, marks)];
54
+ return styledText.text.length > 0
55
+ ? [schema.text(styledText.text, marks)]
56
+ : [];
55
57
  }
56
58
 
57
59
  return (
@@ -259,6 +261,7 @@ export function tableContentToNodes<
259
261
  );
260
262
  columnNodes.push(cellNode);
261
263
  }
264
+
262
265
  const rowNode = schema.nodes["tableRow"].createChecked({}, columnNodes);
263
266
  rowNodes.push(rowNode);
264
267
  }