@korajs/react 0.3.2 → 0.5.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/index.cjs +296 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +88 -13
- package/dist/index.d.ts +88 -13
- package/dist/index.js +298 -64
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/dist/index.cjs
CHANGED
|
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
KoraProvider: () => KoraProvider,
|
|
34
|
+
useApp: () => useApp,
|
|
34
35
|
useCollection: () => useCollection,
|
|
35
36
|
useMutation: () => useMutation,
|
|
36
37
|
useQuery: () => useQuery,
|
|
@@ -49,12 +50,8 @@ function KoraProvider({
|
|
|
49
50
|
fallback,
|
|
50
51
|
children
|
|
51
52
|
}) {
|
|
52
|
-
const [resolvedStore, setResolvedStore] = (0, import_react.useState)(
|
|
53
|
-
|
|
54
|
-
);
|
|
55
|
-
const [resolvedSync, setResolvedSync] = (0, import_react.useState)(
|
|
56
|
-
syncEngine ?? null
|
|
57
|
-
);
|
|
53
|
+
const [resolvedStore, setResolvedStore] = (0, import_react.useState)(store ?? null);
|
|
54
|
+
const [resolvedSync, setResolvedSync] = (0, import_react.useState)(syncEngine ?? null);
|
|
58
55
|
const [ready, setReady] = (0, import_react.useState)(!app);
|
|
59
56
|
const [initError, setInitError] = (0, import_react.useState)(null);
|
|
60
57
|
(0, import_react.useEffect)(() => {
|
|
@@ -93,7 +90,10 @@ function KoraProvider({
|
|
|
93
90
|
}
|
|
94
91
|
const value = {
|
|
95
92
|
store: resolvedStore,
|
|
96
|
-
syncEngine: resolvedSync
|
|
93
|
+
syncEngine: resolvedSync,
|
|
94
|
+
app: app ?? null,
|
|
95
|
+
events: app?.events ?? null,
|
|
96
|
+
subscribeSyncStatus: app?.sync?.subscribeStatus ?? null
|
|
97
97
|
};
|
|
98
98
|
return (0, import_react.createElement)(KoraContext.Provider, { value }, children);
|
|
99
99
|
}
|
|
@@ -107,6 +107,17 @@ function useKoraContext() {
|
|
|
107
107
|
return context;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
+
// src/hooks/use-app.ts
|
|
111
|
+
function useApp() {
|
|
112
|
+
const { app } = useKoraContext();
|
|
113
|
+
if (!app) {
|
|
114
|
+
throw new Error(
|
|
115
|
+
'useApp() requires KoraProvider to be initialized with an "app" prop. Pass your createApp() result to <KoraProvider app={app}>.'
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
return app;
|
|
119
|
+
}
|
|
120
|
+
|
|
110
121
|
// src/hooks/use-query.ts
|
|
111
122
|
var import_react2 = require("react");
|
|
112
123
|
|
|
@@ -182,6 +193,18 @@ var QueryStore = class {
|
|
|
182
193
|
};
|
|
183
194
|
|
|
184
195
|
// src/hooks/use-query.ts
|
|
196
|
+
var APP_NOT_READY_CODE = "APP_NOT_READY";
|
|
197
|
+
function assertQueryReady(query) {
|
|
198
|
+
const descriptor = query.getDescriptor();
|
|
199
|
+
if (descriptor.collection === "__pending__") {
|
|
200
|
+
const err = new Error(
|
|
201
|
+
"Cannot use useQuery() before app.ready. Await app.ready or wrap your UI in <KoraProvider app={app}>."
|
|
202
|
+
);
|
|
203
|
+
err.name = "AppNotReadyError";
|
|
204
|
+
Object.assign(err, { code: APP_NOT_READY_CODE });
|
|
205
|
+
throw err;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
185
208
|
var EMPTY_ARRAY2 = Object.freeze([]);
|
|
186
209
|
var noopSubscribe = (_onStoreChange) => {
|
|
187
210
|
return () => {
|
|
@@ -189,6 +212,9 @@ var noopSubscribe = (_onStoreChange) => {
|
|
|
189
212
|
};
|
|
190
213
|
function useQuery(query, options) {
|
|
191
214
|
const enabled = options?.enabled !== false;
|
|
215
|
+
if (enabled) {
|
|
216
|
+
assertQueryReady(query);
|
|
217
|
+
}
|
|
192
218
|
const descriptorKey = JSON.stringify(query.getDescriptor());
|
|
193
219
|
const queryStoreRef = (0, import_react2.useRef)(null);
|
|
194
220
|
const prevKeyRef = (0, import_react2.useRef)(null);
|
|
@@ -221,7 +247,7 @@ function useQuery(query, options) {
|
|
|
221
247
|
|
|
222
248
|
// src/hooks/use-mutation.ts
|
|
223
249
|
var import_react3 = require("react");
|
|
224
|
-
function useMutation(mutationFn) {
|
|
250
|
+
function useMutation(mutationFn, options) {
|
|
225
251
|
const [state, setState] = (0, import_react3.useState)({
|
|
226
252
|
isLoading: false,
|
|
227
253
|
error: null
|
|
@@ -235,21 +261,35 @@ function useMutation(mutationFn) {
|
|
|
235
261
|
}, []);
|
|
236
262
|
const fnRef = (0, import_react3.useRef)(mutationFn);
|
|
237
263
|
fnRef.current = mutationFn;
|
|
264
|
+
const optionsRef = (0, import_react3.useRef)(options);
|
|
265
|
+
optionsRef.current = options;
|
|
238
266
|
const mutateAsync = (0, import_react3.useCallback)(async (...args) => {
|
|
267
|
+
const opts = optionsRef.current;
|
|
268
|
+
let context;
|
|
239
269
|
if (mountedRef.current) {
|
|
240
270
|
setState({ isLoading: true, error: null });
|
|
241
271
|
}
|
|
242
272
|
try {
|
|
273
|
+
if (opts?.onMutate) {
|
|
274
|
+
context = await opts.onMutate(...args);
|
|
275
|
+
}
|
|
243
276
|
const result = await fnRef.current(...args);
|
|
244
277
|
if (mountedRef.current) {
|
|
245
278
|
setState({ isLoading: false, error: null });
|
|
246
279
|
}
|
|
280
|
+
opts?.onSuccess?.(result, ...args);
|
|
281
|
+
opts?.onSettled?.(result, null, ...args);
|
|
247
282
|
return result;
|
|
248
283
|
} catch (err) {
|
|
249
284
|
const error = err instanceof Error ? err : new Error(String(err));
|
|
285
|
+
if (context !== void 0 && opts?.onRollback) {
|
|
286
|
+
await opts.onRollback(context, ...args);
|
|
287
|
+
}
|
|
250
288
|
if (mountedRef.current) {
|
|
251
289
|
setState({ isLoading: false, error });
|
|
252
290
|
}
|
|
291
|
+
opts?.onError?.(error, ...args);
|
|
292
|
+
opts?.onSettled?.(void 0, error, ...args);
|
|
253
293
|
throw error;
|
|
254
294
|
}
|
|
255
295
|
}, []);
|
|
@@ -276,21 +316,44 @@ function useMutation(mutationFn) {
|
|
|
276
316
|
|
|
277
317
|
// src/hooks/use-sync-status.ts
|
|
278
318
|
var import_react4 = require("react");
|
|
279
|
-
var POLL_INTERVAL_MS = 500;
|
|
280
319
|
var OFFLINE_STATUS = Object.freeze({
|
|
281
320
|
status: "offline",
|
|
282
321
|
pendingOperations: 0,
|
|
283
|
-
lastSyncedAt: null
|
|
322
|
+
lastSyncedAt: null,
|
|
323
|
+
lastSuccessfulPush: null,
|
|
324
|
+
lastSuccessfulPull: null,
|
|
325
|
+
conflicts: 0
|
|
284
326
|
});
|
|
327
|
+
var SYNC_STATUS_EVENT_TYPES = [
|
|
328
|
+
"sync:connected",
|
|
329
|
+
"sync:disconnected",
|
|
330
|
+
"sync:schema-mismatch",
|
|
331
|
+
"sync:auth-failed",
|
|
332
|
+
"sync:sent",
|
|
333
|
+
"sync:received",
|
|
334
|
+
"sync:acknowledged",
|
|
335
|
+
"sync:apply-failed",
|
|
336
|
+
"sync:diagnostics",
|
|
337
|
+
"sync:initial-sync-progress"
|
|
338
|
+
];
|
|
285
339
|
function useSyncStatus() {
|
|
286
|
-
const { syncEngine } = useKoraContext();
|
|
340
|
+
const { syncEngine, subscribeSyncStatus, events } = useKoraContext();
|
|
287
341
|
const snapshotRef = (0, import_react4.useRef)(OFFLINE_STATUS);
|
|
288
342
|
const serializedRef = (0, import_react4.useRef)(JSON.stringify(OFFLINE_STATUS));
|
|
289
343
|
const subscribe = (0, import_react4.useCallback)(
|
|
290
344
|
(onStoreChange) => {
|
|
291
|
-
if (
|
|
292
|
-
|
|
293
|
-
|
|
345
|
+
if (subscribeSyncStatus) {
|
|
346
|
+
return subscribeSyncStatus((status) => {
|
|
347
|
+
snapshotRef.current = status;
|
|
348
|
+
serializedRef.current = JSON.stringify(status);
|
|
349
|
+
onStoreChange();
|
|
350
|
+
});
|
|
351
|
+
}
|
|
352
|
+
if (!syncEngine) {
|
|
353
|
+
return () => {
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
const refresh = () => {
|
|
294
357
|
const newStatus = syncEngine.getStatus();
|
|
295
358
|
const newSerialized = JSON.stringify(newStatus);
|
|
296
359
|
if (newSerialized !== serializedRef.current) {
|
|
@@ -298,29 +361,28 @@ function useSyncStatus() {
|
|
|
298
361
|
serializedRef.current = newSerialized;
|
|
299
362
|
onStoreChange();
|
|
300
363
|
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
364
|
+
};
|
|
365
|
+
if (events) {
|
|
366
|
+
const unsubs = SYNC_STATUS_EVENT_TYPES.map((type) => events.on(type, refresh));
|
|
367
|
+
refresh();
|
|
368
|
+
return () => {
|
|
369
|
+
for (const unsub of unsubs) {
|
|
370
|
+
unsub();
|
|
371
|
+
}
|
|
372
|
+
};
|
|
308
373
|
}
|
|
374
|
+
refresh();
|
|
309
375
|
return () => {
|
|
310
|
-
clearInterval(intervalId);
|
|
311
376
|
};
|
|
312
377
|
},
|
|
313
|
-
[syncEngine]
|
|
378
|
+
[syncEngine, subscribeSyncStatus, events]
|
|
314
379
|
);
|
|
315
380
|
const getSnapshot = (0, import_react4.useCallback)(() => {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
(0, import_react4.useEffect)(() => {
|
|
319
|
-
if (!syncEngine) {
|
|
320
|
-
snapshotRef.current = OFFLINE_STATUS;
|
|
321
|
-
serializedRef.current = JSON.stringify(OFFLINE_STATUS);
|
|
381
|
+
if (subscribeSyncStatus) {
|
|
382
|
+
return snapshotRef.current;
|
|
322
383
|
}
|
|
323
|
-
|
|
384
|
+
return syncEngine ? syncEngine.getStatus() : OFFLINE_STATUS;
|
|
385
|
+
}, [syncEngine, subscribeSyncStatus]);
|
|
324
386
|
return (0, import_react4.useSyncExternalStore)(subscribe, getSnapshot);
|
|
325
387
|
}
|
|
326
388
|
|
|
@@ -334,21 +396,35 @@ function useCollection(name) {
|
|
|
334
396
|
}
|
|
335
397
|
|
|
336
398
|
// src/hooks/use-rich-text.ts
|
|
399
|
+
var import_store = require("@korajs/store");
|
|
337
400
|
var import_react6 = require("react");
|
|
338
401
|
var Y = __toESM(require("yjs"), 1);
|
|
339
402
|
var LOAD_ORIGIN = "kora-load";
|
|
403
|
+
var REMOTE_ORIGIN = "kora-remote";
|
|
404
|
+
var DOC_CHANNEL_ORIGIN = "kora-doc-channel";
|
|
340
405
|
var TEXT_KEY = "content";
|
|
341
406
|
var COMPACT_AFTER_DELTAS = 20;
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
const
|
|
407
|
+
var PERSIST_DEBOUNCE_MS = 400;
|
|
408
|
+
function useRichText(collectionName, recordId, fieldName, options) {
|
|
409
|
+
const { store, syncEngine } = useKoraContext();
|
|
410
|
+
const collection = (0, import_react6.useMemo)(
|
|
411
|
+
() => store.collection(collectionName),
|
|
412
|
+
[store, collectionName]
|
|
413
|
+
);
|
|
345
414
|
const [doc] = (0, import_react6.useState)(() => new Y.Doc());
|
|
346
415
|
const [ready, setReady] = (0, import_react6.useState)(false);
|
|
347
416
|
const [error, setError] = (0, import_react6.useState)(null);
|
|
348
417
|
const [canUndo, setCanUndo] = (0, import_react6.useState)(false);
|
|
349
418
|
const [canRedo, setCanRedo] = (0, import_react6.useState)(false);
|
|
419
|
+
const [cursors, setCursors] = (0, import_react6.useState)([]);
|
|
350
420
|
const baseUpdateRef = (0, import_react6.useRef)(null);
|
|
351
421
|
const pendingDeltasRef = (0, import_react6.useRef)([]);
|
|
422
|
+
const docChannelActiveRef = (0, import_react6.useRef)(false);
|
|
423
|
+
const persistTimerRef = (0, import_react6.useRef)(null);
|
|
424
|
+
const userRef = (0, import_react6.useRef)(options?.user);
|
|
425
|
+
(0, import_react6.useEffect)(() => {
|
|
426
|
+
userRef.current = options?.user;
|
|
427
|
+
}, [options?.user]);
|
|
352
428
|
const text = (0, import_react6.useMemo)(() => doc.getText(TEXT_KEY), [doc]);
|
|
353
429
|
const undoManager = (0, import_react6.useMemo)(() => new Y.UndoManager(text), [text]);
|
|
354
430
|
const syncHistoryState = (0, import_react6.useCallback)(() => {
|
|
@@ -363,6 +439,72 @@ function useRichText(collectionName, recordId, fieldName) {
|
|
|
363
439
|
undoManager.redo();
|
|
364
440
|
syncHistoryState();
|
|
365
441
|
}, [syncHistoryState, undoManager]);
|
|
442
|
+
const resolveAwarenessUser = (0, import_react6.useCallback)(() => {
|
|
443
|
+
if (userRef.current) {
|
|
444
|
+
return userRef.current;
|
|
445
|
+
}
|
|
446
|
+
if (syncEngine) {
|
|
447
|
+
const existing = syncEngine.getAwarenessManager().getLocalState()?.user;
|
|
448
|
+
if (existing) {
|
|
449
|
+
return existing;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
return {
|
|
453
|
+
name: "Anonymous",
|
|
454
|
+
color: "#6366f1"
|
|
455
|
+
};
|
|
456
|
+
}, [syncEngine]);
|
|
457
|
+
const setCursor = (0, import_react6.useCallback)(
|
|
458
|
+
(anchor, head) => {
|
|
459
|
+
if (!syncEngine) {
|
|
460
|
+
return;
|
|
461
|
+
}
|
|
462
|
+
const awareness = syncEngine.getAwarenessManager();
|
|
463
|
+
const state = {
|
|
464
|
+
user: resolveAwarenessUser(),
|
|
465
|
+
cursor: {
|
|
466
|
+
collection: collectionName,
|
|
467
|
+
recordId,
|
|
468
|
+
field: fieldName,
|
|
469
|
+
anchor,
|
|
470
|
+
head
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
awareness.setLocalState(state);
|
|
474
|
+
},
|
|
475
|
+
[collectionName, fieldName, recordId, resolveAwarenessUser, syncEngine]
|
|
476
|
+
);
|
|
477
|
+
const clearCursor = (0, import_react6.useCallback)(() => {
|
|
478
|
+
if (!syncEngine) {
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
const awareness = syncEngine.getAwarenessManager();
|
|
482
|
+
const current = awareness.getLocalState();
|
|
483
|
+
if (!current?.cursor) {
|
|
484
|
+
return;
|
|
485
|
+
}
|
|
486
|
+
if (current.cursor.collection !== collectionName || current.cursor.recordId !== recordId || current.cursor.field !== fieldName) {
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
awareness.setLocalState({ user: current.user });
|
|
490
|
+
}, [collectionName, fieldName, recordId, syncEngine]);
|
|
491
|
+
const applyRemoteSnapshot = (0, import_react6.useCallback)(
|
|
492
|
+
(value) => {
|
|
493
|
+
const encoded = encodeRichtextInput(value);
|
|
494
|
+
if (!encoded) {
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
const currentSnapshot = Y.encodeStateAsUpdate(doc);
|
|
498
|
+
if (updatesEqual(currentSnapshot, encoded)) {
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
Y.applyUpdate(doc, encoded, REMOTE_ORIGIN);
|
|
502
|
+
baseUpdateRef.current = encoded;
|
|
503
|
+
pendingDeltasRef.current = [];
|
|
504
|
+
syncHistoryState();
|
|
505
|
+
},
|
|
506
|
+
[doc, syncHistoryState]
|
|
507
|
+
);
|
|
366
508
|
(0, import_react6.useEffect)(() => {
|
|
367
509
|
let disposed = false;
|
|
368
510
|
const initialize = async () => {
|
|
@@ -381,18 +523,15 @@ function useRichText(collectionName, recordId, fieldName) {
|
|
|
381
523
|
if (encoded) {
|
|
382
524
|
Y.applyUpdate(doc, encoded, LOAD_ORIGIN);
|
|
383
525
|
}
|
|
526
|
+
const channel = syncEngine?.getRichtextDocChannel?.();
|
|
527
|
+
docChannelActiveRef.current = channel?.shouldUseChannel(encoded?.length ?? 0, options?.useDocChannel) ?? false;
|
|
384
528
|
setReady(true);
|
|
385
529
|
} catch (cause) {
|
|
386
530
|
if (disposed) return;
|
|
387
531
|
setError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
388
532
|
}
|
|
389
533
|
};
|
|
390
|
-
const
|
|
391
|
-
syncHistoryState();
|
|
392
|
-
if (origin === LOAD_ORIGIN) {
|
|
393
|
-
return;
|
|
394
|
-
}
|
|
395
|
-
pendingDeltasRef.current.push(_update);
|
|
534
|
+
const flushPersist = async () => {
|
|
396
535
|
const snapshot = composeRichtextSnapshot(baseUpdateRef.current, pendingDeltasRef.current);
|
|
397
536
|
if (pendingDeltasRef.current.length >= COMPACT_AFTER_DELTAS) {
|
|
398
537
|
baseUpdateRef.current = snapshot;
|
|
@@ -408,55 +547,151 @@ function useRichText(collectionName, recordId, fieldName) {
|
|
|
408
547
|
}
|
|
409
548
|
}
|
|
410
549
|
};
|
|
550
|
+
const schedulePersist = () => {
|
|
551
|
+
if (persistTimerRef.current) {
|
|
552
|
+
clearTimeout(persistTimerRef.current);
|
|
553
|
+
}
|
|
554
|
+
persistTimerRef.current = setTimeout(() => {
|
|
555
|
+
persistTimerRef.current = null;
|
|
556
|
+
void flushPersist();
|
|
557
|
+
}, PERSIST_DEBOUNCE_MS);
|
|
558
|
+
};
|
|
559
|
+
const persist = (_update, origin) => {
|
|
560
|
+
syncHistoryState();
|
|
561
|
+
if (origin === LOAD_ORIGIN || origin === REMOTE_ORIGIN || origin === DOC_CHANNEL_ORIGIN) {
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
pendingDeltasRef.current.push(_update);
|
|
565
|
+
if (docChannelActiveRef.current && syncEngine) {
|
|
566
|
+
syncEngine.getRichtextDocChannel().send(collectionName, recordId, fieldName, _update);
|
|
567
|
+
schedulePersist();
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
void flushPersist();
|
|
571
|
+
};
|
|
411
572
|
doc.on("update", persist);
|
|
412
573
|
void initialize();
|
|
413
574
|
syncHistoryState();
|
|
414
575
|
return () => {
|
|
415
576
|
disposed = true;
|
|
577
|
+
if (persistTimerRef.current) {
|
|
578
|
+
clearTimeout(persistTimerRef.current);
|
|
579
|
+
persistTimerRef.current = null;
|
|
580
|
+
}
|
|
416
581
|
doc.off("update", persist);
|
|
417
582
|
undoManager.destroy();
|
|
418
583
|
baseUpdateRef.current = null;
|
|
419
584
|
pendingDeltasRef.current = [];
|
|
585
|
+
docChannelActiveRef.current = false;
|
|
420
586
|
};
|
|
421
|
-
}, [
|
|
422
|
-
|
|
587
|
+
}, [
|
|
588
|
+
collection,
|
|
589
|
+
collectionName,
|
|
590
|
+
doc,
|
|
591
|
+
fieldName,
|
|
592
|
+
options?.useDocChannel,
|
|
593
|
+
recordId,
|
|
594
|
+
syncEngine,
|
|
595
|
+
syncHistoryState,
|
|
596
|
+
undoManager
|
|
597
|
+
]);
|
|
598
|
+
(0, import_react6.useEffect)(() => {
|
|
599
|
+
if (!ready || !syncEngine || !docChannelActiveRef.current) {
|
|
600
|
+
return;
|
|
601
|
+
}
|
|
602
|
+
const channel = syncEngine.getRichtextDocChannel();
|
|
603
|
+
return channel.subscribe(collectionName, recordId, fieldName, (update) => {
|
|
604
|
+
Y.applyUpdate(doc, update, DOC_CHANNEL_ORIGIN);
|
|
605
|
+
syncHistoryState();
|
|
606
|
+
});
|
|
607
|
+
}, [collectionName, doc, fieldName, ready, recordId, syncEngine, syncHistoryState]);
|
|
608
|
+
(0, import_react6.useEffect)(() => {
|
|
609
|
+
if (!ready) {
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
const unsubscribe = store.collection(collectionName).where({ id: recordId }).subscribe((results) => {
|
|
613
|
+
const record = results[0];
|
|
614
|
+
if (!record) {
|
|
615
|
+
return;
|
|
616
|
+
}
|
|
617
|
+
applyRemoteSnapshot(record[fieldName]);
|
|
618
|
+
});
|
|
619
|
+
return unsubscribe;
|
|
620
|
+
}, [applyRemoteSnapshot, collectionName, fieldName, ready, recordId, store]);
|
|
621
|
+
(0, import_react6.useEffect)(() => {
|
|
622
|
+
if (!syncEngine) return;
|
|
623
|
+
const awareness = syncEngine.getAwarenessManager();
|
|
624
|
+
const localClientId = awareness.clientId;
|
|
625
|
+
const updateCursors = () => {
|
|
626
|
+
const states = awareness.getStates();
|
|
627
|
+
const fieldCursors = [];
|
|
628
|
+
for (const [clientId, state] of states) {
|
|
629
|
+
if (clientId === localClientId) continue;
|
|
630
|
+
if (!state.cursor) continue;
|
|
631
|
+
if (state.cursor.collection !== collectionName || state.cursor.recordId !== recordId || state.cursor.field !== fieldName) {
|
|
632
|
+
continue;
|
|
633
|
+
}
|
|
634
|
+
fieldCursors.push({
|
|
635
|
+
clientId,
|
|
636
|
+
userName: state.user.name,
|
|
637
|
+
color: state.user.color,
|
|
638
|
+
anchor: state.cursor.anchor,
|
|
639
|
+
head: state.cursor.head
|
|
640
|
+
});
|
|
641
|
+
}
|
|
642
|
+
setCursors(fieldCursors);
|
|
643
|
+
};
|
|
644
|
+
const unsubscribe = awareness.on("change", () => {
|
|
645
|
+
updateCursors();
|
|
646
|
+
});
|
|
647
|
+
updateCursors();
|
|
648
|
+
return () => {
|
|
649
|
+
unsubscribe();
|
|
650
|
+
clearCursor();
|
|
651
|
+
};
|
|
652
|
+
}, [clearCursor, collectionName, fieldName, recordId, syncEngine]);
|
|
653
|
+
return { doc, text, undo, redo, canUndo, canRedo, ready, error, cursors, setCursor, clearCursor };
|
|
423
654
|
}
|
|
424
655
|
function encodeRichtextInput(value) {
|
|
425
656
|
if (value === null || value === void 0) {
|
|
426
657
|
return null;
|
|
427
658
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
}
|
|
436
|
-
if (value instanceof ArrayBuffer) {
|
|
437
|
-
return new Uint8Array(value);
|
|
438
|
-
}
|
|
439
|
-
if (typeof globalThis !== "undefined" && "Buffer" in globalThis) {
|
|
440
|
-
const BufferCtor = globalThis.Buffer;
|
|
441
|
-
if (BufferCtor.isBuffer(value)) {
|
|
442
|
-
return new Uint8Array(value.buffer, value.byteOffset, value.byteLength);
|
|
659
|
+
try {
|
|
660
|
+
return (0, import_store.encodeRichtext)(value);
|
|
661
|
+
} catch {
|
|
662
|
+
if (typeof value === "string") {
|
|
663
|
+
const fallbackDoc = new Y.Doc();
|
|
664
|
+
fallbackDoc.getText(TEXT_KEY).insert(0, value);
|
|
665
|
+
return Y.encodeStateAsUpdate(fallbackDoc);
|
|
443
666
|
}
|
|
667
|
+
throw new Error("Richtext record value must be a string, Uint8Array, ArrayBuffer, or null.");
|
|
444
668
|
}
|
|
445
|
-
throw new Error("Richtext record value must be a string, Uint8Array, ArrayBuffer, or null.");
|
|
446
669
|
}
|
|
447
670
|
function composeRichtextSnapshot(base, deltas) {
|
|
448
|
-
const
|
|
671
|
+
const mergedDoc = new Y.Doc();
|
|
449
672
|
if (base) {
|
|
450
|
-
Y.applyUpdate(
|
|
673
|
+
Y.applyUpdate(mergedDoc, base);
|
|
451
674
|
}
|
|
452
675
|
for (const delta of deltas) {
|
|
453
|
-
Y.applyUpdate(
|
|
676
|
+
Y.applyUpdate(mergedDoc, delta);
|
|
677
|
+
}
|
|
678
|
+
return Y.encodeStateAsUpdate(mergedDoc);
|
|
679
|
+
}
|
|
680
|
+
function updatesEqual(left, right) {
|
|
681
|
+
if (left.length !== right.length) {
|
|
682
|
+
return false;
|
|
683
|
+
}
|
|
684
|
+
for (let index = 0; index < left.length; index++) {
|
|
685
|
+
if (left[index] !== right[index]) {
|
|
686
|
+
return false;
|
|
687
|
+
}
|
|
454
688
|
}
|
|
455
|
-
return
|
|
689
|
+
return true;
|
|
456
690
|
}
|
|
457
691
|
// Annotate the CommonJS export names for ESM import in node:
|
|
458
692
|
0 && (module.exports = {
|
|
459
693
|
KoraProvider,
|
|
694
|
+
useApp,
|
|
460
695
|
useCollection,
|
|
461
696
|
useMutation,
|
|
462
697
|
useQuery,
|