@ai-matrx/office 0.8.25 → 0.8.27

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.
Files changed (48) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/{chunk-TRA2BTPL.js → chunk-CAWDEHVL.js} +20 -6
  3. package/dist/chunk-CAWDEHVL.js.map +1 -0
  4. package/dist/{chunk-ZDZCCOUB.cjs → chunk-IJNET5TT.cjs} +20 -6
  5. package/dist/chunk-IJNET5TT.cjs.map +1 -0
  6. package/dist/{chunk-JMVPAJJA.cjs → chunk-LHTG72LS.cjs} +20 -6
  7. package/dist/chunk-LHTG72LS.cjs.map +1 -0
  8. package/dist/{chunk-WKBWRG7N.js → chunk-RREOARR4.js} +20 -6
  9. package/dist/chunk-RREOARR4.js.map +1 -0
  10. package/dist/collab-sheets-transform.cjs +215 -14
  11. package/dist/collab-sheets-transform.cjs.map +1 -1
  12. package/dist/collab-sheets-transform.d.cts +7 -2
  13. package/dist/collab-sheets-transform.d.ts +7 -2
  14. package/dist/collab-sheets-transform.js +215 -14
  15. package/dist/collab-sheets-transform.js.map +1 -1
  16. package/dist/docs-host.cjs.map +1 -1
  17. package/dist/docs-host.d.cts +8 -0
  18. package/dist/docs-host.d.ts +8 -0
  19. package/dist/docs-host.js.map +1 -1
  20. package/dist/docs-io.cjs +6 -6
  21. package/dist/docs-io.js +1 -1
  22. package/dist/docs.cjs +36 -2
  23. package/dist/docs.cjs.map +1 -1
  24. package/dist/docs.d.cts +25 -3
  25. package/dist/docs.d.ts +25 -3
  26. package/dist/docs.js +35 -1
  27. package/dist/docs.js.map +1 -1
  28. package/dist/sheets-comment-anchors.cjs +44 -3
  29. package/dist/sheets-comment-anchors.cjs.map +1 -1
  30. package/dist/sheets-comment-anchors.js +42 -1
  31. package/dist/sheets-comment-anchors.js.map +1 -1
  32. package/dist/sheets-xlsx.cjs +6 -6
  33. package/dist/sheets-xlsx.js +1 -1
  34. package/dist/sheets.cjs +3 -3
  35. package/dist/sheets.d.cts +9 -1
  36. package/dist/sheets.d.ts +9 -1
  37. package/dist/sheets.js +1 -1
  38. package/dist/univer-collab.cjs +315 -37
  39. package/dist/univer-collab.cjs.map +1 -1
  40. package/dist/univer-collab.d.cts +9 -5
  41. package/dist/univer-collab.d.ts +9 -5
  42. package/dist/univer-collab.js +300 -18
  43. package/dist/univer-collab.js.map +1 -1
  44. package/package.json +2 -2
  45. package/dist/chunk-JMVPAJJA.cjs.map +0 -1
  46. package/dist/chunk-TRA2BTPL.js.map +0 -1
  47. package/dist/chunk-WKBWRG7N.js.map +0 -1
  48. package/dist/chunk-ZDZCCOUB.cjs.map +0 -1
