@ai-matrx/office 0.8.23 → 0.8.24

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.
@@ -1,6 +1,6 @@
1
1
  import * as Y from 'yjs';
2
2
  import { Awareness } from 'y-protocols/awareness';
3
- import { OpSessionNotice, OpSession, OpTransport, OpPresence, OpSessionIncident, OpSessionStatus, AwarenessState, CollabRpcClient, collabReplace, OpDocumentAdapter } from '@ai-matrx/data/realtime/collab';
3
+ import { OpSessionNotice, OpSession, OpTransport, OpPresence, OpSessionIncident, OpSessionStatus, OpOfflineOptions, OpOfflineState, AwarenessState, CollabRpcClient, collabReplace, OpDocumentAdapter, CollabGroup } from '@ai-matrx/data/realtime/collab';
4
4
  import { IDocumentData } from '@univerjs/core';
5
5
  import { CommitReason } from '@ai-matrx/kit/working-copy';
6
6
 
@@ -362,6 +362,10 @@ interface OpenDocsOtRoomOptions {
362
362
  onNotice?: (n: DocsOtNotice) => void;
363
363
  onIncident?: (i: OpSessionIncident) => void;
364
364
  onStatus?: (s: OpSessionStatus) => void;
365
+ /** C10: unsent edits persist on this device under a lease (the host builds it: `browserOfflineOptions`). */
366
+ offline?: OpOfflineOptions | null;
367
+ /** C10: the chrome's offline state. */
368
+ onOffline?: (s: OpOfflineState) => void;
365
369
  /** Watch IME composition here (default: `document`). */
366
370
  compositionTarget?: Pick<Document, "addEventListener" | "removeEventListener"> | null;
367
371
  }
@@ -481,6 +485,8 @@ interface DocumentOtFactoryOptions {
481
485
  onNotice?: (n: DocsOtNotice) => void;
482
486
  onIncident?: (i: OpSessionIncident) => void;
483
487
  onStatus?: (s: OpSessionStatus) => void;
488
+ /** C10: the chrome's offline state (unsent edits are kept on this device, one tab per document and person owns them). */
489
+ onOffline?: (s: OpOfflineState) => void;
484
490
  }
485
491
  /** The `DocumentModel.startOtRoom` factory for one document. */
486
492
  declare function documentOtFactory(o: DocumentOtFactoryOptions): DocumentOtFactory;
@@ -683,6 +689,10 @@ interface OpenSheetsOtRoomOptions {
683
689
  onNotice?: (n: SheetsOtNotice) => void;
684
690
  onIncident?: (i: OpSessionIncident) => void;
685
691
  onStatus?: (s: OpSessionStatus) => void;
692
+ /** C10: unsent edits persist on this device under a lease (the host builds it: `browserOfflineOptions`). */
693
+ offline?: OpOfflineOptions | null;
694
+ /** C10: the chrome's offline state. */
695
+ onOffline?: (s: OpOfflineState) => void;
686
696
  }
