@haklex/rich-collab-loro 0.62.2 → 0.63.1
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/binding.d.ts +2 -0
- package/dist/binding.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +24 -5
- package/dist/stream.d.ts +2 -0
- package/dist/stream.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/binding.ts +35 -5
- package/src/index.ts +1 -0
- package/src/stream.ts +2 -0
package/dist/binding.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { LexicalEditor, NodeKey } from 'lexical';
|
|
2
2
|
import { LoroDoc, TreeID } from 'loro-crdt';
|
|
3
3
|
export declare const COLLAB_TAG = "collab";
|
|
4
|
+
export declare const AGENT_WRITE_TAG = "agent-write";
|
|
4
5
|
export declare const TREE_NAME = "lexical";
|
|
5
6
|
export interface LoroBinding {
|
|
6
7
|
dispose: () => void;
|
|
@@ -9,6 +10,7 @@ export interface LoroBinding {
|
|
|
9
10
|
keyOf: (id: TreeID) => NodeKey | undefined;
|
|
10
11
|
}
|
|
11
12
|
export interface LoroBindingOptions {
|
|
13
|
+
agentLiveWindowSeconds?: number;
|
|
12
14
|
commitMessage?: string;
|
|
13
15
|
}
|
|
14
16
|
export declare function createLoroBinding(editor: LexicalEditor, doc: LoroDoc, options?: LoroBindingOptions): LoroBinding;
|
package/dist/binding.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,aAAa,EAElB,KAAK,OAAO,EAGb,MAAM,SAAS,CAAC;AACjB,OAAO,
|
|
1
|
+
{"version":3,"file":"binding.d.ts","sourceRoot":"","sources":["../src/binding.ts"],"names":[],"mappings":"AAAA,OAAO,EASL,KAAK,aAAa,EAElB,KAAK,OAAO,EAGb,MAAM,SAAS,CAAC;AACjB,OAAO,EAEL,KAAK,OAAO,EAGZ,KAAK,MAAM,EACZ,MAAM,WAAW,CAAC;AAMnB,eAAO,MAAM,UAAU,WAAW,CAAC;AACnC,eAAO,MAAM,eAAe,gBAAgB,CAAC;AAG7C,eAAO,MAAM,SAAS,YAAY,CAAC;AAGnC,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,IAAI,CAAC;IACpB,IAAI,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC;IAC3C,MAAM,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACpC,KAAK,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,GAAG,SAAS,CAAC;CAC5C;AAED,MAAM,WAAW,kBAAkB;IACjC,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAmBD,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,aAAa,EACrB,GAAG,EAAE,OAAO,EACZ,OAAO,GAAE,kBAAuB,GAC/B,WAAW,CAiOb"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { COLLAB_TAG, createLoroBinding, type LoroBinding, type LoroBindingOptions, TREE_NAME, } from './binding';
|
|
1
|
+
export { AGENT_WRITE_TAG, COLLAB_TAG, createLoroBinding, type LoroBinding, type LoroBindingOptions, TREE_NAME, } from './binding';
|
|
2
2
|
export { $reconcileRoot, editAtVersion } from './reconcile';
|
|
3
3
|
export { type Frame, type FrameFocus, planFrames, streamAtVersion, type StreamStep, } from './stream';
|
|
4
4
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,SAAS,GACV,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EACL,KAAK,KAAK,EACV,KAAK,UAAU,EACf,UAAU,EACV,eAAe,EACf,KAAK,UAAU,GAChB,MAAM,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,eAAe,EACf,UAAU,EACV,iBAAiB,EACjB,KAAK,WAAW,EAChB,KAAK,kBAAkB,EACvB,SAAS,GACV,MAAM,WAAW,CAAC;AACnB,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EACL,KAAK,KAAK,EACV,KAAK,UAAU,EACf,UAAU,EACV,eAAe,EACf,KAAK,UAAU,GAChB,MAAM,UAAU,CAAC"}
|
package/dist/index.mjs
CHANGED
|
@@ -140,6 +140,9 @@ function editAtVersion(doc, frontiers, createEditor, target, commitMessage) {
|
|
|
140
140
|
//#endregion
|
|
141
141
|
//#region src/binding.ts
|
|
142
142
|
var COLLAB_TAG = "collab";
|
|
143
|
+
var AGENT_WRITE_TAG = "agent-write";
|
|
144
|
+
var AGENT_MESSAGE_PREFIX = "agent:";
|
|
145
|
+
var AGENT_LIVE_WINDOW_SECONDS = 15;
|
|
143
146
|
var TREE_NAME = "lexical";
|
|
144
147
|
var TEXT_KEY = "#text";
|
|
145
148
|
function sameValue(a, b) {
|
|
@@ -306,7 +309,7 @@ function createLoroBinding(editor, doc, options = {}) {
|
|
|
306
309
|
if ($isElementNode(child)) $collectDescendants(child, out);
|
|
307
310
|
}
|
|
308
311
|
};
|
|
309
|
-
const pullFromDoc = () => {
|
|
312
|
+
const pullFromDoc = (agent = false) => {
|
|
310
313
|
editor.update(() => {
|
|
311
314
|
const rootTreeNode = tree.roots()[0];
|
|
312
315
|
if (!rootTreeNode) return;
|
|
@@ -321,7 +324,7 @@ function createLoroBinding(editor, doc, options = {}) {
|
|
|
321
324
|
for (const node of all) if (!visited.has(node.getKey()) && node.isAttached()) node.remove();
|
|
322
325
|
}, {
|
|
323
326
|
discrete: true,
|
|
324
|
-
tag: COLLAB_TAG
|
|
327
|
+
tag: agent ? [COLLAB_TAG, AGENT_WRITE_TAG] : COLLAB_TAG
|
|
325
328
|
});
|
|
326
329
|
};
|
|
327
330
|
if (tree.roots().length > 0) pullFromDoc();
|
|
@@ -334,14 +337,28 @@ function createLoroBinding(editor, doc, options = {}) {
|
|
|
334
337
|
idOf: (key) => keyToId.get(key),
|
|
335
338
|
keyOf: (id) => idToKey.get(id),
|
|
336
339
|
import: (bytes) => {
|
|
337
|
-
doc.import(bytes);
|
|
338
|
-
pullFromDoc();
|
|
340
|
+
pullFromDoc(hasLiveAgentChange(doc, doc.import(bytes), options.agentLiveWindowSeconds ?? AGENT_LIVE_WINDOW_SECONDS));
|
|
339
341
|
}
|
|
340
342
|
};
|
|
341
343
|
}
|
|
342
344
|
function $isTextLike(props) {
|
|
343
345
|
return typeof props.format === "number" && typeof props.detail === "number" && "mode" in props;
|
|
344
346
|
}
|
|
347
|
+
function hasLiveAgentChange(doc, status, windowSeconds) {
|
|
348
|
+
const oldest = Date.now() / 1e3 - windowSeconds;
|
|
349
|
+
for (const [peer, span] of status.success) {
|
|
350
|
+
let counter = span.start;
|
|
351
|
+
while (counter < span.end) {
|
|
352
|
+
const change = doc.getChangeAt({
|
|
353
|
+
peer,
|
|
354
|
+
counter
|
|
355
|
+
});
|
|
356
|
+
if (change.message?.startsWith(AGENT_MESSAGE_PREFIX) && change.timestamp >= oldest) return true;
|
|
357
|
+
counter = change.counter + change.length;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
345
362
|
//#endregion
|
|
346
363
|
//#region src/stream.ts
|
|
347
364
|
var TYPING_FRAME_BUDGET = 120;
|
|
@@ -390,6 +407,7 @@ function pairGap(dels, adds) {
|
|
|
390
407
|
};
|
|
391
408
|
});
|
|
392
409
|
}
|
|
410
|
+
/** @deprecated Agent writes are now played client-side by `@haklex/rich-plugin-agent-stream`; write once with `editAtVersion`. */
|
|
393
411
|
function planFrames(base, target) {
|
|
394
412
|
const before = base.children ?? [];
|
|
395
413
|
const after = target.children ?? [];
|
|
@@ -482,6 +500,7 @@ function $locate(focus) {
|
|
|
482
500
|
offset: 0
|
|
483
501
|
};
|
|
484
502
|
}
|
|
503
|
+
/** @deprecated Agent writes are now played client-side by `@haklex/rich-plugin-agent-stream`; write once with `editAtVersion`. */
|
|
485
504
|
function streamAtVersion(doc, frontiers, createEditor, base, target, commitMessage) {
|
|
486
505
|
const fork = doc.forkAt(frontiers);
|
|
487
506
|
const editor = createEditor();
|
|
@@ -512,4 +531,4 @@ function streamAtVersion(doc, frontiers, createEditor, base, target, commitMessa
|
|
|
512
531
|
};
|
|
513
532
|
}
|
|
514
533
|
//#endregion
|
|
515
|
-
export { $reconcileRoot, COLLAB_TAG, TREE_NAME, createLoroBinding, editAtVersion, planFrames, streamAtVersion };
|
|
534
|
+
export { $reconcileRoot, AGENT_WRITE_TAG, COLLAB_TAG, TREE_NAME, createLoroBinding, editAtVersion, planFrames, streamAtVersion };
|
package/dist/stream.d.ts
CHANGED
|
@@ -16,7 +16,9 @@ export interface StreamStep {
|
|
|
16
16
|
} | null;
|
|
17
17
|
update: Uint8Array;
|
|
18
18
|
}
|
|
19
|
+
/** @deprecated Agent writes are now played client-side by `@haklex/rich-plugin-agent-stream`; write once with `editAtVersion`. */
|
|
19
20
|
export declare function planFrames(base: Json, target: Json): Frame[];
|
|
21
|
+
/** @deprecated Agent writes are now played client-side by `@haklex/rich-plugin-agent-stream`; write once with `editAtVersion`. */
|
|
20
22
|
export declare function streamAtVersion(doc: LoroDoc, frontiers: Frontiers, createEditor: () => LexicalEditor, base: SerializedEditorState, target: SerializedEditorState, commitMessage?: string): {
|
|
21
23
|
frontiers: Frontiers;
|
|
22
24
|
steps: StreamStep[];
|
package/dist/stream.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,aAAa,EAElB,KAAK,qBAAqB,EAC3B,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAGjD,OAAO,EAAkB,KAAK,SAAS,EAAE,KAAK,IAAI,EAAiB,MAAM,aAAa,CAAC;AAGvF,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC9C,MAAM,EAAE,UAAU,CAAC;CACpB;AA6DD,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAG,KAAK,EAAE,CA6E5D;AAoBD,wBAAgB,eAAe,CAC7B,GAAG,EAAE,OAAO,EACZ,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,MAAM,aAAa,EACjC,IAAI,EAAE,qBAAqB,EAC3B,MAAM,EAAE,qBAAqB,EAC7B,aAAa,CAAC,EAAE,MAAM,GACrB;IAAE,SAAS,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,UAAU,EAAE,CAAA;CAAE,CAgB/C"}
|
|
1
|
+
{"version":3,"file":"stream.d.ts","sourceRoot":"","sources":["../src/stream.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,aAAa,EAElB,KAAK,qBAAqB,EAC3B,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AAGjD,OAAO,EAAkB,KAAK,SAAS,EAAE,KAAK,IAAI,EAAiB,MAAM,aAAa,CAAC;AAGvF,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,EAAE,CAAC;CAChB;AAED,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,IAAI,CAAC;CACZ;AAED,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC9C,MAAM,EAAE,UAAU,CAAC;CACpB;AA6DD,kIAAkI;AAClI,wBAAgB,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,GAAG,KAAK,EAAE,CA6E5D;AAoBD,kIAAkI;AAClI,wBAAgB,eAAe,CAC7B,GAAG,EAAE,OAAO,EACZ,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,MAAM,aAAa,EACjC,IAAI,EAAE,qBAAqB,EAC3B,MAAM,EAAE,qBAAqB,EAC7B,aAAa,CAAC,EAAE,MAAM,GACrB;IAAE,SAAS,EAAE,SAAS,CAAC;IAAC,KAAK,EAAE,UAAU,EAAE,CAAA;CAAE,CAgB/C"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@haklex/rich-collab-loro",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.63.1",
|
|
4
4
|
"description": "Loro CRDT binding for Lexical — bidirectional LoroTree sync for real-time collaboration and history",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"src"
|
|
22
22
|
],
|
|
23
23
|
"devDependencies": {
|
|
24
|
-
"@haklex/rich-headless": "0.
|
|
25
|
-
"@haklex/rich-litexml": "0.
|
|
24
|
+
"@haklex/rich-headless": "0.63.1",
|
|
25
|
+
"@haklex/rich-litexml": "0.63.1",
|
|
26
26
|
"@lexical/headless": "^0.53.0",
|
|
27
27
|
"lexical": "^0.53.0",
|
|
28
28
|
"loro-crdt": "^1.16.4",
|
package/src/binding.ts
CHANGED
|
@@ -13,13 +13,22 @@ import {
|
|
|
13
13
|
type SerializedLexicalNode,
|
|
14
14
|
type TextNode,
|
|
15
15
|
} from 'lexical';
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
type ImportStatus,
|
|
18
|
+
type LoroDoc,
|
|
19
|
+
LoroText,
|
|
20
|
+
type LoroTreeNode,
|
|
21
|
+
type TreeID,
|
|
22
|
+
} from 'loro-crdt';
|
|
17
23
|
|
|
18
24
|
import { transformOffset } from './offset';
|
|
19
25
|
import { $updateInPlace } from './reconcile';
|
|
20
26
|
import { stable } from './stable';
|
|
21
27
|
|
|
22
28
|
export const COLLAB_TAG = 'collab';
|
|
29
|
+
export const AGENT_WRITE_TAG = 'agent-write';
|
|
30
|
+
const AGENT_MESSAGE_PREFIX = 'agent:';
|
|
31
|
+
const AGENT_LIVE_WINDOW_SECONDS = 15;
|
|
23
32
|
export const TREE_NAME = 'lexical';
|
|
24
33
|
const TEXT_KEY = '#text';
|
|
25
34
|
|
|
@@ -31,6 +40,7 @@ export interface LoroBinding {
|
|
|
31
40
|
}
|
|
32
41
|
|
|
33
42
|
export interface LoroBindingOptions {
|
|
43
|
+
agentLiveWindowSeconds?: number;
|
|
34
44
|
commitMessage?: string;
|
|
35
45
|
}
|
|
36
46
|
|
|
@@ -235,7 +245,7 @@ export function createLoroBinding(
|
|
|
235
245
|
}
|
|
236
246
|
};
|
|
237
247
|
|
|
238
|
-
const pullFromDoc = () => {
|
|
248
|
+
const pullFromDoc = (agent = false) => {
|
|
239
249
|
editor.update(
|
|
240
250
|
() => {
|
|
241
251
|
const rootTreeNode = tree.roots()[0];
|
|
@@ -254,7 +264,7 @@ export function createLoroBinding(
|
|
|
254
264
|
if (!visited.has(node.getKey()) && node.isAttached()) node.remove();
|
|
255
265
|
}
|
|
256
266
|
},
|
|
257
|
-
{ discrete: true, tag: COLLAB_TAG },
|
|
267
|
+
{ discrete: true, tag: agent ? [COLLAB_TAG, AGENT_WRITE_TAG] : COLLAB_TAG },
|
|
258
268
|
);
|
|
259
269
|
};
|
|
260
270
|
|
|
@@ -271,8 +281,13 @@ export function createLoroBinding(
|
|
|
271
281
|
idOf: (key) => keyToId.get(key),
|
|
272
282
|
keyOf: (id) => idToKey.get(id),
|
|
273
283
|
import: (bytes) => {
|
|
274
|
-
|
|
275
|
-
|
|
284
|
+
pullFromDoc(
|
|
285
|
+
hasLiveAgentChange(
|
|
286
|
+
doc,
|
|
287
|
+
doc.import(bytes),
|
|
288
|
+
options.agentLiveWindowSeconds ?? AGENT_LIVE_WINDOW_SECONDS,
|
|
289
|
+
),
|
|
290
|
+
);
|
|
276
291
|
},
|
|
277
292
|
};
|
|
278
293
|
}
|
|
@@ -280,3 +295,18 @@ export function createLoroBinding(
|
|
|
280
295
|
function $isTextLike(props: Props): boolean {
|
|
281
296
|
return typeof props.format === 'number' && typeof props.detail === 'number' && 'mode' in props;
|
|
282
297
|
}
|
|
298
|
+
|
|
299
|
+
function hasLiveAgentChange(doc: LoroDoc, status: ImportStatus, windowSeconds: number): boolean {
|
|
300
|
+
const oldest = Date.now() / 1000 - windowSeconds;
|
|
301
|
+
for (const [peer, span] of status.success) {
|
|
302
|
+
let counter = span.start;
|
|
303
|
+
while (counter < span.end) {
|
|
304
|
+
const change = doc.getChangeAt({ peer, counter });
|
|
305
|
+
if (change.message?.startsWith(AGENT_MESSAGE_PREFIX) && change.timestamp >= oldest) {
|
|
306
|
+
return true;
|
|
307
|
+
}
|
|
308
|
+
counter = change.counter + change.length;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return false;
|
|
312
|
+
}
|
package/src/index.ts
CHANGED
package/src/stream.ts
CHANGED
|
@@ -86,6 +86,7 @@ function pairGap(dels: Json[], adds: Json[]): Item[] {
|
|
|
86
86
|
});
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
/** @deprecated Agent writes are now played client-side by `@haklex/rich-plugin-agent-stream`; write once with `editAtVersion`. */
|
|
89
90
|
export function planFrames(base: Json, target: Json): Frame[] {
|
|
90
91
|
const before = base.children ?? [];
|
|
91
92
|
const after = target.children ?? [];
|
|
@@ -183,6 +184,7 @@ function $locate(focus: FrameFocus | null): { key: NodeKey; offset: number } | n
|
|
|
183
184
|
: { key: block.getKey(), offset: 0 };
|
|
184
185
|
}
|
|
185
186
|
|
|
187
|
+
/** @deprecated Agent writes are now played client-side by `@haklex/rich-plugin-agent-stream`; write once with `editAtVersion`. */
|
|
186
188
|
export function streamAtVersion(
|
|
187
189
|
doc: LoroDoc,
|
|
188
190
|
frontiers: Frontiers,
|