@pixerate/editor 0.21.0 → 0.22.0

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 (70) hide show
  1. package/README.md +20 -11
  2. package/dist/agent/index.cjs +8 -8
  3. package/dist/agent/index.d.cts +2 -2
  4. package/dist/agent/index.d.ts +2 -2
  5. package/dist/agent/index.js +2 -2
  6. package/dist/canvas/index.cjs +1269 -13
  7. package/dist/canvas/index.cjs.map +1 -1
  8. package/dist/canvas/index.d.cts +432 -6
  9. package/dist/canvas/index.d.ts +432 -6
  10. package/dist/canvas/index.js +1264 -8
  11. package/dist/canvas/index.js.map +1 -1
  12. package/dist/{chunk-U6NDQFOK.cjs → chunk-6M3BCTBD.cjs} +427 -138
  13. package/dist/chunk-6M3BCTBD.cjs.map +1 -0
  14. package/dist/{chunk-TVRC4KVT.cjs → chunk-AA4ZEVCC.cjs} +845 -90
  15. package/dist/chunk-AA4ZEVCC.cjs.map +1 -0
  16. package/dist/{chunk-LA7PEGU3.cjs → chunk-CL37JGFW.cjs} +4 -4
  17. package/dist/{chunk-LA7PEGU3.cjs.map → chunk-CL37JGFW.cjs.map} +1 -1
  18. package/dist/{chunk-A7DKIA72.js → chunk-DENSCKFC.js} +642 -69
  19. package/dist/chunk-DENSCKFC.js.map +1 -0
  20. package/dist/{chunk-4QPLEUL2.cjs → chunk-GVSMRPHB.cjs} +1046 -54
  21. package/dist/chunk-GVSMRPHB.cjs.map +1 -0
  22. package/dist/{chunk-KYRZDWM4.js → chunk-JJDLPEWL.js} +2 -2
  23. package/dist/{chunk-3TYMNULT.js → chunk-MJZBNWG7.js} +807 -52
  24. package/dist/chunk-MJZBNWG7.js.map +1 -0
  25. package/dist/{chunk-CYFJCKC4.js → chunk-QGHGJ56O.js} +997 -5
  26. package/dist/chunk-QGHGJ56O.js.map +1 -0
  27. package/dist/{chunk-GVW4P7BG.js → chunk-RU52I7QO.js} +305 -16
  28. package/dist/chunk-RU52I7QO.js.map +1 -0
  29. package/dist/{chunk-JYRIOFEN.cjs → chunk-SAZRENEZ.cjs} +647 -74
  30. package/dist/chunk-SAZRENEZ.cjs.map +1 -0
  31. package/dist/{controller-OUdxPY-X.d.ts → controller-B7GIybc4.d.cts} +139 -7
  32. package/dist/{controller-OUdxPY-X.d.cts → controller-B7GIybc4.d.ts} +139 -7
  33. package/dist/extensions/index.cjs +14 -4
  34. package/dist/extensions/index.cjs.map +1 -1
  35. package/dist/extensions/index.d.cts +3 -3
  36. package/dist/extensions/index.d.ts +3 -3
  37. package/dist/extensions/index.js +13 -3
  38. package/dist/{index-k3mFbPx_.d.cts → index-CCr_z9kF.d.cts} +3 -3
  39. package/dist/{index-Ng1pG7JE.d.ts → index-Ck6ajwXh.d.ts} +3 -3
  40. package/dist/index.cjs +250 -16
  41. package/dist/index.cjs.map +1 -1
  42. package/dist/index.d.cts +294 -8
  43. package/dist/index.d.ts +294 -8
  44. package/dist/index.js +241 -7
  45. package/dist/index.js.map +1 -1
  46. package/dist/{registry-iAHbqkYt.d.cts → registry-GJI4gToU.d.cts} +15 -4
  47. package/dist/{registry-iAHbqkYt.d.ts → registry-GJI4gToU.d.ts} +15 -4
  48. package/dist/serializers/index.cjs +3 -3
  49. package/dist/serializers/index.d.cts +1 -1
  50. package/dist/serializers/index.d.ts +1 -1
  51. package/dist/serializers/index.js +2 -2
  52. package/dist/spreadsheet/index.cjs +7 -3
  53. package/dist/spreadsheet/index.cjs.map +1 -1
  54. package/dist/spreadsheet/index.d.cts +33 -4
  55. package/dist/spreadsheet/index.d.ts +33 -4
  56. package/dist/spreadsheet/index.js +6 -2
  57. package/dist/{track-changes-G8JcWB8b.d.cts → track-changes-BSF58RK0.d.cts} +92 -9
  58. package/dist/{track-changes-G8JcWB8b.d.ts → track-changes-BSF58RK0.d.ts} +92 -9
  59. package/dist/{types-DMe0vFB1.d.cts → types-CJ4U-HHI.d.cts} +1 -1
  60. package/dist/{types-DMe0vFB1.d.ts → types-CJ4U-HHI.d.ts} +1 -1
  61. package/package.json +1 -1
  62. package/dist/chunk-3TYMNULT.js.map +0 -1
  63. package/dist/chunk-4QPLEUL2.cjs.map +0 -1
  64. package/dist/chunk-A7DKIA72.js.map +0 -1
  65. package/dist/chunk-CYFJCKC4.js.map +0 -1
  66. package/dist/chunk-GVW4P7BG.js.map +0 -1
  67. package/dist/chunk-JYRIOFEN.cjs.map +0 -1
  68. package/dist/chunk-TVRC4KVT.cjs.map +0 -1
  69. package/dist/chunk-U6NDQFOK.cjs.map +0 -1
  70. /package/dist/{chunk-KYRZDWM4.js.map → chunk-JJDLPEWL.js.map} +0 -0
@@ -10,7 +10,9 @@
10
10
 
11
11
 
12
12
 
13
- var _chunkJYRIOFENcjs = require('./chunk-JYRIOFEN.cjs');
13
+
14
+
15
+ var _chunkSAZRENEZcjs = require('./chunk-SAZRENEZ.cjs');
14
16
 
15
17
  // src/spreadsheet/formula/dependency-graph.ts
16
18
  var columnDependencyKey = (col) => `${col.toUpperCase()}:`;