@@ -474,7 +474,7 @@ interface DocumentAwareness {
474
474
  /** `collab.ot_rooms` (§12.2): which documents without a room open one. */
475
475
  type OtRoomsKnob = "off" | "new" | "all";
476
476
  /** Whether this document opens on the ordered log: its room exists, or the knob opens one. */
477
- declare function documentUsesOrderedRoom(documentId: string, knob: OtRoomsKnob, isNewDocument: () => Promise<boolean>): Promise<boolean>;
477
+ declare function documentUsesOrderedRoom(documentId: string, knob: OtRoomsKnob, isNewDocument: () => Promise<boolean>, offlineKey?: string): Promise<boolean>;
478
478
  interface DocumentOtFactoryOptions {
479
479
  documentId: string;
480
480
  uid: string;
@@ -569,7 +569,12 @@ type SheetOp = SheetMutation[] | null;
569
569
  * The room's protocol (§6.4): Univer 1.0.4 + transformer `SHEET_TRANSFORMER_VERSION`. Any Univer or transformer
570
570
  * change raises it; a room at another proto opens read-only for this build.
571
571
  */
572
- declare const SHEETS_COLLAB_PROTO = 2;
572
+ declare const SHEETS_COLLAB_PROTO = 3;
573
+ /**
574
+ * Every Sheets protocol this package still reads (the fold, the server): each proto's transforms are a superset of
575
+ * the one before, so a room still at an older protocol folds here. Raising the proto appends, never replaces.
576
+ */
577
+ declare const SHEETS_READABLE_PROTOS: readonly number[];
573
578
  declare const SHEETS_EDIT_GROUP = "sheets.edit";
574
579
  /** One outermost command's traveling mutations, as the port hands them over. */
575
580
  interface SheetsLocalGroup {
@@ -850,8 +855,7 @@ declare function offlineTextOfGroups(buffers: CollabGroup[][]): string;
850
855
 
851
856
  /** `collab.ot_rooms` (§12.2): which workbooks without a room open one. */
852
857
  type WorkbookOtRoomsKnob = "off" | "new" | "all";
853
- /** Whether this workbook opens on the ordered log: its room exists, or the knob opens one. */
854
- declare function workbookUsesOrderedRoom(workbookId: string, knob: WorkbookOtRoomsKnob, isNewWorkbook: () => Promise<boolean>): Promise<boolean>;
858
+ declare function workbookUsesOrderedRoom(workbookId: string, knob: WorkbookOtRoomsKnob, isNewWorkbook: () => Promise<boolean>, offlineKey?: string): Promise<boolean>;
855
859
  interface WorkbookOtRoomOptions {
856
860
  workbookId: string;
857
861
  uid: string;
@@ -886,4 +890,4 @@ declare function startWorkbookOtRoom(port: SheetsOtPort & {
886
890
  dispose?: () => void;
887
891
  }, o: WorkbookOtRoomOptions): Promise<WorkbookOtRoomHandle>;
888
892
 
889
- 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 };
893
+ 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, SHEETS_READABLE_PROTOS, 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 };
@@ -474,7 +474,7 @@ interface DocumentAwareness {
474
474
  /** `collab.ot_rooms` (§12.2): which documents without a room open one. */
475
475
  type OtRoomsKnob = "off" | "new" | "all";
476
476
  /** Whether this document opens on the ordered log: its room exists, or the knob opens one. */
477
- declare function documentUsesOrderedRoom(documentId: string, knob: OtRoomsKnob, isNewDocument: () => Promise<boolean>): Promise<boolean>;
477
+ declare function documentUsesOrderedRoom(documentId: string, knob: OtRoomsKnob, isNewDocument: () => Promise<boolean>, offlineKey?: string): Promise<boolean>;
478
478
  interface DocumentOtFactoryOptions {
479
479
  documentId: string;
480
480
  uid: string;
@@ -569,7 +569,12 @@ type SheetOp = SheetMutation[] | null;
569
569
  * The room's protocol (§6.4): Univer 1.0.4 + transformer `SHEET_TRANSFORMER_VERSION`. Any Univer or transformer
570
570
  * change raises it; a room at another proto opens read-only for this build.
571
571
  */
572
- declare const SHEETS_COLLAB_PROTO = 2;
572
+ declare const SHEETS_COLLAB_PROTO = 3;
573
+ /**
574
+ * Every Sheets protocol this package still reads (the fold, the server): each proto's transforms are a superset of
575
+ * the one before, so a room still at an older protocol folds here. Raising the proto appends, never replaces.
576
+ */
577
+ declare const SHEETS_READABLE_PROTOS: readonly number[];
573
578
  declare const SHEETS_EDIT_GROUP = "sheets.edit";
574
579
  /** One outermost command's traveling mutations, as the port hands them over. */
575
580
  interface SheetsLocalGroup {
@@ -850,8 +855,7 @@ declare function offlineTextOfGroups(buffers: CollabGroup[][]): string;
850
855
 
851
856
  /** `collab.ot_rooms` (§12.2): which workbooks without a room open one. */
852
857
  type WorkbookOtRoomsKnob = "off" | "new" | "all";
853
- /** Whether this workbook opens on the ordered log: its room exists, or the knob opens one. */
854
- declare function workbookUsesOrderedRoom(workbookId: string, knob: WorkbookOtRoomsKnob, isNewWorkbook: () => Promise<boolean>): Promise<boolean>;
858
+ declare function workbookUsesOrderedRoom(workbookId: string, knob: WorkbookOtRoomsKnob, isNewWorkbook: () => Promise<boolean>, offlineKey?: string): Promise<boolean>;
855
859
  interface WorkbookOtRoomOptions {
856
860
  workbookId: string;
857
861
  uid: string;
@@ -886,4 +890,4 @@ declare function startWorkbookOtRoom(port: SheetsOtPort & {
886
890
  dispose?: () => void;
887
891
  }, o: WorkbookOtRoomOptions): Promise<WorkbookOtRoomHandle>;
888
892
 
889
- 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 };
893
+ 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, SHEETS_READABLE_PROTOS, 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 };
@@ -1085,6 +1085,38 @@ function stampDeterministicIds(snapshot) {
1085
1085
 
1086
1086
  // src/univer/collab/docs-ot/room.ts
1087
1087
  import { isOpBarrier, openOpSession } from "@ai-matrx/data/realtime/collab";
1088
+
1089
+ // src/docs/comment-anchors.ts
1090
+ import { BuildTextUtils, CustomRangeType, JSONX as JSONX2 } from "@univerjs/core";
1091
+ var COMMENT_ANCHOR_RANGE_TYPE = CustomRangeType.COMMENT;
1092
+ var RETAIN_KEYS = /* @__PURE__ */ new Set(["t", "len", "body", "coverType"]);
1093
+ var RANGE_KEYS = /* @__PURE__ */ new Set(["rangeId", "rangeType", "startIndex", "endIndex", "properties"]);
1094
+ var isObj = (v) => !!v && typeof v === "object" && !Array.isArray(v);
1095
+ var keysOf = (o) => Object.keys(o).filter((k) => o[k] !== void 0);
1096
+ function isDocsAnchorOnlyOp(op) {
1097
+ if (!Array.isArray(op) || op.length !== 2 || op[0] !== "body") return false;
1098
+ const x = op[1];
1099
+ if (!isObj(x) || x.et !== "text-x" || !Array.isArray(x.e) || x.e.length === 0) return false;
1100
+ if (keysOf(x).some((k) => k !== "et" && k !== "e")) return false;
1101
+ for (const c of x.e) {
1102
+ if (!isObj(c) || c.t !== "r" || typeof c.len !== "number") return false;
1103
+ if (keysOf(c).some((k) => !RETAIN_KEYS.has(k))) return false;
1104
+ if ("coverType" in c && typeof c.coverType !== "number") return false;
1105
+ if ("body" in c) {
1106
+ const b = c.body;
1107
+ if (!isObj(b) || keysOf(b).some((k) => k !== "dataStream" && k !== "customRanges")) return false;
1108
+ if (b.dataStream !== void 0 && b.dataStream !== "") return false;
1109
+ if (!Array.isArray(b.customRanges) || b.customRanges.length === 0) return false;
1110
+ for (const r of b.customRanges) {
1111
+ if (!isObj(r) || r.rangeType !== COMMENT_ANCHOR_RANGE_TYPE) return false;
1112
+ if (keysOf(r).some((k) => !RANGE_KEYS.has(k))) return false;
1113
+ }
1114
+ }
1115
+ }
1116
+ return true;
1117
+ }
1118
+
1119
+ // src/univer/collab/docs-ot/room.ts
1088
1120
  var RICH_TEXT_EDITING = "doc.mutation.rich-text-editing";
1089
1121
  var DOCS_COLLAB_PROTO = 1;
1090
1122
  function canonicalKeys(v) {
@@ -1185,6 +1217,7 @@ function openDocsOtRoom(o) {
1185
1217
  return { op: isNoopDocsOp(r.op) ? null : r.op, lost: r.lost };
1186
1218
  },
1187
1219
  compose: (a, b) => composeDocsOps(a, b),
1220
+ isAnchorOnly: (op) => isDocsAnchorOnlyOp(op),
1188
1221
  isNoop: (op) => isNoopDocsOp(op),
1189
1222
  encode: (op) => [{ cmd: "docs.edit", mutations: [{ id: RICH_TEXT_EDITING, params: { segmentId: "", actions: canonicalKeys(op) } }] }],
1190
1223
  decode(groups) {
@@ -1331,6 +1364,8 @@ import { Awareness as Awareness2 } from "y-protocols/awareness";
1331
1364
  import {
1332
1365
  awarenessOpPresence,
1333
1366
  browserOfflineOptions,
1367
+ createIndexedDbOfflineStore,
1368
+ refusedKey,
1334
1369
  claimCollaboratorColor as claimCollaboratorColor2,
1335
1370
  collabReplace,
1336
1371
  collabRoomExists,
@@ -1485,10 +1520,23 @@ async function loadSnapshotRow(documentId, ref) {
1485
1520
  if (!data) throw new Error(`The saved version ${ref} of this document is missing.`);
1486
1521
  return data.snapshot ?? null;
1487
1522
  }
1488
- async function documentUsesOrderedRoom(documentId, knob, isNewDocument) {
1523
+ async function deviceHoldsBuffer(key) {
1524
+ if (!key || typeof indexedDB === "undefined") return false;
1525
+ try {
1526
+ const store = createIndexedDbOfflineStore();
1527
+ return await store.load(key) != null || await store.load(refusedKey(key)) != null;
1528
+ } catch {
1529
+ return false;
1530
+ }
1531
+ }
1532
+ async function documentUsesOrderedRoom(documentId, knob, isNewDocument, offlineKey) {
1489
1533
  try {
1490
1534
  if (await collabRoomExists(db2(), documentId)) return true;
1491
1535
  } catch (err) {
1536
+ if (await deviceHoldsBuffer(offlineKey)) {
1537
+ console.warn("[document] could not ask whether this document has a co-editing room; unsent edits on this device say it does", err);
1538
+ return true;
1539
+ }
1492
1540
  console.warn("[document] could not ask whether this document has a co-editing room; opening the relay room", err);
1493
1541
  return false;
1494
1542
  }
@@ -1767,16 +1815,17 @@ var SHEET_MUTATIONS = {
1767
1815
  "formula.mutation.remove-super-table": barrier("C5c", "table names in structured references belong to tables (C5c)"),
1768
1816
  "formula.mutation.set-super-table-option": barrier("C5c", "table names in structured references belong to tables (C5c)"),
1769
1817
  // ── barriers until C5c (ranged resources) ────────────────────────────────────────────────────────
1770
- "sheet.mutation.add-conditional-rule": barrier("C5c", RESOURCE),
1771
- "sheet.mutation.set-conditional-rule": barrier("C5c", RESOURCE),
1772
- "sheet.mutation.delete-conditional-rule": barrier("C5c", RESOURCE),
1818
+ // ── covered (C5d): ranged resources, transformed by ranged-resources.ts ──
1819
+ "sheet.mutation.add-conditional-rule": covered("cf", "rule", "derived"),
1820
+ "sheet.mutation.set-conditional-rule": covered("cf", "cfId", "rule", "derived"),
1821
+ "sheet.mutation.delete-conditional-rule": covered("cf", "cfId", "derived"),
1773
1822
  "sheet.mutation.move-conditional-rule": barrier("C5c", "conditional-format priority (ordered list)"),
1774
- "data-validation.mutation.addRule": barrier("C5c", RESOURCE),
1775
- "data-validation.mutation.updateRule": barrier("C5c", RESOURCE),
1776
- "data-validation.mutation.removeRule": barrier("C5c", RESOURCE),
1777
- "sheet.mutation.set-filter-range": barrier("C5c", RESOURCE),
1778
- "sheet.mutation.set-filter-criteria": barrier("C5c", RESOURCE),
1779
- "sheet.mutation.remove-filter": barrier("C5c", RESOURCE),
1823
+ "data-validation.mutation.addRule": covered("dv", "rule", "index", "source", "derived"),
1824
+ "data-validation.mutation.updateRule": covered("dv", "ruleId", "payload", "source", "derived"),
1825
+ "data-validation.mutation.removeRule": covered("dv", "ruleId", "source", "derived"),
1826
+ "sheet.mutation.set-filter-range": covered("filter", "range", "derived"),
1827
+ "sheet.mutation.set-filter-criteria": covered("filter", "col", "criteria", "reCalc", "derived"),
1828
+ "sheet.mutation.remove-filter": covered("filter", "derived"),
1780
1829
  "sheet.mutation.add-table": barrier("C5c", RESOURCE),
1781
1830
  "sheet.mutation.delete-table": barrier("C5c", RESOURCE),
1782
1831
  "sheet.mutation.set-sheet-table": barrier("C5c", RESOURCE),
@@ -2118,6 +2167,9 @@ function rewriteFormula(text, ctx, change) {
2118
2167
  if (change.kind === "rename") {
2119
2168
  if (!sheetName) continue;
2120
2169
  token = serializeRangeToRefString({ range, sheetName: change.name, unitId });
2170
+ } else if (change.kind === "remove-sheet") {
2171
+ if (!sheetName) continue;
2172
+ token = REF_ERROR;
2121
2173
  } else {
2122
2174
  const moved = moveRange(Rectangle.moveOffset(range, 0, 0), change.axis, change.op);
2123
2175
  if (moved === void 0) continue;
@@ -2136,6 +2188,10 @@ function rewriteFormula(text, ctx, change) {
2136
2188
  return { text: out, changed: out !== text };
2137
2189
  }
2138
2190
  function refSheetsAfter(refSheets, change) {
2191
+ if (change.kind === "remove-sheet" && refSheets) {
2192
+ if (!Object.values(refSheets).includes(change.sheet)) return refSheets;
2193
+ return Object.fromEntries(Object.entries(refSheets).map(([n, id]) => [n, id === change.sheet ? null : id]));
2194
+ }
2139
2195
  if (change.kind !== "rename" || !refSheets) return refSheets;
2140
2196
  const entries = Object.entries(refSheets);
2141
2197
  if (!entries.some(([name, id]) => id === change.sheet || name === change.name)) return refSheets;
@@ -2153,6 +2209,9 @@ function refSheetsFor(texts, sheets) {
2153
2209
 
2154
2210
  // src/collab/sheets/comment-anchor-transform.ts
2155
2211
  var SET_SHEET_COMMENT_ANCHOR = "matrx.mutation.set-comment-anchor";
2212
+ function isSheetsAnchorOnlyOp(op) {
2213
+ return !!op && op.length > 0 && op.every((m) => m.id === SET_SHEET_COMMENT_ANCHOR);
2214
+ }
2156
2215
  var REMOVE_SHEET = "sheet.mutation.remove-sheet";
2157
2216
  function structuralAxisOp(id, params) {
2158
2217
  const p = params;
@@ -2219,8 +2278,111 @@ function transformCommentAnchor(x, y, side) {
2219
2278
  return ok([moved === cell ? x : { id: x.id, params: { ...x.params, cell: moved } }]);
2220
2279
  }
2221
2280
 
2281
+ // src/collab/sheets/transform/ranged-resources.ts
2282
+ var specs = /* @__PURE__ */ new Map();
2283
+ var byId = /* @__PURE__ */ new Map();
2284
+ function registerRangedResource(spec) {
2285
+ specs.set(spec.family, spec);
2286
+ for (const id of Object.keys(spec.ids)) byId.set(id, spec);
2287
+ }
2288
+ var rangedSpecOf = (id) => byId.get(id);
2289
+ var cfRule = (m) => m.params.rule ?? {};
2290
+ var CF_FORMULA = (r) => {
2291
+ const rule = r.rule ?? {};
2292
+ if (rule.type === "highlightCell" && rule.subType === "formula") return true;
2293
+ return JSON.stringify(rule).includes('"type":"formula"');
2294
+ };
2295
+ registerRangedResource({
2296
+ family: "cf",
2297
+ ids: { "sheet.mutation.add-conditional-rule": "add", "sheet.mutation.set-conditional-rule": "set", "sheet.mutation.delete-conditional-rule": "remove" },
2298
+ keys: (m) => [String(m.id === "sheet.mutation.add-conditional-rule" ? cfRule(m).cfId : m.params.cfId)],
2299
+ rects: (m) => cfRule(m).ranges ?? null,
2300
+ withRects: (m, rects) => ({ id: m.id, params: { ...m.params, rule: { ...cfRule(m), ranges: rects } } }),
2301
+ setFor: (m, rects) => ({
2302
+ id: "sheet.mutation.set-conditional-rule",
2303
+ params: { subUnitId: m.params.subUnitId, cfId: cfRule(m).cfId ?? m.params.cfId, rule: { ...cfRule(m), ranges: rects }, derived: true }
2304
+ }),
2305
+ removeOf: (sheet, key) => ({ id: "sheet.mutation.delete-conditional-rule", params: { subUnitId: sheet, cfId: key, derived: true } }),
2306
+ hasFormula: (m) => CF_FORMULA(cfRule(m)),
2307
+ orderedAdds: true
2308
+ });
2309
+ var DV = { add: "data-validation.mutation.addRule", update: "data-validation.mutation.updateRule", remove: "data-validation.mutation.removeRule" };
2310
+ var DV_RANGE = 1;
2311
+ var DV_FACETS = { 0: "setting", 1: "range", 2: "options", 3: "all" };
2312
+ var dvPayload = (m) => m.params.payload ?? {};
2313
+ var dvFormula = (o) => !!o && [o.formula1, o.formula2].some((f) => typeof f === "string" && f.trim().startsWith("="));
2314
+ registerRangedResource({
2315
+ family: "dv",
2316
+ ids: { [DV.add]: "add", [DV.update]: "set", [DV.remove]: "remove" },
2317
+ keys: (m) => {
2318
+ if (m.id === DV.add) return [String((m.params.rule ?? {}).uid)];
2319
+ const id = m.params.ruleId;
2320
+ return Array.isArray(id) ? id.map(String) : [String(id)];
2321
+ },
2322
+ facet: (m) => m.id === DV.update ? DV_FACETS[Number(dvPayload(m).type)] ?? "all" : "all",
2323
+ rects: (m) => {
2324
+ if (m.id === DV.add) return m.params.rule?.ranges ?? null;
2325
+ if (m.id === DV.update && dvPayload(m).type === DV_RANGE) return dvPayload(m).payload ?? null;
2326
+ if (m.id === DV.update && dvPayload(m).type === 3) return (dvPayload(m).payload ?? {}).ranges ?? null;
2327
+ return null;
2328
+ },
2329
+ withRects: (m, rects) => {
2330
+ if (m.id === DV.add) return { id: m.id, params: { ...m.params, rule: { ...m.params.rule, ranges: rects } } };
2331
+ const p = dvPayload(m);
2332
+ return { id: m.id, params: { ...m.params, payload: p.type === DV_RANGE ? { ...p, payload: rects } : { ...p, payload: { ...p.payload, ranges: rects } } } };
2333
+ },
2334
+ setFor: (m, rects) => ({
2335
+ id: DV.update,
2336
+ params: { subUnitId: m.params.subUnitId, ruleId: m.id === DV.add ? (m.params.rule ?? {}).uid : m.params.ruleId, payload: { type: DV_RANGE, payload: rects }, derived: true }
2337
+ }),
2338
+ removeOf: (sheet, key) => ({ id: DV.remove, params: { subUnitId: sheet, ruleId: key, derived: true } }),
2339
+ withoutKeys: (m, keys) => {
2340
+ const left = (Array.isArray(m.params.ruleId) ? m.params.ruleId : [m.params.ruleId]).filter((k) => !keys.includes(String(k)));
2341
+ return left.length ? { id: m.id, params: { ...m.params, ruleId: Array.isArray(m.params.ruleId) ? left : left[0] } } : null;
2342
+ },
2343
+ hasFormula: (m) => {
2344
+ if (m.id === DV.add) return dvFormula(m.params.rule);
2345
+ const p = dvPayload(m);
2346
+ return p.type === 0 || p.type === 3 ? dvFormula(p.payload) : false;
2347
+ },
2348
+ orderedAdds: true,
2349
+ // an ALL update (the ref-range handler restates the whole rule) losing to a setting/options change keeps its ranges
2350
+ narrow: (m, lost) => {
2351
+ if (m.id !== DV.update || dvPayload(m).type !== 3 || lost === "range") return null;
2352
+ const ranges = (dvPayload(m).payload ?? {}).ranges;
2353
+ return Array.isArray(ranges) ? { id: m.id, params: { ...m.params, payload: { type: DV_RANGE, payload: ranges } } } : null;
2354
+ }
2355
+ });
2356
+ var FILTER = { range: "sheet.mutation.set-filter-range", criteria: "sheet.mutation.set-filter-criteria", remove: "sheet.mutation.remove-filter" };
2357
+ var hull = (rects) => ({
2358
+ startRow: Math.min(...rects.map((r) => r.startRow)),
2359
+ endRow: Math.max(...rects.map((r) => r.endRow)),
2360
+ startColumn: Math.min(...rects.map((r) => r.startColumn)),
2361
+ endColumn: Math.max(...rects.map((r) => r.endColumn))
2362
+ });
2363
+ var filterWith = (m, rects) => m.id === FILTER.range ? { id: m.id, params: { ...m.params, range: hull(rects) } } : { id: m.id, params: { ...m.params, col: rects[0].startColumn } };
2364
+ var colRect = (col) => ({ startRow: 0, endRow: 1e9, startColumn: col, endColumn: col });
2365
+ registerRangedResource({
2366
+ family: "filter",
2367
+ ids: { [FILTER.range]: "set", [FILTER.criteria]: "set", [FILTER.remove]: "remove" },
2368
+ keys: () => ["filter"],
2369
+ facet: (m) => m.id === FILTER.criteria ? `col:${String(m.params.col)}` : "range",
2370
+ rects: (m) => m.id === FILTER.range ? [m.params.range] : m.id === FILTER.criteria ? [colRect(Number(m.params.col))] : null,
2371
+ withRects: (m, rects) => filterWith(m, rects),
2372
+ setFor: (m, rects) => {
2373
+ const f = filterWith(m, rects);
2374
+ return { id: f.id, params: { ...f.params, derived: true } };
2375
+ },
2376
+ removeOf: (sheet) => ({ id: FILTER.remove, params: { subUnitId: sheet, derived: true } }),
2377
+ /** a criteria column that was removed takes only its criteria with it, never the filter */
2378
+ emptyDrops: (m) => m.id === FILTER.criteria,
2379
+ // a column move can carry a criteria column out of the filter's range (the model refuses it): not yet
2380
+ // where a criteria column lands depends on how the filter's own range moved (Univer's rule at its edges): not yet
2381
+ barrierOver: (m, axis) => m.id === FILTER.criteria && axis === "col" ? "filter: a column's criteria against a column insert, remove or move" : null
2382
+ });
2383
+
2222
2384
  // src/collab/sheets/transform/transform.ts
2223
- var SHEET_TRANSFORMER_VERSION = "c5c-1";
2385
+ var SHEET_TRANSFORMER_VERSION = "c5d-1";
2224
2386
  var ID = {
2225
2387
  values: "sheet.mutation.set-range-values",
2226
2388
  insertRow: "sheet.mutation.insert-row",
@@ -2995,19 +3157,44 @@ function sheetPair(x, y, side) {
2995
3157
  if (ys) {
2996
3158
  if (y.id !== ID.removeSheet) return ok2([x]);
2997
3159
  const gone2 = sheetOf(y);
2998
- if (namesSheet(x, gone2)) return barrier2("a formula names a sheet removed at the same time");
2999
3160
  if (sheetOf(x) === gone2 && x.id !== ID.setName && x.id !== ID.removeName)
3000
3161
  return ok2([], [], isDerived(x) ? [] : [`${gone2}: the sheet was removed`]);
3162
+ if (namesSheet(x, gone2)) {
3163
+ const ch = { kind: "remove-sheet", sheet: gone2 };
3164
+ if (x.id === ID.values) return cellsOverChange(x, ch);
3165
+ const r = definedNameThrough(x, ch);
3166
+ return typeof r === "string" ? barrier2(r) : ok2([r.op]);
3167
+ }
3001
3168
  return ok2([x]);
3002
3169
  }
3003
3170
  if (x.id !== ID.removeSheet) return ok2([x]);
3004
3171
  const gone = sheetOf(x);
3005
- if (namesSheet(y, gone)) return barrier2("a formula names a sheet removed at the same time");
3006
- return ok2([x], sheetOf(y) === gone && !isDerived(y) && y.id !== ID.setName && y.id !== ID.removeName ? [`${gone}: the sheet was removed`] : []);
3172
+ const ry = rangedSpecOf(y.id);
3173
+ if (ry && sheetOf(y) === gone && ry.ids[y.id] !== "remove")
3174
+ return ok2([...ry.keys(y).map((k) => ({ id: ry.removeOf(gone, k).id, params: { ...ry.removeOf(gone, k).params } })), x]);
3175
+ const lost = sheetOf(y) === gone && !isDerived(y) && y.id !== ID.setName && y.id !== ID.removeName ? [`${gone}: the sheet was removed`] : [];
3176
+ if (sheetOf(y) !== gone && namesSheet(y, gone)) {
3177
+ const ch = { kind: "remove-sheet", sheet: gone };
3178
+ if (y.id === ID.values) {
3179
+ const add = rewritesOf(y, ch);
3180
+ if (typeof add === "string") return barrier2(add);
3181
+ return ok2(add ? [x, add] : [x], lost);
3182
+ }
3183
+ const r = definedNameThrough(y, ch);
3184
+ if (typeof r === "string") return barrier2(r);
3185
+ return ok2(r.changed ? [x, asDerived(r.op)] : [x], lost);
3186
+ }
3187
+ return ok2([x], lost);
3007
3188
  }
3008
3189
  function annotateSheetOps(group, sheetOrder) {
3190
+ return cleanupsBeforeSheetRemoval(annotateSheetOpsInOrder(group, sheetOrder));
3191
+ }
3192
+ function annotateSheetOpsInOrder(group, sheetOrder) {
3009
3193
  const order = [...sheetOrder];
3010
- return group.map((m) => {
3194
+ let structural = false;
3195
+ return group.map((m0) => {
3196
+ if (structuralOf(m0)) structural = true;
3197
+ const m = structural && rangedSpecOf(m0.id) && !isDerived(m0) ? { id: m0.id, params: { ...m0.params, derived: true } } : m0;
3011
3198
  if (m.id === ID.insertSheet) {
3012
3199
  const index = typeof m.params.index === "number" ? Math.min(m.params.index, order.length) : order.length;
3013
3200
  order.splice(index, 0, sheetIdOf(m));
@@ -3026,6 +3213,16 @@ function annotateSheetOps(group, sheetOrder) {
3026
3213
  return m;
3027
3214
  });
3028
3215
  }
3216
+ function cleanupsBeforeSheetRemoval(group) {
3217
+ const at = group.findIndex((m) => m.id === ID.removeSheet);
3218
+ if (at < 0) return group;
3219
+ const gone = sheetOf(group[at]);
3220
+ const moved = group.filter((m, k2) => k2 > at && rangedSpecOf(m.id) && sheetOf(m) === gone);
3221
+ if (!moved.length) return group;
3222
+ const rest = group.filter((m) => !moved.includes(m));
3223
+ const k = rest.indexOf(group[at]);
3224
+ return [...rest.slice(0, k), ...moved, ...rest.slice(k)];
3225
+ }
3029
3226
  function inlineSheetStyles(sheet, styleOf) {
3030
3227
  const out = JSON.parse(JSON.stringify(sheet));
3031
3228
  const fix = (o) => {
@@ -3155,6 +3352,59 @@ function sortPair(x, y, sx, sy) {
3155
3352
  }
3156
3353
  return ok2([x]);
3157
3354
  }
3355
+ function rangedPair(x, y, side, sx, sy) {
3356
+ const px = rangedSpecOf(x.id);
3357
+ const py = rangedSpecOf(y.id);
3358
+ if (!px && !py) return null;
3359
+ const told = (m, what) => isDerived(m) ? [] : [`${sheetOf(m)}: ${what}`];
3360
+ if (px && sy) {
3361
+ const role = px.ids[x.id];
3362
+ const rects = role === "remove" ? null : px.rects(x);
3363
+ if (!rects) return ok2([x]);
3364
+ const why = px.barrierOver?.(x, sy.axis, sy.op.kind);
3365
+ if (why) return barrier2(why);
3366
+ if (px.hasFormula?.(x)) return barrier2(`${px.family}: a rule with formula text against a row or column change (its formulas do not move yet)`);
3367
+ const moved = rectsThrough(sy, rects);
3368
+ if (moved.length) return ok2([px.withRects(x, moved)]);
3369
+ if (role === "add" || px.emptyDrops?.(x)) return ok2([], [], told(x, `${px.family} ${px.keys(x).join(",")}: its cells were removed`));
3370
+ return ok2(px.keys(x).map((k) => px.removeOf(sheetOf(x), k)), [], told(x, `${px.family} ${px.keys(x).join(",")}: its cells were removed`));
3371
+ }
3372
+ if (py && sx) {
3373
+ const role = py.ids[y.id];
3374
+ const rects = role === "remove" ? null : py.rects(y);
3375
+ if (!rects) return ok2([x]);
3376
+ const why = py.barrierOver?.(y, sx.axis, sx.op.kind);
3377
+ if (why) return barrier2(why);
3378
+ if (py.hasFormula?.(y)) return barrier2(`${py.family}: a rule with formula text against a row or column change (its formulas do not move yet)`);
3379
+ const moved = rectsThrough(sx, rects);
3380
+ if (!moved.length && py.emptyDrops?.(y)) return ok2([x], told(y, `${py.family} ${py.keys(y).join(",")}: its cells were removed`));
3381
+ return ok2([x, ...moved.length ? [py.setFor(y, moved)] : py.keys(y).map((k) => py.removeOf(sheetOf(y), k))], moved.length ? [] : told(y, `${py.family} ${py.keys(y).join(",")}: its cells were removed`));
3382
+ }
3383
+ if (!px || !py || px !== py) return ok2([x]);
3384
+ const rx = px.ids[x.id];
3385
+ const ry = py.ids[y.id];
3386
+ const ky = py.keys(y);
3387
+ const common = px.keys(x).filter((k) => ky.includes(k));
3388
+ if (!common.length) {
3389
+ if (rx === "add" && ry === "add" && px.orderedAdds) return barrier2(`${px.family}: two added to one sheet at once (their order is state)`);
3390
+ return ok2([x]);
3391
+ }
3392
+ if (rx === "remove") {
3393
+ if (ry !== "remove") return ok2([x]);
3394
+ const rest = px.withoutKeys ? px.withoutKeys(x, common) : null;
3395
+ return ok2(rest ? [rest] : []);
3396
+ }
3397
+ if (ry === "remove") return ok2([], [], told(x, `${px.family} ${common.join(",")}: removed at the same time`));
3398
+ const fx = px.facet?.(x) ?? "all";
3399
+ const fy = py.facet?.(y) ?? "all";
3400
+ if (fx !== fy && fx !== "all" && fy !== "all") return ok2([x]);
3401
+ const dx = isDerived(x);
3402
+ const dy = isDerived(y);
3403
+ const xWins = dx !== dy ? !dx : side === "left";
3404
+ if (xWins) return ok2([x], told(y, `${py.family} ${common.join(",")}`));
3405
+ const narrowed = fx === "all" && fy !== "all" ? px.narrow?.(x, fy) : null;
3406
+ return ok2(narrowed ? [narrowed] : [], [], told(x, `${px.family} ${common.join(",")}`));
3407
+ }
3158
3408
  function familyOf(id) {
3159
3409
  const e = SHEET_MUTATIONS[id];
3160
3410
  return e && e.kind === "covered" ? e.family : "";
@@ -3182,6 +3432,8 @@ function transformMutation(x, y, side) {
3182
3432
  const fy = familyOf(y.id);
3183
3433
  const sy = structuralOf(y);
3184
3434
  const sx = structuralOf(x);
3435
+ const rp = rangedPair(x, y, side, sx, sy);
3436
+ if (rp) return rp;
3185
3437
  const sortp = sortPair(x, y, sx, sy);
3186
3438
  if (sortp) return sortp;
3187
3439
  const cp = countPair(x, y, sy);
@@ -3364,6 +3616,12 @@ function parsedResource(data) {
3364
3616
  return data;
3365
3617
  }
3366
3618
  }
3619
+ function withoutComputed(name, data) {
3620
+ if (name !== "SHEET_FILTER_PLUGIN" || !data || typeof data !== "object") return data;
3621
+ const out = {};
3622
+ for (const [sheet, f] of Object.entries(data)) out[sheet] = f && typeof f === "object" ? (({ cachedFilteredOut: _c, ...rest }) => rest)(f) : f;
3623
+ return out;
3624
+ }
3367
3625
  function canonicalSheet(snapshot) {
3368
3626
  const s = structuredClone(snapshot ?? {});
3369
3627
  for (const k of SHEET_LOCAL_KEYS) delete s[k];
@@ -3391,7 +3649,7 @@ function canonicalSheet(snapshot) {
3391
3649
  sheet.mergeData = sheet.mergeData.map((m) => JSON.stringify(sortDeep2(m))).sort().map((j) => JSON.parse(j));
3392
3650
  }
3393
3651
  if (Array.isArray(s.resources))
3394
- s.resources = s.resources.map((r) => ({ name: r.name, data: sortDeep2(parsedResource(r.data)) })).filter((r) => r.data != null && r.data !== "" && !(typeof r.data === "object" && Object.keys(r.data).length === 0)).sort((a, b) => String(a.name).localeCompare(String(b.name)));
3652
+ s.resources = s.resources.map((r) => ({ name: r.name, data: sortDeep2(withoutComputed(r.name, parsedResource(r.data))) })).filter((r) => r.data != null && r.data !== "" && !(typeof r.data === "object" && Object.keys(r.data).length === 0)).sort((a, b) => String(a.name).localeCompare(String(b.name)));
3395
3653
  if (Array.isArray(s.definedNames)) s.definedNames = [...s.definedNames].sort((x, y) => String(x.id).localeCompare(String(y.id)));
3396
3654
  return sortDeep2(s);
3397
3655
  }
@@ -3403,7 +3661,8 @@ async function sheetsStateHash(snapshot) {
3403
3661
  }
3404
3662
 
3405
3663
  // src/univer/collab/sheets-ot/room.ts
3406
- var SHEETS_COLLAB_PROTO = 2;
3664
+ var SHEETS_COLLAB_PROTO = 3;
3665
+ var SHEETS_READABLE_PROTOS = [2, SHEETS_COLLAB_PROTO];
3407
3666
  var SHEETS_EDIT_GROUP = "sheets.edit";
3408
3667
  var flip2 = (side) => side === "left" ? "right" : "left";
3409
3668
  var isNoopSheetOp = (op) => op == null || op.length === 0;
@@ -3566,6 +3825,7 @@ function createSheetsOtAdapter(port, hooks = {}) {
3566
3825
  version: SHEET_TRANSFORMER_VERSION,
3567
3826
  transform: (x, y, side) => transformSheetOp(x, y, side),
3568
3827
  compose: (a, b) => [...a ?? [], ...b ?? []],
3828
+ isAnchorOnly: (op) => isSheetsAnchorOnlyOp(op),
3569
3829
  isNoop: isNoopSheetOp,
3570
3830
  encode: encodeSheetOp,
3571
3831
  decode: decodeSheetOp,
@@ -4085,6 +4345,12 @@ function univerSheetsOtPort(o) {
4085
4345
  try {
4086
4346
  const ok3 = cs.syncExecuteCommand(a.id, { ...json(a.params), unitId: u }, { fromCollab: true });
4087
4347
  if (ok3 === false) throw new Error(`"${m.id}" was refused by the editor`);
4348
+ if (a.id.startsWith("sheet.mutation.set-filter") || isStructuralMutation(a)) {
4349
+ try {
4350
+ cs.syncExecuteCommand("sheet.mutation.re-calc-filter", { unitId: u, subUnitId: a.params.subUnitId }, { onlyLocal: true });
4351
+ } catch {
4352
+ }
4353
+ }
4088
4354
  } finally {
4089
4355
  applying = false;
4090
4356
  }
@@ -4249,6 +4515,8 @@ import { Awareness as Awareness3 } from "y-protocols/awareness";
4249
4515
  import {
4250
4516
  awarenessOpPresence as awarenessOpPresence2,
4251
4517
  browserOfflineOptions as browserOfflineOptions2,
4518
+ createIndexedDbOfflineStore as createIndexedDbOfflineStore2,
4519
+ refusedKey as refusedKey2,
4252
4520
  claimCollaboratorColor as claimCollaboratorColor3,
4253
4521
  collabReplace as collabReplace3,
4254
4522
  collabRoomExists as collabRoomExists3,
@@ -4308,10 +4576,23 @@ async function loadSnapshotRow2(workbookId, ref) {
4308
4576
  if (!data) throw new Error(`The saved version ${ref} of this workbook is missing.`);
4309
4577
  return data.snapshot ?? null;
4310
4578
  }
4311
- async function workbookUsesOrderedRoom(workbookId, knob, isNewWorkbook) {
4579
+ async function deviceHoldsBuffer2(key) {
4580
+ if (!key || typeof indexedDB === "undefined") return false;
4581
+ try {
4582
+ const store = createIndexedDbOfflineStore2();
4583
+ return await store.load(key) != null || await store.load(refusedKey2(key)) != null;
4584
+ } catch {
4585
+ return false;
4586
+ }
4587
+ }
4588
+ async function workbookUsesOrderedRoom(workbookId, knob, isNewWorkbook, offlineKey) {
4312
4589
  try {
4313
4590
  if (await collabRoomExists3(db4(), workbookId)) return true;
4314
4591
  } catch (err) {
4592
+ if (await deviceHoldsBuffer2(offlineKey)) {
4593
+ console.warn("[workbook] could not ask whether this workbook has a co-editing room; unsent edits on this device say it does", err);
4594
+ return true;
4595
+ }
4315
4596
  console.warn("[workbook] could not ask whether this workbook has a co-editing room; opening the relay room", err);
4316
4597
  return false;
4317
4598
  }
@@ -4408,6 +4689,7 @@ export {
4408
4689
  SENDER_VIEW_PARAMS,
4409
4690
  SHEETS_COLLAB_PROTO,
4410
4691
  SHEETS_EDIT_GROUP,
4692
+ SHEETS_READABLE_PROTOS,
4411
4693
  WorkbookCollabSession,
4412
4694
  applyDocsOp,
4413
4695
  asLoadedDocsSnapshot,