@liveblocks/prosemirror 0.0.0 → 3.21.0-exp5
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/LICENSE +16 -0
- package/README.md +3 -0
- package/dist/cursors.cjs +217 -0
- package/dist/cursors.cjs.map +1 -0
- package/dist/cursors.js +211 -0
- package/dist/cursors.js.map +1 -0
- package/dist/index.cjs +24 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +109 -0
- package/dist/index.d.ts +109 -0
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -0
- package/dist/mapping.cjs +218 -0
- package/dist/mapping.cjs.map +1 -0
- package/dist/mapping.js +207 -0
- package/dist/mapping.js.map +1 -0
- package/dist/plugin.cjs +227 -0
- package/dist/plugin.cjs.map +1 -0
- package/dist/plugin.js +223 -0
- package/dist/plugin.js.map +1 -0
- package/dist/remote.cjs +210 -0
- package/dist/remote.cjs.map +1 -0
- package/dist/remote.js +207 -0
- package/dist/remote.js.map +1 -0
- package/dist/schema.cjs +149 -0
- package/dist/schema.cjs.map +1 -0
- package/dist/schema.js +135 -0
- package/dist/schema.js.map +1 -0
- package/dist/steps.cjs +359 -0
- package/dist/steps.cjs.map +1 -0
- package/dist/steps.js +356 -0
- package/dist/steps.js.map +1 -0
- package/dist/version.cjs +10 -0
- package/dist/version.cjs.map +1 -0
- package/dist/version.js +6 -0
- package/dist/version.js.map +1 -0
- package/package.json +55 -3
- package/index.js +0 -1
package/dist/plugin.cjs
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var client = require('@liveblocks/client');
|
|
4
|
+
var prosemirrorModel = require('prosemirror-model');
|
|
5
|
+
var prosemirrorState = require('prosemirror-state');
|
|
6
|
+
var remote = require('./remote.cjs');
|
|
7
|
+
var schema = require('./schema.cjs');
|
|
8
|
+
var steps = require('./steps.cjs');
|
|
9
|
+
|
|
10
|
+
const LIVEBLOCKS_COLLABORATION_PLUGIN_KEY = new prosemirrorState.PluginKey("liveblocks-collaboration");
|
|
11
|
+
const LIVEBLOCKS_TIPTAP_DOCUMENTS_KEY = "_tiptap_docs";
|
|
12
|
+
function isProseMirrorJsonNode(value) {
|
|
13
|
+
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
14
|
+
}
|
|
15
|
+
function getInitialDocument(initialContent, fallbackDocument, view) {
|
|
16
|
+
if (isProseMirrorJsonNode(initialContent)) {
|
|
17
|
+
return initialContent;
|
|
18
|
+
}
|
|
19
|
+
const currentDocument = view.state.doc.toJSON();
|
|
20
|
+
if (isProseMirrorJsonNode(currentDocument)) {
|
|
21
|
+
return currentDocument;
|
|
22
|
+
}
|
|
23
|
+
const fallback = fallbackDocument?.();
|
|
24
|
+
if (isProseMirrorJsonNode(fallback)) {
|
|
25
|
+
return fallback;
|
|
26
|
+
}
|
|
27
|
+
throw new Error(
|
|
28
|
+
"[Liveblocks] The Liveblocks collaboration plugin could not resolve an initial document."
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
function replaceEditorDocument(view, document, fallbackDocument) {
|
|
32
|
+
let nextDocument;
|
|
33
|
+
try {
|
|
34
|
+
nextDocument = view.state.schema.nodeFromJSON(document);
|
|
35
|
+
} catch {
|
|
36
|
+
const fallback = fallbackDocument?.();
|
|
37
|
+
if (!isProseMirrorJsonNode(fallback)) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
nextDocument = view.state.schema.nodeFromJSON(fallback);
|
|
41
|
+
}
|
|
42
|
+
if (nextDocument.childCount === 0) {
|
|
43
|
+
const fallback = fallbackDocument?.();
|
|
44
|
+
if (!isProseMirrorJsonNode(fallback)) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
nextDocument = view.state.schema.nodeFromJSON(fallback);
|
|
48
|
+
}
|
|
49
|
+
const tr = view.state.tr.replace(
|
|
50
|
+
0,
|
|
51
|
+
view.state.doc.content.size,
|
|
52
|
+
new prosemirrorModel.Slice(nextDocument.content, 0, 0)
|
|
53
|
+
);
|
|
54
|
+
tr.setMeta(LIVEBLOCKS_COLLABORATION_PLUGIN_KEY, { isRemote: true }).setMeta(
|
|
55
|
+
"addToHistory",
|
|
56
|
+
false
|
|
57
|
+
);
|
|
58
|
+
view.dispatch(tr);
|
|
59
|
+
}
|
|
60
|
+
function getDocumentRoot(root, field) {
|
|
61
|
+
const documents = root.get(LIVEBLOCKS_TIPTAP_DOCUMENTS_KEY);
|
|
62
|
+
if (!(documents instanceof client.LiveMap)) {
|
|
63
|
+
return void 0;
|
|
64
|
+
}
|
|
65
|
+
const documentRoot = documents.get(field);
|
|
66
|
+
if (!(documentRoot instanceof client.LiveObject)) {
|
|
67
|
+
return void 0;
|
|
68
|
+
}
|
|
69
|
+
return documentRoot;
|
|
70
|
+
}
|
|
71
|
+
function setDocumentRoot(root, field, document) {
|
|
72
|
+
let documents = root.get(LIVEBLOCKS_TIPTAP_DOCUMENTS_KEY);
|
|
73
|
+
if (!(documents instanceof client.LiveMap)) {
|
|
74
|
+
documents = new client.LiveMap();
|
|
75
|
+
root.set(LIVEBLOCKS_TIPTAP_DOCUMENTS_KEY, documents);
|
|
76
|
+
}
|
|
77
|
+
documents.set(field, schema.createLiveblocksProsemirrorNode(document));
|
|
78
|
+
}
|
|
79
|
+
function createLiveblocksCollaborationPlugin(options) {
|
|
80
|
+
const room = options.room;
|
|
81
|
+
if (room === void 0) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
"[Liveblocks] The Liveblocks collaboration plugin requires a room."
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
let view;
|
|
87
|
+
let root;
|
|
88
|
+
let unsubscribe;
|
|
89
|
+
let destroyed = false;
|
|
90
|
+
let isApplyingRemoteUpdate = false;
|
|
91
|
+
let isApplyingLocalUpdate = false;
|
|
92
|
+
let lastDocument = "";
|
|
93
|
+
const applyStorageToEditor = (updates) => {
|
|
94
|
+
if (view === void 0 || root === void 0) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const documentRoot = getDocumentRoot(root, options.field);
|
|
98
|
+
if (documentRoot === void 0) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
if (isApplyingLocalUpdate) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const document = schema.liveblocksProsemirrorNodeToJson(
|
|
105
|
+
documentRoot,
|
|
106
|
+
options.fallbackDocument
|
|
107
|
+
);
|
|
108
|
+
const serializedDocument = schema.stringifyDocument(document);
|
|
109
|
+
if (serializedDocument === lastDocument) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (updates !== void 0) {
|
|
113
|
+
const result = remote.applyRemoteStorageUpdates(view, documentRoot, updates);
|
|
114
|
+
if (result.type === "applied") {
|
|
115
|
+
lastDocument = serializedDocument;
|
|
116
|
+
isApplyingRemoteUpdate = true;
|
|
117
|
+
try {
|
|
118
|
+
view.dispatch(
|
|
119
|
+
result.tr.setMeta(LIVEBLOCKS_COLLABORATION_PLUGIN_KEY, { isRemote: true }).setMeta("addToHistory", false)
|
|
120
|
+
);
|
|
121
|
+
} finally {
|
|
122
|
+
isApplyingRemoteUpdate = false;
|
|
123
|
+
}
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
lastDocument = serializedDocument;
|
|
128
|
+
isApplyingRemoteUpdate = true;
|
|
129
|
+
try {
|
|
130
|
+
replaceEditorDocument(view, document, options.fallbackDocument);
|
|
131
|
+
} finally {
|
|
132
|
+
isApplyingRemoteUpdate = false;
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
return new prosemirrorState.Plugin({
|
|
136
|
+
key: LIVEBLOCKS_COLLABORATION_PLUGIN_KEY,
|
|
137
|
+
state: {
|
|
138
|
+
init: () => ({ isReady: false }),
|
|
139
|
+
apply(tr, state) {
|
|
140
|
+
const meta = tr.getMeta(LIVEBLOCKS_COLLABORATION_PLUGIN_KEY);
|
|
141
|
+
return meta?.isReady !== void 0 ? { ...state, isReady: meta.isReady } : state;
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
appendTransaction(transactions, oldState, newState) {
|
|
145
|
+
if (root === void 0 || isApplyingRemoteUpdate || !transactions.some((transaction) => transaction.docChanged) || transactions.some(
|
|
146
|
+
(transaction) => Boolean(transaction.getMeta(LIVEBLOCKS_COLLABORATION_PLUGIN_KEY))
|
|
147
|
+
)) {
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
const currentRoot = root;
|
|
151
|
+
const documentRoot = getDocumentRoot(currentRoot, options.field);
|
|
152
|
+
const classified = documentRoot !== void 0 ? steps.classifyTransaction(
|
|
153
|
+
transactions,
|
|
154
|
+
oldState.doc,
|
|
155
|
+
newState.doc,
|
|
156
|
+
documentRoot
|
|
157
|
+
) : { type: "unsupported" };
|
|
158
|
+
room.batch(() => {
|
|
159
|
+
if (classified.type === "incremental") {
|
|
160
|
+
isApplyingLocalUpdate = true;
|
|
161
|
+
try {
|
|
162
|
+
steps.applyIncrementalOperations(classified.operations);
|
|
163
|
+
} finally {
|
|
164
|
+
isApplyingLocalUpdate = false;
|
|
165
|
+
}
|
|
166
|
+
} else {
|
|
167
|
+
const document = newState.doc.toJSON();
|
|
168
|
+
if (!isProseMirrorJsonNode(document)) {
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const serializedDocument = schema.stringifyDocument(document);
|
|
172
|
+
if (serializedDocument === lastDocument) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
lastDocument = serializedDocument;
|
|
176
|
+
setDocumentRoot(currentRoot, options.field, document);
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
return null;
|
|
180
|
+
},
|
|
181
|
+
view(editorView) {
|
|
182
|
+
view = editorView;
|
|
183
|
+
room.getStorage().then(({ root: storageRoot }) => {
|
|
184
|
+
if (destroyed) {
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
root = storageRoot;
|
|
188
|
+
if (getDocumentRoot(storageRoot, options.field) === void 0) {
|
|
189
|
+
const initialDocument = getInitialDocument(
|
|
190
|
+
options.initialContent,
|
|
191
|
+
options.fallbackDocument,
|
|
192
|
+
editorView
|
|
193
|
+
);
|
|
194
|
+
room.history.disable(() => {
|
|
195
|
+
setDocumentRoot(storageRoot, options.field, initialDocument);
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
applyStorageToEditor();
|
|
199
|
+
const tr = editorView.state.tr.setMeta(
|
|
200
|
+
LIVEBLOCKS_COLLABORATION_PLUGIN_KEY,
|
|
201
|
+
{ isReady: true }
|
|
202
|
+
);
|
|
203
|
+
editorView.dispatch(tr);
|
|
204
|
+
unsubscribe = room.subscribe(storageRoot, applyStorageToEditor, {
|
|
205
|
+
isDeep: true
|
|
206
|
+
});
|
|
207
|
+
});
|
|
208
|
+
return {
|
|
209
|
+
update(nextView) {
|
|
210
|
+
view = nextView;
|
|
211
|
+
},
|
|
212
|
+
destroy() {
|
|
213
|
+
destroyed = true;
|
|
214
|
+
unsubscribe?.();
|
|
215
|
+
unsubscribe = void 0;
|
|
216
|
+
view = void 0;
|
|
217
|
+
root = void 0;
|
|
218
|
+
}
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
exports.LIVEBLOCKS_COLLABORATION_PLUGIN_KEY = LIVEBLOCKS_COLLABORATION_PLUGIN_KEY;
|
|
225
|
+
exports.LIVEBLOCKS_TIPTAP_DOCUMENTS_KEY = LIVEBLOCKS_TIPTAP_DOCUMENTS_KEY;
|
|
226
|
+
exports.createLiveblocksCollaborationPlugin = createLiveblocksCollaborationPlugin;
|
|
227
|
+
//# sourceMappingURL=plugin.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.cjs","sources":["../src/plugin.ts"],"sourcesContent":["import type { LsonObject, StorageUpdate } from \"@liveblocks/client\";\nimport { LiveMap, LiveObject } from \"@liveblocks/client\";\nimport { Slice } from \"prosemirror-model\";\nimport { Plugin, PluginKey } from \"prosemirror-state\";\nimport type { EditorView } from \"prosemirror-view\";\n\nimport { applyRemoteStorageUpdates } from \"./remote\";\nimport {\n createLiveblocksProsemirrorNode,\n type LiveblocksProsemirrorNode,\n liveblocksProsemirrorNodeToJson,\n type ProseMirrorJsonNode,\n stringifyDocument,\n} from \"./schema\";\nimport { applyIncrementalOperations, classifyTransaction } from \"./steps\";\nimport type { LiveblocksProsemirrorRoom } from \"./types\";\n\nexport const LIVEBLOCKS_COLLABORATION_PLUGIN_KEY = new PluginKey<{\n isReady: boolean;\n}>(\"liveblocks-collaboration\");\nexport const LIVEBLOCKS_TIPTAP_DOCUMENTS_KEY = \"_tiptap_docs\";\n\nexport type LiveblocksCollaborationOptions = {\n room?: LiveblocksProsemirrorRoom;\n field: string;\n initialContent?: ProseMirrorJsonNode;\n fallbackDocument?: () => ProseMirrorJsonNode;\n};\n\nfunction isProseMirrorJsonNode(value: unknown): value is ProseMirrorJsonNode {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { type?: unknown }).type === \"string\"\n );\n}\n\nfunction getInitialDocument(\n initialContent: ProseMirrorJsonNode | undefined,\n fallbackDocument: (() => ProseMirrorJsonNode) | undefined,\n view: EditorView\n): ProseMirrorJsonNode {\n if (isProseMirrorJsonNode(initialContent)) {\n return initialContent;\n }\n\n const currentDocument: unknown = view.state.doc.toJSON();\n if (isProseMirrorJsonNode(currentDocument)) {\n return currentDocument;\n }\n\n const fallback = fallbackDocument?.();\n if (isProseMirrorJsonNode(fallback)) {\n return fallback;\n }\n\n throw new Error(\n \"[Liveblocks] The Liveblocks collaboration plugin could not resolve an initial document.\"\n );\n}\n\nfunction replaceEditorDocument(\n view: EditorView,\n document: ProseMirrorJsonNode,\n fallbackDocument: (() => ProseMirrorJsonNode) | undefined\n): void {\n let nextDocument;\n try {\n nextDocument = view.state.schema.nodeFromJSON(document);\n } catch {\n const fallback = fallbackDocument?.();\n if (!isProseMirrorJsonNode(fallback)) {\n return;\n }\n\n nextDocument = view.state.schema.nodeFromJSON(fallback);\n }\n\n if (nextDocument.childCount === 0) {\n const fallback = fallbackDocument?.();\n if (!isProseMirrorJsonNode(fallback)) {\n return;\n }\n\n nextDocument = view.state.schema.nodeFromJSON(fallback);\n }\n\n const tr = view.state.tr.replace(\n 0,\n view.state.doc.content.size,\n new Slice(nextDocument.content, 0, 0)\n );\n\n tr.setMeta(LIVEBLOCKS_COLLABORATION_PLUGIN_KEY, { isRemote: true }).setMeta(\n \"addToHistory\",\n false\n );\n\n view.dispatch(tr);\n}\n\nfunction getDocumentRoot(\n root: LiveObject<LsonObject>,\n field: string\n): LiveblocksProsemirrorNode | undefined {\n const documents = root.get(LIVEBLOCKS_TIPTAP_DOCUMENTS_KEY);\n if (!(documents instanceof LiveMap)) {\n return undefined;\n }\n\n const documentRoot = documents.get(field);\n if (!(documentRoot instanceof LiveObject)) {\n return undefined;\n }\n\n return documentRoot as LiveblocksProsemirrorNode;\n}\n\nfunction setDocumentRoot(\n root: LiveObject<LsonObject>,\n field: string,\n document: ProseMirrorJsonNode\n): void {\n let documents = root.get(LIVEBLOCKS_TIPTAP_DOCUMENTS_KEY);\n if (!(documents instanceof LiveMap)) {\n documents = new LiveMap<string, LiveblocksProsemirrorNode>();\n root.set(LIVEBLOCKS_TIPTAP_DOCUMENTS_KEY, documents);\n }\n\n documents.set(field, createLiveblocksProsemirrorNode(document));\n}\n\nexport function createLiveblocksCollaborationPlugin(\n options: LiveblocksCollaborationOptions\n): Plugin {\n const room = options.room;\n if (room === undefined) {\n throw new Error(\n \"[Liveblocks] The Liveblocks collaboration plugin requires a room.\"\n );\n }\n\n let view: EditorView | undefined;\n let root: LiveObject<LsonObject> | undefined;\n let unsubscribe: (() => void) | undefined;\n let destroyed = false;\n let isApplyingRemoteUpdate = false;\n let isApplyingLocalUpdate = false;\n let lastDocument = \"\";\n\n const applyStorageToEditor = (updates?: StorageUpdate[]) => {\n if (view === undefined || root === undefined) {\n return;\n }\n\n const documentRoot = getDocumentRoot(root, options.field);\n if (documentRoot === undefined) {\n return;\n }\n\n if (isApplyingLocalUpdate) {\n return;\n }\n\n const document = liveblocksProsemirrorNodeToJson(\n documentRoot,\n options.fallbackDocument\n );\n const serializedDocument = stringifyDocument(document);\n\n if (serializedDocument === lastDocument) {\n return;\n }\n\n if (updates !== undefined) {\n const result = applyRemoteStorageUpdates(view, documentRoot, updates);\n if (result.type === \"applied\") {\n lastDocument = serializedDocument;\n isApplyingRemoteUpdate = true;\n try {\n view.dispatch(\n result.tr\n .setMeta(LIVEBLOCKS_COLLABORATION_PLUGIN_KEY, { isRemote: true })\n .setMeta(\"addToHistory\", false)\n );\n } finally {\n isApplyingRemoteUpdate = false;\n }\n return;\n }\n }\n\n lastDocument = serializedDocument;\n isApplyingRemoteUpdate = true;\n try {\n replaceEditorDocument(view, document, options.fallbackDocument);\n } finally {\n isApplyingRemoteUpdate = false;\n }\n };\n\n return new Plugin({\n key: LIVEBLOCKS_COLLABORATION_PLUGIN_KEY,\n state: {\n init: () => ({ isReady: false }),\n apply(tr, state) {\n const meta = tr.getMeta(LIVEBLOCKS_COLLABORATION_PLUGIN_KEY) as\n | { isReady?: boolean }\n | undefined;\n\n return meta?.isReady !== undefined\n ? { ...state, isReady: meta.isReady }\n : state;\n },\n },\n appendTransaction(transactions, oldState, newState) {\n if (\n root === undefined ||\n isApplyingRemoteUpdate ||\n !transactions.some((transaction) => transaction.docChanged) ||\n transactions.some((transaction) =>\n Boolean(transaction.getMeta(LIVEBLOCKS_COLLABORATION_PLUGIN_KEY))\n )\n ) {\n return null;\n }\n\n const currentRoot = root;\n\n const documentRoot = getDocumentRoot(currentRoot, options.field);\n const classified =\n documentRoot !== undefined\n ? classifyTransaction(\n transactions,\n oldState.doc,\n newState.doc,\n documentRoot\n )\n : { type: \"unsupported\" as const };\n\n room.batch(() => {\n if (classified.type === \"incremental\") {\n isApplyingLocalUpdate = true;\n try {\n applyIncrementalOperations(classified.operations);\n } finally {\n isApplyingLocalUpdate = false;\n }\n } else {\n const document: unknown = newState.doc.toJSON();\n if (!isProseMirrorJsonNode(document)) {\n return;\n }\n\n const serializedDocument = stringifyDocument(document);\n if (serializedDocument === lastDocument) {\n return;\n }\n\n lastDocument = serializedDocument;\n setDocumentRoot(currentRoot, options.field, document);\n }\n });\n\n return null;\n },\n view(editorView) {\n view = editorView;\n\n room.getStorage().then(({ root: storageRoot }) => {\n if (destroyed) {\n return;\n }\n\n root = storageRoot;\n\n if (getDocumentRoot(storageRoot, options.field) === undefined) {\n const initialDocument = getInitialDocument(\n options.initialContent,\n options.fallbackDocument,\n editorView\n );\n room.history.disable(() => {\n setDocumentRoot(storageRoot, options.field, initialDocument);\n });\n }\n\n applyStorageToEditor();\n\n const tr = editorView.state.tr.setMeta(\n LIVEBLOCKS_COLLABORATION_PLUGIN_KEY,\n { isReady: true }\n );\n editorView.dispatch(tr);\n\n unsubscribe = room.subscribe(storageRoot, applyStorageToEditor, {\n isDeep: true,\n });\n });\n\n return {\n update(nextView) {\n view = nextView;\n },\n destroy() {\n destroyed = true;\n unsubscribe?.();\n unsubscribe = undefined;\n view = undefined;\n root = undefined;\n },\n };\n },\n });\n}\n"],"names":["PluginKey","Slice","LiveMap","LiveObject","createLiveblocksProsemirrorNode","liveblocksProsemirrorNodeToJson","stringifyDocument","applyRemoteStorageUpdates","Plugin","classifyTransaction","applyIncrementalOperations"],"mappings":"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"}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import { LiveMap, LiveObject } from '@liveblocks/client';
|
|
2
|
+
import { Slice } from 'prosemirror-model';
|
|
3
|
+
import { PluginKey, Plugin } from 'prosemirror-state';
|
|
4
|
+
import { applyRemoteStorageUpdates } from './remote.js';
|
|
5
|
+
import { createLiveblocksProsemirrorNode, liveblocksProsemirrorNodeToJson, stringifyDocument } from './schema.js';
|
|
6
|
+
import { classifyTransaction, applyIncrementalOperations } from './steps.js';
|
|
7
|
+
|
|
8
|
+
const LIVEBLOCKS_COLLABORATION_PLUGIN_KEY = new PluginKey("liveblocks-collaboration");
|
|
9
|
+
const LIVEBLOCKS_TIPTAP_DOCUMENTS_KEY = "_tiptap_docs";
|
|
10
|
+
function isProseMirrorJsonNode(value) {
|
|
11
|
+
return typeof value === "object" && value !== null && typeof value.type === "string";
|
|
12
|
+
}
|
|
13
|
+
function getInitialDocument(initialContent, fallbackDocument, view) {
|
|
14
|
+
if (isProseMirrorJsonNode(initialContent)) {
|
|
15
|
+
return initialContent;
|
|
16
|
+
}
|
|
17
|
+
const currentDocument = view.state.doc.toJSON();
|
|
18
|
+
if (isProseMirrorJsonNode(currentDocument)) {
|
|
19
|
+
return currentDocument;
|
|
20
|
+
}
|
|
21
|
+
const fallback = fallbackDocument?.();
|
|
22
|
+
if (isProseMirrorJsonNode(fallback)) {
|
|
23
|
+
return fallback;
|
|
24
|
+
}
|
|
25
|
+
throw new Error(
|
|
26
|
+
"[Liveblocks] The Liveblocks collaboration plugin could not resolve an initial document."
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
function replaceEditorDocument(view, document, fallbackDocument) {
|
|
30
|
+
let nextDocument;
|
|
31
|
+
try {
|
|
32
|
+
nextDocument = view.state.schema.nodeFromJSON(document);
|
|
33
|
+
} catch {
|
|
34
|
+
const fallback = fallbackDocument?.();
|
|
35
|
+
if (!isProseMirrorJsonNode(fallback)) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
nextDocument = view.state.schema.nodeFromJSON(fallback);
|
|
39
|
+
}
|
|
40
|
+
if (nextDocument.childCount === 0) {
|
|
41
|
+
const fallback = fallbackDocument?.();
|
|
42
|
+
if (!isProseMirrorJsonNode(fallback)) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
nextDocument = view.state.schema.nodeFromJSON(fallback);
|
|
46
|
+
}
|
|
47
|
+
const tr = view.state.tr.replace(
|
|
48
|
+
0,
|
|
49
|
+
view.state.doc.content.size,
|
|
50
|
+
new Slice(nextDocument.content, 0, 0)
|
|
51
|
+
);
|
|
52
|
+
tr.setMeta(LIVEBLOCKS_COLLABORATION_PLUGIN_KEY, { isRemote: true }).setMeta(
|
|
53
|
+
"addToHistory",
|
|
54
|
+
false
|
|
55
|
+
);
|
|
56
|
+
view.dispatch(tr);
|
|
57
|
+
}
|
|
58
|
+
function getDocumentRoot(root, field) {
|
|
59
|
+
const documents = root.get(LIVEBLOCKS_TIPTAP_DOCUMENTS_KEY);
|
|
60
|
+
if (!(documents instanceof LiveMap)) {
|
|
61
|
+
return void 0;
|
|
62
|
+
}
|
|
63
|
+
const documentRoot = documents.get(field);
|
|
64
|
+
if (!(documentRoot instanceof LiveObject)) {
|
|
65
|
+
return void 0;
|
|
66
|
+
}
|
|
67
|
+
return documentRoot;
|
|
68
|
+
}
|
|
69
|
+
function setDocumentRoot(root, field, document) {
|
|
70
|
+
let documents = root.get(LIVEBLOCKS_TIPTAP_DOCUMENTS_KEY);
|
|
71
|
+
if (!(documents instanceof LiveMap)) {
|
|
72
|
+
documents = new LiveMap();
|
|
73
|
+
root.set(LIVEBLOCKS_TIPTAP_DOCUMENTS_KEY, documents);
|
|
74
|
+
}
|
|
75
|
+
documents.set(field, createLiveblocksProsemirrorNode(document));
|
|
76
|
+
}
|
|
77
|
+
function createLiveblocksCollaborationPlugin(options) {
|
|
78
|
+
const room = options.room;
|
|
79
|
+
if (room === void 0) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
"[Liveblocks] The Liveblocks collaboration plugin requires a room."
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
let view;
|
|
85
|
+
let root;
|
|
86
|
+
let unsubscribe;
|
|
87
|
+
let destroyed = false;
|
|
88
|
+
let isApplyingRemoteUpdate = false;
|
|
89
|
+
let isApplyingLocalUpdate = false;
|
|
90
|
+
let lastDocument = "";
|
|
91
|
+
const applyStorageToEditor = (updates) => {
|
|
92
|
+
if (view === void 0 || root === void 0) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
const documentRoot = getDocumentRoot(root, options.field);
|
|
96
|
+
if (documentRoot === void 0) {
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
if (isApplyingLocalUpdate) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const document = liveblocksProsemirrorNodeToJson(
|
|
103
|
+
documentRoot,
|
|
104
|
+
options.fallbackDocument
|
|
105
|
+
);
|
|
106
|
+
const serializedDocument = stringifyDocument(document);
|
|
107
|
+
if (serializedDocument === lastDocument) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (updates !== void 0) {
|
|
111
|
+
const result = applyRemoteStorageUpdates(view, documentRoot, updates);
|
|
112
|
+
if (result.type === "applied") {
|
|
113
|
+
lastDocument = serializedDocument;
|
|
114
|
+
isApplyingRemoteUpdate = true;
|
|
115
|
+
try {
|
|
116
|
+
view.dispatch(
|
|
117
|
+
result.tr.setMeta(LIVEBLOCKS_COLLABORATION_PLUGIN_KEY, { isRemote: true }).setMeta("addToHistory", false)
|
|
118
|
+
);
|
|
119
|
+
} finally {
|
|
120
|
+
isApplyingRemoteUpdate = false;
|
|
121
|
+
}
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
lastDocument = serializedDocument;
|
|
126
|
+
isApplyingRemoteUpdate = true;
|
|
127
|
+
try {
|
|
128
|
+
replaceEditorDocument(view, document, options.fallbackDocument);
|
|
129
|
+
} finally {
|
|
130
|
+
isApplyingRemoteUpdate = false;
|
|
131
|
+
}
|
|
132
|
+
};
|
|
133
|
+
return new Plugin({
|
|
134
|
+
key: LIVEBLOCKS_COLLABORATION_PLUGIN_KEY,
|
|
135
|
+
state: {
|
|
136
|
+
init: () => ({ isReady: false }),
|
|
137
|
+
apply(tr, state) {
|
|
138
|
+
const meta = tr.getMeta(LIVEBLOCKS_COLLABORATION_PLUGIN_KEY);
|
|
139
|
+
return meta?.isReady !== void 0 ? { ...state, isReady: meta.isReady } : state;
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
appendTransaction(transactions, oldState, newState) {
|
|
143
|
+
if (root === void 0 || isApplyingRemoteUpdate || !transactions.some((transaction) => transaction.docChanged) || transactions.some(
|
|
144
|
+
(transaction) => Boolean(transaction.getMeta(LIVEBLOCKS_COLLABORATION_PLUGIN_KEY))
|
|
145
|
+
)) {
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
148
|
+
const currentRoot = root;
|
|
149
|
+
const documentRoot = getDocumentRoot(currentRoot, options.field);
|
|
150
|
+
const classified = documentRoot !== void 0 ? classifyTransaction(
|
|
151
|
+
transactions,
|
|
152
|
+
oldState.doc,
|
|
153
|
+
newState.doc,
|
|
154
|
+
documentRoot
|
|
155
|
+
) : { type: "unsupported" };
|
|
156
|
+
room.batch(() => {
|
|
157
|
+
if (classified.type === "incremental") {
|
|
158
|
+
isApplyingLocalUpdate = true;
|
|
159
|
+
try {
|
|
160
|
+
applyIncrementalOperations(classified.operations);
|
|
161
|
+
} finally {
|
|
162
|
+
isApplyingLocalUpdate = false;
|
|
163
|
+
}
|
|
164
|
+
} else {
|
|
165
|
+
const document = newState.doc.toJSON();
|
|
166
|
+
if (!isProseMirrorJsonNode(document)) {
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const serializedDocument = stringifyDocument(document);
|
|
170
|
+
if (serializedDocument === lastDocument) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
lastDocument = serializedDocument;
|
|
174
|
+
setDocumentRoot(currentRoot, options.field, document);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
return null;
|
|
178
|
+
},
|
|
179
|
+
view(editorView) {
|
|
180
|
+
view = editorView;
|
|
181
|
+
room.getStorage().then(({ root: storageRoot }) => {
|
|
182
|
+
if (destroyed) {
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
root = storageRoot;
|
|
186
|
+
if (getDocumentRoot(storageRoot, options.field) === void 0) {
|
|
187
|
+
const initialDocument = getInitialDocument(
|
|
188
|
+
options.initialContent,
|
|
189
|
+
options.fallbackDocument,
|
|
190
|
+
editorView
|
|
191
|
+
);
|
|
192
|
+
room.history.disable(() => {
|
|
193
|
+
setDocumentRoot(storageRoot, options.field, initialDocument);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
applyStorageToEditor();
|
|
197
|
+
const tr = editorView.state.tr.setMeta(
|
|
198
|
+
LIVEBLOCKS_COLLABORATION_PLUGIN_KEY,
|
|
199
|
+
{ isReady: true }
|
|
200
|
+
);
|
|
201
|
+
editorView.dispatch(tr);
|
|
202
|
+
unsubscribe = room.subscribe(storageRoot, applyStorageToEditor, {
|
|
203
|
+
isDeep: true
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
return {
|
|
207
|
+
update(nextView) {
|
|
208
|
+
view = nextView;
|
|
209
|
+
},
|
|
210
|
+
destroy() {
|
|
211
|
+
destroyed = true;
|
|
212
|
+
unsubscribe?.();
|
|
213
|
+
unsubscribe = void 0;
|
|
214
|
+
view = void 0;
|
|
215
|
+
root = void 0;
|
|
216
|
+
}
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export { LIVEBLOCKS_COLLABORATION_PLUGIN_KEY, LIVEBLOCKS_TIPTAP_DOCUMENTS_KEY, createLiveblocksCollaborationPlugin };
|
|
223
|
+
//# sourceMappingURL=plugin.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.js","sources":["../src/plugin.ts"],"sourcesContent":["import type { LsonObject, StorageUpdate } from \"@liveblocks/client\";\nimport { LiveMap, LiveObject } from \"@liveblocks/client\";\nimport { Slice } from \"prosemirror-model\";\nimport { Plugin, PluginKey } from \"prosemirror-state\";\nimport type { EditorView } from \"prosemirror-view\";\n\nimport { applyRemoteStorageUpdates } from \"./remote\";\nimport {\n createLiveblocksProsemirrorNode,\n type LiveblocksProsemirrorNode,\n liveblocksProsemirrorNodeToJson,\n type ProseMirrorJsonNode,\n stringifyDocument,\n} from \"./schema\";\nimport { applyIncrementalOperations, classifyTransaction } from \"./steps\";\nimport type { LiveblocksProsemirrorRoom } from \"./types\";\n\nexport const LIVEBLOCKS_COLLABORATION_PLUGIN_KEY = new PluginKey<{\n isReady: boolean;\n}>(\"liveblocks-collaboration\");\nexport const LIVEBLOCKS_TIPTAP_DOCUMENTS_KEY = \"_tiptap_docs\";\n\nexport type LiveblocksCollaborationOptions = {\n room?: LiveblocksProsemirrorRoom;\n field: string;\n initialContent?: ProseMirrorJsonNode;\n fallbackDocument?: () => ProseMirrorJsonNode;\n};\n\nfunction isProseMirrorJsonNode(value: unknown): value is ProseMirrorJsonNode {\n return (\n typeof value === \"object\" &&\n value !== null &&\n typeof (value as { type?: unknown }).type === \"string\"\n );\n}\n\nfunction getInitialDocument(\n initialContent: ProseMirrorJsonNode | undefined,\n fallbackDocument: (() => ProseMirrorJsonNode) | undefined,\n view: EditorView\n): ProseMirrorJsonNode {\n if (isProseMirrorJsonNode(initialContent)) {\n return initialContent;\n }\n\n const currentDocument: unknown = view.state.doc.toJSON();\n if (isProseMirrorJsonNode(currentDocument)) {\n return currentDocument;\n }\n\n const fallback = fallbackDocument?.();\n if (isProseMirrorJsonNode(fallback)) {\n return fallback;\n }\n\n throw new Error(\n \"[Liveblocks] The Liveblocks collaboration plugin could not resolve an initial document.\"\n );\n}\n\nfunction replaceEditorDocument(\n view: EditorView,\n document: ProseMirrorJsonNode,\n fallbackDocument: (() => ProseMirrorJsonNode) | undefined\n): void {\n let nextDocument;\n try {\n nextDocument = view.state.schema.nodeFromJSON(document);\n } catch {\n const fallback = fallbackDocument?.();\n if (!isProseMirrorJsonNode(fallback)) {\n return;\n }\n\n nextDocument = view.state.schema.nodeFromJSON(fallback);\n }\n\n if (nextDocument.childCount === 0) {\n const fallback = fallbackDocument?.();\n if (!isProseMirrorJsonNode(fallback)) {\n return;\n }\n\n nextDocument = view.state.schema.nodeFromJSON(fallback);\n }\n\n const tr = view.state.tr.replace(\n 0,\n view.state.doc.content.size,\n new Slice(nextDocument.content, 0, 0)\n );\n\n tr.setMeta(LIVEBLOCKS_COLLABORATION_PLUGIN_KEY, { isRemote: true }).setMeta(\n \"addToHistory\",\n false\n );\n\n view.dispatch(tr);\n}\n\nfunction getDocumentRoot(\n root: LiveObject<LsonObject>,\n field: string\n): LiveblocksProsemirrorNode | undefined {\n const documents = root.get(LIVEBLOCKS_TIPTAP_DOCUMENTS_KEY);\n if (!(documents instanceof LiveMap)) {\n return undefined;\n }\n\n const documentRoot = documents.get(field);\n if (!(documentRoot instanceof LiveObject)) {\n return undefined;\n }\n\n return documentRoot as LiveblocksProsemirrorNode;\n}\n\nfunction setDocumentRoot(\n root: LiveObject<LsonObject>,\n field: string,\n document: ProseMirrorJsonNode\n): void {\n let documents = root.get(LIVEBLOCKS_TIPTAP_DOCUMENTS_KEY);\n if (!(documents instanceof LiveMap)) {\n documents = new LiveMap<string, LiveblocksProsemirrorNode>();\n root.set(LIVEBLOCKS_TIPTAP_DOCUMENTS_KEY, documents);\n }\n\n documents.set(field, createLiveblocksProsemirrorNode(document));\n}\n\nexport function createLiveblocksCollaborationPlugin(\n options: LiveblocksCollaborationOptions\n): Plugin {\n const room = options.room;\n if (room === undefined) {\n throw new Error(\n \"[Liveblocks] The Liveblocks collaboration plugin requires a room.\"\n );\n }\n\n let view: EditorView | undefined;\n let root: LiveObject<LsonObject> | undefined;\n let unsubscribe: (() => void) | undefined;\n let destroyed = false;\n let isApplyingRemoteUpdate = false;\n let isApplyingLocalUpdate = false;\n let lastDocument = \"\";\n\n const applyStorageToEditor = (updates?: StorageUpdate[]) => {\n if (view === undefined || root === undefined) {\n return;\n }\n\n const documentRoot = getDocumentRoot(root, options.field);\n if (documentRoot === undefined) {\n return;\n }\n\n if (isApplyingLocalUpdate) {\n return;\n }\n\n const document = liveblocksProsemirrorNodeToJson(\n documentRoot,\n options.fallbackDocument\n );\n const serializedDocument = stringifyDocument(document);\n\n if (serializedDocument === lastDocument) {\n return;\n }\n\n if (updates !== undefined) {\n const result = applyRemoteStorageUpdates(view, documentRoot, updates);\n if (result.type === \"applied\") {\n lastDocument = serializedDocument;\n isApplyingRemoteUpdate = true;\n try {\n view.dispatch(\n result.tr\n .setMeta(LIVEBLOCKS_COLLABORATION_PLUGIN_KEY, { isRemote: true })\n .setMeta(\"addToHistory\", false)\n );\n } finally {\n isApplyingRemoteUpdate = false;\n }\n return;\n }\n }\n\n lastDocument = serializedDocument;\n isApplyingRemoteUpdate = true;\n try {\n replaceEditorDocument(view, document, options.fallbackDocument);\n } finally {\n isApplyingRemoteUpdate = false;\n }\n };\n\n return new Plugin({\n key: LIVEBLOCKS_COLLABORATION_PLUGIN_KEY,\n state: {\n init: () => ({ isReady: false }),\n apply(tr, state) {\n const meta = tr.getMeta(LIVEBLOCKS_COLLABORATION_PLUGIN_KEY) as\n | { isReady?: boolean }\n | undefined;\n\n return meta?.isReady !== undefined\n ? { ...state, isReady: meta.isReady }\n : state;\n },\n },\n appendTransaction(transactions, oldState, newState) {\n if (\n root === undefined ||\n isApplyingRemoteUpdate ||\n !transactions.some((transaction) => transaction.docChanged) ||\n transactions.some((transaction) =>\n Boolean(transaction.getMeta(LIVEBLOCKS_COLLABORATION_PLUGIN_KEY))\n )\n ) {\n return null;\n }\n\n const currentRoot = root;\n\n const documentRoot = getDocumentRoot(currentRoot, options.field);\n const classified =\n documentRoot !== undefined\n ? classifyTransaction(\n transactions,\n oldState.doc,\n newState.doc,\n documentRoot\n )\n : { type: \"unsupported\" as const };\n\n room.batch(() => {\n if (classified.type === \"incremental\") {\n isApplyingLocalUpdate = true;\n try {\n applyIncrementalOperations(classified.operations);\n } finally {\n isApplyingLocalUpdate = false;\n }\n } else {\n const document: unknown = newState.doc.toJSON();\n if (!isProseMirrorJsonNode(document)) {\n return;\n }\n\n const serializedDocument = stringifyDocument(document);\n if (serializedDocument === lastDocument) {\n return;\n }\n\n lastDocument = serializedDocument;\n setDocumentRoot(currentRoot, options.field, document);\n }\n });\n\n return null;\n },\n view(editorView) {\n view = editorView;\n\n room.getStorage().then(({ root: storageRoot }) => {\n if (destroyed) {\n return;\n }\n\n root = storageRoot;\n\n if (getDocumentRoot(storageRoot, options.field) === undefined) {\n const initialDocument = getInitialDocument(\n options.initialContent,\n options.fallbackDocument,\n editorView\n );\n room.history.disable(() => {\n setDocumentRoot(storageRoot, options.field, initialDocument);\n });\n }\n\n applyStorageToEditor();\n\n const tr = editorView.state.tr.setMeta(\n LIVEBLOCKS_COLLABORATION_PLUGIN_KEY,\n { isReady: true }\n );\n editorView.dispatch(tr);\n\n unsubscribe = room.subscribe(storageRoot, applyStorageToEditor, {\n isDeep: true,\n });\n });\n\n return {\n update(nextView) {\n view = nextView;\n },\n destroy() {\n destroyed = true;\n unsubscribe?.();\n unsubscribe = undefined;\n view = undefined;\n root = undefined;\n },\n };\n },\n });\n}\n"],"names":[],"mappings":"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"}
|