@ai-matrx/office 0.7.0 → 0.8.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/CHANGELOG.md +15 -0
- package/dist/docs.cjs +177 -28
- package/dist/docs.cjs.map +1 -1
- package/dist/docs.d.cts +83 -2
- package/dist/docs.d.ts +83 -2
- package/dist/docs.js +154 -5
- package/dist/docs.js.map +1 -1
- package/dist/univer-collab.cjs +1004 -1
- package/dist/univer-collab.cjs.map +1 -1
- package/dist/univer-collab.d.cts +252 -1
- package/dist/univer-collab.d.ts +252 -1
- package/dist/univer-collab.js +1010 -1
- package/dist/univer-collab.js.map +1 -1
- package/package.json +3 -2
package/dist/univer-collab.d.cts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import * as Y from 'yjs';
|
|
2
2
|
import { Awareness } from 'y-protocols/awareness';
|
|
3
|
+
import { OpSessionNotice, OpSession, OpTransport, OpPresence, OpSessionIncident, OpSessionStatus, AwarenessState } from '@ai-matrx/data/realtime/collab';
|
|
4
|
+
import { IDocumentData } from '@univerjs/core';
|
|
5
|
+
import { CommitReason } from '@ai-matrx/kit/working-copy';
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* A saved snapshot names the collab-room entries it already holds, so a seat that boots from it never
|
|
@@ -210,4 +213,252 @@ declare const SENDER_VIEW_PARAMS: readonly string[];
|
|
|
210
213
|
declare function stripSenderView(params: MutationParams): MutationParams;
|
|
211
214
|
declare function isCellEditorScratch(info: Pick<CollabMutationInfo, "id">): boolean;
|
|
212
215
|
|
|
213
|
-
|
|
216
|
+
type Act = Record<string, any> & {
|
|
217
|
+
t: "i" | "r" | "d";
|
|
218
|
+
len: number;
|
|
219
|
+
};
|
|
220
|
+
/** One `lost` entry: what a transform dropped from either side (R1), for the author's notice. */
|
|
221
|
+
interface DocsLost {
|
|
222
|
+
rule: string;
|
|
223
|
+
side: "insert" | "delete";
|
|
224
|
+
len: number;
|
|
225
|
+
text?: string;
|
|
226
|
+
}
|
|
227
|
+
/** The travelling shape of a Docs JSON op: text-x edits normalised (R1 zones, R5, no oldBody). */
|
|
228
|
+
declare function normalizeDocsOp<T>(op: T): T;
|
|
229
|
+
/** R6: a retain whose style lost every key to the other side is a plain retain. */
|
|
230
|
+
declare function dropEmptyStyleRetains<T>(op: T): T;
|
|
231
|
+
/** x rewritten to apply after y over one text-x body; `side` is x's side. Drops land in `lost` (R1). */
|
|
232
|
+
declare function transformTextX(xIn: Act[], yIn: Act[], side: "left" | "right", lost?: DocsLost[]): Act[];
|
|
233
|
+
/** A Docs operation: an ot-json1 op over the whole Univer snapshot (what `rich-text-editing` carries as `actions`). */
|
|
234
|
+
type DocsOp = unknown[] | null;
|
|
235
|
+
interface DocsTransformResult {
|
|
236
|
+
op: DocsOp;
|
|
237
|
+
lost: DocsLost[];
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* `T(x, y, side)` for Docs: ot-json1 `transformNoConflict` (never throws on DROP_COLLISION / RM_UNEXPECTED_CONTENT /
|
|
241
|
+
* BLACKHOLE, §3) with our text-x transform registered only for the duration of one call, so Univer's own apply is
|
|
242
|
+
* untouched even when the bundler shares one ot-json1 instance with `@univerjs/core`. R6 runs on the result.
|
|
243
|
+
*/
|
|
244
|
+
declare function transformDocsOp(x: DocsOp, y: DocsOp, side: "left" | "right"): DocsTransformResult;
|
|
245
|
+
/** `a` then `b` as one op (TextX is the subtype every reader composes with). */
|
|
246
|
+
declare function composeDocsOps(a: DocsOp, b: DocsOp): DocsOp;
|
|
247
|
+
declare function isNoopDocsOp(op: DocsOp): boolean;
|
|
248
|
+
/** Apply an op to a plain snapshot (tests, the fold). Univer's own JSONX, so TextX applies as the editor applies. */
|
|
249
|
+
declare function applyDocsOp<T>(doc: T, op: DocsOp): T;
|
|
250
|
+
/** The inverse of `op` against the state it applied to. JSONX.invertWithDoc MUTATES its op: invert a copy (C0). */
|
|
251
|
+
declare function invertDocsOp(op: DocsOp, preDoc: unknown): DocsOp;
|
|
252
|
+
/** Part of `proto` (§6.4): Univer 1.0.4 + this transformer, rules R1–R6. */
|
|
253
|
+
declare const DOCS_TRANSFORM_VERSION = "univer-1.0.4+docs-ot-r6";
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* The canonical Docs state hash (COLLAB-DESIGN §7.2; C0 item 6, `spikes/c0-collab/lib.mjs`).
|
|
257
|
+
*
|
|
258
|
+
* SHA-256 of the canonical snapshot: keys sorted, empty values dropped, the unit id and seat-local fields
|
|
259
|
+
* removed (revision counter, collab stamps, layout state, selections), adjacent equal text runs merged, split
|
|
260
|
+
* pieces of one custom range or decoration folded, and `paragraphId` dropped (Univer's TextX.apply mints a
|
|
261
|
+
* random one per seat, C0 surprise 3). Header, footer and note bodies get the same rules.
|
|
262
|
+
*
|
|
263
|
+
* Two seats with equal content hash equal; one character more hashes differently. Browser and Node: the
|
|
264
|
+
* digest is Web Crypto's.
|
|
265
|
+
*/
|
|
266
|
+
/** Never content: the collab stamps, revision counters, seat-local view state. */
|
|
267
|
+
declare const DOC_LOCAL_KEYS: string[];
|
|
268
|
+
/** The canonical Docs snapshot: content only, keys sorted, runs normalised, local fields removed. */
|
|
269
|
+
declare function canonicalDocsSnapshot(snapshot: unknown): unknown;
|
|
270
|
+
/** The canonical form as the string the hash digests (tests compare these directly). */
|
|
271
|
+
declare const canonicalDocsString: (snapshot: unknown) => string;
|
|
272
|
+
/** SHA-256 hex of the canonical snapshot (§7.2). */
|
|
273
|
+
declare function docsStateHash(snapshot: unknown): Promise<string>;
|
|
274
|
+
/**
|
|
275
|
+
* Ids every seat agrees on for a snapshot that lacks them (C0 surprise 5): a raw snapshot (an import, markdown)
|
|
276
|
+
* loaded on two seats would otherwise get two random `sectionId`s / `paragraphId`s. Missing ids become
|
|
277
|
+
* positional ones; ids already present are kept. Applied to every snapshot a room loads.
|
|
278
|
+
*/
|
|
279
|
+
declare function stampDeterministicIds<T>(snapshot: T): T;
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* The Docs co-editing room on the ordered log (COLLAB-DESIGN §6, unit C3): the `OpDocumentAdapter` for
|
|
283
|
+
* `openOpSession` over a document's editors, plus saves, restore and the saver choice.
|
|
284
|
+
*
|
|
285
|
+
* - CAPTURE (§6.3). The document model hands over every local `doc.mutation.rich-text-editing` with the applied
|
|
286
|
+
* params (the handler's own rewrite, C0 surprise 6), the pre-state cloned in `beforeCommandExecuted`, and the
|
|
287
|
+
* outermost command id. `onlyLocal` / `fromCollab` never arrive here (the model filters them). Only `segmentId`
|
|
288
|
+
* and `actions` travel; the inverse is computed from the APPLIED actions on a COPY (`invertWithDoc` mutates its
|
|
289
|
+
* op). Each mutation goes to the session as it happens, so a remote apply can never land between a local
|
|
290
|
+
* mutation and its hand-over; the command id rides along as the change's label.
|
|
291
|
+
* - APPLY (§6.7). `syncExecuteCommand(id, { unitId: own, segmentId, actions, textRanges: null,
|
|
292
|
+
* noNeedSetTextRange: true }, { onlyLocal: true, fromCollab: true })` through the model (every view of the
|
|
293
|
+
* document in this tab). A throw, `false`, or actions the handler emptied fail the apply: the session rebuilds,
|
|
294
|
+
* never skips. While an IME composition is open, remote applies QUEUE (acks continue); a local op captured
|
|
295
|
+
* meanwhile is rebased over the queue (`L' = T(L, Q, 'left')`, `Q' = T(Q, L, 'right')`), and the queue drains
|
|
296
|
+
* at `compositionend`.
|
|
297
|
+
* - UNDO (§6.9). Each applied remote op rebases every stored undo and redo item of this seat
|
|
298
|
+
* (`T(item, R1, 'left')`); `textRanges` and `layoutState` are stripped from rebased items; an item whose rebase
|
|
299
|
+
* is a barrier, throws or comes back empty is dropped. Undo therefore only ever reverts this person's changes.
|
|
300
|
+
* - SAVES (§7.3). Only a quiet seat whose hash matches every peer at its rev saves, and only the chosen saver
|
|
301
|
+
* (the lowest client id among seats publishing a quiet hash) saves on its own; a manual save always tries.
|
|
302
|
+
* - RESTORE (§20). A whole-document replace goes through `collab_replace`: the snapshot is loaded into this seat's
|
|
303
|
+
* editor first, so the stored snapshot and its hash are the Univer-normalised form every seat will load.
|
|
304
|
+
*/
|
|
305
|
+
|
|
306
|
+
declare const RICH_TEXT_EDITING = "doc.mutation.rich-text-editing";
|
|
307
|
+
/**
|
|
308
|
+
* The room's protocol (§6.4): Univer 1.0.4 + transformer `DOCS_TRANSFORM_VERSION`. Any Univer or transformer
|
|
309
|
+
* change raises it; a room at another proto opens read-only for this build.
|
|
310
|
+
*/
|
|
311
|
+
declare const DOCS_COLLAB_PROTO = 1;
|
|
312
|
+
/** One local mutation as the document model hands it over. */
|
|
313
|
+
interface DocsLocalMutation {
|
|
314
|
+
id: string;
|
|
315
|
+
params: unknown;
|
|
316
|
+
/** The document before this mutation (cloned in `beforeCommandExecuted`), when the view could supply it. */
|
|
317
|
+
pre: unknown | null;
|
|
318
|
+
/** The outermost command this mutation belongs to (the depth tracker with its try/finally guard). */
|
|
319
|
+
cmd: string | null;
|
|
320
|
+
/** Whether the mutation changes what a snapshot stores. */
|
|
321
|
+
content: boolean;
|
|
322
|
+
}
|
|
323
|
+
/** What the document model lends the room (implemented by `DocumentModel`). */
|
|
324
|
+
interface DocsOtPort {
|
|
325
|
+
onLocal(listener: (m: DocsLocalMutation) => void): () => void;
|
|
326
|
+
/** Apply one committed mutation to every view of the document; throws when any view could not. */
|
|
327
|
+
applyRemote(id: string, params: Record<string, unknown>): void;
|
|
328
|
+
/** The document as the editor holds it now (`doc.save()`), or null when no view is mounted. */
|
|
329
|
+
snapshot(): unknown | null;
|
|
330
|
+
/** Replace the document in every view (and the kept editor). */
|
|
331
|
+
load(snapshot: unknown): void;
|
|
332
|
+
/** Univer's undo/redo service of each mounted view and the unit it holds. */
|
|
333
|
+
undoTargets(): Array<{
|
|
334
|
+
service: unknown;
|
|
335
|
+
unitId: string;
|
|
336
|
+
}>;
|
|
337
|
+
/** Ask for a save (the record's one coalesced save). */
|
|
338
|
+
markDirty(): void;
|
|
339
|
+
}
|
|
340
|
+
type DocsOtNotice = OpSessionNotice<DocsOp> | {
|
|
341
|
+
kind: "not_shared";
|
|
342
|
+
mutationId: string;
|
|
343
|
+
} | {
|
|
344
|
+
kind: "not_live";
|
|
345
|
+
};
|
|
346
|
+
interface OpenDocsOtRoomOptions {
|
|
347
|
+
port: DocsOtPort;
|
|
348
|
+
transport: OpTransport;
|
|
349
|
+
clientId: string;
|
|
350
|
+
presence?: OpPresence;
|
|
351
|
+
/** `collab_replace` for this room (restore, whole import). */
|
|
352
|
+
replace: (req: {
|
|
353
|
+
snapshot: unknown;
|
|
354
|
+
stateHash: string;
|
|
355
|
+
clientId: string;
|
|
356
|
+
}) => Promise<{
|
|
357
|
+
rev: number;
|
|
358
|
+
ref: string;
|
|
359
|
+
}>;
|
|
360
|
+
onNotice?: (n: DocsOtNotice) => void;
|
|
361
|
+
onIncident?: (i: OpSessionIncident) => void;
|
|
362
|
+
onStatus?: (s: OpSessionStatus) => void;
|
|
363
|
+
/** Watch IME composition here (default: `document`). */
|
|
364
|
+
compositionTarget?: Pick<Document, "addEventListener" | "removeEventListener"> | null;
|
|
365
|
+
}
|
|
366
|
+
interface DocsOtRoom {
|
|
367
|
+
session: OpSession<DocsOp>;
|
|
368
|
+
/** The record's save: a checkpoint at a quiet point (§7.3). False when this seat does not write now. */
|
|
369
|
+
write(reason: "manual" | string): Promise<boolean>;
|
|
370
|
+
/** Restore / whole import through `collab_replace` (§20). */
|
|
371
|
+
replace(snapshot: unknown): Promise<void>;
|
|
372
|
+
/** This seat is the one that saves on its own. */
|
|
373
|
+
isSaver(): boolean;
|
|
374
|
+
stop(): void;
|
|
375
|
+
}
|
|
376
|
+
interface UndoItem {
|
|
377
|
+
unitID?: string;
|
|
378
|
+
undoMutations?: Array<{
|
|
379
|
+
id: string;
|
|
380
|
+
params: Record<string, unknown>;
|
|
381
|
+
}>;
|
|
382
|
+
redoMutations?: Array<{
|
|
383
|
+
id: string;
|
|
384
|
+
params: Record<string, unknown>;
|
|
385
|
+
}>;
|
|
386
|
+
}
|
|
387
|
+
/** The Univer 1.0.4 fields the undo rebase reads; a forcing test fails if they move (Univer is pinned). */
|
|
388
|
+
declare function undoStacksOf(service: unknown): {
|
|
389
|
+
undo: Map<string, UndoItem[]>;
|
|
390
|
+
redo: Map<string, UndoItem[]>;
|
|
391
|
+
} | null;
|
|
392
|
+
/** §6.9: rebase one stack over a remote op; items that cannot follow are dropped. Returns how many were dropped. */
|
|
393
|
+
declare function rebaseUndoStack(items: UndoItem[], remote: DocsOp): number;
|
|
394
|
+
declare function openDocsOtRoom(o: OpenDocsOtRoomOptions): DocsOtRoom;
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* THE DOCUMENT MODEL — the engine of one cloud document per tab.
|
|
398
|
+
*
|
|
399
|
+
* Before this, the document lived only inside each `DocumentEditor`'s Univer
|
|
400
|
+
* instance. A remount (a board tile waking, a tile removed and undone, a route
|
|
401
|
+
* change and back) rebuilt the editor from the SERVER copy, so whatever the
|
|
402
|
+
* last save had not carried yet was gone; two mounts of one document were two
|
|
403
|
+
* editors, each saving full snapshots of its own copy (last write wins), each
|
|
404
|
+
* with its own realtime channel and its own collab room.
|
|
405
|
+
*
|
|
406
|
+
* The document is a record on THE one working-copy primitive
|
|
407
|
+
* (`@ai-matrx/kit/working-copy`, kind `udt_document`,
|
|
408
|
+
* `./documentModels.ts`): its save status, dirty and view count are Redux state
|
|
409
|
+
* (`workingCopies["udt_document:<id>"]`), its one coalesced save is the kind's.
|
|
410
|
+
* What Redux cannot hold — Univer's body — is this engine, registered on the
|
|
411
|
+
* record's session under the same key:
|
|
412
|
+
*
|
|
413
|
+
* - BODY. Every local mutation in one view is replayed into every other view
|
|
414
|
+
* of the document (the same mutation stream collab sends between machines),
|
|
415
|
+
* so two views show one document. A new view boots from the engine's latest
|
|
416
|
+
* state — a live view's snapshot, or the snapshot the last view left behind
|
|
417
|
+
* — never the stale server copy while the record's session is alive.
|
|
418
|
+
* - SIDE EFFECTS, ONCE. The snapshot realtime channel, the collab room, and
|
|
419
|
+
* the pagehide / visibility / beforeunload flush are opened once per
|
|
420
|
+
* document and closed once.
|
|
421
|
+
* - THE EDITOR ITSELF, KEPT. Univer keeps its undo / redo history inside the
|
|
422
|
+
* editor instance, so rebuilding the editor on a remount threw the history
|
|
423
|
+
* away. When the last view leaves, its editor (a Univer instance with its
|
|
424
|
+
* own DOM host) is parked here — off the page, alive — and the next view of
|
|
425
|
+
* the document RE-ATTACHES that same instance to its own container instead
|
|
426
|
+
* of booting a new one (`parkEditor` / `takeParkedEditor`). Undo and redo
|
|
427
|
+
* therefore work across hide / show, a remount and a removed tile undone,
|
|
428
|
+
* for as long as the record's session is warm; `close()` disposes it.
|
|
429
|
+
*/
|
|
430
|
+
|
|
431
|
+
type DocumentSnapshotData = Partial<IDocumentData>;
|
|
432
|
+
/** The document's room on the ordered log (COLLAB-DESIGN §6, C3), bound through `startOtRoom`. */
|
|
433
|
+
interface DocumentOtHandle {
|
|
434
|
+
stop: () => void;
|
|
435
|
+
write: (reason: CommitReason) => Promise<boolean>;
|
|
436
|
+
replace: (snapshot: DocumentSnapshotData) => Promise<void>;
|
|
437
|
+
isSaver: () => boolean;
|
|
438
|
+
}
|
|
439
|
+
type DocumentOtFactory = (port: DocsOtPort, onAwareness: (awareness: DocumentAwareness) => void) => Promise<DocumentOtHandle | null>;
|
|
440
|
+
/** Who else is in the document's room (shared by every view of it). */
|
|
441
|
+
interface DocumentAwareness {
|
|
442
|
+
states: Map<number, AwarenessState>;
|
|
443
|
+
selfUid: string;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/** `collab.ot_rooms` (§12.2): which documents without a room open one. */
|
|
447
|
+
type OtRoomsKnob = "off" | "new" | "all";
|
|
448
|
+
/** Whether this document opens on the ordered log: its room exists, or the knob opens one. */
|
|
449
|
+
declare function documentUsesOrderedRoom(documentId: string, knob: OtRoomsKnob, isNewDocument: () => Promise<boolean>): Promise<boolean>;
|
|
450
|
+
interface DocumentOtFactoryOptions {
|
|
451
|
+
documentId: string;
|
|
452
|
+
uid: string;
|
|
453
|
+
/** One per open tab; never reused. */
|
|
454
|
+
clientId: string;
|
|
455
|
+
displayName?: string;
|
|
456
|
+
readAntiEntropyIntervalMs?: () => Promise<number>;
|
|
457
|
+
onNotice?: (n: DocsOtNotice) => void;
|
|
458
|
+
onIncident?: (i: OpSessionIncident) => void;
|
|
459
|
+
onStatus?: (s: OpSessionStatus) => void;
|
|
460
|
+
}
|
|
461
|
+
/** The `DocumentModel.startOtRoom` factory for one document. */
|
|
462
|
+
declare function documentOtFactory(o: DocumentOtFactoryOptions): DocumentOtFactory;
|
|
463
|
+
|
|
464
|
+
export { COLLAB_COVERED_KEY, type CollabBase, type CollabMutationInfo, type CollabProviderLike, type CommandServiceLike, DOCS_COLLAB_PROTO, DOCS_TRANSFORM_VERSION, DOC_LOCAL_KEYS, type DocsLocalMutation, type DocsLost, type DocsOp, type DocsOtNotice, type DocsOtPort, type DocsOtRoom, type DocsTransformResult, type DocumentOtFactoryOptions, type OpenDocsOtRoomOptions, type OtRoomsKnob, RICH_TEXT_EDITING, SENDER_VIEW_PARAMS, WorkbookCollabSession, type WorkbookCollabSessionOptions, applyDocsOp, canonicalDocsSnapshot, canonicalDocsString, composeDocsOps, docsStateHash, documentOtFactory, documentUsesOrderedRoom, dropEmptyStyleRetains, invertDocsOp, isCellEditorScratch, isNoopDocsOp, normalizeDocsOp, openDocsOtRoom, rebaseUndoStack, stampCovered, stampDeterministicIds, stripSenderView, takeCovered, transformDocsOp, transformTextX, undoStacksOf };
|
package/dist/univer-collab.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import * as Y from 'yjs';
|
|
2
2
|
import { Awareness } from 'y-protocols/awareness';
|
|
3
|
+
import { OpSessionNotice, OpSession, OpTransport, OpPresence, OpSessionIncident, OpSessionStatus, AwarenessState } from '@ai-matrx/data/realtime/collab';
|
|
4
|
+
import { IDocumentData } from '@univerjs/core';
|
|
5
|
+
import { CommitReason } from '@ai-matrx/kit/working-copy';
|
|
3
6
|
|
|
4
7
|
/**
|
|
5
8
|
* A saved snapshot names the collab-room entries it already holds, so a seat that boots from it never
|
|
@@ -210,4 +213,252 @@ declare const SENDER_VIEW_PARAMS: readonly string[];
|
|
|
210
213
|
declare function stripSenderView(params: MutationParams): MutationParams;
|
|
211
214
|
declare function isCellEditorScratch(info: Pick<CollabMutationInfo, "id">): boolean;
|
|
212
215
|
|
|
213
|
-
|
|
216
|
+
type Act = Record<string, any> & {
|
|
217
|
+
t: "i" | "r" | "d";
|
|
218
|
+
len: number;
|
|
219
|
+
};
|
|
220
|
+
/** One `lost` entry: what a transform dropped from either side (R1), for the author's notice. */
|
|
221
|
+
interface DocsLost {
|
|
222
|
+
rule: string;
|
|
223
|
+
side: "insert" | "delete";
|
|
224
|
+
len: number;
|
|
225
|
+
text?: string;
|
|
226
|
+
}
|
|
227
|
+
/** The travelling shape of a Docs JSON op: text-x edits normalised (R1 zones, R5, no oldBody). */
|
|
228
|
+
declare function normalizeDocsOp<T>(op: T): T;
|
|
229
|
+
/** R6: a retain whose style lost every key to the other side is a plain retain. */
|
|
230
|
+
declare function dropEmptyStyleRetains<T>(op: T): T;
|
|
231
|
+
/** x rewritten to apply after y over one text-x body; `side` is x's side. Drops land in `lost` (R1). */
|
|
232
|
+
declare function transformTextX(xIn: Act[], yIn: Act[], side: "left" | "right", lost?: DocsLost[]): Act[];
|
|
233
|
+
/** A Docs operation: an ot-json1 op over the whole Univer snapshot (what `rich-text-editing` carries as `actions`). */
|
|
234
|
+
type DocsOp = unknown[] | null;
|
|
235
|
+
interface DocsTransformResult {
|
|
236
|
+
op: DocsOp;
|
|
237
|
+
lost: DocsLost[];
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* `T(x, y, side)` for Docs: ot-json1 `transformNoConflict` (never throws on DROP_COLLISION / RM_UNEXPECTED_CONTENT /
|
|
241
|
+
* BLACKHOLE, §3) with our text-x transform registered only for the duration of one call, so Univer's own apply is
|
|
242
|
+
* untouched even when the bundler shares one ot-json1 instance with `@univerjs/core`. R6 runs on the result.
|
|
243
|
+
*/
|
|
244
|
+
declare function transformDocsOp(x: DocsOp, y: DocsOp, side: "left" | "right"): DocsTransformResult;
|
|
245
|
+
/** `a` then `b` as one op (TextX is the subtype every reader composes with). */
|
|
246
|
+
declare function composeDocsOps(a: DocsOp, b: DocsOp): DocsOp;
|
|
247
|
+
declare function isNoopDocsOp(op: DocsOp): boolean;
|
|
248
|
+
/** Apply an op to a plain snapshot (tests, the fold). Univer's own JSONX, so TextX applies as the editor applies. */
|
|
249
|
+
declare function applyDocsOp<T>(doc: T, op: DocsOp): T;
|
|
250
|
+
/** The inverse of `op` against the state it applied to. JSONX.invertWithDoc MUTATES its op: invert a copy (C0). */
|
|
251
|
+
declare function invertDocsOp(op: DocsOp, preDoc: unknown): DocsOp;
|
|
252
|
+
/** Part of `proto` (§6.4): Univer 1.0.4 + this transformer, rules R1–R6. */
|
|
253
|
+
declare const DOCS_TRANSFORM_VERSION = "univer-1.0.4+docs-ot-r6";
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* The canonical Docs state hash (COLLAB-DESIGN §7.2; C0 item 6, `spikes/c0-collab/lib.mjs`).
|
|
257
|
+
*
|
|
258
|
+
* SHA-256 of the canonical snapshot: keys sorted, empty values dropped, the unit id and seat-local fields
|
|
259
|
+
* removed (revision counter, collab stamps, layout state, selections), adjacent equal text runs merged, split
|
|
260
|
+
* pieces of one custom range or decoration folded, and `paragraphId` dropped (Univer's TextX.apply mints a
|
|
261
|
+
* random one per seat, C0 surprise 3). Header, footer and note bodies get the same rules.
|
|
262
|
+
*
|
|
263
|
+
* Two seats with equal content hash equal; one character more hashes differently. Browser and Node: the
|
|
264
|
+
* digest is Web Crypto's.
|
|
265
|
+
*/
|
|
266
|
+
/** Never content: the collab stamps, revision counters, seat-local view state. */
|
|
267
|
+
declare const DOC_LOCAL_KEYS: string[];
|
|
268
|
+
/** The canonical Docs snapshot: content only, keys sorted, runs normalised, local fields removed. */
|
|
269
|
+
declare function canonicalDocsSnapshot(snapshot: unknown): unknown;
|
|
270
|
+
/** The canonical form as the string the hash digests (tests compare these directly). */
|
|
271
|
+
declare const canonicalDocsString: (snapshot: unknown) => string;
|
|
272
|
+
/** SHA-256 hex of the canonical snapshot (§7.2). */
|
|
273
|
+
declare function docsStateHash(snapshot: unknown): Promise<string>;
|
|
274
|
+
/**
|
|
275
|
+
* Ids every seat agrees on for a snapshot that lacks them (C0 surprise 5): a raw snapshot (an import, markdown)
|
|
276
|
+
* loaded on two seats would otherwise get two random `sectionId`s / `paragraphId`s. Missing ids become
|
|
277
|
+
* positional ones; ids already present are kept. Applied to every snapshot a room loads.
|
|
278
|
+
*/
|
|
279
|
+
declare function stampDeterministicIds<T>(snapshot: T): T;
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* The Docs co-editing room on the ordered log (COLLAB-DESIGN §6, unit C3): the `OpDocumentAdapter` for
|
|
283
|
+
* `openOpSession` over a document's editors, plus saves, restore and the saver choice.
|
|
284
|
+
*
|
|
285
|
+
* - CAPTURE (§6.3). The document model hands over every local `doc.mutation.rich-text-editing` with the applied
|
|
286
|
+
* params (the handler's own rewrite, C0 surprise 6), the pre-state cloned in `beforeCommandExecuted`, and the
|
|
287
|
+
* outermost command id. `onlyLocal` / `fromCollab` never arrive here (the model filters them). Only `segmentId`
|
|
288
|
+
* and `actions` travel; the inverse is computed from the APPLIED actions on a COPY (`invertWithDoc` mutates its
|
|
289
|
+
* op). Each mutation goes to the session as it happens, so a remote apply can never land between a local
|
|
290
|
+
* mutation and its hand-over; the command id rides along as the change's label.
|
|
291
|
+
* - APPLY (§6.7). `syncExecuteCommand(id, { unitId: own, segmentId, actions, textRanges: null,
|
|
292
|
+
* noNeedSetTextRange: true }, { onlyLocal: true, fromCollab: true })` through the model (every view of the
|
|
293
|
+
* document in this tab). A throw, `false`, or actions the handler emptied fail the apply: the session rebuilds,
|
|
294
|
+
* never skips. While an IME composition is open, remote applies QUEUE (acks continue); a local op captured
|
|
295
|
+
* meanwhile is rebased over the queue (`L' = T(L, Q, 'left')`, `Q' = T(Q, L, 'right')`), and the queue drains
|
|
296
|
+
* at `compositionend`.
|
|
297
|
+
* - UNDO (§6.9). Each applied remote op rebases every stored undo and redo item of this seat
|
|
298
|
+
* (`T(item, R1, 'left')`); `textRanges` and `layoutState` are stripped from rebased items; an item whose rebase
|
|
299
|
+
* is a barrier, throws or comes back empty is dropped. Undo therefore only ever reverts this person's changes.
|
|
300
|
+
* - SAVES (§7.3). Only a quiet seat whose hash matches every peer at its rev saves, and only the chosen saver
|
|
301
|
+
* (the lowest client id among seats publishing a quiet hash) saves on its own; a manual save always tries.
|
|
302
|
+
* - RESTORE (§20). A whole-document replace goes through `collab_replace`: the snapshot is loaded into this seat's
|
|
303
|
+
* editor first, so the stored snapshot and its hash are the Univer-normalised form every seat will load.
|
|
304
|
+
*/
|
|
305
|
+
|
|
306
|
+
declare const RICH_TEXT_EDITING = "doc.mutation.rich-text-editing";
|
|
307
|
+
/**
|
|
308
|
+
* The room's protocol (§6.4): Univer 1.0.4 + transformer `DOCS_TRANSFORM_VERSION`. Any Univer or transformer
|
|
309
|
+
* change raises it; a room at another proto opens read-only for this build.
|
|
310
|
+
*/
|
|
311
|
+
declare const DOCS_COLLAB_PROTO = 1;
|
|
312
|
+
/** One local mutation as the document model hands it over. */
|
|
313
|
+
interface DocsLocalMutation {
|
|
314
|
+
id: string;
|
|
315
|
+
params: unknown;
|
|
316
|
+
/** The document before this mutation (cloned in `beforeCommandExecuted`), when the view could supply it. */
|
|
317
|
+
pre: unknown | null;
|
|
318
|
+
/** The outermost command this mutation belongs to (the depth tracker with its try/finally guard). */
|
|
319
|
+
cmd: string | null;
|
|
320
|
+
/** Whether the mutation changes what a snapshot stores. */
|
|
321
|
+
content: boolean;
|
|
322
|
+
}
|
|
323
|
+
/** What the document model lends the room (implemented by `DocumentModel`). */
|
|
324
|
+
interface DocsOtPort {
|
|
325
|
+
onLocal(listener: (m: DocsLocalMutation) => void): () => void;
|
|
326
|
+
/** Apply one committed mutation to every view of the document; throws when any view could not. */
|
|
327
|
+
applyRemote(id: string, params: Record<string, unknown>): void;
|
|
328
|
+
/** The document as the editor holds it now (`doc.save()`), or null when no view is mounted. */
|
|
329
|
+
snapshot(): unknown | null;
|
|
330
|
+
/** Replace the document in every view (and the kept editor). */
|
|
331
|
+
load(snapshot: unknown): void;
|
|
332
|
+
/** Univer's undo/redo service of each mounted view and the unit it holds. */
|
|
333
|
+
undoTargets(): Array<{
|
|
334
|
+
service: unknown;
|
|
335
|
+
unitId: string;
|
|
336
|
+
}>;
|
|
337
|
+
/** Ask for a save (the record's one coalesced save). */
|
|
338
|
+
markDirty(): void;
|
|
339
|
+
}
|
|
340
|
+
type DocsOtNotice = OpSessionNotice<DocsOp> | {
|
|
341
|
+
kind: "not_shared";
|
|
342
|
+
mutationId: string;
|
|
343
|
+
} | {
|
|
344
|
+
kind: "not_live";
|
|
345
|
+
};
|
|
346
|
+
interface OpenDocsOtRoomOptions {
|
|
347
|
+
port: DocsOtPort;
|
|
348
|
+
transport: OpTransport;
|
|
349
|
+
clientId: string;
|
|
350
|
+
presence?: OpPresence;
|
|
351
|
+
/** `collab_replace` for this room (restore, whole import). */
|
|
352
|
+
replace: (req: {
|
|
353
|
+
snapshot: unknown;
|
|
354
|
+
stateHash: string;
|
|
355
|
+
clientId: string;
|
|
356
|
+
}) => Promise<{
|
|
357
|
+
rev: number;
|
|
358
|
+
ref: string;
|
|
359
|
+
}>;
|
|
360
|
+
onNotice?: (n: DocsOtNotice) => void;
|
|
361
|
+
onIncident?: (i: OpSessionIncident) => void;
|
|
362
|
+
onStatus?: (s: OpSessionStatus) => void;
|
|
363
|
+
/** Watch IME composition here (default: `document`). */
|
|
364
|
+
compositionTarget?: Pick<Document, "addEventListener" | "removeEventListener"> | null;
|
|
365
|
+
}
|
|
366
|
+
interface DocsOtRoom {
|
|
367
|
+
session: OpSession<DocsOp>;
|
|
368
|
+
/** The record's save: a checkpoint at a quiet point (§7.3). False when this seat does not write now. */
|
|
369
|
+
write(reason: "manual" | string): Promise<boolean>;
|
|
370
|
+
/** Restore / whole import through `collab_replace` (§20). */
|
|
371
|
+
replace(snapshot: unknown): Promise<void>;
|
|
372
|
+
/** This seat is the one that saves on its own. */
|
|
373
|
+
isSaver(): boolean;
|
|
374
|
+
stop(): void;
|
|
375
|
+
}
|
|
376
|
+
interface UndoItem {
|
|
377
|
+
unitID?: string;
|
|
378
|
+
undoMutations?: Array<{
|
|
379
|
+
id: string;
|
|
380
|
+
params: Record<string, unknown>;
|
|
381
|
+
}>;
|
|
382
|
+
redoMutations?: Array<{
|
|
383
|
+
id: string;
|
|
384
|
+
params: Record<string, unknown>;
|
|
385
|
+
}>;
|
|
386
|
+
}
|
|
387
|
+
/** The Univer 1.0.4 fields the undo rebase reads; a forcing test fails if they move (Univer is pinned). */
|
|
388
|
+
declare function undoStacksOf(service: unknown): {
|
|
389
|
+
undo: Map<string, UndoItem[]>;
|
|
390
|
+
redo: Map<string, UndoItem[]>;
|
|
391
|
+
} | null;
|
|
392
|
+
/** §6.9: rebase one stack over a remote op; items that cannot follow are dropped. Returns how many were dropped. */
|
|
393
|
+
declare function rebaseUndoStack(items: UndoItem[], remote: DocsOp): number;
|
|
394
|
+
declare function openDocsOtRoom(o: OpenDocsOtRoomOptions): DocsOtRoom;
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* THE DOCUMENT MODEL — the engine of one cloud document per tab.
|
|
398
|
+
*
|
|
399
|
+
* Before this, the document lived only inside each `DocumentEditor`'s Univer
|
|
400
|
+
* instance. A remount (a board tile waking, a tile removed and undone, a route
|
|
401
|
+
* change and back) rebuilt the editor from the SERVER copy, so whatever the
|
|
402
|
+
* last save had not carried yet was gone; two mounts of one document were two
|
|
403
|
+
* editors, each saving full snapshots of its own copy (last write wins), each
|
|
404
|
+
* with its own realtime channel and its own collab room.
|
|
405
|
+
*
|
|
406
|
+
* The document is a record on THE one working-copy primitive
|
|
407
|
+
* (`@ai-matrx/kit/working-copy`, kind `udt_document`,
|
|
408
|
+
* `./documentModels.ts`): its save status, dirty and view count are Redux state
|
|
409
|
+
* (`workingCopies["udt_document:<id>"]`), its one coalesced save is the kind's.
|
|
410
|
+
* What Redux cannot hold — Univer's body — is this engine, registered on the
|
|
411
|
+
* record's session under the same key:
|
|
412
|
+
*
|
|
413
|
+
* - BODY. Every local mutation in one view is replayed into every other view
|
|
414
|
+
* of the document (the same mutation stream collab sends between machines),
|
|
415
|
+
* so two views show one document. A new view boots from the engine's latest
|
|
416
|
+
* state — a live view's snapshot, or the snapshot the last view left behind
|
|
417
|
+
* — never the stale server copy while the record's session is alive.
|
|
418
|
+
* - SIDE EFFECTS, ONCE. The snapshot realtime channel, the collab room, and
|
|
419
|
+
* the pagehide / visibility / beforeunload flush are opened once per
|
|
420
|
+
* document and closed once.
|
|
421
|
+
* - THE EDITOR ITSELF, KEPT. Univer keeps its undo / redo history inside the
|
|
422
|
+
* editor instance, so rebuilding the editor on a remount threw the history
|
|
423
|
+
* away. When the last view leaves, its editor (a Univer instance with its
|
|
424
|
+
* own DOM host) is parked here — off the page, alive — and the next view of
|
|
425
|
+
* the document RE-ATTACHES that same instance to its own container instead
|
|
426
|
+
* of booting a new one (`parkEditor` / `takeParkedEditor`). Undo and redo
|
|
427
|
+
* therefore work across hide / show, a remount and a removed tile undone,
|
|
428
|
+
* for as long as the record's session is warm; `close()` disposes it.
|
|
429
|
+
*/
|
|
430
|
+
|
|
431
|
+
type DocumentSnapshotData = Partial<IDocumentData>;
|
|
432
|
+
/** The document's room on the ordered log (COLLAB-DESIGN §6, C3), bound through `startOtRoom`. */
|
|
433
|
+
interface DocumentOtHandle {
|
|
434
|
+
stop: () => void;
|
|
435
|
+
write: (reason: CommitReason) => Promise<boolean>;
|
|
436
|
+
replace: (snapshot: DocumentSnapshotData) => Promise<void>;
|
|
437
|
+
isSaver: () => boolean;
|
|
438
|
+
}
|
|
439
|
+
type DocumentOtFactory = (port: DocsOtPort, onAwareness: (awareness: DocumentAwareness) => void) => Promise<DocumentOtHandle | null>;
|
|
440
|
+
/** Who else is in the document's room (shared by every view of it). */
|
|
441
|
+
interface DocumentAwareness {
|
|
442
|
+
states: Map<number, AwarenessState>;
|
|
443
|
+
selfUid: string;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/** `collab.ot_rooms` (§12.2): which documents without a room open one. */
|
|
447
|
+
type OtRoomsKnob = "off" | "new" | "all";
|
|
448
|
+
/** Whether this document opens on the ordered log: its room exists, or the knob opens one. */
|
|
449
|
+
declare function documentUsesOrderedRoom(documentId: string, knob: OtRoomsKnob, isNewDocument: () => Promise<boolean>): Promise<boolean>;
|
|
450
|
+
interface DocumentOtFactoryOptions {
|
|
451
|
+
documentId: string;
|
|
452
|
+
uid: string;
|
|
453
|
+
/** One per open tab; never reused. */
|
|
454
|
+
clientId: string;
|
|
455
|
+
displayName?: string;
|
|
456
|
+
readAntiEntropyIntervalMs?: () => Promise<number>;
|
|
457
|
+
onNotice?: (n: DocsOtNotice) => void;
|
|
458
|
+
onIncident?: (i: OpSessionIncident) => void;
|
|
459
|
+
onStatus?: (s: OpSessionStatus) => void;
|
|
460
|
+
}
|
|
461
|
+
/** The `DocumentModel.startOtRoom` factory for one document. */
|
|
462
|
+
declare function documentOtFactory(o: DocumentOtFactoryOptions): DocumentOtFactory;
|
|
463
|
+
|
|
464
|
+
export { COLLAB_COVERED_KEY, type CollabBase, type CollabMutationInfo, type CollabProviderLike, type CommandServiceLike, DOCS_COLLAB_PROTO, DOCS_TRANSFORM_VERSION, DOC_LOCAL_KEYS, type DocsLocalMutation, type DocsLost, type DocsOp, type DocsOtNotice, type DocsOtPort, type DocsOtRoom, type DocsTransformResult, type DocumentOtFactoryOptions, type OpenDocsOtRoomOptions, type OtRoomsKnob, RICH_TEXT_EDITING, SENDER_VIEW_PARAMS, WorkbookCollabSession, type WorkbookCollabSessionOptions, applyDocsOp, canonicalDocsSnapshot, canonicalDocsString, composeDocsOps, docsStateHash, documentOtFactory, documentUsesOrderedRoom, dropEmptyStyleRetains, invertDocsOp, isCellEditorScratch, isNoopDocsOp, normalizeDocsOp, openDocsOtRoom, rebaseUndoStack, stampCovered, stampDeterministicIds, stripSenderView, takeCovered, transformDocsOp, transformTextX, undoStacksOf };
|