@ai-matrx/office 0.8.11 → 0.8.13
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/CHANGELOG.md +5 -1
- package/dist/collab-sheets-transform.cjs +457 -39
- package/dist/collab-sheets-transform.cjs.map +1 -1
- package/dist/collab-sheets-transform.d.cts +65 -7
- package/dist/collab-sheets-transform.d.ts +65 -7
- package/dist/collab-sheets-transform.js +463 -36
- package/dist/collab-sheets-transform.js.map +1 -1
- package/dist/docs.cjs +35 -21
- package/dist/docs.cjs.map +1 -1
- package/dist/docs.d.cts +32 -2
- package/dist/docs.d.ts +32 -2
- package/dist/docs.js +16 -2
- package/dist/docs.js.map +1 -1
- package/dist/univer-collab.cjs +201 -5
- package/dist/univer-collab.cjs.map +1 -1
- package/dist/univer-collab.d.cts +71 -2
- package/dist/univer-collab.d.ts +71 -2
- package/dist/univer-collab.js +201 -5
- package/dist/univer-collab.js.map +1 -1
- package/dist/univer.cjs +4 -0
- package/dist/univer.cjs.map +1 -1
- package/dist/univer.d.cts +3 -1
- package/dist/univer.d.ts +3 -1
- package/dist/univer.js +4 -0
- package/dist/univer.js.map +1 -1
- package/package.json +5 -5
package/dist/univer-collab.cjs
CHANGED
|
@@ -53,6 +53,7 @@ __export(collab_exports, {
|
|
|
53
53
|
normalizeDocsOp: () => normalizeDocsOp,
|
|
54
54
|
openDocsOtRoom: () => openDocsOtRoom,
|
|
55
55
|
rebaseUndoStack: () => rebaseUndoStack,
|
|
56
|
+
restoreDocumentSnapshot: () => restoreDocumentSnapshot,
|
|
56
57
|
stampCovered: () => stampCovered,
|
|
57
58
|
stampDeterministicIds: () => stampDeterministicIds,
|
|
58
59
|
stripSenderView: () => stripSenderView,
|
|
@@ -963,10 +964,10 @@ function canonicalRuns(runs) {
|
|
|
963
964
|
}
|
|
964
965
|
return out;
|
|
965
966
|
}
|
|
966
|
-
function foldPieces(items,
|
|
967
|
+
function foldPieces(items, keyOf2) {
|
|
967
968
|
const groups = /* @__PURE__ */ new Map();
|
|
968
969
|
for (const it of items ?? []) {
|
|
969
|
-
const k =
|
|
970
|
+
const k = keyOf2(it);
|
|
970
971
|
const g = groups.get(k) ?? [];
|
|
971
972
|
g.push(it);
|
|
972
973
|
groups.set(k, g);
|
|
@@ -1277,6 +1278,120 @@ var Y2 = __toESM(require("yjs"), 1);
|
|
|
1277
1278
|
var import_awareness2 = require("y-protocols/awareness");
|
|
1278
1279
|
var import_collab3 = require("@ai-matrx/data/realtime/collab");
|
|
1279
1280
|
|
|
1281
|
+
// src/docs/document-model.ts
|
|
1282
|
+
var import_core2 = require("@univerjs/core");
|
|
1283
|
+
|
|
1284
|
+
// src/univer/openEditorContent.ts
|
|
1285
|
+
var replacers = /* @__PURE__ */ new Map();
|
|
1286
|
+
var keyOf = (kind, recordId) => `${kind}:${recordId}`;
|
|
1287
|
+
function hasOpenEditorContent(kind, recordId) {
|
|
1288
|
+
return replacers.has(keyOf(kind, recordId));
|
|
1289
|
+
}
|
|
1290
|
+
async function showSavedSnapshot(kind, recordId, row) {
|
|
1291
|
+
const replace = replacers.get(keyOf(kind, recordId));
|
|
1292
|
+
if (!replace) return false;
|
|
1293
|
+
await replace(row);
|
|
1294
|
+
return true;
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
// src/docs/document-model.ts
|
|
1298
|
+
function moveRangesThroughBodyEdit(ranges, actions) {
|
|
1299
|
+
const a = actions;
|
|
1300
|
+
if (!Array.isArray(a) || a[0] !== "body") return null;
|
|
1301
|
+
const sub = a[1];
|
|
1302
|
+
if (!sub || sub.et !== "text-x" || !Array.isArray(sub.e)) return null;
|
|
1303
|
+
const e = sub.e;
|
|
1304
|
+
return ranges.map((r) => {
|
|
1305
|
+
const collapsed = r.startOffset === r.endOffset;
|
|
1306
|
+
const startOffset = import_core2.TextX.transformPosition(e, r.startOffset, collapsed);
|
|
1307
|
+
return { startOffset, endOffset: collapsed ? startOffset : import_core2.TextX.transformPosition(e, r.endOffset, true) };
|
|
1308
|
+
});
|
|
1309
|
+
}
|
|
1310
|
+
|
|
1311
|
+
// src/docs/remote-carets.ts
|
|
1312
|
+
var CARET_HEARTBEAT_MS = 2e3;
|
|
1313
|
+
var CARET_STALE_MS = 12e3;
|
|
1314
|
+
var CARET_MAX_RANGES = 4;
|
|
1315
|
+
function publishedCaretFromSelection(sel, now) {
|
|
1316
|
+
if (!sel || sel.length === 0) return null;
|
|
1317
|
+
if (sel.some((r) => r.segmentId)) return null;
|
|
1318
|
+
const ranges = sel.slice(0, CARET_MAX_RANGES).map((r) => ({ start: Math.min(r.startOffset, r.endOffset), end: Math.max(r.startOffset, r.endOffset) }));
|
|
1319
|
+
return { ranges, at: now };
|
|
1320
|
+
}
|
|
1321
|
+
var sameRanges = (a, b) => a.length === b.length && a.every((r, i) => r.start === b[i].start && r.end === b[i].end);
|
|
1322
|
+
function readCaret(raw) {
|
|
1323
|
+
const c = raw?.caret;
|
|
1324
|
+
if (!c || !Array.isArray(c.ranges) || typeof c.at !== "number") return null;
|
|
1325
|
+
const ranges = [];
|
|
1326
|
+
for (const r of c.ranges.slice(0, CARET_MAX_RANGES)) {
|
|
1327
|
+
const s = r?.start;
|
|
1328
|
+
const e = r?.end;
|
|
1329
|
+
if (typeof s !== "number" || typeof e !== "number" || !Number.isFinite(s) || !Number.isFinite(e) || s < 0 || e < 0) return null;
|
|
1330
|
+
ranges.push({ start: s, end: e });
|
|
1331
|
+
}
|
|
1332
|
+
return ranges.length ? { ranges, at: c.at } : null;
|
|
1333
|
+
}
|
|
1334
|
+
var RemoteCaretBook = class {
|
|
1335
|
+
constructor(selfClientId, now = Date.now) {
|
|
1336
|
+
this.selfClientId = selfClientId;
|
|
1337
|
+
this.now = now;
|
|
1338
|
+
}
|
|
1339
|
+
selfClientId;
|
|
1340
|
+
now;
|
|
1341
|
+
carets = /* @__PURE__ */ new Map();
|
|
1342
|
+
/** Take the room's awareness states: new or republished carets replace ours, seats that left are dropped. Returns whether anything changed. */
|
|
1343
|
+
ingest(states) {
|
|
1344
|
+
let changed = false;
|
|
1345
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1346
|
+
states.forEach((raw, id) => {
|
|
1347
|
+
if (id === this.selfClientId) return;
|
|
1348
|
+
const s = raw;
|
|
1349
|
+
const caret = readCaret(raw);
|
|
1350
|
+
if (!s || typeof s.uid !== "string" || !caret) return;
|
|
1351
|
+
const key = String(id);
|
|
1352
|
+
seen.add(key);
|
|
1353
|
+
const prev = this.carets.get(key);
|
|
1354
|
+
const name = typeof s.name === "string" && s.name ? s.name : "Someone";
|
|
1355
|
+
const color = typeof s.color === "string" && s.color ? s.color : "#3b82f6";
|
|
1356
|
+
if (prev && prev.at === caret.at && prev.name === name && prev.color === color) return;
|
|
1357
|
+
this.carets.set(key, { key, uid: s.uid, name, color, ranges: caret.ranges, at: caret.at, seenAt: this.now() });
|
|
1358
|
+
changed = true;
|
|
1359
|
+
});
|
|
1360
|
+
for (const key of [...this.carets.keys()]) {
|
|
1361
|
+
if (!seen.has(key)) {
|
|
1362
|
+
this.carets.delete(key);
|
|
1363
|
+
changed = true;
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
return changed;
|
|
1367
|
+
}
|
|
1368
|
+
/** Move every caret through one body edit that was just applied (`actions` of a rich-text mutation). Returns whether any moved. */
|
|
1369
|
+
shiftThrough(actions) {
|
|
1370
|
+
let moved = false;
|
|
1371
|
+
for (const c of this.carets.values()) {
|
|
1372
|
+
const next = moveRangesThroughBodyEdit(c.ranges.map((r) => ({ startOffset: r.start, endOffset: r.end })), actions);
|
|
1373
|
+
if (!next) return false;
|
|
1374
|
+
const ranges = next.map((r) => ({ start: r.startOffset, end: r.endOffset }));
|
|
1375
|
+
if (!sameRanges(ranges, c.ranges)) {
|
|
1376
|
+
c.ranges = ranges;
|
|
1377
|
+
moved = true;
|
|
1378
|
+
}
|
|
1379
|
+
}
|
|
1380
|
+
return moved;
|
|
1381
|
+
}
|
|
1382
|
+
/** The carets to draw now (stale ones left out). */
|
|
1383
|
+
list() {
|
|
1384
|
+
const t = this.now();
|
|
1385
|
+
const out = [];
|
|
1386
|
+
for (const c of this.carets.values()) {
|
|
1387
|
+
if (t - c.seenAt > CARET_STALE_MS) continue;
|
|
1388
|
+
out.push({ key: c.key, uid: c.uid, name: c.name, color: c.color, ranges: c.ranges.map((r) => ({ ...r })) });
|
|
1389
|
+
}
|
|
1390
|
+
out.sort((a, b) => a.key.localeCompare(b.key));
|
|
1391
|
+
return out;
|
|
1392
|
+
}
|
|
1393
|
+
};
|
|
1394
|
+
|
|
1280
1395
|
// src/docs/document-service.ts
|
|
1281
1396
|
var import_api = require("@ai-matrx/api");
|
|
1282
1397
|
var import_auth = require("@ai-matrx/auth");
|
|
@@ -1349,11 +1464,11 @@ function pickColor2(uid) {
|
|
|
1349
1464
|
return palette[Math.abs(h) % palette.length] ?? "#ec4899";
|
|
1350
1465
|
}
|
|
1351
1466
|
function documentOtFactory(o) {
|
|
1352
|
-
return async (
|
|
1467
|
+
return async (basePort, onAwareness) => {
|
|
1353
1468
|
const latest = await getLatestDocumentSnapshot(o.documentId);
|
|
1354
1469
|
if (!latest.success) throw new Error(latest.error);
|
|
1355
1470
|
if (!latest.data) {
|
|
1356
|
-
const mounted =
|
|
1471
|
+
const mounted = basePort.snapshot();
|
|
1357
1472
|
if (mounted) {
|
|
1358
1473
|
const saved = await saveDocumentSnapshot({ documentId: o.documentId, snapshot: mounted, origin: "autosave" });
|
|
1359
1474
|
if (!saved.success) throw new Error(saved.error);
|
|
@@ -1362,7 +1477,31 @@ function documentOtFactory(o) {
|
|
|
1362
1477
|
const doc = new Y2.Doc();
|
|
1363
1478
|
const awareness = new import_awareness2.Awareness(doc);
|
|
1364
1479
|
awareness.setLocalState({ uid: o.uid, clientId: o.clientId, name: o.displayName ?? o.uid.slice(0, 8), color: pickColor2(o.uid), ts: Date.now() });
|
|
1365
|
-
const
|
|
1480
|
+
const carets = new RemoteCaretBook(awareness.clientID);
|
|
1481
|
+
const emit = () => onAwareness({ states: new Map(awareness.getStates()), selfUid: o.uid, carets: carets.list() });
|
|
1482
|
+
const publishAwareness = () => {
|
|
1483
|
+
carets.ingest(awareness.getStates());
|
|
1484
|
+
emit();
|
|
1485
|
+
};
|
|
1486
|
+
const bodyActions = (params) => {
|
|
1487
|
+
const p = params;
|
|
1488
|
+
return p && !p.segmentId ? p.actions : void 0;
|
|
1489
|
+
};
|
|
1490
|
+
const port = {
|
|
1491
|
+
...basePort,
|
|
1492
|
+
applyRemote(id, params) {
|
|
1493
|
+
basePort.applyRemote(id, params);
|
|
1494
|
+
const a = bodyActions(params);
|
|
1495
|
+
if (a !== void 0 && carets.shiftThrough(a)) emit();
|
|
1496
|
+
},
|
|
1497
|
+
onLocal: (listener) => basePort.onLocal((m) => {
|
|
1498
|
+
if (m.id === RICH_TEXT_EDITING) {
|
|
1499
|
+
const a = bodyActions(m.params);
|
|
1500
|
+
if (a !== void 0 && carets.shiftThrough(a)) emit();
|
|
1501
|
+
}
|
|
1502
|
+
listener(m);
|
|
1503
|
+
})
|
|
1504
|
+
};
|
|
1366
1505
|
awareness.on("change", publishAwareness);
|
|
1367
1506
|
const provider = new import_collab3.SupabaseYjsProvider({
|
|
1368
1507
|
workbookId: o.documentId,
|
|
@@ -1398,11 +1537,31 @@ function documentOtFactory(o) {
|
|
|
1398
1537
|
...o.onStatus ? { onStatus: o.onStatus } : {}
|
|
1399
1538
|
});
|
|
1400
1539
|
await room.session.ready;
|
|
1540
|
+
let lastKey = "";
|
|
1541
|
+
let lastAt = 0;
|
|
1542
|
+
const publishCaret = () => {
|
|
1543
|
+
if (typeof document !== "undefined" && document.hidden) return;
|
|
1544
|
+
let sel = null;
|
|
1545
|
+
try {
|
|
1546
|
+
sel = port.selection?.() ?? null;
|
|
1547
|
+
} catch {
|
|
1548
|
+
sel = null;
|
|
1549
|
+
}
|
|
1550
|
+
const t = Date.now();
|
|
1551
|
+
const c = publishedCaretFromSelection(sel, t);
|
|
1552
|
+
const key = c ? JSON.stringify(c.ranges) : "";
|
|
1553
|
+
if (key === lastKey && (key === "" || t - lastAt < CARET_HEARTBEAT_MS)) return;
|
|
1554
|
+
lastKey = key;
|
|
1555
|
+
lastAt = t;
|
|
1556
|
+
awareness.setLocalStateField("caret", c);
|
|
1557
|
+
};
|
|
1558
|
+
const caretTimer = setInterval(publishCaret, 120);
|
|
1401
1559
|
return {
|
|
1402
1560
|
isSaver: () => room.isSaver(),
|
|
1403
1561
|
write: (reason) => room.write(reason),
|
|
1404
1562
|
replace: (snapshot) => room.replace(snapshot),
|
|
1405
1563
|
stop: () => {
|
|
1564
|
+
clearInterval(caretTimer);
|
|
1406
1565
|
room.stop();
|
|
1407
1566
|
awareness.off("change", publishAwareness);
|
|
1408
1567
|
provider.disconnect();
|
|
@@ -1412,4 +1571,41 @@ function documentOtFactory(o) {
|
|
|
1412
1571
|
};
|
|
1413
1572
|
};
|
|
1414
1573
|
}
|
|
1574
|
+
|
|
1575
|
+
// src/docs/document-restore.ts
|
|
1576
|
+
var import_collab4 = require("@ai-matrx/data/realtime/collab");
|
|
1577
|
+
var defaultDeps = {
|
|
1578
|
+
client: () => getDocsHost().getSupabase(),
|
|
1579
|
+
roomExists: import_collab4.collabRoomExists,
|
|
1580
|
+
replace: import_collab4.collabReplace,
|
|
1581
|
+
stateHash: (snapshot) => docsStateHash(snapshot),
|
|
1582
|
+
hasOpenEditor: (documentId) => hasOpenEditorContent("document", documentId),
|
|
1583
|
+
showInOpenEditor: (documentId, row) => showSavedSnapshot("document", documentId, row),
|
|
1584
|
+
async saveRow(args) {
|
|
1585
|
+
const res = await saveDocumentSnapshot({ documentId: args.documentId, snapshot: args.snapshot, origin: "restored", label: args.label });
|
|
1586
|
+
if (!res.success) throw new Error(res.error);
|
|
1587
|
+
return { id: res.data.id };
|
|
1588
|
+
}
|
|
1589
|
+
};
|
|
1590
|
+
async function restoreDocumentSnapshot(args, deps = {}) {
|
|
1591
|
+
const d = { ...defaultDeps, ...deps };
|
|
1592
|
+
if (d.hasOpenEditor(args.documentId)) {
|
|
1593
|
+
const row2 = await d.saveRow(args);
|
|
1594
|
+
await d.showInOpenEditor(args.documentId, { id: row2.id, snapshot: args.snapshot });
|
|
1595
|
+
return { via: "open_editor", snapshotId: row2.id };
|
|
1596
|
+
}
|
|
1597
|
+
const client = d.client();
|
|
1598
|
+
if (await d.roomExists(client, args.documentId)) {
|
|
1599
|
+
const next = stampDeterministicIds(structuredClone(args.snapshot ?? {}));
|
|
1600
|
+
const out = await d.replace(client, {
|
|
1601
|
+
roomId: args.documentId,
|
|
1602
|
+
snapshot: next,
|
|
1603
|
+
stateHash: await d.stateHash(next),
|
|
1604
|
+
origin: "restore"
|
|
1605
|
+
});
|
|
1606
|
+
return { via: "room", rev: out.rev, snapshotId: out.ref };
|
|
1607
|
+
}
|
|
1608
|
+
const row = await d.saveRow(args);
|
|
1609
|
+
return { via: "saved", snapshotId: row.id };
|
|
1610
|
+
}
|
|
1415
1611
|
//# sourceMappingURL=univer-collab.cjs.map
|