687
697
  interface SheetsOtRoom {
688
698
  session: OpSession<SheetOp>;
@@ -821,6 +831,15 @@ declare const canonicalSheetString: (snapshot: unknown) => string;
821
831
  /** SHA-256 (hex) of the canonical form. Web Crypto, so the browser and Node compute the same hash. */
822
832
  declare function sheetsStateHash(snapshot: unknown): Promise<string>;
823
833
 
834
+ /**
835
+ * "Copy my offline text" (C10): the words a person typed while offline, pulled out of the wire groups of a buffer
836
+ * the room refused (changed protocol, document trashed). Format-light on purpose: Docs inserts carry their text in
837
+ * `body.dataStream`; Sheets writes carry cell values in `cellValue`. Anything else contributes nothing, and the
838
+ * caller says so when nothing could be read (the record stays on the device regardless).
839
+ */
840
+
841
+ declare function offlineTextOfGroups(buffers: CollabGroup[][]): string;
842
+
824
843
  /** `collab.ot_rooms` (§12.2): which workbooks without a room open one. */
825
844
  type WorkbookOtRoomsKnob = "off" | "new" | "all";
826
845
  /** Whether this workbook opens on the ordered log: its room exists, or the knob opens one. */
@@ -836,6 +855,8 @@ interface WorkbookOtRoomOptions {
836
855
  onNotice?: (n: SheetsOtNotice) => void;
837
856
  onIncident?: (i: OpSessionIncident) => void;
838
857
  onStatus?: (s: OpSessionStatus) => void;
858
+ /** C10: the chrome's offline state (unsent edits are kept on this device, one tab per workbook and person owns them). */
859
+ onOffline?: (s: OpOfflineState) => void;
839
860
  }
840
861
  interface WorkbookOtRoomHandle {
841
862
  session: OpSession<SheetOp>;
@@ -857,4 +878,4 @@ declare function startWorkbookOtRoom(port: SheetsOtPort & {
857
878
  dispose?: () => void;
858
879
  }, o: WorkbookOtRoomOptions): Promise<WorkbookOtRoomHandle>;
859
880
 
860
- 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 DocumentRestoreDeps, type DocumentRestoreResult, type OpenDocsOtRoomOptions, type OpenSheetsOtRoomOptions, type OtRoomsKnob, RICH_TEXT_EDITING, SENDER_VIEW_PARAMS, SHEETS_COLLAB_PROTO, SHEETS_EDIT_GROUP, type SheetOp, type SheetSelection, type SheetsCommandService, type SheetsLocalEvent, type SheetsLocalGroup, type SheetsNotShared, type SheetsOtAdapter, type SheetsOtAdapterHooks, type SheetsOtNotice, type SheetsOtPort, type SheetsOtRoom, type SheetsWorkbookLike, type SheetsWorksheetLike, type UniverSheetsOtPortOptions, WorkbookCollabSession, type WorkbookCollabSessionOptions, type WorkbookOtRoomHandle, type WorkbookOtRoomOptions, type WorkbookOtRoomsKnob, applyDocsOp, asLoadedDocsSnapshot, canonicalDocsSnapshot, canonicalDocsString, canonicalKeys, canonicalSheet, canonicalSheetString, composeDocsOps, createSheetsOtAdapter, decodeSheetOp, docsStateHash, documentOtFactory, documentUsesOrderedRoom, dropEmptyStyleRetains, encodeSheetOp, invertDocsOp, isCellEditorScratch, isNoopDocsOp, moveSelectionsThrough, normalizeDocsOp, openDocsOtRoom, openSheetsOtRoom, rebaseSheetUndoStack, rebaseUndoStack, restoreDocumentSnapshot, sheetsStateHash, stampCovered, stampDeterministicIds, startWorkbookOtRoom, stripSenderView, takeCovered, transformDocsOp, transformSheetOp, transformTextX, undoStacksOf, univerSheetsOtPort, workbookUsesOrderedRoom };
881
+ 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 DocumentRestoreDeps, type DocumentRestoreResult, type OpenDocsOtRoomOptions, type OpenSheetsOtRoomOptions, type OtRoomsKnob, RICH_TEXT_EDITING, SENDER_VIEW_PARAMS, SHEETS_COLLAB_PROTO, SHEETS_EDIT_GROUP, type SheetOp, type SheetSelection, type SheetsCommandService, type SheetsLocalEvent, type SheetsLocalGroup, type SheetsNotShared, type SheetsOtAdapter, type SheetsOtAdapterHooks, type SheetsOtNotice, type SheetsOtPort, type SheetsOtRoom, type SheetsWorkbookLike, type SheetsWorksheetLike, type UniverSheetsOtPortOptions, WorkbookCollabSession, type WorkbookCollabSessionOptions, type WorkbookOtRoomHandle, type WorkbookOtRoomOptions, type WorkbookOtRoomsKnob, applyDocsOp, asLoadedDocsSnapshot, canonicalDocsSnapshot, canonicalDocsString, canonicalKeys, canonicalSheet, canonicalSheetString, composeDocsOps, createSheetsOtAdapter, decodeSheetOp, docsStateHash, documentOtFactory, documentUsesOrderedRoom, dropEmptyStyleRetains, encodeSheetOp, invertDocsOp, isCellEditorScratch, isNoopDocsOp, moveSelectionsThrough, normalizeDocsOp, offlineTextOfGroups, openDocsOtRoom, openSheetsOtRoom, rebaseSheetUndoStack, rebaseUndoStack, restoreDocumentSnapshot, sheetsStateHash, stampCovered, stampDeterministicIds, startWorkbookOtRoom, stripSenderView, takeCovered, transformDocsOp, transformSheetOp, transformTextX, undoStacksOf, univerSheetsOtPort, workbookUsesOrderedRoom };
@@ -1,6 +1,6 @@
1
1
  import * as Y from 'yjs';
