@blocknote/core 0.23.0 → 0.23.2-hotfix.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 (32) hide show
  1. package/dist/blocknote.js +2921 -2603
  2. package/dist/blocknote.js.map +1 -1
  3. package/dist/blocknote.umd.cjs +6 -6
  4. package/dist/blocknote.umd.cjs.map +1 -1
  5. package/dist/tsconfig.tsbuildinfo +1 -1
  6. package/dist/webpack-stats.json +1 -1
  7. package/package.json +3 -3
  8. package/src/api/blockManipulation/commands/updateBlock/updateBlock.ts +4 -2
  9. package/src/api/exporters/html/__snapshots__/codeBlock/contains-newlines/external.html +1 -0
  10. package/src/api/exporters/html/__snapshots__/codeBlock/contains-newlines/internal.html +3 -0
  11. package/src/api/exporters/html/util/serializeBlocksInternalHTML.ts +6 -2
  12. package/src/api/exporters/markdown/__snapshots__/codeBlock/contains-newlines/markdown.md +4 -0
  13. package/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap +27 -0
  14. package/src/api/nodeConversions/blockToNode.ts +35 -9
  15. package/src/api/testUtil/cases/defaultSchema.ts +9 -0
  16. package/src/editor/BlockNoteEditor.ts +20 -12
  17. package/src/editor/BlockNoteExtensions.ts +4 -128
  18. package/src/extensions/Collaboration/createCollaborationExtensions.ts +144 -0
  19. package/src/extensions/SideMenu/SideMenuPlugin.ts +89 -28
  20. package/src/i18n/locales/fr.ts +2 -2
  21. package/src/i18n/locales/index.ts +2 -1
  22. package/src/i18n/locales/uk.ts +289 -0
  23. package/types/src/api/exporters/html/util/serializeBlocksInternalHTML.d.ts +1 -1
  24. package/types/src/api/nodeConversions/blockToNode.d.ts +1 -1
  25. package/types/src/editor/BlockNoteEditor.d.ts +3 -2
  26. package/types/src/editor/BlockNoteExtensions.d.ts +1 -1
  27. package/types/src/extensions/Collaboration/createCollaborationExtensions.d.ts +17 -0
  28. package/types/src/extensions/SideMenu/SideMenuPlugin.d.ts +1 -5
  29. package/types/src/i18n/locales/index.d.ts +2 -1
  30. package/types/src/i18n/locales/uk.d.ts +2 -0
  31. package/types/src/pm-nodes/BlockContainer.d.ts +1 -1
  32. package/types/src/pm-nodes/BlockGroup.d.ts +1 -1
