@ricsam/formula-engine 0.2.11 → 0.2.13

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 (36) hide show
  1. package/dist/cjs/core/autofill-utils.cjs +68 -2
  2. package/dist/cjs/core/autofill-utils.cjs.map +3 -3
  3. package/dist/cjs/core/engine-snapshot.cjs +2 -2
  4. package/dist/cjs/core/engine-snapshot.cjs.map +3 -3
  5. package/dist/cjs/core/engine.cjs +47 -3
  6. package/dist/cjs/core/engine.cjs.map +3 -3
  7. package/dist/cjs/core/managers/copy-manager.cjs +163 -14
  8. package/dist/cjs/core/managers/copy-manager.cjs.map +3 -3
  9. package/dist/cjs/core/managers/range-metadata-manager.cjs +135 -0
  10. package/dist/cjs/core/managers/range-metadata-manager.cjs.map +10 -0
  11. package/dist/cjs/core/managers/style-manager.cjs +19 -14
  12. package/dist/cjs/core/managers/style-manager.cjs.map +3 -3
  13. package/dist/cjs/core/types.cjs.map +1 -1
  14. package/dist/cjs/package.json +1 -1
  15. package/dist/mjs/core/autofill-utils.mjs +68 -2
  16. package/dist/mjs/core/autofill-utils.mjs.map +3 -3
  17. package/dist/mjs/core/engine-snapshot.mjs +2 -2
  18. package/dist/mjs/core/engine-snapshot.mjs.map +3 -3
  19. package/dist/mjs/core/engine.mjs +47 -3
  20. package/dist/mjs/core/engine.mjs.map +3 -3
  21. package/dist/mjs/core/managers/copy-manager.mjs +163 -14
  22. package/dist/mjs/core/managers/copy-manager.mjs.map +3 -3
  23. package/dist/mjs/core/managers/range-metadata-manager.mjs +95 -0
  24. package/dist/mjs/core/managers/range-metadata-manager.mjs.map +10 -0
  25. package/dist/mjs/core/managers/style-manager.mjs +19 -14
  26. package/dist/mjs/core/managers/style-manager.mjs.map +3 -3
  27. package/dist/mjs/core/types.mjs.map +1 -1
  28. package/dist/mjs/package.json +1 -1
  29. package/dist/types/core/autofill-utils.d.ts +7 -1
  30. package/dist/types/core/engine-snapshot.d.ts +4 -2
  31. package/dist/types/core/engine.d.ts +32 -1
  32. package/dist/types/core/managers/copy-manager.d.ts +11 -1
  33. package/dist/types/core/managers/range-metadata-manager.d.ts +22 -0
  34. package/dist/types/core/managers/style-manager.d.ts +4 -3
  35. package/dist/types/core/types.d.ts +26 -6
  36. package/package.json +1 -1
@@ -0,0 +1,95 @@
1
+ // src/core/managers/range-metadata-manager.ts
2
+ import { isCellInRange } from "../utils.mjs";
3
+ import { rangesIntersect, subtractRange } from "../utils/range-utils.mjs";
4
+ var cloneArea = (area) => ({
5
+ workbookName: area.workbookName,
6
+ sheetName: area.sheetName,
7
+ range: {
8
+ start: { ...area.range.start },
9
+ end: {
10
+ col: { ...area.range.end.col },
11
+ row: { ...area.range.end.row }
12
+ }
13
+ }
14
+ });
15
+ var cloneEntry = (entry) => ({
16
+ id: entry.id,
17
+ areas: entry.areas.map(cloneArea),
18
+ metadata: entry.metadata
19
+ });
20
+
21
+ class RangeMetadataManager {
22
+ rangeMetadata = [];
23
+ addRangeMetadata(entry) {
24
+ const id = entry.id ?? crypto.randomUUID();
25
+ if (this.rangeMetadata.some((existing) => existing.id === id)) {
26
+ throw new Error(`Range metadata with id "${id}" already exists`);
27
+ }
28
+ this.rangeMetadata.push({
29
+ id,
30
+ areas: entry.areas.map(cloneArea),
31
+ metadata: entry.metadata
32
+ });
33
+ return id;
34
+ }
35
+ removeRangeMetadata(id) {
36
+ const beforeLength = this.rangeMetadata.length;
37
+ this.rangeMetadata = this.rangeMetadata.filter((entry) => entry.id !== id);
38
+ return this.rangeMetadata.length !== beforeLength;
39
+ }
40
+ getAllRangeMetadata() {
41
+ return this.rangeMetadata.map(cloneEntry);
42
+ }
43
+ getRangeMetadataForCell(cellAddress) {
44
+ return this.rangeMetadata.filter((entry) => entry.areas.some((area) => area.workbookName === cellAddress.workbookName && area.sheetName === cellAddress.sheetName && isCellInRange(cellAddress, area.range))).map(cloneEntry);
45
+ }
46
+ getRangeMetadataIntersectingWithRange(range) {
47
+ return this.rangeMetadata.filter((entry) => entry.areas.some((area) => area.workbookName === range.workbookName && area.sheetName === range.sheetName && rangesIntersect(area.range, range.range))).map(cloneEntry);
48
+ }
49
+ clearRangeMetadataInRange(range) {
50
+ this.rangeMetadata = this.rangeMetadata.map((entry) => ({
51
+ ...entry,
52
+ areas: entry.areas.flatMap((area) => {
53
+ if (area.workbookName !== range.workbookName || area.sheetName !== range.sheetName) {
54
+ return [area];
55
+ }
56
+ return subtractRange(area.range, range.range).map((remainingRange) => ({
57
+ ...area,
58
+ range: remainingRange
59
+ }));
60
+ })
61
+ })).filter((entry) => entry.areas.length > 0);
62
+ }
63
+ removeWorkbookRangeMetadata(workbookName) {
64
+ this.rangeMetadata = this.rangeMetadata.filter((entry) => !entry.areas.some((area) => area.workbookName === workbookName));
65
+ }
66
+ removeSheetRangeMetadata(workbookName, sheetName) {
67
+ this.rangeMetadata = this.rangeMetadata.filter((entry) => !entry.areas.some((area) => area.workbookName === workbookName && area.sheetName === sheetName));
68
+ }
69
+ updateWorkbookName(oldName, newName) {
70
+ this.rangeMetadata = this.rangeMetadata.map((entry) => ({
71
+ ...entry,
72
+ areas: entry.areas.map((area) => area.workbookName === oldName ? { ...area, workbookName: newName } : area)
73
+ }));
74
+ }
75
+ updateSheetName(workbookName, oldSheetName, newSheetName) {
76
+ this.rangeMetadata = this.rangeMetadata.map((entry) => ({
77
+ ...entry,
78
+ areas: entry.areas.map((area) => area.workbookName === workbookName && area.sheetName === oldSheetName ? { ...area, sheetName: newSheetName } : area)
79
+ }));
80
+ }
81
+ resetRangeMetadata(rangeMetadata) {
82
+ this.rangeMetadata = rangeMetadata ? rangeMetadata.map(cloneEntry) : [];
83
+ }
84
+ toSnapshot() {
85
+ return this.getAllRangeMetadata();
86
+ }
87
+ restoreFromSnapshot(snapshot) {
88
+ this.resetRangeMetadata(snapshot);
89
+ }
90
+ }
91
+ export {
92
+ RangeMetadataManager
93
+ };
94
+
95
+ //# debugId=7530C074EFE9F64464756E2164756E21
@@ -0,0 +1,10 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../src/core/managers/range-metadata-manager.ts"],
4
+ "sourcesContent": [
5
+ "/**\n * RangeMetadataManager - Manages arbitrary consumer-defined metadata attached\n * to ranges rather than individual cells.\n */\n\nimport type {\n CellAddress,\n RangeAddress,\n RangeMetadata,\n RangeMetadataInput,\n} from \"../types.mjs\";\nimport type { RangeMetadataManagerSnapshot } from \"../engine-snapshot.mjs\";\nimport { isCellInRange } from \"../utils.mjs\";\nimport { rangesIntersect, subtractRange } from \"../utils/range-utils.mjs\";\n\nconst cloneArea = (area: RangeAddress): RangeAddress => ({\n workbookName: area.workbookName,\n sheetName: area.sheetName,\n range: {\n start: { ...area.range.start },\n end: {\n col: { ...area.range.end.col },\n row: { ...area.range.end.row },\n },\n },\n});\n\nconst cloneEntry = <TMetadata>(\n entry: RangeMetadata<TMetadata>\n): RangeMetadata<TMetadata> => ({\n id: entry.id,\n areas: entry.areas.map(cloneArea),\n metadata: entry.metadata,\n});\n\nexport class RangeMetadataManager<TMetadata = unknown> {\n private rangeMetadata: RangeMetadata<TMetadata>[] = [];\n\n addRangeMetadata(entry: RangeMetadataInput<TMetadata>): string {\n const id = entry.id ?? crypto.randomUUID();\n if (this.rangeMetadata.some((existing) => existing.id === id)) {\n throw new Error(`Range metadata with id \"${id}\" already exists`);\n }\n\n this.rangeMetadata.push({\n id,\n areas: entry.areas.map(cloneArea),\n metadata: entry.metadata,\n });\n\n return id;\n }\n\n removeRangeMetadata(id: string): boolean {\n const beforeLength = this.rangeMetadata.length;\n this.rangeMetadata = this.rangeMetadata.filter((entry) => entry.id !== id);\n return this.rangeMetadata.length !== beforeLength;\n }\n\n getAllRangeMetadata(): RangeMetadata<TMetadata>[] {\n return this.rangeMetadata.map(cloneEntry);\n }\n\n getRangeMetadataForCell(cellAddress: CellAddress): RangeMetadata<TMetadata>[] {\n return this.rangeMetadata\n .filter((entry) =>\n entry.areas.some(\n (area) =>\n area.workbookName === cellAddress.workbookName &&\n area.sheetName === cellAddress.sheetName &&\n isCellInRange(cellAddress, area.range)\n )\n )\n .map(cloneEntry);\n }\n\n getRangeMetadataIntersectingWithRange(\n range: RangeAddress\n ): RangeMetadata<TMetadata>[] {\n return this.rangeMetadata\n .filter((entry) =>\n entry.areas.some(\n (area) =>\n area.workbookName === range.workbookName &&\n area.sheetName === range.sheetName &&\n rangesIntersect(area.range, range.range)\n )\n )\n .map(cloneEntry);\n }\n\n clearRangeMetadataInRange(range: RangeAddress): void {\n this.rangeMetadata = this.rangeMetadata\n .map((entry) => ({\n ...entry,\n areas: entry.areas.flatMap((area) => {\n if (\n area.workbookName !== range.workbookName ||\n area.sheetName !== range.sheetName\n ) {\n return [area];\n }\n\n return subtractRange(area.range, range.range).map((remainingRange) => ({\n ...area,\n range: remainingRange,\n }));\n }),\n }))\n .filter((entry) => entry.areas.length > 0);\n }\n\n removeWorkbookRangeMetadata(workbookName: string): void {\n this.rangeMetadata = this.rangeMetadata.filter(\n (entry) => !entry.areas.some((area) => area.workbookName === workbookName)\n );\n }\n\n removeSheetRangeMetadata(workbookName: string, sheetName: string): void {\n this.rangeMetadata = this.rangeMetadata.filter(\n (entry) =>\n !entry.areas.some(\n (area) =>\n area.workbookName === workbookName && area.sheetName === sheetName\n )\n );\n }\n\n updateWorkbookName(oldName: string, newName: string): void {\n this.rangeMetadata = this.rangeMetadata.map((entry) => ({\n ...entry,\n areas: entry.areas.map((area) =>\n area.workbookName === oldName ? { ...area, workbookName: newName } : area\n ),\n }));\n }\n\n updateSheetName(\n workbookName: string,\n oldSheetName: string,\n newSheetName: string\n ): void {\n this.rangeMetadata = this.rangeMetadata.map((entry) => ({\n ...entry,\n areas: entry.areas.map((area) =>\n area.workbookName === workbookName && area.sheetName === oldSheetName\n ? { ...area, sheetName: newSheetName }\n : area\n ),\n }));\n }\n\n resetRangeMetadata(rangeMetadata?: RangeMetadata<TMetadata>[]): void {\n this.rangeMetadata = rangeMetadata ? rangeMetadata.map(cloneEntry) : [];\n }\n\n toSnapshot(): RangeMetadataManagerSnapshot {\n return this.getAllRangeMetadata();\n }\n\n restoreFromSnapshot(snapshot: RangeMetadataManagerSnapshot): void {\n this.resetRangeMetadata(snapshot as RangeMetadata<TMetadata>[]);\n }\n}\n"
6
+ ],
7
+ "mappings": "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",
8
+ "debugId": "7530C074EFE9F64464756E2164756E21",
9
+ "names": []
10
+ }
@@ -129,6 +129,21 @@ class StyleManager {
129
129
  this.cellStyles = this.cellStyles.filter((style) => !style.areas.some((area) => area.workbookName === workbookName && area.sheetName === sheetName));
130
130
  }
