ooxml-core 1.2.0 → 1.2.1

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 (82) hide show
  1. package/dist/{PptxHandler-SJTINPU4.mjs → PptxHandler-VQQS4VS6.mjs} +2 -2
  2. package/dist/automation/index.cjs +1059 -328
  3. package/dist/automation/index.mjs +1059 -328
  4. package/dist/chart/bar-cluster-geometry.d.ts +6 -0
  5. package/dist/chart/bar-cluster-geometry.d.ts.map +1 -1
  6. package/dist/chart/index.cjs +5 -2
  7. package/dist/chart/index.mjs +5 -3
  8. package/dist/chart/render/chart-spacing.d.ts.map +1 -1
  9. package/dist/{chunk-TEOAOAIF.mjs → chunk-5U27IVB7.mjs} +3 -3
  10. package/dist/{chunk-VYTA2OUN.mjs → chunk-5VLNF4OX.mjs} +1 -1
  11. package/dist/{chunk-CQQAPUAG.mjs → chunk-V2BIGIH4.mjs} +11 -9
  12. package/dist/{chunk-FI4RSJ24.mjs → chunk-XBNSBDNT.mjs} +1 -1
  13. package/dist/{chunk-XCZ7U3GZ.mjs → chunk-XSFHGZFH.mjs} +11 -9
  14. package/dist/docx/index.cjs +4 -2
  15. package/dist/docx/index.mjs +4 -2
  16. package/dist/docx/layout/index.cjs +4 -2
  17. package/dist/docx/layout/index.mjs +4 -2
  18. package/dist/docx/ui/index.cjs +4 -2
  19. package/dist/docx/ui/index.mjs +4 -2
  20. package/dist/index.cjs +5 -2
  21. package/dist/index.mjs +5 -2
  22. package/dist/pptx/automation/index.cjs +27 -17
  23. package/dist/pptx/automation/index.mjs +5 -5
  24. package/dist/pptx/chunks/index-EZJZ7USt.d.ts.map +1 -1
  25. package/dist/pptx/cli/index.cjs +27 -17
  26. package/dist/pptx/cli/index.mjs +4 -4
  27. package/dist/pptx/converter/index.mjs +2 -2
  28. package/dist/pptx/index.cjs +27 -17
  29. package/dist/pptx/index.mjs +5 -5
  30. package/dist/teams/index.cjs +157 -82
  31. package/dist/teams/index.mjs +157 -82
  32. package/dist/xlsx/edit/context.d.ts +3 -1
  33. package/dist/xlsx/edit/context.d.ts.map +1 -1
  34. package/dist/xlsx/edit/history.d.ts +18 -0
  35. package/dist/xlsx/edit/history.d.ts.map +1 -1
  36. package/dist/xlsx/edit/session-calc.d.ts +30 -8
  37. package/dist/xlsx/edit/session-calc.d.ts.map +1 -1
  38. package/dist/xlsx/edit/session.d.ts.map +1 -1
  39. package/dist/xlsx/edit/sheets.d.ts.map +1 -1
  40. package/dist/xlsx/edit/types.d.ts +23 -3
  41. package/dist/xlsx/edit/types.d.ts.map +1 -1
  42. package/dist/xlsx/formula/engine-core.d.ts.map +1 -1
  43. package/dist/xlsx/formula/engine-host.d.ts +6 -2
  44. package/dist/xlsx/formula/engine-host.d.ts.map +1 -1
  45. package/dist/xlsx/formula/engine-prepare.d.ts +33 -0
  46. package/dist/xlsx/formula/engine-prepare.d.ts.map +1 -0
  47. package/dist/xlsx/formula/engine-structure.d.ts +40 -0
  48. package/dist/xlsx/formula/engine-structure.d.ts.map +1 -0
  49. package/dist/xlsx/formula/engine-types.d.ts +26 -0
  50. package/dist/xlsx/formula/engine-types.d.ts.map +1 -1
  51. package/dist/xlsx/formula/engine.d.ts.map +1 -1
  52. package/dist/xlsx/formula/formula-shape.d.ts +28 -0
  53. package/dist/xlsx/formula/formula-shape.d.ts.map +1 -0
  54. package/dist/xlsx/formula/graph-index.d.ts +3 -1
  55. package/dist/xlsx/formula/graph-index.d.ts.map +1 -1
  56. package/dist/xlsx/formula/graph.d.ts +5 -0
  57. package/dist/xlsx/formula/graph.d.ts.map +1 -1
  58. package/dist/xlsx/formula/lexemes.d.ts.map +1 -1
  59. package/dist/xlsx/formula/parse-cache.d.ts +9 -0
  60. package/dist/xlsx/formula/parse-cache.d.ts.map +1 -0
  61. package/dist/xlsx/formula/parser.d.ts +3 -0
  62. package/dist/xlsx/formula/parser.d.ts.map +1 -1
  63. package/dist/xlsx/formula/ref-text.d.ts +14 -0
  64. package/dist/xlsx/formula/ref-text.d.ts.map +1 -0
  65. package/dist/xlsx/formula/references.d.ts.map +1 -1
  66. package/dist/xlsx/formula/shift-formula.d.ts +19 -0
  67. package/dist/xlsx/formula/shift-formula.d.ts.map +1 -0
  68. package/dist/xlsx/formula/shift-graph.d.ts +42 -0
  69. package/dist/xlsx/formula/shift-graph.d.ts.map +1 -0
  70. package/dist/xlsx/formula/transform.d.ts +2 -23
  71. package/dist/xlsx/formula/transform.d.ts.map +1 -1
  72. package/dist/xlsx/index.cjs +1069 -329
  73. package/dist/xlsx/index.mjs +1069 -330
  74. package/dist/xlsx/layout/index.d.ts +1 -1
  75. package/dist/xlsx/layout/index.d.ts.map +1 -1
  76. package/dist/xlsx/layout/sheet-queries.d.ts +7 -0
  77. package/dist/xlsx/layout/sheet-queries.d.ts.map +1 -1
  78. package/dist/xlsx/load/index.cjs +152 -90
  79. package/dist/xlsx/load/index.mjs +152 -90
  80. package/dist/xlsx/ui/index.cjs +1071 -340
  81. package/dist/xlsx/ui/index.mjs +1071 -340
  82. package/package.json +1 -1
@@ -2108,7 +2108,7 @@ var NAME_RE = /^[A-Za-z_\\\u00A1-\uFFFF][A-Za-z0-9_.?\\\u00A1-\uFFFF]*/;
2108
2108
  var CELL_RE = /^(\$?)([A-Za-z]{1,3})(\$?)(\d{1,7})/;
2109
2109
  var COLS_RE = /^(\$?)([A-Za-z]{1,3}):(\$?)([A-Za-z]{1,3})/;
2110
2110
  var ROWS_RE = /^(\$?)(\d{1,7}):(\$?)(\d{1,7})/;