@@ -0,0 +1,144 @@
1
+ import Collaboration from "@tiptap/extension-collaboration";
2
+ import { Awareness } from "y-protocols/awareness";
3
+ import CollaborationCursor from "@tiptap/extension-collaboration-cursor";
4
+ import * as Y from "yjs";
5
+
6
+ export const createCollaborationExtensions = (collaboration: {
7
+ fragment: Y.XmlFragment;
8
+ user: {
9
+ name: string;
10
+ color: string;
11
+ [key: string]: string;
12
+ };
13
+ provider: any;
14
+ renderCursor?: (user: any) => HTMLElement;
15
+ showCursorLabels?: "always" | "activity";
16
+ }) => {
17
+ const tiptapExtensions = [];
18
+
19
+ tiptapExtensions.push(
20
+ Collaboration.configure({
21
+ fragment: collaboration.fragment,
22
+ })
23
+ );
24
+
25
+ const awareness = collaboration.provider?.awareness as Awareness | undefined;
26
+
27
+ if (awareness) {
28
+ const cursors = new Map<
29
+ number,
30
+ { element: HTMLElement; hideTimeout: NodeJS.Timeout | undefined }
31
+ >();
32
+
33
+ if (collaboration.showCursorLabels !== "always") {
34
+ awareness.on(
35
+ "change",
36
+ ({
37
+ updated,
38
+ }: {
39
+ added: Array<number>;
40
+ updated: Array<number>;
41
+ removed: Array<number>;
42
+ }) => {
43
+ for (const clientID of updated) {
44
+ const cursor = cursors.get(clientID);
45
+
46
+ if (cursor) {
47
+ cursor.element.setAttribute("data-active", "");
48
+
49
+ if (cursor.hideTimeout) {
50
+ clearTimeout(cursor.hideTimeout);
51
+ }
52
+
53
+ cursors.set(clientID, {
54
+ element: cursor.element,
55
+ hideTimeout: setTimeout(() => {
56
+ cursor.element.removeAttribute("data-active");
57
+ }, 2000),
58
+ });
59
+ }
60
+ }
61
+ }
62
+ );
63
+ }
64
+
65
+ const renderCursor = (user: { name: string; color: string }) => {
66
+ const cursorElement = document.createElement("span");
67
+
68
+ cursorElement.classList.add("collaboration-cursor__caret");
69
+ cursorElement.setAttribute("style", `border-color: ${user.color}`);
70
+ if (collaboration?.showCursorLabels === "always") {
71
+ cursorElement.setAttribute("data-active", "");
72
+ }
73
+
74
+ const labelElement = document.createElement("span");
75
+
76
+ labelElement.classList.add("collaboration-cursor__label");
77
+ labelElement.setAttribute("style", `background-color: ${user.color}`);
78
+ labelElement.insertBefore(document.createTextNode(user.name), null);
79
+
80
+ cursorElement.insertBefore(document.createTextNode("\u2060"), null); // Non-breaking space
81
+ cursorElement.insertBefore(labelElement, null);
82
+ cursorElement.insertBefore(document.createTextNode("\u2060"), null); // Non-breaking space
83
+
84
+ return cursorElement;
85
+ };
86
+
87
+ const render = (
88
+ user: { color: string; name: string },
89
+ clientID: number
90
+ ) => {
91
+ let cursorData = cursors.get(clientID);
92
+
93
+ if (!cursorData) {
94
+ const cursorElement =
95
+ collaboration?.renderCursor?.(user) || renderCursor(user);
96
+
97
+ if (collaboration?.showCursorLabels !== "always") {
98
+ cursorElement.addEventListener("mouseenter", () => {
99
+ const cursor = cursors.get(clientID)!;
100
+ cursor.element.setAttribute("data-active", "");
101
+
102
+ if (cursor.hideTimeout) {
103
+ clearTimeout(cursor.hideTimeout);
104
+ cursors.set(clientID, {
105
+ element: cursor.element,
106
+ hideTimeout: undefined,
107
+ });
108
+ }
109
+ });
110
+
111
+ cursorElement.addEventListener("mouseleave", () => {
112
+ const cursor = cursors.get(clientID)!;
113
+
114
+ cursors.set(clientID, {
115
+ element: cursor.element,
116
+ hideTimeout: setTimeout(() => {
117
+ cursor.element.removeAttribute("data-active");
118
+ }, 2000),
119
+ });
120
+ });
121
+ }
122
+
123
+ cursorData = {
124
+ element: cursorElement,
125
+ hideTimeout: undefined,
126
+ };
127
+
128
+ cursors.set(clientID, cursorData);
129
+ }
130
+
131
+ return cursorData.element;
132
+ };
133
+
134
+ tiptapExtensions.push(
135
+ CollaborationCursor.configure({
136
+ user: collaboration.user,
137
+ render: render as any, // tiptap type not compatible with latest y-prosemirror
138
+ provider: collaboration.provider,
139
+ })
140
+ );
141
+ }
142
+
143
+ return tiptapExtensions;
144
+ };
@@ -1,5 +1,11 @@
1
1
  import { DOMParser, Slice } from "@tiptap/pm/model";