2
2
  import { Awareness } from 'y-protocols/awareness';
3
- import { OpSessionNotice, OpSession, OpTransport, OpPresence, OpSessionIncident, OpSessionStatus, AwarenessState, CollabRpcClient, collabReplace, OpDocumentAdapter } from '@ai-matrx/data/realtime/collab';
3
+ import { OpSessionNotice, OpSession, OpTransport, OpPresence, OpSessionIncident, OpSessionStatus, OpOfflineOptions, OpOfflineState, AwarenessState, CollabRpcClient, collabReplace, OpDocumentAdapter, CollabGroup } from '@ai-matrx/data/realtime/collab';
4
4
  import { IDocumentData } from '@univerjs/core';
5
5
  import { CommitReason } from '@ai-matrx/kit/working-copy';
6
6
 
@@ -362,6 +362,10 @@ interface OpenDocsOtRoomOptions {
362
362
  onNotice?: (n: DocsOtNotice) => void;
363
363
  onIncident?: (i: OpSessionIncident) => void;
364
364
  onStatus?: (s: OpSessionStatus) => void;
365
+ /** C10: unsent edits persist on this device under a lease (the host builds it: `browserOfflineOptions`). */
366
+ offline?: OpOfflineOptions | null;
367
+ /** C10: the chrome's offline state. */
368
+ onOffline?: (s: OpOfflineState) => void;
365
369
  /** Watch IME composition here (default: `document`). */
366
370
  compositionTarget?: Pick<Document, "addEventListener" | "removeEventListener"> | null;
367
371
  }
@@ -481,6 +485,8 @@ interface DocumentOtFactoryOptions {
481
485
  onNotice?: (n: DocsOtNotice) => void;
482
486
  onIncident?: (i: OpSessionIncident) => void;
483
487
  onStatus?: (s: OpSessionStatus) => void;
488
+ /** C10: the chrome's offline state (unsent edits are kept on this device, one tab per document and person owns them). */
489
+ onOffline?: (s: OpOfflineState) => void;
484
490
  }
485
491
  /** The `DocumentModel.startOtRoom` factory for one document. */
486
492
  declare function documentOtFactory(o: DocumentOtFactoryOptions): DocumentOtFactory;
@@ -683,6 +689,10 @@ interface OpenSheetsOtRoomOptions {
683
689
  onNotice?: (n: SheetsOtNotice) => void;
684
690
  onIncident?: (i: OpSessionIncident) => void;
685
691
  onStatus?: (s: OpSessionStatus) => void;
692
+ /** C10: unsent edits persist on this device under a lease (the host builds it: `browserOfflineOptions`). */
693
+ offline?: OpOfflineOptions | null;
694
+ /** C10: the chrome's offline state. */
695
+ onOffline?: (s: OpOfflineState) => void;
686
696
  }
