@yorkie-js/sdk 0.6.36 → 0.6.38
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/quill.html +474 -287
- package/dist/yorkie-js-sdk.d.ts +5 -1
- package/dist/yorkie-js-sdk.es.js +53 -28
- package/dist/yorkie-js-sdk.es.js.map +1 -1
- package/dist/yorkie-js-sdk.js +53 -28
- package/dist/yorkie-js-sdk.js.map +1 -1
- package/package.json +2 -2
- package/dist/quill-two-clients.html +0 -504
- /package/dist/{quill-two-clients.css → quill.css} +0 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yorkie-js/sdk",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.38",
|
|
4
4
|
"description": "Yorkie JS SDK",
|
|
5
5
|
"main": "./dist/yorkie-js-sdk.js",
|
|
6
6
|
"publishConfig": {
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"@connectrpc/connect": "^1.4.0",
|
|
57
57
|
"@connectrpc/connect-web": "^1.4.0",
|
|
58
58
|
"long": "^5.2.0",
|
|
59
|
-
"@yorkie-js/schema": "0.6.
|
|
59
|
+
"@yorkie-js/schema": "0.6.38"
|
|
60
60
|
},
|
|
61
61
|
"scripts": {
|
|
62
62
|
"build": "tsc && vite build -c vite.build.ts",
|
|
@@ -1,504 +0,0 @@
|
|
|
1
|
-
<!DOCTYPE html>
|
|
2
|
-
<html lang="en">
|
|
3
|
-
<head>
|
|
4
|
-
<meta charset="UTF-8" />
|
|
5
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
-
<title>Yorkie + Quill Two Clients Example</title>
|
|
7
|
-
<link
|
|
8
|
-
href="https://cdn.quilljs.com/1.3.6/quill.snow.css"
|
|
9
|
-
rel="stylesheet"
|
|
10
|
-
/>
|
|
11
|
-
<link rel="stylesheet" href="style.css" />
|
|
12
|
-
<link rel="stylesheet" href="quill-two-clients.css" />
|
|
13
|
-
<script src="https://cdn.quilljs.com/1.3.6/quill.js"></script>
|
|
14
|
-
<script src="https://cdn.jsdelivr.net/npm/quill-cursors@3.1.0/dist/quill-cursors.min.js"></script>
|
|
15
|
-
<script src="https://cdn.jsdelivr.net/npm/color-hash@1.0.3/dist/color-hash.js"></script>
|
|
16
|
-
</head>
|
|
17
|
-
<body>
|
|
18
|
-
<div class="client-container">
|
|
19
|
-
<div id="client-a">
|
|
20
|
-
Client A ( id:<span class="client-id"></span>)
|
|
21
|
-
<span class="network-status"></span>
|
|
22
|
-
<div class="syncmode-option">
|
|
23
|
-
<span>SyncMode: </span>
|
|
24
|
-
<div class="realtime-sync">
|
|
25
|
-
<span class="realtime-sync-title">Realtime Sync</span>
|
|
26
|
-
<div class="option">
|
|
27
|
-
<input
|
|
28
|
-
type="radio"
|
|
29
|
-
id="realtime-pushpull-a"
|
|
30
|
-
name="syncMode-a"
|
|
31
|
-
value="pushpull"
|
|
32
|
-
checked
|
|
33
|
-
/>
|
|
34
|
-
<label for="realtime-pushpull-a">PushPull</label>
|
|
35
|
-
</div>
|
|
36
|
-
<div class="option">
|
|
37
|
-
<input
|
|
38
|
-
type="radio"
|
|
39
|
-
id="realtime-pushonly-a"
|
|
40
|
-
name="syncMode-a"
|
|
41
|
-
value="pushonly"
|
|
42
|
-
/>
|
|
43
|
-
<label for="realtime-pushonly-a">PushOnly</label>
|
|
44
|
-
</div>
|
|
45
|
-
<div class="option">
|
|
46
|
-
<input
|
|
47
|
-
type="radio"
|
|
48
|
-
id="realtime-syncoff-a"
|
|
49
|
-
name="syncMode-a"
|
|
50
|
-
value="syncoff"
|
|
51
|
-
/>
|
|
52
|
-
<label for="realtime-syncoff-a">SyncOff</label>
|
|
53
|
-
</div>
|
|
54
|
-
</div>
|
|
55
|
-
<div class="option">
|
|
56
|
-
<input
|
|
57
|
-
type="radio"
|
|
58
|
-
id="manual-a"
|
|
59
|
-
name="syncMode-a"
|
|
60
|
-
value="manual"
|
|
61
|
-
/>
|
|
62
|
-
<label for="manual-a">Manual Sync</label>
|
|
63
|
-
<button class="manual-sync">sync</button>
|
|
64
|
-
</div>
|
|
65
|
-
</div>
|
|
66
|
-
<div class="editor"></div>
|
|
67
|
-
<div class="online-clients"></div>
|
|
68
|
-
</div>
|
|
69
|
-
<div id="client-b">
|
|
70
|
-
Client B ( id:<span class="client-id"></span>)
|
|
71
|
-
<span class="network-status"></span>
|
|
72
|
-
<div class="syncmode-option">
|
|
73
|
-
<span>SyncMode: </span>
|
|
74
|
-
<div class="realtime-sync">
|
|
75
|
-
<span class="realtime-sync-title">Realtime Sync</span>
|
|
76
|
-
<div class="option">
|
|
77
|
-
<input
|
|
78
|
-
type="radio"
|
|
79
|
-
id="realtime-pushpull-b"
|
|
80
|
-
name="syncMode-b"
|
|
81
|
-
value="pushpull"
|
|
82
|
-
checked
|
|
83
|
-
/>
|
|
84
|
-
<label for="realtime-pushpull-b">PushPull</label>
|
|
85
|
-
</div>
|
|
86
|
-
<div class="option">
|
|
87
|
-
<input
|
|
88
|
-
type="radio"
|
|
89
|
-
id="realtime-pushonly-b"
|
|
90
|
-
name="syncMode-b"
|
|
91
|
-
value="pushonly"
|
|
92
|
-
/>
|
|
93
|
-
<label for="realtime-pushonly-b">PushOnly</label>
|
|
94
|
-
</div>
|
|
95
|
-
<div class="option">
|
|
96
|
-
<input
|
|
97
|
-
type="radio"
|
|
98
|
-
id="realtime-syncoff-b"
|
|
99
|
-
name="syncMode-b"
|
|
100
|
-
value="syncoff"
|
|
101
|
-
/>
|
|
102
|
-
<label for="realtime-syncoff-b">SyncOff</label>
|
|
103
|
-
</div>
|
|
104
|
-
</div>
|
|
105
|
-
<div class="option">
|
|
106
|
-
<input
|
|
107
|
-
type="radio"
|
|
108
|
-
id="manual-b"
|
|
109
|
-
name="syncMode-b"
|
|
110
|
-
value="manual"
|
|
111
|
-
/>
|
|
112
|
-
<label for="manual-b">Manual Sync</label>
|
|
113
|
-
<button class="manual-sync">sync</button>
|
|
114
|
-
</div>
|
|
115
|
-
</div>
|
|
116
|
-
<div class="editor"></div>
|
|
117
|
-
<div class="online-clients"></div>
|
|
118
|
-
</div>
|
|
119
|
-
</div>
|
|
120
|
-
<script type="module">
|
|
121
|
-
import './src/yorkie.ts';
|
|
122
|
-
import Network from './devtool/network.js';
|
|
123
|
-
|
|
124
|
-
const colorHash = new ColorHash();
|
|
125
|
-
const clientAElem = document.getElementById('client-a');
|
|
126
|
-
const clientBElem = document.getElementById('client-b');
|
|
127
|
-
const documentKey = 'quill-two-clients';
|
|
128
|
-
|
|
129
|
-
function toDeltaOperation(textValue) {
|
|
130
|
-
const { embed, ...restAttributes } = textValue.attributes ?? {};
|
|
131
|
-
|
|
132
|
-
if (embed) {
|
|
133
|
-
return { insert: embed, attributes: restAttributes };
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return {
|
|
137
|
-
insert: textValue.content || '',
|
|
138
|
-
attributes: textValue.attributes,
|
|
139
|
-
};
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
async function main() {
|
|
143
|
-
try {
|
|
144
|
-
async function initializeRealtimeEditor(clientElem) {
|
|
145
|
-
// 01. create client with RPCAddr(envoy) then activate it.
|
|
146
|
-
const client = new yorkie.Client({
|
|
147
|
-
rpcAddr: 'http://localhost:8080',
|
|
148
|
-
});
|
|
149
|
-
await client.activate();
|
|
150
|
-
const clientID = client.getID().slice(-2);
|
|
151
|
-
clientElem.querySelector('.client-id').textContent = clientID;
|
|
152
|
-
|
|
153
|
-
// 02. create a document then attach it into the client.
|
|
154
|
-
const doc = new yorkie.Document(documentKey, {
|
|
155
|
-
enableDevtools: true,
|
|
156
|
-
});
|
|
157
|
-
doc.subscribe(
|
|
158
|
-
'connection',
|
|
159
|
-
new Network(clientElem.querySelector('.network-status'))
|
|
160
|
-
.statusListener,
|
|
161
|
-
);
|
|
162
|
-
const onlineClients = clientElem.querySelector('.online-clients');
|
|
163
|
-
doc.subscribe('presence', (event) => {
|
|
164
|
-
// Update online clients list
|
|
165
|
-
if (event.type !== 'presence-changed') {
|
|
166
|
-
const clientIDs = doc
|
|
167
|
-
.getPresences()
|
|
168
|
-
.map(({ clientID }) => clientID)
|
|
169
|
-
.join(', ');
|
|
170
|
-
onlineClients.textContent = clientIDs;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
console.warn(
|
|
174
|
-
`%c${clientID}`,
|
|
175
|
-
`color:white; padding: 2px 4px; border-radius: 3px;
|
|
176
|
-
background: ${colorHash.hex(clientID)}; `,
|
|
177
|
-
event.type,
|
|
178
|
-
);
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
await client.attach(doc, {
|
|
182
|
-
initialPresence: { name: clientID },
|
|
183
|
-
});
|
|
184
|
-
|
|
185
|
-
doc.update((root) => {
|
|
186
|
-
if (!root.content) {
|
|
187
|
-
root.content = new yorkie.Text();
|
|
188
|
-
root.content.edit(0, 0, '\n');
|
|
189
|
-
}
|
|
190
|
-
}, 'create content if not exists');
|
|
191
|
-
|
|
192
|
-
// 02-2. subscribe document event.
|
|
193
|
-
doc.subscribe((event) => {
|
|
194
|
-
if (event.type === 'snapshot') {
|
|
195
|
-
// The text is replaced to snapshot and must be re-synced.
|
|
196
|
-
syncText(doc, quill);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
console.warn(
|
|
200
|
-
`%c${clientID}`,
|
|
201
|
-
`color:white; padding: 2px 4px; border-radius: 3px;
|
|
202
|
-
background: ${colorHash.hex(clientID)}; `,
|
|
203
|
-
event.type,
|
|
204
|
-
);
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
doc.subscribe('$.content', (event) => {
|
|
208
|
-
if (event.type === 'remote-change') {
|
|
209
|
-
const { message, operations } = event.value;
|
|
210
|
-
handleOperations(quill, operations);
|
|
211
|
-
}
|
|
212
|
-
updateAllCursors();
|
|
213
|
-
});
|
|
214
|
-
|
|
215
|
-
doc.subscribe('presence', (event) => {
|
|
216
|
-
// Update cursors
|
|
217
|
-
if (event.type === 'initialized') {
|
|
218
|
-
updateAllCursors();
|
|
219
|
-
} else if (event.type === 'unwatched') {
|
|
220
|
-
cursors.removeCursor(event.value.clientID);
|
|
221
|
-
} else {
|
|
222
|
-
updateCursor(event.value);
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
|
|
226
|
-
// 03. create an instance of Quill
|
|
227
|
-
const editorElem = clientElem?.getElementsByClassName('editor')[0];
|
|
228
|
-
Quill.register('modules/cursors', QuillCursors);
|
|
229
|
-
const quill = new Quill(editorElem, {
|
|
230
|
-
modules: {
|
|
231
|
-
toolbar: [
|
|
232
|
-
['bold', 'italic', 'underline', 'strike', 'link'],
|
|
233
|
-
['blockquote', 'code-block'],
|
|
234
|
-
[{ header: 1 }, { header: 2 }],
|
|
235
|
-
[{ list: 'ordered' }, { list: 'bullet' }],
|
|
236
|
-
[{ script: 'sub' }, { script: 'super' }],
|
|
237
|
-
[{ indent: '-1' }, { indent: '+1' }],
|
|
238
|
-
[{ direction: 'rtl' }],
|
|
239
|
-
[{ size: ['small', false, 'large', 'huge'] }],
|
|
240
|
-
[{ header: [1, 2, 3, 4, 5, 6, false] }],
|
|
241
|
-
[{ color: [] }, { background: [] }],
|
|
242
|
-
[{ font: [] }],
|
|
243
|
-
[{ align: [] }],
|
|
244
|
-
['image', 'video'],
|
|
245
|
-
['clean'],
|
|
246
|
-
],
|
|
247
|
-
cursors: {
|
|
248
|
-
hideDelayMs: Number.MAX_VALUE,
|
|
249
|
-
},
|
|
250
|
-
},
|
|
251
|
-
theme: 'snow',
|
|
252
|
-
});
|
|
253
|
-
const cursors = quill.getModule('cursors');
|
|
254
|
-
|
|
255
|
-
function updateCursor(user) {
|
|
256
|
-
const { clientID, presence } = user;
|
|
257
|
-
// TODO(chacha912): After resolving the presence initialization issue(#608),
|
|
258
|
-
// remove the following check.
|
|
259
|
-
if (!presence) return;
|
|
260
|
-
|
|
261
|
-
const { name, selection } = presence;
|
|
262
|
-
if (!selection) return;
|
|
263
|
-
const range = doc
|
|
264
|
-
.getRoot()
|
|
265
|
-
.content.posRangeToIndexRange(selection);
|
|
266
|
-
cursors.createCursor(clientID, name, colorHash.hex(name));
|
|
267
|
-
cursors.moveCursor(clientID, {
|
|
268
|
-
index: range[0],
|
|
269
|
-
length: range[1] - range[0],
|
|
270
|
-
});
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
function updateAllCursors() {
|
|
274
|
-
cursors.clearCursors();
|
|
275
|
-
for (const user of doc.getPresences()) {
|
|
276
|
-
updateCursor(user);
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// 04. bind the document with the Quill.
|
|
281
|
-
// 04-1. Quill to Document.
|
|
282
|
-
quill
|
|
283
|
-
.on('text-change', (delta, _, source) => {
|
|
284
|
-
if (source === 'api' || !delta.ops) {
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
let from = 0,
|
|
289
|
-
to = 0;
|
|
290
|
-
console.log(
|
|
291
|
-
`%c quill: ${JSON.stringify(delta.ops)}`,
|
|
292
|
-
'color: green',
|
|
293
|
-
);
|
|
294
|
-
for (const op of delta.ops) {
|
|
295
|
-
if (op.attributes !== undefined || op.insert !== undefined) {
|
|
296
|
-
if (op.retain !== undefined) {
|
|
297
|
-
to = from + op.retain;
|
|
298
|
-
}
|
|
299
|
-
console.log(
|
|
300
|
-
`%c local: ${from}-${to}: ${op.insert} ${
|
|
301
|
-
op.attributes ? JSON.stringify(op.attributes) : '{}'
|
|
302
|
-
}`,
|
|
303
|
-
'color: green',
|
|
304
|
-
);
|
|
305
|
-
|
|
306
|
-
doc.update((root, presence) => {
|
|
307
|
-
let range;
|
|
308
|
-
if (
|
|
309
|
-
op.attributes !== undefined &&
|
|
310
|
-
op.insert === undefined
|
|
311
|
-
) {
|
|
312
|
-
root.content.setStyle(from, to, op.attributes);
|
|
313
|
-
} else if (op.insert !== undefined) {
|
|
314
|
-
if (to < from) {
|
|
315
|
-
to = from;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
if (typeof op.insert === 'object') {
|
|
319
|
-
range = root.content.edit(from, to, ' ', {
|
|
320
|
-
embed: op.insert,
|
|
321
|
-
...op.attributes,
|
|
322
|
-
});
|
|
323
|
-
} else {
|
|
324
|
-
range = root.content.edit(
|
|
325
|
-
from,
|
|
326
|
-
to,
|
|
327
|
-
op.insert,
|
|
328
|
-
op.attributes,
|
|
329
|
-
);
|
|
330
|
-
}
|
|
331
|
-
from = to + op.insert.length;
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
if (range) {
|
|
335
|
-
presence.set({
|
|
336
|
-
selection: root.content.indexRangeToPosRange(range),
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
}, `update style by ${client.getID()}`);
|
|
340
|
-
} else if (op.delete !== undefined) {
|
|
341
|
-
to = from + op.delete;
|
|
342
|
-
console.log(`%c local: ${from}-${to}: ''`, 'color: green');
|
|
343
|
-
|
|
344
|
-
doc.update((root, presence) => {
|
|
345
|
-
const range = root.content.edit(from, to, '');
|
|
346
|
-
if (range) {
|
|
347
|
-
presence.set({
|
|
348
|
-
selection: root.content.indexRangeToPosRange(range),
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
}, `update content by ${client.getID()}`);
|
|
352
|
-
} else if (op.retain !== undefined) {
|
|
353
|
-
from = to + op.retain;
|
|
354
|
-
to = from;
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
})
|
|
358
|
-
.on('selection-change', (range, _, source) => {
|
|
359
|
-
if (!range) {
|
|
360
|
-
return;
|
|
361
|
-
}
|
|
362
|
-
// NOTE(chacha912): If the selection in the Quill editor does not match the range computed by yorkie,
|
|
363
|
-
// additional updates are necessary. This condition addresses situations where Quill's selection behaves
|
|
364
|
-
// differently, such as when inserting text before a range selection made by another user, causing
|
|
365
|
-
// the second character onwards to be included in the selection.
|
|
366
|
-
if (source === 'api') {
|
|
367
|
-
const [from, to] = doc
|
|
368
|
-
.getRoot()
|
|
369
|
-
.content.posRangeToIndexRange(
|
|
370
|
-
doc.getMyPresence().selection,
|
|
371
|
-
);
|
|
372
|
-
const { index, length } = range;
|
|
373
|
-
if (from === index && to === index + length) {
|
|
374
|
-
return;
|
|
375
|
-
}
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
doc.update((root, presence) => {
|
|
379
|
-
presence.set({
|
|
380
|
-
selection: root.content.indexRangeToPosRange([
|
|
381
|
-
range.index,
|
|
382
|
-
range.index + range.length,
|
|
383
|
-
]),
|
|
384
|
-
});
|
|
385
|
-
}, `update selection by ${client.getID()}`);
|
|
386
|
-
});
|
|
387
|
-
|
|
388
|
-
// 04-2. document to Quill(remote).
|
|
389
|
-
function handleOperations(quill, ops) {
|
|
390
|
-
const deltaOperations = [];
|
|
391
|
-
let prevTo = 0;
|
|
392
|
-
for (const op of ops) {
|
|
393
|
-
const from = op.from;
|
|
394
|
-
const to = op.to;
|
|
395
|
-
const retainFrom = from - prevTo;
|
|
396
|
-
const retainTo = to - from;
|
|
397
|
-
|
|
398
|
-
if (op.type === 'edit') {
|
|
399
|
-
const { insert, attributes } = toDeltaOperation(op.value);
|
|
400
|
-
console.log(
|
|
401
|
-
`%c remote: ${from}-${to}: ${insert}`,
|
|
402
|
-
'color: skyblue',
|
|
403
|
-
);
|
|
404
|
-
|
|
405
|
-
if (retainFrom) {
|
|
406
|
-
deltaOperations.push({ retain: retainFrom });
|
|
407
|
-
}
|
|
408
|
-
if (retainTo) {
|
|
409
|
-
deltaOperations.push({ delete: retainTo });
|
|
410
|
-
}
|
|
411
|
-
if (insert) {
|
|
412
|
-
const deltaOp = { insert };
|
|
413
|
-
if (attributes) {
|
|
414
|
-
deltaOp.attributes = attributes;
|
|
415
|
-
}
|
|
416
|
-
deltaOperations.push(deltaOp);
|
|
417
|
-
}
|
|
418
|
-
} else if (op.type === 'style') {
|
|
419
|
-
const { attributes } = toDeltaOperation(op.value);
|
|
420
|
-
console.log(
|
|
421
|
-
`%c remote: ${from}-${to}: ${JSON.stringify(attributes)}`,
|
|
422
|
-
'color: skyblue',
|
|
423
|
-
);
|
|
424
|
-
|
|
425
|
-
if (retainFrom) {
|
|
426
|
-
deltaOperations.push({ retain: retainFrom });
|
|
427
|
-
}
|
|
428
|
-
if (attributes) {
|
|
429
|
-
const deltaOp = { attributes };
|
|
430
|
-
if (retainTo) {
|
|
431
|
-
deltaOp.retain = retainTo;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
deltaOperations.push(deltaOp);
|
|
435
|
-
}
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
prevTo = to;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
if (deltaOperations.length) {
|
|
442
|
-
console.log(
|
|
443
|
-
`%c to quill: ${JSON.stringify(deltaOperations)}`,
|
|
444
|
-
'color: green',
|
|
445
|
-
);
|
|
446
|
-
quill.updateContents({ ops: deltaOperations }, 'api');
|
|
447
|
-
}
|
|
448
|
-
}
|
|
449
|
-
|
|
450
|
-
// 05. synchronize text of document and Quill.
|
|
451
|
-
function syncText(doc, quill) {
|
|
452
|
-
const text = doc.getRoot().content;
|
|
453
|
-
const delta = {
|
|
454
|
-
ops: text.values().map((val) => toDeltaOperation(val)),
|
|
455
|
-
};
|
|
456
|
-
quill.setContents(delta, 'api');
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// 06. sync option
|
|
460
|
-
const option = clientElem.querySelector('.syncmode-option');
|
|
461
|
-
option.addEventListener('change', async (e) => {
|
|
462
|
-
if (!event.target.matches('input[type="radio"]')) {
|
|
463
|
-
return;
|
|
464
|
-
}
|
|
465
|
-
const syncMode = event.target.value;
|
|
466
|
-
switch (syncMode) {
|
|
467
|
-
case 'pushpull':
|
|
468
|
-
await client.changeSyncMode(doc, 'realtime');
|
|
469
|
-
break;
|
|
470
|
-
case 'pushonly':
|
|
471
|
-
await client.changeSyncMode(doc, 'realtime-pushonly');
|
|
472
|
-
break;
|
|
473
|
-
case 'syncoff':
|
|
474
|
-
await client.changeSyncMode(doc, 'realtime-syncoff');
|
|
475
|
-
break;
|
|
476
|
-
case 'manual':
|
|
477
|
-
await client.changeSyncMode(doc, 'manual');
|
|
478
|
-
break;
|
|
479
|
-
default:
|
|
480
|
-
break;
|
|
481
|
-
}
|
|
482
|
-
});
|
|
483
|
-
const syncButton = clientElem.querySelector('.manual-sync');
|
|
484
|
-
syncButton.addEventListener('click', async () => {
|
|
485
|
-
await client.sync(doc);
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
syncText(doc, quill);
|
|
489
|
-
updateAllCursors();
|
|
490
|
-
|
|
491
|
-
return { client, doc, quill };
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
await initializeRealtimeEditor(clientAElem);
|
|
495
|
-
await initializeRealtimeEditor(clientBElem);
|
|
496
|
-
} catch (e) {
|
|
497
|
-
console.error(e);
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
main();
|
|
502
|
-
</script>
|
|
503
|
-
</body>
|
|
504
|
-
</html>
|
|
File without changes
|