@blocknote/core 0.25.2 → 0.27.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.
- package/dist/blocknote.cjs +11 -10
- package/dist/blocknote.cjs.map +1 -1
- package/dist/blocknote.js +3722 -9856
- package/dist/blocknote.js.map +1 -1
- package/dist/comments.cjs +1 -1
- package/dist/comments.cjs.map +1 -1
- package/dist/comments.js +45 -44
- package/dist/comments.js.map +1 -1
- package/dist/en-B7ycW7c8.js +360 -0
- package/dist/en-B7ycW7c8.js.map +1 -0
- package/dist/en-D4taoCs4.cjs +2 -0
- package/dist/en-D4taoCs4.cjs.map +1 -0
- package/dist/locales.cjs +2 -0
- package/dist/locales.cjs.map +1 -0
- package/dist/locales.js +6129 -0
- package/dist/locales.js.map +1 -0
- package/dist/style.css +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/webpack-stats.json +1 -1
- package/package.json +36 -27
- package/src/api/clipboard/__snapshots__/internal/basicBlocks.html +1 -1
- package/src/api/clipboard/__snapshots__/internal/basicBlocksWithProps.html +1 -1
- package/src/api/clipboard/clipboardExternal.test.ts +1 -1
- package/src/api/clipboard/clipboardInternal.test.ts +1 -1
- package/src/api/clipboard/fromClipboard/acceptedMIMETypes.ts +1 -0
- package/src/api/clipboard/fromClipboard/pasteExtension.ts +96 -42
- package/src/api/exporters/html/__snapshots__/codeBlock/contains-newlines/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/codeBlock/contains-newlines/internal.html +1 -1
- package/src/api/exporters/html/__snapshots__/codeBlock/defaultLanguage/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/codeBlock/defaultLanguage/internal.html +1 -1
- package/src/api/exporters/html/__snapshots__/codeBlock/empty/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/codeBlock/empty/internal.html +1 -1
- package/src/api/exporters/html/__snapshots__/codeBlock/python/external.html +1 -1
- package/src/api/exporters/html/__snapshots__/codeBlock/python/internal.html +1 -1
- package/src/api/exporters/html/htmlConversion.test.ts +2 -2
- package/src/api/exporters/html/util/serializeBlocksExternalHTML.ts +4 -4
- package/src/api/exporters/markdown/__snapshots__/codeBlock/defaultLanguage/markdown.md +1 -1
- package/src/api/exporters/markdown/__snapshots__/codeBlock/empty/markdown.md +1 -1
- package/src/api/exporters/markdown/__snapshots__/complex/misc/markdown.md +1 -1
- package/src/api/exporters/markdown/__snapshots__/lists/basic/markdown.md +8 -6
- package/src/api/exporters/markdown/__snapshots__/lists/nested/markdown.md +6 -6
- package/src/api/exporters/markdown/markdownExporter.test.ts +2 -2
- package/src/api/nodeConversions/__snapshots__/nodeConversions.test.ts.snap +2 -2
- package/src/api/nodeConversions/nodeToBlock.ts +1 -0
- package/src/api/parsers/html/__snapshots__/parse-2-tables.json +129 -0
- package/src/api/parsers/html/__snapshots__/parse-codeblocks.json +1 -1
- package/src/api/parsers/html/parseHTML.test.ts +36 -1
- package/src/api/parsers/markdown/__snapshots__/pasted/whitespace bold.json +42 -0
- package/src/api/parsers/markdown/__snapshots__/whitespace bold.json +19 -0
- package/src/api/parsers/markdown/detectMarkdown.ts +60 -0
- package/src/api/parsers/markdown/parseMarkdown.test.ts +7 -2
- package/src/api/parsers/markdown/parseMarkdown.ts +19 -18
- package/src/api/testUtil/cases/defaultSchema.ts +13 -0
- package/src/blocks/CodeBlockContent/CodeBlockContent.ts +100 -69
- package/src/blocks/QuoteBlockContent/QuoteBlockContent.ts +98 -0
- package/src/blocks/TableBlockContent/TableExtension.ts +1 -1
- package/src/blocks/defaultBlocks.ts +2 -2
- package/src/comments/threadstore/yjs/YjsThreadStore.ts +1 -0
- package/src/comments/threadstore/yjs/yjsHelpers.ts +3 -1
- package/src/comments/types.ts +4 -0
- package/src/editor/Block.css +14 -1
- package/src/editor/BlockNoteEditor.ts +103 -11
- package/src/editor/BlockNoteExtensions.ts +18 -4
- package/src/editor/BlockNoteTipTapEditor.ts +18 -7
- package/src/extensions/Comments/CommentsPlugin.ts +77 -30
- package/src/extensions/HardBreak/HardBreak.ts +35 -0
- package/src/extensions/KeyboardShortcuts/KeyboardShortcutsExtension.ts +100 -3
- package/src/extensions/SideMenu/dragging.ts +13 -0
- package/src/extensions/SuggestionMenu/SuggestionPlugin.ts +3 -1
- package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +18 -2
- package/src/extensions/TableHandles/TableHandlesPlugin.ts +24 -23
- package/src/i18n/index.ts +2 -0
- package/src/i18n/locales/ar.ts +10 -0
- package/src/i18n/locales/de.ts +21 -1
- package/src/i18n/locales/en.ts +10 -0
- package/src/i18n/locales/es.ts +21 -1
- package/src/i18n/locales/fr.ts +10 -0
- package/src/i18n/locales/hr.ts +27 -4
- package/src/i18n/locales/is.ts +10 -0
- package/src/i18n/locales/it.ts +21 -1
- package/src/i18n/locales/ja.ts +10 -0
- package/src/i18n/locales/ko.ts +10 -0
- package/src/i18n/locales/nl.ts +10 -0
- package/src/i18n/locales/no.ts +10 -0
- package/src/i18n/locales/pl.ts +10 -0
- package/src/i18n/locales/pt.ts +10 -0
- package/src/i18n/locales/ru.ts +10 -0
- package/src/i18n/locales/uk.ts +10 -0
- package/src/i18n/locales/vi.ts +10 -0
- package/src/i18n/locales/zh.ts +10 -0
- package/src/index.ts +2 -3
- package/src/locales.ts +1 -0
- package/src/schema/blocks/types.ts +1 -0
- package/types/src/api/blockManipulation/commands/updateBlock/updateBlock.d.ts +1 -1
- package/types/src/api/blockManipulation/setupTestEnv.d.ts +34 -2
- package/types/src/api/clipboard/fromClipboard/acceptedMIMETypes.d.ts +1 -1
- package/types/src/api/clipboard/fromClipboard/fileDropExtension.d.ts +2 -2
- package/types/src/api/clipboard/fromClipboard/pasteExtension.d.ts +3 -3
- package/types/src/api/clipboard/testUtil.d.ts +66 -34
- package/types/src/api/clipboard/toClipboard/copyExtension.d.ts +1 -1
- package/types/src/api/exporters/html/externalHTMLExporter.d.ts +2 -2
- package/types/src/api/exporters/html/internalHTMLSerializer.d.ts +2 -2
- package/types/src/api/exporters/html/util/serializeBlocksExternalHTML.d.ts +1 -1
- package/types/src/api/exporters/html/util/serializeBlocksInternalHTML.d.ts +1 -1
- package/types/src/api/parsers/markdown/detectMarkdown.d.ts +6 -0
- package/types/src/api/parsers/markdown/parseMarkdown.d.ts +1 -0
- package/types/src/api/testUtil/cases/customBlocks.d.ts +72 -40
- package/types/src/api/testUtil/cases/customInlineContent.d.ts +34 -2
- package/types/src/api/testUtil/cases/customStyles.d.ts +34 -2
- package/types/src/blocks/AudioBlockContent/AudioBlockContent.d.ts +3 -3
- package/types/src/blocks/CodeBlockContent/CodeBlockContent.d.ts +46 -34
- package/types/src/blocks/FileBlockContent/FileBlockContent.d.ts +3 -3
- package/types/src/blocks/FileBlockContent/helpers/render/createFileBlockWrapper.d.ts +1 -1
- package/types/src/blocks/HeadingBlockContent/HeadingBlockContent.d.ts +2 -2
- package/types/src/blocks/ImageBlockContent/ImageBlockContent.d.ts +2 -2
- package/types/src/blocks/ListItemBlockContent/BulletListItemBlockContent/BulletListItemBlockContent.d.ts +2 -2
- package/types/src/blocks/ListItemBlockContent/CheckListItemBlockContent/CheckListItemBlockContent.d.ts +2 -2
- package/types/src/blocks/ListItemBlockContent/NumberedListItemBlockContent/NumberedListItemBlockContent.d.ts +2 -2
- package/types/src/blocks/PageBreakBlockContent/PageBreakBlockContent.d.ts +2 -2
- package/types/src/blocks/PageBreakBlockContent/schema.d.ts +13 -13
- package/types/src/blocks/ParagraphBlockContent/ParagraphBlockContent.d.ts +2 -2
- package/types/src/blocks/QuoteBlockContent/QuoteBlockContent.d.ts +52 -0
- package/types/src/blocks/TableBlockContent/TableBlockContent.d.ts +2 -2
- package/types/src/blocks/VideoBlockContent/VideoBlockContent.d.ts +2 -2
- package/types/src/blocks/defaultBlockHelpers.d.ts +2 -2
- package/types/src/blocks/defaultBlocks.d.ts +107 -44
- package/types/src/comments/threadstore/yjs/YjsThreadStore.d.ts +3 -3
- package/types/src/comments/types.d.ts +4 -0
- package/types/src/editor/BlockNoteEditor.d.ts +58 -0
- package/types/src/editor/BlockNoteExtensions.d.ts +3 -2
- package/types/src/editor/BlockNoteTipTapEditor.d.ts +2 -1
- package/types/src/exporter/mapping.d.ts +2 -2
- package/types/src/extensions/BackgroundColor/BackgroundColorMark.d.ts +1 -1
- package/types/src/extensions/Collaboration/createCollaborationExtensions.d.ts +1 -1
- package/types/src/extensions/Comments/CommentsPlugin.d.ts +16 -1
- package/types/src/extensions/HardBreak/HardBreak.d.ts +2 -0
- package/types/src/extensions/TableHandles/TableHandlesPlugin.d.ts +4 -4
- package/types/src/extensions/TextColor/TextColorMark.d.ts +1 -1
- package/types/src/i18n/index.d.ts +2 -0
- package/types/src/i18n/locales/de.d.ts +2 -304
- package/types/src/i18n/locales/en.d.ts +11 -1
- package/types/src/i18n/locales/es.d.ts +2 -269
- package/types/src/i18n/locales/hr.d.ts +2 -266
- package/types/src/i18n/locales/it.d.ts +2 -269
- package/types/src/index.d.ts +1 -2
- package/types/src/locales.d.ts +1 -0
- package/types/src/pm-nodes/BlockContainer.d.ts +2 -7
- package/types/src/pm-nodes/BlockGroup.d.ts +2 -7
- package/types/src/schema/blocks/types.d.ts +1 -0
- package/types/src/schema/inlineContent/internal.d.ts +1 -1
- package/README.md +0 -125
- package/src/blocks/CodeBlockContent/defaultSupportedLanguages.ts +0 -116
- package/types/src/blocks/CodeBlockContent/shiki.bundle.d.ts +0 -82
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { AnyExtension, Extension, extensions } from "@tiptap/core";
|
|
2
2
|
import { Gapcursor } from "@tiptap/extension-gapcursor";
|
|
3
|
-
import { HardBreak } from "@tiptap/extension-hard-break";
|
|
4
3
|
import { History } from "@tiptap/extension-history";
|
|
5
4
|
import { Link } from "@tiptap/extension-link";
|
|
6
5
|
import { Text } from "@tiptap/extension-text";
|
|
@@ -17,6 +16,7 @@ import { CommentsPlugin } from "../extensions/Comments/CommentsPlugin.js";
|
|
|
17
16
|
import type { ThreadStore } from "../comments/index.js";
|
|
18
17
|
import { FilePanelProsemirrorPlugin } from "../extensions/FilePanel/FilePanelPlugin.js";
|
|
19
18
|
import { FormattingToolbarProsemirrorPlugin } from "../extensions/FormattingToolbar/FormattingToolbarPlugin.js";
|
|
19
|
+
import { HardBreak } from "../extensions/HardBreak/HardBreak.js";
|
|
20
20
|
import { KeyboardShortcutsExtension } from "../extensions/KeyboardShortcuts/KeyboardShortcutsExtension.js";
|
|
21
21
|
import { LinkToolbarProsemirrorPlugin } from "../extensions/LinkToolbar/LinkToolbarPlugin.js";
|
|
22
22
|
import {
|
|
@@ -44,7 +44,11 @@ import {
|
|
|
44
44
|
StyleSchema,
|
|
45
45
|
StyleSpecs,
|
|
46
46
|
} from "../schema/index.js";
|
|
47
|
-
import type {
|
|
47
|
+
import type {
|
|
48
|
+
BlockNoteEditor,
|
|
49
|
+
BlockNoteEditorOptions,
|
|
50
|
+
BlockNoteExtension,
|
|
51
|
+
} from "./BlockNoteEditor.js";
|
|
48
52
|
|
|
49
53
|
type ExtensionOptions<
|
|
50
54
|
BSchema extends BlockSchema,
|
|
@@ -82,6 +86,7 @@ type ExtensionOptions<
|
|
|
82
86
|
comments?: {
|
|
83
87
|
threadStore: ThreadStore;
|
|
84
88
|
};
|
|
89
|
+
pasteHandler: BlockNoteEditorOptions<any, any, any>["pasteHandler"];
|
|
85
90
|
};
|
|
86
91
|
|
|
87
92
|
/**
|
|
@@ -179,7 +184,7 @@ const getTipTapExtensions = <
|
|
|
179
184
|
types: ["blockContainer", "columnList", "column"],
|
|
180
185
|
setIdAttribute: opts.setIdAttribute,
|
|
181
186
|
}),
|
|
182
|
-
HardBreak
|
|
187
|
+
HardBreak,
|
|
183
188
|
// Comments,
|
|
184
189
|
|
|
185
190
|
// basics:
|
|
@@ -258,7 +263,16 @@ const getTipTapExtensions = <
|
|
|
258
263
|
];
|
|
259
264
|
}),
|
|
260
265
|
createCopyToClipboardExtension(opts.editor),
|
|
261
|
-
createPasteFromClipboardExtension(
|
|
266
|
+
createPasteFromClipboardExtension(
|
|
267
|
+
opts.editor,
|
|
268
|
+
opts.pasteHandler ||
|
|
269
|
+
((context: {
|
|
270
|
+
defaultPasteHandler: (context?: {
|
|
271
|
+
prioritizeMarkdownOverHTML?: boolean;
|
|
272
|
+
plainTextAsMarkdown?: boolean;
|
|
273
|
+
}) => boolean | undefined;
|
|
274
|
+
}) => context.defaultPasteHandler())
|
|
275
|
+
),
|
|
262
276
|
createDropFileExtension(opts.editor),
|
|
263
277
|
|
|
264
278
|
// This needs to be at the bottom of this list, because Key events (such as enter, when selecting a /command),
|
|
@@ -10,6 +10,7 @@ import { EditorState, Transaction } from "@tiptap/pm/state";
|
|
|
10
10
|
import { blockToNode } from "../api/nodeConversions/blockToNode.js";
|
|
11
11
|
import { PartialBlock } from "../blocks/defaultBlocks.js";
|
|
12
12
|
import { StyleSchema } from "../schema/index.js";
|
|
13
|
+
import type { BlockNoteEditor } from "./BlockNoteEditor.js";
|
|
13
14
|
|
|
14
15
|
export type BlockNoteTipTapEditorOptions = Partial<
|
|
15
16
|
Omit<EditorOptions, "content">
|
|
@@ -149,7 +150,10 @@ export class BlockNoteTipTapEditor extends TiptapEditor {
|
|
|
149
150
|
/**
|
|
150
151
|
* Replace the default `createView` method with a custom one - which we call on mount
|
|
151
152
|
*/
|
|
152
|
-
private createViewAlternative(
|
|
153
|
+
private createViewAlternative(
|
|
154
|
+
blockNoteEditor: BlockNoteEditor<any, any, any>,
|
|
155
|
+
contentComponent?: any
|
|
156
|
+
) {
|
|
153
157
|
(this as any).contentComponent = contentComponent;
|
|
154
158
|
|
|
155
159
|
const markViews: any = {};
|
|
@@ -157,7 +161,8 @@ export class BlockNoteTipTapEditor extends TiptapEditor {
|
|
|
157
161
|
if (extension.type === "mark" && extension.config.addMarkView) {
|
|
158
162
|
// Note: migrate to using `addMarkView` from tiptap as soon as this lands
|
|
159
163
|
// (currently tiptap doesn't support markviews)
|
|
160
|
-
markViews[extension.name] =
|
|
164
|
+
markViews[extension.name] =
|
|
165
|
+
extension.config.addMarkView(blockNoteEditor);
|
|
161
166
|
}
|
|
162
167
|
});
|
|
163
168
|
|
|
@@ -169,19 +174,21 @@ export class BlockNoteTipTapEditor extends TiptapEditor {
|
|
|
169
174
|
dispatchTransaction: this.dispatchTransaction.bind(this),
|
|
170
175
|
state: this.state,
|
|
171
176
|
markViews,
|
|
177
|
+
nodeViews: this.extensionManager.nodeViews,
|
|
172
178
|
}
|
|
173
179
|
);
|
|
174
180
|
|
|
175
181
|
// `editor.view` is not yet available at this time.
|
|
176
|
-
// Therefore we will add all plugins
|
|
182
|
+
// Therefore we will add all plugins directly afterwards.
|
|
183
|
+
//
|
|
184
|
+
// To research: this is the default tiptap behavior, but might actually not be necessary
|
|
185
|
+
// it feels like it's a workaround for plugins that don't account for the view not being available yet
|
|
177
186
|
const newState = this.state.reconfigure({
|
|
178
187
|
plugins: this.extensionManager.plugins,
|
|
179
188
|
});
|
|
180
189
|
|
|
181
190
|
this.view.updateState(newState);
|
|
182
191
|
|
|
183
|
-
this.createNodeViews();
|
|
184
|
-
|
|
185
192
|
// emit the created event, call here manually because we blocked the default call in the constructor
|
|
186
193
|
// (https://github.com/ueberdosis/tiptap/blob/45bac803283446795ad1b03f43d3746fa54a68ff/packages/core/src/Editor.ts#L117)
|
|
187
194
|
this.commands.focus(
|
|
@@ -198,12 +205,16 @@ export class BlockNoteTipTapEditor extends TiptapEditor {
|
|
|
198
205
|
*
|
|
199
206
|
* @param element DOM element to mount to, ur null / undefined to destroy
|
|
200
207
|
*/
|
|
201
|
-
public mount = (
|
|
208
|
+
public mount = (
|
|
209
|
+
blockNoteEditor: BlockNoteEditor<any, any, any>,
|
|
210
|
+
element?: HTMLElement | null,
|
|
211
|
+
contentComponent?: any
|
|
212
|
+
) => {
|
|
202
213
|
if (!element) {
|
|
203
214
|
this.destroy();
|
|
204
215
|
} else {
|
|
205
216
|
this.options.element = element;
|
|
206
|
-
this.createViewAlternative(contentComponent);
|
|
217
|
+
this.createViewAlternative(blockNoteEditor, contentComponent);
|
|
207
218
|
}
|
|
208
219
|
};
|
|
209
220
|
}
|
|
@@ -16,11 +16,6 @@ const PLUGIN_KEY = new PluginKey(`blocknote-comments`);
|
|
|
16
16
|
const SET_SELECTED_THREAD_ID = "SET_SELECTED_THREAD_ID";
|
|
17
17
|
|
|
18
18
|
type CommentsPluginState = {
|
|
19
|
-
/**
|
|
20
|
-
* Store the positions of all threads in the document.
|
|
21
|
-
* this can be used later to implement a floating sidebar
|
|
22
|
-
*/
|
|
23
|
-
threadPositions: Map<string, { from: number; to: number }>;
|
|
24
19
|
/**
|
|
25
20
|
* Decorations to be rendered, specifically to indicate the selected thread
|
|
26
21
|
*/
|
|
@@ -28,15 +23,11 @@ type CommentsPluginState = {
|
|
|
28
23
|
};
|
|
29
24
|
|
|
30
25
|
/**
|
|
31
|
-
*
|
|
26
|
+
* Calculate the thread positions from the current document state
|
|
32
27
|
*/
|
|
33
|
-
function
|
|
34
|
-
doc: Node,
|
|
35
|
-
selectedThreadId: string | undefined,
|
|
36
|
-
markType: string
|
|
37
|
-
): CommentsPluginState {
|
|
28
|
+
function getUpdatedThreadPositions(doc: Node, markType: string) {
|
|
38
29
|
const threadPositions = new Map<string, { from: number; to: number }>();
|
|
39
|
-
|
|
30
|
+
|
|
40
31
|
// find all thread marks and store their position + create decoration for selected thread
|
|
41
32
|
doc.descendants((node, pos) => {
|
|
42
33
|
node.marks.forEach((mark) => {
|
|
@@ -59,34 +50,38 @@ function updateState(
|
|
|
59
50
|
from: Math.min(from, currentPosition.from),
|
|
60
51
|
to: Math.max(to, currentPosition.to),
|
|
61
52
|
});
|
|
62
|
-
|
|
63
|
-
if (selectedThreadId === thisThreadId) {
|
|
64
|
-
decorations.push(
|
|
65
|
-
Decoration.inline(from, to, {
|
|
66
|
-
class: "bn-thread-mark-selected",
|
|
67
|
-
})
|
|
68
|
-
);
|
|
69
|
-
}
|
|
70
53
|
}
|
|
71
54
|
});
|
|
72
55
|
});
|
|
73
|
-
return
|
|
74
|
-
decorations: DecorationSet.create(doc, decorations),
|
|
75
|
-
threadPositions,
|
|
76
|
-
};
|
|
56
|
+
return threadPositions;
|
|
77
57
|
}
|
|
78
58
|
|
|
79
59
|
export class CommentsPlugin extends EventEmitter<any> {
|
|
80
60
|
public readonly plugin: Plugin;
|
|
81
61
|
public readonly userStore: UserStore<User>;
|
|
82
62
|
|
|
63
|
+
/**
|
|
64
|
+
* Whether a comment is currently being composed
|
|
65
|
+
*/
|
|
83
66
|
private pendingComment = false;
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* The currently selected thread id
|
|
70
|
+
*/
|
|
84
71
|
private selectedThreadId: string | undefined;
|
|
85
72
|
|
|
73
|
+
/**
|
|
74
|
+
* Store the positions of all threads in the document.
|
|
75
|
+
* this can be used later to implement a floating sidebar
|
|
76
|
+
*/
|
|
77
|
+
private threadPositions: Map<string, { from: number; to: number }> =
|
|
78
|
+
new Map();
|
|
79
|
+
|
|
86
80
|
private emitStateUpdate() {
|
|
87
81
|
this.emit("update", {
|
|
88
82
|
selectedThreadId: this.selectedThreadId,
|
|
89
83
|
pendingComment: this.pendingComment,
|
|
84
|
+
threadPositions: this.threadPositions,
|
|
90
85
|
});
|
|
91
86
|
}
|
|
92
87
|
|
|
@@ -111,7 +106,7 @@ export class CommentsPlugin extends EventEmitter<any> {
|
|
|
111
106
|
pos + node.nodeSize,
|
|
112
107
|
ttEditor.state.doc.content.size - 1
|
|
113
108
|
);
|
|
114
|
-
tr.removeMark(trimmedFrom, trimmedTo,
|
|
109
|
+
tr.removeMark(trimmedFrom, trimmedTo, mark);
|
|
115
110
|
tr.addMark(
|
|
116
111
|
trimmedFrom,
|
|
117
112
|
trimmedTo,
|
|
@@ -168,18 +163,51 @@ export class CommentsPlugin extends EventEmitter<any> {
|
|
|
168
163
|
state: {
|
|
169
164
|
init() {
|
|
170
165
|
return {
|
|
171
|
-
threadPositions: new Map<string, { from: number; to: number }>(),
|
|
172
166
|
decorations: DecorationSet.empty,
|
|
173
167
|
};
|
|
174
168
|
},
|
|
175
169
|
apply(tr, state) {
|
|
176
170
|
const action = tr.getMeta(PLUGIN_KEY);
|
|
171
|
+
|
|
177
172
|
if (!tr.docChanged && !action) {
|
|
178
173
|
return state;
|
|
179
174
|
}
|
|
180
175
|
|
|
181
|
-
//
|
|
182
|
-
|
|
176
|
+
// only update threadPositions if the doc changed
|
|
177
|
+
const threadPositions = tr.docChanged
|
|
178
|
+
? getUpdatedThreadPositions(tr.doc, self.markType)
|
|
179
|
+
: self.threadPositions;
|
|
180
|
+
|
|
181
|
+
if (threadPositions.size > 0 || self.threadPositions.size > 0) {
|
|
182
|
+
// small optimization; don't emit event if threadPositions before / after were both empty
|
|
183
|
+
self.threadPositions = threadPositions;
|
|
184
|
+
self.emitStateUpdate();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// update decorations if doc or selected thread changed
|
|
188
|
+
const decorations = [];
|
|
189
|
+
|
|
190
|
+
if (self.selectedThreadId) {
|
|
191
|
+
const selectedThreadPosition = threadPositions.get(
|
|
192
|
+
self.selectedThreadId
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
if (selectedThreadPosition) {
|
|
196
|
+
decorations.push(
|
|
197
|
+
Decoration.inline(
|
|
198
|
+
selectedThreadPosition.from,
|
|
199
|
+
selectedThreadPosition.to,
|
|
200
|
+
{
|
|
201
|
+
class: "bn-thread-mark-selected",
|
|
202
|
+
}
|
|
203
|
+
)
|
|
204
|
+
);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
decorations: DecorationSet.create(tr.doc, decorations),
|
|
210
|
+
};
|
|
183
211
|
},
|
|
184
212
|
},
|
|
185
213
|
props: {
|
|
@@ -206,7 +234,7 @@ export class CommentsPlugin extends EventEmitter<any> {
|
|
|
206
234
|
);
|
|
207
235
|
|
|
208
236
|
const threadId = commentMark?.attrs.threadId as string | undefined;
|
|
209
|
-
self.selectThread(threadId);
|
|
237
|
+
self.selectThread(threadId, false);
|
|
210
238
|
},
|
|
211
239
|
},
|
|
212
240
|
});
|
|
@@ -219,6 +247,7 @@ export class CommentsPlugin extends EventEmitter<any> {
|
|
|
219
247
|
callback: (state: {
|
|
220
248
|
pendingComment: boolean;
|
|
221
249
|
selectedThreadId: string | undefined;
|
|
250
|
+
threadPositions: Map<string, { from: number; to: number }>;
|
|
222
251
|
}) => void
|
|
223
252
|
) {
|
|
224
253
|
return this.on("update", callback);
|
|
@@ -227,7 +256,7 @@ export class CommentsPlugin extends EventEmitter<any> {
|
|
|
227
256
|
/**
|
|
228
257
|
* Set the selected thread
|
|
229
258
|
*/
|
|
230
|
-
public selectThread(threadId: string | undefined) {
|
|
259
|
+
public selectThread(threadId: string | undefined, scrollToThread = true) {
|
|
231
260
|
if (this.selectedThreadId === threadId) {
|
|
232
261
|
return;
|
|
233
262
|
}
|
|
@@ -238,6 +267,24 @@ export class CommentsPlugin extends EventEmitter<any> {
|
|
|
238
267
|
name: SET_SELECTED_THREAD_ID,
|
|
239
268
|
})
|
|
240
269
|
);
|
|
270
|
+
|
|
271
|
+
if (threadId && scrollToThread) {
|
|
272
|
+
const selectedThreadPosition = this.threadPositions.get(threadId);
|
|
273
|
+
|
|
274
|
+
if (!selectedThreadPosition) {
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// When a new thread is selected, scrolls the page to its reference text in
|
|
279
|
+
// the editor.
|
|
280
|
+
(
|
|
281
|
+
this.editor.prosemirrorView?.domAtPos(selectedThreadPosition.from)
|
|
282
|
+
.node as Element | undefined
|
|
283
|
+
)?.scrollIntoView({
|
|
284
|
+
behavior: "smooth",
|
|
285
|
+
block: "center",
|
|
286
|
+
});
|
|
287
|
+
}
|
|
241
288
|
}
|
|
242
289
|
|
|
243
290
|
/**
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
// Stripped down version of the TipTap HardBreak extension:
|
|
2
|
+
// https://github.com/ueberdosis/tiptap/blob/f3258d9ee5fb7979102fe63434f6ea4120507311/packages/extension-hard-break/src/hard-break.ts#L80
|
|
3
|
+
// Changes:
|
|
4
|
+
// - Removed options
|
|
5
|
+
// - Removed keyboard shortcuts & moved them to the `KeyboardShortcutsExtension`
|
|
6
|
+
// - Removed `setHardBreak` command (added a simpler version in the Shift+Enter
|
|
7
|
+
// handler in `KeyboardShortcutsExtension`).
|
|
8
|
+
// - Added priority
|
|
9
|
+
import { mergeAttributes, Node } from "@tiptap/core";
|
|
10
|
+
|
|
11
|
+
export const HardBreak = Node.create({
|
|
12
|
+
name: "hardBreak",
|
|
13
|
+
|
|
14
|
+
inline: true,
|
|
15
|
+
|
|
16
|
+
group: "inline",
|
|
17
|
+
|
|
18
|
+
selectable: false,
|
|
19
|
+
|
|
20
|
+
linebreakReplacement: true,
|
|
21
|
+
|
|
22
|
+
priority: 10,
|
|
23
|
+
|
|
24
|
+
parseHTML() {
|
|
25
|
+
return [{ tag: "br" }];
|
|
26
|
+
},
|
|
27
|
+
|
|
28
|
+
renderHTML({ HTMLAttributes }) {
|
|
29
|
+
return ["br", mergeAttributes(this.options.HTMLAttributes, HTMLAttributes)];
|
|
30
|
+
},
|
|
31
|
+
|
|
32
|
+
renderText() {
|
|
33
|
+
return "\n";
|
|
34
|
+
},
|
|
35
|
+
});
|
|
@@ -260,6 +260,73 @@ export const KeyboardShortcutsExtension = Extension.create<{
|
|
|
260
260
|
|
|
261
261
|
return true;
|
|
262
262
|
}),
|
|
263
|
+
// Deletes the current block if it's an empty block with inline content,
|
|
264
|
+
// and moves the selection to the previous block.
|
|
265
|
+
() =>
|
|
266
|
+
commands.command(({ state }) => {
|
|
267
|
+
const blockInfo = getBlockInfoFromSelection(state);
|
|
268
|
+
if (!blockInfo.isBlockContainer) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const blockEmpty =
|
|
273
|
+
blockInfo.blockContent.node.childCount === 0 &&
|
|
274
|
+
blockInfo.blockContent.node.type.spec.content === "inline*";
|
|
275
|
+
|
|
276
|
+
if (blockEmpty) {
|
|
277
|
+
const prevBlockInfo = getPrevBlockInfo(
|
|
278
|
+
state.doc,
|
|
279
|
+
blockInfo.bnBlock.beforePos
|
|
280
|
+
);
|
|
281
|
+
if (!prevBlockInfo || !prevBlockInfo.isBlockContainer) {
|
|
282
|
+
return false;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
let chainedCommands = chain();
|
|
286
|
+
|
|
287
|
+
if (
|
|
288
|
+
prevBlockInfo.blockContent.node.type.spec.content ===
|
|
289
|
+
"tableRow+"
|
|
290
|
+
) {
|
|
291
|
+
const tableBlockEndPos = blockInfo.bnBlock.beforePos - 1;
|
|
292
|
+
const tableBlockContentEndPos = tableBlockEndPos - 1;
|
|
293
|
+
const lastRowEndPos = tableBlockContentEndPos - 1;
|
|
294
|
+
const lastCellEndPos = lastRowEndPos - 1;
|
|
295
|
+
const lastCellParagraphEndPos = lastCellEndPos - 1;
|
|
296
|
+
|
|
297
|
+
chainedCommands = chainedCommands.setTextSelection(
|
|
298
|
+
lastCellParagraphEndPos
|
|
299
|
+
);
|
|
300
|
+
} else if (
|
|
301
|
+
prevBlockInfo.blockContent.node.type.spec.content === ""
|
|
302
|
+
) {
|
|
303
|
+
const nonEditableBlockContentStartPos =
|
|
304
|
+
prevBlockInfo.blockContent.afterPos -
|
|
305
|
+
prevBlockInfo.blockContent.node.nodeSize;
|
|
306
|
+
|
|
307
|
+
chainedCommands = chainedCommands.setNodeSelection(
|
|
308
|
+
nonEditableBlockContentStartPos
|
|
309
|
+
);
|
|
310
|
+
} else {
|
|
311
|
+
const blockContentStartPos =
|
|
312
|
+
prevBlockInfo.blockContent.afterPos -
|
|
313
|
+
prevBlockInfo.blockContent.node.nodeSize;
|
|
314
|
+
|
|
315
|
+
chainedCommands =
|
|
316
|
+
chainedCommands.setTextSelection(blockContentStartPos);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
return chainedCommands
|
|
320
|
+
.deleteRange({
|
|
321
|
+
from: blockInfo.bnBlock.beforePos,
|
|
322
|
+
to: blockInfo.bnBlock.afterPos,
|
|
323
|
+
})
|
|
324
|
+
.scrollIntoView()
|
|
325
|
+
.run();
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return false;
|
|
329
|
+
}),
|
|
263
330
|
// Deletes previous block if it contains no content and isn't a table,
|
|
264
331
|
// when the selection is empty and at the start of the block. Moves the
|
|
265
332
|
// current block into the deleted block's place.
|
|
@@ -370,8 +437,8 @@ export const KeyboardShortcutsExtension = Extension.create<{
|
|
|
370
437
|
}),
|
|
371
438
|
]);
|
|
372
439
|
|
|
373
|
-
const handleEnter = () =>
|
|
374
|
-
this.editor.commands.first(({ commands }) => [
|
|
440
|
+
const handleEnter = (withShift = false) => {
|
|
441
|
+
return this.editor.commands.first(({ commands }) => [
|
|
375
442
|
// Removes a level of nesting if the block is empty & indented, while the selection is also empty & at the start
|
|
376
443
|
// of the block.
|
|
377
444
|
() =>
|
|
@@ -400,6 +467,34 @@ export const KeyboardShortcutsExtension = Extension.create<{
|
|
|
400
467
|
return commands.liftListItem("blockContainer");
|
|
401
468
|
}
|
|
402
469
|
|
|
470
|
+
return false;
|
|
471
|
+
}),
|
|
472
|
+
// Creates a hard break if block is configured to do so.
|
|
473
|
+
() =>
|
|
474
|
+
commands.command(({ state }) => {
|
|
475
|
+
const blockInfo = getBlockInfoFromSelection(state);
|
|
476
|
+
|
|
477
|
+
const blockHardBreakShortcut: "shift+enter" | "enter" | "none" =
|
|
478
|
+
this.options.editor.schema.blockSchema[blockInfo.blockNoteType]
|
|
479
|
+
.hardBreakShortcut ?? "shift+enter";
|
|
480
|
+
|
|
481
|
+
if (blockHardBreakShortcut === "none") {
|
|
482
|
+
return false;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (
|
|
486
|
+
// If shortcut is not configured, or is configured as "shift+enter",
|
|
487
|
+
// create a hard break for shift+enter, but not for enter.
|
|
488
|
+
(blockHardBreakShortcut === "shift+enter" && withShift) ||
|
|
489
|
+
// If shortcut is configured as "enter", create a hard break for
|
|
490
|
+
// both enter and shift+enter.
|
|
491
|
+
blockHardBreakShortcut === "enter"
|
|
492
|
+
) {
|
|
493
|
+
return commands.insertContent({
|
|
494
|
+
type: "hardBreak",
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
|
|
403
498
|
return false;
|
|
404
499
|
}),
|
|
405
500
|
// Creates a new block and moves the selection to it if the current one is empty, while the selection is also
|
|
@@ -471,11 +566,13 @@ export const KeyboardShortcutsExtension = Extension.create<{
|
|
|
471
566
|
return false;
|
|
472
567
|
}),
|
|
473
568
|
]);
|
|
569
|
+
};
|
|
474
570
|
|
|
475
571
|
return {
|
|
476
572
|
Backspace: handleBackspace,
|
|
477
573
|
Delete: handleDelete,
|
|
478
|
-
Enter: handleEnter,
|
|
574
|
+
Enter: () => handleEnter(),
|
|
575
|
+
"Shift-Enter": () => handleEnter(true),
|
|
479
576
|
// Always returning true for tab key presses ensures they're not captured by the browser. Otherwise, they blur the
|
|
480
577
|
// editor since the browser will try to use tab for keyboard navigation.
|
|
481
578
|
Tab: () => {
|
|
@@ -99,6 +99,19 @@ function setDragImage(view: EditorView, from: number, to = from) {
|
|
|
99
99
|
unsetDragImage(view.root);
|
|
100
100
|
dragImageElement = parentClone;
|
|
101
101
|
|
|
102
|
+
// Browsers may have CORS policies which prevents iframes from being
|
|
103
|
+
// manipulated, so better to stay on the safe side and remove them from the
|
|
104
|
+
// drag preview. The drag preview doesn't work with iframes anyway.
|
|
105
|
+
const iframes = dragImageElement.getElementsByTagName("iframe");
|
|
106
|
+
for (let i = 0; i < iframes.length; i++) {
|
|
107
|
+
const iframe = iframes[i];
|
|
108
|
+
const parent = iframe.parentElement;
|
|
109
|
+
|
|
110
|
+
if (parent) {
|
|
111
|
+
parent.removeChild(iframe);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
102
115
|
// TODO: This is hacky, need a better way of assigning classes to the editor so that they can also be applied to the
|
|
103
116
|
// drag preview.
|
|
104
117
|
const classes = view.dom.className.split(" ");
|
|
@@ -86,7 +86,9 @@ class SuggestionMenuView<
|
|
|
86
86
|
this.pluginState = stopped ? prev : next;
|
|
87
87
|
|
|
88
88
|
if (stopped || !this.editor.isEditable) {
|
|
89
|
-
this.state
|
|
89
|
+
if (this.state) {
|
|
90
|
+
this.state.show = false;
|
|
91
|
+
}
|
|
90
92
|
this.emitUpdate(this.pluginState!.triggerCharacter);
|
|
91
93
|
|
|
92
94
|
return;
|
|
@@ -20,11 +20,15 @@ function setSelectionToNextContentEditableBlock<
|
|
|
20
20
|
I extends InlineContentSchema,
|
|
21
21
|
S extends StyleSchema
|
|
22
22
|
>(editor: BlockNoteEditor<BSchema, I, S>) {
|
|
23
|
-
let block =
|
|
23
|
+
let block: Block<BSchema, I, S> | undefined =
|
|
24
|
+
editor.getTextCursorPosition().block;
|
|
24
25
|
let contentType = editor.schema.blockSchema[block.type].content;
|
|
25
26
|
|
|
26
27
|
while (contentType === "none") {
|
|
27
|
-
block = editor.getTextCursorPosition().nextBlock
|
|
28
|
+
block = editor.getTextCursorPosition().nextBlock;
|
|
29
|
+
if (block === undefined) {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
28
32
|
contentType = editor.schema.blockSchema[block.type].content as
|
|
29
33
|
| "inline"
|
|
30
34
|
| "table"
|
|
@@ -121,6 +125,18 @@ export function getDefaultSlashMenuItems<
|
|
|
121
125
|
);
|
|
122
126
|
}
|
|
123
127
|
|
|
128
|
+
if (checkDefaultBlockTypeInSchema("quote", editor)) {
|
|
129
|
+
items.push({
|
|
130
|
+
onItemClick: () => {
|
|
131
|
+
insertOrUpdateBlock(editor, {
|
|
132
|
+
type: "quote",
|
|
133
|
+
});
|
|
134
|
+
},
|
|
135
|
+
key: "quote",
|
|
136
|
+
...editor.dictionary.slash_menu.quote,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
124
140
|
if (checkDefaultBlockTypeInSchema("numberedListItem", editor)) {
|
|
125
141
|
items.push({
|
|
126
142
|
onItemClick: () => {
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { Plugin, PluginKey, PluginView } from "prosemirror-state";
|
|
2
2
|
import {
|
|
3
|
+
CellSelection,
|
|
3
4
|
addColumnAfter,
|
|
4
5
|
addColumnBefore,
|
|
5
6
|
addRowAfter,
|
|
6
7
|
addRowBefore,
|
|
7
|
-
CellSelection,
|
|
8
8
|
deleteColumn,
|
|
9
9
|
deleteRow,
|
|
10
10
|
mergeCells,
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from "prosemirror-tables";
|
|
13
13
|
import { Decoration, DecorationSet, EditorView } from "prosemirror-view";
|
|
14
14
|
import {
|
|
15
|
+
RelativeCellIndices,
|
|
15
16
|
addRowsOrColumns,
|
|
16
17
|
areInSameColumn,
|
|
17
18
|
canColumnBeDraggedInto,
|
|
@@ -22,7 +23,6 @@ import {
|
|
|
22
23
|
getDimensionsOfTable,
|
|
23
24
|
moveColumn,
|
|
24
25
|
moveRow,
|
|
25
|
-
RelativeCellIndices,
|
|
26
26
|
} from "../../api/blockManipulation/tables/tables.js";
|
|
27
27
|
import { nodeToBlock } from "../../api/nodeConversions/nodeToBlock.js";
|
|
28
28
|
import { getNodeById } from "../../api/nodeUtil.js";
|
|
@@ -539,7 +539,12 @@ export class TableHandlesView<
|
|
|
539
539
|
|
|
540
540
|
// Hide handles if the table block has been removed.
|
|
541
541
|
this.state.block = this.editor.getBlock(this.state.block.id)!;
|
|
542
|
-
if (
|
|
542
|
+
if (
|
|
543
|
+
!this.state.block ||
|
|
544
|
+
// when collaborating, the table element might be replaced and out of date
|
|
545
|
+
// because yjs replaces the element when for example you change the color via the side menu
|
|
546
|
+
!this.tableElement?.isConnected
|
|
547
|
+
) {
|
|
543
548
|
this.state.show = false;
|
|
544
549
|
this.state.showAddOrRemoveRowsButton = false;
|
|
545
550
|
this.state.showAddOrRemoveColumnsButton = false;
|
|
@@ -569,6 +574,7 @@ export class TableHandlesView<
|
|
|
569
574
|
|
|
570
575
|
// Update bounding boxes.
|
|
571
576
|
const tableBody = this.tableElement!.querySelector("tbody");
|
|
577
|
+
|
|
572
578
|
if (!tableBody) {
|
|
573
579
|
throw new Error(
|
|
574
580
|
"Table block does not contain a 'tbody' HTML element. This should never happen."
|
|
@@ -656,32 +662,27 @@ export class TableHandlesProsemirrorPlugin<
|
|
|
656
662
|
}
|
|
657
663
|
|
|
658
664
|
const decorations: Decoration[] = [];
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
this.view.state.block,
|
|
675
|
-
this.view.state.draggingState.originalIndex,
|
|
676
|
-
newIndex
|
|
677
|
-
)
|
|
665
|
+
const { block, draggingState } = this.view.state;
|
|
666
|
+
const { originalIndex, draggedCellOrientation } = draggingState;
|
|
667
|
+
|
|
668
|
+
// Return empty decorations if:
|
|
669
|
+
// - Dragging to same position
|
|
670
|
+
// - No block exists
|
|
671
|
+
// - Row drag not allowed
|
|
672
|
+
// - Column drag not allowed
|
|
673
|
+
if (
|
|
674
|
+
newIndex === originalIndex ||
|
|
675
|
+
!block ||
|
|
676
|
+
(draggedCellOrientation === "row" &&
|
|
677
|
+
!canRowBeDraggedInto(block, originalIndex, newIndex)) ||
|
|
678
|
+
(draggedCellOrientation === "col" &&
|
|
679
|
+
!canColumnBeDraggedInto(block, originalIndex, newIndex))
|
|
678
680
|
) {
|
|
679
681
|
return DecorationSet.create(state.doc, decorations);
|
|
680
682
|
}
|
|
681
683
|
|
|
682
684
|
// Gets the table to show the drop cursor in.
|
|
683
685
|
const tableResolvedPos = state.doc.resolve(this.view.tablePos + 1);
|
|
684
|
-
const originalIndex = this.view.state.draggingState.originalIndex;
|
|
685
686
|
|
|
686
687
|
if (this.view.state.draggingState.draggedCellOrientation === "row") {
|
|
687
688
|
const cellsInRow = getCellsAtRowHandle(
|