@blocknote/core 0.42.3 → 0.44.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 +2 -0
- package/dist/BlockNoteExtension-BWw0r8Gy.cjs.map +1 -0
- package/dist/BlockNoteExtension-C2X7LW-V.js +25 -0
- package/dist/BlockNoteExtension-C2X7LW-V.js.map +1 -0
- package/dist/BlockNoteSchema-B4gm-Qco.cjs +2 -0
- package/dist/BlockNoteSchema-B4gm-Qco.cjs.map +1 -0
- package/dist/BlockNoteSchema-C-l154WP.js +270 -0
- package/dist/BlockNoteSchema-C-l154WP.js.map +1 -0
- package/dist/EventEmitter-CLwfmbqG.cjs +2 -0
- package/dist/EventEmitter-CLwfmbqG.cjs.map +1 -0
- package/dist/EventEmitter-CjSwpTbz.js +27 -0
- package/dist/EventEmitter-CjSwpTbz.js.map +1 -0
- package/dist/ShowSelection-BW37oJ6h.cjs +2 -0
- package/dist/ShowSelection-BW37oJ6h.cjs.map +1 -0
- package/dist/ShowSelection-Dz-NEase.js +43 -0
- package/dist/ShowSelection-Dz-NEase.js.map +1 -0
- package/dist/TrailingNode-B_zPMWxw.js +2098 -0
- package/dist/TrailingNode-B_zPMWxw.js.map +1 -0
- package/dist/TrailingNode-CRHrgOnK.cjs +2 -0
- package/dist/TrailingNode-CRHrgOnK.cjs.map +1 -0
- package/dist/{blockToNode-DIfPWLH8.js → blockToNode-DBNbhwwC.js} +33 -33
- package/dist/blockToNode-DBNbhwwC.js.map +1 -0
- package/dist/blockToNode-w7H99R6p.cjs.map +1 -1
- package/dist/blocknote.cjs +4 -4
- package/dist/blocknote.cjs.map +1 -1
- package/dist/blocknote.js +2496 -5686
- package/dist/blocknote.js.map +1 -1
- package/dist/blocks.cjs +1 -1
- package/dist/blocks.js +71 -70
- package/dist/blocks.js.map +1 -1
- package/dist/comments.cjs +1 -1
- package/dist/comments.cjs.map +1 -1
- package/dist/comments.js +451 -137
- package/dist/comments.js.map +1 -1
- package/dist/defaultBlocks-DLJ4Q1_J.cjs +6 -0
- package/dist/defaultBlocks-DLJ4Q1_J.cjs.map +1 -0
- package/dist/{BlockNoteSchema-Bi-eeHal.js → defaultBlocks-DgA_mtQV.js} +974 -1027
- package/dist/defaultBlocks-DgA_mtQV.js.map +1 -0
- package/dist/extensions.cjs +2 -0
- package/dist/extensions.cjs.map +1 -0
- package/dist/extensions.js +57 -0
- package/dist/extensions.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/webpack-stats.json +1 -1
- package/dist/yjs.js +1 -1
- package/package.json +9 -3
- package/src/api/nodeConversions/blockToNode.ts +1 -1
- package/src/api/nodeConversions/nodeToBlock.ts +1 -1
- package/src/blocks/Code/block.ts +4 -4
- package/src/blocks/Divider/block.ts +2 -2
- package/src/blocks/File/helpers/render/createAddFileButton.ts +7 -5
- package/src/blocks/Heading/block.ts +23 -20
- package/src/blocks/ListItem/BulletListItem/block.ts +2 -2
- package/src/blocks/ListItem/CheckListItem/block.ts +2 -2
- package/src/blocks/ListItem/NumberedListItem/block.ts +3 -3
- package/src/blocks/ListItem/ToggleListItem/block.ts +2 -2
- package/src/blocks/PageBreak/getPageBreakSlashMenuItems.ts +2 -2
- package/src/blocks/Paragraph/block.ts +2 -2
- package/src/blocks/Quote/block.ts +2 -2
- package/src/blocks/Table/block.ts +4 -3
- package/src/blocks/ToggleWrapper/createToggleWrapper.ts +2 -1
- package/src/comments/extension.ts +353 -0
- package/src/comments/index.ts +2 -1
- package/src/comments/types.ts +8 -0
- package/src/{extensions/Comments → comments}/userstore/UserStore.ts +2 -2
- package/src/editor/BlockNoteEditor.test.ts +2 -23
- package/src/editor/BlockNoteEditor.ts +60 -453
- package/src/editor/BlockNoteExtension.test.ts +103 -0
- package/src/editor/BlockNoteExtension.ts +174 -56
- package/src/editor/managers/EventManager.ts +64 -35
- package/src/editor/managers/ExtensionManager/extensions.ts +214 -0
- package/src/editor/managers/ExtensionManager/index.ts +514 -0
- package/src/editor/managers/ExtensionManager/symbol.ts +6 -0
- package/src/editor/managers/SelectionManager.ts +5 -1
- package/src/editor/managers/StateManager.ts +29 -17
- package/src/editor/managers/index.ts +1 -5
- package/src/extensions/BlockChange/{BlockChangePlugin.ts → BlockChange.ts} +27 -29
- package/src/extensions/Collaboration/{ForkYDocPlugin.test.ts → ForkYDoc.test.ts} +6 -5
- package/src/extensions/Collaboration/ForkYDoc.ts +158 -0
- package/src/extensions/Collaboration/YCursorPlugin.ts +183 -0
- package/src/extensions/Collaboration/YSync.ts +16 -0
- package/src/extensions/Collaboration/YUndo.ts +12 -0
- package/src/extensions/Collaboration/schemaMigration/SchemaMigration.ts +59 -0
- package/src/extensions/DropCursor/DropCursor.ts +26 -0
- package/src/extensions/FilePanel/FilePanel.ts +41 -0
- package/src/extensions/FormattingToolbar/FormattingToolbar.ts +119 -0
- package/src/extensions/History/History.ts +11 -0
- package/src/extensions/LinkToolbar/LinkToolbar.ts +121 -0
- package/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboard.ts +74 -0
- package/src/extensions/Placeholder/Placeholder.ts +148 -0
- package/src/extensions/PreviousBlockType/{PreviousBlockTypePlugin.ts → PreviousBlockType.ts} +9 -13
- package/src/extensions/ShowSelection/{ShowSelectionPlugin.ts → ShowSelection.ts} +27 -33
- package/src/extensions/SideMenu/{SideMenuPlugin.ts → SideMenu.ts} +63 -83
- package/src/extensions/SuggestionMenu/{SuggestionPlugin.ts → SuggestionMenu.ts} +71 -77
- package/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.ts +29 -44
- package/src/extensions/TableHandles/{TableHandlesPlugin.ts → TableHandles.ts} +416 -437
- package/src/extensions/TrailingNode/{TrailingNodeExtension.ts → TrailingNode.ts} +8 -17
- package/src/extensions/index.ts +24 -0
- package/src/extensions/{BackgroundColor → tiptap-extensions/BackgroundColor}/BackgroundColorExtension.ts +1 -1
- package/src/extensions/{KeyboardShortcuts → tiptap-extensions/KeyboardShortcuts}/KeyboardShortcutsExtension.ts +21 -16
- package/src/extensions/{TextColor → tiptap-extensions/TextColor}/TextColorExtension.ts +1 -1
- package/src/extensions/tiptap-extensions/index.ts +31 -0
- package/src/index.ts +1 -13
- package/src/schema/blocks/createSpec.ts +14 -11
- package/src/schema/blocks/internal.ts +2 -2
- package/src/schema/blocks/types.ts +8 -5
- package/src/schema/schema.ts +11 -36
- package/src/util/topo-sort.ts +46 -0
- package/types/src/comments/extension.d.ts +70 -0
- package/types/src/comments/index.d.ts +2 -1
- package/types/src/comments/types.d.ts +8 -0
- package/types/src/{extensions/Comments → comments}/userstore/UserStore.d.ts +2 -2
- package/types/src/editor/BlockNoteEditor.d.ts +34 -105
- package/types/src/editor/BlockNoteExtension.d.ts +87 -22
- package/types/src/editor/managers/EventManager.d.ts +25 -16
- package/types/src/editor/managers/ExtensionManager/extensions.d.ts +8 -0
- package/types/src/editor/managers/ExtensionManager/index.d.ts +83 -0
- package/types/src/editor/managers/ExtensionManager/symbol.d.ts +5 -0
- package/types/src/editor/managers/StateManager.d.ts +1 -12
- package/types/src/editor/managers/index.d.ts +1 -2
- package/types/src/extensions/BlockChange/BlockChange.d.ts +16 -0
- package/types/src/extensions/Collaboration/ForkYDoc.d.ts +34 -0
- package/types/src/extensions/Collaboration/ForkYDoc.test.d.ts +1 -0
- package/types/src/extensions/Collaboration/YCursorPlugin.d.ts +24 -0
- package/types/src/extensions/Collaboration/YSync.d.ts +8 -0
- package/types/src/extensions/Collaboration/YUndo.d.ts +12 -0
- package/types/src/extensions/Collaboration/schemaMigration/SchemaMigration.d.ts +8 -0
- package/types/src/extensions/DropCursor/DropCursor.d.ts +5 -0
- package/types/src/extensions/FilePanel/FilePanel.d.ts +11 -0
- package/types/src/extensions/FormattingToolbar/FormattingToolbar.d.ts +9 -0
- package/types/src/extensions/History/History.d.ts +6 -0
- package/types/src/extensions/LinkToolbar/LinkToolbar.d.ts +24 -0
- package/types/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboard.d.ts +5 -0
- package/types/src/extensions/Placeholder/Placeholder.d.ts +6 -0
- package/types/src/extensions/PreviousBlockType/{PreviousBlockTypePlugin.d.ts → PreviousBlockType.d.ts} +9 -5
- package/types/src/extensions/ShowSelection/ShowSelection.d.ts +21 -0
- package/types/src/extensions/SideMenu/{SideMenuPlugin.d.ts → SideMenu.d.ts} +11 -15
- package/types/src/extensions/SuggestionMenu/SuggestionMenu.d.ts +54 -0
- package/types/src/extensions/SuggestionMenu/getDefaultSlashMenuItems.d.ts +1 -1
- package/types/src/extensions/TableHandles/{TableHandlesPlugin.d.ts → TableHandles.d.ts} +28 -31
- package/types/src/extensions/TrailingNode/TrailingNode.d.ts +8 -0
- package/types/src/extensions/index.d.ts +24 -0
- package/types/src/extensions/{KeyboardShortcuts → tiptap-extensions/KeyboardShortcuts}/KeyboardShortcutsExtension.d.ts +1 -1
- package/types/src/extensions/tiptap-extensions/index.d.ts +11 -0
- package/types/src/index.d.ts +1 -13
- package/types/src/schema/blocks/createSpec.d.ts +4 -4
- package/types/src/schema/blocks/internal.d.ts +2 -2
- package/types/src/schema/blocks/types.d.ts +5 -5
- package/types/src/util/topo-sort.d.ts +8 -0
- package/dist/BlockNoteSchema-Bi-eeHal.js.map +0 -1
- package/dist/BlockNoteSchema-DjDaA2C3.cjs +0 -6
- package/dist/BlockNoteSchema-DjDaA2C3.cjs.map +0 -1
- package/dist/blockToNode-DIfPWLH8.js.map +0 -1
- package/src/comments/models/User.ts +0 -8
- package/src/editor/BlockNoteExtensions.ts +0 -325
- package/src/editor/managers/CollaborationManager.ts +0 -212
- package/src/editor/managers/ExtensionManager.ts +0 -130
- package/src/extensions/Collaboration/CursorPlugin.ts +0 -189
- package/src/extensions/Collaboration/ForkYDocPlugin.ts +0 -192
- package/src/extensions/Collaboration/SyncPlugin.ts +0 -18
- package/src/extensions/Collaboration/UndoPlugin.ts +0 -18
- package/src/extensions/Collaboration/schemaMigration/SchemaMigrationPlugin.ts +0 -59
- package/src/extensions/Comments/CommentsPlugin.ts +0 -392
- package/src/extensions/FilePanel/FilePanelPlugin.ts +0 -206
- package/src/extensions/FormattingToolbar/FormattingToolbarPlugin.ts +0 -363
- package/src/extensions/LinkToolbar/LinkToolbarPlugin.ts +0 -380
- package/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.ts +0 -75
- package/src/extensions/Placeholder/PlaceholderPlugin.ts +0 -147
- package/types/src/comments/models/User.d.ts +0 -8
- package/types/src/editor/BlockNoteExtensions.d.ts +0 -43
- package/types/src/editor/managers/CollaborationManager.d.ts +0 -115
- package/types/src/editor/managers/ExtensionManager.d.ts +0 -68
- package/types/src/extensions/BlockChange/BlockChangePlugin.d.ts +0 -15
- package/types/src/extensions/Collaboration/CursorPlugin.d.ts +0 -37
- package/types/src/extensions/Collaboration/ForkYDocPlugin.d.ts +0 -41
- package/types/src/extensions/Collaboration/SyncPlugin.d.ts +0 -7
- package/types/src/extensions/Collaboration/UndoPlugin.d.ts +0 -9
- package/types/src/extensions/Collaboration/schemaMigration/SchemaMigrationPlugin.d.ts +0 -7
- package/types/src/extensions/Comments/CommentsPlugin.d.ts +0 -66
- package/types/src/extensions/FilePanel/FilePanelPlugin.d.ts +0 -31
- package/types/src/extensions/FormattingToolbar/FormattingToolbarPlugin.d.ts +0 -41
- package/types/src/extensions/LinkToolbar/LinkToolbarPlugin.d.ts +0 -42
- package/types/src/extensions/NodeSelectionKeyboard/NodeSelectionKeyboardPlugin.d.ts +0 -5
- package/types/src/extensions/Placeholder/PlaceholderPlugin.d.ts +0 -6
- package/types/src/extensions/ShowSelection/ShowSelectionPlugin.d.ts +0 -15
- package/types/src/extensions/SuggestionMenu/SuggestionPlugin.d.ts +0 -31
- package/types/src/extensions/TrailingNode/TrailingNodeExtension.d.ts +0 -13
- /package/src/{extensions/Comments/CommentMark.ts → comments/mark.ts} +0 -0
- /package/src/extensions/{HardBreak → tiptap-extensions/HardBreak}/HardBreak.ts +0 -0
- /package/src/extensions/{Suggestions → tiptap-extensions/Suggestions}/SuggestionMarks.ts +0 -0
- /package/src/extensions/{TextAlignment → tiptap-extensions/TextAlignment}/TextAlignmentExtension.ts +0 -0
- /package/src/extensions/{UniqueID → tiptap-extensions/UniqueID}/UniqueID.ts +0 -0
- /package/types/src/{extensions/Comments/CommentMark.d.ts → comments/mark.d.ts} +0 -0
- /package/types/src/{extensions/Collaboration/ForkYDocPlugin.test.d.ts → editor/BlockNoteExtension.test.d.ts} +0 -0
- /package/types/src/extensions/{BackgroundColor → tiptap-extensions/BackgroundColor}/BackgroundColorExtension.d.ts +0 -0
- /package/types/src/extensions/{HardBreak → tiptap-extensions/HardBreak}/HardBreak.d.ts +0 -0
- /package/types/src/extensions/{Suggestions → tiptap-extensions/Suggestions}/SuggestionMarks.d.ts +0 -0
- /package/types/src/extensions/{TextAlignment → tiptap-extensions/TextAlignment}/TextAlignmentExtension.d.ts +0 -0
- /package/types/src/extensions/{TextColor → tiptap-extensions/TextColor}/TextColorExtension.d.ts +0 -0
- /package/types/src/extensions/{UniqueID → tiptap-extensions/UniqueID}/UniqueID.d.ts +0 -0
|
@@ -2,6 +2,7 @@ import { expect, it } from "vitest";
|
|
|
2
2
|
import * as Y from "yjs";
|
|
3
3
|
import { Awareness } from "y-protocols/awareness";
|
|
4
4
|
import { BlockNoteEditor } from "../../index.js";
|
|
5
|
+
import { ForkYDocExtension } from "./ForkYDoc.js";
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* @vitest-environment jsdom
|
|
@@ -36,7 +37,7 @@ it("can fork a document", async () => {
|
|
|
36
37
|
"__snapshots__/fork-yjs-snap-editor.json",
|
|
37
38
|
);
|
|
38
39
|
|
|
39
|
-
editor.
|
|
40
|
+
editor.getExtension(ForkYDocExtension)!.fork();
|
|
40
41
|
|
|
41
42
|
editor.replaceBlocks(editor.document, [
|
|
42
43
|
{
|
|
@@ -83,7 +84,7 @@ it("can merge a document", async () => {
|
|
|
83
84
|
"__snapshots__/fork-yjs-snap-editor.json",
|
|
84
85
|
);
|
|
85
86
|
|
|
86
|
-
editor.
|
|
87
|
+
editor.getExtension(ForkYDocExtension)!.fork();
|
|
87
88
|
|
|
88
89
|
editor.replaceBlocks(editor.document, [
|
|
89
90
|
{
|
|
@@ -99,7 +100,7 @@ it("can merge a document", async () => {
|
|
|
99
100
|
"__snapshots__/fork-yjs-snap-editor-forked.json",
|
|
100
101
|
);
|
|
101
102
|
|
|
102
|
-
editor.
|
|
103
|
+
editor.getExtension(ForkYDocExtension)!.merge({ keepChanges: false });
|
|
103
104
|
|
|
104
105
|
await expect(fragment.toJSON()).toMatchFileSnapshot(
|
|
105
106
|
"__snapshots__/fork-yjs-snap.html",
|
|
@@ -139,7 +140,7 @@ it("can fork an keep the changes to the original document", async () => {
|
|
|
139
140
|
"__snapshots__/fork-yjs-snap-editor.json",
|
|
140
141
|
);
|
|
141
142
|
|
|
142
|
-
editor.
|
|
143
|
+
editor.getExtension(ForkYDocExtension)!.fork();
|
|
143
144
|
|
|
144
145
|
editor.replaceBlocks(editor.document, [
|
|
145
146
|
{
|
|
@@ -155,7 +156,7 @@ it("can fork an keep the changes to the original document", async () => {
|
|
|
155
156
|
"__snapshots__/fork-yjs-snap-editor-forked.json",
|
|
156
157
|
);
|
|
157
158
|
|
|
158
|
-
editor.
|
|
159
|
+
editor.getExtension(ForkYDocExtension)!.merge({ keepChanges: true });
|
|
159
160
|
|
|
160
161
|
await expect(fragment.toJSON()).toMatchFileSnapshot(
|
|
161
162
|
"__snapshots__/fork-yjs-snap-forked.html",
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { yUndoPluginKey } from "y-prosemirror";
|
|
2
|
+
import * as Y from "yjs";
|
|
3
|
+
import {
|
|
4
|
+
createExtension,
|
|
5
|
+
createStore,
|
|
6
|
+
ExtensionOptions,
|
|
7
|
+
} from "../../editor/BlockNoteExtension.js";
|
|
8
|
+
import { YCursorExtension } from "./YCursorPlugin.js";
|
|
9
|
+
import { YSyncExtension } from "./YSync.js";
|
|
10
|
+
import { YUndoExtension } from "./YUndo.js";
|
|
11
|
+
import { BlockNoteEditorOptions } from "../../editor/BlockNoteEditor.js";
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* To find a fragment in another ydoc, we need to search for it.
|
|
15
|
+
*/
|
|
16
|
+
function findTypeInOtherYdoc<T extends Y.AbstractType<any>>(
|
|
17
|
+
ytype: T,
|
|
18
|
+
otherYdoc: Y.Doc,
|
|
19
|
+
): T {
|
|
20
|
+
const ydoc = ytype.doc!;
|
|
21
|
+
if (ytype._item === null) {
|
|
22
|
+
/**
|
|
23
|
+
* If is a root type, we need to find the root key in the original ydoc
|
|
24
|
+
* and use it to get the type in the other ydoc.
|
|
25
|
+
*/
|
|
26
|
+
const rootKey = Array.from(ydoc.share.keys()).find(
|
|
27
|
+
(key) => ydoc.share.get(key) === ytype,
|
|
28
|
+
);
|
|
29
|
+
if (rootKey == null) {
|
|
30
|
+
throw new Error("type does not exist in other ydoc");
|
|
31
|
+
}
|
|
32
|
+
return otherYdoc.get(rootKey, ytype.constructor as new () => T) as T;
|
|
33
|
+
} else {
|
|
34
|
+
/**
|
|
35
|
+
* If it is a sub type, we use the item id to find the history type.
|
|
36
|
+
*/
|
|
37
|
+
const ytypeItem = ytype._item;
|
|
38
|
+
const otherStructs = otherYdoc.store.clients.get(ytypeItem.id.client) ?? [];
|
|
39
|
+
const itemIndex = Y.findIndexSS(otherStructs, ytypeItem.id.clock);
|
|
40
|
+
const otherItem = otherStructs[itemIndex] as Y.Item;
|
|
41
|
+
const otherContent = otherItem.content as Y.ContentType;
|
|
42
|
+
return otherContent.type as T;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const ForkYDocExtension = createExtension(
|
|
47
|
+
({
|
|
48
|
+
editor,
|
|
49
|
+
options,
|
|
50
|
+
}: ExtensionOptions<
|
|
51
|
+
NonNullable<BlockNoteEditorOptions<any, any, any>["collaboration"]>
|
|
52
|
+
>) => {
|
|
53
|
+
let forkedState:
|
|
54
|
+
| {
|
|
55
|
+
originalFragment: Y.XmlFragment;
|
|
56
|
+
undoStack: Y.UndoManager["undoStack"];
|
|
57
|
+
forkedFragment: Y.XmlFragment;
|
|
58
|
+
}
|
|
59
|
+
| undefined = undefined;
|
|
60
|
+
|
|
61
|
+
const store = createStore({ isForked: false });
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
key: "yForkDoc",
|
|
65
|
+
store,
|
|
66
|
+
/**
|
|
67
|
+
* Fork the Y.js document from syncing to the remote,
|
|
68
|
+
* allowing modifications to the document without affecting the remote.
|
|
69
|
+
* These changes can later be rolled back or applied to the remote.
|
|
70
|
+
*/
|
|
71
|
+
fork() {
|
|
72
|
+
if (forkedState) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const originalFragment = options.fragment;
|
|
77
|
+
|
|
78
|
+
if (!originalFragment) {
|
|
79
|
+
throw new Error("No fragment to fork from");
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const doc = new Y.Doc();
|
|
83
|
+
// Copy the original document to a new Yjs document
|
|
84
|
+
Y.applyUpdate(doc, Y.encodeStateAsUpdate(originalFragment.doc!));
|
|
85
|
+
|
|
86
|
+
// Find the forked fragment in the new Yjs document
|
|
87
|
+
const forkedFragment = findTypeInOtherYdoc(originalFragment, doc);
|
|
88
|
+
|
|
89
|
+
forkedState = {
|
|
90
|
+
undoStack: yUndoPluginKey.getState(editor.prosemirrorState)!
|
|
91
|
+
.undoManager.undoStack,
|
|
92
|
+
originalFragment,
|
|
93
|
+
forkedFragment,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// Need to reset all the yjs plugins
|
|
97
|
+
editor.unregisterExtension([
|
|
98
|
+
YUndoExtension,
|
|
99
|
+
YCursorExtension,
|
|
100
|
+
YSyncExtension,
|
|
101
|
+
]);
|
|
102
|
+
const newOptions = {
|
|
103
|
+
...options,
|
|
104
|
+
fragment: forkedFragment,
|
|
105
|
+
};
|
|
106
|
+
// Register them again, based on the new forked fragment
|
|
107
|
+
editor.registerExtension([
|
|
108
|
+
YSyncExtension(newOptions),
|
|
109
|
+
// No need to register the cursor plugin again, it's a local fork
|
|
110
|
+
YUndoExtension({}),
|
|
111
|
+
]);
|
|
112
|
+
|
|
113
|
+
// Tell the store that the editor is now forked
|
|
114
|
+
store.setState({ isForked: true });
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Resume syncing the Y.js document to the remote
|
|
119
|
+
* If `keepChanges` is true, any changes that have been made to the forked document will be applied to the original document.
|
|
120
|
+
* Otherwise, the original document will be restored and the changes will be discarded.
|
|
121
|
+
*/
|
|
122
|
+
merge({ keepChanges }: { keepChanges: boolean }) {
|
|
123
|
+
if (!forkedState) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
// Remove the forked fragment's plugins
|
|
127
|
+
editor.unregisterExtension(["ySync", "yCursor", "yUndo"]);
|
|
128
|
+
|
|
129
|
+
const { originalFragment, forkedFragment, undoStack } = forkedState;
|
|
130
|
+
// Register the plugins again, based on the original fragment (which is still in the original options)
|
|
131
|
+
editor.registerExtension([
|
|
132
|
+
YSyncExtension(options),
|
|
133
|
+
YCursorExtension(options),
|
|
134
|
+
YUndoExtension({}),
|
|
135
|
+
]);
|
|
136
|
+
|
|
137
|
+
// Reset the undo stack to the original undo stack
|
|
138
|
+
yUndoPluginKey.getState(
|
|
139
|
+
editor.prosemirrorState,
|
|
140
|
+
)!.undoManager.undoStack = undoStack;
|
|
141
|
+
|
|
142
|
+
if (keepChanges) {
|
|
143
|
+
// Apply any changes that have been made to the fork, onto the original doc
|
|
144
|
+
const update = Y.encodeStateAsUpdate(
|
|
145
|
+
forkedFragment.doc!,
|
|
146
|
+
Y.encodeStateVector(originalFragment.doc!),
|
|
147
|
+
);
|
|
148
|
+
// Applying this change will add to the undo stack, allowing it to be undone normally
|
|
149
|
+
Y.applyUpdate(originalFragment.doc!, update, editor);
|
|
150
|
+
}
|
|
151
|
+
// Reset the forked state
|
|
152
|
+
forkedState = undefined;
|
|
153
|
+
// Tell the store that the editor is no longer forked
|
|
154
|
+
store.setState({ isForked: false });
|
|
155
|
+
},
|
|
156
|
+
} as const;
|
|
157
|
+
},
|
|
158
|
+
);
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { defaultSelectionBuilder, yCursorPlugin } from "y-prosemirror";
|
|
2
|
+
import {
|
|
3
|
+
createExtension,
|
|
4
|
+
ExtensionOptions,
|
|
5
|
+
} from "../../editor/BlockNoteExtension.js";
|
|
6
|
+
import { BlockNoteEditorOptions } from "../../editor/BlockNoteEditor.js";
|
|
7
|
+
|
|
8
|
+
export type CollaborationUser = {
|
|
9
|
+
name: string;
|
|
10
|
+
color: string;
|
|
11
|
+
[key: string]: string;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Determine whether the foreground color should be white or black based on a provided background color
|
|
16
|
+
* Inspired by: https://stackoverflow.com/a/3943023
|
|
17
|
+
*/
|
|
18
|
+
function isDarkColor(bgColor: string): boolean {
|
|
19
|
+
const color = bgColor.charAt(0) === "#" ? bgColor.substring(1, 7) : bgColor;
|
|
20
|
+
const r = parseInt(color.substring(0, 2), 16); // hexToR
|
|
21
|
+
const g = parseInt(color.substring(2, 4), 16); // hexToG
|
|
22
|
+
const b = parseInt(color.substring(4, 6), 16); // hexToB
|
|
23
|
+
const uicolors = [r / 255, g / 255, b / 255];
|
|
24
|
+
const c = uicolors.map((col) => {
|
|
25
|
+
if (col <= 0.03928) {
|
|
26
|
+
return col / 12.92;
|
|
27
|
+
}
|
|
28
|
+
return Math.pow((col + 0.055) / 1.055, 2.4);
|
|
29
|
+
});
|
|
30
|
+
const L = 0.2126 * c[0] + 0.7152 * c[1] + 0.0722 * c[2];
|
|
31
|
+
return L <= 0.179;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function defaultCursorRender(user: CollaborationUser) {
|
|
35
|
+
const cursorElement = document.createElement("span");
|
|
36
|
+
|
|
37
|
+
cursorElement.classList.add("bn-collaboration-cursor__base");
|
|
38
|
+
|
|
39
|
+
const caretElement = document.createElement("span");
|
|
40
|
+
caretElement.setAttribute("contentedEditable", "false");
|
|
41
|
+
caretElement.classList.add("bn-collaboration-cursor__caret");
|
|
42
|
+
caretElement.setAttribute(
|
|
43
|
+
"style",
|
|
44
|
+
`background-color: ${user.color}; color: ${
|
|
45
|
+
isDarkColor(user.color) ? "white" : "black"
|
|
46
|
+
}`,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
const labelElement = document.createElement("span");
|
|
50
|
+
|
|
51
|
+
labelElement.classList.add("bn-collaboration-cursor__label");
|
|
52
|
+
labelElement.setAttribute(
|
|
53
|
+
"style",
|
|
54
|
+
`background-color: ${user.color}; color: ${
|
|
55
|
+
isDarkColor(user.color) ? "white" : "black"
|
|
56
|
+
}`,
|
|
57
|
+
);
|
|
58
|
+
labelElement.insertBefore(document.createTextNode(user.name), null);
|
|
59
|
+
|
|
60
|
+
caretElement.insertBefore(labelElement, null);
|
|
61
|
+
|
|
62
|
+
cursorElement.insertBefore(document.createTextNode("\u2060"), null); // Non-breaking space
|
|
63
|
+
cursorElement.insertBefore(caretElement, null);
|
|
64
|
+
cursorElement.insertBefore(document.createTextNode("\u2060"), null); // Non-breaking space
|
|
65
|
+
|
|
66
|
+
return cursorElement;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const YCursorExtension = createExtension(
|
|
70
|
+
({
|
|
71
|
+
options,
|
|
72
|
+
}: ExtensionOptions<
|
|
73
|
+
NonNullable<BlockNoteEditorOptions<any, any, any>["collaboration"]>
|
|
74
|
+
>) => {
|
|
75
|
+
const recentlyUpdatedCursors = new Map();
|
|
76
|
+
|
|
77
|
+
if (
|
|
78
|
+
options.provider &&
|
|
79
|
+
"awareness" in options.provider &&
|
|
80
|
+
typeof options.provider.awareness === "object"
|
|
81
|
+
) {
|
|
82
|
+
if (
|
|
83
|
+
"setLocalStateField" in options.provider.awareness &&
|
|
84
|
+
typeof options.provider.awareness.setLocalStateField === "function"
|
|
85
|
+
) {
|
|
86
|
+
options.provider.awareness.setLocalStateField("user", options.user);
|
|
87
|
+
}
|
|
88
|
+
if (
|
|
89
|
+
"on" in options.provider.awareness &&
|
|
90
|
+
typeof options.provider.awareness.on === "function"
|
|
91
|
+
) {
|
|
92
|
+
if (options.showCursorLabels !== "always") {
|
|
93
|
+
options.provider.awareness.on(
|
|
94
|
+
"change",
|
|
95
|
+
({
|
|
96
|
+
updated,
|
|
97
|
+
}: {
|
|
98
|
+
added: Array<number>;
|
|
99
|
+
updated: Array<number>;
|
|
100
|
+
removed: Array<number>;
|
|
101
|
+
}) => {
|
|
102
|
+
for (const clientID of updated) {
|
|
103
|
+
const cursor = recentlyUpdatedCursors.get(clientID);
|
|
104
|
+
|
|
105
|
+
if (cursor) {
|
|
106
|
+
cursor.element.setAttribute("data-active", "");
|
|
107
|
+
|
|
108
|
+
if (cursor.hideTimeout) {
|
|
109
|
+
clearTimeout(cursor.hideTimeout);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
recentlyUpdatedCursors.set(clientID, {
|
|
113
|
+
element: cursor.element,
|
|
114
|
+
hideTimeout: setTimeout(() => {
|
|
115
|
+
cursor.element.removeAttribute("data-active");
|
|
116
|
+
}, 2000),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return {
|
|
127
|
+
key: "yCursor",
|
|
128
|
+
prosemirrorPlugins: [
|
|
129
|
+
yCursorPlugin(options.provider.awareness, {
|
|
130
|
+
selectionBuilder: defaultSelectionBuilder,
|
|
131
|
+
cursorBuilder(user: CollaborationUser, clientID: number) {
|
|
132
|
+
let cursorData = recentlyUpdatedCursors.get(clientID);
|
|
133
|
+
|
|
134
|
+
if (!cursorData) {
|
|
135
|
+
const cursorElement = (
|
|
136
|
+
options.renderCursor ?? defaultCursorRender
|
|
137
|
+
)(user);
|
|
138
|
+
|
|
139
|
+
if (options.showCursorLabels !== "always") {
|
|
140
|
+
cursorElement.addEventListener("mouseenter", () => {
|
|
141
|
+
const cursor = recentlyUpdatedCursors.get(clientID)!;
|
|
142
|
+
cursor.element.setAttribute("data-active", "");
|
|
143
|
+
|
|
144
|
+
if (cursor.hideTimeout) {
|
|
145
|
+
clearTimeout(cursor.hideTimeout);
|
|
146
|
+
recentlyUpdatedCursors.set(clientID, {
|
|
147
|
+
element: cursor.element,
|
|
148
|
+
hideTimeout: undefined,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
cursorElement.addEventListener("mouseleave", () => {
|
|
154
|
+
const cursor = recentlyUpdatedCursors.get(clientID)!;
|
|
155
|
+
|
|
156
|
+
recentlyUpdatedCursors.set(clientID, {
|
|
157
|
+
element: cursor.element,
|
|
158
|
+
hideTimeout: setTimeout(() => {
|
|
159
|
+
cursor.element.removeAttribute("data-active");
|
|
160
|
+
}, 2000),
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
cursorData = {
|
|
166
|
+
element: cursorElement,
|
|
167
|
+
hideTimeout: undefined,
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
recentlyUpdatedCursors.set(clientID, cursorData);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return cursorData.element;
|
|
174
|
+
},
|
|
175
|
+
}),
|
|
176
|
+
],
|
|
177
|
+
dependsOn: ["ySync"],
|
|
178
|
+
updateUser(user: { name: string; color: string; [key: string]: string }) {
|
|
179
|
+
options.provider.awareness.setLocalStateField("user", user);
|
|
180
|
+
},
|
|
181
|
+
} as const;
|
|
182
|
+
},
|
|
183
|
+
);
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { ySyncPlugin } from "y-prosemirror";
|
|
2
|
+
import { XmlFragment } from "yjs";
|
|
3
|
+
import {
|
|
4
|
+
ExtensionOptions,
|
|
5
|
+
createExtension,
|
|
6
|
+
} from "../../editor/BlockNoteExtension.js";
|
|
7
|
+
|
|
8
|
+
export const YSyncExtension = createExtension(
|
|
9
|
+
({ options }: ExtensionOptions<{ fragment: XmlFragment }>) => {
|
|
10
|
+
return {
|
|
11
|
+
key: "ySync",
|
|
12
|
+
prosemirrorPlugins: [ySyncPlugin(options.fragment)],
|
|
13
|
+
runsBefore: ["default"],
|
|
14
|
+
} as const;
|
|
15
|
+
},
|
|
16
|
+
);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { redoCommand, undoCommand, yUndoPlugin } from "y-prosemirror";
|
|
2
|
+
import { createExtension } from "../../editor/BlockNoteExtension.js";
|
|
3
|
+
|
|
4
|
+
export const YUndoExtension = createExtension(({ editor }) => {
|
|
5
|
+
return {
|
|
6
|
+
key: "yUndo",
|
|
7
|
+
prosemirrorPlugins: [yUndoPlugin({ trackedOrigins: [editor] })],
|
|
8
|
+
dependsOn: ["yCursor", "ySync"],
|
|
9
|
+
undoCommand: undoCommand,
|
|
10
|
+
redoCommand: redoCommand,
|
|
11
|
+
} as const;
|
|
12
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { Plugin, PluginKey } from "@tiptap/pm/state";
|
|
2
|
+
import * as Y from "yjs";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
createExtension,
|
|
6
|
+
ExtensionOptions,
|
|
7
|
+
} from "../../../editor/BlockNoteExtension.js";
|
|
8
|
+
import migrationRules from "./migrationRules/index.js";
|
|
9
|
+
|
|
10
|
+
// This plugin allows us to update collaboration YDocs whenever BlockNote's
|
|
11
|
+
// underlying ProseMirror schema changes. The plugin reads the current Yjs
|
|
12
|
+
// fragment and dispatches additional transactions to the ProseMirror state, in
|
|
13
|
+
// case things are found in the fragment that don't adhere to the editor schema
|
|
14
|
+
// and need to be fixed. These fixes are defined as `MigrationRule`s within the
|
|
15
|
+
// `migrationRules` directory.
|
|
16
|
+
export const SchemaMigration = createExtension(
|
|
17
|
+
({ options }: ExtensionOptions<{ fragment: Y.XmlFragment }>) => {
|
|
18
|
+
let migrationDone = false;
|
|
19
|
+
const pluginKey = new PluginKey("schemaMigration");
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
key: "schemaMigration",
|
|
23
|
+
prosemirrorPlugins: [
|
|
24
|
+
new Plugin({
|
|
25
|
+
key: pluginKey,
|
|
26
|
+
appendTransaction: (transactions, _oldState, newState) => {
|
|
27
|
+
if (migrationDone) {
|
|
28
|
+
return undefined;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (
|
|
32
|
+
// If any of the transactions are not due to a yjs sync, we don't need to run the migration
|
|
33
|
+
!transactions.some((tr) => tr.getMeta("y-sync$")) ||
|
|
34
|
+
// If none of the transactions result in a document change, we don't need to run the migration
|
|
35
|
+
transactions.every((tr) => !tr.docChanged) ||
|
|
36
|
+
// If the fragment is still empty, we can't run the migration (since it has not yet been applied to the Y.Doc)
|
|
37
|
+
!options.fragment.firstChild
|
|
38
|
+
) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const tr = newState.tr;
|
|
43
|
+
for (const migrationRule of migrationRules) {
|
|
44
|
+
migrationRule(options.fragment, tr);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
migrationDone = true;
|
|
48
|
+
|
|
49
|
+
if (!tr.docChanged) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return tr;
|
|
54
|
+
},
|
|
55
|
+
}),
|
|
56
|
+
],
|
|
57
|
+
} as const;
|
|
58
|
+
},
|
|
59
|
+
);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { dropCursor } from "prosemirror-dropcursor";
|
|
2
|
+
import {
|
|
3
|
+
createExtension,
|
|
4
|
+
ExtensionOptions,
|
|
5
|
+
} from "../../editor/BlockNoteExtension.js";
|
|
6
|
+
import { BlockNoteEditorOptions } from "../../editor/BlockNoteEditor.js";
|
|
7
|
+
|
|
8
|
+
export const DropCursorExtension = createExtension(
|
|
9
|
+
({
|
|
10
|
+
editor,
|
|
11
|
+
options,
|
|
12
|
+
}: ExtensionOptions<
|
|
13
|
+
Pick<BlockNoteEditorOptions<any, any, any>, "dropCursor">
|
|
14
|
+
>) => {
|
|
15
|
+
return {
|
|
16
|
+
key: "dropCursor",
|
|
17
|
+
prosemirrorPlugins: [
|
|
18
|
+
(options.dropCursor ?? dropCursor)({
|
|
19
|
+
width: 5,
|
|
20
|
+
color: "#ddeeff",
|
|
21
|
+
editor: editor,
|
|
22
|
+
}),
|
|
23
|
+
],
|
|
24
|
+
} as const;
|
|
25
|
+
},
|
|
26
|
+
);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createExtension,
|
|
3
|
+
createStore,
|
|
4
|
+
} from "../../editor/BlockNoteExtension.js";
|
|
5
|
+
|
|
6
|
+
export const FilePanelExtension = createExtension(({ editor }) => {
|
|
7
|
+
const store = createStore<string | undefined>(undefined);
|
|
8
|
+
|
|
9
|
+
function closeMenu() {
|
|
10
|
+
store.setState(undefined);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
key: "filePanel",
|
|
15
|
+
store,
|
|
16
|
+
mount({ signal }) {
|
|
17
|
+
// Reset the menu when the document changes.
|
|
18
|
+
const unsubscribeOnChange = editor.onChange(
|
|
19
|
+
closeMenu,
|
|
20
|
+
// Don't trigger if the changes are caused by a remote user.
|
|
21
|
+
false,
|
|
22
|
+
);
|
|
23
|
+
|
|
24
|
+
// reset the menu when the selection changes
|
|
25
|
+
const unsubscribeOnSelectionChange = editor.onSelectionChange(
|
|
26
|
+
closeMenu,
|
|
27
|
+
// Don't trigger if the changes are caused by a remote user.
|
|
28
|
+
false,
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
signal.addEventListener("abort", () => {
|
|
32
|
+
unsubscribeOnChange();
|
|
33
|
+
unsubscribeOnSelectionChange();
|
|
34
|
+
});
|
|
35
|
+
},
|
|
36
|
+
closeMenu,
|
|
37
|
+
showMenu(blockId: string) {
|
|
38
|
+
store.setState(blockId);
|
|
39
|
+
},
|
|
40
|
+
} as const;
|
|
41
|
+
});
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { NodeSelection, TextSelection } from "prosemirror-state";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createExtension,
|
|
5
|
+
createStore,
|
|
6
|
+
} from "../../editor/BlockNoteExtension.js";
|
|
7
|
+
|
|
8
|
+
export const FormattingToolbarExtension = createExtension(({ editor }) => {
|
|
9
|
+
const store = createStore(false);
|
|
10
|
+
|
|
11
|
+
const shouldShow = () => {
|
|
12
|
+
return editor.transact((tr) => {
|
|
13
|
+
// Don't show if the selection is empty, or is a text selection with no
|
|
14
|
+
// text.
|
|
15
|
+
if (tr.selection.empty) {
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Don't show if a block with inline content is selected.
|
|
20
|
+
if (
|
|
21
|
+
tr.selection instanceof NodeSelection &&
|
|
22
|
+
(tr.selection.node.type.spec.content === "inline*" ||
|
|
23
|
+
tr.selection.node.firstChild?.type.spec.content === "inline*")
|
|
24
|
+
) {
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Don't show if the selection is a text selection but contains no text.
|
|
29
|
+
if (
|
|
30
|
+
tr.selection instanceof TextSelection &&
|
|
31
|
+
tr.doc.textBetween(tr.selection.from, tr.selection.to).length === 0
|
|
32
|
+
) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Searches the content of the selection to see if it spans a node with a
|
|
37
|
+
// code spec.
|
|
38
|
+
let spansCode = false;
|
|
39
|
+
tr.selection.content().content.descendants((node) => {
|
|
40
|
+
if (node.type.spec.code) {
|
|
41
|
+
spansCode = true;
|
|
42
|
+
}
|
|
43
|
+
return !spansCode; // keep descending if we haven't found a code block
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
// Don't show if the selection spans a code block.
|
|
47
|
+
if (spansCode) {
|
|
48
|
+
return false;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Show toolbar otherwise.
|
|
52
|
+
return true;
|
|
53
|
+
});
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
return {
|
|
57
|
+
key: "formattingToolbar",
|
|
58
|
+
store,
|
|
59
|
+
mount({ dom, signal }) {
|
|
60
|
+
/**
|
|
61
|
+
* We want to mimic the Notion behavior of not showing the toolbar while the user is holding down the mouse button (to create a selection)
|
|
62
|
+
*/
|
|
63
|
+
let preventShowWhileMouseDown = false;
|
|
64
|
+
|
|
65
|
+
const unsubscribeOnChange = editor.onChange(() => {
|
|
66
|
+
if (preventShowWhileMouseDown) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
// re-evaluate whether the toolbar should be shown
|
|
70
|
+
store.setState(shouldShow());
|
|
71
|
+
});
|
|
72
|
+
const unsubscribeOnSelectionChange = editor.onSelectionChange(() => {
|
|
73
|
+
if (preventShowWhileMouseDown) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
// re-evaluate whether the toolbar should be shown
|
|
77
|
+
store.setState(shouldShow());
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
// To mimic Notion's behavior, we listen to the mouse down event to set the `preventShowWhileMouseDown` flag
|
|
81
|
+
dom.addEventListener(
|
|
82
|
+
"pointerdown",
|
|
83
|
+
() => {
|
|
84
|
+
preventShowWhileMouseDown = true;
|
|
85
|
+
store.setState(false);
|
|
86
|
+
},
|
|
87
|
+
{ signal },
|
|
88
|
+
);
|
|
89
|
+
// To mimic Notion's behavior, we listen to the mouse up event to reset the `preventShowWhileMouseDown` flag and show the toolbar (if it should)
|
|
90
|
+
editor.prosemirrorView.root.addEventListener(
|
|
91
|
+
"pointerup",
|
|
92
|
+
() => {
|
|
93
|
+
preventShowWhileMouseDown = false;
|
|
94
|
+
// We only want to re-show the toolbar if the mouse made the selection
|
|
95
|
+
if (editor.isFocused()) {
|
|
96
|
+
store.setState(shouldShow());
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
{ signal, capture: true },
|
|
100
|
+
);
|
|
101
|
+
// If the pointer gets cancelled, we don't want to be stuck in the `preventShowWhileMouseDown` state
|
|
102
|
+
dom.addEventListener(
|
|
103
|
+
"pointercancel",
|
|
104
|
+
() => {
|
|
105
|
+
preventShowWhileMouseDown = false;
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
signal,
|
|
109
|
+
capture: true,
|
|
110
|
+
},
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
signal.addEventListener("abort", () => {
|
|
114
|
+
unsubscribeOnChange();
|
|
115
|
+
unsubscribeOnSelectionChange();
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
} as const;
|
|
119
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { history, redo, undo } from "@tiptap/pm/history";
|
|
2
|
+
import { createExtension } from "../../editor/BlockNoteExtension.js";
|
|
3
|
+
|
|
4
|
+
export const HistoryExtension = createExtension(() => {
|
|
5
|
+
return {
|
|
6
|
+
key: "history",
|
|
7
|
+
prosemirrorPlugins: [history()],
|
|
8
|
+
undoCommand: undo,
|
|
9
|
+
redoCommand: redo,
|
|
10
|
+
} as const;
|
|
11
|
+
});
|