687
697
  interface SheetsOtRoom {
688
698
  session: OpSession<SheetOp>;
@@ -821,6 +831,15 @@ declare const canonicalSheetString: (snapshot: unknown) => string;
821
831
  /** SHA-256 (hex) of the canonical form. Web Crypto, so the browser and Node compute the same hash. */
822
832
  declare function sheetsStateHash(snapshot: unknown): Promise<string>;
823
833
 
834
+ /**
835
+ * "Copy my offline text" (C10): the words a person typed while offline, pulled out of the wire groups of a buffer
836
+ * the room refused (changed protocol, document trashed). Format-light on purpose: Docs inserts carry their text in
837
+ * `body.dataStream`; Sheets writes carry cell values in `cellValue`. Anything else contributes nothing, and the
838
+ * caller says so when nothing could be read (the record stays on the device regardless).
839
+ */
840
+
841
+ declare function offlineTextOfGroups(buffers: CollabGroup[][]): string;
842
+
824
843
  /** `collab.ot_rooms` (§12.2): which workbooks without a room open one. */
825
844
  type WorkbookOtRoomsKnob = "off" | "new" | "all";
826
845
  /** Whether this workbook opens on the ordered log: its room exists, or the knob opens one. */
@@ -836,6 +855,8 @@ interface WorkbookOtRoomOptions {
836
855
  onNotice?: (n: SheetsOtNotice) => void;
837
856
  onIncident?: (i: OpSessionIncident) => void;
838
857
  onStatus?: (s: OpSessionStatus) => void;
858
+ /** C10: the chrome's offline state (unsent edits are kept on this device, one tab per workbook and person owns them). */
859
+ onOffline?: (s: OpOfflineState) => void;
839
860
  }
840
861
  interface WorkbookOtRoomHandle {
841
862
  session: OpSession<SheetOp>;
@@ -857,4 +878,4 @@ declare function startWorkbookOtRoom(port: SheetsOtPort & {
857
878
  dispose?: () => void;
858
879
  }, o: WorkbookOtRoomOptions): Promise<WorkbookOtRoomHandle>;
859
880
 
860
- 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 DocumentRestoreDeps, type DocumentRestoreResult, type OpenDocsOtRoomOptions, type OpenSheetsOtRoomOptions, type OtRoomsKnob, RICH_TEXT_EDITING, SENDER_VIEW_PARAMS, SHEETS_COLLAB_PROTO, SHEETS_EDIT_GROUP, type SheetOp, type SheetSelection, type SheetsCommandService, type SheetsLocalEvent, type SheetsLocalGroup, type SheetsNotShared, type SheetsOtAdapter, type SheetsOtAdapterHooks, type SheetsOtNotice, type SheetsOtPort, type SheetsOtRoom, type SheetsWorkbookLike, type SheetsWorksheetLike, type UniverSheetsOtPortOptions, WorkbookCollabSession, type WorkbookCollabSessionOptions, type WorkbookOtRoomHandle, type WorkbookOtRoomOptions, type WorkbookOtRoomsKnob, applyDocsOp, asLoadedDocsSnapshot, canonicalDocsSnapshot, canonicalDocsString, canonicalKeys, canonicalSheet, canonicalSheetString, composeDocsOps, createSheetsOtAdapter, decodeSheetOp, docsStateHash, documentOtFactory, documentUsesOrderedRoom, dropEmptyStyleRetains, encodeSheetOp, invertDocsOp, isCellEditorScratch, isNoopDocsOp, moveSelectionsThrough, normalizeDocsOp, openDocsOtRoom, openSheetsOtRoom, rebaseSheetUndoStack, rebaseUndoStack, restoreDocumentSnapshot, sheetsStateHash, stampCovered, stampDeterministicIds, startWorkbookOtRoom, stripSenderView, takeCovered, transformDocsOp, transformSheetOp, transformTextX, undoStacksOf, univerSheetsOtPort, workbookUsesOrderedRoom };
881
+ 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 DocumentRestoreDeps, type DocumentRestoreResult, type OpenDocsOtRoomOptions, type OpenSheetsOtRoomOptions, type OtRoomsKnob, RICH_TEXT_EDITING, SENDER_VIEW_PARAMS, SHEETS_COLLAB_PROTO, SHEETS_EDIT_GROUP, type SheetOp, type SheetSelection, type SheetsCommandService, type SheetsLocalEvent, type SheetsLocalGroup, type SheetsNotShared, type SheetsOtAdapter, type SheetsOtAdapterHooks, type SheetsOtNotice, type SheetsOtPort, type SheetsOtRoom, type SheetsWorkbookLike, type SheetsWorksheetLike, type UniverSheetsOtPortOptions, WorkbookCollabSession, type WorkbookCollabSessionOptions, type WorkbookOtRoomHandle, type WorkbookOtRoomOptions, type WorkbookOtRoomsKnob, applyDocsOp, asLoadedDocsSnapshot, canonicalDocsSnapshot, canonicalDocsString, canonicalKeys, canonicalSheet, canonicalSheetString, composeDocsOps, createSheetsOtAdapter, decodeSheetOp, docsStateHash, documentOtFactory, documentUsesOrderedRoom, dropEmptyStyleRetains, encodeSheetOp, invertDocsOp, isCellEditorScratch, isNoopDocsOp, moveSelectionsThrough, normalizeDocsOp, offlineTextOfGroups, openDocsOtRoom, openSheetsOtRoom, rebaseSheetUndoStack, rebaseUndoStack, restoreDocumentSnapshot, sheetsStateHash, stampCovered, stampDeterministicIds, startWorkbookOtRoom, stripSenderView, takeCovered, transformDocsOp, transformSheetOp, transformTextX, undoStacksOf, univerSheetsOtPort, workbookUsesOrderedRoom };
@@ -1290,7 +1290,9 @@ function openDocsOtRoom(o) {
1290
1290
  ...o.presence ? { presence: o.presence } : {},
1291
1291
  onNotice: (n) => notice(n),
1292
1292
  ...o.onIncident ? { onIncident: o.onIncident } : {},
1293
- ...o.onStatus ? { onStatus: o.onStatus } : {}
1293
+ ...o.onStatus ? { onStatus: o.onStatus } : {},
1294
+ ...o.offline ? { offline: o.offline } : {},
1295
+ ...o.onOffline ? { onOffline: o.onOffline } : {}
1294
1296
  });