@@ -19,22 +21,24 @@ function extractCellDependencies(node) {
19
21
  const visit = (curr) => {
20
22
  if (curr.type === "CellRef") {
21
23
  deps.push({ col: curr.col, row: curr.row });
22
- } else if (curr.type === "Range" && curr.endRow >= _chunkJYRIOFENcjs.WHOLE_COLUMN_END_ROW) {
23
- const startC = _chunkJYRIOFENcjs.colNameToIndex.call(void 0, curr.startCol);
24
- const endC = _chunkJYRIOFENcjs.colNameToIndex.call(void 0, curr.endCol);
24
+ } else if (curr.type === "SpillRef") {
25
+ deps.push({ col: curr.col, row: curr.row });
26
+ } else if (curr.type === "Range" && curr.endRow >= _chunkSAZRENEZcjs.WHOLE_COLUMN_END_ROW) {
27
+ const startC = _chunkSAZRENEZcjs.colNameToIndex.call(void 0, curr.startCol);
28
+ const endC = _chunkSAZRENEZcjs.colNameToIndex.call(void 0, curr.endCol);
25
29
  for (let c = Math.min(startC, endC); c <= Math.max(startC, endC); c++) {
26
- deps.push({ col: _chunkJYRIOFENcjs.indexToColName.call(void 0, c), row: 0 });
30
+ deps.push({ col: _chunkSAZRENEZcjs.indexToColName.call(void 0, c), row: 0 });
27
31
  }
28
32
  } else if (curr.type === "Range") {
29
- const startC = _chunkJYRIOFENcjs.colNameToIndex.call(void 0, curr.startCol);
30
- const endC = _chunkJYRIOFENcjs.colNameToIndex.call(void 0, curr.endCol);
33
+ const startC = _chunkSAZRENEZcjs.colNameToIndex.call(void 0, curr.startCol);
34
+ const endC = _chunkSAZRENEZcjs.colNameToIndex.call(void 0, curr.endCol);
31
35
  const minC = Math.min(startC, endC);
32
36
  const maxC = Math.max(startC, endC);
33
37
  const minR = Math.min(curr.startRow, curr.endRow);
34
38
  const maxR = Math.max(curr.startRow, curr.endRow);
35
39
  for (let c = minC; c <= maxC; c++) {
36
40
  for (let r = minR; r <= maxR; r++) {
37
- deps.push({ col: _chunkJYRIOFENcjs.indexToColName.call(void 0, c), row: r });
41
+ deps.push({ col: _chunkSAZRENEZcjs.indexToColName.call(void 0, c), row: r });
38
42
  }
39
43
  }
40
44
  } else if (curr.type === "BinaryOp") {
@@ -49,11 +53,19 @@ function extractCellDependencies(node) {
49
53
  visit(node);
50
54
  return deps;
51
55
  }
52
- var DependencyGraph = (_class = class {constructor() { _class.prototype.__init.call(this);_class.prototype.__init2.call(this); }
56
+ var DependencyGraph = (_class = class {constructor() { _class.prototype.__init.call(this);_class.prototype.__init2.call(this);_class.prototype.__init3.call(this);_class.prototype.__init4.call(this);_class.prototype.__init5.call(this); }
53
57
  // Map of cellKey (e.g. "B2") -> Set of cellKeys that this cell depends ON
54
58
  __init() {this.dependencies = /* @__PURE__ */ new Map()}
55
59
  // Map of cellKey (e.g. "A1") -> Set of cellKeys that depend ON this cell (dependents)
56
60
  __init2() {this.dependents = /* @__PURE__ */ new Map()}
61
+ // Dynamic-array edges, set by the controller after evaluating an anchor:
62
+ // spilled cells depend on their anchor, and a blocked (#SPILL!) anchor
63
+ // depends on the cells blocking it. Kept apart from formula dependencies,
64
+ // so re-entering a formula doesn't drop them.
65
+ __init3() {this.spillDependencies = /* @__PURE__ */ new Map()}
66
+ __init4() {this.spillDependents = /* @__PURE__ */ new Map()}
67
+ // anchor -> the edges it registered (so they can be replaced).
68
+ __init5() {this.spillEdges = /* @__PURE__ */ new Map()}
57
69
  setCellFormula(cellKey, formulaStr) {
58
70
  const key = cellKey.toUpperCase();
59
71
  const oldDeps = this.dependencies.get(key) || /* @__PURE__ */ new Set();
@@ -65,7 +77,7 @@ var DependencyGraph = (_class = class {constructor() { _class.prototype.__init.c
65
77
  return;
66
78
  }
67
79
  try {
68
- const ast = _chunkJYRIOFENcjs.parseFormula.call(void 0, formulaStr);
80
+ const ast = _chunkSAZRENEZcjs.parseFormula.call(void 0, formulaStr);
69
81
  const deps = extractCellDependencies(ast);
70
82
  const newDeps = /* @__PURE__ */ new Set();
71
83
  for (const d of deps) {
@@ -84,12 +96,49 @@ var DependencyGraph = (_class = class {constructor() { _class.prototype.__init.c
84
96
  clear() {
85
97
  this.dependencies.clear();
86
98
  this.dependents.clear();
99
+ this.spillDependencies.clear();
100
+ this.spillDependents.clear();
101
+ this.spillEdges.clear();
87
102
  }
103
+ /**
104
+ * Records the dynamic-array edges of the anchor `anchorKey`, replacing its
105
+ * previous ones: every key in `spilledKeys` (cells holding its spilled
106
+ * values) depends on the anchor, and the anchor depends on every key in
107
+ * `blockingKeys` (cells that stop it spilling). Pass empty lists to remove
108
+ * the anchor's edges.
109
+ */
110
+ setSpillEdges(anchorKey, spilledKeys, blockingKeys = []) {
111
+ const anchor = anchorKey.toUpperCase();
112
+ for (const [from, to] of _nullishCoalesce(this.spillEdges.get(anchor), () => ( []))) {
113
+ _optionalChain([this, 'access', _7 => _7.spillDependencies, 'access', _8 => _8.get, 'call', _9 => _9(from), 'optionalAccess', _10 => _10.delete, 'call', _11 => _11(to)]);
114
+ _optionalChain([this, 'access', _12 => _12.spillDependents, 'access', _13 => _13.get, 'call', _14 => _14(to), 'optionalAccess', _15 => _15.delete, 'call', _16 => _16(from)]);
115
+ }
116
+ const edges = [];
117
+ for (const key of spilledKeys) edges.push([key.toUpperCase(), anchor]);
118
+ for (const key of blockingKeys) edges.push([anchor, key.toUpperCase()]);
119
+ for (const [from, to] of edges) {
120
+ if (!this.spillDependencies.has(from)) this.spillDependencies.set(from, /* @__PURE__ */ new Set());
121
+ this.spillDependencies.get(from).add(to);
122
+ if (!this.spillDependents.has(to)) this.spillDependents.set(to, /* @__PURE__ */ new Set());
123
+ this.spillDependents.get(to).add(from);
124
+ }
125
+ if (edges.length > 0) this.spillEdges.set(anchor, edges);
126
+ else this.spillEdges.delete(anchor);
127
+ }
128
+ /** The spilled cells registered for `anchorKey` by `setSpillEdges`. */
129
+ getSpilledKeys(anchorKey) {
130
+ const anchor = anchorKey.toUpperCase();
131
+ return (_nullishCoalesce(this.spillEdges.get(anchor), () => ( []))).filter(([, to]) => to === anchor).map(([from]) => from);
132
+ }
133
+ /** Formula dependencies plus dynamic-array (spill) dependencies of a cell. */
88
134
  getDependencies(cellKey) {
89
- return Array.from(this.dependencies.get(cellKey.toUpperCase()) || []);
135
+ const key = cellKey.toUpperCase();
136
+ return unionOf(this.dependencies.get(key), this.spillDependencies.get(key));
90
137
  }
138
+ /** Cells depending on `cellKey`, through formulas or spills. */
91
139
  getDependents(cellKey) {
92
- return Array.from(this.dependents.get(cellKey.toUpperCase()) || []);
140
+ const key = cellKey.toUpperCase();
141
+ return unionOf(this.dependents.get(key), this.spillDependents.get(key));
93
142
  }
94
143
  /**
95
144
  * Detects if there is a cycle containing cellKey.
@@ -101,7 +150,7 @@ var DependencyGraph = (_class = class {constructor() { _class.prototype.__init.c
101
150
  const dfs = (node) => {
102
151
  visited.add(node);
103
152
  recStack.add(node);
104
- const deps = this.dependencies.get(node) || /* @__PURE__ */ new Set();
153
+ const deps = this.getDependencies(node);
105
154
  for (const neighbor of deps) {
106
155
  if (!visited.has(neighbor)) {
107
156
  if (dfs(neighbor)) return true;
@@ -180,6 +229,7 @@ var DependencyGraph = (_class = class {constructor() { _class.prototype.__init.c
180
229
  const node = queue.pop();
181
230
  const direct = [
182
231
  ..._nullishCoalesce(this.dependents.get(node), () => ( [])),
232
+ ..._nullishCoalesce(this.spillDependents.get(node), () => ( [])),
183
233
  ..._nullishCoalesce(this.dependents.get(columnDependencyKey(node.replace(/\d+$/, ""))), () => ( []))
184
234
  ];
185
235
  for (const dep of direct) {
@@ -192,7 +242,7 @@ var DependencyGraph = (_class = class {constructor() { _class.prototype.__init.c
192
242
  }
193
243
  const neighbors = (node) => {
194
244
  const result = [];
195
- for (const dep of _nullishCoalesce(this.dependencies.get(node), () => ( []))) {
245
+ for (const dep of this.getDependencies(node)) {
196
246
  if (dep.endsWith(":")) {
197
247
  const col = dep.slice(0, -1);
198
248
  for (const candidate of nodes) {
@@ -258,6 +308,11 @@ var DependencyGraph = (_class = class {constructor() { _class.prototype.__init.c
258
308
  return { order, cyclic };
259
309
  }
260
310
  }, _class);
311
+ function unionOf(a, b) {
312
+ if (!b || b.size === 0) return Array.from(_nullishCoalesce(a, () => ( [])));
313
+ if (!a || a.size === 0) return Array.from(b);
314
+ return Array.from(/* @__PURE__ */ new Set([...a, ...b]));
315
+ }
261
316
 
262
317
  // src/spreadsheet/formula/references.ts
263
318
  var CELL_PART = /^(\$?)([A-Za-z]+)(\$?)(\d+)$/;
@@ -268,19 +323,19 @@ function parsePart(text) {
268
323
  if (cell) {
269
324
  return {
270
325
  colAbs: cell[1],
271
- col: _chunkJYRIOFENcjs.colNameToIndex.call(void 0, cell[2]),
326
+ col: _chunkSAZRENEZcjs.colNameToIndex.call(void 0, cell[2]),
272
327
  rowAbs: cell[3],
273
328
  row: parseInt(cell[4], 10) - 1
274
329
  };
275
330
  }
276
331
  const column = COLUMN_PART.exec(text);
277
332
  if (column) {
278
- return { colAbs: column[1], col: _chunkJYRIOFENcjs.colNameToIndex.call(void 0, column[2]), rowAbs: "", row: null };
333
+ return { colAbs: column[1], col: _chunkSAZRENEZcjs.colNameToIndex.call(void 0, column[2]), rowAbs: "", row: null };
279
334
  }
280
335
  return null;
281
336
  }
282
337
  function formatPart(part) {
283
- const col = `${part.colAbs}${_chunkJYRIOFENcjs.indexToColName.call(void 0, part.col)}`;
338
+ const col = `${part.colAbs}${_chunkSAZRENEZcjs.indexToColName.call(void 0, part.col)}`;
284
339
  return part.row === null ? col : `${col}${part.rowAbs}${part.row + 1}`;
285
340
  }
286
341
  function shiftCoordinate(value, change) {
@@ -327,7 +382,7 @@ function mapFormulaReferences(formula, rewrite) {
327
382
  if (!formula.startsWith("=")) return formula;
328
383
  let tokens;
329
384
  try {
330
- tokens = _chunkJYRIOFENcjs.tokenizeFormula.call(void 0, formula);
385
+ tokens = _chunkSAZRENEZcjs.tokenizeFormula.call(void 0, formula);
331
386
  } catch (e2) {
332
387
  return formula;
333
388
  }
@@ -335,11 +390,17 @@ function mapFormulaReferences(formula, rewrite) {
335
390
  let cursor = 0;
336
391
  for (let i = 0; i < tokens.length; i++) {
337
392
  const token = tokens[i];
338
- if (token.type !== "CELL_REF" && token.type !== "RANGE") continue;
339
- if (_optionalChain([tokens, 'access', _7 => _7[i + 1], 'optionalAccess', _8 => _8.type]) === "LPAREN") continue;
393
+ if (token.type !== "CELL_REF" && token.type !== "RANGE" && token.type !== "SPILL_REF") continue;
394
+ if (_optionalChain([tokens, 'access', _17 => _17[i + 1], 'optionalAccess', _18 => _18.type]) === "LPAREN") continue;
340
395
  let end = token.position;
341
396
  while (end < formula.length && REF_CHAR.test(formula[end])) end++;
342
397
  const original = formula.slice(token.position, end);
398
+ if (token.type === "SPILL_REF") {
399
+ const rewritten = rewrite(original);
400
+ result += formula.slice(cursor, token.position) + (rewritten === "#REF!" ? rewritten : `${rewritten}#`);
401
+ cursor = end + 1;
402
+ continue;
403
+ }
343
404
  result += formula.slice(cursor, token.position) + rewrite(original);
344
405
  cursor = end;
345
406
  }
@@ -459,7 +520,7 @@ function innermostCall(stack) {
459
520
  }
460
521
  var toSuggestion = (def) => ({
461
522
  name: def.name,
462
- signature: _chunkJYRIOFENcjs.getFormulaSignature.call(void 0, def),
523
+ signature: _chunkSAZRENEZcjs.getFormulaSignature.call(void 0, def),
463
524
  description: def.description,
464
525
  category: def.category
465
526
  });
@@ -468,20 +529,20 @@ function getFormulaHints(input, caret = input.length, options = {}) {
468
529
  let suggestions = [];
469
530
  if (context.prefix) {
470
531
  const query = context.prefix.text.toUpperCase();
471
- suggestions = _chunkJYRIOFENcjs.searchFormulaFunctions.call(void 0, query, { limit: void 0 }).filter((def) => def.name.startsWith(query)).slice(0, _nullishCoalesce(options.limit, () => ( 8))).map(toSuggestion);
532
+ suggestions = _chunkSAZRENEZcjs.searchFormulaFunctions.call(void 0, query, { limit: void 0 }).filter((def) => def.name.startsWith(query)).slice(0, _nullishCoalesce(options.limit, () => ( 8))).map(toSuggestion);
472
533
  const followedByParen = input.slice(context.prefix.end).trimStart().startsWith("(");
473
534
  if (followedByParen || suggestions.length === 1 && suggestions[0].name === query) suggestions = [];
474
535
  }
475
536
  let activeCall = null;
476
537
  if (context.call) {
477
- const def = _chunkJYRIOFENcjs.getFormulaFunction.call(void 0, context.call.name);
538
+ const def = _chunkSAZRENEZcjs.getFormulaFunction.call(void 0, context.call.name);
478
539
  if (def) {
479
- const specIndex = _chunkJYRIOFENcjs.getFormulaArgumentSpecIndex.call(void 0, def, context.call.argIndex);
540
+ const specIndex = _chunkSAZRENEZcjs.getFormulaArgumentSpecIndex.call(void 0, def, context.call.argIndex);
480
541
  activeCall = {
481
542
  name: def.name,
482
- signature: _chunkJYRIOFENcjs.getFormulaSignature.call(void 0, def),
543
+ signature: _chunkSAZRENEZcjs.getFormulaSignature.call(void 0, def),
483
544
  description: def.description,
484
- parts: _chunkJYRIOFENcjs.getFormulaSignatureParts.call(void 0, def, context.call.argIndex),
545
+ parts: _chunkSAZRENEZcjs.getFormulaSignatureParts.call(void 0, def, context.call.argIndex),
485
546
  argIndex: context.call.argIndex,
486
547
  argument: specIndex >= 0 ? def.args[specIndex] : null
487
548
  };
@@ -491,8 +552,8 @@ function getFormulaHints(input, caret = input.length, options = {}) {
491
552
  }
492
553
  function applyFormulaSuggestion(input, caret, name) {
493
554
  const context = getFormulaCaretContext(input, caret);
494
- const start = _nullishCoalesce(_optionalChain([context, 'access', _9 => _9.prefix, 'optionalAccess', _10 => _10.start]), () => ( caret));
495
- const end = _nullishCoalesce(_optionalChain([context, 'access', _11 => _11.prefix, 'optionalAccess', _12 => _12.end]), () => ( caret));
555
+ const start = _nullishCoalesce(_optionalChain([context, 'access', _19 => _19.prefix, 'optionalAccess', _20 => _20.start]), () => ( caret));
556
+ const end = _nullishCoalesce(_optionalChain([context, 'access', _21 => _21.prefix, 'optionalAccess', _22 => _22.end]), () => ( caret));
496
557
  const hasParen = input[end] === "(";
497
558
  const insert = hasParen ? name.toUpperCase() : `${name.toUpperCase()}(`;
498
559
  const value = input.slice(0, start) + insert + input.slice(end);
@@ -500,8 +561,8 @@ function applyFormulaSuggestion(input, caret, name) {
500
561
  }
501
562
 
502
563
  // src/spreadsheet/datasource/index.ts
503
- var DataSourceBinder = (_class2 = class {constructor() { _class2.prototype.__init3.call(this); }
504
- __init3() {this.dataSources = /* @__PURE__ */ new Map()}
564
+ var DataSourceBinder = (_class2 = class {constructor() { _class2.prototype.__init6.call(this); }
565
+ __init6() {this.dataSources = /* @__PURE__ */ new Map()}
505
566
  register(dataSource) {
506
567
  this.dataSources.set(dataSource.id, dataSource);
507
568
  }
@@ -532,10 +593,10 @@ var DataSourceBinder = (_class2 = class {constructor() { _class2.prototype.__ini
532
593
  for (const record of records) {
533
594
  const existing = existingRecordRowMap.get(record.id);
534
595
  nextRows.push({
535
- ..._optionalChain([existing, 'optionalAccess', _13 => _13.hidden]) ? { hidden: true } : {},
536
- id: _optionalChain([existing, 'optionalAccess', _14 => _14.id]) || `row_${record.id}`,
596
+ ..._optionalChain([existing, 'optionalAccess', _23 => _23.hidden]) ? { hidden: true } : {},
597
+ id: _optionalChain([existing, 'optionalAccess', _24 => _24.id]) || `row_${record.id}`,
537
598
  index: rowIndex++,
538
- height: _optionalChain([existing, 'optionalAccess', _15 => _15.height]) || 32,
599
+ height: _optionalChain([existing, 'optionalAccess', _25 => _25.height]) || 32,
539
600
  recordId: record.id,
540
601
  type: "record"
541
602
  });
@@ -991,8 +1052,8 @@ function parseHtmlTable(html) {
991
1052
  while (cellMatch = cellPattern.exec(rowMatch[1])) {
992
1053
  while (taken.has(col)) row[col++] = "";
993
1054
  const attrs = _nullishCoalesce(cellMatch[2], () => ( ""));
994
- const colspan = Math.max(1, parseInt(_nullishCoalesce(_optionalChain([/colspan\s*=\s*["']?(\d+)/i, 'access', _16 => _16.exec, 'call', _17 => _17(attrs), 'optionalAccess', _18 => _18[1]]), () => ( "1")), 10));
995
- const rowspan = Math.max(1, parseInt(_nullishCoalesce(_optionalChain([/rowspan\s*=\s*["']?(\d+)/i, 'access', _19 => _19.exec, 'call', _20 => _20(attrs), 'optionalAccess', _21 => _21[1]]), () => ( "1")), 10));
1055
+ const colspan = Math.max(1, parseInt(_nullishCoalesce(_optionalChain([/colspan\s*=\s*["']?(\d+)/i, 'access', _26 => _26.exec, 'call', _27 => _27(attrs), 'optionalAccess', _28 => _28[1]]), () => ( "1")), 10));
1056
+ const rowspan = Math.max(1, parseInt(_nullishCoalesce(_optionalChain([/rowspan\s*=\s*["']?(\d+)/i, 'access', _29 => _29.exec, 'call', _30 => _30(attrs), 'optionalAccess', _31 => _31[1]]), () => ( "1")), 10));
996
1057
  row[col] = cellHtmlToText(cellMatch[3]);
997
1058
  for (let c = col; c < col + colspan; c++) {
998
1059
  if (c > col) row[c] = "";
@@ -1031,7 +1092,7 @@ function readFormat(source, format) {
1031
1092
  function parsePayload(json) {
1032
1093
  try {
1033
1094
  const payload = JSON.parse(json);
1034
- if (_optionalChain([payload, 'optionalAccess', _22 => _22.type]) !== "pixerate-spreadsheet" || !Array.isArray(payload.cells) || !payload.cells.every((row) => Array.isArray(row)) || typeof _optionalChain([payload, 'access', _23 => _23.origin, 'optionalAccess', _24 => _24.row]) !== "number" || typeof _optionalChain([payload, 'access', _25 => _25.origin, 'optionalAccess', _26 => _26.col]) !== "number") {
1095
+ if (_optionalChain([payload, 'optionalAccess', _32 => _32.type]) !== "pixerate-spreadsheet" || !Array.isArray(payload.cells) || !payload.cells.every((row) => Array.isArray(row)) || typeof _optionalChain([payload, 'access', _33 => _33.origin, 'optionalAccess', _34 => _34.row]) !== "number" || typeof _optionalChain([payload, 'access', _35 => _35.origin, 'optionalAccess', _36 => _36.col]) !== "number") {
1035
1096
  return null;
1036
1097
  }
1037
1098
  return payload;
@@ -1045,7 +1106,7 @@ function readSpreadsheetClipboard(source) {
1045
1106
  return {
1046
1107
  format: "internal",
1047
1108
  origin: payload.origin,
1048
- rows: payload.cells.map((row) => row.map((cell) => typeof _optionalChain([cell, 'optionalAccess', _27 => _27.raw]) === "string" ? cell.raw : displayText(_optionalChain([cell, 'optionalAccess', _28 => _28.value]))))
1109
+ rows: payload.cells.map((row) => row.map((cell) => typeof _optionalChain([cell, 'optionalAccess', _37 => _37.raw]) === "string" ? cell.raw : displayText(_optionalChain([cell, 'optionalAccess', _38 => _38.value]))))
1049
1110
  };
1050
1111
  }
1051
1112
  const html = readFormat(source, "text/html");
@@ -1074,8 +1135,8 @@ var DEFAULT_COLUMN_WIDTH = 130;
1074
1135
  var clampCount = (value, max) => Math.max(0, Math.min(Math.floor(_nullishCoalesce(value, () => ( 0))), max));
1075
1136
  function getTableSections(doc) {
1076
1137
  const total = doc.rows.length;
1077
- const headerRows = clampCount(_optionalChain([doc, 'access', _29 => _29.table, 'optionalAccess', _30 => _30.headerRowCount]), total);
1078
- const footerRows = clampCount(_optionalChain([doc, 'access', _31 => _31.table, 'optionalAccess', _32 => _32.footerRowCount]), total - headerRows);
1138
+ const headerRows = clampCount(_optionalChain([doc, 'access', _39 => _39.table, 'optionalAccess', _40 => _40.headerRowCount]), total);
1139
+ const footerRows = clampCount(_optionalChain([doc, 'access', _41 => _41.table, 'optionalAccess', _42 => _42.footerRowCount]), total - headerRows);
1079
1140
  return { headerRows, footerRows };
1080
1141
  }
1081
1142
  function getRowSection(doc, rowIndex) {
@@ -1085,8 +1146,8 @@ function getRowSection(doc, rowIndex) {
1085
1146
  return "body";
1086
1147
  }
1087
1148
  function rowPassesFilters(doc, rowIndex, getValue) {
1088
- const filter = _optionalChain([doc, 'access', _33 => _33.table, 'optionalAccess', _34 => _34.filter]);
1089
- if (!_optionalChain([filter, 'optionalAccess', _35 => _35.enabled]) || filter.rules.length === 0) return true;
1149
+ const filter = _optionalChain([doc, 'access', _43 => _43.table, 'optionalAccess', _44 => _44.filter]);
1150
+ if (!_optionalChain([filter, 'optionalAccess', _45 => _45.enabled]) || filter.rules.length === 0) return true;
1090
1151
  if (getRowSection(doc, rowIndex) !== "body") return true;
1091
1152
  return filter.rules.every((rule) => {
1092
1153
  const colIndex = doc.columns.findIndex((c) => c.id === rule.colId);
@@ -1095,7 +1156,7 @@ function rowPassesFilters(doc, rowIndex, getValue) {
1095
1156
  });
1096
1157
  }
1097
1158
  function computeRowGroups(doc, getValue) {
1098
- const grouping = _optionalChain([doc, 'access', _36 => _36.table, 'optionalAccess', _37 => _37.grouping]);
1159
+ const grouping = _optionalChain([doc, 'access', _46 => _46.table, 'optionalAccess', _47 => _47.grouping]);
1099
1160
  if (!grouping) return [];
1100
1161
  const groupCol = doc.columns.findIndex((c) => c.id === grouping.colId);
1101
1162
  if (groupCol === -1) return [];
@@ -1125,9 +1186,9 @@ function computeRowGroups(doc, getValue) {
1125
1186
  }
1126
1187
  function buildSpreadsheetView(doc, getValue) {
1127
1188
  const { headerRows, footerRows } = getTableSections(doc);
1128
- const frozenRows = clampCount(_optionalChain([doc, 'access', _38 => _38.table, 'optionalAccess', _39 => _39.frozenRowCount]), doc.rows.length);
1129
- const frozenCols = clampCount(_optionalChain([doc, 'access', _40 => _40.table, 'optionalAccess', _41 => _41.frozenColumnCount]), doc.columns.length);
1130
- const headerCols = clampCount(_optionalChain([doc, 'access', _42 => _42.table, 'optionalAccess', _43 => _43.headerColumnCount]), doc.columns.length);
1189
+ const frozenRows = clampCount(_optionalChain([doc, 'access', _48 => _48.table, 'optionalAccess', _49 => _49.frozenRowCount]), doc.rows.length);
1190
+ const frozenCols = clampCount(_optionalChain([doc, 'access', _50 => _50.table, 'optionalAccess', _51 => _51.frozenColumnCount]), doc.columns.length);
1191
+ const headerCols = clampCount(_optionalChain([doc, 'access', _52 => _52.table, 'optionalAccess', _53 => _53.headerColumnCount]), doc.columns.length);
1131
1192
  const columns = [];
1132
1193
  let left = 0;
1133
1194
  let hiddenColumnCount = 0;
@@ -1157,7 +1218,7 @@ function buildSpreadsheetView(doc, getValue) {
1157
1218
  if (doc.rows[r].hidden) hiddenRowCount++;
1158
1219
  else if (!rowPassesFilters(doc, r, getValue)) filteredOutCount++;
1159
1220
  }
1160
- const grouping = _optionalChain([doc, 'access', _44 => _44.table, 'optionalAccess', _45 => _45.grouping]);
1221
+ const grouping = _optionalChain([doc, 'access', _54 => _54.table, 'optionalAccess', _55 => _55.grouping]);
1161
1222
  if (grouping && doc.columns.some((c) => c.id === grouping.colId)) {
1162
1223
  for (const group of groups) {
1163
1224
  rows.push({ kind: "group", group });
@@ -1191,7 +1252,7 @@ function buildSpreadsheetView(doc, getValue) {
1191
1252
  while (run < visible.length && positions.get(visible[run]) === positions.get(visible[0]) + run) run++;
1192
1253
  return { first: visible[0], run };
1193
1254
  };
1194
- for (const raw of _nullishCoalesce(_optionalChain([doc, 'access', _46 => _46.table, 'optionalAccess', _47 => _47.merges]), () => ( []))) {
1255
+ for (const raw of _nullishCoalesce(_optionalChain([doc, 'access', _56 => _56.table, 'optionalAccess', _57 => _57.merges]), () => ( []))) {
1195
1256
  const merge = normalizeRange(raw);
1196
1257
  const rowIdx = [];
1197
1258
  const colIdx = [];
@@ -1228,29 +1289,43 @@ function buildSpreadsheetView(doc, getValue) {
1228
1289
 
1229
1290
  // src/spreadsheet/controller.ts
1230
1291
  var nextIdSequence = 0;
1292
+ var MAX_SPILL_PASSES = 25;
1293
+ var spillCellId = (rowId, colId) => `${rowId}\0${colId}`;
1231
1294
  var SpreadsheetController = (_class3 = class {
1232
1295
 
1233
- __init4() {this.activeCell = null}
1234
- __init5() {this.selectedRange = null}
1235
- __init6() {this.editingCell = null}
1236
- __init7() {this.draftValue = ""}
1237
- __init8() {this.binder = new DataSourceBinder()}
1238
- __init9() {this.depGraph = new DependencyGraph()}
1296
+ __init7() {this.activeCell = null}
1297
+ __init8() {this.selectedRange = null}
1298
+ __init9() {this.editingCell = null}
1299
+ __init10() {this.draftValue = ""}
1300
+ __init11() {this.binder = new DataSourceBinder()}
1301
+ __init12() {this.depGraph = new DependencyGraph()}
1239
1302
 
1240
1303
 
1241
1304
 
1242
1305
 
1243
1306
 
1244
1307
  // History stack for undo/redo
1245
- __init10() {this.undoStack = []}
1246
- __init11() {this.redoStack = []}
1247
- __init12() {this.isApplyingHistory = false}
1308
+ __init13() {this.undoStack = []}
1309
+ __init14() {this.redoStack = []}
1310
+ __init15() {this.isApplyingHistory = false}
1248
1311
  // Set while importing TSV: history and recalculation are done once at the end.
1249
- __init13() {this.batching = false}
1250
- __init14() {this.pendingCommits = []}
1312
+ __init16() {this.batching = false}
1313
+ __init17() {this.pendingCommits = []}
1251
1314
  // Row being evaluated, so [property] references resolve per row.
1252
- __init15() {this.evaluatingRowIndex = null}
1253
- constructor(options = {}) {;_class3.prototype.__init4.call(this);_class3.prototype.__init5.call(this);_class3.prototype.__init6.call(this);_class3.prototype.__init7.call(this);_class3.prototype.__init8.call(this);_class3.prototype.__init9.call(this);_class3.prototype.__init10.call(this);_class3.prototype.__init11.call(this);_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);_class3.prototype.__init14.call(this);_class3.prototype.__init15.call(this);_class3.prototype.__init16.call(this);
1315
+ __init18() {this.evaluatingRowIndex = null}
1316
+ // Cell being evaluated, for implicit intersection (`=@A:A`).
1317
+ __init19() {this.evaluatingCell = null}
1318
+ // Dynamic arrays. Spilled values are derived state: they live here, not in
1319
+ // the document, and every recalculation rebuilds them.
1320
+ __init20() {this.spills = /* @__PURE__ */ new Map()}
1321
+ // anchor key -> spill
1322
+ __init21() {this.spilledBy = /* @__PURE__ */ new Map()}
1323
+ // spilled cell key -> anchor key
1324
+ __init22() {this.spillCells = /* @__PURE__ */ new Map()}
1325
+ // spillCellId -> displayed cell
1326
+ // Cells whose spill ownership changed during the current pass.
1327
+ __init23() {this.spillChanges = null}
1328
+ constructor(options = {}) {;_class3.prototype.__init7.call(this);_class3.prototype.__init8.call(this);_class3.prototype.__init9.call(this);_class3.prototype.__init10.call(this);_class3.prototype.__init11.call(this);_class3.prototype.__init12.call(this);_class3.prototype.__init13.call(this);_class3.prototype.__init14.call(this);_class3.prototype.__init15.call(this);_class3.prototype.__init16.call(this);_class3.prototype.__init17.call(this);_class3.prototype.__init18.call(this);_class3.prototype.__init19.call(this);_class3.prototype.__init20.call(this);_class3.prototype.__init21.call(this);_class3.prototype.__init22.call(this);_class3.prototype.__init23.call(this);_class3.prototype.__init24.call(this);
1254
1329
  const defaultCols = [
1255
1330
  { id: "col_a", key: "A", title: "A", width: 120, type: "freeform" },
1256
1331
  { id: "col_b", key: "B", title: "B", width: 120, type: "freeform" },
@@ -1264,14 +1339,14 @@ var SpreadsheetController = (_class3 = class {
1264
1339
  type: "freeform"
1265
1340
  }));
1266
1341
  this.document = {
1267
- id: _optionalChain([options, 'access', _48 => _48.document, 'optionalAccess', _49 => _49.id]) || `sheet_${Date.now()}`,
1268
- name: _optionalChain([options, 'access', _50 => _50.document, 'optionalAccess', _51 => _51.name]) || "Sheet 1",
1269
- columns: _optionalChain([options, 'access', _52 => _52.document, 'optionalAccess', _53 => _53.columns]) || defaultCols,
1270
- rows: _optionalChain([options, 'access', _54 => _54.document, 'optionalAccess', _55 => _55.rows]) || defaultRows,
1271
- cells: _optionalChain([options, 'access', _56 => _56.document, 'optionalAccess', _57 => _57.cells]) || {},
1272
- createdAt: _optionalChain([options, 'access', _58 => _58.document, 'optionalAccess', _59 => _59.createdAt]) || Date.now(),
1273
- updatedAt: _optionalChain([options, 'access', _60 => _60.document, 'optionalAccess', _61 => _61.updatedAt]) || Date.now(),
1274
- ..._optionalChain([options, 'access', _62 => _62.document, 'optionalAccess', _63 => _63.table]) ? { table: options.document.table } : {}
1342
+ id: _optionalChain([options, 'access', _58 => _58.document, 'optionalAccess', _59 => _59.id]) || `sheet_${Date.now()}`,
1343
+ name: _optionalChain([options, 'access', _60 => _60.document, 'optionalAccess', _61 => _61.name]) || "Sheet 1",
1344
+ columns: _optionalChain([options, 'access', _62 => _62.document, 'optionalAccess', _63 => _63.columns]) || defaultCols,
1345
+ rows: _optionalChain([options, 'access', _64 => _64.document, 'optionalAccess', _65 => _65.rows]) || defaultRows,
1346
+ cells: _optionalChain([options, 'access', _66 => _66.document, 'optionalAccess', _67 => _67.cells]) || {},
1347
+ createdAt: _optionalChain([options, 'access', _68 => _68.document, 'optionalAccess', _69 => _69.createdAt]) || Date.now(),
1348
+ updatedAt: _optionalChain([options, 'access', _70 => _70.document, 'optionalAccess', _71 => _71.updatedAt]) || Date.now(),
1349
+ ..._optionalChain([options, 'access', _72 => _72.document, 'optionalAccess', _73 => _73.table]) ? { table: options.document.table } : {}
1275
1350
  };
1276
1351
  this.primaryDataSourceId = options.primaryDataSourceId;
1277
1352
  this.onDocumentChange = options.onDocumentChange;
@@ -1280,10 +1355,12 @@ var SpreadsheetController = (_class3 = class {
1280
1355
  if (options.dataSources) {
1281
1356
  options.dataSources.forEach((ds) => this.binder.register(ds));
1282
1357
  }
1283
- this.evaluator = new (0, _chunkJYRIOFENcjs.FormulaEvaluator)({
1358
+ this.evaluator = new (0, _chunkSAZRENEZcjs.FormulaEvaluator)({
1284
1359
  resolveCell: (colStr, rowNum) => this.resolveCellByCoord(colStr, rowNum),
1285
1360
  resolveRange: (sCol, sRow, eCol, eRow) => this.resolveRangeByCoords(sCol, sRow, eCol, eRow),
1286
- resolveProperty: (prop) => this.resolveCurrentRowProperty(prop)
1361
+ resolveProperty: (prop) => this.resolveCurrentRowProperty(prop),
1362
+ resolveSpill: (colStr, rowNum) => this.resolveSpillByCoord(colStr, rowNum),
1363
+ getFormulaCell: () => this.evaluatingCell
1287
1364
  });
1288
1365
  this.syncWithDataSource();
1289
1366
  this.recalculateAll();
@@ -1320,7 +1397,7 @@ var SpreadsheetController = (_class3 = class {
1320
1397
  r.index = idx;
1321
1398
  });
1322
1399
  this.document.columns.forEach((c, idx) => {
1323
- c.key = _chunkJYRIOFENcjs.indexToColName.call(void 0, idx);
1400
+ c.key = _chunkSAZRENEZcjs.indexToColName.call(void 0, idx);
1324
1401
  });
1325
1402
  }
1326
1403
  getColumn(colIdOrKey) {
@@ -1335,7 +1412,12 @@ var SpreadsheetController = (_class3 = class {
1335
1412
  return this.document.rows.find((r) => r.id === rowIdOrIndex);
1336
1413
  }
1337
1414
  getCell(rowId, colId) {
1338
- const existing = _optionalChain([this, 'access', _64 => _64.document, 'access', _65 => _65.cells, 'access', _66 => _66[rowId], 'optionalAccess', _67 => _67[colId]]);
1415
+ const existing = _optionalChain([this, 'access', _74 => _74.document, 'access', _75 => _75.cells, 'access', _76 => _76[rowId], 'optionalAccess', _77 => _77[colId]]);
1416
+ if (existing && existing.raw !== "") return existing;
1417
+ if (this.spillCells.size > 0) {
1418
+ const spilled = this.spillCells.get(spillCellId(rowId, colId));
1419
+ if (spilled) return spilled;
1420
+ }
1339
1421
  if (existing) return existing;
1340
1422
  const col = this.document.columns.find((c) => c.id === colId);
1341
1423
  const row = this.document.rows.find((r) => r.id === rowId);
@@ -1362,7 +1444,7 @@ var SpreadsheetController = (_class3 = class {
1362
1444
  format: col.format
1363
1445
  };
1364
1446
  }
1365
- return { raw: "", value: "", format: _optionalChain([col, 'optionalAccess', _68 => _68.format]) };
1447
+ return { raw: "", value: "", format: _optionalChain([col, 'optionalAccess', _78 => _78.format]) };
1366
1448
  }
1367
1449
  setCellValue(rowId, colId, raw) {
1368
1450
  this.recordSnapshot();
@@ -1371,7 +1453,7 @@ var SpreadsheetController = (_class3 = class {
1371
1453
  }
1372
1454
  const col = this.document.columns.find((c) => c.id === colId);
1373
1455
  const row = this.document.rows.find((r) => r.id === rowId);
1374
- if (_optionalChain([col, 'optionalAccess', _69 => _69.type]) === "bound" && col.binding && _optionalChain([row, 'optionalAccess', _70 => _70.recordId])) {
1456
+ if (_optionalChain([col, 'optionalAccess', _79 => _79.type]) === "bound" && col.binding && _optionalChain([row, 'optionalAccess', _80 => _80.recordId])) {
1375
1457
  this.binder.commitBoundValue(
1376
1458
  col.binding.dataSource,
1377
1459
  row.recordId,
@@ -1384,7 +1466,7 @@ var SpreadsheetController = (_class3 = class {
1384
1466
  this.document.cells[rowId][colId] = {
1385
1467
  raw,
1386
1468
  value: raw,
1387
- format: _optionalChain([col, 'optionalAccess', _71 => _71.format])
1469
+ format: _optionalChain([col, 'optionalAccess', _81 => _81.format])
1388
1470
  };
1389
1471
  this.document.updatedAt = Date.now();
1390
1472
  if (this.batching) {
@@ -1393,8 +1475,8 @@ var SpreadsheetController = (_class3 = class {
1393
1475
  }
1394
1476
  this.recalculateCell(rowId, colId);
1395
1477
  this.notifyChange();
1396
- const evaluatedVal = _optionalChain([this, 'access', _72 => _72.document, 'access', _73 => _73.cells, 'access', _74 => _74[rowId], 'access', _75 => _75[colId], 'optionalAccess', _76 => _76.value]);
1397
- _optionalChain([this, 'access', _77 => _77.onCellCommit, 'optionalCall', _78 => _78(rowId, colId, raw, evaluatedVal)]);
1478
+ const evaluatedVal = _optionalChain([this, 'access', _82 => _82.document, 'access', _83 => _83.cells, 'access', _84 => _84[rowId], 'access', _85 => _85[colId], 'optionalAccess', _86 => _86.value]);
1479
+ _optionalChain([this, 'access', _87 => _87.onCellCommit, 'optionalCall', _88 => _88(rowId, colId, raw, evaluatedVal)]);
1398
1480
  }
1399
1481
  getCellKey(rowId, colId) {
1400
1482
  const row = this.document.rows.find((r) => r.id === rowId);
@@ -1408,7 +1490,30 @@ var SpreadsheetController = (_class3 = class {
1408
1490
  */
1409
1491
  recalculateCell(rowId, colId) {
1410
1492
  const cellKey = this.getCellKey(rowId, colId);
1411
- this.runEvaluationPlan(this.depGraph.getEvaluationPlan([cellKey], { includeDependents: true }));
1493
+ const anchor = this.spilledBy.get(cellKey);
1494
+ this.recalculateKeys(anchor ? [cellKey, anchor] : [cellKey], true);
1495
+ }
1496
+ /**
1497
+ * Recalculates `keys` (and, with `includeDependents`, their dependents) in
1498
+ * dependency order. Spill ranges are only known after evaluating their
1499
+ * anchors, so when a pass changes which cells hold spilled values (and so
1500
+ * the dependency graph), the dependents of those cells are recalculated in
1501
+ * another pass, until nothing changes.
1502
+ */
1503
+ recalculateKeys(keys, includeDependents) {
1504
+ let pending = Array.from(keys);
1505
+ let withDependents = includeDependents;
1506
+ for (let pass = 0; pass < MAX_SPILL_PASSES && pending.length > 0; pass++) {
1507
+ const changed = /* @__PURE__ */ new Set();
1508
+ this.spillChanges = changed;
1509
+ try {
1510
+ this.runEvaluationPlan(this.depGraph.getEvaluationPlan(pending, { includeDependents: withDependents }));
1511
+ } finally {
1512
+ this.spillChanges = null;
1513
+ }
1514
+ pending = Array.from(changed);
1515
+ withDependents = true;
1516
+ }
1412
1517
  }
1413
1518
  /**
1414
1519
  * Rebuilds the dependency graph from scratch and recalculates every formula
@@ -1416,10 +1521,13 @@ var SpreadsheetController = (_class3 = class {
1416
1521
  */
1417
1522
  recalculateAll() {
1418
1523
  this.depGraph.clear();
1524
+ this.spills.clear();
1525
+ this.spilledBy.clear();
1526
+ this.spillCells.clear();
1419
1527
  const formulaKeys = [];
1420
1528
  for (const row of this.document.rows) {
1421
1529
  for (const col of this.document.columns) {
1422
- const cell = _nullishCoalesce(_optionalChain([this, 'access', _79 => _79.document, 'access', _80 => _80.cells, 'access', _81 => _81[row.id], 'optionalAccess', _82 => _82[col.id]]), () => ( this.getDerivedCell(row, col)));
1530
+ const cell = _nullishCoalesce(_optionalChain([this, 'access', _89 => _89.document, 'access', _90 => _90.cells, 'access', _91 => _91[row.id], 'optionalAccess', _92 => _92[col.id]]), () => ( this.getDerivedCell(row, col)));
1423
1531
  if (cell.raw.startsWith("=")) {
1424
1532
  const cellKey = `${col.key}${row.index + 1}`.toUpperCase();
1425
1533
  this.depGraph.setCellFormula(cellKey, cell.raw);
@@ -1427,7 +1535,7 @@ var SpreadsheetController = (_class3 = class {
1427
1535
  }
1428
1536
  }
1429
1537
  }
1430
- this.runEvaluationPlan(this.depGraph.getEvaluationPlan(formulaKeys));
1538
+ this.recalculateKeys(formulaKeys, false);
1431
1539
  }
1432
1540
  runEvaluationPlan(plan) {
1433
1541
  for (const key of plan.order) {
@@ -1454,7 +1562,10 @@ var SpreadsheetController = (_class3 = class {
1454
1562
  if (!this.document.cells[row.id]) this.document.cells[row.id] = {};
1455
1563
  const deps = this.depGraph.getDependencies(cellKey);
1456
1564
  const selfReference = deps.includes(cellKey) || deps.includes(`${col.key.toUpperCase()}:`);
1457
- const errorMessage = selfReference ? "This formula can't reference its own cell." : "This formula is part of a circular reference: it depends on its own result through other cells.";
1565
+ const spilledKeys = this.depGraph.getSpilledKeys(cellKey);
1566
+ const ownSpill = deps.find((dep) => spilledKeys.includes(dep));
1567
+ const errorMessage = selfReference ? "This formula can't reference its own cell." : ownSpill ? `This formula refers to ${ownSpill}, which holds its own spilled results (a circular reference).` : "This formula is part of a circular reference: it depends on its own result through other cells.";
1568
+ this.clearSpill(cellKey.toUpperCase(), true);
1458
1569
  this.document.cells[row.id][col.id] = { ...cell, value: "#CYCLE!", error: "#CYCLE!", errorMessage };
1459
1570
  }
1460
1571
  evalSingleCellByKey(cellKey) {
@@ -1471,25 +1582,176 @@ var SpreadsheetController = (_class3 = class {
1471
1582
  cell.error = void 0;
1472
1583
  cell.errorMessage = void 0;
1473
1584
  }
1585
+ this.clearSpill(`${colName}${rowNum}`);
1474
1586
  return;
1475
1587
  }
1476
1588
  try {
1477
1589
  this.evaluatingRowIndex = rowNum - 1;
1590
+ this.evaluatingCell = { col: colName, row: rowNum };
1478
1591
  const result = this.evaluator.evaluateWithDiagnostics(cell.raw);
1479
1592
  if (!this.document.cells[row.id]) {
1480
1593
  this.document.cells[row.id] = {};
1481
1594
  }
1595
+ let { value, error, message } = result;
1596
+ if (result.array) {
1597
+ const blocked = this.placeSpill(`${colName}${rowNum}`, rowNum - 1, this.document.columns.indexOf(col), result.array);
1598
+ if (blocked) {
1599
+ value = "#SPILL!";
1600
+ error = "#SPILL!";
1601
+ message = blocked;
1602
+ }
1603
+ } else {
1604
+ this.clearSpill(`${colName}${rowNum}`);
1605
+ }
1482
1606
  this.document.cells[row.id][col.id] = {
1483
1607
  ...cell,
1484
- value: result.value,
1608
+ value,
1485
1609
  // `#ERROR!` (malformed formula, wrong argument count) keeps its
1486
1610
  // message in `error`, as before; other errors store their code.
1487
- error: result.error === "#ERROR!" ? result.message : result.error,
1488
- errorMessage: result.message
1611
+ error: error === "#ERROR!" ? message : error,
1612
+ errorMessage: message
1489
1613
  };
1490
1614
  } finally {
1491
1615
  this.evaluatingRowIndex = null;
1616
+ this.evaluatingCell = null;
1617
+ }
1618
+ }
1619
+ // --- Dynamic arrays (spilling) ---
1620
+ /**
1621
+ * Spills `array` from the anchor at (row, col) into the cells below and to
1622
+ * the right. Returns why it can't (the anchor then shows `#SPILL!`), or
1623
+ * null once placed. The grid never grows to fit an array, as in Excel.
1624
+ */
1625
+ placeSpill(anchor, row, col, array) {
1626
+ const { rows, cols } = array;
1627
+ const area = { startRow: row, startCol: col, endRow: row + rows - 1, endCol: col + cols - 1 };
1628
+ const label = `${_chunkSAZRENEZcjs.indexToColName.call(void 0, area.startCol)}${area.startRow + 1}:${_chunkSAZRENEZcjs.indexToColName.call(void 0, area.endCol)}${area.endRow + 1}`;
1629
+ let reason = null;
1630
+ const blockers = [];
1631
+ if (area.endRow >= this.document.rows.length || area.endCol >= this.document.columns.length) {
1632
+ reason = `The ${rows}\xD7${cols} result needs ${label}, which extends past the edge of the sheet.`;
1633
+ } else if (this.getMerges().some((m) => rangesIntersect(m, area))) {
1634
+ reason = `The result can't spill into merged cells (it needs ${label}).`;
1635
+ } else {
1636
+ for (let c = area.startCol; c <= area.endCol; c++) {
1637
+ const column = this.document.columns[c];
1638
+ for (let r = area.startRow; r <= area.endRow; r++) {
1639
+ if (r === row && c === col) continue;
1640
+ const rowObj = this.document.rows[r];
1641
+ const key = `${column.key}${r + 1}`.toUpperCase();
1642
+ const stored = _optionalChain([this, 'access', _93 => _93.document, 'access', _94 => _94.cells, 'access', _95 => _95[rowObj.id], 'optionalAccess', _96 => _96[column.id]]);
1643
+ const raw = stored ? stored.raw : this.getDerivedCell(rowObj, column).raw;
1644
+ const owner = this.spilledBy.get(key);
1645
+ if (raw !== "") {
1646
+ blockers.push(key);
1647
+ reason ??= `The result needs ${label}, but ${key} isn't empty.`;
1648
+ } else if (owner && owner !== anchor) {
1649
+ blockers.push(key);
1650
+ reason ??= `The result needs ${label}, but ${key} already holds values spilled from ${owner}.`;
1651
+ }
1652
+ }
1653
+ }
1654
+ }
1655
+ if (reason) {
1656
+ this.clearSpill(anchor);
1657
+ this.depGraph.setSpillEdges(anchor, [], blockers);
1658
+ return reason;
1659
+ }
1660
+ const previous = this.spills.get(anchor);
1661
+ const cells = [];
1662
+ for (let c = 0; c < cols; c++) {
1663
+ const column = this.document.columns[col + c];
1664
+ for (let r = 0; r < rows; r++) {
1665
+ if (r === 0 && c === 0) continue;
1666
+ const rowObj = this.document.rows[row + r];
1667
+ const key = `${column.key}${row + r + 1}`.toUpperCase();
1668
+ const value = _nullishCoalesce(array.values[c * rows + r], () => ( ""));
1669
+ const display = { raw: "", value, format: column.format, spillAnchor: anchor };
1670
+ if (_chunkSAZRENEZcjs.isFormulaError.call(void 0, value)) {
1671
+ display.error = value;
1672
+ display.errorMessage = _chunkSAZRENEZcjs.describeFormulaError.call(void 0, value);
1673
+ }
1674
+ this.spillCells.set(spillCellId(rowObj.id, column.id), display);
1675
+ if (this.spilledBy.get(key) !== anchor) {
1676
+ this.spilledBy.set(key, anchor);
1677
+ _optionalChain([this, 'access', _97 => _97.spillChanges, 'optionalAccess', _98 => _98.add, 'call', _99 => _99(key)]);
1678
+ }
1679
+ cells.push({ key, rowId: rowObj.id, colId: column.id });
1680
+ }
1681
+ }
1682
+ if (previous) {
1683
+ const kept = new Set(cells.map((cell) => cell.key));
1684
+ for (const old of previous.cells) {
1685
+ if (kept.has(old.key)) continue;
1686
+ this.forgetSpilledCell(anchor, old);
1687
+ }
1492
1688
  }
1689
+ this.spills.set(anchor, { anchor, row, col, rows, cols, values: array.values, cells });
1690
+ this.depGraph.setSpillEdges(anchor, cells.map((cell) => cell.key));
1691
+ return null;
1692
+ }
1693
+ /** Removes the array spilled from `anchor`. With `keepEdges`, its dependency edges stay. */
1694
+ clearSpill(anchor, keepEdges = false) {
1695
+ const record = this.spills.get(anchor);
1696
+ if (record) {
1697
+ for (const cell of record.cells) this.forgetSpilledCell(anchor, cell);
1698
+ this.spills.delete(anchor);
1699
+ }
1700
+ if (!keepEdges) this.depGraph.setSpillEdges(anchor, []);
1701
+ }
1702
+ forgetSpilledCell(anchor, cell) {
1703
+ if (this.spilledBy.get(cell.key) !== anchor) return;
1704
+ this.spilledBy.delete(cell.key);
1705
+ this.spillCells.delete(spillCellId(cell.rowId, cell.colId));
1706
+ _optionalChain([this, 'access', _100 => _100.spillChanges, 'optionalAccess', _101 => _101.add, 'call', _102 => _102(cell.key)]);
1707
+ }
1708
+ resolveSpillByCoord(colStr, rowNum) {
1709
+ const record = this.spills.get(`${colStr}${rowNum}`.toUpperCase());
1710
+ if (record) return { rows: record.rows, cols: record.cols, values: record.values };
1711
+ const value = this.resolveCellByCoord(colStr, rowNum);
1712
+ return _chunkSAZRENEZcjs.isFormulaError.call(void 0, value) ? value : "#REF!";
1713
+ }
1714
+ toSpill(record) {
1715
+ const row = this.document.rows[record.row];
1716
+ const col = this.document.columns[record.col];
1717
+ return {
1718
+ key: record.anchor,
1719
+ anchor: { row: record.row, col: record.col },
1720
+ range: {
1721
+ startRow: record.row,
1722
+ startCol: record.col,
1723
+ endRow: record.row + record.rows - 1,
1724
+ endCol: record.col + record.cols - 1
1725
+ },
1726
+ formula: row && col ? this.getCell(row.id, col.id).raw : ""
1727
+ };
1728
+ }
1729
+ /**
1730
+ * The dynamic array covering (row, col), whether it is the anchor (the cell
1731
+ * holding the formula) or a spilled cell; null when there is none.
1732
+ */
1733
+ getSpillAt(row, col) {
1734
+ if (this.spills.size === 0) return null;
1735
+ const key = `${_chunkSAZRENEZcjs.indexToColName.call(void 0, col)}${row + 1}`;
1736
+ const record = _nullishCoalesce(this.spills.get(key), () => ( this.spills.get(_nullishCoalesce(this.spilledBy.get(key), () => ( "")))));
1737
+ return record ? this.toSpill(record) : null;
1738
+ }
1739
+ /** Every dynamic array currently spilled. */
1740
+ getSpills() {
1741
+ return Array.from(this.spills.values(), (record) => this.toSpill(record));
1742
+ }
1743
+ /** Whether `range` overlaps a spilled array (its anchor or spilled cells). */
1744
+ intersectsSpill(range) {
1745
+ for (const record of this.spills.values()) {
1746
+ const area = {
1747
+ startRow: record.row,
1748
+ startCol: record.col,
1749
+ endRow: record.row + record.rows - 1,
1750
+ endCol: record.col + record.cols - 1
1751
+ };
1752
+ if (rangesIntersect(area, range)) return true;
1753
+ }
1754
+ return false;
1493
1755
  }
1494
1756
  resolveCellByCoord(colStr, rowNum) {
1495
1757
  const col = this.document.columns.find((c) => c.key.toUpperCase() === colStr.toUpperCase());
@@ -1498,15 +1760,15 @@ var SpreadsheetController = (_class3 = class {
1498
1760
  return _nullishCoalesce(this.getCell(row.id, col.id).value, () => ( ""));
1499
1761
  }
1500
1762
  resolveRangeByCoords(sCol, sRow, eCol, eRow) {
1501
- const startC = _chunkJYRIOFENcjs.colNameToIndex.call(void 0, sCol);
1502
- const endC = _chunkJYRIOFENcjs.colNameToIndex.call(void 0, eCol);
1763
+ const startC = _chunkSAZRENEZcjs.colNameToIndex.call(void 0, sCol);
1764
+ const endC = _chunkSAZRENEZcjs.colNameToIndex.call(void 0, eCol);
1503
1765
  const minC = Math.min(startC, endC);
1504
1766
  const maxC = Math.max(startC, endC);
1505
1767
  const minR = Math.min(sRow, eRow);
1506
- const maxR = Math.min(Math.max(sRow, eRow), _chunkJYRIOFENcjs.WHOLE_COLUMN_END_ROW, this.document.rows.length);
1768
+ const maxR = Math.min(Math.max(sRow, eRow), _chunkSAZRENEZcjs.WHOLE_COLUMN_END_ROW, this.document.rows.length);
1507
1769
  const results = [];
1508
1770
  for (let c = minC; c <= maxC; c++) {
1509
- const colName = _chunkJYRIOFENcjs.indexToColName.call(void 0, c);
1771
+ const colName = _chunkSAZRENEZcjs.indexToColName.call(void 0, c);
1510
1772
  const col = this.document.columns.find((colDef) => colDef.key.toUpperCase() === colName);
1511
1773
  if (!col) continue;
1512
1774
  for (let r = minR; r <= maxR; r++) {
@@ -1518,7 +1780,7 @@ var SpreadsheetController = (_class3 = class {
1518
1780
  return results;
1519
1781
  }
1520
1782
  resolveCurrentRowProperty(prop) {
1521
- const rowIndex = _nullishCoalesce(this.evaluatingRowIndex, () => ( _optionalChain([this, 'access', _83 => _83.activeCell, 'optionalAccess', _84 => _84.row])));
1783
+ const rowIndex = _nullishCoalesce(this.evaluatingRowIndex, () => ( _optionalChain([this, 'access', _103 => _103.activeCell, 'optionalAccess', _104 => _104.row])));
1522
1784
  if (rowIndex === void 0 || rowIndex === null) return 0;
1523
1785
  const row = this.document.rows[rowIndex];
1524
1786
  if (!row || !row.recordId || !this.primaryDataSourceId) return 0;
@@ -1536,8 +1798,8 @@ var SpreadsheetController = (_class3 = class {
1536
1798
  this.activeCell = {
1537
1799
  row: activeRow,
1538
1800
  col: activeCol,
1539
- rowId: _optionalChain([rowObj, 'optionalAccess', _85 => _85.id]),
1540
- colId: _optionalChain([colObj, 'optionalAccess', _86 => _86.id])
1801
+ rowId: _optionalChain([rowObj, 'optionalAccess', _105 => _105.id]),
1802
+ colId: _optionalChain([colObj, 'optionalAccess', _106 => _106.id])
1541
1803
  };
1542
1804
  if (extendSelection && this.selectedRange) {
1543
1805
  this.selectedRange = this.expandRangeToMerges({
@@ -1556,16 +1818,16 @@ var SpreadsheetController = (_class3 = class {
1556
1818
  endCol: clampedCol
1557
1819
  };
1558
1820
  }
1559
- _optionalChain([this, 'access', _87 => _87.onSelectionChange, 'optionalCall', _88 => _88(this.activeCell, this.selectedRange)]);
1821
+ _optionalChain([this, 'access', _107 => _107.onSelectionChange, 'optionalCall', _108 => _108(this.activeCell, this.selectedRange)]);
1560
1822
  }
1561
1823
  selectRange(range) {
1562
1824
  this.selectedRange = range;
1563
- _optionalChain([this, 'access', _89 => _89.onSelectionChange, 'optionalCall', _90 => _90(this.activeCell, this.selectedRange)]);
1825
+ _optionalChain([this, 'access', _109 => _109.onSelectionChange, 'optionalCall', _110 => _110(this.activeCell, this.selectedRange)]);
1564
1826
  }
1565
1827
  startEditing(row, col, initialValue) {
1566
- this.selectCell(_nullishCoalesce(_nullishCoalesce(row, () => ( _optionalChain([this, 'access', _91 => _91.activeCell, 'optionalAccess', _92 => _92.row]))), () => ( 0)), _nullishCoalesce(_nullishCoalesce(col, () => ( _optionalChain([this, 'access', _93 => _93.activeCell, 'optionalAccess', _94 => _94.col]))), () => ( 0)));
1567
- const targetRow = _nullishCoalesce(_optionalChain([this, 'access', _95 => _95.activeCell, 'optionalAccess', _96 => _96.row]), () => ( 0));
1568
- const targetCol = _nullishCoalesce(_optionalChain([this, 'access', _97 => _97.activeCell, 'optionalAccess', _98 => _98.col]), () => ( 0));
1828
+ this.selectCell(_nullishCoalesce(_nullishCoalesce(row, () => ( _optionalChain([this, 'access', _111 => _111.activeCell, 'optionalAccess', _112 => _112.row]))), () => ( 0)), _nullishCoalesce(_nullishCoalesce(col, () => ( _optionalChain([this, 'access', _113 => _113.activeCell, 'optionalAccess', _114 => _114.col]))), () => ( 0)));
1829
+ const targetRow = _nullishCoalesce(_optionalChain([this, 'access', _115 => _115.activeCell, 'optionalAccess', _116 => _116.row]), () => ( 0));
1830
+ const targetCol = _nullishCoalesce(_optionalChain([this, 'access', _117 => _117.activeCell, 'optionalAccess', _118 => _118.col]), () => ( 0));
1569
1831
  const rowObj = this.document.rows[targetRow];
1570
1832
  const colObj = this.document.columns[targetCol];
1571
1833
  if (!rowObj || !colObj) return;
@@ -1619,17 +1881,17 @@ var SpreadsheetController = (_class3 = class {
1619
1881
  // --- Grid Structure Mutations ---
1620
1882
  insertColumn(index, config) {
1621
1883
  this.recordSnapshot();
1622
- const newId = _optionalChain([config, 'optionalAccess', _99 => _99.id]) || this.generateId("col");
1884
+ const newId = _optionalChain([config, 'optionalAccess', _119 => _119.id]) || this.generateId("col");
1623
1885
  const newCol = {
1624
1886
  id: newId,
1625
- key: _chunkJYRIOFENcjs.indexToColName.call(void 0, index),
1626
- title: _optionalChain([config, 'optionalAccess', _100 => _100.title]) || _chunkJYRIOFENcjs.indexToColName.call(void 0, index),
1627
- width: _optionalChain([config, 'optionalAccess', _101 => _101.width]) || 130,
1628
- type: _optionalChain([config, 'optionalAccess', _102 => _102.type]) || "freeform",
1629
- binding: _optionalChain([config, 'optionalAccess', _103 => _103.binding]),
1630
- formula: _optionalChain([config, 'optionalAccess', _104 => _104.formula]),
1631
- format: _optionalChain([config, 'optionalAccess', _105 => _105.format]) || "text",
1632
- readOnly: _optionalChain([config, 'optionalAccess', _106 => _106.readOnly])
1887
+ key: _chunkSAZRENEZcjs.indexToColName.call(void 0, index),
1888
+ title: _optionalChain([config, 'optionalAccess', _120 => _120.title]) || _chunkSAZRENEZcjs.indexToColName.call(void 0, index),
1889
+ width: _optionalChain([config, 'optionalAccess', _121 => _121.width]) || 130,
1890
+ type: _optionalChain([config, 'optionalAccess', _122 => _122.type]) || "freeform",
1891
+ binding: _optionalChain([config, 'optionalAccess', _123 => _123.binding]),
1892
+ formula: _optionalChain([config, 'optionalAccess', _124 => _124.formula]),
1893
+ format: _optionalChain([config, 'optionalAccess', _125 => _125.format]) || "text",
1894
+ readOnly: _optionalChain([config, 'optionalAccess', _126 => _126.readOnly])
1633
1895
  };
1634
1896
  this.shiftReferences({ axis: "col", index, type: "insert" });
1635
1897
  this.adjustTableForStructuralChange({ axis: "col", index, type: "insert" });
@@ -1654,13 +1916,13 @@ var SpreadsheetController = (_class3 = class {
1654
1916
  }
1655
1917
  insertRow(index, config) {
1656
1918
  this.recordSnapshot();
1657
- const newId = _optionalChain([config, 'optionalAccess', _107 => _107.id]) || this.generateId("row");
1919
+ const newId = _optionalChain([config, 'optionalAccess', _127 => _127.id]) || this.generateId("row");
1658
1920
  const newRow = {
1659
1921
  id: newId,
1660
1922
  index,
1661
- height: _optionalChain([config, 'optionalAccess', _108 => _108.height]) || 32,
1662
- recordId: _optionalChain([config, 'optionalAccess', _109 => _109.recordId]),
1663
- type: _optionalChain([config, 'optionalAccess', _110 => _110.type]) || "freeform"
1923
+ height: _optionalChain([config, 'optionalAccess', _128 => _128.height]) || 32,
1924
+ recordId: _optionalChain([config, 'optionalAccess', _129 => _129.recordId]),
1925
+ type: _optionalChain([config, 'optionalAccess', _130 => _130.type]) || "freeform"
1664
1926
  };
1665
1927
  this.shiftReferences({ axis: "row", index, type: "insert" });
1666
1928
  this.adjustTableForStructuralChange({ axis: "row", index, type: "insert" });
@@ -1691,14 +1953,14 @@ var SpreadsheetController = (_class3 = class {
1691
1953
  shiftReferences(change) {
1692
1954
  for (const rowCells of Object.values(this.document.cells)) {
1693
1955
  for (const cell of Object.values(rowCells)) {
1694
- if (_optionalChain([cell, 'access', _111 => _111.raw, 'optionalAccess', _112 => _112.startsWith, 'call', _113 => _113("=")])) {
1956
+ if (_optionalChain([cell, 'access', _131 => _131.raw, 'optionalAccess', _132 => _132.startsWith, 'call', _133 => _133("=")])) {
1695
1957
  cell.raw = shiftFormulaReferences(cell.raw, change);
1696
1958
  }
1697
1959
  }
1698
1960
  }
1699
1961
  if (change.axis === "col") {
1700
1962
  for (const col of this.document.columns) {
1701
- if (_optionalChain([col, 'access', _114 => _114.formula, 'optionalAccess', _115 => _115.startsWith, 'call', _116 => _116("=")])) {
1963
+ if (_optionalChain([col, 'access', _134 => _134.formula, 'optionalAccess', _135 => _135.startsWith, 'call', _136 => _136("=")])) {
1702
1964
  col.formula = shiftFormulaReferences(col.formula, change);
1703
1965
  }
1704
1966
  }
@@ -1766,7 +2028,7 @@ var SpreadsheetController = (_class3 = class {
1766
2028
  this.document.rows.push({ id: this.generateId("row"), index, height: 32, type: "freeform" });
1767
2029
  }
1768
2030
  while (colIndex >= this.document.columns.length) {
1769
- const key = _chunkJYRIOFENcjs.indexToColName.call(void 0, this.document.columns.length);
2031
+ const key = _chunkSAZRENEZcjs.indexToColName.call(void 0, this.document.columns.length);
1770
2032
  this.document.columns.push({
1771
2033
  id: this.generateId("col"),
1772
2034
  key,
@@ -1813,7 +2075,7 @@ var SpreadsheetController = (_class3 = class {
1813
2075
  const commits = this.pendingCommits;
1814
2076
  this.pendingCommits = [];
1815
2077
  for (const [rowId, colId, raw] of commits) {
1816
- _optionalChain([this, 'access', _117 => _117.onCellCommit, 'optionalCall', _118 => _118(rowId, colId, raw, _optionalChain([this, 'access', _119 => _119.document, 'access', _120 => _120.cells, 'access', _121 => _121[rowId], 'optionalAccess', _122 => _122[colId], 'optionalAccess', _123 => _123.value]))]);
2078
+ _optionalChain([this, 'access', _137 => _137.onCellCommit, 'optionalCall', _138 => _138(rowId, colId, raw, _optionalChain([this, 'access', _139 => _139.document, 'access', _140 => _140.cells, 'access', _141 => _141[rowId], 'optionalAccess', _142 => _142[colId], 'optionalAccess', _143 => _143.value]))]);
1817
2079
  }
1818
2080
  }
1819
2081
  // --- CSV ---
@@ -1861,7 +2123,15 @@ var SpreadsheetController = (_class3 = class {
1861
2123
  */
1862
2124
  copyToClipboardData(range = this.selectedRange) {
1863
2125
  const r = range ? normalizeRange(range) : { startRow: 0, startCol: 0, endRow: this.document.rows.length - 1, endCol: this.document.columns.length - 1 };
1864
- const cells = this.readMatrix(r).map((row) => row.map((cell) => ({ raw: cell.raw, value: cell.error || cell.value })));
2126
+ const cells = this.readMatrix(r).map(
2127
+ (row) => row.map((cell) => {
2128
+ const value = cell.error || cell.value;
2129
+ if (cell.spillAnchor && !this.spillAnchorInRange(cell.spillAnchor, r)) {
2130
+ return { raw: value === null || value === void 0 ? "" : String(value), value };
2131
+ }
2132
+ return { raw: cell.raw, value };
2133
+ })
2134
+ );
1865
2135
  return createSpreadsheetClipboardData(cells, { row: r.startRow, col: r.startCol });
1866
2136
  }
1867
2137
  /**
@@ -1884,12 +2154,16 @@ var SpreadsheetController = (_class3 = class {
1884
2154
  this.selectRange(r);
1885
2155
  return true;
1886
2156
  }
2157
+ spillAnchorInRange(anchor, range) {
2158
+ const record = this.spills.get(anchor);
2159
+ return !!record && rangeContains(range, record.row, record.col);
2160
+ }
1887
2161
  getSelectionTopLeft() {
1888
2162
  if (this.selectedRange) {
1889
2163
  const r = normalizeRange(this.selectedRange);
1890
2164
  return { row: r.startRow, col: r.startCol };
1891
2165
  }
1892
- return { row: _nullishCoalesce(_optionalChain([this, 'access', _124 => _124.activeCell, 'optionalAccess', _125 => _125.row]), () => ( 0)), col: _nullishCoalesce(_optionalChain([this, 'access', _126 => _126.activeCell, 'optionalAccess', _127 => _127.col]), () => ( 0)) };
2166
+ return { row: _nullishCoalesce(_optionalChain([this, 'access', _144 => _144.activeCell, 'optionalAccess', _145 => _145.row]), () => ( 0)), col: _nullishCoalesce(_optionalChain([this, 'access', _146 => _146.activeCell, 'optionalAccess', _147 => _147.col]), () => ( 0)) };
1893
2167
  }
1894
2168
  // --- Table operations: shared helpers ---
1895
2169
  /** The table settings object, created on first use. */
@@ -1913,7 +2187,7 @@ var SpreadsheetController = (_class3 = class {
1913
2187
  const { headerRows, footerRows } = getTableSections(this.document);
1914
2188
  return { start: headerRows, end: this.document.rows.length - footerRows - 1 };
1915
2189
  }
1916
- __init16() {this.getCellValueAt = (rowIndex, colIndex) => {
2190
+ __init24() {this.getCellValueAt = (rowIndex, colIndex) => {
1917
2191
  const row = this.document.rows[rowIndex];
1918
2192
  const col = this.document.columns[colIndex];
1919
2193
  if (!row || !col) return "";
@@ -1923,7 +2197,7 @@ var SpreadsheetController = (_class3 = class {
1923
2197
  remapAllFormulas(moveCell) {
1924
2198
  for (const rowCells of Object.values(this.document.cells)) {
1925
2199
  for (const cell of Object.values(rowCells)) {
1926
- if (_optionalChain([cell, 'access', _128 => _128.raw, 'optionalAccess', _129 => _129.startsWith, 'call', _130 => _130("=")])) {
2200
+ if (_optionalChain([cell, 'access', _148 => _148.raw, 'optionalAccess', _149 => _149.startsWith, 'call', _150 => _150("=")])) {
1927
2201
  cell.raw = remapFormulaReferences(cell.raw, moveCell);
1928
2202
  }
1929
2203
  }
@@ -1935,8 +2209,8 @@ var SpreadsheetController = (_class3 = class {
1935
2209
  * merges reduced to a single cell are dropped.
1936
2210
  */
1937
2211
  adjustTableForStructuralChange(change) {
1938
- const merges = _optionalChain([this, 'access', _131 => _131.document, 'access', _132 => _132.table, 'optionalAccess', _133 => _133.merges]);
1939
- if (!_optionalChain([merges, 'optionalAccess', _134 => _134.length])) return;
2212
+ const merges = _optionalChain([this, 'access', _151 => _151.document, 'access', _152 => _152.table, 'optionalAccess', _153 => _153.merges]);
2213
+ if (!_optionalChain([merges, 'optionalAccess', _154 => _154.length])) return;
1940
2214
  const startKey = change.axis === "row" ? "startRow" : "startCol";
1941
2215
  const endKey = change.axis === "row" ? "endRow" : "endCol";
1942
2216
  const next = [];
@@ -1967,8 +2241,8 @@ var SpreadsheetController = (_class3 = class {
1967
2241
  const colCount = this.document.columns.length;
1968
2242
  const body = this.getBodyRowBounds();
1969
2243
  const range = options.range ? normalizeRange(options.range) : null;
1970
- const startRow = Math.max(_nullishCoalesce(_optionalChain([range, 'optionalAccess', _135 => _135.startRow]), () => ( body.start)), body.start);
1971
- const endRow = Math.min(_nullishCoalesce(_optionalChain([range, 'optionalAccess', _136 => _136.endRow]), () => ( body.end)), body.end);
2244
+ const startRow = Math.max(_nullishCoalesce(_optionalChain([range, 'optionalAccess', _155 => _155.startRow]), () => ( body.start)), body.start);
2245
+ const endRow = Math.min(_nullishCoalesce(_optionalChain([range, 'optionalAccess', _156 => _156.endRow]), () => ( body.end)), body.end);
1972
2246
  const startCol = range ? Math.max(0, range.startCol) : 0;
1973
2247
  const endCol = range ? Math.min(colCount - 1, range.endCol) : colCount - 1;
1974
2248
  if (endRow - startRow < 1 || startCol > endCol) return false;
@@ -1978,7 +2252,8 @@ var SpreadsheetController = (_class3 = class {
1978
2252
  }));
1979
2253
  if (keyCols.length === 0 || keyCols.some((k) => k.index < startCol || k.index > endCol)) return false;
1980
2254
  const area = { startRow, startCol, endRow, endCol };
1981
- if ((_nullishCoalesce(_optionalChain([this, 'access', _137 => _137.document, 'access', _138 => _138.table, 'optionalAccess', _139 => _139.merges]), () => ( []))).some((m) => rangesIntersect(m, area))) return false;
2255
+ if ((_nullishCoalesce(_optionalChain([this, 'access', _157 => _157.document, 'access', _158 => _158.table, 'optionalAccess', _159 => _159.merges]), () => ( []))).some((m) => rangesIntersect(m, area))) return false;
2256
+ if (this.intersectsSpill(area)) return false;
1982
2257
  const sourceRows = [];
1983
2258
  for (let r = startRow; r <= endRow; r++) sourceRows.push(r);
1984
2259
  const sorted = sourceRows.map((r) => ({ r, values: keyCols.map((k) => this.getCellValueAt(r, k.index)) })).sort((a, b) => {
@@ -2005,7 +2280,7 @@ var SpreadsheetController = (_class3 = class {
2005
2280
  const colIds = this.document.columns.slice(startCol, endCol + 1).map((c) => c.id);
2006
2281
  const snapshot = sorted.map((oldRow) => {
2007
2282
  const rowId = this.document.rows[oldRow].id;
2008
- return colIds.map((colId) => _optionalChain([this, 'access', _140 => _140.document, 'access', _141 => _141.cells, 'access', _142 => _142[rowId], 'optionalAccess', _143 => _143[colId]]));
2283
+ return colIds.map((colId) => _optionalChain([this, 'access', _160 => _160.document, 'access', _161 => _161.cells, 'access', _162 => _162[rowId], 'optionalAccess', _163 => _163[colId]]));
2009
2284
  });
2010
2285
  snapshot.forEach((cells, i) => {
2011
2286
  const rowId = this.document.rows[startRow + i].id;
@@ -2021,7 +2296,7 @@ var SpreadsheetController = (_class3 = class {
2021
2296
  }
2022
2297
  // --- Filters ---
2023
2298
  getFilterState() {
2024
- return _nullishCoalesce(_optionalChain([this, 'access', _144 => _144.document, 'access', _145 => _145.table, 'optionalAccess', _146 => _146.filter]), () => ( { enabled: false, rules: [] }));
2299
+ return _nullishCoalesce(_optionalChain([this, 'access', _164 => _164.document, 'access', _165 => _165.table, 'optionalAccess', _166 => _166.filter]), () => ( { enabled: false, rules: [] }));
2025
2300
  }
2026
2301
  /** Replaces all filter rules (and turns filters on when there are any). */
2027
2302
  setFilterRules(rules) {
@@ -2098,7 +2373,7 @@ var SpreadsheetController = (_class3 = class {
2098
2373
  }
2099
2374
  setTableCount(key, count, max) {
2100
2375
  const value = Math.max(0, Math.min(Math.floor(count), max));
2101
- if ((_nullishCoalesce(_optionalChain([this, 'access', _147 => _147.document, 'access', _148 => _148.table, 'optionalAccess', _149 => _149[key]]), () => ( 0))) === value) return;
2376
+ if ((_nullishCoalesce(_optionalChain([this, 'access', _167 => _167.document, 'access', _168 => _168.table, 'optionalAccess', _169 => _169[key]]), () => ( 0))) === value) return;
2102
2377
  this.applyTableChange(() => {
2103
2378
  this.table[key] = value;
2104
2379
  });
@@ -2139,7 +2414,7 @@ var SpreadsheetController = (_class3 = class {
2139
2414
  }
2140
2415
  // --- Merged cells ---
2141
2416
  getMerges() {
2142
- return _nullishCoalesce(_optionalChain([this, 'access', _150 => _150.document, 'access', _151 => _151.table, 'optionalAccess', _152 => _152.merges]), () => ( []));
2417
+ return _nullishCoalesce(_optionalChain([this, 'access', _170 => _170.document, 'access', _171 => _171.table, 'optionalAccess', _172 => _172.merges]), () => ( []));
2143
2418
  }
2144
2419
  /** The merge containing (row, col), or null. */
2145
2420
  getMergeAt(row, col) {
@@ -2186,12 +2461,13 @@ var SpreadsheetController = (_class3 = class {
2186
2461
  }
2187
2462
  const merges = this.getMerges();
2188
2463
  if (merges.some((m) => rangesIntersect(m, r) && !rangeContainsRange(r, m))) return false;
2464
+ if (this.intersectsSpill(r)) return false;
2189
2465
  this.applyTableChange(() => {
2190
2466
  for (let row = r.startRow; row <= r.endRow; row++) {
2191
2467
  const rowId = this.document.rows[row].id;
2192
2468
  for (let col = r.startCol; col <= r.endCol; col++) {
2193
2469
  if (row === r.startRow && col === r.startCol) continue;
2194
- _optionalChainDelete([this, 'access', _153 => _153.document, 'access', _154 => _154.cells, 'access', _155 => _155[rowId], 'optionalAccess', _156 => _156[this.document.columns[col].id]]);
2470
+ _optionalChainDelete([this, 'access', _173 => _173.document, 'access', _174 => _174.cells, 'access', _175 => _175[rowId], 'optionalAccess', _176 => _176[this.document.columns[col].id]]);
2195
2471
  }
2196
2472
  }
2197
2473
  this.table.merges = [...merges.filter((m) => !rangeContainsRange(r, m)), r];
@@ -2207,7 +2483,7 @@ var SpreadsheetController = (_class3 = class {
2207
2483
  if (remaining.length === merges.length) return false;
2208
2484
  this.applyTableChange(() => {
2209
2485
  this.table.merges = remaining;
2210
- });
2486
+ }, { recalculate: true });
2211
2487
  return true;
2212
2488
  }
2213
2489
  // --- Autofill ---
@@ -2277,14 +2553,14 @@ var SpreadsheetController = (_class3 = class {
2277
2553
  });
2278
2554
  }
2279
2555
  ungroupRows() {
2280
- if (!_optionalChain([this, 'access', _157 => _157.document, 'access', _158 => _158.table, 'optionalAccess', _159 => _159.grouping])) return;
2556
+ if (!_optionalChain([this, 'access', _177 => _177.document, 'access', _178 => _178.table, 'optionalAccess', _179 => _179.grouping])) return;
2281
2557
  this.applyTableChange(() => {
2282
2558
  delete this.table.grouping;
2283
2559
  });
2284
2560
  }
2285
2561
  /** Sets (or with `null`, removes) the group summary for a column. */
2286
2562
  setGroupSummary(colId, aggregate) {
2287
- const grouping = _optionalChain([this, 'access', _160 => _160.document, 'access', _161 => _161.table, 'optionalAccess', _162 => _162.grouping]);
2563
+ const grouping = _optionalChain([this, 'access', _180 => _180.document, 'access', _181 => _181.table, 'optionalAccess', _182 => _182.grouping]);
2288
2564
  if (!grouping) return;
2289
2565
  this.applyTableChange(() => {
2290
2566
  const others = grouping.summaries.filter((s) => s.colId !== colId);
@@ -2295,7 +2571,7 @@ var SpreadsheetController = (_class3 = class {
2295
2571
  });
2296
2572
  }
2297
2573
  setGroupCollapsed(key, collapsed) {
2298
- const grouping = _optionalChain([this, 'access', _163 => _163.document, 'access', _164 => _164.table, 'optionalAccess', _165 => _165.grouping]);
2574
+ const grouping = _optionalChain([this, 'access', _183 => _183.document, 'access', _184 => _184.table, 'optionalAccess', _185 => _185.grouping]);
2299
2575
  if (!grouping || grouping.collapsed.includes(key) === collapsed) return;
2300
2576
  this.applyTableChange(() => {
2301
2577
  this.table.grouping = {
@@ -2305,7 +2581,7 @@ var SpreadsheetController = (_class3 = class {
2305
2581
  });
2306
2582
  }
2307
2583
  toggleGroupCollapsed(key) {
2308
- const grouping = _optionalChain([this, 'access', _166 => _166.document, 'access', _167 => _167.table, 'optionalAccess', _168 => _168.grouping]);
2584
+ const grouping = _optionalChain([this, 'access', _186 => _186.document, 'access', _187 => _187.table, 'optionalAccess', _188 => _188.grouping]);
2309
2585
  if (!grouping) return;
2310
2586
  this.setGroupCollapsed(key, !grouping.collapsed.includes(key));
2311
2587
  }
@@ -2328,6 +2604,7 @@ var SpreadsheetController = (_class3 = class {
2328
2604
  const width = s.endCol - s.startCol + 1;
2329
2605
  const t = { startRow: s.startRow, startCol: s.startCol, endRow: s.startRow + width - 1, endCol: s.startCol + height - 1 };
2330
2606
  if (this.getMerges().some((m) => rangesIntersect(m, s) || rangesIntersect(m, t))) return false;
2607
+ if (this.intersectsSpill(s) || this.intersectsSpill(t)) return false;
2331
2608
  this.applyTableChange(() => {
2332
2609
  this.growGrid(t.endRow, t.endCol);
2333
2610
  this.remapAllFormulas(({ row, col }) => {
@@ -2342,9 +2619,9 @@ var SpreadsheetController = (_class3 = class {
2342
2619
  moved.push({
2343
2620
  row: s.startRow + (c - s.startCol),
2344
2621
  col: s.startCol + (r - s.startRow),
2345
- cell: _optionalChain([this, 'access', _169 => _169.document, 'access', _170 => _170.cells, 'access', _171 => _171[rowId(r)], 'optionalAccess', _172 => _172[colId(c)]])
2622
+ cell: _optionalChain([this, 'access', _189 => _189.document, 'access', _190 => _190.cells, 'access', _191 => _191[rowId(r)], 'optionalAccess', _192 => _192[colId(c)]])
2346
2623
  });
2347
- _optionalChainDelete([this, 'access', _173 => _173.document, 'access', _174 => _174.cells, 'access', _175 => _175[rowId(r)], 'optionalAccess', _176 => _176[colId(c)]]);
2624
+ _optionalChainDelete([this, 'access', _193 => _193.document, 'access', _194 => _194.cells, 'access', _195 => _195[rowId(r)], 'optionalAccess', _196 => _196[colId(c)]]);
2348
2625
  }
2349
2626
  }
2350
2627
  for (const { row, col, cell } of moved) {
@@ -2397,7 +2674,7 @@ var SpreadsheetController = (_class3 = class {
2397
2674
  }
2398
2675
  }
2399
2676
  notifyChange() {
2400
- _optionalChain([this, 'access', _177 => _177.onDocumentChange, 'optionalCall', _178 => _178(this.document)]);
2677
+ _optionalChain([this, 'access', _197 => _197.onDocumentChange, 'optionalCall', _198 => _198(this.document)]);
2401
2678
  }
2402
2679
  }, _class3);
2403
2680
  function stepInOrder(order, from, delta) {
@@ -2412,6 +2689,17 @@ function stepInOrder(order, from, delta) {
2412
2689
  }
2413
2690
 
2414
2691
  // src/spreadsheet/menu.ts
2692
+ function clampMenuPosition(point, size, viewport, margin = 4) {
2693
+ const place = (start, length, limit) => {
2694
+ if (start + length <= limit - margin) return Math.max(margin, start);
2695
+ const flipped = start - length;
2696
+ return Math.max(margin, flipped >= margin ? flipped : limit - margin - length);
2697
+ };
2698
+ return {
2699
+ x: place(point.x, size.width, viewport.width),
2700
+ y: place(point.y, size.height, viewport.height)
2701
+ };
2702
+ }
2415
2703
  var quote = (value) => {
2416
2704
  const text = String(_nullishCoalesce(value, () => ( "")));
2417
2705
  return text.length > 24 ? `"${text.slice(0, 23)}\u2026"` : `"${text}"`;
@@ -2422,7 +2710,7 @@ function getSpreadsheetMenuItems(controller, cell) {
2422
2710
  const row = doc.rows[cell.row];
2423
2711
  if (!column || !row) return [];
2424
2712
  const selection = controller.selectedRange && rangeContains(controller.selectedRange, cell.row, cell.col) ? normalizeRange(controller.selectedRange) : { startRow: cell.row, startCol: cell.col, endRow: cell.row, endCol: cell.col };
2425
- const colName = _chunkJYRIOFENcjs.indexToColName.call(void 0, cell.col);
2713
+ const colName = _chunkSAZRENEZcjs.indexToColName.call(void 0, cell.col);
2426
2714
  const value = controller.getCell(row.id, column.id).value;
2427
2715
  const table = _nullishCoalesce(doc.table, () => ( {}));
2428
2716
  const filter = controller.getFilterState();
@@ -2702,5 +2990,6 @@ async function importFileIntoSpreadsheet(controller, input, options) {
2702
2990
 
2703
2991
 
2704
2992
 
2705
- exports.columnDependencyKey = columnDependencyKey; exports.extractCellDependencies = extractCellDependencies; exports.DependencyGraph = DependencyGraph; exports.shiftFormulaReferences = shiftFormulaReferences; exports.mapFormulaReferences = mapFormulaReferences; exports.offsetFormulaReferences = offsetFormulaReferences; exports.remapFormulaReferences = remapFormulaReferences; exports.getFormulaCaretContext = getFormulaCaretContext; exports.getFormulaHints = getFormulaHints; exports.applyFormulaSuggestion = applyFormulaSuggestion; exports.DataSourceBinder = DataSourceBinder; exports.normalizeRange = normalizeRange; exports.rangeContains = rangeContains; exports.rangesIntersect = rangesIntersect; exports.rangeContainsRange = rangeContainsRange; exports.isSingleCellRange = isSingleCellRange; exports.isBlankValue = isBlankValue; exports.toNumber = toNumber; exports.compareCellValues = compareCellValues; exports.matchesFilterRule = matchesFilterRule; exports.aggregateValues = aggregateValues; exports.roundFloat = roundFloat; exports.fillSeries = fillSeries; exports.computeFillTarget = computeFillTarget; exports.getFillDirection = getFillDirection; exports.parseCsv = parseCsv; exports.escapeCsvField = escapeCsvField; exports.serializeCsv = serializeCsv; exports.parseTsv = parseTsv; exports.serializeTsv = serializeTsv; exports.SPREADSHEET_CLIPBOARD_MIME = SPREADSHEET_CLIPBOARD_MIME; exports.serializeHtmlTable = serializeHtmlTable; exports.parseHtmlTable = parseHtmlTable; exports.createSpreadsheetClipboardData = createSpreadsheetClipboardData; exports.readSpreadsheetClipboard = readSpreadsheetClipboard; exports.rebaseClipboardRows = rebaseClipboardRows; exports.DEFAULT_ROW_HEIGHT = DEFAULT_ROW_HEIGHT; exports.DEFAULT_COLUMN_WIDTH = DEFAULT_COLUMN_WIDTH; exports.getTableSections = getTableSections; exports.getRowSection = getRowSection; exports.rowPassesFilters = rowPassesFilters; exports.computeRowGroups = computeRowGroups; exports.buildSpreadsheetView = buildSpreadsheetView; exports.SpreadsheetController = SpreadsheetController; exports.getSpreadsheetMenuItems = getSpreadsheetMenuItems; exports.SPREADSHEET_TABLE_ACTIONS = SPREADSHEET_TABLE_ACTIONS; exports.bindSpreadsheetTableActions = bindSpreadsheetTableActions; exports.csvImportAdapter = csvImportAdapter; exports.tsvImportAdapter = tsvImportAdapter; exports.SPREADSHEET_IMPORT_ADAPTERS = SPREADSHEET_IMPORT_ADAPTERS; exports.registerSpreadsheetImportAdapters = registerSpreadsheetImportAdapters; exports.importFileIntoSpreadsheet = importFileIntoSpreadsheet;
2706
- //# sourceMappingURL=chunk-U6NDQFOK.cjs.map
2993
+
2994
+ exports.columnDependencyKey = columnDependencyKey; exports.extractCellDependencies = extractCellDependencies; exports.DependencyGraph = DependencyGraph; exports.shiftFormulaReferences = shiftFormulaReferences; exports.mapFormulaReferences = mapFormulaReferences; exports.offsetFormulaReferences = offsetFormulaReferences; exports.remapFormulaReferences = remapFormulaReferences; exports.getFormulaCaretContext = getFormulaCaretContext; exports.getFormulaHints = getFormulaHints; exports.applyFormulaSuggestion = applyFormulaSuggestion; exports.DataSourceBinder = DataSourceBinder; exports.normalizeRange = normalizeRange; exports.rangeContains = rangeContains; exports.rangesIntersect = rangesIntersect; exports.rangeContainsRange = rangeContainsRange; exports.isSingleCellRange = isSingleCellRange; exports.isBlankValue = isBlankValue; exports.toNumber = toNumber; exports.compareCellValues = compareCellValues; exports.matchesFilterRule = matchesFilterRule; exports.aggregateValues = aggregateValues; exports.roundFloat = roundFloat; exports.fillSeries = fillSeries; exports.computeFillTarget = computeFillTarget; exports.getFillDirection = getFillDirection; exports.parseCsv = parseCsv; exports.escapeCsvField = escapeCsvField; exports.serializeCsv = serializeCsv; exports.parseTsv = parseTsv; exports.serializeTsv = serializeTsv; exports.SPREADSHEET_CLIPBOARD_MIME = SPREADSHEET_CLIPBOARD_MIME; exports.serializeHtmlTable = serializeHtmlTable; exports.parseHtmlTable = parseHtmlTable; exports.createSpreadsheetClipboardData = createSpreadsheetClipboardData; exports.readSpreadsheetClipboard = readSpreadsheetClipboard; exports.rebaseClipboardRows = rebaseClipboardRows; exports.DEFAULT_ROW_HEIGHT = DEFAULT_ROW_HEIGHT; exports.DEFAULT_COLUMN_WIDTH = DEFAULT_COLUMN_WIDTH; exports.getTableSections = getTableSections; exports.getRowSection = getRowSection; exports.rowPassesFilters = rowPassesFilters; exports.computeRowGroups = computeRowGroups; exports.buildSpreadsheetView = buildSpreadsheetView; exports.SpreadsheetController = SpreadsheetController; exports.clampMenuPosition = clampMenuPosition; exports.getSpreadsheetMenuItems = getSpreadsheetMenuItems; exports.SPREADSHEET_TABLE_ACTIONS = SPREADSHEET_TABLE_ACTIONS; exports.bindSpreadsheetTableActions = bindSpreadsheetTableActions; exports.csvImportAdapter = csvImportAdapter; exports.tsvImportAdapter = tsvImportAdapter; exports.SPREADSHEET_IMPORT_ADAPTERS = SPREADSHEET_IMPORT_ADAPTERS; exports.registerSpreadsheetImportAdapters = registerSpreadsheetImportAdapters; exports.importFileIntoSpreadsheet = importFileIntoSpreadsheet;
2995
+ //# sourceMappingURL=chunk-6M3BCTBD.cjs.map