@blocknote/core 0.29.1 → 0.30.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 (69) hide show
  1. package/README.md +125 -0
  2. package/dist/blocknote.cjs +9 -9
  3. package/dist/blocknote.cjs.map +1 -1
  4. package/dist/blocknote.js +1479 -1339
  5. package/dist/blocknote.js.map +1 -1
  6. package/dist/locales.cjs +1 -1
  7. package/dist/locales.cjs.map +1 -1
  8. package/dist/locales.js +751 -9
  9. package/dist/locales.js.map +1 -1
  10. package/dist/style.css +1 -1
  11. package/dist/tsconfig.tsbuildinfo +1 -1
  12. package/dist/webpack-stats.json +1 -1
  13. package/package.json +3 -6
  14. package/src/api/blockManipulation/commands/insertBlocks/__snapshots__/insertBlocks.test.ts.snap +0 -7
  15. package/src/api/blockManipulation/commands/mergeBlocks/__snapshots__/mergeBlocks.test.ts.snap +0 -5
  16. package/src/api/blockManipulation/commands/moveBlocks/__snapshots__/moveBlocks.test.ts.snap +0 -20
  17. package/src/api/blockManipulation/commands/replaceBlocks/__snapshots__/replaceBlocks.test.ts.snap +0 -12
  18. package/src/api/blockManipulation/commands/splitBlock/__snapshots__/splitBlock.test.ts.snap +0 -6
  19. package/src/api/blockManipulation/commands/updateBlock/__snapshots__/updateBlock.test.ts.snap +0 -17
  20. package/src/api/clipboard/fromClipboard/pasteExtension.ts +19 -1
  21. package/src/blocks/AudioBlockContent/AudioBlockContent.ts +5 -0
  22. package/src/blocks/CodeBlockContent/CodeBlockContent.ts +32 -18
  23. package/src/blocks/FileBlockContent/FileBlockContent.ts +5 -0
  24. package/src/blocks/FileBlockContent/helpers/render/createResizableFileBlockWrapper.ts +9 -2
  25. package/src/blocks/HeadingBlockContent/HeadingBlockContent.ts +3 -0
  26. package/src/blocks/ImageBlockContent/ImageBlockContent.ts +10 -2
  27. package/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.ts +9 -25
  28. package/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.ts +14 -3
  29. package/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.ts +9 -26
  30. package/src/blocks/ListItemBlockContent/getListItemContent.ts +115 -0
  31. package/src/blocks/ParagraphBlockContent/ParagraphBlockContent.ts +6 -2
  32. package/src/blocks/QuoteBlockContent/QuoteBlockContent.ts +6 -1
  33. package/src/blocks/TableBlockContent/TableBlockContent.ts +71 -14
  34. package/src/blocks/VideoBlockContent/VideoBlockContent.ts +10 -2
  35. package/src/blocks/defaultBlockHelpers.ts +16 -0
  36. package/src/editor/Block.css +2 -1
  37. package/src/editor/BlockNoteEditor.ts +103 -60
  38. package/src/editor/BlockNoteExtensions.ts +14 -5
  39. package/src/extensions/Collaboration/CursorPlugin.ts +152 -0
  40. package/src/extensions/Collaboration/SyncPlugin.ts +15 -0
  41. package/src/extensions/Collaboration/UndoPlugin.ts +14 -0
  42. package/src/extensions/FilePanel/FilePanelPlugin.ts +31 -22
  43. package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +2 -4
  44. package/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts +3 -0
  45. package/src/i18n/locales/index.ts +2 -0
  46. package/src/i18n/locales/ru.ts +2 -2
  47. package/src/i18n/locales/sk.ts +355 -0
  48. package/src/i18n/locales/zh-tw.ts +390 -0
  49. package/src/pm-nodes/BlockContainer.ts +7 -6
  50. package/src/schema/blocks/createSpec.ts +1 -1
  51. package/src/schema/blocks/internal.ts +0 -1
  52. package/src/schema/blocks/types.ts +2 -1
  53. package/types/src/api/blockManipulation/setupTestEnv.d.ts +8 -4
  54. package/types/src/blocks/ImageBlockContent/ImageBlockContent.d.ts +8 -4
  55. package/types/src/blocks/ListItemBlockContent/getListItemContent.d.ts +28 -0
  56. package/types/src/blocks/VideoBlockContent/VideoBlockContent.d.ts +8 -4
  57. package/types/src/blocks/defaultBlockHelpers.d.ts +1 -0
  58. package/types/src/blocks/defaultBlocks.d.ts +16 -8
  59. package/types/src/editor/BlockNoteEditor.d.ts +18 -1
  60. package/types/src/extensions/Collaboration/CursorPlugin.d.ts +31 -0
  61. package/types/src/extensions/Collaboration/SyncPlugin.d.ts +7 -0
  62. package/types/src/extensions/Collaboration/UndoPlugin.d.ts +6 -0
  63. package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +1 -1
  64. package/types/src/i18n/locales/index.d.ts +2 -0
  65. package/types/src/i18n/locales/sk.d.ts +313 -0
  66. package/types/src/i18n/locales/zh-tw.d.ts +2 -0
  67. package/types/src/schema/blocks/types.d.ts +2 -1
  68. package/src/extensions/Collaboration/createCollaborationExtensions.ts +0 -147
  69. package/types/src/extensions/Collaboration/createCollaborationExtensions.d.ts +0 -17
