@blocknote/core 0.44.2 → 0.46.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/BlockNoteExtension-BWw0r8Gy.cjs.map +1 -1
- package/dist/BlockNoteExtension-C2X7LW-V.js.map +1 -1
- package/dist/{BlockNoteSchema-BsTi0fNS.js → BlockNoteSchema-DsMVJZv4.js} +2 -2
- package/dist/{BlockNoteSchema-BsTi0fNS.js.map → BlockNoteSchema-DsMVJZv4.js.map} +1 -1
- package/dist/{BlockNoteSchema-CBNkNhkw.cjs → BlockNoteSchema-qt4Czo0-.cjs} +2 -2
- package/dist/{BlockNoteSchema-CBNkNhkw.cjs.map → BlockNoteSchema-qt4Czo0-.cjs.map} +1 -1
- package/dist/ShowSelection-B0ch3unP.js +51 -0
- package/dist/ShowSelection-B0ch3unP.js.map +1 -0
- package/dist/ShowSelection-BxnbRvy4.cjs +2 -0
- package/dist/ShowSelection-BxnbRvy4.cjs.map +1 -0
- package/dist/{TrailingNode-CG2a-HDA.js → TrailingNode-C-Kyrtf1.js} +715 -709
- package/dist/TrailingNode-C-Kyrtf1.js.map +1 -0
- package/dist/TrailingNode-W7GJVng5.cjs +2 -0
- package/dist/TrailingNode-W7GJVng5.cjs.map +1 -0
- package/dist/{blockToNode-DBNbhwwC.js → blockToNode-BNoNIXU7.js} +2 -2
- package/dist/{blockToNode-DBNbhwwC.js.map → blockToNode-BNoNIXU7.js.map} +1 -1
- package/dist/{blockToNode-w7H99R6p.cjs → blockToNode-CumVjgem.cjs} +2 -2
- package/dist/{blockToNode-w7H99R6p.cjs.map → blockToNode-CumVjgem.cjs.map} +1 -1
- package/dist/blocknote.cjs +4 -4
- package/dist/blocknote.cjs.map +1 -1
- package/dist/blocknote.js +1118 -1077
- package/dist/blocknote.js.map +1 -1
- package/dist/blocks.cjs +1 -1
- package/dist/blocks.js +2 -2
- package/dist/comments.cjs +1 -1
- package/dist/comments.cjs.map +1 -1
- package/dist/comments.js +3 -3
- package/dist/comments.js.map +1 -1
- package/dist/{defaultBlocks-B63ufZ5N.js → defaultBlocks-CXOCngjC.js} +273 -312
- package/dist/defaultBlocks-CXOCngjC.js.map +1 -0
- package/dist/defaultBlocks-IsUGVZIq.cjs +6 -0
- package/dist/defaultBlocks-IsUGVZIq.cjs.map +1 -0
- package/dist/extensions.cjs +1 -1
- package/dist/extensions.js +4 -4
- package/dist/style.css +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/webpack-stats.json +1 -1
- package/dist/yjs.cjs +1 -1
- package/dist/yjs.js +1 -1
- package/package.json +18 -18
- package/src/api/blockManipulation/selections/selection.ts +9 -4
- package/src/api/blockManipulation/tables/tables.test.ts +140 -0
- package/src/api/blockManipulation/tables/tables.ts +1 -1
- package/src/api/exporters/html/util/serializeBlocksExternalHTML.ts +17 -0
- package/src/api/parsers/markdown/parseMarkdown.ts +11 -0
- package/src/blocks/ListItem/BulletListItem/block.ts +1 -1
- package/src/blocks/ListItem/CheckListItem/block.ts +6 -4
- package/src/blocks/ListItem/NumberedListItem/block.ts +6 -2
- package/src/comments/extension.ts +6 -2
- package/src/editor/Block.css +1 -1
- package/src/editor/BlockNoteEditor.test.ts +0 -1
- package/src/editor/BlockNoteEditor.ts +9 -39
- package/src/editor/BlockNoteExtension.ts +5 -0
- package/src/editor/managers/EventManager.ts +1 -1
- package/src/editor/managers/ExtensionManager/extensions.ts +3 -13
- package/src/editor/managers/ExtensionManager/index.ts +7 -2
- package/src/editor/managers/SelectionManager.ts +10 -10
- package/src/extensions/BlockChange/BlockChange.ts +2 -2
- package/src/extensions/Collaboration/Collaboration.ts +55 -0
- package/src/extensions/Collaboration/ForkYDoc.ts +4 -9
- package/src/extensions/Collaboration/YCursorPlugin.ts +56 -60
- package/src/extensions/Collaboration/YSync.ts +2 -2
- package/src/extensions/Collaboration/YUndo.ts +2 -2
- package/src/extensions/LinkToolbar/LinkToolbar.ts +1 -1
- package/src/extensions/ShowSelection/ShowSelection.ts +14 -4
- package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +40 -68
- package/src/extensions/TableHandles/TableHandles.ts +9 -5
- package/src/index.ts +2 -1
- package/src/schema/blocks/createSpec.ts +3 -0
- package/src/util/expandToWords.ts +38 -0
- package/types/src/api/blockManipulation/selections/selection.d.ts +1 -1
- package/types/src/editor/BlockNoteEditor.d.ts +5 -34
- package/types/src/editor/BlockNoteExtension.d.ts +4 -0
- package/types/src/editor/managers/SelectionManager.d.ts +4 -4
- package/types/src/extensions/Collaboration/Collaboration.d.ts +76 -0
- package/types/src/extensions/Collaboration/ForkYDoc.d.ts +2 -11
- package/types/src/extensions/Collaboration/YCursorPlugin.d.ts +3 -11
- package/types/src/extensions/Collaboration/YSync.d.ts +2 -4
- package/types/src/extensions/Collaboration/YUndo.d.ts +1 -1
- package/types/src/extensions/ShowSelection/ShowSelection.d.ts +10 -4
- package/types/src/index.d.ts +2 -1
- package/types/src/util/expandToWords.d.ts +13 -0
- package/dist/ShowSelection-BW37oJ6h.cjs +0 -2
- package/dist/ShowSelection-BW37oJ6h.cjs.map +0 -1
- package/dist/ShowSelection-Dz-NEase.js +0 -43
- package/dist/ShowSelection-Dz-NEase.js.map +0 -1
- package/dist/TrailingNode-CG2a-HDA.js.map +0 -1
- package/dist/TrailingNode-Du4SNHun.cjs +0 -2
- package/dist/TrailingNode-Du4SNHun.cjs.map +0 -1
- package/dist/defaultBlocks-B63ufZ5N.js.map +0 -1
- package/dist/defaultBlocks-BX6UxQa8.cjs +0 -6
- package/dist/defaultBlocks-BX6UxQa8.cjs.map +0 -1
|
@@ -7,8 +7,6 @@ import {
|
|
|
7
7
|
} from "@tiptap/core";
|
|
8
8
|
import { type Command, type Plugin, type Transaction } from "@tiptap/pm/state";
|
|
9
9
|
import { Node, Schema } from "prosemirror-model";
|
|
10
|
-
import * as Y from "yjs";
|
|
11
|
-
|
|
12
10
|
import type { BlocksChanged } from "../api/getBlocksChangedByTransaction.js";
|
|
13
11
|
import { blockToNode } from "../api/nodeConversions/blockToNode.js";
|
|
14
12
|
import {
|
|
@@ -19,6 +17,8 @@ import {
|
|
|
19
17
|
DefaultStyleSchema,
|
|
20
18
|
PartialBlock,
|
|
21
19
|
} from "../blocks/index.js";
|
|
20
|
+
import type { CollaborationOptions } from "../extensions/Collaboration/Collaboration.js";
|
|
21
|
+
import { BlockChangeExtension } from "../extensions/index.js";
|
|
22
22
|
import { UniqueID } from "../extensions/tiptap-extensions/UniqueID/UniqueID.js";
|
|
23
23
|
import type { Dictionary } from "../i18n/dictionary.js";
|
|
24
24
|
import { en } from "../i18n/locales/index.js";
|
|
@@ -52,7 +52,6 @@ import {
|
|
|
52
52
|
} from "./managers/index.js";
|
|
53
53
|
import type { Selection } from "./selectionTypes.js";
|
|
54
54
|
import { transformPasted } from "./transformPasted.js";
|
|
55
|
-
import { BlockChangeExtension } from "../extensions/index.js";
|
|
56
55
|
|
|
57
56
|
export type BlockCache<
|
|
58
57
|
BSchema extends BlockSchema = any,
|
|
@@ -82,37 +81,8 @@ export interface BlockNoteEditorOptions<
|
|
|
82
81
|
/**
|
|
83
82
|
* When enabled, allows for collaboration between multiple users.
|
|
84
83
|
* See [Real-time Collaboration](https://www.blocknotejs.org/docs/advanced/real-time-collaboration) for more info.
|
|
85
|
-
*
|
|
86
|
-
* @remarks `CollaborationOptions`
|
|
87
84
|
*/
|
|
88
|
-
collaboration?:
|
|
89
|
-
/**
|
|
90
|
-
* The Yjs XML fragment that's used for collaboration.
|
|
91
|
-
*/
|
|
92
|
-
fragment: Y.XmlFragment;
|
|
93
|
-
/**
|
|
94
|
-
* The user info for the current user that's shown to other collaborators.
|
|
95
|
-
*/
|
|
96
|
-
user: {
|
|
97
|
-
name: string;
|
|
98
|
-
color: string;
|
|
99
|
-
};
|
|
100
|
-
/**
|
|
101
|
-
* A Yjs provider (used for awareness / cursor information)
|
|
102
|
-
*/
|
|
103
|
-
provider: any;
|
|
104
|
-
/**
|
|
105
|
-
* Optional function to customize how cursors of users are rendered
|
|
106
|
-
*/
|
|
107
|
-
renderCursor?: (user: any) => HTMLElement;
|
|
108
|
-
/**
|
|
109
|
-
* Optional flag to set when the user label should be shown with the default
|
|
110
|
-
* collaboration cursor. Setting to "always" will always show the label,
|
|
111
|
-
* while "activity" will only show the label when the user moves the cursor
|
|
112
|
-
* or types. Defaults to "activity".
|
|
113
|
-
*/
|
|
114
|
-
showCursorLabels?: "always" | "activity";
|
|
115
|
-
};
|
|
85
|
+
collaboration?: CollaborationOptions;
|
|
116
86
|
|
|
117
87
|
/**
|
|
118
88
|
* Use default BlockNote font and reset the styles of <p> <li> <h1> elements etc., that are used in BlockNote.
|
|
@@ -912,13 +882,13 @@ export class BlockNoteEditor<
|
|
|
912
882
|
*/
|
|
913
883
|
public onBeforeChange(
|
|
914
884
|
callback: (context: {
|
|
915
|
-
getChanges: () => BlocksChanged<
|
|
885
|
+
getChanges: () => BlocksChanged<BSchema, ISchema, SSchema>;
|
|
916
886
|
tr: Transaction;
|
|
917
887
|
}) => boolean | void,
|
|
918
|
-
) {
|
|
888
|
+
): () => void {
|
|
919
889
|
return this._extensionManager
|
|
920
|
-
.getExtension(BlockChangeExtension)
|
|
921
|
-
|
|
890
|
+
.getExtension(BlockChangeExtension)!
|
|
891
|
+
.subscribe(callback);
|
|
922
892
|
}
|
|
923
893
|
|
|
924
894
|
/**
|
|
@@ -963,8 +933,8 @@ export class BlockNoteEditor<
|
|
|
963
933
|
* If the selection starts / ends halfway through a block, the returned block will be
|
|
964
934
|
* only the part of the block that is included in the selection.
|
|
965
935
|
*/
|
|
966
|
-
public getSelectionCutBlocks() {
|
|
967
|
-
return this._selectionManager.getSelectionCutBlocks();
|
|
936
|
+
public getSelectionCutBlocks(expandToWords = false) {
|
|
937
|
+
return this._selectionManager.getSelectionCutBlocks(expandToWords);
|
|
968
938
|
}
|
|
969
939
|
|
|
970
940
|
/**
|
|
@@ -89,6 +89,11 @@ export interface Extension<State = any, Key extends string = string> {
|
|
|
89
89
|
* Add additional tiptap extensions to the editor.
|
|
90
90
|
*/
|
|
91
91
|
readonly tiptapExtensions?: ReadonlyArray<AnyExtension>;
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Add additional BlockNote extensions to the editor.
|
|
95
|
+
*/
|
|
96
|
+
readonly blockNoteExtensions?: ReadonlyArray<ExtensionFactoryInstance>;
|
|
92
97
|
}
|
|
93
98
|
|
|
94
99
|
/**
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
Node,
|
|
5
5
|
Extension as TiptapExtension,
|
|
6
6
|
} from "@tiptap/core";
|
|
7
|
-
import { Gapcursor } from "@tiptap/
|
|
7
|
+
import { Gapcursor } from "@tiptap/extensions/gap-cursor";
|
|
8
8
|
import { Link } from "@tiptap/extension-link";
|
|
9
9
|
import { Text } from "@tiptap/extension-text";
|
|
10
10
|
import { createDropFileExtension } from "../../../api/clipboard/fromClipboard/fileDropExtension.js";
|
|
@@ -14,22 +14,17 @@ import {
|
|
|
14
14
|
BlockChangeExtension,
|
|
15
15
|
DropCursorExtension,
|
|
16
16
|
FilePanelExtension,
|
|
17
|
-
ForkYDocExtension,
|
|
18
17
|
FormattingToolbarExtension,
|
|
19
18
|
HistoryExtension,
|
|
20
19
|
LinkToolbarExtension,
|
|
21
20
|
NodeSelectionKeyboardExtension,
|
|
22
21
|
PlaceholderExtension,
|
|
23
22
|
PreviousBlockTypeExtension,
|
|
24
|
-
SchemaMigration,
|
|
25
23
|
ShowSelectionExtension,
|
|
26
24
|
SideMenuExtension,
|
|
27
25
|
SuggestionMenu,
|
|
28
26
|
TableHandlesExtension,
|
|
29
27
|
TrailingNodeExtension,
|
|
30
|
-
YCursorExtension,
|
|
31
|
-
YSyncExtension,
|
|
32
|
-
YUndoExtension,
|
|
33
28
|
} from "../../../extensions/index.js";
|
|
34
29
|
import {
|
|
35
30
|
DEFAULT_LINK_PROTOCOL,
|
|
@@ -52,6 +47,7 @@ import {
|
|
|
52
47
|
BlockNoteEditorOptions,
|
|
53
48
|
} from "../../BlockNoteEditor.js";
|
|
54
49
|
import { ExtensionFactoryInstance } from "../../BlockNoteExtension.js";
|
|
50
|
+
import { CollaborationExtension } from "../../../extensions/Collaboration/Collaboration.js";
|
|
55
51
|
|
|
56
52
|
// TODO remove linkify completely by vendoring the link extension & dropping linkifyjs as a dependency
|
|
57
53
|
let LINKIFY_INITIALIZED = false;
|
|
@@ -190,13 +186,7 @@ export function getDefaultExtensions(
|
|
|
190
186
|
] as ExtensionFactoryInstance[];
|
|
191
187
|
|
|
192
188
|
if (options.collaboration) {
|
|
193
|
-
extensions.push(
|
|
194
|
-
if (options.collaboration.provider?.awareness) {
|
|
195
|
-
extensions.push(YCursorExtension(options.collaboration));
|
|
196
|
-
}
|
|
197
|
-
extensions.push(YSyncExtension(options.collaboration));
|
|
198
|
-
extensions.push(YUndoExtension(options.collaboration));
|
|
199
|
-
extensions.push(SchemaMigration(options.collaboration));
|
|
189
|
+
extensions.push(CollaborationExtension(options.collaboration));
|
|
200
190
|
} else {
|
|
201
191
|
// YUndo is not compatible with ProseMirror's history plugin
|
|
202
192
|
extensions.push(HistoryExtension());
|
|
@@ -203,6 +203,12 @@ export class ExtensionManager {
|
|
|
203
203
|
|
|
204
204
|
this.extensions.push(instance);
|
|
205
205
|
|
|
206
|
+
if (instance.blockNoteExtensions) {
|
|
207
|
+
for (const extension of instance.blockNoteExtensions) {
|
|
208
|
+
this.addExtension(extension);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
206
212
|
return instance as any;
|
|
207
213
|
}
|
|
208
214
|
|
|
@@ -317,8 +323,7 @@ export class ExtensionManager {
|
|
|
317
323
|
const tiptapExtensions = getDefaultTiptapExtensions(
|
|
318
324
|
this.editor,
|
|
319
325
|
this.options,
|
|
320
|
-
);
|
|
321
|
-
// TODO filter out the default extensions via the disabledExtensions set?
|
|
326
|
+
).filter((extension) => !this.disabledExtensions.has(extension.name));
|
|
322
327
|
|
|
323
328
|
const getPriority = sortByDependencies(this.extensions);
|
|
324
329
|
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isNodeSelection, posToDOMRect } from "@tiptap/core";
|
|
1
2
|
import {
|
|
2
3
|
getSelection,
|
|
3
4
|
getSelectionCutBlocks,
|
|
@@ -7,21 +8,20 @@ import {
|
|
|
7
8
|
getTextCursorPosition,
|
|
8
9
|
setTextCursorPosition,
|
|
9
10
|
} from "../../api/blockManipulation/selections/textCursorPosition.js";
|
|
10
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
DefaultBlockSchema,
|
|
13
|
+
DefaultInlineContentSchema,
|
|
14
|
+
DefaultStyleSchema,
|
|
15
|
+
} from "../../blocks/defaultBlocks.js";
|
|
11
16
|
import {
|
|
12
17
|
BlockIdentifier,
|
|
13
18
|
BlockSchema,
|
|
14
19
|
InlineContentSchema,
|
|
15
20
|
StyleSchema,
|
|
16
21
|
} from "../../schema/index.js";
|
|
17
|
-
import {
|
|
18
|
-
DefaultBlockSchema,
|
|
19
|
-
DefaultInlineContentSchema,
|
|
20
|
-
DefaultStyleSchema,
|
|
21
|
-
} from "../../blocks/defaultBlocks.js";
|
|
22
|
-
import { Selection } from "../selectionTypes.js";
|
|
23
|
-
import { TextCursorPosition } from "../cursorPositionTypes.js";
|
|
24
22
|
import { BlockNoteEditor } from "../BlockNoteEditor.js";
|
|
23
|
+
import { TextCursorPosition } from "../cursorPositionTypes.js";
|
|
24
|
+
import { Selection } from "../selectionTypes.js";
|
|
25
25
|
|
|
26
26
|
export class SelectionManager<
|
|
27
27
|
BSchema extends BlockSchema = DefaultBlockSchema,
|
|
@@ -47,8 +47,8 @@ export class SelectionManager<
|
|
|
47
47
|
* If the selection starts / ends halfway through a block, the returned block will be
|
|
48
48
|
* only the part of the block that is included in the selection.
|
|
49
49
|
*/
|
|
50
|
-
public getSelectionCutBlocks() {
|
|
51
|
-
return this.editor.transact((tr) => getSelectionCutBlocks(tr));
|
|
50
|
+
public getSelectionCutBlocks(expandToWords = false) {
|
|
51
|
+
return this.editor.transact((tr) => getSelectionCutBlocks(tr, expandToWords));
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
/**
|
|
@@ -20,7 +20,7 @@ export const BlockChangeExtension = createExtension(() => {
|
|
|
20
20
|
key: new PluginKey("blockChange"),
|
|
21
21
|
filterTransaction: (tr) => {
|
|
22
22
|
let changes:
|
|
23
|
-
| ReturnType<typeof getBlocksChangedByTransaction
|
|
23
|
+
| ReturnType<typeof getBlocksChangedByTransaction<any, any, any>>
|
|
24
24
|
| undefined = undefined;
|
|
25
25
|
|
|
26
26
|
return beforeChangeCallbacks.reduce((acc, cb) => {
|
|
@@ -34,7 +34,7 @@ export const BlockChangeExtension = createExtension(() => {
|
|
|
34
34
|
if (changes) {
|
|
35
35
|
return changes;
|
|
36
36
|
}
|
|
37
|
-
changes = getBlocksChangedByTransaction(tr);
|
|
37
|
+
changes = getBlocksChangedByTransaction<any, any, any>(tr);
|
|
38
38
|
return changes;
|
|
39
39
|
},
|
|
40
40
|
tr,
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import type * as Y from "yjs";
|
|
2
|
+
import type { Awareness } from "y-protocols/awareness";
|
|
3
|
+
import {
|
|
4
|
+
createExtension,
|
|
5
|
+
ExtensionOptions,
|
|
6
|
+
} from "../../editor/BlockNoteExtension.js";
|
|
7
|
+
import { ForkYDocExtension } from "./ForkYDoc.js";
|
|
8
|
+
import { SchemaMigration } from "./schemaMigration/SchemaMigration.js";
|
|
9
|
+
import { YCursorExtension } from "./YCursorPlugin.js";
|
|
10
|
+
import { YSyncExtension } from "./YSync.js";
|
|
11
|
+
import { YUndoExtension } from "./YUndo.js";
|
|
12
|
+
|
|
13
|
+
export type CollaborationOptions = {
|
|
14
|
+
/**
|
|
15
|
+
* The Yjs XML fragment that's used for collaboration.
|
|
16
|
+
*/
|
|
17
|
+
fragment: Y.XmlFragment;
|
|
18
|
+
/**
|
|
19
|
+
* The user info for the current user that's shown to other collaborators.
|
|
20
|
+
*/
|
|
21
|
+
user: {
|
|
22
|
+
name: string;
|
|
23
|
+
color: string;
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* A Yjs provider (used for awareness / cursor information)
|
|
27
|
+
*/
|
|
28
|
+
provider?: { awareness?: Awareness };
|
|
29
|
+
/**
|
|
30
|
+
* Optional function to customize how cursors of users are rendered
|
|
31
|
+
*/
|
|
32
|
+
renderCursor?: (user: any) => HTMLElement;
|
|
33
|
+
/**
|
|
34
|
+
* Optional flag to set when the user label should be shown with the default
|
|
35
|
+
* collaboration cursor. Setting to "always" will always show the label,
|
|
36
|
+
* while "activity" will only show the label when the user moves the cursor
|
|
37
|
+
* or types. Defaults to "activity".
|
|
38
|
+
*/
|
|
39
|
+
showCursorLabels?: "always" | "activity";
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const CollaborationExtension = createExtension(
|
|
43
|
+
({ options }: ExtensionOptions<CollaborationOptions>) => {
|
|
44
|
+
return {
|
|
45
|
+
key: "collaboration",
|
|
46
|
+
blockNoteExtensions: [
|
|
47
|
+
ForkYDocExtension(options),
|
|
48
|
+
YCursorExtension(options),
|
|
49
|
+
YSyncExtension(options),
|
|
50
|
+
YUndoExtension(),
|
|
51
|
+
SchemaMigration(options),
|
|
52
|
+
],
|
|
53
|
+
} as const;
|
|
54
|
+
},
|
|
55
|
+
);
|
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
createStore,
|
|
6
6
|
ExtensionOptions,
|
|
7
7
|
} from "../../editor/BlockNoteExtension.js";
|
|
8
|
+
import { CollaborationOptions } from "./Collaboration.js";
|
|
8
9
|
import { YCursorExtension } from "./YCursorPlugin.js";
|
|
9
10
|
import { YSyncExtension } from "./YSync.js";
|
|
10
11
|
import { YUndoExtension } from "./YUndo.js";
|
|
11
|
-
import { BlockNoteEditorOptions } from "../../editor/BlockNoteEditor.js";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* To find a fragment in another ydoc, we need to search for it.
|
|
@@ -44,12 +44,7 @@ function findTypeInOtherYdoc<T extends Y.AbstractType<any>>(
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
export const ForkYDocExtension = createExtension(
|
|
47
|
-
({
|
|
48
|
-
editor,
|
|
49
|
-
options,
|
|
50
|
-
}: ExtensionOptions<
|
|
51
|
-
NonNullable<BlockNoteEditorOptions<any, any, any>["collaboration"]>
|
|
52
|
-
>) => {
|
|
47
|
+
({ editor, options }: ExtensionOptions<CollaborationOptions>) => {
|
|
53
48
|
let forkedState:
|
|
54
49
|
| {
|
|
55
50
|
originalFragment: Y.XmlFragment;
|
|
@@ -107,7 +102,7 @@ export const ForkYDocExtension = createExtension(
|
|
|
107
102
|
editor.registerExtension([
|
|
108
103
|
YSyncExtension(newOptions),
|
|
109
104
|
// No need to register the cursor plugin again, it's a local fork
|
|
110
|
-
YUndoExtension(
|
|
105
|
+
YUndoExtension(),
|
|
111
106
|
]);
|
|
112
107
|
|
|
113
108
|
// Tell the store that the editor is now forked
|
|
@@ -131,7 +126,7 @@ export const ForkYDocExtension = createExtension(
|
|
|
131
126
|
editor.registerExtension([
|
|
132
127
|
YSyncExtension(options),
|
|
133
128
|
YCursorExtension(options),
|
|
134
|
-
YUndoExtension(
|
|
129
|
+
YUndoExtension(),
|
|
135
130
|
]);
|
|
136
131
|
|
|
137
132
|
// Reset the undo stack to the original undo stack
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
createExtension,
|
|
4
4
|
ExtensionOptions,
|
|
5
5
|
} from "../../editor/BlockNoteExtension.js";
|
|
6
|
-
import {
|
|
6
|
+
import { CollaborationOptions } from "./Collaboration.js";
|
|
7
7
|
|
|
8
8
|
export type CollaborationUser = {
|
|
9
9
|
name: string;
|
|
@@ -67,30 +67,24 @@ function defaultCursorRender(user: CollaborationUser) {
|
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
export const YCursorExtension = createExtension(
|
|
70
|
-
({
|
|
71
|
-
options,
|
|
72
|
-
}: ExtensionOptions<
|
|
73
|
-
NonNullable<BlockNoteEditorOptions<any, any, any>["collaboration"]>
|
|
74
|
-
>) => {
|
|
70
|
+
({ options }: ExtensionOptions<CollaborationOptions>) => {
|
|
75
71
|
const recentlyUpdatedCursors = new Map();
|
|
76
|
-
|
|
77
|
-
if (
|
|
72
|
+
const awareness =
|
|
78
73
|
options.provider &&
|
|
79
74
|
"awareness" in options.provider &&
|
|
80
75
|
typeof options.provider.awareness === "object"
|
|
81
|
-
|
|
76
|
+
? options.provider.awareness
|
|
77
|
+
: undefined;
|
|
78
|
+
if (awareness) {
|
|
82
79
|
if (
|
|
83
|
-
"setLocalStateField" in
|
|
84
|
-
typeof
|
|
80
|
+
"setLocalStateField" in awareness &&
|
|
81
|
+
typeof awareness.setLocalStateField === "function"
|
|
85
82
|
) {
|
|
86
|
-
|
|
83
|
+
awareness.setLocalStateField("user", options.user);
|
|
87
84
|
}
|
|
88
|
-
if (
|
|
89
|
-
"on" in options.provider.awareness &&
|
|
90
|
-
typeof options.provider.awareness.on === "function"
|
|
91
|
-
) {
|
|
85
|
+
if ("on" in awareness && typeof awareness.on === "function") {
|
|
92
86
|
if (options.showCursorLabels !== "always") {
|
|
93
|
-
|
|
87
|
+
awareness.on(
|
|
94
88
|
"change",
|
|
95
89
|
({
|
|
96
90
|
updated,
|
|
@@ -126,57 +120,59 @@ export const YCursorExtension = createExtension(
|
|
|
126
120
|
return {
|
|
127
121
|
key: "yCursor",
|
|
128
122
|
prosemirrorPlugins: [
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
123
|
+
awareness
|
|
124
|
+
? yCursorPlugin(awareness, {
|
|
125
|
+
selectionBuilder: defaultSelectionBuilder,
|
|
126
|
+
cursorBuilder(user: CollaborationUser, clientID: number) {
|
|
127
|
+
let cursorData = recentlyUpdatedCursors.get(clientID);
|
|
128
|
+
|
|
129
|
+
if (!cursorData) {
|
|
130
|
+
const cursorElement = (
|
|
131
|
+
options.renderCursor ?? defaultCursorRender
|
|
132
|
+
)(user);
|
|
133
|
+
|
|
134
|
+
if (options.showCursorLabels !== "always") {
|
|
135
|
+
cursorElement.addEventListener("mouseenter", () => {
|
|
136
|
+
const cursor = recentlyUpdatedCursors.get(clientID)!;
|
|
137
|
+
cursor.element.setAttribute("data-active", "");
|
|
138
|
+
|
|
139
|
+
if (cursor.hideTimeout) {
|
|
140
|
+
clearTimeout(cursor.hideTimeout);
|
|
141
|
+
recentlyUpdatedCursors.set(clientID, {
|
|
142
|
+
element: cursor.element,
|
|
143
|
+
hideTimeout: undefined,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
149
146
|
});
|
|
150
|
-
}
|
|
151
|
-
});
|
|
152
147
|
|
|
153
|
-
|
|
154
|
-
|
|
148
|
+
cursorElement.addEventListener("mouseleave", () => {
|
|
149
|
+
const cursor = recentlyUpdatedCursors.get(clientID)!;
|
|
155
150
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
151
|
+
recentlyUpdatedCursors.set(clientID, {
|
|
152
|
+
element: cursor.element,
|
|
153
|
+
hideTimeout: setTimeout(() => {
|
|
154
|
+
cursor.element.removeAttribute("data-active");
|
|
155
|
+
}, 2000),
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
}
|
|
164
159
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
160
|
+
cursorData = {
|
|
161
|
+
element: cursorElement,
|
|
162
|
+
hideTimeout: undefined,
|
|
163
|
+
};
|
|
169
164
|
|
|
170
|
-
|
|
171
|
-
|
|
165
|
+
recentlyUpdatedCursors.set(clientID, cursorData);
|
|
166
|
+
}
|
|
172
167
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
168
|
+
return cursorData.element;
|
|
169
|
+
},
|
|
170
|
+
})
|
|
171
|
+
: undefined,
|
|
172
|
+
].filter(Boolean),
|
|
177
173
|
dependsOn: ["ySync"],
|
|
178
174
|
updateUser(user: { name: string; color: string; [key: string]: string }) {
|
|
179
|
-
|
|
175
|
+
awareness?.setLocalStateField("user", user);
|
|
180
176
|
},
|
|
181
177
|
} as const;
|
|
182
178
|
},
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { ySyncPlugin } from "y-prosemirror";
|
|
2
|
-
import { XmlFragment } from "yjs";
|
|
3
2
|
import {
|
|
4
3
|
ExtensionOptions,
|
|
5
4
|
createExtension,
|
|
6
5
|
} from "../../editor/BlockNoteExtension.js";
|
|
6
|
+
import { CollaborationOptions } from "./Collaboration.js";
|
|
7
7
|
|
|
8
8
|
export const YSyncExtension = createExtension(
|
|
9
|
-
({ options }: ExtensionOptions<
|
|
9
|
+
({ options }: ExtensionOptions<Pick<CollaborationOptions, "fragment">>) => {
|
|
10
10
|
return {
|
|
11
11
|
key: "ySync",
|
|
12
12
|
prosemirrorPlugins: [ySyncPlugin(options.fragment)],
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { redoCommand, undoCommand, yUndoPlugin } from "y-prosemirror";
|
|
2
2
|
import { createExtension } from "../../editor/BlockNoteExtension.js";
|
|
3
3
|
|
|
4
|
-
export const YUndoExtension = createExtension((
|
|
4
|
+
export const YUndoExtension = createExtension(() => {
|
|
5
5
|
return {
|
|
6
6
|
key: "yUndo",
|
|
7
|
-
prosemirrorPlugins: [yUndoPlugin(
|
|
7
|
+
prosemirrorPlugins: [yUndoPlugin()],
|
|
8
8
|
dependsOn: ["yCursor", "ySync"],
|
|
9
9
|
undoCommand: undoCommand,
|
|
10
10
|
redoCommand: redoCommand,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { getMarkRange, posToDOMRect } from "@tiptap/core";
|
|
2
|
-
import { createExtension } from "../../editor/BlockNoteExtension.js";
|
|
3
2
|
import { getPmSchema } from "../../api/pmUtil.js";
|
|
3
|
+
import { createExtension } from "../../editor/BlockNoteExtension.js";
|
|
4
4
|
|
|
5
5
|
export const LinkToolbarExtension = createExtension(({ editor }) => {
|
|
6
6
|
function getLinkElementAtPos(pos: number) {
|
|
@@ -14,7 +14,7 @@ const PLUGIN_KEY = new PluginKey(`blocknote-show-selection`);
|
|
|
14
14
|
*/
|
|
15
15
|
export const ShowSelectionExtension = createExtension(({ editor }) => {
|
|
16
16
|
const store = createStore(
|
|
17
|
-
{
|
|
17
|
+
{ enabledSet: new Set<string>() },
|
|
18
18
|
{
|
|
19
19
|
onUpdate() {
|
|
20
20
|
editor.transact((tr) => tr.setMeta(PLUGIN_KEY, {}));
|
|
@@ -30,7 +30,7 @@ export const ShowSelectionExtension = createExtension(({ editor }) => {
|
|
|
30
30
|
props: {
|
|
31
31
|
decorations: (state) => {
|
|
32
32
|
const { doc, selection } = state;
|
|
33
|
-
if (
|
|
33
|
+
if (store.state.enabledSet.size === 0) {
|
|
34
34
|
return DecorationSet.empty;
|
|
35
35
|
}
|
|
36
36
|
const dec = Decoration.inline(selection.from, selection.to, {
|
|
@@ -43,9 +43,19 @@ export const ShowSelectionExtension = createExtension(({ editor }) => {
|
|
|
43
43
|
],
|
|
44
44
|
/**
|
|
45
45
|
* Show or hide the selection decoration
|
|
46
|
+
*
|
|
47
|
+
* @param shouldShow - Whether to show the selection decoration
|
|
48
|
+
* @param key - The key of the selection to show or hide,
|
|
49
|
+
* this is necessary to prevent disabling ShowSelection from one place
|
|
50
|
+
* will interfere with other parts of the code that need to show the selection decoration
|
|
51
|
+
* (e.g.: CreateLinkButton and AIExtension)
|
|
46
52
|
*/
|
|
47
|
-
showSelection(shouldShow: boolean) {
|
|
48
|
-
store.setState({
|
|
53
|
+
showSelection(shouldShow: boolean, key: string) {
|
|
54
|
+
store.setState({
|
|
55
|
+
enabledSet: shouldShow
|
|
56
|
+
? new Set([...store.state.enabledSet, key])
|
|
57
|
+
: new Set([...store.state.enabledSet].filter((k) => k !== key)),
|
|
58
|
+
});
|
|
49
59
|
},
|
|
50
60
|
} as const;
|
|
51
61
|
});
|