@ai-matrx/office 0.8.26 → 0.8.28

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,6 +1,35 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.8.28
4
+
5
+ UNVERIFIED: this release has no Unreleased entry and the source comparison could not be made (the caller supplied no source comparison). Whether source changed is unknown; consumer action is unknown.
6
+ Check the package's gitHead on npm against this version's gitHead: `git diff <previous gitHead> <this gitHead> -- apps/shared/office/src`.
7
+
3
8
  ## 0.8.26
9
+
10
+ SOURCE CHANGED WITH NO RELEASE NOTE. This release shipped source changes that nobody described in an Unreleased entry,
11
+ so they may include breaking API changes. Consumer action is UNKNOWN: read the diff before upgrading.
12
+ Diff: `git diff 4c54b44dc8 5f2e7d1bcc -- apps/shared/office/src`
13
+
14
+ Changed shipped source files (4):
15
+ - `apps/shared/office/src/docs/document-service.ts`
16
+ - `apps/shared/office/src/docs/ports.ts`
17
+ - `apps/shared/office/src/sheets/ports.ts`
18
+ - `apps/shared/office/src/sheets/workbook-service.ts`
19
+
20
+ ## 0.8.25
21
+
22
+ SOURCE CHANGED WITH NO RELEASE NOTE. This release shipped source changes that nobody described in an Unreleased entry,
23
+ so they may include breaking API changes. Consumer action is UNKNOWN: read the diff before upgrading.
24
+ Diff: `git diff 766ad5513a 4c54b44dc8 -- apps/shared/office/src`
25
+
26
+ Changed shipped source files (4):
27
+ - `apps/shared/office/src/collab/sheets/transform/fuzz/seats.ts`
28
+ - `apps/shared/office/src/univer/collab/fold/fold.ts`
29
+ - `apps/shared/office/src/univer/collab/sheets-ot/room.ts`
30
+ - `apps/shared/office/src/univer/collab/sheets-ot/univer-port.ts`
31
+
32
+ ## 0.8.24
4
33
  ### C9: comment anchors that follow concurrent edits
5
34
 