@@ -39,10 +39,14 @@ export const ParagraphBlockContent = createStronglyTypedTiptapNode({
39
39
 
40
40
  parseHTML() {
41
41
  return [
42
- { tag: "div[data-content-type=" + this.name + "]" },
42
+ // Parse from internal HTML.
43
+ {
44
+ tag: "div[data-content-type=" + this.name + "]",
45
+ contentElement: ".bn-inline-content",
46
+ },
47
+ // Parse from external HTML.
43
48
  {
44
49
  tag: "p",
45
- priority: 200,
46
50
  getAttrs: (element) => {
47
51
  if (typeof element === "string" || !element.textContent?.trim()) {
48
52
  return false;
@@ -67,7 +67,12 @@ export const QuoteBlockContent = createStronglyTypedTiptapNode({
67
67
 
68
68
  parseHTML() {
69
69
  return [
70
- { tag: "div[data-content-type=" + this.name + "]" },
70
+ // Parse from internal HTML.
71
+ {
72
+ tag: "div[data-content-type=" + this.name + "]",
73
+ contentElement: ".bn-inline-content",
74
+ },
75
+ // Parse from external HTML.
71
76
  {
72
77
  tag: "blockquote",
73
78
  node: "quote",
@@ -1,7 +1,7 @@
1
1
  import { TableCell } from "@tiptap/extension-table-cell";
2
2
  import { TableHeader } from "@tiptap/extension-table-header";
3
3
  import { TableRow } from "@tiptap/extension-table-row";
4
- import { Node as PMNode } from "prosemirror-model";
4
+ import { DOMParser, Fragment, Node as PMNode, Schema } from "prosemirror-model";
5
5
  import { TableView } from "prosemirror-tables";
6
6
 
7
7
  import { NodeView } from "prosemirror-view";
@@ -27,7 +27,11 @@ export const TableBlockContent = createStronglyTypedTiptapNode({
27
27
  isolating: true,
28
28
 
29
29
  parseHTML() {
30
- return [{ tag: "table" }];
30
+ return [
31
+ {
32
+ tag: "table",
33
+ },
34
+ ];
31
35
  },
32
36
 
33
37
  renderHTML({ HTMLAttributes }) {
@@ -113,17 +117,6 @@ const TableParagraph = createStronglyTypedTiptapNode({
113
117
 
114
118
  parseHTML() {
115
119
  return [
116
- {
117
- preserveWhitespace: "full",
118
- // set this rule as high priority so it takes precedence over the default paragraph rule,
119
- // but only if we're in the tableContent context
120
- priority: 210,
121
- context: "tableContent",
122
- tag: "p",
123
- getAttrs: (_element) => {
124
- return {};
125
- },
126
- },
127
120
  {
128
121
  tag: "p",
129
122
  getAttrs: (element) => {
@@ -131,18 +124,24 @@ const TableParagraph = createStronglyTypedTiptapNode({
131
124
  return false;
132
125
  }
133
126
 
127
+ // Only parse in internal HTML.
128
+ if (!element.closest("[data-content-type]")) {
129
+ return false;
130
+ }
131
+
134
132
  const parent = element.parentElement;
135
133
 
136
134
  if (parent === null) {
137
135
  return false;
138
136
  }
139
137
 
140
- if (parent.tagName === "TD") {
138
+ if (parent.tagName === "TD" || parent.tagName === "TH") {
141
139
  return {};
142
140
  }
143
141
 
144
142
  return false;
145
143
  },
144
+ node: "tableParagraph",
146
145
  },
147
146
  ];
148
147
  },
@@ -152,6 +151,42 @@ const TableParagraph = createStronglyTypedTiptapNode({
152
151
  },
153
152
  });
154
153
 
154
+ /**
155
+ * This will flatten a node's content to fit into a table cell's paragraph.
156
+ */
157
+ function parseTableContent(node: HTMLElement, schema: Schema) {
158
+ const parser = DOMParser.fromSchema(schema);
159
+
160
+ // This will parse the content of the table paragraph as though it were a blockGroup.
161
+ // Resulting in a structure like:
162
+ // <blockGroup>
163
+ // <blockContainer>
164
+ // <p>Hello</p>
165
+ // </blockContainer>
166
+ // <blockContainer>
167
+ // <p>Hello</p>
168
+ // </blockContainer>
169
+ // </blockGroup>
170
+ const parsedContent = parser.parse(node, {
171
+ topNode: schema.nodes.blockGroup.create(),
172
+ });
173
+ const extractedContent: PMNode[] = [];
174
+
175
+ // Try to extract any content within the blockContainer.
176
+ parsedContent.content.descendants((child) => {
177
+ // As long as the child is an inline node, we can append it to the fragment.
178
+ if (child.isInline) {
179
+ // And append it to the fragment
180
+ extractedContent.push(child);
181
+ return false;
182
+ }
183
+
184
+ return undefined;
185
+ });
186
+
187
+ return Fragment.fromArray(extractedContent);
188
+ }
189
+
155
190
  export const Table = createBlockSpecFromStronglyTypedTiptapNode(
156
191
  TableBlockContent,
157
192
  tablePropSchema,
@@ -167,9 +202,31 @@ export const Table = createBlockSpecFromStronglyTypedTiptapNode(
167
202
  * So, we manually fix this up when reading back in the `nodeToBlock` and only ever place a single tableContent back into the cell.
168
203
  */
169
204
  content: "tableContent+",
205
+ parseHTML() {
206
+ return [
207
+ {
208
+ tag: "th",
209
+ // As `th` elements can contain multiple paragraphs, we need to merge their contents
210
+ // into a single one so that ProseMirror can parse everything correctly.
211
+ getContent: (node, schema) =>
212
+ parseTableContent(node as HTMLElement, schema),
213
+ },
214
+ ];
215
+ },
170
216
  }),
171
217
  TableCell.extend({
172
218
  content: "tableContent+",
219
+ parseHTML() {
220
+ return [
221
+ {
222
+ tag: "td",
223
+ // As `td` elements can contain multiple paragraphs, we need to merge their contents
224
+ // into a single one so that ProseMirror can parse everything correctly.
225
+ getContent: (node, schema) =>
226
+ parseTableContent(node as HTMLElement, schema),
227
+ },
228
+ ];
229
+ },
173
230
  }),
174
231
  TableRow,
175
232
  ]
@@ -37,7 +37,8 @@ export const videoPropSchema = {
37
37
  },
38
38
  // File preview width in px.
39
39
  previewWidth: {
40
- default: 512,
40
+ default: undefined,
41
+ type: "number",
41
42
  },
42
43
  } satisfies PropSchema;
43
44
 
@@ -88,6 +89,11 @@ export const videoParse = (
88
89
  element: HTMLElement
89
90
  ): Partial<Props<typeof videoBlockConfig.propSchema>> | undefined => {
90
91
  if (element.tagName === "VIDEO") {
92
+ // Ignore if parent figure has already been parsed.
93
+ if (element.closest("figure")) {
94
+ return undefined;
95
+ }
96
+
91
97
  return parseVideoElement(element as HTMLVideoElement);
92
98
  }
93
99
 
@@ -124,7 +130,9 @@ export const videoToExternalHTML = (
124
130
  if (block.props.showPreview) {
125
131
  video = document.createElement("video");
126
132
  video.src = block.props.url;
127
- video.width = block.props.previewWidth;
133
+ if (block.props.previewWidth) {
134
+ video.width = block.props.previewWidth;
135
+ }
128
136
  } else {
129
137
  video = document.createElement("a");
130
138
  video.href = block.props.url;
@@ -95,3 +95,19 @@ export const defaultBlockToHTML = <
95
95
  contentDOM?: HTMLElement;
96
96
  };
97
97
  };
98
+
99
+ // Function that merges all paragraphs into a single one separated by line breaks.
100
+ // This is used when parsing blocks like list items and table cells, as they may
101
+ // contain multiple paragraphs that ProseMirror will not be able to handle
102
+ // properly.
103
+ export function mergeParagraphs(element: HTMLElement) {
104
+ const paragraphs = element.querySelectorAll("p");
105
+ if (paragraphs.length > 1) {
106
+ const firstParagraph = paragraphs[0];
107
+ for (let i = 1; i < paragraphs.length; i++) {
108
+ const paragraph = paragraphs[i];
109
+ firstParagraph.innerHTML += "<br>" + paragraph.innerHTML;
110
+ paragraph.remove();
111
+ }
112
+ }
113
+ }
@@ -14,7 +14,6 @@ BASIC STYLES
14
14
  }
15
15
 
16
16
  .bn-block-content {
17
- display: flex;
18
17
  padding: 3px 0;
19
18
  transition: font-size 0.2s;
20
19
  width: 100%;
@@ -163,6 +162,7 @@ NESTED BLOCKS
163
162
  .bn-block-content::before {
164
163
  margin-right: 0;
165
164
  content: "";
165
+ display: inline;
166
166
  }
167
167
 
168
168
  /* Ordered */
@@ -411,6 +411,7 @@ NESTED BLOCKS
411
411
  display: flex;
412
412
  align-items: center;
413
413
  position: relative;
414
+ max-width: 100%;
414
415
  }
415
416
 
416
417
  [data-file-block] .bn-visual-media {