@ai-matrx/office 0.8.19 → 0.8.21

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 CHANGED
@@ -1,5 +1,28 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.21
4
+ ### C5c: Sheets co-editing — merge divergence fixed, number formats, counts and sheets travel
5
+
6
+ - **Merge divergence across chained row/column deletes, fixed at its cause.** Univer 1.0.4's structural commands only fix the merges that intersect the COMMAND's range on the other axis (any `-by-range` call with a partial range, an API call) and only while its ref-range registration is current (never refreshed after a content load or a remove-merge). A cut merge it skipped stayed put on the sender while every transform moved it, so seats ended a column apart (the session fuzz's merge at column -1 vs 0). Capture now completes the command (`mergeFixupsOwed`): the cut merges still on the sheet are removed right before the structural mutation and re-added at their model position right after it, in the traveling group and on the sender alike. Multi-seat session fuzz: 0 hash mismatches (was 2–18 per run).
7
+ - **Number formats travel** (`sheet.mutation.set.numfmt` / `remove.numfmt`, family `numfmt`): typing a date or a currency no longer rolls back. Ranges follow row/column inserts, removes and moves cell by cell; against another write of the style key `n` (a numfmt, a style with `n`, a style clear, a cell delete) the later-committed op wins.
8
+ - **Row/column counts travel** (`set-worksheet-row-count` / `-column-count`, family `count`): a concurrent insert/remove on that axis shifts the count; two counts at once, the larger wins. "Refresh from table" growing a sheet is no longer rolled back.
9
+ - **Sheets: add, remove, order travel** (family `sheet`): the sheet order is transformed as one more axis. Capture records a removed sheet's index and how many sheets are left (`annotateSheetOps`, side-car `sheetIndex`, `sheetsLeft`); an insert-sheet's styles are inlined (`inlineSheetStyles`). An edit to a sheet removed at the same time is dropped and its author told; a formula naming a removed sheet, and two removes that would leave no sheet, are barriers.
10
+ - **Selections: a seat's own row/column/count/sheet change keeps its selection inside the sheet** (API calls set no selection; the editor crashed with "Range is out of bounds"); a remote count/sheet change clamps it too.
11
+ - **Transform speed:** the group transform walks the OT grid iteratively; untouched cell ops pass through by identity (no overlap, no formula, change elsewhere); barrier checks are cached per op; undo-stack rebase reuses each list's traveling shape and compares by identity.
12
+ - **Tests:** new chained TP1 fuzz (`sheet-transform-chain-fuzz.test.ts`, 3–5 real commands per side, plus capture fidelity); the multi-seat session fuzz is seed-repeatable (`createSheetsOtAdapter` hook `stateHash`, a synchronous hash in tests) and asserts zero incidents. `SHEET_TRANSFORMER_VERSION` = `c5c-1`.
13
+ - `SHEETS_COLLAB_PROTO` = 2: a room opened by this build is raised to proto 2 (when its checkpoint is at head) and older tabs open it read-only, so seats with and without merge completion never share a room.
14
+ - Consumer action: none.
15
+
16
+ ## 0.8.20
17
+
18
+ SOURCE CHANGED WITH NO RELEASE NOTE. This release shipped source changes that nobody described in an Unreleased entry,
19
+ so they may include breaking API changes. Consumer action is UNKNOWN: read the diff before upgrading.
20
+ Diff: `git diff 046a506e40 150104f478 -- apps/shared/office/src`
21
+
22
+ Changed shipped source files (2):
23
+ - `apps/shared/office/src/sheets/workbook-ot-room.ts`
24
+ - `apps/shared/office/src/univer/collab/WorkbookCollabSession.ts`
25
+
3
26
  ## 0.8.19
4
27
  ### C7a: the browser editor's plugin resources
5
28
 
@@ -28,19 +28,24 @@ __export(transform_exports, {
28
28
  SIDE_CAR_PARAMS: () => SIDE_CAR_PARAMS,
29
29
  allowParams: () => allowParams,
30
30
  annotateFormulas: () => annotateFormulas,
31
+ annotateSheetOps: () => annotateSheetOps,
31
32
  cellRef: () => cellRef,
32
33
  completeCellContent: () => completeCellContent,
33
34
  exactMergeRemovals: () => exactMergeRemovals,
34
35
  forApply: () => forApply,
35
36
  formulaBarrier: () => formulaBarrier,
37
+ inlineSheetStyles: () => inlineSheetStyles,
36
38
  isBarrier: () => isBarrier,
37
39
  isFormulaText: () => isFormulaText,
40
+ isStructuralMutation: () => isStructuralMutation,
38
41
  mapIndex: () => mapIndex,
42
+ mergeFixupsOwed: () => mergeFixupsOwed,
39
43
  mergeThrough: () => mergeThrough,
40
44
  refSheetsAfter: () => refSheetsAfter,
41
45
  refSheetsFor: () => refSheetsFor,
42
46
  rewriteFormula: () => rewriteFormula,
43
47
  sheetTransformer: () => sheetTransformer,
48
+ spanThrough: () => spanThrough,
44
49
  transformAxis: () => transformAxis,
45
50
  transformGroup: () => transformGroup,
46
51
  transformMutation: () => transformMutation
@@ -73,6 +78,14 @@ var SHEET_MUTATIONS = {
73
78
  "sheet.mutation.set-worksheet-row-auto-height": covered("size", "rowsAutoHeightInfo"),
74
79
  "sheet.mutation.set-worksheet-col-width": covered("size", "ranges", "colWidth"),
75
80
  "sheet.mutation.set-worksheet-name": covered("name", "name"),
81
+ // ── covered (C5c) ────────────────────────────────────────────────────────────────────────────────
82
+ "sheet.mutation.set.numfmt": covered("numfmt", "values", "refMap"),
83
+ "sheet.mutation.remove.numfmt": covered("numfmt", "ranges"),
84
+ "sheet.mutation.set-worksheet-row-count": covered("count", "rowCount"),
85
+ "sheet.mutation.set-worksheet-column-count": covered("count", "columnCount"),
86
+ "sheet.mutation.insert-sheet": covered("sheet", "index", "sheet"),
87
+ "sheet.mutation.remove-sheet": covered("sheet", "sheetIndex", "sheetsLeft"),
88
+ "sheet.mutation.set-worksheet-order": covered("sheet", "fromOrder", "toOrder"),
76
89
  // ── never travels ────────────────────────────────────────────────────────────────────────────────
77
90
  "formula.mutation.set-formula-calculation-start": never(FORMULA_RESULT),
78
91
  "formula.mutation.set-trigger-formula-calculation-start": never(FORMULA_RESULT),
@@ -107,12 +120,7 @@ var SHEET_MUTATIONS = {
107
120
  "thread-comment.mutation.delete-comment": never(UNIVER_COMMENTS),
108
121
  // ── barriers: X (no transform yet, design §10) ───────────────────────────────────────────────────
109
122
  "sheet.mutation.reorder-range": barrier("X", "sort: a permutation, not transformed yet"),
110
- "sheet.mutation.set-worksheet-row-count": barrier("X", "absolute row count; needs delta semantics"),
111
- "sheet.mutation.set-worksheet-column-count": barrier("X", "absolute column count; needs delta semantics"),
112
- "sheet.mutation.set-worksheet-order": barrier("X", "absolute sheet order; needs move semantics"),
113
123
  "sheet.mutation.move-range": barrier("X", "cut-paste carries the sender's cell matrices"),
114
- "sheet.mutation.insert-sheet": barrier("X", "sheet add: not in C5a"),
115
- "sheet.mutation.remove-sheet": barrier("X", "sheet remove: not in C5a"),
116
124
  "sheet.mutation.set-worksheet-hidden": barrier("X", "sheet-level setting: not in C5a"),
117
125
  "sheet.mutation.set-tab-color": barrier("X", "sheet-level setting: not in C5a"),
118
126
  "sheet.mutation.set-workbook-name": barrier("X", "workbook-level setting: not in C5a"),
@@ -127,8 +135,6 @@ var SHEET_MUTATIONS = {
127
135
  "sheet.mutation.set-row-visible": barrier("X", "row visibility: not in C5a"),
128
136
  "sheet.mutation.set-col-hidden": barrier("X", "column visibility: not in C5a"),
129
137
  "sheet.mutation.set-col-visible": barrier("X", "column visibility: not in C5a"),
130
- "sheet.mutation.set.numfmt": barrier("X", "number formats by range: not in C5a"),
131
- "sheet.mutation.remove.numfmt": barrier("X", "number formats by range: not in C5a"),
132
138
  "sheet.mutation.add-range-protection": barrier("X", "permissions: not in C5a"),
133
139
  "sheet.mutation.set-range-protection": barrier("X", "permissions: not in C5a"),
134
140
  "sheet.mutation.delete-range-protection": barrier("X", "permissions: not in C5a"),
@@ -523,7 +529,7 @@ function refSheetsFor(texts, sheets) {
523
529
  }
524
530
 
525
531
  // src/collab/sheets/transform/transform.ts
526
- var SHEET_TRANSFORMER_VERSION = "c5b-1";
532
+ var SHEET_TRANSFORMER_VERSION = "c5c-1";
527
533
  var ID = {
528
534
  values: "sheet.mutation.set-range-values",
529
535
  insertRow: "sheet.mutation.insert-row",
@@ -540,7 +546,14 @@ var ID = {
540
546
  colWidth: "sheet.mutation.set-worksheet-col-width",
541
547
  name: "sheet.mutation.set-worksheet-name",
542
548
  setName: "formula.mutation.set-defined-name",
543
- removeName: "formula.mutation.remove-defined-name"
549
+ removeName: "formula.mutation.remove-defined-name",
550
+ setNumfmt: "sheet.mutation.set.numfmt",
551
+ removeNumfmt: "sheet.mutation.remove.numfmt",
552
+ rowCount: "sheet.mutation.set-worksheet-row-count",
553
+ colCount: "sheet.mutation.set-worksheet-column-count",
554
+ insertSheet: "sheet.mutation.insert-sheet",
555
+ removeSheet: "sheet.mutation.remove-sheet",
556
+ sheetOrder: "sheet.mutation.set-worksheet-order"
544
557
  };
545
558
  var PAIR_BARRIERS = {};
546
559
  var pairKey = (a, b) => a < b ? `${a}|${b}` : `${b}|${a}`;
@@ -652,6 +665,20 @@ function mergeThrough(s, m) {
652
665
  }
653
666
  return { range: m, fixup: false };
654
667
  }
668
+ function mergeFixupsOwed(m, mergesBefore) {
669
+ const st = structuralOf(m);
670
+ if (!st) return null;
671
+ const remove = [];
672
+ const add = [];
673
+ for (const q of mergesBefore) {
674
+ const t = mergeThrough(st, q);
675
+ if (!t.fixup) continue;
676
+ remove.push({ startRow: q.startRow, endRow: q.endRow, startColumn: q.startColumn, endColumn: q.endColumn, ...q.rangeType !== void 0 ? { rangeType: q.rangeType } : {} });
677
+ if (t.range) add.push({ ...t.range });
678
+ }
679
+ return remove.length ? { remove, add } : null;
680
+ }
681
+ var isStructuralMutation = (m) => structuralOf(m) !== null;
655
682
  function exactMergeRemovals(group, merges) {
656
683
  const sim = new Map(Object.entries(merges).map(([k, v]) => [k, v.map((r) => ({ ...r }))]));
657
684
  const out = [];
@@ -698,8 +725,24 @@ function cellsMutation(template, cells) {
698
725
  function putCell(cells, r, c, v) {
699
726
  (cells[r] ??= {})[c] = v;
700
727
  }
728
+ function cellsOverlap(a, b) {
729
+ for (const r of Object.keys(a)) {
730
+ const br = b[r];
731
+ if (!br) continue;
732
+ for (const c of Object.keys(a[r] ?? {})) if (c in br) return true;
733
+ }
734
+ return false;
735
+ }
701
736
  var isEmptyMatrix = (cells) => Object.values(cells).every((row) => Object.keys(row).length === 0);
737
+ var cellBarrierCache = /* @__PURE__ */ new WeakMap();
702
738
  function cellBarrier(m) {
739
+ const hit = cellBarrierCache.get(m);
740
+ if (hit !== void 0) return hit;
741
+ const why = cellBarrierOf(m);
742
+ cellBarrierCache.set(m, why);
743
+ return why;
744
+ }
745
+ function cellBarrierOf(m) {
703
746
  if (m.params.isOverrideStyle) return "set-range-values with isOverrideStyle (whole-style replace) is not covered";
704
747
  let why = null;
705
748
  forEachCell(cellsOf(m), (_r, _c, v) => {
@@ -781,6 +824,7 @@ function transformCell(X, Y, side) {
781
824
  function transformCellsOverCells(x, y, side) {
782
825
  if (sheetOf(x) !== sheetOf(y)) return ok([x]);
783
826
  const ycells = cellsOf(y);
827
+ if (!cellsOverlap(cellsOf(x), ycells)) return ok([x]);
784
828
  const xd = isDerived(x);
785
829
  const yd = isDerived(y);
786
830
  const eff = derivedSide(x, y, side);
@@ -808,7 +852,7 @@ function transformCellsOverCells(x, y, side) {
808
852
  const op = passes.filter((p) => !isEmptyMatrix(p)).map((p) => cellsMutation(x, p));
809
853
  return ok(op, lost, dropped);
810
854
  }
811
- var SIDE_CAR_PARAMS = ["refSheets", "previousName", "derived"];
855
+ var SIDE_CAR_PARAMS = ["refSheets", "previousName", "derived", "sheetIndex", "sheetsLeft"];
812
856
  var isDerived = (m) => m.params.derived === true;
813
857
  function derivedSide(x, y, side) {
814
858
  const xd = isDerived(x);
@@ -830,7 +874,15 @@ function withRefSheets(params, rs) {
830
874
  return out;
831
875
  }
832
876
  var hasFormulaText = (v) => !!v && (isFormulaText(v.f) || typeof v.ref === "string");
877
+ var hasFormulaCache = /* @__PURE__ */ new WeakMap();
833
878
  function hasFormula(m) {
879
+ const hit = hasFormulaCache.get(m);
880
+ if (hit !== void 0) return hit;
881
+ const any = hasFormulaOf(m);
882
+ hasFormulaCache.set(m, any);
883
+ return any;
884
+ }
885
+ function hasFormulaOf(m) {
834
886
  let any = false;
835
887
  forEachCell(cellsOf(m), (_r, _c, v) => {
836
888
  any ||= hasFormulaText(v);
@@ -869,15 +921,18 @@ function cellThrough(sheet, change, r, c) {
869
921
  return nr === null || nc === null ? null : [nr, nc];
870
922
  }
871
923
  function cellsOverChange(x, change) {
924
+ if (!hasFormula(x) && (change.kind !== "axis" || change.sheet !== sheetOf(x))) return ok([x]);
872
925
  const ctx = formulaCtx(x);
873
926
  const out = {};
874
927
  const dropped = [];
875
928
  let why = null;
929
+ let changed = false;
876
930
  forEachCell(cellsOf(x), (r, c, v) => {
877
931
  if (why) return;
878
932
  const at = cellThrough(sheetOf(x), change, r, c);
879
933
  if (!at) {
880
934
  dropped.push(cellRef(sheetOf(x), r, c));
935
+ changed = true;
881
936
  return;
882
937
  }
883
938
  let e = v;
@@ -888,14 +943,19 @@ function cellsOverChange(x, change) {
888
943
  return;
889
944
  }
890
945
  e = res.entry;
946
+ if (res.changed) changed = true;
891
947
  }
948
+ if (at[0] !== r || at[1] !== c) changed = true;
892
949
  putCell(out, at[0], at[1], e);
893
950
  });
894
951
  if (why) return barrier2(why);
895
- const params = { ...withRefSheets(x.params, refSheetsAfter(ctx.refSheets, change)), cellValue: out };
952
+ const rsAfter = refSheetsAfter(ctx.refSheets, change);
953
+ if (!changed && JSON.stringify(rsAfter ?? null) === JSON.stringify(ctx.refSheets ?? null)) return ok([x]);
954
+ const params = { ...withRefSheets(x.params, rsAfter), cellValue: out };
896
955
  return ok(isEmptyMatrix(out) ? [] : [{ id: x.id, params }], [], dropped);
897
956
  }
898
957
  function rewritesOf(y, change) {
958
+ if (!hasFormula(y)) return null;
899
959
  const ctx = formulaCtx(y);
900
960
  const out = {};
901
961
  let why = null;
@@ -1041,6 +1101,255 @@ function buildSize(template, values) {
1041
1101
  const value = uniform !== void 0 && [...values.values()].every((v) => v === uniform) ? uniform : Object.fromEntries(values);
1042
1102
  return { id: template.id, params: { ...p, ranges, [field]: value } };
1043
1103
  }
1104
+ var isNumfmt = (m) => m.id === ID.setNumfmt || m.id === ID.removeNumfmt;
1105
+ function nfParts(m) {
1106
+ if (m.id === ID.removeNumfmt) return [{ key: null, ranges: (m.params.ranges ?? []).map((r) => ({ ...r })) }];
1107
+ const values = m.params.values ?? {};
1108
+ return Object.keys(values).map((k) => ({ key: k, ranges: (values[k]?.ranges ?? []).map((r) => ({ ...r })) }));
1109
+ }
1110
+ function nfBuild(m, parts) {
1111
+ const keep = parts.filter((p) => p.ranges.length);
1112
+ if (!keep.length) return [];
1113
+ if (m.id === ID.removeNumfmt) return [{ id: m.id, params: { ...m.params, ranges: keep[0].ranges } }];
1114
+ const values = {};
1115
+ const refMap = {};
1116
+ const srcValues = m.params.values ?? {};
1117
+ const srcRef = m.params.refMap ?? {};
1118
+ for (const p of keep) {
1119
+ values[p.key] = { ...srcValues[p.key], ranges: p.ranges };
1120
+ refMap[p.key] = srcRef[p.key];
1121
+ }
1122
+ return [{ id: m.id, params: { ...m.params, values, refMap } }];
1123
+ }
1124
+ function nfValueAt(m, row, col) {
1125
+ for (const p of nfParts(m))
1126
+ if (p.ranges.some((r) => r.startRow <= row && row <= r.endRow && r.startColumn <= col && col <= r.endColumn)) {
1127
+ if (p.key === null) return null;
1128
+ const ref = (m.params.refMap ?? {})[p.key];
1129
+ return { pattern: ref?.pattern };
1130
+ }
1131
+ return void 0;
1132
+ }
1133
+ function spanThrough(op, lo, hi) {
1134
+ const pieces = [];
1135
+ if (op.kind === "insert") pieces.push([0, op.at - 1, 0], [op.at, Infinity, op.count]);
1136
+ else if (op.kind === "remove") pieces.push([0, op.start - 1, 0], [op.start + op.count, Infinity, -op.count]);
1137
+ else if (op.count <= 0 || op.to >= op.from && op.to <= op.from + op.count) pieces.push([0, Infinity, 0]);
1138
+ else if (op.to > op.from) {
1139
+ const n = op.count;
1140
+ pieces.push([0, op.from - 1, 0], [op.from, op.from + n - 1, op.to - n - op.from], [op.from + n, op.to - 1, -n], [op.to, Infinity, 0]);
1141
+ } else {
1142
+ const n = op.count;
1143
+ pieces.push([0, op.to - 1, 0], [op.to, op.from - 1, n], [op.from, op.from + n - 1, op.to - op.from], [op.from + n, Infinity, 0]);
1144
+ }
1145
+ const out = [];
1146
+ for (const [a, b, d] of pieces) {
1147
+ const x0 = Math.max(a, lo);
1148
+ const x1 = Math.min(b, hi);
1149
+ if (x0 <= x1) out.push([x0 + d, x1 + d]);
1150
+ }
1151
+ out.sort((p, q) => p[0] - q[0]);
1152
+ const merged = [];
1153
+ for (const p of out) {
1154
+ const last = merged[merged.length - 1];
1155
+ if (last && last[1] + 1 >= p[0]) last[1] = Math.max(last[1], p[1]);
1156
+ else merged.push([p[0], p[1]]);
1157
+ }
1158
+ return merged;
1159
+ }
1160
+ function rectsThrough(st, rects) {
1161
+ const [a0, a1] = S(st.axis);
1162
+ const out = [];
1163
+ for (const r of rects) for (const [lo, hi] of spanThrough(st.op, r[a0], r[a1])) out.push({ ...r, [a0]: lo, [a1]: hi });
1164
+ return out;
1165
+ }
1166
+ function rectMinus(a, b) {
1167
+ if (a.startRow > b.endRow || b.startRow > a.endRow || a.startColumn > b.endColumn || b.startColumn > a.endColumn) return [a];
1168
+ const out = [];
1169
+ if (a.startRow < b.startRow) out.push({ ...a, endRow: b.startRow - 1 });
1170
+ if (a.endRow > b.endRow) out.push({ ...a, startRow: b.endRow + 1 });
1171
+ const r0 = Math.max(a.startRow, b.startRow);
1172
+ const r1 = Math.min(a.endRow, b.endRow);
1173
+ if (a.startColumn < b.startColumn) out.push({ ...a, startRow: r0, endRow: r1, endColumn: b.startColumn - 1 });
1174
+ if (a.endColumn > b.endColumn) out.push({ ...a, startRow: r0, endRow: r1, startColumn: b.endColumn + 1 });
1175
+ return out;
1176
+ }
1177
+ function rectsMinus(as, bs) {
1178
+ let cur = as;
1179
+ for (const b of bs) cur = cur.flatMap((a) => rectMinus(a, b));
1180
+ return cur;
1181
+ }
1182
+ function nWrites(m) {
1183
+ const out = [];
1184
+ forEachCell(cellsOf(m), (r, c, v) => {
1185
+ if (v === null || v.s === null || v.s && typeof v.s === "object" && "n" in v.s)
1186
+ out.push({ startRow: r, endRow: r, startColumn: c, endColumn: c });
1187
+ });
1188
+ return out;
1189
+ }
1190
+ function numfmtPair(x, y, side, sx, sy) {
1191
+ const nx = isNumfmt(x);
1192
+ const ny = isNumfmt(y);
1193
+ if (!nx && !ny) return null;
1194
+ if (nx && sy) return ok(nfBuild(x, nfParts(x).map((p) => ({ ...p, ranges: rectsThrough(sy, p.ranges) }))));
1195
+ if (ny && sx) return ok([x]);
1196
+ if (nx && ny) {
1197
+ if (side === "left") return ok([x]);
1198
+ const ys = nfParts(y).flatMap((p) => p.ranges);
1199
+ return ok(nfBuild(x, nfParts(x).map((p) => ({ ...p, ranges: rectsMinus(p.ranges, ys) }))));
1200
+ }
1201
+ if (nx && y.id === ID.values) {
1202
+ if (side === "left") return ok([x]);
1203
+ const ys = nWrites(y);
1204
+ return ys.length ? ok(nfBuild(x, nfParts(x).map((p) => ({ ...p, ranges: rectsMinus(p.ranges, ys) })))) : ok([x]);
1205
+ }
1206
+ if (ny && x.id === ID.values) {
1207
+ if (side === "left") return ok([x]);
1208
+ const out = {};
1209
+ const tail = {};
1210
+ let touched = false;
1211
+ forEachCell(cellsOf(x), (r, c, v) => {
1212
+ const n = nfValueAt(y, r, c);
1213
+ if (n === void 0) return putCell(out, r, c, v);
1214
+ if (v === null || v.s === null) {
1215
+ putCell(out, r, c, v);
1216
+ if (n) {
1217
+ putCell(tail, r, c, { s: { n } });
1218
+ touched = true;
1219
+ }
1220
+ return;
1221
+ }
1222
+ if (v.s && typeof v.s === "object" && "n" in v.s) {
1223
+ const { n: _drop, ...rest } = v.s;
1224
+ const e = { ...v };
1225
+ if (Object.keys(rest).length) e.s = rest;
1226
+ else delete e.s;
1227
+ touched = true;
1228
+ if (Object.keys(e).length) putCell(out, r, c, e);
1229
+ return;
1230
+ }
1231
+ putCell(out, r, c, v);
1232
+ });
1233
+ if (!touched) return ok([x]);
1234
+ const ops = [];
1235
+ if (!isEmptyMatrix(out)) ops.push(cellsMutation(x, out));
1236
+ if (!isEmptyMatrix(tail)) ops.push({ id: ID.values, params: { subUnitId: sheetOf(x), cellValue: tail } });
1237
+ return ok(ops);
1238
+ }
1239
+ return ok([x]);
1240
+ }
1241
+ var isCount = (m) => m.id === ID.rowCount || m.id === ID.colCount;
1242
+ var countAxis = (m) => m.id === ID.rowCount ? "row" : "col";
1243
+ var countKey = (m) => m.id === ID.rowCount ? "rowCount" : "columnCount";
1244
+ function countPair(x, y, sy) {
1245
+ if (!isCount(x)) return isCount(y) ? ok([x]) : null;
1246
+ const k = countKey(x);
1247
+ const v = Number(x.params[k]);
1248
+ if (sy) {
1249
+ if (sy.axis !== countAxis(x) || sy.op.kind === "move") return ok([x]);
1250
+ const d = sy.op.kind === "insert" ? sy.op.count : -sy.op.count;
1251
+ return ok([{ id: x.id, params: { ...x.params, [k]: Math.max(1, v + d) } }]);
1252
+ }
1253
+ if (isCount(y) && y.id === x.id) {
1254
+ const w = Number(y.params[k]);
1255
+ return ok([w > v ? { id: x.id, params: { ...x.params, [k]: w } } : x]);
1256
+ }
1257
+ return ok([x]);
1258
+ }
1259
+ var isSheetOp = (m) => m.id === ID.insertSheet || m.id === ID.removeSheet || m.id === ID.sheetOrder;
1260
+ var sheetIdOf = (m) => m.id === ID.insertSheet ? String(m.params.sheet?.id ?? "") : sheetOf(m);
1261
+ function sheetAxisOf(m) {
1262
+ const p = m.params;
1263
+ if (m.id === ID.insertSheet) return typeof p.index === "number" ? { kind: "insert", at: p.index, count: 1 } : null;
1264
+ if (m.id === ID.removeSheet) return typeof p.sheetIndex === "number" ? { kind: "remove", start: p.sheetIndex, count: 1 } : null;
1265
+ if (m.id === ID.sheetOrder) {
1266
+ const f = Number(p.fromOrder);
1267
+ const t = Number(p.toOrder);
1268
+ return { kind: "move", from: f, count: 1, to: t > f ? t + 1 : t };
1269
+ }
1270
+ return null;
1271
+ }
1272
+ function buildSheetOp(template, op) {
1273
+ if (op.kind === "insert") return { id: template.id, params: { ...template.params, index: op.at } };
1274
+ if (op.kind === "remove") return { id: template.id, params: { ...template.params, sheetIndex: op.start } };
1275
+ return { id: template.id, params: { ...template.params, fromOrder: op.from, toOrder: op.to > op.from ? op.to - 1 : op.to } };
1276
+ }
1277
+ function namesSheet(m, id) {
1278
+ const rs = refSheetsOf(m);
1279
+ return !!rs && Object.values(rs).includes(id) && (m.id === ID.setName || hasFormula(m));
1280
+ }
1281
+ function sheetPair(x, y, side) {
1282
+ const xs = isSheetOp(x);
1283
+ const ys = isSheetOp(y);
1284
+ if (!xs && !ys) return null;
1285
+ if (xs && ys) {
1286
+ const a = sheetAxisOf(x);
1287
+ const b = sheetAxisOf(y);
1288
+ if (!a || !b) return barrier2("a sheet add or remove without its index (capture must call annotateSheetOps)");
1289
+ if (x.id === ID.insertSheet && y.id === ID.insertSheet && sheetIdOf(x) === sheetIdOf(y)) {
1290
+ return JSON.stringify(x.params) === JSON.stringify(y.params) ? ok([]) : barrier2("the same sheet id added twice with different content");
1291
+ }
1292
+ if (x.id === ID.removeSheet && typeof x.params.sheetsLeft === "number" && sheetIdOf(x) !== sheetIdOf(y) && (y.id === ID.removeSheet || y.id === ID.insertSheet)) {
1293
+ const left = x.params.sheetsLeft + (y.id === ID.insertSheet ? 1 : -1);
1294
+ if (left < 1) return barrier2("concurrent sheet removes would leave the workbook without a sheet");
1295
+ const out2 = transformAxis(a, b, side);
1296
+ if (!out2) return barrier2("sheet order: no transform");
1297
+ return ok(out2.map((o) => buildSheetOp({ id: x.id, params: { ...x.params, sheetsLeft: left } }, o)));
1298
+ }
1299
+ const out = transformAxis(a, b, side);
1300
+ if (!out) return barrier2("sheet order: no transform");
1301
+ return ok(out.map((o) => buildSheetOp(x, o)), [], out.length === 0 && x.id === ID.sheetOrder ? [`${sheetIdOf(x)}: sheet order`] : []);
1302
+ }
1303
+ if (ys) {
1304
+ if (y.id !== ID.removeSheet) return ok([x]);
1305
+ const gone2 = sheetOf(y);
1306
+ if (namesSheet(x, gone2)) return barrier2("a formula names a sheet removed at the same time");
1307
+ if (sheetOf(x) === gone2 && x.id !== ID.setName && x.id !== ID.removeName)
1308
+ return ok([], [], isDerived(x) ? [] : [`${gone2}: the sheet was removed`]);
1309
+ return ok([x]);
1310
+ }
1311
+ if (x.id !== ID.removeSheet) return ok([x]);
1312
+ const gone = sheetOf(x);
1313
+ if (namesSheet(y, gone)) return barrier2("a formula names a sheet removed at the same time");
1314
+ return ok([x], sheetOf(y) === gone && !isDerived(y) && y.id !== ID.setName && y.id !== ID.removeName ? [`${gone}: the sheet was removed`] : []);
1315
+ }
1316
+ function annotateSheetOps(group, sheetOrder) {
1317
+ const order = [...sheetOrder];
1318
+ return group.map((m) => {
1319
+ if (m.id === ID.insertSheet) {
1320
+ const index = typeof m.params.index === "number" ? Math.min(m.params.index, order.length) : order.length;
1321
+ order.splice(index, 0, sheetIdOf(m));
1322
+ return index === m.params.index ? m : { id: m.id, params: { ...m.params, index } };
1323
+ }
1324
+ if (m.id === ID.removeSheet) {
1325
+ const i = order.indexOf(sheetOf(m));
1326
+ if (i >= 0) order.splice(i, 1);
1327
+ return i >= 0 ? { id: m.id, params: { ...m.params, sheetIndex: i, sheetsLeft: order.length } } : m;
1328
+ }
1329
+ if (m.id === ID.sheetOrder) {
1330
+ const f = Number(m.params.fromOrder);
1331
+ const [id] = order.splice(f, 1);
1332
+ order.splice(Number(m.params.toOrder), 0, id ?? sheetOf(m));
1333
+ }
1334
+ return m;
1335
+ });
1336
+ }
1337
+ function inlineSheetStyles(sheet, styleOf) {
1338
+ const out = JSON.parse(JSON.stringify(sheet));
1339
+ const fix = (o) => {
1340
+ if (o && typeof o.s === "string") {
1341
+ const st = styleOf(o.s);
1342
+ if (st) o.s = JSON.parse(JSON.stringify(st));
1343
+ }
1344
+ };
1345
+ for (const row of Object.values(out.cellData ?? {})) for (const cell of Object.values(row ?? {})) fix(cell);
1346
+ for (const key of ["rowData", "columnData"]) for (const v of Object.values(out[key] ?? {})) fix(v);
1347
+ if (typeof out.defaultStyle === "string") {
1348
+ const st = styleOf(out.defaultStyle);
1349
+ if (st) out.defaultStyle = JSON.parse(JSON.stringify(st));
1350
+ }
1351
+ return out;
1352
+ }
1044
1353
  function familyOf(id) {
1045
1354
  const e = SHEET_MUTATIONS[id];
1046
1355
  return e && e.kind === "covered" ? e.family : "";
@@ -1057,6 +1366,8 @@ function transformMutation(x, y, side) {
1057
1366
  }
1058
1367
  const pb = PAIR_BARRIERS[pairKey(x.id, y.id)];
1059
1368
  if (pb) return barrier2(`${x.id} \xD7 ${y.id}: ${pb.reason}${pb.seed === null ? "" : ` (fuzz seed ${pb.seed})`}`);
1369
+ const sp = sheetPair(x, y, side);
1370
+ if (sp) return sp;
1060
1371
  const fp = formulaPair(x, y, side);
1061
1372
  if (fp) return fp;
1062
1373
  if (sheetOf(x) !== sheetOf(y)) return ok([x]);
@@ -1064,6 +1375,10 @@ function transformMutation(x, y, side) {
1064
1375
  const fy = familyOf(y.id);
1065
1376
  const sy = structuralOf(y);
1066
1377
  const sx = structuralOf(x);
1378
+ const cp = countPair(x, y, sy);
1379
+ if (cp) return cp;
1380
+ const np = numfmtPair(x, y, side, sx, sy);
1381
+ if (np) return np;
1067
1382
  if (sx && sy) {
1068
1383
  if (sx.axis !== sy.axis) return ok([x]);
1069
1384
  const out = transformAxis(sx.op, sy.op, side);
@@ -1171,22 +1486,45 @@ function tSeq(xs, ys, side, acc) {
1171
1486
  acc.dropped.push(...a.dropped);
1172
1487
  return [a.op, b.op];
1173
1488
  }
1489
+ const outX = [];
1490
+ let curY = ys;
1491
+ for (const x of xs) {
1492
+ let curX = [x];
1493
+ const nextY = [];
1494
+ for (const y of curY) {
1495
+ if (curX.length === 0) {
1496
+ nextY.push(y);
1497
+ continue;
1498
+ }
1499
+ const r = tSeq(curX, [y], side, acc);
1500
+ if (blocked(r)) return r;
1501
+ curX = r[0];
1502
+ for (const m of r[1]) nextY.push(m);
1503
+ }
1504
+ for (const m of curX) outX.push(m);
1505
+ curY = nextY;
1506
+ }
1507
+ return [outX, curY];
1508
+ }
1509
+ function tSeqOld(xs, ys, side, acc) {
1510
+ if (xs.length === 0 || ys.length === 0) return [xs, ys];
1511
+ if (xs.length === 1 && ys.length === 1) return tSeq(xs, ys, side, acc);
1174
1512
  if (xs.length > 1) {
1175
- const first2 = tSeq([xs[0]], ys, side, acc);
1513
+ const first2 = tSeqOld([xs[0]], ys, side, acc);
1176
1514
  if (blocked(first2)) return first2;
1177
- const rest2 = tSeq(xs.slice(1), first2[1], side, acc);
1515
+ const rest2 = tSeqOld(xs.slice(1), first2[1], side, acc);
1178
1516
  if (blocked(rest2)) return rest2;
1179
1517
  return [[...first2[0], ...rest2[0]], rest2[1]];
1180
1518
  }
1181
- const first = tSeq(xs, [ys[0]], side, acc);
1519
+ const first = tSeqOld(xs, [ys[0]], side, acc);
1182
1520
  if (blocked(first)) return first;
1183
- const rest = tSeq(first[0], ys.slice(1), side, acc);
1521
+ const rest = tSeqOld(first[0], ys.slice(1), side, acc);
1184
1522
  if (blocked(rest)) return rest;
1185
1523
  return [rest[0], [...first[1], ...rest[1]]];
1186
1524
  }
1187
1525
  function transformGroup(x, y, side) {
1188
1526
  const acc = { lost: [], dropped: [] };
1189
- const r = tSeq([...x], [...y], side, acc);
1527
+ const r = (process.env.OLD_TSEQ ? tSeqOld : tSeq)([...x], [...y], side, acc);
1190
1528
  if (blocked(r)) return r;
1191
1529
  return { op: r[0], lost: [...new Set(acc.lost)], dropped: [...new Set(acc.dropped)] };
1192
1530
  }