2111
- var NUMBER_RE2 = /^(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?/;
2111
+ var NUMBER_RE2 = /^(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?/;
2112
2112
  var sticky = (re) => new RegExp(re.source.replace(/^\^/, ""), "y");
2113
2113
  var CELL_AT = sticky(CELL_RE);
2114
2114
  var COLS_AT = sticky(COLS_RE);
@@ -2480,7 +2480,9 @@ var PREC_INTERSECT = 9;
2480
2480
  var PREC_RANGE = 10;
2481
2481
  var OPERAND_START = /* @__PURE__ */ new Set(["ref", "name", "func", "open", "structured"]);
2482
2482
  function parseFormula(formula) {
2483
- const all2 = tokenize(formula);
2483
+ return parseTokens(tokenize(formula));
2484
+ }
2485
+ function parseTokens(all2) {
2484
2486
  const tokens2 = [];
2485
2487
  const wsBefore = [];
2486
2488
  let sawWs = false;
@@ -2765,6 +2767,222 @@ function localDateToSerial(date, date1904 = date1904Mode) {
2765
2767
  return base + (utc - dayStart) / DAY_MS;
2766
2768
  }
2767
2769
 
2770
+ // xlsx/formula/area-index.ts
2771
+ var BUCKET_WIDTH = 64;
2772
+ var CHUNK_SHIFT = 6;
2773
+ var CHUNKS = 64;
2774
+ function push(map, key, value) {
2775
+ const list = map.get(key);
2776
+ if (list) list.push(value);
2777
+ else map.set(key, [value]);
2778
+ }
2779
+ function inner(map, sheet) {
2780
+ let found = map.get(sheet);
2781
+ if (!found) map.set(sheet, found = /* @__PURE__ */ new Map());
2782
+ return found;
2783
+ }
2784
+ var AreaIndex = class {
2785
+ cells = /* @__PURE__ */ new Map();
2786
+ chunked = /* @__PURE__ */ new Map();
2787
+ tall = /* @__PURE__ */ new Map();
2788
+ wide = /* @__PURE__ */ new Map();
2789
+ add(area, node) {
2790
+ const { start, end } = area.range;
2791
+ if (start.row === end.row && end.col - start.col + 1 <= BUCKET_WIDTH) {
2792
+ const cells = inner(this.cells, area.sheet);
2793
+ for (let col = start.col; col <= end.col; col++) push(cells, cellKey(start.row, col), node);
2794
+ return;
2795
+ }
2796
+ const entry3 = { range: area.range, node };
2797
+ if (end.col - start.col + 1 > BUCKET_WIDTH) {
2798
+ const list = this.wide.get(area.sheet);
2799
+ if (list) list.push(entry3);
2800
+ else this.wide.set(area.sheet, [entry3]);
2801
+ return;
2802
+ }
2803
+ const first2 = start.row >> CHUNK_SHIFT;
2804
+ const last = end.row >> CHUNK_SHIFT;
2805
+ if (last - first2 + 1 > CHUNKS) {
2806
+ const cols2 = inner(this.tall, area.sheet);
2807
+ for (let c2 = start.col; c2 <= end.col; c2++) push(cols2, c2, entry3);
2808
+ return;
2809
+ }
2810
+ const cols = inner(this.chunked, area.sheet);
2811
+ for (let c2 = start.col; c2 <= end.col; c2++) {
2812
+ let chunks = cols.get(c2);
2813
+ if (!chunks) cols.set(c2, chunks = /* @__PURE__ */ new Map());
2814
+ for (let k = first2; k <= last; k++) push(chunks, k, entry3);
2815
+ }
2816
+ }
2817
+ /** Items whose areas intersect `range` on `sheet` (each once). */
2818
+ dependents(sheet, range, out) {
2819
+ const found = out instanceof Set ? out : /* @__PURE__ */ new Set();
2820
+ this.collect(sheet, range, found);
2821
+ if (Array.isArray(out)) for (const node of found) out.push(node);
2822
+ }
2823
+ collect(sheet, range, out) {
2824
+ const { start, end } = range;
2825
+ const check3 = (list) => {
2826
+ if (list) {
2827
+ for (const entry3 of list) if (rangesIntersect(entry3.range, range)) out.add(entry3.node);
2828
+ }
2829
+ };
2830
+ const cells = this.cells.get(sheet);
2831
+ if (cells) {
2832
+ const size = (end.row - start.row + 1) * (end.col - start.col + 1);
2833
+ if (size <= 4096) {
2834
+ for (let r = start.row; r <= end.row; r++) {
2835
+ for (let c2 = start.col; c2 <= end.col; c2++) {
2836
+ const list = cells.get(cellKey(r, c2));
2837
+ if (list) for (const node of list) out.add(node);
2838
+ }
2839
+ }
2840
+ } else {
2841
+ for (const [key, list] of cells) {
2842
+ const { row: row2, col } = keyToAddress(key);
2843
+ if (row2 >= start.row && row2 <= end.row && col >= start.col && col <= end.col)
2844
+ for (const node of list) out.add(node);
2845
+ }
2846
+ }
2847
+ }
2848
+ const columns = (map, visit2) => {
2849
+ if (!map) return;
2850
+ if (end.col - start.col + 1 <= map.size) {
2851
+ for (let c2 = start.col; c2 <= end.col; c2++) if (map.has(c2)) visit2(c2);
2852
+ } else for (const c2 of map.keys()) if (c2 >= start.col && c2 <= end.col) visit2(c2);
2853
+ };
2854
+ const chunked = this.chunked.get(sheet);
2855
+ const first2 = start.row >> CHUNK_SHIFT;
2856
+ const last = end.row >> CHUNK_SHIFT;
2857
+ columns(chunked, (c2) => {
2858
+ const chunks = chunked?.get(c2);
2859
+ if (last - first2 + 1 <= chunks.size) {
2860
+ for (let k = first2; k <= last; k++) check3(chunks.get(k));
2861
+ } else for (const [k, list] of chunks) if (k >= first2 && k <= last) check3(list);
2862
+ });
2863
+ const tall = this.tall.get(sheet);
2864
+ columns(tall, (c2) => check3(tall?.get(c2)));
2865
+ check3(this.wide.get(sheet));
2866
+ }
2867
+ };
2868
+
2869
+ // xlsx/formula/graph-index.ts
2870
+ var ColumnIndex = class {
2871
+ sheets = /* @__PURE__ */ new Map();
2872
+ constructor(nodes) {
2873
+ for (const node of nodes) {
2874
+ let cols = this.sheets.get(node.sheet);
2875
+ if (!cols) this.sheets.set(node.sheet, cols = /* @__PURE__ */ new Map());
2876
+ let list = cols.get(node.col);
2877
+ if (!list) cols.set(node.col, list = []);
2878
+ list.push(node);
2879
+ }
2880
+ for (const cols of this.sheets.values())
2881
+ for (const list of cols.values()) list.sort((a2, b) => a2.row - b.row);
2882
+ }
2883
+ nodesIn(area, out) {
2884
+ const cols = this.sheets.get(area.sheet);
2885
+ if (!cols) return;
2886
+ const { start, end } = area.range;
2887
+ const width = end.col - start.col + 1;
2888
+ const scan4 = (list) => {
2889
+ let lo = 0;
2890
+ let hi = list.length;
2891
+ while (lo < hi) {
2892
+ const mid = lo + hi >> 1;
2893
+ if (list[mid].row < start.row) lo = mid + 1;
2894
+ else hi = mid;
2895
+ }
2896
+ for (let i2 = lo; i2 < list.length; i2++) {
2897
+ const node = list[i2];
2898
+ if (node.row > end.row) break;
2899
+ out.push(node);
2900
+ }
2901
+ };
2902
+ if (width <= cols.size) {
2903
+ for (let c2 = start.col; c2 <= end.col; c2++) {
2904
+ const list = cols.get(c2);
2905
+ if (list) scan4(list);
2906
+ }
2907
+ } else {
2908
+ for (const [c2, list] of cols) if (c2 >= start.col && c2 <= end.col) scan4(list);
2909
+ }
2910
+ }
2911
+ };
2912
+ var ReverseIndex = class extends AreaIndex {
2913
+ constructor(nodes = []) {
2914
+ super();
2915
+ for (const node of nodes) this.addNode(node);
2916
+ }
2917
+ /** Indexes one more formula's precedents. */
2918
+ addNode(node) {
2919
+ for (const dep of node.deps) this.add(dep, node);
2920
+ for (const dep of node.dynamicDeps) this.add(dep, node);
2921
+ }
2922
+ };
2923
+
2924
+ // xlsx/formula/engine-prepare.ts
2925
+ var CHECK_EVERY = 32;
2926
+ var GraphPreparation = class {
2927
+ constructor(workbook, create) {
2928
+ this.workbook = workbook;
2929
+ this.create = create;
2930
+ }
2931
+ workbook;
2932
+ create;
2933
+ reverse = new ReverseIndex();
2934
+ formulas = { dynamic: [], unvalued: [] };
2935
+ sheet = 0;
2936
+ rows;
2937
+ row = 0;
2938
+ cells;
2939
+ finished = false;
2940
+ /**
2941
+ * Adds formulas to the graph until `timeRemaining` (milliseconds, as an idle deadline reports
2942
+ * them) reaches zero, or until every formula is in. Returns true when the graph is complete.
2943
+ */
2944
+ run(timeRemaining) {
2945
+ let visited = 0;
2946
+ while (!this.finished) {
2947
+ const cells = this.cells ?? this.nextRow();
2948
+ if (!cells) continue;
2949
+ const next = cells.next();
2950
+ if (next.done) {
2951
+ this.cells = void 0;
2952
+ continue;
2953
+ }
2954
+ const [col, cell] = next.value;
2955
+ if (cell.formula) this.add(this.create(this.sheet, this.row, col, cell), cell);
2956
+ if (timeRemaining && ++visited % CHECK_EVERY === 0 && timeRemaining() <= 0) break;
2957
+ }
2958
+ return this.finished;
2959
+ }
2960
+ add(node, cell) {
2961
+ this.reverse.addNode(node);
2962
+ if (!node.legacy && !node.arrayRange) this.formulas.dynamic.push(node);
2963
+ if (cell.value === null && !node.keepCached) this.formulas.unvalued.push(node);
2964
+ }
2965
+ /** Moves to the next stored row (and sheet), or marks the walk finished. */
2966
+ nextRow() {
2967
+ for (; ; ) {
2968
+ const sheet = this.workbook.sheets[this.sheet];
2969
+ if (!sheet) {
2970
+ this.finished = true;
2971
+ return void 0;
2972
+ }
2973
+ this.rows ??= sheet.rows.entries();
2974
+ const next = this.rows.next();
2975
+ if (!next.done) {
2976
+ this.row = next.value[0];
2977
+ this.cells = next.value[1].entries();
2978
+ return this.cells;
2979
+ }
2980
+ this.rows = void 0;
2981
+ this.sheet++;
2982
+ }
2983
+ }
2984
+ };
2985
+
2768
2986
  // xlsx/formula/text-number.ts
2769
2987
  var MONTHS2 = [
2770
2988
  "jan",
@@ -5899,8 +6117,10 @@ var FINANCIAL_FUNCTIONS = [
5899
6117
 
5900
6118
  // xlsx/formula/references.ts
5901
6119
  function sheetIndex(host, name) {
6120
+ const sheets = host.workbook.sheets;
6121
+ for (let i2 = 0; i2 < sheets.length; i2++) if (sheets[i2]?.name === name) return i2;
5902
6122
  const lower2 = name.toLowerCase();
5903
- return host.workbook.sheets.findIndex((sheet) => sheet.name.toLowerCase() === lower2);
6123
+ return sheets.findIndex((sheet) => sheet.name.toLowerCase() === lower2);
5904
6124
  }
5905
6125
  function prefixSheets(host, prefix, current) {
5906
6126
  if (!prefix) return [current];
@@ -7470,9 +7690,9 @@ function readBlock(ctx, area, rows, cols) {
7470
7690
  if (host.readBlock) return host.readBlock(area.sheet, range);
7471
7691
  return Matrix.build(rows, cols, (r, c2) => ctx.readCell(area.sheet, row2 + r, col + c2));
7472
7692
  }
7473
- function shapedMatrices(ctx, values2, sameShape) {
7693
+ function shapedMatrices(ctx, values2, sameShape2) {
7474
7694
  let { rows, cols } = shapeOf(values2[0] ?? null);
7475
- if (sameShape) {
7695
+ if (sameShape2) {
7476
7696
  for (const value of values2) {
7477
7697
  const shape2 = shapeOf(value);
7478
7698
  if (shape2.rows !== rows || shape2.cols !== cols) fail(ERR.VALUE);
@@ -7498,12 +7718,12 @@ function shapedMatrices(ctx, values2, sameShape) {
7498
7718
  });
7499
7719
  return { matrices, total: total2, block: rows * cols };
7500
7720
  }
7501
- function matchingCells(ctx, pairs, target, sameShape) {
7721
+ function matchingCells(ctx, pairs, target, sameShape2) {
7502
7722
  const ranges = pairs.map((p) => p.range);
7503
7723
  const { matrices, total: total2, block } = shapedMatrices(
7504
7724
  ctx,
7505
7725
  target === void 0 ? ranges : [...ranges, target],
7506
- sameShape
7726
+ sameShape2
7507
7727
  );
7508
7728
  const tests = pairs.map((p) => makeCriteria(scalar(p.criteria)));
7509
7729
  const values2 = matrices[matrices.length - 1];
@@ -7542,7 +7762,7 @@ function indexedCandidates(pairs, matrices) {
7542
7762
  }
7543
7763
  return void 0;
7544
7764
  }
7545
- var matchingValues = (ctx, pairs, target, sameShape) => matchingCells(ctx, pairs, target, sameShape).values;
7765
+ var matchingValues = (ctx, pairs, target, sameShape2) => matchingCells(ctx, pairs, target, sameShape2).values;
7546
7766
  function criteriaPairs(args, from) {
7547
7767
  if ((args.length - from) % 2 !== 0) fail(ERR.VALUE);
7548
7768
  const pairs = [];
@@ -11436,7 +11656,7 @@ var REFERENCE_ARGUMENT = /* @__PURE__ */ new Set([
11436
11656
  "AREAS",
11437
11657
  "ISREF"
11438
11658
  ]);
11439
- var push = (c2, area) => {
11659
+ var push2 = (c2, area) => {
11440
11660
  (c2.dynamic > 0 ? c2.dynamicDeps : c2.deps).push(area);
11441
11661
  };
11442
11662
  function spans(areas) {
@@ -11462,7 +11682,7 @@ function visit(node, c2) {
11462
11682
  if (!sheets) return;
11463
11683
  const range = specRange(node.ref);
11464
11684
  const target = node.spill ? { start: range.start, end: { ...range.start } } : range;
11465
- for (const sheet of sheets) push(c2, { sheet, range: target });
11685
+ for (const sheet of sheets) push2(c2, { sheet, range: target });
11466
11686
  return;
11467
11687
  }
11468
11688
  case "name": {
@@ -11536,7 +11756,7 @@ function visitBinary(node, c2) {
11536
11756
  const list = c2.dynamic > 0 ? c2.dynamicDeps : c2.deps;
11537
11757
  const l = list[list.length - 2];
11538
11758
  const r = list[list.length - 1];
11539
- if (l && r && l.sheet === r.sheet) push(c2, spans([l, r])[0]);
11759
+ if (l && r && l.sheet === r.sheet) push2(c2, spans([l, r])[0]);
11540
11760
  continue;
11541
11761
  }
11542
11762
  const mentioned = [...c2.deps.slice(staticStart), ...c2.dynamicDeps.slice(dynamicStart)];
@@ -11552,7 +11772,7 @@ function addStructured(ref, c2) {
11552
11772
  sheet: c2.sheet,
11553
11773
  row: c2.row,
11554
11774
  col: c2.col});
11555
- if (value instanceof RefValue) for (const area of value.areas) push(c2, area);
11775
+ if (value instanceof RefValue) for (const area of value.areas) push2(c2, area);
11556
11776
  }
11557
11777
  function analyze(ast, host, sheet, row2, col) {
11558
11778
  const c2 = {
@@ -11576,155 +11796,191 @@ function analyze(ast, host, sheet, row2, col) {
11576
11796
  };
11577
11797
  }
11578
11798
 
11579
- // xlsx/formula/area-index.ts
11580
- var BUCKET_WIDTH = 64;
11581
- var CHUNK_SHIFT = 6;
11582
- var CHUNKS = 64;
11583
- function push2(map, key, value) {
11584
- const list = map.get(key);
11585
- if (list) list.push(value);
11586
- else map.set(key, [value]);
11799
+ // xlsx/formula/formula-shape.ts
11800
+ var isDigit4 = (code) => code >= 48 && code <= 57;
11801
+ function formulaShape(formula) {
11802
+ const starts = [];
11803
+ const ends = [];
11804
+ let hash = 2166136261;
11805
+ for (let i2 = 0; i2 < formula.length; i2++) {
11806
+ let code = formula.charCodeAt(i2);
11807
+ if (isDigit4(code)) {
11808
+ let j = i2 + 1;
11809
+ while (j < formula.length && isDigit4(formula.charCodeAt(j))) j++;
11810
+ starts.push(i2);
11811
+ ends.push(j);
11812
+ i2 = j - 1;
11813
+ code = 0;
11814
+ }
11815
+ hash = Math.imul(hash ^ code, 16777619);
11816
+ }
11817
+ return { hash: hash >>> 0, starts, ends };
11587
11818
  }
11588
- function inner(map, sheet) {
11589
- let found = map.get(sheet);
11590
- if (!found) map.set(sheet, found = /* @__PURE__ */ new Map());
11591
- return found;
11819
+ function sameText(a2, from, b, at, length) {
11820
+ for (let k = 0; k < length; k++)
11821
+ if (a2.charCodeAt(from + k) !== b.charCodeAt(at + k)) return false;
11822
+ return true;
11592
11823
  }
11593
- var AreaIndex = class {
11594
- cells = /* @__PURE__ */ new Map();
11595
- chunked = /* @__PURE__ */ new Map();
11596
- tall = /* @__PURE__ */ new Map();
11597
- wide = /* @__PURE__ */ new Map();
11598
- add(area, node) {
11599
- const { start, end } = area.range;
11600
- if (start.row === end.row && end.col - start.col + 1 <= BUCKET_WIDTH) {
11601
- const cells = inner(this.cells, area.sheet);
11602
- for (let col = start.col; col <= end.col; col++) push2(cells, cellKey(start.row, col), node);
11603
- return;
11824
+ function sameShape(a2, as, b, bs) {
11825
+ const runs = as.starts.length;
11826
+ if (runs !== bs.starts.length) return false;
11827
+ let fromA = 0;
11828
+ let fromB = 0;
11829
+ for (let i2 = 0; i2 <= runs; i2++) {
11830
+ const toA = i2 < runs ? as.starts[i2] : a2.length;
11831
+ const toB = i2 < runs ? bs.starts[i2] : b.length;
11832
+ if (toA - fromA !== toB - fromB || !sameText(a2, fromA, b, fromB, toA - fromA)) return false;
11833
+ fromA = as.ends[i2] ?? 0;
11834
+ fromB = bs.ends[i2] ?? 0;
11835
+ }
11836
+ return true;
11837
+ }
11838
+ function makeTemplate(formula, shape2) {
11839
+ let tokens2;
11840
+ let ast;
11841
+ try {
11842
+ tokens2 = tokenize(formula);
11843
+ ast = parseTokens(tokens2);
11844
+ } catch (e) {
11845
+ if (e instanceof RangeError) return new FormulaError(e.message, -1, "#VALUE!");
11846
+ if (e instanceof FormulaError) return e;
11847
+ throw e;
11848
+ }
11849
+ const { starts } = shape2;
11850
+ const slotted = starts.map(() => false);
11851
+ const refs = /* @__PURE__ */ new Map();
11852
+ let run2 = 0;
11853
+ for (const token of tokens2) {
11854
+ const end = token.start + token.text.length;
11855
+ const first2 = run2;
11856
+ while (run2 < starts.length && starts[run2] < end) run2++;
11857
+ const ref = token.kind === "ref" ? token.ref : void 0;
11858
+ if (!ref || ref.kind === "cols") continue;
11859
+ const count2 = ref.kind === "cell" ? 1 : 2;
11860
+ if (run2 - first2 < count2) continue;
11861
+ const at = run2 - count2;
11862
+ const indexes2 = count2 === 1 ? [at] : [at, at + 1];
11863
+ if (indexes2.some((i2) => formula.charCodeAt(starts[i2]) === 48)) continue;
11864
+ for (const i2 of indexes2) slotted[i2] = true;
11865
+ refs.set(ref, count2 === 1 ? { start: at } : { start: at, end: at + 1 });
11866
+ }
11867
+ return { formula, shape: shape2, ast, slotted, refs };
11868
+ }
11869
+ function rowsFor(t, formula, shape2) {
11870
+ if (!sameShape(t.formula, t.shape, formula, shape2)) return void 0;
11871
+ const rows = [];
11872
+ for (let i2 = 0; i2 < shape2.starts.length; i2++) {
11873
+ const start = shape2.starts[i2];
11874
+ const length = shape2.ends[i2] - start;
11875
+ if (!t.slotted[i2]) {
11876
+ const own = t.shape.starts[i2];
11877
+ if (t.shape.ends[i2] - own !== length) return void 0;
11878
+ if (!sameText(t.formula, own, formula, start, length)) return void 0;
11879
+ rows.push(-1);
11880
+ continue;
11604
11881
  }
11605
- const entry3 = { range: area.range, node };
11606
- if (end.col - start.col + 1 > BUCKET_WIDTH) {
11607
- const list = this.wide.get(area.sheet);
11608
- if (list) list.push(entry3);
11609
- else this.wide.set(area.sheet, [entry3]);
11610
- return;
11882
+ if (length > 7 || formula.charCodeAt(start) === 48) return void 0;
11883
+ let row2 = 0;
11884
+ for (let k = start; k < start + length; k++) row2 = row2 * 10 + formula.charCodeAt(k) - 48;
11885
+ if (row2 < 1 || row2 > MAX_ROW + 1) return void 0;
11886
+ rows.push(row2 - 1);
11887
+ }
11888
+ return rows;
11889
+ }
11890
+ function shiftSpec(spec2, slots, rows) {
11891
+ const startRow = rows[slots.start];
11892
+ const endRow = slots.end === void 0 ? startRow : rows[slots.end];
11893
+ if (startRow === spec2.start.row && endRow === spec2.end.row) return spec2;
11894
+ const { start: from, end: to } = spec2;
11895
+ const start = { row: startRow, col: from.col, rowAbs: from.rowAbs, colAbs: from.colAbs };
11896
+ if (slots.end === void 0) return { kind: spec2.kind, start, end: start };
11897
+ const end = { row: endRow, col: to.col, rowAbs: to.rowAbs, colAbs: to.colAbs };
11898
+ return { kind: spec2.kind, start, end };
11899
+ }
11900
+ function copyTree(node, t, rows) {
11901
+ switch (node.type) {
11902
+ case "ref": {
11903
+ const slots = node.ref ? t.refs.get(node.ref) : void 0;
11904
+ if (!slots || !node.ref) return node;
11905
+ const ref = shiftSpec(node.ref, slots, rows);
11906
+ if (ref === node.ref) return node;
11907
+ const copy = { type: "ref" };
11908
+ if (node.prefix) copy.prefix = node.prefix;
11909
+ copy.ref = ref;
11910
+ if (node.spill) copy.spill = true;
11911
+ return copy;
11611
11912
  }
11612
- const first2 = start.row >> CHUNK_SHIFT;
11613
- const last = end.row >> CHUNK_SHIFT;
11614
- if (last - first2 + 1 > CHUNKS) {
11615
- const cols2 = inner(this.tall, area.sheet);
11616
- for (let c2 = start.col; c2 <= end.col; c2++) push2(cols2, c2, entry3);
11617
- return;
11913
+ case "unary":
11914
+ case "percent": {
11915
+ const operand4 = copyTree(node.operand, t, rows);
11916
+ if (operand4 === node.operand) return node;
11917
+ return node.type === "unary" ? { type: "unary", op: node.op, operand: operand4 } : { type: "percent", operand: operand4 };
11618
11918
  }
11619
- const cols = inner(this.chunked, area.sheet);
11620
- for (let c2 = start.col; c2 <= end.col; c2++) {
11621
- let chunks = cols.get(c2);
11622
- if (!chunks) cols.set(c2, chunks = /* @__PURE__ */ new Map());
11623
- for (let k = first2; k <= last; k++) push2(chunks, k, entry3);
11919
+ case "binary": {
11920
+ const left2 = copyTree(node.left, t, rows);
11921
+ const right2 = copyTree(node.right, t, rows);
11922
+ if (left2 === node.left && right2 === node.right) return node;
11923
+ return { type: "binary", op: node.op, left: left2, right: right2 };
11624
11924
  }
11625
- }
11626
- /** Items whose areas intersect `range` on `sheet` (each once). */
11627
- dependents(sheet, range, out) {
11628
- const found = out instanceof Set ? out : /* @__PURE__ */ new Set();
11629
- this.collect(sheet, range, found);
11630
- if (Array.isArray(out)) for (const node of found) out.push(node);
11631
- }
11632
- collect(sheet, range, out) {
11633
- const { start, end } = range;
11634
- const check3 = (list) => {
11635
- if (list) {
11636
- for (const entry3 of list) if (rangesIntersect(entry3.range, range)) out.add(entry3.node);
11637
- }
11638
- };
11639
- const cells = this.cells.get(sheet);
11640
- if (cells) {
11641
- const size = (end.row - start.row + 1) * (end.col - start.col + 1);
11642
- if (size <= 4096) {
11643
- for (let r = start.row; r <= end.row; r++) {
11644
- for (let c2 = start.col; c2 <= end.col; c2++) {
11645
- const list = cells.get(cellKey(r, c2));
11646
- if (list) for (const node of list) out.add(node);
11647
- }
11648
- }
11649
- } else {
11650
- for (const [key, list] of cells) {
11651
- const { row: row2, col } = keyToAddress(key);
11652
- if (row2 >= start.row && row2 <= end.row && col >= start.col && col <= end.col)
11653
- for (const node of list) out.add(node);
11654
- }
11655
- }
11925
+ case "call": {
11926
+ const args = copyAll(node.args, t, rows);
11927
+ if (args === node.args) return node;
11928
+ return { type: "call", name: node.name, rawName: node.rawName, args };
11656
11929
  }
11657
- const columns = (map, visit2) => {
11658
- if (!map) return;
11659
- if (end.col - start.col + 1 <= map.size) {
11660
- for (let c2 = start.col; c2 <= end.col; c2++) if (map.has(c2)) visit2(c2);
11661
- } else for (const c2 of map.keys()) if (c2 >= start.col && c2 <= end.col) visit2(c2);
11662
- };
11663
- const chunked = this.chunked.get(sheet);
11664
- const first2 = start.row >> CHUNK_SHIFT;
11665
- const last = end.row >> CHUNK_SHIFT;
11666
- columns(chunked, (c2) => {
11667
- const chunks = chunked?.get(c2);
11668
- if (last - first2 + 1 <= chunks.size) {
11669
- for (let k = first2; k <= last; k++) check3(chunks.get(k));
11670
- } else for (const [k, list] of chunks) if (k >= first2 && k <= last) check3(list);
11671
- });
11672
- const tall = this.tall.get(sheet);
11673
- columns(tall, (c2) => check3(tall?.get(c2)));
11674
- check3(this.wide.get(sheet));
11675
- }
11676
- };
11677
-
11678
- // xlsx/formula/graph-index.ts
11679
- var ColumnIndex = class {
11680
- sheets = /* @__PURE__ */ new Map();
11681
- constructor(nodes) {
11682
- for (const node of nodes) {
11683
- let cols = this.sheets.get(node.sheet);
11684
- if (!cols) this.sheets.set(node.sheet, cols = /* @__PURE__ */ new Map());
11685
- let list = cols.get(node.col);
11686
- if (!list) cols.set(node.col, list = []);
11687
- list.push(node);
11930
+ case "invoke": {
11931
+ const callee = copyTree(node.callee, t, rows);
11932
+ const args = copyAll(node.args, t, rows);
11933
+ if (callee === node.callee && args === node.args) return node;
11934
+ return { type: "invoke", callee, args };
11688
11935
  }
11689
- for (const cols of this.sheets.values())
11690
- for (const list of cols.values()) list.sort((a2, b) => a2.row - b.row);
11936
+ default:
11937
+ return node;
11691
11938
  }
11692
- nodesIn(area, out) {
11693
- const cols = this.sheets.get(area.sheet);
11694
- if (!cols) return;
11695
- const { start, end } = area.range;
11696
- const width = end.col - start.col + 1;
11697
- const scan4 = (list) => {
11698
- let lo = 0;
11699
- let hi = list.length;
11700
- while (lo < hi) {
11701
- const mid = lo + hi >> 1;
11702
- if (list[mid].row < start.row) lo = mid + 1;
11703
- else hi = mid;
11704
- }
11705
- for (let i2 = lo; i2 < list.length; i2++) {
11706
- const node = list[i2];
11707
- if (node.row > end.row) break;
11708
- out.push(node);
11709
- }
11710
- };
11711
- if (width <= cols.size) {
11712
- for (let c2 = start.col; c2 <= end.col; c2++) {
11713
- const list = cols.get(c2);
11714
- if (list) scan4(list);
11715
- }
11716
- } else {
11717
- for (const [c2, list] of cols) if (c2 >= start.col && c2 <= end.col) scan4(list);
11718
- }
11939
+ }
11940
+ function copyAll(nodes, t, rows) {
11941
+ let out;
11942
+ for (let i2 = 0; i2 < nodes.length; i2++) {
11943
+ const node = nodes[i2];
11944
+ const copy = copyTree(node, t, rows);
11945
+ if (copy !== node && !out) out = nodes.slice(0, i2);
11946
+ out?.push(copy);
11947
+ }
11948
+ return out ?? nodes;
11949
+ }
11950
+ function fromTemplate(t, formula, shape2) {
11951
+ const rows = rowsFor(t, formula, shape2);
11952
+ return rows ? copyTree(t.ast, t, rows) : void 0;
11953
+ }
11954
+
11955
+ // xlsx/formula/parse-cache.ts
11956
+ var MAX_TEMPLATE_LENGTH = 2048;
11957
+ var MAX_ENTRIES = 1e5;
11958
+ var MAX_TEMPLATES = 1e4;
11959
+ var NO_RUNS = { hash: 0, starts: [], ends: [] };
11960
+ var treeOf = (made) => "ast" in made ? made.ast : made;
11961
+ var ParseCache = class {
11962
+ byText = /* @__PURE__ */ new Map();
11963
+ templates = /* @__PURE__ */ new Map();
11964
+ parse(formula) {
11965
+ let ast = this.byText.get(formula);
11966
+ if (ast) return ast;
11967
+ ast = this.parseNew(formula);
11968
+ if (this.byText.size > MAX_ENTRIES) this.byText.clear();
11969
+ this.byText.set(formula, ast);
11970
+ return ast;
11719
11971
  }
11720
- };
11721
- var ReverseIndex = class extends AreaIndex {
11722
- constructor(nodes) {
11723
- super();
11724
- for (const node of nodes) {
11725
- for (const dep of node.deps) this.add(dep, node);
11726
- for (const dep of node.dynamicDeps) this.add(dep, node);
11972
+ parseNew(formula) {
11973
+ if (formula.length > MAX_TEMPLATE_LENGTH) return treeOf(makeTemplate(formula, NO_RUNS));
11974
+ const shape2 = formulaShape(formula);
11975
+ const template = this.templates.get(shape2.hash);
11976
+ const reused = template && fromTemplate(template, formula, shape2);
11977
+ if (reused) return reused;
11978
+ const made = makeTemplate(formula, shape2);
11979
+ if (!template && "ast" in made) {
11980
+ if (this.templates.size > MAX_TEMPLATES) this.templates.clear();
11981
+ this.templates.set(shape2.hash, made);
11727
11982
  }
11983
+ return treeOf(made);
11728
11984
  }
11729
11985
  };
11730
11986
 
@@ -11865,7 +12121,7 @@ var EngineHost = class {
11865
12121
  options;
11866
12122
  nodes = /* @__PURE__ */ new Map();
11867
12123
  footprints = /* @__PURE__ */ new Set();
11868
- astCache = /* @__PURE__ */ new Map();
12124
+ parsed = new ParseCache();
11869
12125
  boundsCache = /* @__PURE__ */ new Map();
11870
12126
  inProgress = /* @__PURE__ */ new Set();
11871
12127
  cycles = /* @__PURE__ */ new Set();
@@ -11889,6 +12145,8 @@ var EngineHost = class {
11889
12145
  evaluations = 0;
11890
12146
  built = false;
11891
12147
  needsFull = false;
12148
+ /** No recalculation has run since the workbook was opened: stored values may stand. */
12149
+ fresh = true;
11892
12150
  // ---- EvalHost ----
11893
12151
  readCell(sheet, row2, col) {
11894
12152
  const cell = this.workbook.sheets[sheet]?.rows.get(row2)?.get(col);
@@ -11977,41 +12235,15 @@ var EngineHost = class {
11977
12235
  return this.workbook.sheets[sheet]?.rows.get(row2)?.get(col)?.formula;
11978
12236
  }
11979
12237
  parse(formula) {
11980
- let ast = this.astCache.get(formula);
11981
- if (!ast) {
11982
- try {
11983
- ast = parseFormula(formula);
11984
- } catch (e) {
11985
- if (e instanceof RangeError) ast = new FormulaError(e.message, -1, "#VALUE!");
11986
- else if (!(e instanceof FormulaError)) throw e;
11987
- else ast = e;
11988
- }
11989
- if (this.astCache.size > 1e5) this.astCache.clear();
11990
- this.astCache.set(formula, ast);
11991
- }
11992
- return ast;
12238
+ return this.parsed.parse(formula);
11993
12239
  }
11994
12240
  now() {
11995
12241
  return this.options.now ? this.options.now() : /* @__PURE__ */ new Date();
11996
12242
  }
11997
- random() {
11998
- return this.options.random ? this.options.random() : Math.random();
11999
- }
12000
- // ---- graph ----
12001
- build() {
12002
- this.nodes.clear();
12003
- this.footprints.clear();
12004
- this.footprintIndex = void 0;
12005
- this.workbook.sheets.forEach((sheet, s) => {
12006
- for (const [row2, cells] of sheet.rows) {
12007
- for (const [col, cell] of cells) {
12008
- if (cell.formula) this.createNode(s, row2, col, cell, true);
12009
- }
12010
- }
12011
- });
12012
- this.built = true;
12013
- this.graphChanged();
12014
- }
12243
+ random() {
12244
+ return this.options.random ? this.options.random() : Math.random();
12245
+ }
12246
+ // ---- graph ----
12015
12247
  createNode(sheet, row2, col, cell, initial) {
12016
12248
  const formula = cell.formula ?? "";
12017
12249
  const parsed = this.parse(formula);
@@ -12040,6 +12272,14 @@ var EngineHost = class {
12040
12272
  map.set(cellKey(row2, col), node);
12041
12273
  return node;
12042
12274
  }
12275
+ /** Parses a node's text again after a structural edit rewrote it (see `FormulaNode.stale`). */
12276
+ refreshAst(node) {
12277
+ const parsed = this.parse(node.formula);
12278
+ delete node.stale;
12279
+ delete node.parseError;
12280
+ node.ast = parsed instanceof FormulaError ? void 0 : parsed;
12281
+ if (parsed instanceof FormulaError && parsed.code) node.parseError = err(parsed.code);
12282
+ }
12043
12283
  removeNode(node, changed) {
12044
12284
  const sheet = this.workbook.sheets[node.sheet];
12045
12285
  if (node.spill && sheet) {
@@ -12470,6 +12710,7 @@ var EngineCore = class extends EngineHost {
12470
12710
  return { host: this, sheet, row: row2, col, scope: void 0, depth: 0, root, legacy };
12471
12711
  }
12472
12712
  evaluateFormula(node) {
12713
+ if (node.stale) this.refreshAst(node);
12473
12714
  if (!node.ast) return node.parseError ?? ERR.NAME;
12474
12715
  const ast = node.ast;
12475
12716
  const frame = this.frameFor(node.sheet, node.row, node.col, ast, node.legacy);
@@ -12487,20 +12728,363 @@ var EngineCore = class extends EngineHost {
12487
12728
  }
12488
12729
  };
12489
12730
 
12731
+ // xlsx/formula/shift-graph.ts
12732
+ var POSITIONAL = /* @__PURE__ */ new Set(["ROW", "COLUMN"]);
12733
+ var DESCRIPTIVE = /* @__PURE__ */ new Set(["FORMULATEXT", "CELL", "SHEET"]);
12734
+ var traitsCache = /* @__PURE__ */ new WeakMap();
12735
+ function astTraits(ast) {
12736
+ const cached = traitsCache.get(ast);
12737
+ if (cached) return cached;
12738
+ const sheets = /* @__PURE__ */ new Set();
12739
+ const traits = { complex: false, opaque: false, positional: false, descriptive: false, sheets };
12740
+ const stack2 = [ast];
12741
+ while (stack2.length) {
12742
+ const node = stack2.pop();
12743
+ switch (node.type) {
12744
+ case "ref":
12745
+ if (node.prefix) {
12746
+ sheets.add(node.prefix.sheet.toLowerCase());
12747
+ if (node.prefix.sheet2 !== void 0) {
12748
+ sheets.add(node.prefix.sheet2.toLowerCase());
12749
+ traits.complex = true;
12750
+ }
12751
+ }
12752
+ break;
12753
+ case "name":
12754
+ if (node.prefix) sheets.add(node.prefix.sheet.toLowerCase());
12755
+ traits.complex = true;
12756
+ break;
12757
+ case "structured":
12758
+ traits.complex = true;
12759
+ break;
12760
+ case "unary":
12761
+ case "percent":
12762
+ stack2.push(node.operand);
12763
+ break;
12764
+ case "binary":
12765
+ stack2.push(node.left, node.right);
12766
+ break;
12767
+ case "call":
12768
+ if (POSITIONAL.has(node.name)) traits.positional = true;
12769
+ if (DESCRIPTIVE.has(node.name)) traits.descriptive = true;
12770
+ if (!getFunction(node.name)) traits.complex = traits.opaque = true;
12771
+ stack2.push(...node.args);
12772
+ break;
12773
+ case "invoke":
12774
+ stack2.push(node.callee, ...node.args);
12775
+ break;
12776
+ }
12777
+ }
12778
+ traitsCache.set(ast, traits);
12779
+ return traits;
12780
+ }
12781
+ function intervalDelta(a2, b, shift2) {
12782
+ const { at, count: count2 } = shift2;
12783
+ if (count2 > 0) {
12784
+ if (b < at) return 0;
12785
+ if (a2 < at) return void 0;
12786
+ return b + count2 > (shift2.axis === "row" ? MAX_ROW : MAX_COL) ? void 0 : count2;
12787
+ }
12788
+ const last = at - count2 - 1;
12789
+ if (b < at) return 0;
12790
+ if (a2 > last) return count2;
12791
+ return void 0;
12792
+ }
12793
+ function areaDelta(area, sheet, shift2) {
12794
+ if (area.sheet !== sheet) return 0;
12795
+ const { start, end } = area.range;
12796
+ return shift2.axis === "row" ? intervalDelta(start.row, end.row, shift2) : intervalDelta(start.col, end.col, shift2);
12797
+ }
12798
+ function movedRange(range, axis, delta) {
12799
+ if (delta === 0) return range;
12800
+ return axis === "row" ? {
12801
+ start: { row: range.start.row + delta, col: range.start.col },
12802
+ end: { row: range.end.row + delta, col: range.end.col }
12803
+ } : {
12804
+ start: { row: range.start.row, col: range.start.col + delta },
12805
+ end: { row: range.end.row, col: range.end.col + delta }
12806
+ };
12807
+ }
12808
+ function reachesEdit(areas, sheet, shift2) {
12809
+ for (const area of areas) {
12810
+ if (area.sheet !== sheet) continue;
12811
+ if ((shift2.axis === "row" ? area.range.end.row : area.range.end.col) >= shift2.at) return true;
12812
+ }
12813
+ return false;
12814
+ }
12815
+ function intersectionMoves(area, delta, pos, nodeDelta, axis) {
12816
+ if (delta === nodeDelta) return false;
12817
+ const { start, end } = area.range;
12818
+ const a2 = axis === "row" ? start.row : start.col;
12819
+ const b = axis === "row" ? end.row : end.col;
12820
+ if (a2 === b) return false;
12821
+ return pos >= a2 && pos <= b || pos + nodeDelta >= a2 + delta && pos + nodeDelta <= b + delta;
12822
+ }
12823
+
12824
+ // xlsx/formula/engine-structure.ts
12825
+ var sameAreas = (a2, b) => a2.length === b.length && a2.every((x, i2) => {
12826
+ const y = b[i2];
12827
+ return x.sheet === y.sheet && sameRange(x.range, y.range);
12828
+ });
12829
+ var EngineStructure = class extends EngineCore {
12830
+ /** Formulas a structural edit left to recalculate (with their dependents) on the next pass. */
12831
+ structuralSeeds = /* @__PURE__ */ new Set();
12832
+ /** Whether the graph describes the workbook as it was before the edit just reported. */
12833
+ graphReady() {
12834
+ return this.built && !this.needsFull;
12835
+ }
12836
+ /** Parses a node's current text again and recomputes its precedents. */
12837
+ reanalyze(node) {
12838
+ const parsed = this.parse(node.formula);
12839
+ delete node.stale;
12840
+ delete node.parseError;
12841
+ if (parsed instanceof FormulaError) {
12842
+ node.ast = void 0;
12843
+ if (parsed.code) node.parseError = err(parsed.code);
12844
+ node.deps = [];
12845
+ node.dynamicDeps = [];
12846
+ return;
12847
+ }
12848
+ node.ast = parsed;
12849
+ const found = analyze(parsed, this, node.sheet, node.row, node.col);
12850
+ node.deps = found.deps;
12851
+ node.dynamicDeps = found.dynamicDeps;
12852
+ node.volatile = found.volatile;
12853
+ node.unsupported = found.unsupported;
12854
+ }
12855
+ /**
12856
+ * Moves the graph for rows or columns inserted or deleted on `sheet`, after the workbook was
12857
+ * changed (cells moved, formulas rewritten). Spills on the sheet, array formulas the edit cuts
12858
+ * and anything unexpected fall back to a rebuild on the next recalculation.
12859
+ */
12860
+ shiftCells(sheet, shift2) {
12861
+ if (!this.graphReady() || !this.shiftable(sheet, shift2)) {
12862
+ this.invalidate();
12863
+ return;
12864
+ }
12865
+ const ws = this.workbook.sheets[sheet];
12866
+ const own = this.nodes.get(sheet);
12867
+ if (!ws) return;
12868
+ const moved = /* @__PURE__ */ new Map();
12869
+ if (own) {
12870
+ const next = /* @__PURE__ */ new Map();
12871
+ for (const node of own.values()) {
12872
+ const pos = shift2.axis === "row" ? node.row : node.col;
12873
+ const delta = intervalDelta(pos, pos, shift2);
12874
+ if (delta === void 0) {
12875
+ this.dropNode(node);
12876
+ continue;
12877
+ }
12878
+ if (delta !== 0) {
12879
+ moved.set(node, delta);
12880
+ if (shift2.axis === "row") node.row += delta;
12881
+ else node.col += delta;
12882
+ }
12883
+ next.set(cellKey(node.row, node.col), node);
12884
+ }
12885
+ this.nodes.set(sheet, next);
12886
+ }
12887
+ for (const map of this.nodes.values())
12888
+ for (const node of map.values()) {
12889
+ if (!this.shiftNode(node, sheet, shift2, moved.get(node) ?? 0)) {
12890
+ this.invalidate();
12891
+ return;
12892
+ }
12893
+ }
12894
+ this.boundsCache.clear();
12895
+ this.graphChanged();
12896
+ }
12897
+ /** No spill or stale spilled value on the sheet, and no array formula the edit cuts. */
12898
+ shiftable(sheet, shift2) {
12899
+ for (const node of this.footprints) {
12900
+ if (node.sheet !== sheet) continue;
12901
+ if (node.spill || node.blockedSpill) return false;
12902
+ if (node.arrayRange && areaDelta({ sheet, range: node.arrayRange }, sheet, shift2) === void 0)
12903
+ return false;
12904
+ }
12905
+ const ws = this.workbook.sheets[sheet];
12906
+ let spilled = false;
12907
+ ws?.rows.forEach((cells) => {
12908
+ if (!spilled) cells.forEach((cell) => spilled ||= isSpilledCell(cell));
12909
+ });
12910
+ return !spilled;
12911
+ }
12912
+ dropNode(node) {
12913
+ this.structuralSeeds.delete(node);
12914
+ this.cycles.delete(node);
12915
+ this.footprints.delete(node);
12916
+ }
12917
+ /** Updates one formula for the edit; false when the graph cannot follow it. */
12918
+ shiftNode(node, sheet, shift2, nodeDelta) {
12919
+ const cell = this.workbook.sheets[node.sheet]?.rows.get(node.row)?.get(node.col);
12920
+ if (cell?.formula === void 0) return false;
12921
+ if (node.arrayRange) {
12922
+ const delta = areaDelta({ sheet: node.sheet, range: node.arrayRange }, sheet, shift2);
12923
+ if (delta === void 0) return false;
12924
+ node.arrayRange = movedRange(node.arrayRange, shift2.axis, delta);
12925
+ if (!cell.arrayRange || !sameRange(node.arrayRange, normalizeRange(cell.arrayRange)))
12926
+ return false;
12927
+ }
12928
+ const changed = cell.formula !== node.formula;
12929
+ const traits = node.ast && astTraits(node.ast);
12930
+ const reached = reachesEdit(node.deps, sheet, shift2) || reachesEdit(node.dynamicDeps, sheet, shift2);
12931
+ if (!changed && !reached && nodeDelta === 0 && !traits?.descriptive && !traits?.opaque)
12932
+ return true;
12933
+ if (traits?.complex) {
12934
+ node.formula = cell.formula;
12935
+ this.reanalyze(node);
12936
+ this.structuralSeeds.add(node);
12937
+ return true;
12938
+ }
12939
+ const pos = shift2.axis === "row" ? node.row - nodeDelta : node.col - nodeDelta;
12940
+ const moves = { seed: !!traits?.descriptive, moved: false, cut: false };
12941
+ const deps = this.moveAreas(node.deps, sheet, shift2, pos, nodeDelta, moves);
12942
+ const dynamicDeps = this.moveAreas(node.dynamicDeps, sheet, shift2, pos, nodeDelta, moves);
12943
+ const { cut, moved: anyMoved } = moves;
12944
+ let seed = moves.seed;
12945
+ if (cut || changed !== anyMoved) {
12946
+ node.formula = cell.formula;
12947
+ this.reanalyze(node);
12948
+ this.structuralSeeds.add(node);
12949
+ return true;
12950
+ }
12951
+ if (traits?.positional && (anyMoved || nodeDelta !== 0)) seed = true;
12952
+ node.deps = deps;
12953
+ node.dynamicDeps = dynamicDeps;
12954
+ if (changed) {
12955
+ node.formula = cell.formula;
12956
+ node.stale = true;
12957
+ }
12958
+ if (seed) this.structuralSeeds.add(node);
12959
+ return true;
12960
+ }
12961
+ /**
12962
+ * Areas moved for the edit (the same list when none moves). Records in `moves` whether one
12963
+ * moved, one was cut (deleted, widened, pushed off the grid) and whether implicit intersection
12964
+ * with one can now pick another cell.
12965
+ */
12966
+ moveAreas(list, sheet, shift2, pos, nodeDelta, moves) {
12967
+ let out;
12968
+ for (let i2 = 0; i2 < list.length; i2++) {
12969
+ const area = list[i2];
12970
+ const delta = areaDelta(area, sheet, shift2);
12971
+ if (delta === void 0) {
12972
+ moves.cut = true;
12973
+ continue;
12974
+ }
12975
+ if (intersectionMoves(area, delta, pos, nodeDelta, shift2.axis)) moves.seed = true;
12976
+ if (delta === 0) continue;
12977
+ moves.moved = true;
12978
+ out ??= list.slice();
12979
+ out[i2] = { sheet: area.sheet, range: movedRange(area.range, shift2.axis, delta) };
12980
+ }
12981
+ return out ?? list;
12982
+ }
12983
+ /**
12984
+ * Follows a sheet rename (`from` to `to`, formulas already rewritten): precedents are kept by
12985
+ * sheet index, so only rewritten formulas need their text parsed again, and only formulas that
12986
+ * name the new sheet without having been rewritten (previously unresolved), describe sheets
12987
+ * (CELL, SHEET, FORMULATEXT) or read defined names whose areas changed are recalculated.
12988
+ */
12989
+ renameSheet(from, to) {
12990
+ if (!this.graphReady()) {
12991
+ this.invalidate();
12992
+ return;
12993
+ }
12994
+ const target = to.toLowerCase();
12995
+ let depsChanged = false;
12996
+ for (const map of this.nodes.values())
12997
+ for (const node of map.values()) {
12998
+ const cell = this.workbook.sheets[node.sheet]?.rows.get(node.row)?.get(node.col);
12999
+ if (cell?.formula === void 0) {
13000
+ this.invalidate();
13001
+ return;
13002
+ }
13003
+ const changed = cell.formula !== node.formula;
13004
+ const traits = node.ast && astTraits(node.ast);
13005
+ node.formula = cell.formula;
13006
+ if (traits?.complex || !changed && traits?.sheets.has(target)) {
13007
+ const deps = node.deps;
13008
+ const dynamicDeps = node.dynamicDeps;
13009
+ this.reanalyze(node);
13010
+ const same4 = sameAreas(deps, node.deps) && sameAreas(dynamicDeps, node.dynamicDeps);
13011
+ if (!same4) depsChanged = true;
13012
+ if (!same4 || !traits?.complex) this.structuralSeeds.add(node);
13013
+ } else if (changed) node.stale = true;
13014
+ if (traits?.descriptive || traits?.opaque) this.structuralSeeds.add(node);
13015
+ }
13016
+ if (depsChanged) this.graphChanged();
13017
+ }
13018
+ /** Takes the formulas queued by structural edits. */
13019
+ takeStructuralSeeds(into) {
13020
+ for (const node of this.structuralSeeds)
13021
+ if (this.nodes.get(node.sheet)?.get(cellKey(node.row, node.col)) === node) into.add(node);
13022
+ this.structuralSeeds.clear();
13023
+ }
13024
+ };
13025
+
12490
13026
  // xlsx/formula/engine.ts
12491
- var Engine = class extends EngineCore {
13027
+ var Engine = class extends EngineStructure {
13028
+ preparation;
13029
+ /** The prepared formulas the first recalculation checks (see `trustsStoredValues`). */
13030
+ prepared;
13031
+ // ---- the graph ----
13032
+ prepare(timeRemaining) {
13033
+ if (this.built || this.needsFull) return true;
13034
+ this.preparation ??= this.startPreparation();
13035
+ if (!this.preparation.run(timeRemaining)) return false;
13036
+ this.finishPreparation(this.preparation);
13037
+ return true;
13038
+ }
13039
+ discardPreparation() {
13040
+ if (!this.fresh || this.needsFull) return;
13041
+ this.preparation = void 0;
13042
+ this.prepared = void 0;
13043
+ this.structuralSeeds.clear();
13044
+ this.nodes.clear();
13045
+ this.footprints.clear();
13046
+ this.graphChanged();
13047
+ this.built = false;
13048
+ }
13049
+ /** Completes the graph, continuing a preparation that is under way. */
13050
+ build() {
13051
+ const preparation = this.preparation ?? this.startPreparation();
13052
+ preparation.run();
13053
+ this.finishPreparation(preparation);
13054
+ }
13055
+ startPreparation() {
13056
+ this.nodes.clear();
13057
+ this.footprints.clear();
13058
+ this.footprintIndex = void 0;
13059
+ this.prepared = void 0;
13060
+ return new GraphPreparation(
13061
+ this.workbook,
13062
+ (s, row2, col, cell) => this.createNode(s, row2, col, cell, true)
13063
+ );
13064
+ }
13065
+ finishPreparation(preparation) {
13066
+ this.preparation = void 0;
13067
+ this.built = true;
13068
+ this.graphChanged();
13069
+ this.reverseIndex = preparation.reverse;
13070
+ this.prepared = preparation.formulas;
13071
+ }
12492
13072
  // ---- recalculation ----
12493
13073
  recalculateAll() {
13074
+ this.fresh = false;
13075
+ this.prepared = void 0;
12494
13076
  if (this.needsFull) this.releaseAllSpills();
12495
13077
  if (!this.built || this.needsFull) this.build();
12496
13078
  this.needsFull = false;
13079
+ this.structuralSeeds.clear();
12497
13080
  this.cycles = /* @__PURE__ */ new Set();
12498
13081
  this.run(new Set(this.allNodes()), true);
12499
13082
  }
12500
13083
  recalculateFrom(changes) {
12501
13084
  const seeds = /* @__PURE__ */ new Set();
12502
- if (!this.built && !this.needsFull) {
12503
- this.build();
13085
+ if (this.fresh && !this.needsFull) {
13086
+ this.fresh = false;
13087
+ if (!this.built) this.build();
12504
13088
  if (!this.trustsStoredValues(changes, seeds)) {
12505
13089
  this.recalculateAll();
12506
13090
  return;
@@ -12544,6 +13128,7 @@ var Engine = class extends EngineCore {
12544
13128
  }
12545
13129
  }
12546
13130
  if (structural) this.graphChanged();
13131
+ this.takeStructuralSeeds(seeds);
12547
13132
  const reverse2 = this.reverse();
12548
13133
  for (const area of changed) reverse2.dependents(area.sheet, area.range, seeds);
12549
13134
  for (const node of this.allNodes()) if (node.volatile) seeds.add(node);
@@ -12555,13 +13140,18 @@ var Engine = class extends EngineCore {
12555
13140
  * without a value into `missing`.
12556
13141
  */
12557
13142
  trustsStoredValues(changes, missing) {
13143
+ const prepared = this.prepared;
13144
+ this.prepared = void 0;
13145
+ if (!prepared) return false;
12558
13146
  const changed = new Set(changes.map((c2) => `${c2.sheet}:${cellKey(c2.row, c2.col)}`));
12559
- for (const node of this.allNodes()) {
12560
- const legacyOrArray = node.legacy || node.arrayRange;
12561
- if (!legacyOrArray && !changed.has(`${node.sheet}:${cellKey(node.row, node.col)}`))
13147
+ const current = (node) => this.nodes.get(node.sheet)?.get(cellKey(node.row, node.col)) === node;
13148
+ for (const node of prepared.dynamic) {
13149
+ if (current(node) && !changed.has(`${node.sheet}:${cellKey(node.row, node.col)}`))
12562
13150
  return false;
13151
+ }
13152
+ for (const node of prepared.unvalued) {
12563
13153
  const cell = this.workbook.sheets[node.sheet]?.rows.get(node.row)?.get(node.col);
12564
- if (cell && cell.value === null && !node.keepCached) missing.add(node);
13154
+ if (current(node) && cell && cell.value === null) missing.add(node);
12565
13155
  }
12566
13156
  return true;
12567
13157
  }
@@ -12575,7 +13165,10 @@ var Engine = class extends EngineCore {
12575
13165
  return !!anchor?.spill && rangeHas(anchor.spill, row2, col);
12576
13166
  }
12577
13167
  invalidate() {
13168
+ this.structuralSeeds.clear();
12578
13169
  this.releaseAllSpills();
13170
+ this.preparation = void 0;
13171
+ this.prepared = void 0;
12579
13172
  this.nodes.clear();
12580
13173
  this.footprints.clear();
12581
13174
  this.graphChanged();
@@ -12620,7 +13213,7 @@ function createCalcEngine(workbook, options = {}) {
12620
13213
  return new Engine(workbook, options);
12621
13214
  }
12622
13215
 
12623
- // xlsx/formula/transform.ts
13216
+ // xlsx/formula/ref-text.ts
12624
13217
  function safeTokens(formula) {
12625
13218
  try {
12626
13219
  return tokenize(formula);
@@ -12639,50 +13232,26 @@ function formatRefSpec(spec2) {
12639
13232
  if (spec2.kind === "cell") return a2;
12640
13233
  return `${a2}:${cornerText(spec2.end, spec2.kind)}`;
12641
13234
  }
12642
- function rewrite(token, ref) {
13235
+ function rewriteRef(token, ref) {
12643
13236
  const prefix = token.prefix?.text ?? "";
12644
13237
  if (!ref) return `${prefix}#REF!`;
12645
13238
  return `${prefix}${formatRefSpec(ref)}${token.spill ? "#" : ""}`;
12646
13239
  }
12647
13240
  var sameSpec = (a2, b) => a2.kind === b.kind && a2.start.row === b.start.row && a2.start.col === b.start.col && a2.end.row === b.end.row && a2.end.col === b.end.col;
12648
- function translateFormula(formula, dRow, dCol) {
12649
- const tokens2 = safeTokens(formula);
12650
- if (!tokens2) return formula;
12651
- for (const token of tokens2) {
12652
- if (token.kind !== "ref" || !token.ref) continue;
12653
- const spec2 = token.ref;
12654
- const move = (c2) => {
12655
- const row2 = spec2.kind === "cols" || c2.rowAbs ? c2.row : c2.row + dRow;
12656
- const col = spec2.kind === "rows" || c2.colAbs ? c2.col : c2.col + dCol;
12657
- if (row2 < 0 || row2 > MAX_ROW || col < 0 || col > MAX_COL) return void 0;
12658
- return { ...c2, row: row2, col };
12659
- };
12660
- const start = move(spec2.start);
12661
- const end = move(spec2.end);
12662
- const next = start && end ? { kind: spec2.kind, start, end } : void 0;
12663
- if (next && sameSpec(next, spec2)) continue;
12664
- token.text = rewrite(token, next);
12665
- }
12666
- return joinTokens(tokens2);
13241
+ var needsQuote = (name) => !/^[A-Za-z_¡-￿][A-Za-z0-9_.¡-￿]*$/.test(name) || /^[A-Za-z]{1,3}\d+$/.test(name) || /^(?:R\d*C?\d*|C\d*)$/i.test(name) || /^(?:TRUE|FALSE)$/i.test(name);
13242
+ function prefixText(prefix) {
13243
+ const sheets = prefix.sheet2 !== void 0 ? `${prefix.sheet}:${prefix.sheet2}` : prefix.sheet;
13244
+ const book = prefix.book !== void 0 ? `[${prefix.book}]` : "";
13245
+ const quote2 = needsQuote(prefix.sheet) || prefix.sheet2 !== void 0 && needsQuote(prefix.sheet2);
13246
+ return quote2 ? `'${(book + sheets).replace(/'/g, "''")}'!` : `${book}${sheets}!`;
12667
13247
  }
12668
- var sheetMatches = (prefix, formulaSheet, sheet) => {
12669
- if (prefix?.book !== void 0) return false;
12670
- const name = prefix ? prefix.sheet : formulaSheet;
12671
- return name.toLowerCase() === sheet.toLowerCase();
12672
- };
12673
- function shiftInterval(a2, b, at, count2, max) {
12674
- if (count2 > 0) {
12675
- const na2 = a2 >= at ? a2 + count2 : a2;
12676
- const nb2 = b >= at ? b + count2 : b;
12677
- if (nb2 > max) return na2 > max ? void 0 : [na2, max];
12678
- return [na2, nb2];
12679
- }
12680
- const n2 = -count2;
12681
- const last = at + n2 - 1;
12682
- if (a2 >= at && b <= last) return void 0;
12683
- const na = a2 < at ? a2 : a2 <= last ? at : a2 - n2;
12684
- const nb = b < at ? b : b <= last ? at - 1 : b - n2;
12685
- return [na, nb];
13248
+
13249
+ // xlsx/formula/shift-formula.ts
13250
+ var templates = /* @__PURE__ */ new Map();
13251
+ var MAX_TEMPLATES2 = 2e5;
13252
+ function remember(text3, template) {
13253
+ if (templates.size >= MAX_TEMPLATES2) templates.clear();
13254
+ templates.set(text3, template);
12686
13255
  }
12687
13256
  function mergeSpacedAreas(tokens2) {
12688
13257
  const out = [];
@@ -12709,63 +13278,145 @@ function mergeSpacedAreas(tokens2) {
12709
13278
  }
12710
13279
  return out;
12711
13280
  }
12712
- function shiftFormula(formula, formulaSheet, spec2) {
12713
- if (spec2.count === 0) return formula;
13281
+ function templateOf(formula) {
13282
+ const cached = templates.get(formula);
13283
+ if (cached !== void 0) return cached;
12714
13284
  const raw2 = safeTokens(formula);
12715
- if (!raw2) return formula;
12716
- const tokens2 = mergeSpacedAreas(raw2);
12717
- const isRow = spec2.axis === "row";
12718
- for (const token of tokens2) {
12719
- if (token.kind !== "ref" || !token.ref || !sheetMatches(token.prefix, formulaSheet, spec2.sheet))
12720
- continue;
12721
- const ref = token.ref;
12722
- if (isRow && ref.kind === "cols" || !isRow && ref.kind === "rows") continue;
12723
- const startFirst = isRow ? ref.start.row <= ref.end.row : ref.start.col <= ref.end.col;
12724
- const lo = startFirst ? ref.start : ref.end;
12725
- const hi = startFirst ? ref.end : ref.start;
12726
- const shifted2 = shiftInterval(
12727
- isRow ? lo.row : lo.col,
12728
- isRow ? hi.row : hi.col,
12729
- spec2.at,
12730
- spec2.count,
12731
- isRow ? MAX_ROW : MAX_COL
12732
- );
12733
- let next;
12734
- if (shifted2) {
12735
- const [a2, b] = shifted2;
12736
- const nlo = isRow ? { ...lo, row: a2 } : { ...lo, col: a2 };
12737
- const nhi = isRow ? { ...hi, row: b } : { ...hi, col: b };
12738
- next = { kind: ref.kind, start: startFirst ? nlo : nhi, end: startFirst ? nhi : nlo };
12739
- if (ref.kind === "cell") next.end = next.start;
13285
+ let template = null;
13286
+ if (raw2) {
13287
+ template = [];
13288
+ let literal2 = "";
13289
+ for (const token of mergeSpacedAreas(raw2)) {
13290
+ if (token.kind !== "ref" && !(token.kind === "name" && token.prefix)) {
13291
+ literal2 += token.text;
13292
+ continue;
13293
+ }
13294
+ if (literal2) template.push(literal2);
13295
+ literal2 = "";
13296
+ const part = { kind: token.kind, text: token.text };
13297
+ if (token.prefix) {
13298
+ part.prefix = token.prefix;
13299
+ if (token.prefix.book === void 0) part.sheet = token.prefix.sheet.toLowerCase();
13300
+ }
13301
+ if (token.ref) part.ref = token.ref;
13302
+ if (token.spill) part.spill = true;
13303
+ template.push(part);
12740
13304
  }
12741
- if (next && sameSpec(next, ref)) continue;
12742
- token.text = rewrite(token, next);
13305
+ if (literal2) template.push(literal2);
12743
13306
  }
12744
- return joinTokens(tokens2);
13307
+ remember(formula, template);
13308
+ return template;
12745
13309
  }
12746
- var needsQuote = (name) => !/^[A-Za-z_\u00A1-\uFFFF][A-Za-z0-9_.\u00A1-\uFFFF]*$/.test(name) || /^[A-Za-z]{1,3}\d+$/.test(name) || /^(?:R\d*C?\d*|C\d*)$/i.test(name) || /^(?:TRUE|FALSE)$/i.test(name);
12747
- function prefixText(prefix) {
12748
- const sheets = prefix.sheet2 !== void 0 ? `${prefix.sheet}:${prefix.sheet2}` : prefix.sheet;
12749
- const book = prefix.book !== void 0 ? `[${prefix.book}]` : "";
12750
- const quote2 = needsQuote(prefix.sheet) || prefix.sheet2 !== void 0 && needsQuote(prefix.sheet2);
12751
- return quote2 ? `'${(book + sheets).replace(/'/g, "''")}'!` : `${book}${sheets}!`;
13310
+ function rewriteTemplate(formula, rewrite2) {
13311
+ const template = templateOf(formula);
13312
+ if (!template) return formula;
13313
+ let next;
13314
+ for (let i2 = 0; i2 < template.length; i2++) {
13315
+ const part = template[i2];
13316
+ if (typeof part === "string") continue;
13317
+ const changed = rewrite2(part);
13318
+ if (!changed) continue;
13319
+ next ??= template.slice();
13320
+ next[i2] = changed;
13321
+ }
13322
+ if (!next) return formula;
13323
+ const text3 = next.map((part) => typeof part === "string" ? part : part.text).join("");
13324
+ remember(text3, next);
13325
+ return text3;
13326
+ }
13327
+ function shiftInterval(a2, b, at, count2, max) {
13328
+ if (count2 > 0) {
13329
+ const na2 = a2 >= at ? a2 + count2 : a2;
13330
+ const nb2 = b >= at ? b + count2 : b;
13331
+ if (nb2 > max) return na2 > max ? void 0 : [na2, max];
13332
+ return [na2, nb2];
13333
+ }
13334
+ const n2 = -count2;
13335
+ const last = at + n2 - 1;
13336
+ if (a2 >= at && b <= last) return void 0;
13337
+ const na = a2 < at ? a2 : a2 <= last ? at : a2 - n2;
13338
+ const nb = b < at ? b : b <= last ? at - 1 : b - n2;
13339
+ return [na, nb];
13340
+ }
13341
+ function shiftRef(ref, spec2) {
13342
+ const isRow = spec2.axis === "row";
13343
+ if (isRow && ref.kind === "cols" || !isRow && ref.kind === "rows") return ref;
13344
+ const startFirst = isRow ? ref.start.row <= ref.end.row : ref.start.col <= ref.end.col;
13345
+ const lo = startFirst ? ref.start : ref.end;
13346
+ const hi = startFirst ? ref.end : ref.start;
13347
+ const shifted2 = shiftInterval(
13348
+ isRow ? lo.row : lo.col,
13349
+ isRow ? hi.row : hi.col,
13350
+ spec2.at,
13351
+ spec2.count,
13352
+ isRow ? MAX_ROW : MAX_COL
13353
+ );
13354
+ if (!shifted2) return void 0;
13355
+ const [a2, b] = shifted2;
13356
+ const nlo = isRow ? { ...lo, row: a2 } : { ...lo, col: a2 };
13357
+ const nhi = isRow ? { ...hi, row: b } : { ...hi, col: b };
13358
+ const next = {
13359
+ kind: ref.kind,
13360
+ start: startFirst ? nlo : nhi,
13361
+ end: startFirst ? nhi : nlo
13362
+ };
13363
+ if (ref.kind === "cell") next.end = next.start;
13364
+ return sameSpec(next, ref) ? ref : next;
13365
+ }
13366
+ function shiftFormula(formula, formulaSheet, spec2) {
13367
+ if (spec2.count === 0) return formula;
13368
+ const sheet = spec2.sheet.toLowerCase();
13369
+ const own = formulaSheet.toLowerCase() === sheet;
13370
+ return rewriteTemplate(formula, (part) => {
13371
+ if (part.kind !== "ref" || !part.ref || (part.prefix ? part.sheet !== sheet : !own))
13372
+ return void 0;
13373
+ const next = shiftRef(part.ref, spec2);
13374
+ if (next === part.ref) return void 0;
13375
+ const out = { kind: "ref", text: rewriteRef(part, next) };
13376
+ if (part.prefix) out.prefix = part.prefix;
13377
+ if (part.sheet !== void 0) out.sheet = part.sheet;
13378
+ if (next) out.ref = next;
13379
+ if (part.spill) out.spill = true;
13380
+ return out;
13381
+ });
12752
13382
  }
12753
13383
  function renameSheetInFormula(formula, oldName, newName) {
12754
- const tokens2 = safeTokens(formula);
12755
- if (!tokens2) return formula;
12756
13384
  const lower2 = oldName.toLowerCase();
12757
- for (const token of tokens2) {
12758
- const prefix = token.prefix;
12759
- if (!prefix || prefix.book !== void 0) continue;
12760
- const first2 = prefix.sheet.toLowerCase() === lower2;
13385
+ return rewriteTemplate(formula, (part) => {
13386
+ const prefix = part.prefix;
13387
+ if (!prefix || part.sheet === void 0) return void 0;
13388
+ const first2 = part.sheet === lower2;
12761
13389
  const second = prefix.sheet2?.toLowerCase() === lower2;
12762
- if (!first2 && !second) continue;
13390
+ if (!first2 && !second) return void 0;
12763
13391
  const renamed = { ...prefix, sheet: first2 ? newName : prefix.sheet };
12764
13392
  if (prefix.sheet2 !== void 0) renamed.sheet2 = second ? newName : prefix.sheet2;
12765
13393
  const text3 = prefixText(renamed);
12766
- token.text = text3 + token.text.slice(prefix.text.length);
12767
13394
  renamed.text = text3;
12768
- token.prefix = renamed;
13395
+ const out = { ...part, text: text3 + part.text.slice(prefix.text.length) };
13396
+ out.prefix = renamed;
13397
+ out.sheet = renamed.sheet.toLowerCase();
13398
+ return out;
13399
+ });
13400
+ }
13401
+
13402
+ // xlsx/formula/transform.ts
13403
+ function translateFormula(formula, dRow, dCol) {
13404
+ const tokens2 = safeTokens(formula);
13405
+ if (!tokens2) return formula;
13406
+ for (const token of tokens2) {
13407
+ if (token.kind !== "ref" || !token.ref) continue;
13408
+ const spec2 = token.ref;
13409
+ const move = (c2) => {
13410
+ const row2 = spec2.kind === "cols" || c2.rowAbs ? c2.row : c2.row + dRow;
13411
+ const col = spec2.kind === "rows" || c2.colAbs ? c2.col : c2.col + dCol;
13412
+ if (row2 < 0 || row2 > MAX_ROW || col < 0 || col > MAX_COL) return void 0;
13413
+ return { ...c2, row: row2, col };
13414
+ };
13415
+ const start = move(spec2.start);
13416
+ const end = move(spec2.end);
13417
+ const next = start && end ? { kind: spec2.kind, start, end } : void 0;
13418
+ if (next && sameSpec(next, spec2)) continue;
13419
+ token.text = rewriteRef(token, next);
12769
13420
  }
12770
13421
  return joinTokens(tokens2);
12771
13422
  }
@@ -15592,7 +16243,7 @@ var FUTURE_FUNCTIONS = new Set(
15592
16243
  "AGGREGATE BETA.DIST BETA.INV BINOM.DIST BINOM.INV CEILING.PRECISE CHISQ.DIST CHISQ.DIST.RT CHISQ.INV CHISQ.INV.RT CHISQ.TEST CONFIDENCE.NORM CONFIDENCE.T COVARIANCE.P COVARIANCE.S ERF.PRECISE ERFC.PRECISE EXPON.DIST F.DIST F.DIST.RT F.INV F.INV.RT F.TEST FLOOR.PRECISE GAMMA.DIST GAMMA.INV GAMMALN.PRECISE HYPGEOM.DIST LOGNORM.DIST LOGNORM.INV MODE.MULT MODE.SNGL NEGBINOM.DIST NETWORKDAYS.INTL NORM.DIST NORM.INV NORM.S.DIST NORM.S.INV PERCENTILE.EXC PERCENTILE.INC PERCENTRANK.EXC PERCENTRANK.INC POISSON.DIST QUARTILE.EXC QUARTILE.INC RANK.AVG RANK.EQ STDEV.P STDEV.S T.DIST T.DIST.2T T.DIST.RT T.INV T.INV.2T T.TEST VAR.P VAR.S WEIBULL.DIST WORKDAY.INTL ACOT ACOTH ARABIC BASE BINOM.DIST.RANGE BITAND BITLSHIFT BITOR BITRSHIFT BITXOR CEILING.MATH COMBINA COT COTH CSC CSCH DAYS DECIMAL ENCODEURL FILTERXML FLOOR.MATH FORMULATEXT GAMMA GAUSS IFNA IMCOSH IMCOT IMCSC IMCSCH IMSEC IMSECH IMSINH IMTAN ISFORMULA ISOWEEKNUM MUNIT NUMBERVALUE PDURATION PERMUTATIONA PHI RRI SEC SECH SHEET SHEETS SKEW.P UNICHAR UNICODE WEBSERVICE XOR CONCAT FORECAST.ETS FORECAST.ETS.CONFINT FORECAST.ETS.SEASONALITY FORECAST.ETS.STAT FORECAST.LINEAR IFS MAXIFS MINIFS SWITCH TEXTJOIN XLOOKUP XMATCH FILTER SORT SORTBY UNIQUE SEQUENCE RANDARRAY LET LAMBDA TEXTBEFORE TEXTAFTER TEXTSPLIT VSTACK HSTACK TAKE DROP CHOOSEROWS CHOOSECOLS TOCOL TOROW WRAPROWS WRAPCOLS EXPAND ARRAYTOTEXT VALUETOTEXT ISOMITTED BYROW BYCOL MAP REDUCE SCAN MAKEARRAY IMAGE GROUPBY PIVOTBY PERCENTOF REGEXTEST REGEXEXTRACT REGEXREPLACE TRIMRANGE ANCHORARRAY SINGLE STOCKHISTORY FIELDVALUE".split(" ")
15593
16244
  );
15594
16245
  var WORKSHEET_PREFIXED = /* @__PURE__ */ new Set(["FILTER", "SORT"]);
15595
- function rewrite2(formula, visit2) {
16246
+ function rewrite(formula, visit2) {
15596
16247
  const out = [];
15597
16248
  let i2 = 0;
15598
16249
  while (i2 < formula.length) {
@@ -15635,7 +16286,7 @@ function rewrite2(formula, visit2) {
15635
16286
  }
15636
16287
  function stripFuturePrefixes(formula) {
15637
16288
  if (!formula.includes("_xl")) return formula;
15638
- const plain2 = rewrite2(formula, (token, next, out) => {
16289
+ const plain2 = rewrite(formula, (token, next, out) => {
15639
16290
  if (!next.startsWith("(")) return false;
15640
16291
  const stripped = token.replace(/^(_xlfn\.)?(_xlws\.)?/i, "");
15641
16292
  if (stripped === token) return false;
@@ -15645,7 +16296,7 @@ function stripFuturePrefixes(formula) {
15645
16296
  return fromStoredSyntax(plain2);
15646
16297
  }
15647
16298
  function addFuturePrefixes(formula) {
15648
- return rewrite2(toStoredSyntax(formula), (token, next, out) => {
16299
+ return rewrite(toStoredSyntax(formula), (token, next, out) => {
15649
16300
  if (!next.startsWith("(")) return false;
15650
16301
  const upper2 = token.toUpperCase();
15651
16302
  if (!FUTURE_FUNCTIONS.has(upper2)) return false;
@@ -17727,13 +18378,13 @@ function dataBarLengths(rule, node) {
17727
18378
  maxLength: rule.maxLength ?? numAttr(bar, "maxLength") ?? 90
17728
18379
  };
17729
18380
  }
17730
- function rewriteConditionalExtension(rule, rewrite3) {
18381
+ function rewriteConditionalExtension(rule, rewrite2) {
17731
18382
  if (!rule.extensionXml) return;
17732
18383
  const doc = parseXml(rule.extensionXml);
17733
18384
  let changed = false;
17734
18385
  for (const formula of descendants(doc.documentElement, "f", XM)) {
17735
18386
  const before = formula.textContent ?? "";
17736
- const next = rewrite3(before);
18387
+ const next = rewrite2(before);
17737
18388
  if (before !== next) {
17738
18389
  formula.textContent = next;
17739
18390
  changed = true;
@@ -19876,6 +20527,7 @@ Reply:
19876
20527
  }
19877
20528
 
19878
20529
  // chart/bar-cluster-geometry.ts
20530
+ var DEFAULT_BAR_GAP_WIDTH = 150;
19879
20531
  function assertBarClusterOptions(options) {
19880
20532
  for (const [field, min, max] of [
19881
20533
  ["barGapWidth", 0, 500],
@@ -19889,7 +20541,8 @@ function assertBarClusterOptions(options) {
19889
20541
  function clusteredBarGeometry(barGroupWidth, seriesCount, chartData) {
19890
20542
  const overlap = chartData.barOverlap ?? 0;
19891
20543
  const overlapSpan = 1 + (seriesCount - 1) * (1 - overlap / 100);
19892
- const singleBarWidth = chartData.barGapWidth !== void 0 ? barGroupWidth / (overlapSpan + Math.max(chartData.barGapWidth, 0) / 100) : barGroupWidth * 0.7 / seriesCount;
20544
+ const gapWidth = chartData.barGapWidth ?? DEFAULT_BAR_GAP_WIDTH;
20545
+ const singleBarWidth = barGroupWidth / (overlapSpan + Math.max(gapWidth, 0) / 100);
19893
20546
  const step2 = singleBarWidth * (1 - overlap / 100);
19894
20547
  return { singleBarWidth, step: step2, clusterWidth: singleBarWidth + step2 * (seriesCount - 1) };
19895
20548
  }
@@ -24381,13 +25034,13 @@ function linearTrend(values2) {
24381
25034
  const slope = den ? num3 / den : 0;
24382
25035
  return (k) => Number((meanY + slope * (k - meanX)).toPrecision(15));
24383
25036
  }
24384
- var isDigit4 = (ch) => ch !== void 0 && ch >= "0" && ch <= "9";
25037
+ var isDigit5 = (ch) => ch !== void 0 && ch >= "0" && ch <= "9";
24385
25038
  function matchTextNumber(text3) {
24386
25039
  let end = text3.length;
24387
- while (end > 0 && !isDigit4(text3[end - 1])) end--;
25040
+ while (end > 0 && !isDigit5(text3[end - 1])) end--;
24388
25041
  if (end === 0) return null;
24389
25042
  let start = end;
24390
- while (start > 0 && isDigit4(text3[start - 1])) start--;
25043
+ while (start > 0 && isDigit5(text3[start - 1])) start--;
24391
25044
  return [text3, text3.slice(0, start), text3.slice(start, end), text3.slice(end)];
24392
25045
  }
24393
25046
  var QUARTER = /^(q|qtr|quarter)(\s?)([1-4])$/i;
@@ -24570,10 +25223,10 @@ function removeValidationArea(rules, area) {
24570
25223
  }
24571
25224
 
24572
25225
  // xlsx/edit/conditional-formulas.ts
24573
- function rewriteConditionalRule(rule, rewrite3) {
25226
+ function rewriteConditionalRule(rule, rewrite2) {
24574
25227
  const safe2 = (formula) => {
24575
25228
  try {
24576
- return rewrite3(formula);
25229
+ return rewrite2(formula);
24577
25230
  } catch {
24578
25231
  return formula;
24579
25232
  }
@@ -25092,7 +25745,7 @@ function shiftPreservedXml(sheet, shift2, moveRange, banded) {
25092
25745
  (xml) => xml.includes("sqref")
25093
25746
  );
25094
25747
  }
25095
- function rewritePreservedFormulas(sheet, rewrite3) {
25748
+ function rewritePreservedFormulas(sheet, rewrite2) {
25096
25749
  editEntries(
25097
25750
  sheet,
25098
25751
  "extLst",
@@ -25102,7 +25755,7 @@ function rewritePreservedFormulas(sheet, rewrite3) {
25102
25755
  const text3 = f6.textContent ?? "";
25103
25756
  let next = text3;
25104
25757
  try {
25105
- next = rewrite3(text3, sheet.name);
25758
+ next = rewrite2(text3, sheet.name);
25106
25759
  } catch {
25107
25760
  next = text3;
25108
25761
  }
@@ -25117,46 +25770,46 @@ function rewritePreservedFormulas(sheet, rewrite3) {
25117
25770
  }
25118
25771
 
25119
25772
  // xlsx/edit/shift-formulas.ts
25120
- function safe(rewrite3, formula, formulaSheet) {
25773
+ function safe(rewrite2, formula, formulaSheet) {
25121
25774
  try {
25122
- return rewrite3(formula, formulaSheet);
25775
+ return rewrite2(formula, formulaSheet);
25123
25776
  } catch {
25124
25777
  return formula;
25125
25778
  }
25126
25779
  }
25127
- function rewriteFormulas(workbook, rewrite3) {
25128
- for (const sheet of workbook.sheets) rewriteSheetFormulas(sheet, rewrite3);
25780
+ function rewriteFormulas(workbook, rewrite2) {
25781
+ for (const sheet of workbook.sheets) rewriteSheetFormulas(sheet, rewrite2);
25129
25782
  for (const definedName of workbook.definedNames) {
25130
25783
  const local = definedName.localSheet === void 0 ? void 0 : workbook.sheets[definedName.localSheet];
25131
- definedName.formula = safe(rewrite3, definedName.formula, local?.name ?? "");
25784
+ definedName.formula = safe(rewrite2, definedName.formula, local?.name ?? "");
25132
25785
  }
25133
25786
  }
25134
- function rewriteSheetFormulas(sheet, rewrite3) {
25135
- rewritePreservedFormulas(sheet, rewrite3);
25787
+ function rewriteSheetFormulas(sheet, rewrite2) {
25788
+ rewritePreservedFormulas(sheet, rewrite2);
25136
25789
  {
25137
25790
  const name = sheet.name;
25138
25791
  for (const table2 of sheet.tables)
25139
25792
  for (const column of table2.columns)
25140
25793
  if (column.calculatedColumnFormula !== void 0)
25141
- column.calculatedColumnFormula = safe(rewrite3, column.calculatedColumnFormula, name);
25794
+ column.calculatedColumnFormula = safe(rewrite2, column.calculatedColumnFormula, name);
25142
25795
  for (const cells of sheet.rows.values())
25143
25796
  for (const cell of cells.values())
25144
- if (cell.formula !== void 0) cell.formula = safe(rewrite3, cell.formula, name);
25797
+ if (cell.formula !== void 0) cell.formula = safe(rewrite2, cell.formula, name);
25145
25798
  for (const cf of sheet.conditionalFormats)
25146
- for (const rule of cf.rules) rewriteConditionalRule(rule, (f6) => rewrite3(f6, name));
25799
+ for (const rule of cf.rules) rewriteConditionalRule(rule, (f6) => rewrite2(f6, name));
25147
25800
  for (const dv of sheet.dataValidations) {
25148
- if (dv.formula1 !== void 0) dv.formula1 = safe(rewrite3, dv.formula1, name);
25149
- if (dv.formula2 !== void 0) dv.formula2 = safe(rewrite3, dv.formula2, name);
25801
+ if (dv.formula1 !== void 0) dv.formula1 = safe(rewrite2, dv.formula1, name);
25802
+ if (dv.formula2 !== void 0) dv.formula2 = safe(rewrite2, dv.formula2, name);
25150
25803
  }
25151
25804
  for (const link of sheet.hyperlinks)
25152
25805
  if (link.location && /![A-Za-z$]/.test(link.location))
25153
- link.location = safe(rewrite3, link.location, name);
25806
+ link.location = safe(rewrite2, link.location, name);
25154
25807
  for (const drawing of sheet.drawings) {
25155
25808
  if (drawing.kind !== "chart") continue;
25156
25809
  for (const series of drawing.series) {
25157
- if (series.nameRef) series.nameRef = safe(rewrite3, series.nameRef, name);
25158
- if (series.categoriesRef) series.categoriesRef = safe(rewrite3, series.categoriesRef, name);
25159
- if (series.valuesRef) series.valuesRef = safe(rewrite3, series.valuesRef, name);
25810
+ if (series.nameRef) series.nameRef = safe(rewrite2, series.nameRef, name);
25811
+ if (series.categoriesRef) series.categoriesRef = safe(rewrite2, series.categoriesRef, name);
25812
+ if (series.valuesRef) series.valuesRef = safe(rewrite2, series.valuesRef, name);
25160
25813
  }
25161
25814
  }
25162
25815
  }
@@ -25343,7 +25996,7 @@ function shiftAxis(ctx, s, axis, at, count2, insert) {
25343
25996
  applyAxisShift(ctx, sheet, shift2);
25344
25997
  if (insert) inheritFormats(sheet, axis, at, count2);
25345
25998
  },
25346
- { sheet: s, structural: true }
25999
+ { sheet: s, structural: true, calc: { kind: "shift", sheet: s, shift: shift2 } }
25347
26000
  );
25348
26001
  }
25349
26002
  var insertRows = (ctx, s, at, count2) => shiftAxis(ctx, s, "row", at, count2, true);
@@ -27161,6 +27814,14 @@ function setCalcMode(ctx, mode) {
27161
27814
 
27162
27815
  // xlsx/edit/session-calc.ts
27163
27816
  var isStructural = (step2) => step2.structural || step2.entries.some((entry3) => entry3.before.kind !== "cells");
27817
+ function hintFor(step2, mode) {
27818
+ const hint = step2.calc;
27819
+ if (!hint) return void 0;
27820
+ if (hint.kind === "rename")
27821
+ return mode === "undo" ? { kind: "rename", from: hint.to, to: hint.from } : hint;
27822
+ return mode === "undo" ? void 0 : hint;
27823
+ }
27824
+ var settingsOf = (workbook) => `${workbook.iterate ? `${workbook.iterate.count}:${workbook.iterate.delta}` : ""}|${workbook.date1904}`;
27164
27825
  var SessionCalculator = class {
27165
27826
  constructor(workbook, calc, enabled) {
27166
27827
  this.workbook = workbook;
@@ -27170,43 +27831,113 @@ var SessionCalculator = class {
27170
27831
  workbook;
27171
27832
  calc;
27172
27833
  enabled;
27173
- /** Structural edits made in manual mode leave the dependency graph stale until a full pass. */
27834
+ /** Structural edits the engine could not follow leave the dependency graph stale (manual mode). */
27174
27835
  staleGraph = false;
27836
+ /** Cells changed in manual mode since the last calculation. */
27837
+ dirty = [];
27838
+ /** The settings at the last calculation; undefined before the first. */
27839
+ calculated;
27175
27840
  /** Recalculates after an applied, undone or redone step (failures become warnings). */
27176
- afterStep(step2) {
27177
- if (!this.enabled) return;
27841
+ afterStep(step2, mode = "apply") {
27842
+ if (!this.enabled) {
27843
+ this.calc.discardPreparation();
27844
+ return;
27845
+ }
27846
+ const structural = isStructural(step2);
27847
+ const hint = structural ? hintFor(step2, mode) : void 0;
27178
27848
  if (calcModeOf(this.workbook) === "manual") {
27179
- if (isStructural(step2)) this.staleGraph = true;
27849
+ if (hint && !this.staleGraph && !this.dirty.length) {
27850
+ this.guard(() => this.follow(hint));
27851
+ return;
27852
+ }
27853
+ this.calc.discardPreparation();
27854
+ if (structural) this.staleGraph = true;
27855
+ else this.dirty.push(...touchedCells(step2));
27856
+ return;
27857
+ }
27858
+ if (hint && !this.staleGraph) {
27859
+ this.guard(() => {
27860
+ this.follow(hint);
27861
+ this.calc.recalculateFrom(touchedCells(step2));
27862
+ this.calculated = settingsOf(this.workbook);
27863
+ });
27180
27864
  return;
27181
27865
  }
27182
- this.recalc(isStructural(step2), () => touchedCells(step2));
27866
+ this.recalc(structural, () => touchedCells(step2));
27183
27867
  }
27184
27868
  /** Recalculates after a change applied outside the history (a collaborator's edit). */
27185
27869
  afterExternal(structural, cells) {
27186
- if (!this.enabled) return;
27187
- if (calcModeOf(this.workbook) === "manual") {
27870
+ if (!this.enabled || calcModeOf(this.workbook) === "manual") {
27871
+ this.calc.discardPreparation();
27872
+ if (!this.enabled) return;
27188
27873
  if (structural) this.staleGraph = true;
27874
+ else this.dirty.push(...cells);
27189
27875
  return;
27190
27876
  }
27191
27877
  this.recalc(structural, () => cells);
27192
27878
  }
27193
- recalc(structural, cells) {
27879
+ follow(hint) {
27880
+ if (hint.kind === "shift") this.calc.shiftCells(hint.sheet, hint.shift);
27881
+ else this.calc.renameSheet(hint.from, hint.to);
27882
+ }
27883
+ guard(run2) {
27884
+ try {
27885
+ run2();
27886
+ } catch (error) {
27887
+ const message = `Recalculation failed: ${error instanceof Error ? error.message : String(error)}`;
27888
+ if (!this.workbook.warnings.includes(message)) this.workbook.warnings.push(message);
27889
+ }
27890
+ }
27891
+ /** Builds the dependency graph ahead of the first edit (see `EditSession.prepareCalculation`). */
27892
+ prepare(timeRemaining) {
27893
+ if (!this.enabled || calcModeOf(this.workbook) === "manual" || this.staleGraph) return true;
27194
27894
  try {
27895
+ return this.calc.prepare(timeRemaining);
27896
+ } catch {
27897
+ this.calc.discardPreparation();
27898
+ return true;
27899
+ }
27900
+ }
27901
+ /**
27902
+ * Before a structural edit the graph can follow in place: finishes a graph being prepared (or
27903
+ * builds it) while it still matches the workbook, so the edit moves it instead of rebuilding.
27904
+ */
27905
+ beforeStructural() {
27906
+ if (!this.enabled || calcModeOf(this.workbook) === "manual" || this.staleGraph) return;
27907
+ this.prepare();
27908
+ }
27909
+ recalc(structural, cells) {
27910
+ this.guard(() => {
27195
27911
  if (this.staleGraph || structural) this.full();
27196
27912
  else {
27197
27913
  const changed = cells();
27198
27914
  if (changed.length) this.calc.recalculateFrom(changed);
27915
+ this.calculated = settingsOf(this.workbook);
27199
27916
  }
27200
- } catch (error) {
27201
- const message = `Recalculation failed: ${error instanceof Error ? error.message : String(error)}`;
27202
- if (!this.workbook.warnings.includes(message)) this.workbook.warnings.push(message);
27203
- }
27917
+ });
27204
27918
  }
27205
- /** Rebuilds the dependency graph and recalculates every formula (F9). */
27919
+ /** Rebuilds the dependency graph and recalculates every formula (Ctrl+Alt+F9). */
27206
27920
  full() {
27207
27921
  this.staleGraph = false;
27922
+ this.dirty = [];
27208
27923
  this.calc.invalidate();
27209
27924
  this.calc.recalculateAll();
27925
+ this.calculated = settingsOf(this.workbook);
27926
+ }
27927
+ /**
27928
+ * Calculate Now (F9): the cells changed since the last calculation, the formulas structural
27929
+ * edits queued, their dependents and the volatile functions. Everything when `full`, before
27930
+ * the session's first calculation, after the iteration settings changed or while the graph
27931
+ * is stale.
27932
+ */
27933
+ now(full) {
27934
+ if (full || this.staleGraph || this.calculated !== settingsOf(this.workbook)) {
27935
+ this.full();
27936
+ return;
27937
+ }
27938
+ const changed = this.dirty;
27939
+ this.dirty = [];
27940
+ this.calc.recalculateFrom(changed);
27210
27941
  }
27211
27942
  /**
27212
27943
  * Recalculates one sheet's formulas and their dependents (Shift+F9); after structural edits
@@ -30977,12 +31708,12 @@ function renameSheet(ctx, index2, name) {
30977
31708
  ctx.run(
30978
31709
  "Rename sheet",
30979
31710
  "sheets",
30980
- [{ kind: "workbook" }],
31711
+ [{ kind: "refs" }],
30981
31712
  () => {
30982
31713
  rewriteFormulas(workbook, (formula) => renameSheetInFormula(formula, old, name));
30983
31714
  sheet.name = name;
30984
31715
  },
30985
- { sheet: index2, structural: true }
31716
+ { sheet: index2, structural: true, calc: { kind: "rename", from: old, to: name } }
30986
31717
  );
30987
31718
  }
30988
31719
  function moveSheet(ctx, from, to) {
@@ -31372,13 +32103,12 @@ function createEditSession(workbook, options = {}) {
31372
32103
  for (const listener of [...listeners]) listener(change);
31373
32104
  };
31374
32105
  const calculator = new SessionCalculator(workbook, calc, autoRecalc);
31375
- const recalc = (step2) => calculator.afterStep(step2);
31376
- const begin = (label2, kind, info) => ({
31377
- step: { label: label2, entries: [], structural: info.structural ?? false },
31378
- kind,
31379
- sheet: info.sheet,
31380
- ranges: [...info.ranges ?? []]
31381
- });
32106
+ const recalc = (step2, mode = "apply") => calculator.afterStep(step2, mode);
32107
+ const begin = (label2, kind, info) => {
32108
+ const step2 = { label: label2, entries: [], structural: info.structural ?? false };
32109
+ if (info.calc) step2.calc = info.calc;
32110
+ return { step: step2, kind, sheet: info.sheet, ranges: [...info.ranges ?? []] };
32111
+ };
31382
32112
  const finish = (current) => {
31383
32113
  if (!current.step.entries.length) return;
31384
32114
  history.push(current.step);
@@ -31403,10 +32133,12 @@ function createEditSession(workbook, options = {}) {
31403
32133
  const outer = !open;
31404
32134
  const current = open ?? begin(label2, kind, info);
31405
32135
  if (!outer) {
32136
+ delete current.step.calc;
31406
32137
  if (info.structural) current.step.structural = true;
31407
32138
  if (current.sheet === void 0 && info.sheet !== void 0) current.sheet = info.sheet;
31408
32139
  current.ranges.push(...info.ranges ?? []);
31409
32140
  }
32141
+ if (outer && info.calc) calculator.beforeStructural();
31410
32142
  open = current;
31411
32143
  const mark = current.step.entries.length;
31412
32144
  const befores = scopes.map((scope) => captureScope(workbook, scope));
@@ -31454,7 +32186,7 @@ function createEditSession(workbook, options = {}) {
31454
32186
  if (open) throw new Error(`Cannot ${kind} inside a batch`);
31455
32187
  const step2 = kind === "undo" ? history.undo(workbook) : history.redo(workbook);
31456
32188
  if (!step2) return false;
31457
- recalc(step2);
32189
+ recalc(step2, kind);
31458
32190
  emit({ kind, label: step2.label, structural: step2.structural });
31459
32191
  return true;
31460
32192
  };
@@ -31533,10 +32265,11 @@ function createEditSession(workbook, options = {}) {
31533
32265
  setDocumentProperties: (patch2) => setDocumentProperties(ctx, patch2),
31534
32266
  setAutoRecalc: (enabled) => setCalcMode(ctx, enabled ? "auto" : "manual"),
31535
32267
  autoRecalc: () => autoRecalc && calcModeOf(workbook) === "auto",
31536
- calculateNow() {
31537
- calculator.full();
32268
+ calculateNow(options2) {
32269
+ calculator.now(options2?.full ?? false);
31538
32270
  emit({ kind: "cells", label: "Calculate now", structural: false });
31539
32271
  },
32272
+ prepareCalculation: (opts) => calculator.prepare(opts?.timeRemaining),
31540
32273
  calculateSheet(s) {
31541
32274
  calculator.sheet(s);
31542
32275
  emit({ kind: "cells", label: "Calculate sheet", sheet: s, structural: false });
@@ -32191,6 +32924,7 @@ function selectionStats(workbook, sheet, ranges) {
32191
32924
  const seen = /* @__PURE__ */ new Set();
32192
32925
  let min = Infinity;
32193
32926
  let max = -Infinity;
32927
+ let numFmt;
32194
32928
  for (const range of ranges)
32195
32929
  forEachCellInRange(ws, range, (cell, row2, col) => {
32196
32930
  if (ranges.length > 1) {
@@ -32203,6 +32937,7 @@ function selectionStats(workbook, sheet, ranges) {
32203
32937
  out.count++;
32204
32938
  if (typeof value === "number" && Number.isFinite(value)) {
32205
32939
  out.numericCount++;
32940
+ numFmt ??= styleAt(workbook, effectiveStyleId(ws, row2, col)).numFmt || "General";
32206
32941
  out.sum += value;
32207
32942
  min = Math.min(min, value);
32208
32943
  max = Math.max(max, value);
@@ -32212,9 +32947,13 @@ function selectionStats(workbook, sheet, ranges) {
32212
32947
  out.average = out.sum / out.numericCount;
32213
32948
  out.min = min;
32214
32949
  out.max = max;
32950
+ out.numFmt = numFmt ?? "General";
32215
32951
  }
32216
32952
  return out;
32217
32953
  }
32954
+ function formatSelectionStat(workbook, stats, value) {
32955
+ return formatValue(value, stats.numFmt ?? "General", { date1904: workbook.date1904 }).text;
32956
+ }
32218
32957
  function isOverflowTarget(sheet, row2, col) {
32219
32958
  const cell = getCell(sheet, row2, col);
32220
32959
  if (cell && (cell.value !== null && cell.value !== "" || cell.formula)) return false;
@@ -33099,7 +33838,7 @@ function chartAreaRect(model, part, area) {
33099
33838
  function chartBarSpacing(chart) {
33100
33839
  const stacked = chart.grouping === "stacked" || chart.grouping === "percentStacked";
33101
33840
  return {
33102
- barGapWidth: chart.barGapWidth ?? 150,
33841
+ barGapWidth: chart.barGapWidth ?? DEFAULT_BAR_GAP_WIDTH,
33103
33842
  barOverlap: chart.barOverlap ?? (stacked ? 100 : 0)
33104
33843
  };
33105
33844
  }
@@ -34044,6 +34783,7 @@ exports.formatAxisValue = formatAxisValue;
34044
34783
  exports.formatGeneral = formatGeneral;
34045
34784
  exports.formatRange = formatRange;
34046
34785
  exports.formatRefSpec = formatRefSpec;
34786
+ exports.formatSelectionStat = formatSelectionStat;
34047
34787
  exports.formatValue = formatValue;
34048
34788
  exports.formulaBarText = formulaBarText;
34049
34789
  exports.functionMatches = functionMatches;