2
- import { EditorState, Plugin, PluginKey, PluginView } from "@tiptap/pm/state";
2
+ import {
3
+ EditorState,
4
+ Plugin,
5
+ PluginKey,
6
+ TextSelection,
7
+ PluginView,
8
+ } from "@tiptap/pm/state";
3
9
  import { EditorView } from "@tiptap/pm/view";
4
10
 
5
11
  import { Block } from "../../blocks/defaultBlocks.js";
@@ -187,6 +193,11 @@ export class SideMenuView<
187
193
  this.onKeyDown as EventListener,
188
194
  true
189
195
  );
196
+
197
+ // Setting capture=true ensures that any parent container of the editor that
198
+ // gets scrolled will trigger the scroll event. Scroll events do not bubble
199
+ // and so won't propagate to the document by default.
200
+ pmView.root.addEventListener("scroll", this.onScroll, true);
190
201
  }
191
202
 
192
203
  updateState = (state: SideMenuState<BSchema, I, S>) => {
@@ -263,21 +274,56 @@ export class SideMenuView<
263
274
  }
264
275
  };
265
276
 
266
- /**
267
- * If the event is outside the editor contents,
268
- * we dispatch a fake event, so that we can still drop the content
269
- * when dragging / dropping to the side of the editor
270
- */
271
277
  onDrop = (event: DragEvent) => {
278
+ // Content from outside a BlockNote editor is being dropped - just let
279
+ // ProseMirror's default behaviour handle it.
280
+ if (this.pmView.dragging === null) {
281
+ return;
282
+ }
283
+
272
284
  this.editor._tiptapEditor.commands.blur();
273
285
 
274
- // ProseMirror doesn't remove the dragged content if it's dropped outside
275
- // the editor (e.g. to other editors), so we need to do it manually. Since
276
- // the dragged content is the same as the selected content, we can just
277
- // delete the selection.
278
- if (this.isDragOrigin && !this.pmView.dom.contains(event.target as Node)) {
279
- this.pmView.dispatch(this.pmView.state.tr.deleteSelection());
286
+ // When ProseMirror handles a drop event on the editor while
287
+ // `view.dragging` is set, it deletes the selected content. However, if
288
+ // a block from a different editor is being dropped, this causes some
289
+ // issues that the code below fixes:
290
+ if (!this.isDragOrigin && this.pmView.dom.contains(event.target as Node)) {
291
+ // 1. Because the editor selection is unrelated to the dragged content,
292
+ // we don't want PM to delete its content. Therefore, we collapse the
293
+ // selection.
294
+ this.pmView.dispatch(
295
+ this.pmView.state.tr.setSelection(
296
+ TextSelection.create(
297
+ this.pmView.state.tr.doc,
298
+ this.pmView.state.tr.selection.to
299
+ )
300
+ )
301
+ );
302
+ } else if (
303
+ this.isDragOrigin &&
304
+ !this.pmView.dom.contains(event.target as Node)
305
+ ) {
306
+ // 2. Because the editor from which the block originates doesn't get a
307
+ // drop event on it, PM doesn't delete its selected content. Therefore, we
308
+ // need to do so manually.
309
+ //
310
+ // Note: Deleting the selected content from the editor from which the
311
+ // block originates, may change its height. This can cause the position of
312
+ // the editor in which the block is being dropping to shift, before it
313
+ // can handle the drop event. That in turn can cause the drop to happen
314
+ // somewhere other than the user intended. To get around this, we delay
315
+ // deleting the selected content until all editors have had the chance to
316
+ // handle the event.
317
+ setTimeout(
318
+ () => this.pmView.dispatch(this.pmView.state.tr.deleteSelection()),
319
+ 0
320
+ );
280
321
  }
322
+ // 3. PM only clears `view.dragging` on the editor that the block was
323
+ // dropped, so we manually have to clear it on all the others. However,
324
+ // PM also needs to read `view.dragging` while handling the event, so we
325
+ // use a `setTimeout` to ensure it's only cleared after that.
326
+ setTimeout(() => (this.pmView.dragging = null), 0);
281
327
 
282
328
  if (
283
329
  this.sideMenuDetection === "editor" ||
@@ -293,6 +339,11 @@ export class SideMenuView<
293
339
  });
294
340
 
295
341
  if (!pos || pos.inside === -1) {
342
+ /**
343
+ * When `this.sideMenuSelection === "viewport"`, if the event is outside the
344
+ * editor contents, we dispatch a fake event, so that we can still drop the
345
+ * content when dragging / dropping to the side of the editor
346
+ */
296
347
  const evt = this.createSyntheticEvent(event);
297
348
  // console.log("dispatch fake drop");
298
349
  this.pmView.dom.dispatchEvent(evt);
@@ -318,25 +369,27 @@ export class SideMenuView<
318
369
  * access `dataTransfer` contents on `dragstart` and `drop` events.
319
370
  */
320
371
  onDragStart = (event: DragEvent) => {
321
- if (!this.pmView.dragging) {
322
- const html = event.dataTransfer?.getData("blocknote/html");
323
- if (!html) {
324
- return;
325
- }
372
+ const html = event.dataTransfer?.getData("blocknote/html");
373
+ if (!html) {
374
+ return;
375
+ }
326
376
 
327
- const element = document.createElement("div");
328
- element.innerHTML = html;
377
+ if (this.pmView.dragging) {
378
+ throw new Error("New drag was started while an existing drag is ongoing");
379
+ }
329
380
 
330
- const parser = DOMParser.fromSchema(this.pmView.state.schema);
331
- const node = parser.parse(element, {
332
- topNode: this.pmView.state.schema.nodes["blockGroup"].create(),
333
- });
381
+ const element = document.createElement("div");
382
+ element.innerHTML = html;
334
383
 
335
- this.pmView.dragging = {
336
- slice: new Slice(node.content, 0, 0),
337
- move: true,
338
- };
339
- }
384
+ const parser = DOMParser.fromSchema(this.pmView.state.schema);
385
+ const node = parser.parse(element, {
386
+ topNode: this.pmView.state.schema.nodes["blockGroup"].create(),
387
+ });
388
+
389
+ this.pmView.dragging = {
390
+ slice: new Slice(node.content, 0, 0),
391
+ move: true,
392
+ };
340
393
  };
341
394
 
342
395
  /**
@@ -473,6 +526,13 @@ export class SideMenuView<
473
526
  return evt;
474
527
  }
475
528
 
529
+ onScroll = () => {
530
+ if (this.state?.show) {
531
+ this.state.referencePos = this.hoveredBlock!.getBoundingClientRect();
532
+ this.emitUpdate(this.state);
533
+ }
534
+ };
535
+
476
536
  // Needed in cases where the editor state updates without the mouse cursor
477
537
  // moving, as some state updates can require a side menu update. For example,
478
538
  // adding a button to the side menu which removes the block can cause the
@@ -515,6 +575,7 @@ export class SideMenuView<
515
575
  this.onKeyDown as EventListener,
516
576
  true
517
577
  );
578
+ this.pmView.root.removeEventListener("scroll", this.onScroll, true);
518
579
  }
519
580
  }
520
581
 
@@ -281,11 +281,11 @@ export const fr: Dictionary = {
281
281
  tooltip: "Basculer l'aperçu",
282
282
  },
283
283
  nest: {
284
- tooltip: "Emboîter le bloc",
284
+ tooltip: "Augmenter le retrait du bloc",
285
285
  secondary_tooltip: "Tab",
286
286
  },
287
287
  unnest: {
288
- tooltip: "Démboîter le bloc",
288
+ tooltip: "Diminuer le retait du bloc",
289
289
  secondary_tooltip: "Shift+Tab",
290
290
  },
291
291
  align_left: {
@@ -5,12 +5,13 @@ export * from "./es.js";
5
5
  export * from "./fr.js";
6
6
  export * from "./hr.js";
7
7
  export * from "./is.js";
8
+ export * from "./it.js";
8
9
  export * from "./ja.js";
9
10
  export * from "./ko.js";
10
11
  export * from "./nl.js";
11
12
  export * from "./pl.js";
12
13
  export * from "./pt.js";
13
14
  export * from "./ru.js";
15
+ export * from "./uk.js";
14
16
  export * from "./vi.js";
15
17
  export * from "./zh.js";
16
- export * from "./it.js"
@@ -0,0 +1,289 @@
1
+ import { Dictionary } from "../dictionary.js";
2
+
3
+ export const uk: Dictionary = {
4
+ slash_menu: {
5
+ heading: {
6
+ title: "Заголовок 1",
7
+ subtext: "Заголовок найвищого рівня",
8
+ aliases: ["h", "heading1", "h1", "заголовок1"],
9
+ group: "Заголовки",
10
+ },
11
+ heading_2: {
12
+ title: "Заголовок 2",
13
+ subtext: "Основний заголовок розділу",
14
+ aliases: ["h2", "heading2", "subheading", "заголовок2"],
15
+ group: "Заголовки",
16
+ },
17
+ heading_3: {
18
+ title: "Заголовок 3",
19
+ subtext: "Підзаголовок і груповий заголовок",
20
+ aliases: ["h3", "heading3", "subheading", "заголовок3"],
21
+ group: "Заголовки",
22
+ },
23
+ numbered_list: {
24
+ title: "Нумерований список",
25
+ subtext: "Список із впорядкованими елементами",
26
+ aliases: ["ol", "li", "list", "numberedlist", "numbered list", "список", "нумерований список"],
27
+ group: "Базові блоки",
28
+ },
29
+ bullet_list: {
30
+ title: "Маркований список",
31
+ subtext: "Список із невпорядкованими елементами",
32
+ aliases: ["ul", "li", "list", "bulletlist", "bullet list", "список", "маркований список"],
33
+ group: "Базові блоки",
34
+ },
35
+ check_list: {
36
+ title: "Чек-лист",
37
+ subtext: "Список із чекбоксами",
38
+ aliases: ["ul", "li", "list", "checklist", "check list", "checked list", "checkbox", "чекбокс", "чек-лист"],
39
+ group: "Базові блоки",
40
+ },
41
+ paragraph: {
42
+ title: "Параграф",
43
+ subtext: "Основний текст документа",
44
+ aliases: ["p", "paragraph", "параграф"],
45
+ group: "Базові блоки",
46
+ },
47
+ code_block: {
48
+ title: "Блок коду",
49
+ subtext: "Блок коду з підсвіткою синтаксису",
50
+ aliases: ["code", "pre", "блок коду"],
51
+ group: "Базові блоки",
52
+ },
53
+ page_break: {
54
+ title: "Розрив сторінки",
55
+ subtext: "Роздільник сторінки",
56
+ aliases: ["page", "break", "separator", "розрив сторінки", "розділювач"],
57
+ group: "Базові блоки",
58
+ },
59
+ table: {
60
+ title: "Таблиця",
61
+ subtext: "Таблиця з редагованими клітинками",
62
+ aliases: ["table", "таблиця"],
63
+ group: "Розширені",
64
+ },
65
+ image: {
66
+ title: "Зображення",
67
+ subtext: "Масштабоване зображення з підписом",
68
+ aliases: ["image", "imageUpload", "upload", "img", "picture", "media", "url", "зображення", "медіа"],
69
+ group: "Медіа",
70
+ },
71
+ video: {
72
+ title: "Відео",
73
+ subtext: "Масштабоване відео з підписом",
74
+ aliases: ["video", "videoUpload", "upload", "mp4", "film", "media", "url", "відео", "медіа"],
75
+ group: "Медіа",
76
+ },
77
+ audio: {
78
+ title: "Аудіо",
79
+ subtext: "Вбудоване аудіо з підписом",
80
+ aliases: ["audio", "audioUpload", "upload", "mp3", "sound", "media", "url", "аудіо", "медіа"],
81
+ group: "Медіа",
82
+ },
83
+ file: {
84
+ title: "Файл",
85
+ subtext: "Вбудований файл",
86
+ aliases: ["file", "upload", "embed", "media", "url", "файл", "медіа"],
87
+ group: "Медіа",
88
+ },
89
+ emoji: {
90
+ title: "Емодзі",
91
+ subtext: "Пошук і вставка емодзі",
92
+ aliases: ["emoji", "emote", "emotion", "face", "смайлик", "емодзі"],
93
+ group: "Інше",
94
+ },
95
+ },
96
+ placeholders: {
97
+ default: "Введіть текст або наберіть '/' для команд",
98
+ heading: "Заголовок",
99
+ bulletListItem: "Список",
100
+ numberedListItem: "Список",
101
+ checkListItem: "Список",
102
+ },
103
+ file_blocks: {
104
+ image: {
105
+ add_button_text: "Додати зображення",
106
+ },
107
+ video: {
108
+ add_button_text: "Додати відео",
109
+ },
110
+ audio: {
111
+ add_button_text: "Додати аудіо",
112
+ },
113
+ file: {
114
+ add_button_text: "Додати файл",
115
+ },
116
+ },
117
+ // from react package:
118
+ side_menu: {
119
+ add_block_label: "Додати блок",
120
+ drag_handle_label: "Відкрити меню блока",
121
+ },
122
+ drag_handle: {
123
+ delete_menuitem: "Видалити",
124
+ colors_menuitem: "Кольори",
125
+ },
126
+ table_handle: {
127
+ delete_column_menuitem: "Видалити стовпець",
128
+ delete_row_menuitem: "Видалити рядок",
129
+ add_left_menuitem: "Додати стовпець зліва",
130
+ add_right_menuitem: "Додати стовпець справа",
131
+ add_above_menuitem: "Додати рядок вище",
132
+ add_below_menuitem: "Додати рядок нижче",
133
+ },
134
+ suggestion_menu: {
135
+ no_items_title: "Нічого не знайдено",
136
+ loading: "Завантаження…",
137
+ },
138
+ color_picker: {
139
+ text_title: "Текст",
140
+ background_title: "Фон",
141
+ colors: {
142
+ default: "За замовчуванням",
143
+ gray: "Сірий",
144
+ brown: "Коричневий",
145
+ red: "Червоний",
146
+ orange: "Помаранчевий",
147
+ yellow: "Жовтий",
148
+ green: "Зелений",
149
+ blue: "Блакитний",
150
+ purple: "Фіолетовий",
151
+ pink: "Рожевий",
152
+ },
153
+ },
154
+ formatting_toolbar: {
155
+ bold: {
156
+ tooltip: "Жирний",
157
+ secondary_tooltip: "Mod+B",
158
+ },
159
+ italic: {
160
+ tooltip: "Курсив",
161
+ secondary_tooltip: "Mod+I",
162
+ },
163
+ underline: {
164
+ tooltip: "Підкреслений",
165
+ secondary_tooltip: "Mod+U",
166
+ },
167
+ strike: {
168
+ tooltip: "Закреслений",
169
+ secondary_tooltip: "Mod+Shift+X",
170
+ },
171
+ code: {
172
+ tooltip: "Код",
173
+ secondary_tooltip: "",
174
+ },
175
+ colors: {
176
+ tooltip: "Кольори",
177
+ },
178
+ link: {
179
+ tooltip: "Створити посилання",
180
+ secondary_tooltip: "Mod+K",
181
+ },
182
+ file_caption: {
183
+ tooltip: "Редагувати підпис",
184
+ input_placeholder: "Редагувати підпис",
185
+ },
186
+ file_replace: {
187
+ tooltip: {
188
+ image: "Замінити зображення",
189
+ video: "Замінити відео",
190
+ audio: "Замінити аудіо",
191
+ file: "Замінити файл",
192
+ },
193
+ },
194
+ file_rename: {
195
+ tooltip: {
196
+ image: "Перейменувати зображення",
197
+ video: "Перейменувати відео",
198
+ audio: "Перейменувати аудіо",
199
+ file: "Перейменувати файл",
200
+ },
201
+ input_placeholder: {
202
+ image: "Перейменувати зображення",
203
+ video: "Перейменувати відео",
204
+ audio: "Перейменувати аудіо",
205
+ file: "Перейменувати файл",
206
+ },
207
+ },
208
+ file_download: {
209
+ tooltip: {
210
+ image: "Завантажити зображення",
211
+ video: "Завантажити відео",
212
+ audio: "Завантажити аудіо",
213
+ file: "Завантажити файл",
214
+ },
215
+ },
216
+ file_delete: {
217
+ tooltip: {
218
+ image: "Видалити зображення",
219
+ video: "Видалити відео",
220
+ audio: "Видалити аудіо",
221
+ file: "Видалити файл",
222
+ },
223
+ },
224
+ file_preview_toggle: {
225
+ tooltip: "Перемкнути попередній перегляд",
226
+ },
227
+ nest: {
228
+ tooltip: "Вкладений блок",
229
+ secondary_tooltip: "Tab",
230
+ },
231
+ unnest: {
232
+ tooltip: "Розгрупувати блок",
233
+ secondary_tooltip: "Shift+Tab",
234
+ },
235
+ align_left: {
236
+ tooltip: "Вирівняти за лівим краєм",
237
+ },
238
+ align_center: {
239
+ tooltip: "Вирівняти по центру",
240
+ },
241
+ align_right: {
242
+ tooltip: "Вирівняти за правим краєм",
243
+ },
244
+ align_justify: {
245
+ tooltip: "Вирівняти за шириною",
246
+ },
247
+ },
248
+ file_panel: {
249
+ upload: {
250
+ title: "Завантажити",
251
+ file_placeholder: {
252
+ image: "Завантажити зображення",
253
+ video: "Завантажити відео",
254
+ audio: "Завантажити аудіо",
255
+ file: "Завантажити файл",
256
+ },
257
+ upload_error: "Помилка: не вдалося завантажити",
258
+ },
259
+ embed: {
260
+ title: "Вставити",
261
+ embed_button: {
262
+ image: "Вставити зображення",
263
+ video: "Вставити відео",
264
+ audio: "Вставити аудіо",
265
+ file: "Вставити файл",
266
+ },
267
+ url_placeholder: "Введіть URL",
268
+ },
269
+ },
270
+ link_toolbar: {
271
+ delete: {
272
+ tooltip: "Видалити посилання",
273
+ },
274
+ edit: {
275
+ text: "Редагувати посилання",
276
+ tooltip: "Редагувати",
277
+ },
278
+ open: {
279
+ tooltip: "Відкрити в новій вкладці",
280
+ },
281
+ form: {
282
+ title_placeholder: "Редагувати заголовок",
283
+ url_placeholder: "Редагувати URL",
284
+ },
285
+ },
286
+ generic: {
287
+ ctrl_shortcut: "Ctrl",
288
+ },
289
+ };
@@ -2,7 +2,7 @@ import { DOMSerializer } from "prosemirror-model";
2
2
  import { PartialBlock } from "../../../../blocks/defaultBlocks.js";
3
3
  import type { BlockNoteEditor } from "../../../../editor/BlockNoteEditor.js";
4
4
  import { BlockSchema, InlineContentSchema, StyleSchema } from "../../../../schema/index.js";
5
- export declare function serializeInlineContentInternalHTML<BSchema extends BlockSchema, I extends InlineContentSchema, S extends StyleSchema>(editor: BlockNoteEditor<any, I, S>, blockContent: PartialBlock<BSchema, I, S>["content"], serializer: DOMSerializer, options?: {
5
+ export declare function serializeInlineContentInternalHTML<BSchema extends BlockSchema, I extends InlineContentSchema, S extends StyleSchema>(editor: BlockNoteEditor<any, I, S>, blockContent: PartialBlock<BSchema, I, S>["content"], serializer: DOMSerializer, blockType?: string, options?: {
6
6
  document?: Document;
7
7
  }): HTMLElement | DocumentFragment;
8
8
  export declare const serializeBlocksInternalHTML: <BSchema extends Record<string, import("../../../../schema/index.js").BlockConfig>, I extends InlineContentSchema, S extends StyleSchema>(editor: BlockNoteEditor<BSchema, I, S>, blocks: PartialBlock<BSchema, I, S>[], serializer: DOMSerializer, options?: {
@@ -4,7 +4,7 @@ import type { PartialBlock } from "../../blocks/defaultBlocks";
4
4
  /**
5
5
  * converts an array of inline content elements to prosemirror nodes
6
6
  */
7
- export declare function inlineContentToNodes<I extends InlineContentSchema, S extends StyleSchema>(blockContent: PartialInlineContent<I, S>, schema: Schema, styleSchema: S): Node[];
7
+ export declare function inlineContentToNodes<I extends InlineContentSchema, S extends StyleSchema>(blockContent: PartialInlineContent<I, S>, schema: Schema, styleSchema: S, blockType?: string): Node[];
8
8
  /**
9
9
  * converts an array of inline content elements to prosemirror nodes
10
10
  */
@@ -16,8 +16,9 @@ import { BlockNoteSchema } from "./BlockNoteSchema.js";
16
16
  import { BlockNoteTipTapEditor } from "./BlockNoteTipTapEditor.js";
17
17
  import { Dictionary } from "../i18n/dictionary.js";
18
18
  import { Plugin, Transaction } from "@tiptap/pm/state";
19
- import "../style.css";
20
19
  import { EditorView } from "prosemirror-view";
20
+ import "../style.css";
21
+ export type BlockNoteExtensionFactory = (editor: BlockNoteEditor<any, any, any>) => BlockNoteExtension;
21
22
  export type BlockNoteExtension = AnyExtension | {
22
23
  plugin: Plugin;
23
24
  };
@@ -111,7 +112,7 @@ export type BlockNoteEditorOptions<BSchema extends BlockSchema, ISchema extends
111
112
  /**
112
113
  * (experimental) add extra prosemirror plugins or tiptap extensions to the editor
113
114
  */
114
- _extensions: Record<string, BlockNoteExtension>;
115
+ _extensions: Record<string, BlockNoteExtension | BlockNoteExtensionFactory>;
115
116
  trailingBlock?: boolean;
116
117
  /**
117
118
  * Boolean indicating whether the editor is in headless mode.
@@ -1,6 +1,6 @@
1
- import type { BlockNoteEditor, BlockNoteExtension } from "./BlockNoteEditor.js";
2
1
  import { Plugin } from "prosemirror-state";
3
2
  import * as Y from "yjs";
3
+ import type { BlockNoteEditor, BlockNoteExtension } from "./BlockNoteEditor.js";
4
4
  import { BlockNoteDOMAttributes, BlockSchema, BlockSpecs, InlineContentSchema, InlineContentSpecs, StyleSchema, StyleSpecs } from "../schema/index.js";
5
5
  type ExtensionOptions<BSchema extends BlockSchema, I extends InlineContentSchema, S extends StyleSchema> = {
6
6
  editor: BlockNoteEditor<BSchema, I, S>;