131
131
  getCellStyle(cellAddress) {
132
+ let resolvedStyle;
133
+ for (const cellStyle of this.cellStyles) {
134
+ if (!cellStyle || !cellStyle.areas) {
135
+ continue;
136
+ }
137
+ for (const area of cellStyle.areas) {
138
+ if (area.workbookName === cellAddress.workbookName && area.sheetName === cellAddress.sheetName && isCellInRange(cellAddress, area.range)) {
139
+ resolvedStyle = {
140
+ ...resolvedStyle,
141
+ ...cellStyle.style
142
+ };
143
+ break;
144
+ }
145
+ }
146
+ }
132
147
  for (const style of this.conditionalStyles) {
133
148
  if (!style || !style.areas) {
134
149
  continue;
@@ -143,25 +158,15 @@ class StyleManager {
143
158
  if (style.condition.type === "formula") {
144
159
  const result = this.evaluateFormulaCondition(cellAddress, style, area);
145
160
  if (result)
146
- return result;
161
+ return { ...resolvedStyle, ...result };
147
162
  } else {
148
163
  const result = this.evaluateGradientCondition(cellAddress, style, area);
149
164
  if (result)
150
- return result;
165
+ return { ...resolvedStyle, ...result };
151
166
  }
152
167
  }
153
168
  }
154
- for (const cellStyle of this.cellStyles) {
155
- if (!cellStyle || !cellStyle.areas) {
156
- continue;
157
- }
158
- for (const area of cellStyle.areas) {
159
- if (area.workbookName === cellAddress.workbookName && area.sheetName === cellAddress.sheetName && isCellInRange(cellAddress, area.range)) {
160
- return cellStyle.style;
161
- }
162
- }
163
- }
164
- return;
169
+ return resolvedStyle;
165
170
  }
166
171
  evaluateFormulaCondition(cellAddress, style, area) {
167
172
  if (style.condition.type !== "formula") {
@@ -376,4 +381,4 @@ export {
376
381
  StyleManager
377
382
  };
378
383
 
379
- //# debugId=C28AECDA2EA2834464756E2164756E21
384
+ //# debugId=3DC8521C7F85157164756E2164756E21
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/core/managers/style-manager.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * StyleManager - Manages conditional styling for cells\n */\n\nimport type {\n CellAddress,\n CellStyle,\n ConditionalStyle,\n DirectCellStyle,\n RangeAddress,\n SerializedCellValue,\n} from \"../types.mjs\";\nimport type { StyleManagerSnapshot } from \"../engine-snapshot.mjs\";\nimport type { WorkbookManager } from \"./workbook-manager.mjs\";\nimport type { EvaluationManager } from \"./evaluation-manager.mjs\";\nimport { isCellInRange } from \"../utils.mjs\";\nimport {\n calculateGradientFactor,\n interpolateLCH,\n lchToHex,\n} from \"../utils/color-utils.mjs\";\nimport {\n subtractRange,\n rangesIntersect,\n isRangeContained,\n} from \"../utils/range-utils.mjs\";\n\nexport class StyleManager {\n private conditionalStyles: ConditionalStyle[] = [];\n private cellStyles: DirectCellStyle[] = [];\n\n constructor(private evaluationManager: EvaluationManager) {}\n\n /**\n * Add a conditional style rule\n */\n addConditionalStyle(style: ConditionalStyle): void {\n this.conditionalStyles.push(style);\n }\n\n /**\n * Remove a conditional style rule by index for a specific workbook\n */\n removeConditionalStyle(workbookName: string, index: number): boolean {\n const workbookStyles = this.conditionalStyles.filter(\n (style) => style.areas.some(area => area.workbookName === workbookName)\n );\n if (index < 0 || index >= workbookStyles.length) {\n return false;\n }\n // Find the actual index in the full array\n let currentIndex = 0;\n for (let i = 0; i < this.conditionalStyles.length; i++) {\n const style = this.conditionalStyles[i];\n if (style && style.areas.some(area => area.workbookName === workbookName)) {\n if (currentIndex === index) {\n this.conditionalStyles.splice(i, 1);\n return true;\n }\n currentIndex++;\n }\n }\n return false;\n }\n\n /**\n * Get all conditional styles intersecting with a range\n */\n getConditionalStylesIntersectingWithRange(\n range: RangeAddress\n ): ConditionalStyle[] {\n return this.conditionalStyles.filter(\n (style) =>\n style.areas.some(area =>\n area.workbookName === range.workbookName &&\n area.sheetName === range.sheetName &&\n rangesIntersect(area.range, range.range)\n )\n );\n }\n\n /**\n * Add a direct cell style rule\n */\n addCellStyle(style: DirectCellStyle): void {\n this.cellStyles.push(style);\n }\n\n /**\n * Remove a direct cell style rule by index for a specific workbook\n */\n removeCellStyle(workbookName: string, index: number): boolean {\n const workbookStyles = this.cellStyles.filter(\n (style) => style && style.areas && style.areas.some(area => area.workbookName === workbookName)\n );\n if (index < 0 || index >= workbookStyles.length) {\n return false;\n }\n // Find the actual index in the full array\n let currentIndex = 0;\n for (let i = 0; i < this.cellStyles.length; i++) {\n const style = this.cellStyles[i];\n if (style && style.areas && style.areas.some(area => area.workbookName === workbookName)) {\n if (currentIndex === index) {\n this.cellStyles.splice(i, 1);\n return true;\n }\n currentIndex++;\n }\n }\n return false;\n }\n\n /**\n * Get all direct cell styles intersecting with a range\n */\n getStylesIntersectingWithRange(range: RangeAddress): DirectCellStyle[] {\n return this.cellStyles.filter(\n (style) =>\n style &&\n style.areas.some(area =>\n area.sheetName === range.sheetName &&\n area.workbookName === range.workbookName &&\n rangesIntersect(area.range, range.range)\n )\n );\n }\n\n /**\n * Get the style for a range if all cells in the range have the same style\n * Returns the DirectCellStyle if the range is completely contained within a single style's areas\n * Returns undefined if multiple styles, partial coverage, or no styles apply\n */\n getStyleForRange(range: RangeAddress): DirectCellStyle | undefined {\n const intersectingStyles = this.getStylesIntersectingWithRange(range);\n\n // If no styles intersect, return undefined\n if (intersectingStyles.length === 0) {\n return undefined;\n }\n\n // If multiple styles intersect, return undefined (range has mixed styles)\n if (intersectingStyles.length > 1) {\n return undefined;\n }\n\n // Check if the range is completely contained within any of the single style's areas\n const style = intersectingStyles[0]!;\n const isContained = style.areas.some(area =>\n area.workbookName === range.workbookName &&\n area.sheetName === range.sheetName &&\n isRangeContained(range.range, area.range)\n );\n \n if (isContained) {\n return style;\n }\n\n // Range is not completely contained, return undefined\n return undefined;\n }\n\n /**\n * Get all conditional styles across all workbooks (for serialization)\n */\n getAllConditionalStyles(): ConditionalStyle[] {\n return [...this.conditionalStyles];\n }\n\n /**\n * Get all cell styles (for serialization)\n */\n getAllCellStyles(): DirectCellStyle[] {\n return [...this.cellStyles];\n }\n\n /**\n * Reset all styles (for deserialization)\n */\n resetStyles(\n conditionalStyles?: ConditionalStyle[],\n cellStyles?: DirectCellStyle[]\n ): void {\n this.conditionalStyles = conditionalStyles ? [...conditionalStyles] : [];\n this.cellStyles = cellStyles ? [...cellStyles] : [];\n }\n\n toSnapshot(): StyleManagerSnapshot {\n return {\n conditionalStyles: this.getAllConditionalStyles(),\n cellStyles: this.getAllCellStyles(),\n };\n }\n\n restoreFromSnapshot(snapshot: StyleManagerSnapshot): void {\n this.resetStyles(snapshot.conditionalStyles, snapshot.cellStyles);\n }\n\n /**\n * Remove all styles for a workbook\n */\n removeWorkbookStyles(workbookName: string): void {\n this.conditionalStyles = this.conditionalStyles.filter(\n (style) => !style.areas.some(area => area.workbookName === workbookName)\n );\n this.cellStyles = this.cellStyles.filter(\n (style) => !style.areas.some(area => area.workbookName === workbookName)\n );\n }\n\n /**\n * Update workbook name in all style references\n */\n updateWorkbookName(oldName: string, newName: string): void {\n // Update conditional styles\n this.conditionalStyles = this.conditionalStyles.map((style) => ({\n ...style,\n areas: style.areas.map(area =>\n area.workbookName === oldName\n ? { ...area, workbookName: newName }\n : area\n )\n }));\n // Update cell styles\n this.cellStyles = this.cellStyles.map((style) => ({\n ...style,\n areas: style.areas.map(area =>\n area.workbookName === oldName\n ? { ...area, workbookName: newName }\n : area\n )\n }));\n }\n\n /**\n * Update sheet name in style references\n */\n updateSheetName(\n workbookName: string,\n oldSheetName: string,\n newSheetName: string\n ): void {\n // Update conditional styles\n this.conditionalStyles = this.conditionalStyles.map((style) => ({\n ...style,\n areas: style.areas.map(area =>\n area.workbookName === workbookName && area.sheetName === oldSheetName\n ? { ...area, sheetName: newSheetName }\n : area\n )\n }));\n // Update cell styles\n this.cellStyles = this.cellStyles.map((style) => ({\n ...style,\n areas: style.areas.map(area =>\n area.workbookName === workbookName && area.sheetName === oldSheetName\n ? { ...area, sheetName: newSheetName }\n : area\n )\n }));\n }\n\n /**\n * Remove styles that reference a deleted sheet\n */\n removeSheetStyles(workbookName: string, sheetName: string): void {\n this.conditionalStyles = this.conditionalStyles.filter(\n (style) =>\n !style.areas.some(area =>\n area.workbookName === workbookName &&\n area.sheetName === sheetName\n )\n );\n this.cellStyles = this.cellStyles.filter(\n (style) =>\n !style.areas.some(area =>\n area.workbookName === workbookName &&\n area.sheetName === sheetName\n )\n );\n }\n\n /**\n * Get the style for a specific cell\n * Returns the first matching style (first match wins)\n * Checks cellStyles first, then conditionalStyles\n */\n getCellStyle(cellAddress: CellAddress): CellStyle | undefined {\n // First check conditional styles\n for (const style of this.conditionalStyles) {\n if (!style || !style.areas) {\n continue;\n }\n \n // Check if cell is in any of the style's areas\n for (const area of style.areas) {\n if (\n area.sheetName !== cellAddress.sheetName ||\n area.workbookName !== cellAddress.workbookName\n ) {\n continue;\n }\n\n if (!isCellInRange(cellAddress, area.range)) {\n continue;\n }\n\n // Cell is in area, evaluate condition\n if (style.condition.type === \"formula\") {\n const result = this.evaluateFormulaCondition(cellAddress, style, area);\n if (result) return result;\n } else {\n const result = this.evaluateGradientCondition(cellAddress, style, area);\n if (result) return result;\n }\n }\n }\n\n // Then check direct cell styles\n for (const cellStyle of this.cellStyles) {\n if (!cellStyle || !cellStyle.areas) {\n continue;\n }\n \n for (const area of cellStyle.areas) {\n if (\n area.workbookName === cellAddress.workbookName &&\n area.sheetName === cellAddress.sheetName &&\n isCellInRange(cellAddress, area.range)\n ) {\n return cellStyle.style;\n }\n }\n }\n\n return undefined;\n }\n\n /**\n * Evaluate a formula-based style condition\n */\n private evaluateFormulaCondition(\n cellAddress: CellAddress,\n style: ConditionalStyle,\n area: RangeAddress\n ): CellStyle | undefined {\n if (style.condition.type !== \"formula\") {\n return undefined;\n }\n\n try {\n // Evaluate formula in context of the cell\n // evaluateFormula expects a full cell value (with = prefix for formulas)\n const formula = style.condition.formula.startsWith(\"=\")\n ? style.condition.formula\n : `=${style.condition.formula}`;\n\n const result = this.evaluationManager.evaluateFormula(\n formula,\n cellAddress\n );\n\n // Check if result is truthy\n const isTruthy =\n result === true ||\n result === \"TRUE\" ||\n (typeof result === \"number\" && result !== 0);\n\n if (isTruthy) {\n return {\n backgroundColor: lchToHex(style.condition.color),\n };\n }\n } catch (error) {\n // If formula evaluation fails, don't apply style\n console.warn(\"Failed to evaluate formula condition:\", error);\n }\n\n return undefined;\n }\n\n /**\n * Evaluate a gradient-based style condition\n */\n private evaluateGradientCondition(\n cellAddress: CellAddress,\n style: ConditionalStyle,\n area: RangeAddress\n ): CellStyle | undefined {\n if (style.condition.type !== \"gradient\") {\n return undefined;\n }\n\n try {\n // Get the cell's evaluation result\n const evalResult =\n this.evaluationManager.getCellEvaluationResult(cellAddress);\n if (!evalResult || evalResult.type !== \"value\") {\n return undefined;\n }\n if (evalResult.result.type !== \"number\") {\n return undefined;\n }\n const cellValue = evalResult.result.value;\n\n // Calculate min and max values for the gradient\n const { min: minValue, max: maxValue} = this.calculateGradientBounds(\n style,\n cellAddress,\n area\n );\n\n if (minValue === null || maxValue === null) {\n return undefined;\n }\n\n // Calculate interpolation factor\n const factor = calculateGradientFactor(cellValue, minValue, maxValue);\n\n // Interpolate between min and max colors\n const minColor = style.condition.min.color;\n const maxColor = style.condition.max.color;\n const interpolatedColor = interpolateLCH(minColor, maxColor, factor);\n\n return {\n backgroundColor: lchToHex(interpolatedColor),\n };\n } catch (error) {\n console.warn(\"Failed to evaluate gradient condition:\", error);\n return undefined;\n }\n }\n\n /**\n * Calculate min and max bounds for a gradient\n */\n private calculateGradientBounds(\n style: ConditionalStyle,\n cellAddress: CellAddress,\n area: RangeAddress\n ): { min: number | null; max: number | null } {\n if (style.condition.type !== \"gradient\") {\n return { min: null, max: null };\n }\n\n const { min: minConfig, max: maxConfig } = style.condition;\n const topLeftCell: CellAddress = {\n workbookName: area.workbookName,\n sheetName: area.sheetName,\n colIndex: area.range.start.col,\n rowIndex: area.range.start.row,\n };\n\n // Calculate min value\n let minValue: number | null = null;\n if (minConfig.type === \"lowest_value\") {\n // Evaluate MIN(range) formula directly\n try {\n const rangeRef = this.getRangeReference(area);\n const result = this.evaluationManager.evaluateFormula(\n `=MIN(${rangeRef})`,\n topLeftCell\n );\n if (typeof result === \"number\") {\n minValue = result;\n }\n } catch (error) {\n console.warn(\"Failed to calculate MIN:\", error);\n }\n } else {\n // Evaluate valueFormula in context of area's top-left cell\n const formula = minConfig.valueFormula.startsWith(\"=\")\n ? minConfig.valueFormula\n : `=${minConfig.valueFormula}`;\n const result = this.evaluationManager.evaluateFormula(\n formula,\n topLeftCell\n );\n if (typeof result === \"number\") {\n minValue = result;\n }\n }\n\n // Calculate max value\n let maxValue: number | null = null;\n if (maxConfig.type === \"highest_value\") {\n // Evaluate MAX(range) formula directly\n try {\n const rangeRef = this.getRangeReference(area);\n const result = this.evaluationManager.evaluateFormula(\n `=MAX(${rangeRef})`,\n topLeftCell\n );\n if (typeof result === \"number\") {\n maxValue = result;\n }\n } catch (error) {\n console.warn(\"Failed to calculate MAX:\", error);\n }\n } else {\n // Evaluate valueFormula in context of area's top-left cell\n const formula = maxConfig.valueFormula.startsWith(\"=\")\n ? maxConfig.valueFormula\n : `=${maxConfig.valueFormula}`;\n const result = this.evaluationManager.evaluateFormula(\n formula,\n topLeftCell\n );\n if (typeof result === \"number\") {\n maxValue = result;\n }\n }\n\n return { min: minValue, max: maxValue };\n }\n\n /**\n * Get a range reference string from a RangeAddress\n * Follows CANONICAL_RANGES.md format:\n * - Closed: A5:D10\n * - Row-bounded (col-open): A5:10\n * - Col-bounded (row-open): A5:D\n * - Open both: A5:INFINITY\n */\n private getRangeReference(area: RangeAddress): string {\n const colToLetter = (col: number): string => {\n let result = \"\";\n let c = col;\n while (c >= 0) {\n result = String.fromCharCode(65 + (c % 26)) + result;\n c = Math.floor(c / 26) - 1;\n }\n return result;\n };\n\n const startCol = colToLetter(area.range.start.col);\n const startRow = area.range.start.row + 1; // Convert to 1-based\n\n const isColInfinity = area.range.end.col.type === \"infinity\";\n const isRowInfinity = area.range.end.row.type === \"infinity\";\n\n let rangeStr: string;\n\n if (isColInfinity && isRowInfinity) {\n // Open both: A5:INFINITY\n rangeStr = `${startCol}${startRow}:INFINITY`;\n } else if (isColInfinity) {\n // Row-bounded (col-open): A5:10\n if (area.range.end.row.type === \"number\") {\n const endRow = area.range.end.row.value + 1; // Convert to 1-based\n rangeStr = `${startCol}${startRow}:${endRow}`;\n } else {\n rangeStr = `${startCol}${startRow}:INFINITY`;\n }\n } else if (isRowInfinity) {\n // Col-bounded (row-open): A5:D\n if (area.range.end.col.type === \"number\") {\n const endCol = colToLetter(area.range.end.col.value);\n rangeStr = `${startCol}${startRow}:${endCol}`;\n } else {\n rangeStr = `${startCol}${startRow}:INFINITY`;\n }\n } else {\n // Closed rectangle: A5:D10\n if (\n area.range.end.col.type === \"number\" &&\n area.range.end.row.type === \"number\"\n ) {\n const endCol = colToLetter(area.range.end.col.value);\n const endRow = area.range.end.row.value + 1; // Convert to 1-based\n rangeStr = `${startCol}${startRow}:${endCol}${endRow}`;\n } else {\n // Fallback to INFINITY if types don't match\n rangeStr = `${startCol}${startRow}:INFINITY`;\n }\n }\n\n // Quote sheet name if it contains spaces or special characters\n const needsQuotes = /[ '!]/.test(area.sheetName);\n const sheetRef = needsQuotes\n ? `'${area.sheetName.replace(/'/g, \"''\")}'`\n : area.sheetName;\n\n // Construct the full reference: [workbook]'sheet'!range\n return `[${area.workbookName}]${sheetRef}!${rangeStr}`;\n }\n\n /**\n * Clear cell styles and conditional styles for a given range\n * Adjusts existing style ranges rather than deleting them entirely\n */\n clearCellStyles(range: RangeAddress): void {\n // Process cellStyles - punch holes in areas\n this.cellStyles = this.cellStyles.map(cellStyle => {\n if (!cellStyle || !cellStyle.areas) {\n return cellStyle;\n }\n\n const newAreas: RangeAddress[] = [];\n \n for (const area of cellStyle.areas) {\n // Check if this area intersects with the clear range\n if (\n area.workbookName === range.workbookName &&\n area.sheetName === range.sheetName &&\n rangesIntersect(area.range, range.range)\n ) {\n // Subtract the clear range from this area\n const remainingRanges = subtractRange(area.range, range.range);\n\n // Add all remaining ranges as new areas\n for (const remainingRange of remainingRanges) {\n newAreas.push({\n workbookName: area.workbookName,\n sheetName: area.sheetName,\n range: remainingRange,\n });\n }\n } else {\n // No intersection, keep the area as-is\n newAreas.push(area);\n }\n }\n \n return { ...cellStyle, areas: newAreas };\n }).filter(style => style.areas.length > 0);\n\n // Process conditionalStyles - punch holes in areas\n this.conditionalStyles = this.conditionalStyles.map(conditionalStyle => {\n if (!conditionalStyle || !conditionalStyle.areas) {\n return conditionalStyle;\n }\n\n const newAreas: RangeAddress[] = [];\n \n for (const area of conditionalStyle.areas) {\n // Check if this area intersects with the clear range\n if (\n area.workbookName === range.workbookName &&\n area.sheetName === range.sheetName &&\n rangesIntersect(area.range, range.range)\n ) {\n // Subtract the clear range from this area\n const remainingRanges = subtractRange(area.range, range.range);\n\n // Add all remaining ranges as new areas\n for (const remainingRange of remainingRanges) {\n newAreas.push({\n workbookName: area.workbookName,\n sheetName: area.sheetName,\n range: remainingRange,\n });\n }\n } else {\n // No intersection, keep the area as-is\n newAreas.push(area);\n }\n }\n \n return { ...conditionalStyle, areas: newAreas };\n }).filter(style => style.areas.length > 0);\n }\n\n /**\n * Clear cell styles in a range using subtraction\n * For each intersecting style, subtract the cleared range from its areas:\n * - If an area is completely contained: remove that area\n * - If an area partially overlaps: split into remaining rectangles (hole punching)\n * - If no intersection: keep area unchanged\n * \n * This matches Excel's behavior where cutting/pasting creates multi-area styles\n */\n clearCellStylesInRange(range: RangeAddress): void {\n this.cellStyles = this.cellStyles.map(style => {\n const newAreas: RangeAddress[] = [];\n \n for (const area of style.areas) {\n // Skip areas from different sheets/workbooks\n if (\n area.workbookName !== range.workbookName ||\n area.sheetName !== range.sheetName\n ) {\n newAreas.push(area);\n continue;\n }\n\n // Check if this area intersects with the range to clear\n if (!rangesIntersect(area.range, range.range)) {\n // No intersection, keep the area unchanged\n newAreas.push(area);\n continue;\n }\n\n // Area intersects - subtract the cleared range (may produce multiple ranges)\n const remainingRanges = subtractRange(area.range, range.range);\n\n // Add all remaining ranges as new areas for this style\n for (const remainingRange of remainingRanges) {\n newAreas.push({\n workbookName: area.workbookName,\n sheetName: area.sheetName,\n range: remainingRange,\n });\n }\n }\n \n return { ...style, areas: newAreas };\n }).filter(style => style.areas.length > 0); // Remove styles with no areas left\n }\n}\n"
5
+ "/**\n * StyleManager - Manages conditional styling for cells\n */\n\nimport type {\n CellAddress,\n CellStyle,\n ConditionalStyle,\n DirectCellStyle,\n RangeAddress,\n SerializedCellValue,\n} from \"../types.mjs\";\nimport type { StyleManagerSnapshot } from \"../engine-snapshot.mjs\";\nimport type { WorkbookManager } from \"./workbook-manager.mjs\";\nimport type { EvaluationManager } from \"./evaluation-manager.mjs\";\nimport { isCellInRange } from \"../utils.mjs\";\nimport {\n calculateGradientFactor,\n interpolateLCH,\n lchToHex,\n} from \"../utils/color-utils.mjs\";\nimport {\n subtractRange,\n rangesIntersect,\n isRangeContained,\n} from \"../utils/range-utils.mjs\";\n\nexport class StyleManager {\n private conditionalStyles: ConditionalStyle[] = [];\n private cellStyles: DirectCellStyle[] = [];\n\n constructor(private evaluationManager: EvaluationManager) {}\n\n /**\n * Add a conditional style rule\n */\n addConditionalStyle(style: ConditionalStyle): void {\n this.conditionalStyles.push(style);\n }\n\n /**\n * Remove a conditional style rule by index for a specific workbook\n */\n removeConditionalStyle(workbookName: string, index: number): boolean {\n const workbookStyles = this.conditionalStyles.filter(\n (style) => style.areas.some(area => area.workbookName === workbookName)\n );\n if (index < 0 || index >= workbookStyles.length) {\n return false;\n }\n // Find the actual index in the full array\n let currentIndex = 0;\n for (let i = 0; i < this.conditionalStyles.length; i++) {\n const style = this.conditionalStyles[i];\n if (style && style.areas.some(area => area.workbookName === workbookName)) {\n if (currentIndex === index) {\n this.conditionalStyles.splice(i, 1);\n return true;\n }\n currentIndex++;\n }\n }\n return false;\n }\n\n /**\n * Get all conditional styles intersecting with a range\n */\n getConditionalStylesIntersectingWithRange(\n range: RangeAddress\n ): ConditionalStyle[] {\n return this.conditionalStyles.filter(\n (style) =>\n style.areas.some(area =>\n area.workbookName === range.workbookName &&\n area.sheetName === range.sheetName &&\n rangesIntersect(area.range, range.range)\n )\n );\n }\n\n /**\n * Add a direct cell style rule\n */\n addCellStyle(style: DirectCellStyle): void {\n this.cellStyles.push(style);\n }\n\n /**\n * Remove a direct cell style rule by index for a specific workbook\n */\n removeCellStyle(workbookName: string, index: number): boolean {\n const workbookStyles = this.cellStyles.filter(\n (style) => style && style.areas && style.areas.some(area => area.workbookName === workbookName)\n );\n if (index < 0 || index >= workbookStyles.length) {\n return false;\n }\n // Find the actual index in the full array\n let currentIndex = 0;\n for (let i = 0; i < this.cellStyles.length; i++) {\n const style = this.cellStyles[i];\n if (style && style.areas && style.areas.some(area => area.workbookName === workbookName)) {\n if (currentIndex === index) {\n this.cellStyles.splice(i, 1);\n return true;\n }\n currentIndex++;\n }\n }\n return false;\n }\n\n /**\n * Get all direct cell styles intersecting with a range\n */\n getStylesIntersectingWithRange(range: RangeAddress): DirectCellStyle[] {\n return this.cellStyles.filter(\n (style) =>\n style &&\n style.areas.some(area =>\n area.sheetName === range.sheetName &&\n area.workbookName === range.workbookName &&\n rangesIntersect(area.range, range.range)\n )\n );\n }\n\n /**\n * Get the style for a range if all cells in the range have the same style\n * Returns the DirectCellStyle if the range is completely contained within a single style's areas\n * Returns undefined if multiple styles, partial coverage, or no styles apply\n */\n getStyleForRange(range: RangeAddress): DirectCellStyle | undefined {\n const intersectingStyles = this.getStylesIntersectingWithRange(range);\n\n // If no styles intersect, return undefined\n if (intersectingStyles.length === 0) {\n return undefined;\n }\n\n // If multiple styles intersect, return undefined (range has mixed styles)\n if (intersectingStyles.length > 1) {\n return undefined;\n }\n\n // Check if the range is completely contained within any of the single style's areas\n const style = intersectingStyles[0]!;\n const isContained = style.areas.some(area =>\n area.workbookName === range.workbookName &&\n area.sheetName === range.sheetName &&\n isRangeContained(range.range, area.range)\n );\n \n if (isContained) {\n return style;\n }\n\n // Range is not completely contained, return undefined\n return undefined;\n }\n\n /**\n * Get all conditional styles across all workbooks (for serialization)\n */\n getAllConditionalStyles(): ConditionalStyle[] {\n return [...this.conditionalStyles];\n }\n\n /**\n * Get all cell styles (for serialization)\n */\n getAllCellStyles(): DirectCellStyle[] {\n return [...this.cellStyles];\n }\n\n /**\n * Reset all styles (for deserialization)\n */\n resetStyles(\n conditionalStyles?: ConditionalStyle[],\n cellStyles?: DirectCellStyle[]\n ): void {\n this.conditionalStyles = conditionalStyles ? [...conditionalStyles] : [];\n this.cellStyles = cellStyles ? [...cellStyles] : [];\n }\n\n toSnapshot(): StyleManagerSnapshot {\n return {\n conditionalStyles: this.getAllConditionalStyles(),\n cellStyles: this.getAllCellStyles(),\n };\n }\n\n restoreFromSnapshot(snapshot: StyleManagerSnapshot): void {\n this.resetStyles(snapshot.conditionalStyles, snapshot.cellStyles);\n }\n\n /**\n * Remove all styles for a workbook\n */\n removeWorkbookStyles(workbookName: string): void {\n this.conditionalStyles = this.conditionalStyles.filter(\n (style) => !style.areas.some(area => area.workbookName === workbookName)\n );\n this.cellStyles = this.cellStyles.filter(\n (style) => !style.areas.some(area => area.workbookName === workbookName)\n );\n }\n\n /**\n * Update workbook name in all style references\n */\n updateWorkbookName(oldName: string, newName: string): void {\n // Update conditional styles\n this.conditionalStyles = this.conditionalStyles.map((style) => ({\n ...style,\n areas: style.areas.map(area =>\n area.workbookName === oldName\n ? { ...area, workbookName: newName }\n : area\n )\n }));\n // Update cell styles\n this.cellStyles = this.cellStyles.map((style) => ({\n ...style,\n areas: style.areas.map(area =>\n area.workbookName === oldName\n ? { ...area, workbookName: newName }\n : area\n )\n }));\n }\n\n /**\n * Update sheet name in style references\n */\n updateSheetName(\n workbookName: string,\n oldSheetName: string,\n newSheetName: string\n ): void {\n // Update conditional styles\n this.conditionalStyles = this.conditionalStyles.map((style) => ({\n ...style,\n areas: style.areas.map(area =>\n area.workbookName === workbookName && area.sheetName === oldSheetName\n ? { ...area, sheetName: newSheetName }\n : area\n )\n }));\n // Update cell styles\n this.cellStyles = this.cellStyles.map((style) => ({\n ...style,\n areas: style.areas.map(area =>\n area.workbookName === workbookName && area.sheetName === oldSheetName\n ? { ...area, sheetName: newSheetName }\n : area\n )\n }));\n }\n\n /**\n * Remove styles that reference a deleted sheet\n */\n removeSheetStyles(workbookName: string, sheetName: string): void {\n this.conditionalStyles = this.conditionalStyles.filter(\n (style) =>\n !style.areas.some(area =>\n area.workbookName === workbookName &&\n area.sheetName === sheetName\n )\n );\n this.cellStyles = this.cellStyles.filter(\n (style) =>\n !style.areas.some(area =>\n area.workbookName === workbookName &&\n area.sheetName === sheetName\n )\n );\n }\n\n /**\n * Get the style for a specific cell.\n * Direct cell styles compose in insertion order, with later styles overriding\n * earlier styles for the same properties. Conditional styles then layer over\n * direct styles for the properties they define.\n */\n getCellStyle(cellAddress: CellAddress): CellStyle | undefined {\n let resolvedStyle: CellStyle | undefined;\n\n for (const cellStyle of this.cellStyles) {\n if (!cellStyle || !cellStyle.areas) {\n continue;\n }\n\n for (const area of cellStyle.areas) {\n if (\n area.workbookName === cellAddress.workbookName &&\n area.sheetName === cellAddress.sheetName &&\n isCellInRange(cellAddress, area.range)\n ) {\n resolvedStyle = {\n ...resolvedStyle,\n ...cellStyle.style,\n };\n break;\n }\n }\n }\n\n for (const style of this.conditionalStyles) {\n if (!style || !style.areas) {\n continue;\n }\n \n // Check if cell is in any of the style's areas\n for (const area of style.areas) {\n if (\n area.sheetName !== cellAddress.sheetName ||\n area.workbookName !== cellAddress.workbookName\n ) {\n continue;\n }\n\n if (!isCellInRange(cellAddress, area.range)) {\n continue;\n }\n\n // Cell is in area, evaluate condition\n if (style.condition.type === \"formula\") {\n const result = this.evaluateFormulaCondition(cellAddress, style, area);\n if (result) return { ...resolvedStyle, ...result };\n } else {\n const result = this.evaluateGradientCondition(cellAddress, style, area);\n if (result) return { ...resolvedStyle, ...result };\n }\n }\n }\n\n return resolvedStyle;\n }\n\n /**\n * Evaluate a formula-based style condition\n */\n private evaluateFormulaCondition(\n cellAddress: CellAddress,\n style: ConditionalStyle,\n area: RangeAddress\n ): CellStyle | undefined {\n if (style.condition.type !== \"formula\") {\n return undefined;\n }\n\n try {\n // Evaluate formula in context of the cell\n // evaluateFormula expects a full cell value (with = prefix for formulas)\n const formula = style.condition.formula.startsWith(\"=\")\n ? style.condition.formula\n : `=${style.condition.formula}`;\n\n const result = this.evaluationManager.evaluateFormula(\n formula,\n cellAddress\n );\n\n // Check if result is truthy\n const isTruthy =\n result === true ||\n result === \"TRUE\" ||\n (typeof result === \"number\" && result !== 0);\n\n if (isTruthy) {\n return {\n backgroundColor: lchToHex(style.condition.color),\n };\n }\n } catch (error) {\n // If formula evaluation fails, don't apply style\n console.warn(\"Failed to evaluate formula condition:\", error);\n }\n\n return undefined;\n }\n\n /**\n * Evaluate a gradient-based style condition\n */\n private evaluateGradientCondition(\n cellAddress: CellAddress,\n style: ConditionalStyle,\n area: RangeAddress\n ): CellStyle | undefined {\n if (style.condition.type !== \"gradient\") {\n return undefined;\n }\n\n try {\n // Get the cell's evaluation result\n const evalResult =\n this.evaluationManager.getCellEvaluationResult(cellAddress);\n if (!evalResult || evalResult.type !== \"value\") {\n return undefined;\n }\n if (evalResult.result.type !== \"number\") {\n return undefined;\n }\n const cellValue = evalResult.result.value;\n\n // Calculate min and max values for the gradient\n const { min: minValue, max: maxValue} = this.calculateGradientBounds(\n style,\n cellAddress,\n area\n );\n\n if (minValue === null || maxValue === null) {\n return undefined;\n }\n\n // Calculate interpolation factor\n const factor = calculateGradientFactor(cellValue, minValue, maxValue);\n\n // Interpolate between min and max colors\n const minColor = style.condition.min.color;\n const maxColor = style.condition.max.color;\n const interpolatedColor = interpolateLCH(minColor, maxColor, factor);\n\n return {\n backgroundColor: lchToHex(interpolatedColor),\n };\n } catch (error) {\n console.warn(\"Failed to evaluate gradient condition:\", error);\n return undefined;\n }\n }\n\n /**\n * Calculate min and max bounds for a gradient\n */\n private calculateGradientBounds(\n style: ConditionalStyle,\n cellAddress: CellAddress,\n area: RangeAddress\n ): { min: number | null; max: number | null } {\n if (style.condition.type !== \"gradient\") {\n return { min: null, max: null };\n }\n\n const { min: minConfig, max: maxConfig } = style.condition;\n const topLeftCell: CellAddress = {\n workbookName: area.workbookName,\n sheetName: area.sheetName,\n colIndex: area.range.start.col,\n rowIndex: area.range.start.row,\n };\n\n // Calculate min value\n let minValue: number | null = null;\n if (minConfig.type === \"lowest_value\") {\n // Evaluate MIN(range) formula directly\n try {\n const rangeRef = this.getRangeReference(area);\n const result = this.evaluationManager.evaluateFormula(\n `=MIN(${rangeRef})`,\n topLeftCell\n );\n if (typeof result === \"number\") {\n minValue = result;\n }\n } catch (error) {\n console.warn(\"Failed to calculate MIN:\", error);\n }\n } else {\n // Evaluate valueFormula in context of area's top-left cell\n const formula = minConfig.valueFormula.startsWith(\"=\")\n ? minConfig.valueFormula\n : `=${minConfig.valueFormula}`;\n const result = this.evaluationManager.evaluateFormula(\n formula,\n topLeftCell\n );\n if (typeof result === \"number\") {\n minValue = result;\n }\n }\n\n // Calculate max value\n let maxValue: number | null = null;\n if (maxConfig.type === \"highest_value\") {\n // Evaluate MAX(range) formula directly\n try {\n const rangeRef = this.getRangeReference(area);\n const result = this.evaluationManager.evaluateFormula(\n `=MAX(${rangeRef})`,\n topLeftCell\n );\n if (typeof result === \"number\") {\n maxValue = result;\n }\n } catch (error) {\n console.warn(\"Failed to calculate MAX:\", error);\n }\n } else {\n // Evaluate valueFormula in context of area's top-left cell\n const formula = maxConfig.valueFormula.startsWith(\"=\")\n ? maxConfig.valueFormula\n : `=${maxConfig.valueFormula}`;\n const result = this.evaluationManager.evaluateFormula(\n formula,\n topLeftCell\n );\n if (typeof result === \"number\") {\n maxValue = result;\n }\n }\n\n return { min: minValue, max: maxValue };\n }\n\n /**\n * Get a range reference string from a RangeAddress\n * Follows CANONICAL_RANGES.md format:\n * - Closed: A5:D10\n * - Row-bounded (col-open): A5:10\n * - Col-bounded (row-open): A5:D\n * - Open both: A5:INFINITY\n */\n private getRangeReference(area: RangeAddress): string {\n const colToLetter = (col: number): string => {\n let result = \"\";\n let c = col;\n while (c >= 0) {\n result = String.fromCharCode(65 + (c % 26)) + result;\n c = Math.floor(c / 26) - 1;\n }\n return result;\n };\n\n const startCol = colToLetter(area.range.start.col);\n const startRow = area.range.start.row + 1; // Convert to 1-based\n\n const isColInfinity = area.range.end.col.type === \"infinity\";\n const isRowInfinity = area.range.end.row.type === \"infinity\";\n\n let rangeStr: string;\n\n if (isColInfinity && isRowInfinity) {\n // Open both: A5:INFINITY\n rangeStr = `${startCol}${startRow}:INFINITY`;\n } else if (isColInfinity) {\n // Row-bounded (col-open): A5:10\n if (area.range.end.row.type === \"number\") {\n const endRow = area.range.end.row.value + 1; // Convert to 1-based\n rangeStr = `${startCol}${startRow}:${endRow}`;\n } else {\n rangeStr = `${startCol}${startRow}:INFINITY`;\n }\n } else if (isRowInfinity) {\n // Col-bounded (row-open): A5:D\n if (area.range.end.col.type === \"number\") {\n const endCol = colToLetter(area.range.end.col.value);\n rangeStr = `${startCol}${startRow}:${endCol}`;\n } else {\n rangeStr = `${startCol}${startRow}:INFINITY`;\n }\n } else {\n // Closed rectangle: A5:D10\n if (\n area.range.end.col.type === \"number\" &&\n area.range.end.row.type === \"number\"\n ) {\n const endCol = colToLetter(area.range.end.col.value);\n const endRow = area.range.end.row.value + 1; // Convert to 1-based\n rangeStr = `${startCol}${startRow}:${endCol}${endRow}`;\n } else {\n // Fallback to INFINITY if types don't match\n rangeStr = `${startCol}${startRow}:INFINITY`;\n }\n }\n\n // Quote sheet name if it contains spaces or special characters\n const needsQuotes = /[ '!]/.test(area.sheetName);\n const sheetRef = needsQuotes\n ? `'${area.sheetName.replace(/'/g, \"''\")}'`\n : area.sheetName;\n\n // Construct the full reference: [workbook]'sheet'!range\n return `[${area.workbookName}]${sheetRef}!${rangeStr}`;\n }\n\n /**\n * Clear cell styles and conditional styles for a given range\n * Adjusts existing style ranges rather than deleting them entirely\n */\n clearCellStyles(range: RangeAddress): void {\n // Process cellStyles - punch holes in areas\n this.cellStyles = this.cellStyles.map(cellStyle => {\n if (!cellStyle || !cellStyle.areas) {\n return cellStyle;\n }\n\n const newAreas: RangeAddress[] = [];\n \n for (const area of cellStyle.areas) {\n // Check if this area intersects with the clear range\n if (\n area.workbookName === range.workbookName &&\n area.sheetName === range.sheetName &&\n rangesIntersect(area.range, range.range)\n ) {\n // Subtract the clear range from this area\n const remainingRanges = subtractRange(area.range, range.range);\n\n // Add all remaining ranges as new areas\n for (const remainingRange of remainingRanges) {\n newAreas.push({\n workbookName: area.workbookName,\n sheetName: area.sheetName,\n range: remainingRange,\n });\n }\n } else {\n // No intersection, keep the area as-is\n newAreas.push(area);\n }\n }\n \n return { ...cellStyle, areas: newAreas };\n }).filter(style => style.areas.length > 0);\n\n // Process conditionalStyles - punch holes in areas\n this.conditionalStyles = this.conditionalStyles.map(conditionalStyle => {\n if (!conditionalStyle || !conditionalStyle.areas) {\n return conditionalStyle;\n }\n\n const newAreas: RangeAddress[] = [];\n \n for (const area of conditionalStyle.areas) {\n // Check if this area intersects with the clear range\n if (\n area.workbookName === range.workbookName &&\n area.sheetName === range.sheetName &&\n rangesIntersect(area.range, range.range)\n ) {\n // Subtract the clear range from this area\n const remainingRanges = subtractRange(area.range, range.range);\n\n // Add all remaining ranges as new areas\n for (const remainingRange of remainingRanges) {\n newAreas.push({\n workbookName: area.workbookName,\n sheetName: area.sheetName,\n range: remainingRange,\n });\n }\n } else {\n // No intersection, keep the area as-is\n newAreas.push(area);\n }\n }\n \n return { ...conditionalStyle, areas: newAreas };\n }).filter(style => style.areas.length > 0);\n }\n\n /**\n * Clear cell styles in a range using subtraction\n * For each intersecting style, subtract the cleared range from its areas:\n * - If an area is completely contained: remove that area\n * - If an area partially overlaps: split into remaining rectangles (hole punching)\n * - If no intersection: keep area unchanged\n * \n * This matches Excel's behavior where cutting/pasting creates multi-area styles\n */\n clearCellStylesInRange(range: RangeAddress): void {\n this.cellStyles = this.cellStyles.map(style => {\n const newAreas: RangeAddress[] = [];\n \n for (const area of style.areas) {\n // Skip areas from different sheets/workbooks\n if (\n area.workbookName !== range.workbookName ||\n area.sheetName !== range.sheetName\n ) {\n newAreas.push(area);\n continue;\n }\n\n // Check if this area intersects with the range to clear\n if (!rangesIntersect(area.range, range.range)) {\n // No intersection, keep the area unchanged\n newAreas.push(area);\n continue;\n }\n\n // Area intersects - subtract the cleared range (may produce multiple ranges)\n const remainingRanges = subtractRange(area.range, range.range);\n\n // Add all remaining ranges as new areas for this style\n for (const remainingRange of remainingRanges) {\n newAreas.push({\n workbookName: area.workbookName,\n sheetName: area.sheetName,\n range: remainingRange,\n });\n }\n }\n \n return { ...style, areas: newAreas };\n }).filter(style => style.areas.length > 0); // Remove styles with no areas left\n }\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "C28AECDA2EA2834464756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "3DC8521C7F85157164756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -2,7 +2,7 @@
2
2
  "version": 3,
3
3
  "sources": ["../../../src/core/types.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * Core type definitions for FormulaEngine\n * This file contains all fundamental types used throughout the engine\n */\n\nimport type { EvaluationContext } from \"../evaluator/evaluation-context.mjs\";\nimport type { FormulaEvaluator } from \"../evaluator/formula-evaluator.mjs\";\nimport type { FunctionNode } from \"../parser/ast.mjs\";\nimport type { DependencyNode } from \"./managers/dependency-node.mjs\";\nimport type { LookupOrder } from \"./managers/range-eval-order-builder.mjs\";\n\n// Cell addressing types\nexport interface CellAddress {\n sheetName: string;\n workbookName: string;\n colIndex: number;\n rowIndex: number;\n}\n\nexport interface RangeAddress {\n sheetName: string;\n workbookName: string;\n range: SpreadsheetRange;\n}\n\nexport interface LocalCellAddress {\n colIndex: number;\n rowIndex: number;\n}\n\nexport type ArethmeticEvaluator = (\n left: CellValue,\n right: CellValue,\n context: EvaluationContext\n) => CellValue | ErrorEvaluationResult;\n\nexport type PositiveInfinity = {\n type: \"infinity\";\n sign: \"positive\";\n};\n\nexport type CellInfinity = {\n type: \"infinity\";\n sign: \"positive\" | \"negative\";\n};\n\nexport type CellNumber = {\n type: \"number\";\n value: number;\n};\n\nexport type SpreadsheetRangeEnd = CellNumber | PositiveInfinity;\n\nexport type SpreadsheetRange = {\n start: {\n col: number;\n row: number;\n };\n end: {\n col: SpreadsheetRangeEnd;\n row: SpreadsheetRangeEnd;\n };\n};\n\nexport type RelativeRange = {\n start: {\n col: number;\n row: number;\n };\n width: SpreadsheetRangeEnd;\n height: SpreadsheetRangeEnd;\n};\n\nexport type FiniteSpreadsheetRange = {\n start: {\n col: number;\n row: number;\n };\n end: {\n col: number;\n row: number;\n };\n};\n\nexport type CellString = {\n type: \"string\";\n value: string;\n};\n\nexport type CellBoolean = {\n type: \"boolean\";\n value: boolean;\n};\n\n// Cell value types\nexport type CellValue = CellNumber | CellString | CellBoolean | CellInfinity;\n/**\n * undefined and \"\" are considered empty values\n * undefineds are converted to \"\" in the engine\n *\n * any empty values are deleted from the sheet content\n */\nexport type SerializedCellValue = string | number | boolean | undefined;\n\nexport interface SearchOptions {\n workbookName?: string;\n sheetName?: string;\n caseSensitive?: boolean;\n}\n\nexport interface SearchMatch {\n workbookName: string;\n sheetName: string;\n cellReference: string;\n cellContent: string;\n contentKind: \"formula\" | \"text\";\n occurrenceIndex: number;\n startIndex: number;\n endIndexExclusive: number;\n matchedText: string;\n}\n\nexport interface ReplaceTarget {\n workbookName: string;\n sheetName: string;\n cellReference: string;\n occurrenceIndex: number;\n}\n\nexport interface ReplaceChange {\n workbookName: string;\n sheetName: string;\n cellReference: string;\n contentKind: \"formula\" | \"text\";\n occurrenceIndex: number;\n startIndex: number;\n endIndexExclusive: number;\n matchedText: string;\n replacementText: string;\n beforeContent: string;\n afterContent: string;\n}\n\n// Named expressions\nexport interface NamedExpression {\n name: string;\n expression: string;\n}\n\nexport interface TableDefinition {\n name: string;\n start: {\n rowIndex: number;\n colIndex: number;\n };\n headers: Map<string, { name: string; index: number }>;\n endRow: SpreadsheetRangeEnd;\n sheetName: string;\n workbookName: string;\n}\n\n// Formula errors\nexport enum FormulaError {\n DIV0 = \"#DIV/0!\",\n NA = \"#N/A\",\n NAME = \"#NAME?\",\n NUM = \"#NUM!\",\n REF = \"#REF!\",\n VALUE = \"#VALUE!\",\n CYCLE = \"#CYCLE!\",\n ERROR = \"#ERROR!\",\n SPILL = \"#SPILL!\",\n}\n\n// Sheet structure\nexport interface Sheet<TCellMetadata = unknown, TSheetMetadata = unknown> {\n name: string;\n index: number; // 0-based index of the sheet\n content: Map<string, SerializedCellValue>;\n /**\n * Cell metadata - arbitrary consumer-defined data per cell\n * Examples: rich text content, links, comments, custom app data\n * The engine stores and copies this data but doesn't interpret it\n * Keyed by cell reference (e.g., \"A1\")\n */\n metadata: Map<string, TCellMetadata>;\n /**\n * Sheet-level metadata - arbitrary consumer-defined data for the entire sheet\n * Examples: text boxes, drawings, frozen panes, print settings\n * The engine stores and copies this data but doesn't interpret it\n */\n sheetMetadata: TSheetMetadata;\n}\n\nexport interface Workbook<\n TCellMetadata = unknown,\n TSheetMetadata = unknown,\n TWorkbookMetadata = unknown\n> {\n name: string;\n sheets: Map<string, Sheet<TCellMetadata, TSheetMetadata>>;\n /**\n * Workbook-level metadata - arbitrary consumer-defined data for the entire workbook\n * Examples: themes, custom ribbons, document properties, workbook settings\n * The engine stores and copies this data but doesn't interpret it\n */\n workbookMetadata: TWorkbookMetadata;\n}\n\n/**\n * Tracked reference - a stable reference to a range that updates automatically\n * when workbooks/sheets are renamed and becomes invalid when they're deleted\n */\nexport interface TrackedReference {\n id: string; // UUID\n address: RangeAddress; // The range being tracked\n isValid: boolean; // False if sheet/workbook deleted\n}\n\nexport type ValueEvaluationResult = {\n type: \"value\";\n result: CellValue;\n /**\n * If the terminating evaluation result is a reference (see evaluateReference)\n * then we store information about the source cell for context dependent functions like CELL\n */\n sourceCell?: CellAddress;\n};\n\nexport type AwaitingEvaluationResult = {\n type: \"awaiting-evaluation\";\n waitingFor: DependencyNode;\n errAddress: DependencyNode;\n /**\n * If the terminating evaluation result is a reference (see evaluateReference)\n * then we store information about the source cell for context dependent functions like CELL\n */\n sourceCell?: CellAddress;\n};\n\nexport type DoesNotSpillResult = {\n type: \"does-not-spill\";\n};\n\nexport type ErrorEvaluationResult =\n | {\n type: \"error\";\n err: FormulaError;\n errAddress: DependencyNode;\n message: string;\n /**\n * If the terminating evaluation result is a reference (see evaluateReference)\n * then we store information about the source cell for context dependent functions like CELL\n */\n sourceCell?: CellAddress;\n }\n | AwaitingEvaluationResult;\n\nexport type SingleEvaluationResult =\n | ValueEvaluationResult\n | ErrorEvaluationResult;\n\nexport type SpilledValuesEvaluator = (\n spillOffset: { x: number; y: number },\n context: EvaluationContext\n) => SingleEvaluationResult;\n\nexport type SpilledValuesEvaluationResult = {\n type: \"spilled-values\";\n\n /**\n * When a raw range is evaluated, we will add it to the sourceRange so it can be used e.g. for context dependent functions\n */\n sourceRange?: RangeAddress;\n\n /**\n * If the terminating evaluation result is a reference (see evaluateReference)\n * then we store information about the source cell for context dependent functions like CELL\n * sourceCell will only be defined on a spilledValue when a single value is looked up,\n */\n sourceCell?: CellAddress;\n\n spillArea: (origin: CellAddress) => SpreadsheetRange;\n /**\n * for debugging we add a source string to denote where the spilled values were created\n */\n source: string;\n evaluate: SpilledValuesEvaluator;\n /**\n * evaluateAllCells evaluates all non-empty cells in the spilled range.\n * Because a spilled range can be open-ended, we need to have logic for which cells we should evaluate.\n * e.g. when evaluating a range such as D:D only the cells in the current sheet residing in\n * column D should be evaluated and cells producing spilled values that spill onto D:D.\n *\n * In order to evaluate spilled cells in D:D the range evaluateAllCells need to get all cells in the\n * the intersection of the spilled range and D:D, for that reason evaluateAllCells gets an intersection parameter,\n * where the intersection is relative to the origin.\n *\n * #### Producers:\n * In e.g. SEQUENCE and evaluateRange we have logic for which cells in a spilled range we should evaluate,\n *\n * #### Nesting:\n * e.g. evaluation of scalar operators where we want to nest e.g. `5 * right.evaluate()`\n * can be implemented by calling\n * ```ts\n * const vals = child.evaluateAllCells.call(this, options);\n * return vals.map(val => ({ ...val, result: 5 * val.result }));\n * ```\n *\n * #### Consumers:\n * Only functions that need access to all spilled values in a range end up calling evaluateAllCells, e.g.\n * SUM, MIN, MAX, MATCH. Other types of functions like INDEX doesn't need to evaluate all cells in a range,\n * but does a lookup into a spilled range using the evaluate method.\n *\n */\n evaluateAllCells: (\n this: FormulaEvaluator,\n options: {\n /**\n * an intersection relative to the origin\n */\n intersection?: SpreadsheetRange;\n evaluate: SpilledValuesEvaluator;\n context: EvaluationContext;\n /**\n * origin is the cell address that the spilled range is spilled from\n * e.g. in A3=B2:B4 the origin is A3\n */\n origin: CellAddress;\n\n lookupOrder: LookupOrder;\n }\n ) => EvaluateAllCellsResult;\n};\n\nexport type EvaluateAllCellsResult =\n | ErrorEvaluationResult\n | {\n type: \"values\";\n values: CellInRangeResult[];\n };\n\nexport type CellInRangeResult = {\n result: SingleEvaluationResult;\n relativePos: { x: number; y: number };\n};\n\nexport type FunctionEvaluationResult =\n | SingleEvaluationResult\n | SpilledValuesEvaluationResult;\n\nexport type SpilledValue = {\n /**\n * spillOnto is the range that the spilled value is spilled onto\n */\n spillOnto: SpreadsheetRange;\n /**\n * origin is the cell address that the spilled value is spilled from\n */\n origin: CellAddress;\n};\n\n/**\n * Function definition\n */\nexport interface FunctionDefinition {\n name: string;\n evaluate: (\n this: FormulaEvaluator,\n node: FunctionNode,\n context: EvaluationContext\n ) => FunctionEvaluationResult;\n aliases?: string[];\n}\n\n/**\n * Evaluation result\n */\nexport type EvaluationResult = {\n dependencies: Set<string>;\n} & FunctionEvaluationResult;\n\nexport type SCC = {\n id: number;\n nodes: Set<DependencyNode>; // All nodes considering soft + hard edges\n evaluationOrder: DependencyNode[]; // Flat topologically ordered list\n resolved: boolean;\n hardEdgeSCCs: Set<DependencyNode>[]; // SCCs formed by only hard edges (regular dependencies)\n};\n\nexport type SCCDAG = {\n sccList: SCC[];\n sccGraph: Map<number, Set<number>>; // Adjacency list of SCC dependencies\n};\n\nexport type EvaluationOrder = {\n evaluationOrder: Set<DependencyNode>;\n hasCycle: boolean;\n cycleNodes?: Set<DependencyNode>;\n hash: string;\n sccDAG?: SCCDAG;\n};\n\n// Conditional Styling types\nexport interface LCHColor {\n l: number; // Lightness: 0-100\n c: number; // Chroma: 0-150+\n h: number; // Hue: 0-360\n}\n\nexport interface FormulaStyleCondition {\n type: \"formula\";\n formula: string;\n color: LCHColor;\n}\n\nexport interface GradientStyleCondition {\n type: \"gradient\";\n min:\n | { type: \"lowest_value\"; color: LCHColor }\n | { type: \"number\"; color: LCHColor; valueFormula: string };\n max:\n | { type: \"highest_value\"; color: LCHColor }\n | { type: \"number\"; color: LCHColor; valueFormula: string };\n}\n\nexport type StyleCondition = FormulaStyleCondition | GradientStyleCondition;\n\nexport interface ConditionalStyle {\n areas: RangeAddress[];\n condition: StyleCondition;\n}\n\nexport interface DirectCellStyle {\n areas: RangeAddress[];\n style: CellStyle;\n}\n\nexport interface CellStyle {\n backgroundColor?: string; // Hex color format\n color?: string; // Text color in hex format\n fontSize?: number; // Font size in pixels\n bold?: boolean;\n italic?: boolean;\n underline?: boolean;\n}\n\nexport interface CopyCellsOptions {\n /**\n * Whether this is a cut operation (clears source cells after copying)\n * @default false\n */\n cut?: boolean;\n /**\n * What to include in the copy operation.\n * - Use 'all' as shorthand for ['content', 'style', 'metadata']\n * - Use array for fine-grained control over what to copy:\n * - ['content'] - copy only values/formulas\n * - ['style'] - copy only formatting\n * - ['metadata'] - copy only metadata (rich text, links, etc.)\n * - ['content', 'style'] - copy content and formatting\n * - ['content', 'metadata'] - copy content and metadata\n * - ['style', 'metadata'] - copy formatting and metadata\n * - ['content', 'style', 'metadata'] - same as 'all'\n * @default 'all'\n */\n include?: \"all\" | (\"content\" | \"style\" | \"metadata\")[];\n /**\n * The type of the content to copy\n * value: Copy the value from the source to the target,\n * e.g. if the cell has the formula =123 + 123 then the value is 246\n * formula: Copy the formula from the source to the target,\n * e.g. if the cell has the formula =123 + 123 then the formula is =123 + 123 is copied\n * @default 'formula'\n */\n type?: \"value\" | \"formula\";\n}\n"
5
+ "/**\n * Core type definitions for FormulaEngine\n * This file contains all fundamental types used throughout the engine\n */\n\nimport type { EvaluationContext } from \"../evaluator/evaluation-context.mjs\";\nimport type { FormulaEvaluator } from \"../evaluator/formula-evaluator.mjs\";\nimport type { FunctionNode } from \"../parser/ast.mjs\";\nimport type { DependencyNode } from \"./managers/dependency-node.mjs\";\nimport type { LookupOrder } from \"./managers/range-eval-order-builder.mjs\";\n\n// Cell addressing types\nexport interface CellAddress {\n sheetName: string;\n workbookName: string;\n colIndex: number;\n rowIndex: number;\n}\n\nexport interface RangeAddress {\n sheetName: string;\n workbookName: string;\n range: SpreadsheetRange;\n}\n\nexport interface LocalCellAddress {\n colIndex: number;\n rowIndex: number;\n}\n\nexport type ArethmeticEvaluator = (\n left: CellValue,\n right: CellValue,\n context: EvaluationContext\n) => CellValue | ErrorEvaluationResult;\n\nexport type PositiveInfinity = {\n type: \"infinity\";\n sign: \"positive\";\n};\n\nexport type CellInfinity = {\n type: \"infinity\";\n sign: \"positive\" | \"negative\";\n};\n\nexport type CellNumber = {\n type: \"number\";\n value: number;\n};\n\nexport type SpreadsheetRangeEnd = CellNumber | PositiveInfinity;\n\nexport type SpreadsheetRange = {\n start: {\n col: number;\n row: number;\n };\n end: {\n col: SpreadsheetRangeEnd;\n row: SpreadsheetRangeEnd;\n };\n};\n\nexport type RelativeRange = {\n start: {\n col: number;\n row: number;\n };\n width: SpreadsheetRangeEnd;\n height: SpreadsheetRangeEnd;\n};\n\nexport type FiniteSpreadsheetRange = {\n start: {\n col: number;\n row: number;\n };\n end: {\n col: number;\n row: number;\n };\n};\n\nexport type CellString = {\n type: \"string\";\n value: string;\n};\n\nexport type CellBoolean = {\n type: \"boolean\";\n value: boolean;\n};\n\n// Cell value types\nexport type CellValue = CellNumber | CellString | CellBoolean | CellInfinity;\n/**\n * undefined and \"\" are considered empty values\n * undefineds are converted to \"\" in the engine\n *\n * any empty values are deleted from the sheet content\n */\nexport type SerializedCellValue = string | number | boolean | undefined;\n\nexport interface SearchOptions {\n workbookName?: string;\n sheetName?: string;\n caseSensitive?: boolean;\n}\n\nexport interface SearchMatch {\n workbookName: string;\n sheetName: string;\n cellReference: string;\n cellContent: string;\n contentKind: \"formula\" | \"text\";\n occurrenceIndex: number;\n startIndex: number;\n endIndexExclusive: number;\n matchedText: string;\n}\n\nexport interface ReplaceTarget {\n workbookName: string;\n sheetName: string;\n cellReference: string;\n occurrenceIndex: number;\n}\n\nexport interface ReplaceChange {\n workbookName: string;\n sheetName: string;\n cellReference: string;\n contentKind: \"formula\" | \"text\";\n occurrenceIndex: number;\n startIndex: number;\n endIndexExclusive: number;\n matchedText: string;\n replacementText: string;\n beforeContent: string;\n afterContent: string;\n}\n\n// Named expressions\nexport interface NamedExpression {\n name: string;\n expression: string;\n}\n\nexport interface TableDefinition {\n name: string;\n start: {\n rowIndex: number;\n colIndex: number;\n };\n headers: Map<string, { name: string; index: number }>;\n endRow: SpreadsheetRangeEnd;\n sheetName: string;\n workbookName: string;\n}\n\n// Formula errors\nexport enum FormulaError {\n DIV0 = \"#DIV/0!\",\n NA = \"#N/A\",\n NAME = \"#NAME?\",\n NUM = \"#NUM!\",\n REF = \"#REF!\",\n VALUE = \"#VALUE!\",\n CYCLE = \"#CYCLE!\",\n ERROR = \"#ERROR!\",\n SPILL = \"#SPILL!\",\n}\n\n// Sheet structure\nexport interface Sheet<TCellMetadata = unknown, TSheetMetadata = unknown> {\n name: string;\n index: number; // 0-based index of the sheet\n content: Map<string, SerializedCellValue>;\n /**\n * Cell metadata - arbitrary consumer-defined data per cell\n * Examples: rich text content, links, comments, custom app data\n * The engine stores and copies this data but doesn't interpret it\n * Keyed by cell reference (e.g., \"A1\")\n */\n metadata: Map<string, TCellMetadata>;\n /**\n * Sheet-level metadata - arbitrary consumer-defined data for the entire sheet\n * Examples: text boxes, drawings, frozen panes, print settings\n * The engine stores and copies this data but doesn't interpret it\n */\n sheetMetadata: TSheetMetadata;\n}\n\nexport interface Workbook<\n TCellMetadata = unknown,\n TSheetMetadata = unknown,\n TWorkbookMetadata = unknown\n> {\n name: string;\n sheets: Map<string, Sheet<TCellMetadata, TSheetMetadata>>;\n /**\n * Workbook-level metadata - arbitrary consumer-defined data for the entire workbook\n * Examples: themes, custom ribbons, document properties, workbook settings\n * The engine stores and copies this data but doesn't interpret it\n */\n workbookMetadata: TWorkbookMetadata;\n}\n\n/**\n * Tracked reference - a stable reference to a range that updates automatically\n * when workbooks/sheets are renamed and becomes invalid when they're deleted\n */\nexport interface TrackedReference {\n id: string; // UUID\n address: RangeAddress; // The range being tracked\n isValid: boolean; // False if sheet/workbook deleted\n}\n\nexport type ValueEvaluationResult = {\n type: \"value\";\n result: CellValue;\n /**\n * If the terminating evaluation result is a reference (see evaluateReference)\n * then we store information about the source cell for context dependent functions like CELL\n */\n sourceCell?: CellAddress;\n};\n\nexport type AwaitingEvaluationResult = {\n type: \"awaiting-evaluation\";\n waitingFor: DependencyNode;\n errAddress: DependencyNode;\n /**\n * If the terminating evaluation result is a reference (see evaluateReference)\n * then we store information about the source cell for context dependent functions like CELL\n */\n sourceCell?: CellAddress;\n};\n\nexport type DoesNotSpillResult = {\n type: \"does-not-spill\";\n};\n\nexport type ErrorEvaluationResult =\n | {\n type: \"error\";\n err: FormulaError;\n errAddress: DependencyNode;\n message: string;\n /**\n * If the terminating evaluation result is a reference (see evaluateReference)\n * then we store information about the source cell for context dependent functions like CELL\n */\n sourceCell?: CellAddress;\n }\n | AwaitingEvaluationResult;\n\nexport type SingleEvaluationResult =\n | ValueEvaluationResult\n | ErrorEvaluationResult;\n\nexport type SpilledValuesEvaluator = (\n spillOffset: { x: number; y: number },\n context: EvaluationContext\n) => SingleEvaluationResult;\n\nexport type SpilledValuesEvaluationResult = {\n type: \"spilled-values\";\n\n /**\n * When a raw range is evaluated, we will add it to the sourceRange so it can be used e.g. for context dependent functions\n */\n sourceRange?: RangeAddress;\n\n /**\n * If the terminating evaluation result is a reference (see evaluateReference)\n * then we store information about the source cell for context dependent functions like CELL\n * sourceCell will only be defined on a spilledValue when a single value is looked up,\n */\n sourceCell?: CellAddress;\n\n spillArea: (origin: CellAddress) => SpreadsheetRange;\n /**\n * for debugging we add a source string to denote where the spilled values were created\n */\n source: string;\n evaluate: SpilledValuesEvaluator;\n /**\n * evaluateAllCells evaluates all non-empty cells in the spilled range.\n * Because a spilled range can be open-ended, we need to have logic for which cells we should evaluate.\n * e.g. when evaluating a range such as D:D only the cells in the current sheet residing in\n * column D should be evaluated and cells producing spilled values that spill onto D:D.\n *\n * In order to evaluate spilled cells in D:D the range evaluateAllCells need to get all cells in the\n * the intersection of the spilled range and D:D, for that reason evaluateAllCells gets an intersection parameter,\n * where the intersection is relative to the origin.\n *\n * #### Producers:\n * In e.g. SEQUENCE and evaluateRange we have logic for which cells in a spilled range we should evaluate,\n *\n * #### Nesting:\n * e.g. evaluation of scalar operators where we want to nest e.g. `5 * right.evaluate()`\n * can be implemented by calling\n * ```ts\n * const vals = child.evaluateAllCells.call(this, options);\n * return vals.map(val => ({ ...val, result: 5 * val.result }));\n * ```\n *\n * #### Consumers:\n * Only functions that need access to all spilled values in a range end up calling evaluateAllCells, e.g.\n * SUM, MIN, MAX, MATCH. Other types of functions like INDEX doesn't need to evaluate all cells in a range,\n * but does a lookup into a spilled range using the evaluate method.\n *\n */\n evaluateAllCells: (\n this: FormulaEvaluator,\n options: {\n /**\n * an intersection relative to the origin\n */\n intersection?: SpreadsheetRange;\n evaluate: SpilledValuesEvaluator;\n context: EvaluationContext;\n /**\n * origin is the cell address that the spilled range is spilled from\n * e.g. in A3=B2:B4 the origin is A3\n */\n origin: CellAddress;\n\n lookupOrder: LookupOrder;\n }\n ) => EvaluateAllCellsResult;\n};\n\nexport type EvaluateAllCellsResult =\n | ErrorEvaluationResult\n | {\n type: \"values\";\n values: CellInRangeResult[];\n };\n\nexport type CellInRangeResult = {\n result: SingleEvaluationResult;\n relativePos: { x: number; y: number };\n};\n\nexport type FunctionEvaluationResult =\n | SingleEvaluationResult\n | SpilledValuesEvaluationResult;\n\nexport type SpilledValue = {\n /**\n * spillOnto is the range that the spilled value is spilled onto\n */\n spillOnto: SpreadsheetRange;\n /**\n * origin is the cell address that the spilled value is spilled from\n */\n origin: CellAddress;\n};\n\n/**\n * Function definition\n */\nexport interface FunctionDefinition {\n name: string;\n evaluate: (\n this: FormulaEvaluator,\n node: FunctionNode,\n context: EvaluationContext\n ) => FunctionEvaluationResult;\n aliases?: string[];\n}\n\n/**\n * Evaluation result\n */\nexport type EvaluationResult = {\n dependencies: Set<string>;\n} & FunctionEvaluationResult;\n\nexport type SCC = {\n id: number;\n nodes: Set<DependencyNode>; // All nodes considering soft + hard edges\n evaluationOrder: DependencyNode[]; // Flat topologically ordered list\n resolved: boolean;\n hardEdgeSCCs: Set<DependencyNode>[]; // SCCs formed by only hard edges (regular dependencies)\n};\n\nexport type SCCDAG = {\n sccList: SCC[];\n sccGraph: Map<number, Set<number>>; // Adjacency list of SCC dependencies\n};\n\nexport type EvaluationOrder = {\n evaluationOrder: Set<DependencyNode>;\n hasCycle: boolean;\n cycleNodes?: Set<DependencyNode>;\n hash: string;\n sccDAG?: SCCDAG;\n};\n\n// Conditional Styling types\nexport interface LCHColor {\n l: number; // Lightness: 0-100\n c: number; // Chroma: 0-150+\n h: number; // Hue: 0-360\n}\n\nexport interface FormulaStyleCondition {\n type: \"formula\";\n formula: string;\n color: LCHColor;\n}\n\nexport interface GradientStyleCondition {\n type: \"gradient\";\n min:\n | { type: \"lowest_value\"; color: LCHColor }\n | { type: \"number\"; color: LCHColor; valueFormula: string };\n max:\n | { type: \"highest_value\"; color: LCHColor }\n | { type: \"number\"; color: LCHColor; valueFormula: string };\n}\n\nexport type StyleCondition = FormulaStyleCondition | GradientStyleCondition;\n\nexport interface ConditionalStyle {\n areas: RangeAddress[];\n condition: StyleCondition;\n}\n\nexport interface DirectCellStyle {\n areas: RangeAddress[];\n style: CellStyle;\n}\n\nexport interface RangeMetadata<TMetadata = unknown> {\n id: string;\n areas: RangeAddress[];\n metadata: TMetadata;\n}\n\nexport interface RangeMetadataInput<TMetadata = unknown> {\n id?: string;\n areas: RangeAddress[];\n metadata: TMetadata;\n}\n\nexport interface CellStyle {\n backgroundColor?: string; // Hex color format\n color?: string; // Text color in hex format\n borderColor?: string; // Border color in hex format\n borderSides?: {\n top?: boolean;\n right?: boolean;\n bottom?: boolean;\n left?: boolean;\n };\n fontSize?: number; // Font size in pixels\n bold?: boolean;\n italic?: boolean;\n underline?: boolean;\n wrapText?: boolean;\n}\n\nexport type CopyCellsIncludePart =\n | \"content\"\n | \"style\"\n | \"cellMetadata\"\n | \"rangeMetadata\";\n\nexport interface CopyCellsOptions {\n /**\n * Whether this is a cut operation (clears source cells after copying)\n * @default false\n */\n cut?: boolean;\n /**\n * What to include in the copy operation.\n * - Use 'all' as shorthand for ['content', 'style', 'cellMetadata', 'rangeMetadata']\n * - Use array for fine-grained control over what to copy:\n * - ['content'] - copy only values/formulas\n * - ['style'] - copy only formatting\n * - ['cellMetadata'] - copy only cell metadata (rich text, links, etc.)\n * - ['rangeMetadata'] - copy only range metadata\n * - ['content', 'style'] - copy content and formatting\n * - ['content', 'cellMetadata'] - copy content and cell metadata\n * - ['style', 'rangeMetadata'] - copy formatting and range metadata\n * - ['content', 'style', 'cellMetadata', 'rangeMetadata'] - same as 'all'\n * @default 'all'\n */\n include?: \"all\" | CopyCellsIncludePart[];\n /**\n * The type of the content to copy\n * value: Copy the value from the source to the target,\n * e.g. if the cell has the formula =123 + 123 then the value is 246\n * formula: Copy the formula from the source to the target,\n * e.g. if the cell has the formula =123 + 123 then the formula is =123 + 123 is copied\n * @default 'formula'\n */\n type?: \"value\" | \"formula\";\n}\n"
6
6
  ],
7
7
  "mappings": ";AAkKO,IAAK;AAAA,CAAL,CAAK,kBAAL;AAAA,EACL,wBAAO;AAAA,EACP,sBAAK;AAAA,EACL,wBAAO;AAAA,EACP,uBAAM;AAAA,EACN,uBAAM;AAAA,EACN,yBAAQ;AAAA,EACR,yBAAQ;AAAA,EACR,yBAAQ;AAAA,EACR,yBAAQ;AAAA,GATE;",
8
8
  "debugId": "0E7C5AF634572D3964756E2164756E21",
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "name": "@ricsam/formula-engine",
3
- "version": "0.2.11",
3
+ "version": "0.2.13",
4
4
  "type": "module"
5
5
  }
@@ -5,10 +5,12 @@ import type { SpreadsheetRange } from "./types";
5
5
  import type { FillDirection } from "@ricsam/selection-manager";
6
6
  import type { WorkbookManager } from "./managers/workbook-manager";
7
7
  import type { StyleManager } from "./managers/style-manager";
8
+ import type { RangeMetadataManager } from "./managers/range-metadata-manager";
8
9
  export declare class AutoFill {
9
10
  private workbookManager;
10
11
  private styleManager;
11
- constructor(workbookManager: WorkbookManager, styleManager: StyleManager);
12
+ private rangeMetadataManager;
13
+ constructor(workbookManager: WorkbookManager, styleManager: StyleManager, rangeMetadataManager: RangeMetadataManager);
12
14
  /**
13
15
  * Converts a SpreadsheetRange to FiniteSpreadsheetRange, throwing an error if infinite
14
16
  */
@@ -37,4 +39,8 @@ export declare class AutoFill {
37
39
  * Copy metadata from seed range to fill range with pattern repetition
38
40
  */
39
41
  private fillMetadata;
42
+ /**
43
+ * Copy range metadata from seed range to fill range with pattern repetition.
44
+ */
45
+ private fillRangeMetadata;
40
46
  }
@@ -1,7 +1,7 @@
1
1
  import type { ContextDependency } from "../evaluator/evaluation-context";
2
2
  import type { DependencyNode } from "./managers/dependency-node";
3
- import type { CellAddress, CellInRangeResult, CellValue, ConditionalStyle, DirectCellStyle, FormulaError, NamedExpression, RangeAddress, RelativeRange, SpreadsheetRange, TableDefinition, TrackedReference, Workbook } from "./types";
4
- export declare const ENGINE_SNAPSHOT_VERSION: 4;
3
+ import type { CellAddress, CellInRangeResult, CellValue, ConditionalStyle, DirectCellStyle, FormulaError, NamedExpression, RangeAddress, RangeMetadata, RelativeRange, SpreadsheetRange, TableDefinition, TrackedReference, Workbook } from "./types";
4
+ export declare const ENGINE_SNAPSHOT_VERSION: 5;
5
5
  export type NodeSnapshotId = string;
6
6
  export type NamedExpressionManagerSnapshot = {
7
7
  sheetExpressions: Map<string, Map<string, Map<string, NamedExpression>>>;
@@ -14,6 +14,7 @@ export type StyleManagerSnapshot = {
14
14
  conditionalStyles: ConditionalStyle[];
15
15
  cellStyles: DirectCellStyle[];
16
16
  };
17
+ export type RangeMetadataManagerSnapshot = RangeMetadata[];
17
18
  export type ReferenceManagerSnapshot = Map<string, TrackedReference>;
18
19
  export type SerializedValueEvaluationResultSnapshot = {
19
20
  type: "value";
@@ -124,6 +125,7 @@ type EngineSnapshotManagers = {
124
125
  namedExpression: NamedExpressionManagerSnapshot;
125
126
  table: TableManagerSnapshot;
126
127
  style: StyleManagerSnapshot;
128
+ rangeMetadata: RangeMetadataManagerSnapshot;
127
129
  reference: ReferenceManagerSnapshot;
128
130
  dependency: DependencyManagerSnapshot;
129
131
  cache: CacheManagerSnapshot;
@@ -2,7 +2,7 @@
2
2
  * Main FormulaEngine class
3
3
  * Core API implementation for spreadsheet calculations
4
4
  */
5
- import { type CellAddress, type CellStyle, type ConditionalStyle, type CopyCellsOptions, type DirectCellStyle, type NamedExpression, type RangeAddress, type ReplaceChange, type ReplaceTarget, type SearchMatch, type SearchOptions, type SerializedCellValue, type Sheet, type SingleEvaluationResult, type SpreadsheetRange, type SpreadsheetRangeEnd, type TableDefinition } from "./types";
5
+ import { type CellAddress, type CellStyle, type ConditionalStyle, type CopyCellsOptions, type DirectCellStyle, type NamedExpression, type RangeAddress, type RangeMetadata, type RangeMetadataInput, type ReplaceChange, type ReplaceTarget, type SearchMatch, type SearchOptions, type SerializedCellValue, type Sheet, type SingleEvaluationResult, type SpreadsheetRange, type SpreadsheetRangeEnd, type TableDefinition } from "./types";
6
6
  import type { FillDirection } from "@ricsam/selection-manager";
7
7
  import { AutoFill } from "./autofill-utils";
8
8
  import { WorkbookManager } from "./managers/workbook-manager";
@@ -12,8 +12,10 @@ import { EventManager } from "./managers/event-manager";
12
12
  import { EvaluationManager } from "./managers/evaluation-manager";
13
13
  import { DependencyManager } from "./managers/dependency-manager";
14
14
  import { StyleManager } from "./managers/style-manager";
15
+ import { RangeMetadataManager } from "./managers/range-metadata-manager";
15
16
  type Metadata = {
16
17
  cell?: unknown;
18
+ range?: unknown;
17
19
  sheet?: unknown;
18
20
  workbook?: unknown;
19
21
  };
@@ -31,6 +33,7 @@ export declare class FormulaEngine<TMetadata extends Metadata = Metadata> {
31
33
  private autoFillManager;
32
34
  private dependencyManager;
33
35
  private styleManager;
36
+ private rangeMetadataManager;
34
37
  private copyManager;
35
38
  private referenceManager;
36
39
  /**
@@ -44,6 +47,7 @@ export declare class FormulaEngine<TMetadata extends Metadata = Metadata> {
44
47
  _autoFillManager: AutoFill;
45
48
  _dependencyManager: DependencyManager;
46
49
  _styleManager: StyleManager;
50
+ _rangeMetadataManager: RangeMetadataManager<MetadataType<TMetadata, "range">>;
47
51
  constructor();
48
52
  /**
49
53
  * Static factory method to build an empty engine
@@ -98,6 +102,32 @@ export declare class FormulaEngine<TMetadata extends Metadata = Metadata> {
98
102
  * Get metadata for a workbook
99
103
  */
100
104
  getWorkbookMetadata(workbookName: string): MetadataType<TMetadata, "workbook"> | undefined;
105
+ /**
106
+ * Add metadata attached to one or more ranges.
107
+ * Range metadata is arbitrary consumer-defined data that follows range
108
+ * copy/paste, autofill, workbook/sheet rename, and serialization flows.
109
+ */
110
+ addRangeMetadata(metadata: RangeMetadataInput<MetadataType<TMetadata, "range">>): string;
111
+ /**
112
+ * Remove a range metadata entry by id.
113
+ */
114
+ removeRangeMetadata(id: string): void;
115
+ /**
116
+ * Get all range metadata entries.
117
+ */
118
+ getAllRangeMetadata(): RangeMetadata<MetadataType<TMetadata, "range">>[];
119
+ /**
120
+ * Get range metadata entries that apply to a specific cell.
121
+ */
122
+ getRangeMetadataForCell(address: CellAddress): RangeMetadata<MetadataType<TMetadata, "range">>[];
123
+ /**
124
+ * Get range metadata entries intersecting with a range.
125
+ */
126
+ getRangeMetadataIntersectingWithRange(range: RangeAddress): RangeMetadata<MetadataType<TMetadata, "range">>[];
127
+ /**
128
+ * Clear range metadata from a range, preserving non-overlapping portions.
129
+ */
130
+ clearRangeMetadata(range: RangeAddress): void;
101
131
  /**
102
132
  * Create a tracked reference to a range
103
133
  * Returns a stable UUID that can be used to retrieve the address later
@@ -487,6 +517,7 @@ export declare class FormulaEngine<TMetadata extends Metadata = Metadata> {
487
517
  tables: Map<string, Map<string, TableDefinition>>;
488
518
  conditionalStyles: ConditionalStyle[];
489
519
  cellStyles: DirectCellStyle[];
520
+ rangeMetadata: RangeMetadata<MetadataType<TMetadata, "range">>[];
490
521
  references: Map<string, import("./types").TrackedReference>;
491
522
  };
492
523
  onUpdate(listener: () => void): () => void;
@@ -5,11 +5,13 @@ import type { CellAddress, CopyCellsOptions, RangeAddress } from "../types";
5
5
  import type { WorkbookManager } from "./workbook-manager";
6
6
  import type { EvaluationManager } from "./evaluation-manager";
7
7
  import type { StyleManager } from "./style-manager";
8
+ import type { RangeMetadataManager } from "./range-metadata-manager";
8
9
  export declare class CopyManager {
9
10
  private workbookManager;
10
11
  private evaluationManager;
11
12
  private styleManager;
12
- constructor(workbookManager: WorkbookManager, evaluationManager: EvaluationManager, styleManager: StyleManager);
13
+ private rangeMetadataManager;
14
+ constructor(workbookManager: WorkbookManager, evaluationManager: EvaluationManager, styleManager: StyleManager, rangeMetadataManager: RangeMetadataManager);
13
15
  /**
14
16
  * Normalize the include option to an array of parts to copy
15
17
  */
@@ -60,6 +62,7 @@ export declare class CopyManager {
60
62
  * Copy content from a cell snapshot to a target cell
61
63
  */
62
64
  private copyCellContentFromSnapshot;
65
+ private copyCellMetadataFromSnapshot;
63
66
  /**
64
67
  * Update all formula references when cells are cut (moved)
65
68
  */
@@ -77,6 +80,11 @@ export declare class CopyManager {
77
80
  * Clears existing cell styles in target range first (Excel behavior)
78
81
  */
79
82
  private copyFormatting;
83
+ private cloneMetadataValue;
84
+ private getRangeAddressFromBoundingBox;
85
+ private collectRangeMetadataCopies;
86
+ private applyRangeMetadataCopies;
87
+ private copyRangeMetadata;
80
88
  /**
81
89
  * Get bounding box for a set of cells
82
90
  */
@@ -123,6 +131,8 @@ export declare class CopyManager {
123
131
  * Adjust formula references by a specific row/column offset
124
132
  */
125
133
  private adjustFormulaWithOffset;
134
+ private copyCellMetadata;
135
+ private copyCellRangeMetadata;
126
136
  /**
127
137
  * Copy formatting from one cell to another
128
138
  * Clears existing cell styles at target (Excel behavior) before copying new ones
@@ -0,0 +1,22 @@
1
+ /**
2
+ * RangeMetadataManager - Manages arbitrary consumer-defined metadata attached
3
+ * to ranges rather than individual cells.
4
+ */
5
+ import type { CellAddress, RangeAddress, RangeMetadata, RangeMetadataInput } from "../types";
6
+ import type { RangeMetadataManagerSnapshot } from "../engine-snapshot";
7
+ export declare class RangeMetadataManager<TMetadata = unknown> {
8
+ private rangeMetadata;
9
+ addRangeMetadata(entry: RangeMetadataInput<TMetadata>): string;
10
+ removeRangeMetadata(id: string): boolean;
11
+ getAllRangeMetadata(): RangeMetadata<TMetadata>[];
12
+ getRangeMetadataForCell(cellAddress: CellAddress): RangeMetadata<TMetadata>[];
13
+ getRangeMetadataIntersectingWithRange(range: RangeAddress): RangeMetadata<TMetadata>[];
14
+ clearRangeMetadataInRange(range: RangeAddress): void;
15
+ removeWorkbookRangeMetadata(workbookName: string): void;
16
+ removeSheetRangeMetadata(workbookName: string, sheetName: string): void;
17
+ updateWorkbookName(oldName: string, newName: string): void;
18
+ updateSheetName(workbookName: string, oldSheetName: string, newSheetName: string): void;
19
+ resetRangeMetadata(rangeMetadata?: RangeMetadata<TMetadata>[]): void;
20
+ toSnapshot(): RangeMetadataManagerSnapshot;
21
+ restoreFromSnapshot(snapshot: RangeMetadataManagerSnapshot): void;
22
+ }