1295
1297
  const isSaver = () => {
1296
1298
  if (!o.presence) return true;
@@ -1328,6 +1330,7 @@ import * as Y2 from "yjs";
1328
1330
  import { Awareness as Awareness2 } from "y-protocols/awareness";
1329
1331
  import {
1330
1332
  awarenessOpPresence,
1333
+ browserOfflineOptions,
1331
1334
  claimCollaboratorColor as claimCollaboratorColor2,
1332
1335
  collabReplace,
1333
1336
  collabRoomExists,
@@ -1565,7 +1568,9 @@ function documentOtFactory(o) {
1565
1568
  replace: ({ snapshot, stateHash, clientId }) => collabReplace(client, { roomId: o.documentId, snapshot, stateHash, origin: "restore", clientId }),
1566
1569
  ...o.onNotice ? { onNotice: o.onNotice } : {},
1567
1570
  ...o.onIncident ? { onIncident: o.onIncident } : {},
1568
- ...o.onStatus ? { onStatus: o.onStatus } : {}
1571
+ ...o.onStatus ? { onStatus: o.onStatus } : {},
1572
+ offline: browserOfflineOptions(`${o.documentId}:${o.uid}`),
1573
+ ...o.onOffline ? { onOffline: o.onOffline } : {}
1569
1574
  });
1570
1575
  await room.session.ready;
1571
1576
  let lastKey = "";
@@ -3629,7 +3634,9 @@ function openSheetsOtRoom(o) {
3629
3634
  ...o.presence ? { presence: o.presence } : {},
3630
3635
  onNotice: (n) => notice(n),
3631
3636
  ...o.onIncident ? { onIncident: o.onIncident } : {},
3632
- ...o.onStatus ? { onStatus: o.onStatus } : {}
3637
+ ...o.onStatus ? { onStatus: o.onStatus } : {},
3638
+ ...o.offline ? { offline: o.offline } : {},
3639
+ ...o.onOffline ? { onOffline: o.onOffline } : {}
3633
3640
  });
3634
3641
  const isSaver = () => {
3635
3642
  if (!o.presence) return true;
@@ -3660,6 +3667,65 @@ function openSheetsOtRoom(o) {
3660
3667
  };
3661
3668
  }
3662
3669
 