6
35
  - **Docs:** `addCommentAnchorActions(body, start, end, id)` anchors a passage with a `COMMENT` custom range (Univer's own builder, so the op has the link family's shape C0 proved); `commentAnchorsOf(body)` reads every anchor back (split pieces joined, Univer's inclusive end as `[start, end)`); `removeCommentAnchorActions` takes a cancelled draft's anchor out; `commentPartAnchor` / `anchorIdOfPartAnchor` are the thread's `part_anchor` (`anchor:<id>`, quote as `label`). Fuzz on the C0 corpora: anchor × every captured family and anchor × anchor, 0 divergences in 180,000 checks at 2,000 seeds; red check without a transform.
@@ -155,16 +155,17 @@ var SHEET_MUTATIONS = {
155
155
  "formula.mutation.remove-super-table": barrier("C5c", "table names in structured references belong to tables (C5c)"),
156
156
  "formula.mutation.set-super-table-option": barrier("C5c", "table names in structured references belong to tables (C5c)"),
157
157
  // ── barriers until C5c (ranged resources) ────────────────────────────────────────────────────────
158
- "sheet.mutation.add-conditional-rule": barrier("C5c", RESOURCE),
159
- "sheet.mutation.set-conditional-rule": barrier("C5c", RESOURCE),
160
- "sheet.mutation.delete-conditional-rule": barrier("C5c", RESOURCE),
158
+ // ── covered (C5d): ranged resources, transformed by ranged-resources.ts ──
159
+ "sheet.mutation.add-conditional-rule": covered("cf", "rule", "derived"),
160
+ "sheet.mutation.set-conditional-rule": covered("cf", "cfId", "rule", "derived"),
161
+ "sheet.mutation.delete-conditional-rule": covered("cf", "cfId", "derived"),
161
162
  "sheet.mutation.move-conditional-rule": barrier("C5c", "conditional-format priority (ordered list)"),
162
- "data-validation.mutation.addRule": barrier("C5c", RESOURCE),
163
- "data-validation.mutation.updateRule": barrier("C5c", RESOURCE),
164
- "data-validation.mutation.removeRule": barrier("C5c", RESOURCE),
165
- "sheet.mutation.set-filter-range": barrier("C5c", RESOURCE),
166
- "sheet.mutation.set-filter-criteria": barrier("C5c", RESOURCE),
167
- "sheet.mutation.remove-filter": barrier("C5c", RESOURCE),
163
+ "data-validation.mutation.addRule": covered("dv", "rule", "index", "source", "derived"),
164
+ "data-validation.mutation.updateRule": covered("dv", "ruleId", "payload", "source", "derived"),
165
+ "data-validation.mutation.removeRule": covered("dv", "ruleId", "source", "derived"),
166
+ "sheet.mutation.set-filter-range": covered("filter", "range", "derived"),
167
+ "sheet.mutation.set-filter-criteria": covered("filter", "col", "criteria", "reCalc", "derived"),
168
+ "sheet.mutation.remove-filter": covered("filter", "derived"),
168
169
  "sheet.mutation.add-table": barrier("C5c", RESOURCE),
169
170
  "sheet.mutation.delete-table": barrier("C5c", RESOURCE),
170
171
  "sheet.mutation.set-sheet-table": barrier("C5c", RESOURCE),
@@ -497,6 +498,9 @@ function rewriteFormula(text, ctx, change) {
497
498
  if (change.kind === "rename") {
498
499
  if (!sheetName) continue;
499
500
  token = (0, import_engine_formula.serializeRangeToRefString)({ range, sheetName: change.name, unitId });
501
+ } else if (change.kind === "remove-sheet") {
502
+ if (!sheetName) continue;
503
+ token = REF_ERROR;
500
504
  } else {
501
505
  const moved = moveRange(import_core.Rectangle.moveOffset(range, 0, 0), change.axis, change.op);
502
506
  if (moved === void 0) continue;
@@ -515,6 +519,10 @@ function rewriteFormula(text, ctx, change) {
515
519
  return { text: out, changed: out !== text };
516
520
  }
517
521
  function refSheetsAfter(refSheets, change) {
522
+ if (change.kind === "remove-sheet" && refSheets) {
523
+ if (!Object.values(refSheets).includes(change.sheet)) return refSheets;
524
+ return Object.fromEntries(Object.entries(refSheets).map(([n, id]) => [n, id === change.sheet ? null : id]));
525
+ }
518
526
  if (change.kind !== "rename" || !refSheets) return refSheets;
519
527
  const entries = Object.entries(refSheets);
520
528
  if (!entries.some(([name, id]) => id === change.sheet || name === change.name)) return refSheets;
@@ -598,8 +606,111 @@ function transformCommentAnchor(x, y, side) {
598
606
  return ok([moved === cell ? x : { id: x.id, params: { ...x.params, cell: moved } }]);
599
607
  }
600
608
 
609
+ // src/collab/sheets/transform/ranged-resources.ts
610
+ var specs = /* @__PURE__ */ new Map();
611
+ var byId = /* @__PURE__ */ new Map();
612
+ function registerRangedResource(spec) {
613
+ specs.set(spec.family, spec);
614
+ for (const id of Object.keys(spec.ids)) byId.set(id, spec);
615
+ }
616
+ var rangedSpecOf = (id) => byId.get(id);
617
+ var cfRule = (m) => m.params.rule ?? {};
618
+ var CF_FORMULA = (r) => {
619
+ const rule = r.rule ?? {};
620
+ if (rule.type === "highlightCell" && rule.subType === "formula") return true;
621
+ return JSON.stringify(rule).includes('"type":"formula"');
622
+ };
623
+ registerRangedResource({
624
+ family: "cf",
625
+ ids: { "sheet.mutation.add-conditional-rule": "add", "sheet.mutation.set-conditional-rule": "set", "sheet.mutation.delete-conditional-rule": "remove" },
626
+ keys: (m) => [String(m.id === "sheet.mutation.add-conditional-rule" ? cfRule(m).cfId : m.params.cfId)],
627
+ rects: (m) => cfRule(m).ranges ?? null,
628
+ withRects: (m, rects) => ({ id: m.id, params: { ...m.params, rule: { ...cfRule(m), ranges: rects } } }),
629
+ setFor: (m, rects) => ({
630
+ id: "sheet.mutation.set-conditional-rule",
631
+ params: { subUnitId: m.params.subUnitId, cfId: cfRule(m).cfId ?? m.params.cfId, rule: { ...cfRule(m), ranges: rects }, derived: true }
632
+ }),
633
+ removeOf: (sheet, key) => ({ id: "sheet.mutation.delete-conditional-rule", params: { subUnitId: sheet, cfId: key, derived: true } }),
634
+ hasFormula: (m) => CF_FORMULA(cfRule(m)),
635
+ orderedAdds: true
636
+ });
637
+ var DV = { add: "data-validation.mutation.addRule", update: "data-validation.mutation.updateRule", remove: "data-validation.mutation.removeRule" };
638
+ var DV_RANGE = 1;
639
+ var DV_FACETS = { 0: "setting", 1: "range", 2: "options", 3: "all" };
640
+ var dvPayload = (m) => m.params.payload ?? {};
641
+ var dvFormula = (o) => !!o && [o.formula1, o.formula2].some((f) => typeof f === "string" && f.trim().startsWith("="));
642
+ registerRangedResource({
643
+ family: "dv",
644
+ ids: { [DV.add]: "add", [DV.update]: "set", [DV.remove]: "remove" },
645
+ keys: (m) => {
646
+ if (m.id === DV.add) return [String((m.params.rule ?? {}).uid)];
647
+ const id = m.params.ruleId;
648
+ return Array.isArray(id) ? id.map(String) : [String(id)];
649
+ },
650
+ facet: (m) => m.id === DV.update ? DV_FACETS[Number(dvPayload(m).type)] ?? "all" : "all",
651
+ rects: (m) => {
652
+ if (m.id === DV.add) return m.params.rule?.ranges ?? null;
653
+ if (m.id === DV.update && dvPayload(m).type === DV_RANGE) return dvPayload(m).payload ?? null;
654
+ if (m.id === DV.update && dvPayload(m).type === 3) return (dvPayload(m).payload ?? {}).ranges ?? null;
655
+ return null;
656
+ },
657
+ withRects: (m, rects) => {
658
+ if (m.id === DV.add) return { id: m.id, params: { ...m.params, rule: { ...m.params.rule, ranges: rects } } };
659
+ const p = dvPayload(m);
660
+ return { id: m.id, params: { ...m.params, payload: p.type === DV_RANGE ? { ...p, payload: rects } : { ...p, payload: { ...p.payload, ranges: rects } } } };
661
+ },
662
+ setFor: (m, rects) => ({
663
+ id: DV.update,
664
+ 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 }
665
+ }),
666
+ removeOf: (sheet, key) => ({ id: DV.remove, params: { subUnitId: sheet, ruleId: key, derived: true } }),
667
+ withoutKeys: (m, keys) => {
668
+ const left = (Array.isArray(m.params.ruleId) ? m.params.ruleId : [m.params.ruleId]).filter((k) => !keys.includes(String(k)));
669
+ return left.length ? { id: m.id, params: { ...m.params, ruleId: Array.isArray(m.params.ruleId) ? left : left[0] } } : null;
670
+ },
671
+ hasFormula: (m) => {
672
+ if (m.id === DV.add) return dvFormula(m.params.rule);
673
+ const p = dvPayload(m);
674
+ return p.type === 0 || p.type === 3 ? dvFormula(p.payload) : false;
675
+ },
676
+ orderedAdds: true,
677
+ // an ALL update (the ref-range handler restates the whole rule) losing to a setting/options change keeps its ranges
678
+ narrow: (m, lost) => {
679
+ if (m.id !== DV.update || dvPayload(m).type !== 3 || lost === "range") return null;
680
+ const ranges = (dvPayload(m).payload ?? {}).ranges;
681
+ return Array.isArray(ranges) ? { id: m.id, params: { ...m.params, payload: { type: DV_RANGE, payload: ranges } } } : null;
682
+ }
683
+ });
684
+ var FILTER = { range: "sheet.mutation.set-filter-range", criteria: "sheet.mutation.set-filter-criteria", remove: "sheet.mutation.remove-filter" };
685
+ var hull = (rects) => ({
686
+ startRow: Math.min(...rects.map((r) => r.startRow)),
687
+ endRow: Math.max(...rects.map((r) => r.endRow)),
688
+ startColumn: Math.min(...rects.map((r) => r.startColumn)),
689
+ endColumn: Math.max(...rects.map((r) => r.endColumn))
690
+ });
691
+ 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 } };
692
+ var colRect = (col) => ({ startRow: 0, endRow: 1e9, startColumn: col, endColumn: col });
693
+ registerRangedResource({
694
+ family: "filter",
695
+ ids: { [FILTER.range]: "set", [FILTER.criteria]: "set", [FILTER.remove]: "remove" },
696
+ keys: () => ["filter"],
697
+ facet: (m) => m.id === FILTER.criteria ? `col:${String(m.params.col)}` : "range",
698
+ rects: (m) => m.id === FILTER.range ? [m.params.range] : m.id === FILTER.criteria ? [colRect(Number(m.params.col))] : null,
699
+ withRects: (m, rects) => filterWith(m, rects),
700
+ setFor: (m, rects) => {
701
+ const f = filterWith(m, rects);
702
+ return { id: f.id, params: { ...f.params, derived: true } };
703
+ },
704
+ removeOf: (sheet) => ({ id: FILTER.remove, params: { subUnitId: sheet, derived: true } }),
705
+ /** a criteria column that was removed takes only its criteria with it, never the filter */
706
+ emptyDrops: (m) => m.id === FILTER.criteria,
707
+ // a column move can carry a criteria column out of the filter's range (the model refuses it): not yet
708
+ // where a criteria column lands depends on how the filter's own range moved (Univer's rule at its edges): not yet
709
+ barrierOver: (m, axis) => m.id === FILTER.criteria && axis === "col" ? "filter: a column's criteria against a column insert, remove or move" : null
710
+ });
711
+
601
712
  // src/collab/sheets/transform/transform.ts
602
- var SHEET_TRANSFORMER_VERSION = "c5c-1";
713
+ var SHEET_TRANSFORMER_VERSION = "c5d-1";
603
714
  var ID = {
604
715
  values: "sheet.mutation.set-range-values",
605
716
  insertRow: "sheet.mutation.insert-row",
@@ -1374,19 +1485,44 @@ function sheetPair(x, y, side) {
1374
1485
  if (ys) {
1375
1486
  if (y.id !== ID.removeSheet) return ok2([x]);
1376
1487
  const gone2 = sheetOf(y);
1377
- if (namesSheet(x, gone2)) return barrier2("a formula names a sheet removed at the same time");
1378
1488
  if (sheetOf(x) === gone2 && x.id !== ID.setName && x.id !== ID.removeName)
1379
1489
  return ok2([], [], isDerived(x) ? [] : [`${gone2}: the sheet was removed`]);
1490
+ if (namesSheet(x, gone2)) {
1491
+ const ch = { kind: "remove-sheet", sheet: gone2 };
1492
+ if (x.id === ID.values) return cellsOverChange(x, ch);
1493
+ const r = definedNameThrough(x, ch);
1494
+ return typeof r === "string" ? barrier2(r) : ok2([r.op]);
1495
+ }
1380
1496
  return ok2([x]);
1381
1497
  }
1382
1498
  if (x.id !== ID.removeSheet) return ok2([x]);
1383
1499
  const gone = sheetOf(x);
1384
- if (namesSheet(y, gone)) return barrier2("a formula names a sheet removed at the same time");
1385
- return ok2([x], sheetOf(y) === gone && !isDerived(y) && y.id !== ID.setName && y.id !== ID.removeName ? [`${gone}: the sheet was removed`] : []);
1500
+ const ry = rangedSpecOf(y.id);
1501
+ if (ry && sheetOf(y) === gone && ry.ids[y.id] !== "remove")
1502
+ return ok2([...ry.keys(y).map((k) => ({ id: ry.removeOf(gone, k).id, params: { ...ry.removeOf(gone, k).params } })), x]);
1503
+ const lost = sheetOf(y) === gone && !isDerived(y) && y.id !== ID.setName && y.id !== ID.removeName ? [`${gone}: the sheet was removed`] : [];
1504
+ if (sheetOf(y) !== gone && namesSheet(y, gone)) {
1505
+ const ch = { kind: "remove-sheet", sheet: gone };
1506
+ if (y.id === ID.values) {
1507
+ const add = rewritesOf(y, ch);
1508
+ if (typeof add === "string") return barrier2(add);
1509
+ return ok2(add ? [x, add] : [x], lost);
1510
+ }
1511
+ const r = definedNameThrough(y, ch);
1512
+ if (typeof r === "string") return barrier2(r);
1513
+ return ok2(r.changed ? [x, asDerived(r.op)] : [x], lost);
1514
+ }
1515
+ return ok2([x], lost);
1386
1516
  }
1387
1517
  function annotateSheetOps(group, sheetOrder) {
1518
+ return cleanupsBeforeSheetRemoval(annotateSheetOpsInOrder(group, sheetOrder));
1519
+ }
1520
+ function annotateSheetOpsInOrder(group, sheetOrder) {
1388
1521
  const order = [...sheetOrder];
1389
- return group.map((m) => {
1522
+ let structural = false;
1523
+ return group.map((m0) => {
1524
+ if (structuralOf(m0)) structural = true;
1525
+ const m = structural && rangedSpecOf(m0.id) && !isDerived(m0) ? { id: m0.id, params: { ...m0.params, derived: true } } : m0;
1390
1526
  if (m.id === ID.insertSheet) {
1391
1527
  const index = typeof m.params.index === "number" ? Math.min(m.params.index, order.length) : order.length;
1392
1528
  order.splice(index, 0, sheetIdOf(m));
@@ -1405,6 +1541,16 @@ function annotateSheetOps(group, sheetOrder) {
1405
1541
  return m;
1406
1542
  });
1407
1543
  }
1544
+ function cleanupsBeforeSheetRemoval(group) {
1545
+ const at = group.findIndex((m) => m.id === ID.removeSheet);
1546
+ if (at < 0) return group;
1547
+ const gone = sheetOf(group[at]);
1548
+ const moved = group.filter((m, k2) => k2 > at && rangedSpecOf(m.id) && sheetOf(m) === gone);
1549
+ if (!moved.length) return group;
1550
+ const rest = group.filter((m) => !moved.includes(m));
1551
+ const k = rest.indexOf(group[at]);
1552
+ return [...rest.slice(0, k), ...moved, ...rest.slice(k)];
1553
+ }
1408
1554
  function inlineSheetStyles(sheet, styleOf) {
1409
1555
  const out = JSON.parse(JSON.stringify(sheet));
1410
1556
  const fix = (o) => {
@@ -1534,6 +1680,59 @@ function sortPair(x, y, sx, sy) {
1534
1680
  }
1535
1681
  return ok2([x]);
1536
1682
  }
1683
+ function rangedPair(x, y, side, sx, sy) {
1684
+ const px = rangedSpecOf(x.id);
1685
+ const py = rangedSpecOf(y.id);
1686
+ if (!px && !py) return null;
1687
+ const told = (m, what) => isDerived(m) ? [] : [`${sheetOf(m)}: ${what}`];
1688
+ if (px && sy) {
1689
+ const role = px.ids[x.id];
1690
+ const rects = role === "remove" ? null : px.rects(x);
1691
+ if (!rects) return ok2([x]);
1692
+ const why = px.barrierOver?.(x, sy.axis, sy.op.kind);
1693
+ if (why) return barrier2(why);
1694
+ 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)`);
1695
+ const moved = rectsThrough(sy, rects);
1696
+ if (moved.length) return ok2([px.withRects(x, moved)]);
1697
+ if (role === "add" || px.emptyDrops?.(x)) return ok2([], [], told(x, `${px.family} ${px.keys(x).join(",")}: its cells were removed`));
1698
+ return ok2(px.keys(x).map((k) => px.removeOf(sheetOf(x), k)), [], told(x, `${px.family} ${px.keys(x).join(",")}: its cells were removed`));
1699
+ }
1700
+ if (py && sx) {
1701
+ const role = py.ids[y.id];
1702
+ const rects = role === "remove" ? null : py.rects(y);
1703
+ if (!rects) return ok2([x]);
1704
+ const why = py.barrierOver?.(y, sx.axis, sx.op.kind);
1705
+ if (why) return barrier2(why);
1706
+ 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)`);
1707
+ const moved = rectsThrough(sx, rects);
1708
+ if (!moved.length && py.emptyDrops?.(y)) return ok2([x], told(y, `${py.family} ${py.keys(y).join(",")}: its cells were removed`));
1709
+ 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`));
1710
+ }
1711
+ if (!px || !py || px !== py) return ok2([x]);
1712
+ const rx = px.ids[x.id];
1713
+ const ry = py.ids[y.id];
1714
+ const ky = py.keys(y);
1715
+ const common = px.keys(x).filter((k) => ky.includes(k));
1716
+ if (!common.length) {
1717
+ if (rx === "add" && ry === "add" && px.orderedAdds) return barrier2(`${px.family}: two added to one sheet at once (their order is state)`);
1718
+ return ok2([x]);
1719
+ }
1720
+ if (rx === "remove") {
1721
+ if (ry !== "remove") return ok2([x]);
1722
+ const rest = px.withoutKeys ? px.withoutKeys(x, common) : null;
1723
+ return ok2(rest ? [rest] : []);
1724
+ }
1725
+ if (ry === "remove") return ok2([], [], told(x, `${px.family} ${common.join(",")}: removed at the same time`));
1726
+ const fx = px.facet?.(x) ?? "all";
1727
+ const fy = py.facet?.(y) ?? "all";
1728
+ if (fx !== fy && fx !== "all" && fy !== "all") return ok2([x]);
1729
+ const dx = isDerived(x);
1730
+ const dy = isDerived(y);
1731
+ const xWins = dx !== dy ? !dx : side === "left";
1732
+ if (xWins) return ok2([x], told(y, `${py.family} ${common.join(",")}`));
1733
+ const narrowed = fx === "all" && fy !== "all" ? px.narrow?.(x, fy) : null;
1734
+ return ok2(narrowed ? [narrowed] : [], [], told(x, `${px.family} ${common.join(",")}`));
1735
+ }
1537
1736
  function familyOf(id) {
1538
1737
  const e = SHEET_MUTATIONS[id];
1539
1738
  return e && e.kind === "covered" ? e.family : "";
@@ -1561,6 +1760,8 @@ function transformMutation(x, y, side) {
1561
1760
  const fy = familyOf(y.id);
1562
1761
  const sy = structuralOf(y);
1563
1762
  const sx = structuralOf(x);
1763
+ const rp = rangedPair(x, y, side, sx, sy);
1764
+ if (rp) return rp;
1564
1765
  const sortp = sortPair(x, y, sx, sy);
1565
1766
  if (sortp) return sortp;
1566
1767
  const cp = countPair(x, y, sy);