3670
+ // src/univer/collab/sheets-ot/plugin-teardown.ts
3671
+ var perSheetList = (data) => Object.entries(data).flatMap(([sheet, list]) => Array.isArray(list) ? [[sheet, list.filter((x) => !!x && typeof x === "object")]] : []);
3672
+ var PLUGIN_TEARDOWNS = [
3673
+ {
3674
+ resource: "SHEET_CONDITIONAL_FORMATTING_PLUGIN",
3675
+ removals: (unitId, data) => perSheetList(data).flatMap(
3676
+ ([subUnitId, rules]) => rules.filter((r) => typeof r.cfId === "string").map((r) => ({ id: "sheet.mutation.delete-conditional-rule", params: { unitId, subUnitId, cfId: r.cfId } }))
3677
+ )
3678
+ },
3679
+ {
3680
+ resource: "SHEET_DATA_VALIDATION_PLUGIN",
3681
+ removals: (unitId, data) => perSheetList(data).flatMap(([subUnitId, rules]) => {
3682
+ const ids = rules.map((r) => r.uid).filter((u) => typeof u === "string");
3683
+ return ids.length ? [{ id: "data-validation.mutation.removeRule", params: { unitId, subUnitId, ruleId: ids, source: "command" } }] : [];
3684
+ })
3685
+ },
3686
+ {
3687
+ resource: "SHEET_NOTE_PLUGIN",
3688
+ removals: (unitId, data) => Object.entries(data).flatMap(
3689
+ ([sheetId, rows]) => Object.entries(rows ?? {}).flatMap(
3690
+ ([row, cols]) => Object.entries(cols ?? {}).map(([col, note]) => ({
3691
+ id: "sheet.mutation.remove-note",
3692
+ params: { unitId, sheetId, row: Number(row), col: Number(col), ...note && typeof note.id === "string" ? { noteId: note.id } : {}, silent: true }
3693
+ }))
3694
+ )
3695
+ )
3696
+ }
3697
+ ];
3698
+ function resourcesOf(saved) {
3699
+ const out = /* @__PURE__ */ new Map();
3700
+ const list = saved?.resources;
3701
+ for (const r of Array.isArray(list) ? list : []) {
3702
+ if (!r?.name) continue;
3703
+ try {
3704
+ const data = typeof r.data === "string" ? r.data ? JSON.parse(r.data) : null : r.data;
3705
+ if (data && typeof data === "object") out.set(r.name, data);
3706
+ } catch {
3707
+ }
3708
+ }
3709
+ return out;
3710
+ }
3711
+ function releaseUnitPluginState(cs, unitId, saved) {
3712
+ const resources = resourcesOf(saved);
3713
+ let ran = 0;
3714
+ for (const t of PLUGIN_TEARDOWNS) {
3715
+ const data = resources.get(t.resource);
3716
+ if (!data) continue;
3717
+ for (const m of t.removals(unitId, data)) {
3718
+ try {
3719
+ cs.syncExecuteCommand(m.id, m.params, { onlyLocal: true });
3720
+ ran++;
3721
+ } catch (err) {
3722
+ console.warn(`[sheets-ot] could not release ${t.resource} state before a reload`, err);
3723
+ }
3724
+ }
3725
+ }
3726
+ return ran;
3727
+ }
3728
+
3663
3729
  // src/univer/collab/sheets-ot/univer-port.ts
3664
3730
  var COMMAND = 0;
3665
3731
  var MUTATION = 2;
@@ -3979,6 +4045,8 @@ function univerSheetsOtPort(o) {
3979
4045
  group = null;
3980
4046
  applying = true;
3981
4047
  try {
4048
+ const u = unitId();
4049
+ if (u) releaseUnitPluginState(cs, u, o.saveUnit?.(u) ?? null);
3982
4050
  o.load(snapshot);
3983
4051
  } finally {
3984
4052
  applying = false;
@@ -4068,11 +4136,59 @@ function moveSelectionsThrough(selections, m, maxRows, maxCols) {
4068
4136
  return selections.map((s) => ({ ...s, range: box(s.range, s.range.rangeType), ...s.primary ? { primary: box(s.primary) } : {} }));
4069
4137
  }
4070
4138
 
4139
+ // src/univer/collab/offline-text.ts
4140
+ var colName3 = (c) => {
4141
+ let n = c;
4142
+ let s = "";
4143
+ do {
4144
+ s = String.fromCharCode(65 + n % 26) + s;
4145
+ n = Math.floor(n / 26) - 1;
4146
+ } while (n >= 0);
4147
+ return s;
4148
+ };
4149
+ function collectDocText(v, out) {
4150
+ if (Array.isArray(v)) {
4151
+ for (const x of v) collectDocText(x, out);
4152
+ return;
4153
+ }
4154
+ if (!v || typeof v !== "object") return;
4155
+ const o = v;
4156
+ if (typeof o.dataStream === "string" && o.dataStream.length) out.push(o.dataStream);
4157
+ for (const k of Object.keys(o)) if (k !== "dataStream") collectDocText(o[k], out);
4158
+ }
4159
+ function collectCells(cellValue, out) {
4160
+ if (!cellValue || typeof cellValue !== "object") return;
4161
+ for (const [r, cols] of Object.entries(cellValue)) {
4162
+ if (!cols || typeof cols !== "object") continue;
4163
+ for (const [c, cell] of Object.entries(cols)) {
4164
+ const x = cell;
4165
+ const val = x?.f ?? x?.v;
4166
+ if (val !== void 0 && val !== null && val !== "") out.push(`${colName3(Number(c))}${Number(r) + 1}: ${String(val)}`);
4167
+ }
4168
+ }
4169
+ }
4170
+ function offlineTextOfGroups(buffers) {
4171
+ const docs = [];
4172
+ const cells = [];
4173
+ for (const groups of buffers) {
4174
+ for (const g of groups) {
4175
+ for (const m of g.mutations) {
4176
+ const p = m.params;
4177
+ if (p && "cellValue" in p) collectCells(p.cellValue, cells);
4178
+ else collectDocText(p, docs);
4179
+ }
4180
+ }
4181
+ }
4182
+ const text = docs.join("").replace(/\r/g, "\n").replace(/\u0000/g, "");
4183
+ return [text, cells.join("\n")].filter((s) => s.length > 0).join("\n");
4184
+ }
4185
+
4071
4186
  // src/sheets/workbook-ot-room.ts
4072
4187
  import * as Y3 from "yjs";
4073
4188
  import { Awareness as Awareness3 } from "y-protocols/awareness";
4074
4189
  import {
4075
4190
  awarenessOpPresence as awarenessOpPresence2,
4191
+ browserOfflineOptions as browserOfflineOptions2,
4076
4192
  claimCollaboratorColor as claimCollaboratorColor3,
4077
4193
  collabReplace as collabReplace3,
4078
4194
  collabRoomExists as collabRoomExists3,
@@ -4177,6 +4293,7 @@ async function startWorkbookOtRoom(port, o) {
4177
4293
  await provider.connect();
4178
4294
  publishAwareness();
4179
4295
  const client = db4();
4296
+ const offline = browserOfflineOptions2(`${o.workbookId}:${o.uid}`);
4180
4297
  const room = openSheetsOtRoom({
4181
4298
  port,
4182
4299
  clientId: o.clientId,
@@ -4197,7 +4314,9 @@ async function startWorkbookOtRoom(port, o) {
4197
4314
  replace: ({ snapshot, stateHash, clientId }) => collabReplace3(client, { roomId: o.workbookId, snapshot, stateHash, origin: "restore", clientId }),
4198
4315
  ...o.onNotice ? { onNotice: o.onNotice } : {},
4199
4316
  ...o.onIncident ? { onIncident: o.onIncident } : {},
4200
- ...o.onStatus ? { onStatus: o.onStatus } : {}
4317
+ ...o.onStatus ? { onStatus: o.onStatus } : {},
4318
+ ...offline ? { offline } : {},
4319
+ ...o.onOffline ? { onOffline: o.onOffline } : {}
4201
4320
  });
4202
4321
  await room.session.ready;
4203
4322
  return {
@@ -4250,6 +4369,7 @@ export {
4250
4369
  isNoopDocsOp,
4251
4370
  moveSelectionsThrough,
4252
4371
  normalizeDocsOp,
4372
+ offlineTextOfGroups,
4253
4373
  openDocsOtRoom,
4254
4374
  openSheetsOtRoom,
4255
4375
  rebaseSheetUndoStack,