mulmoclaude 2.7.0 → 2.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/assets/Graph-ChAxkn9o.js +1 -0
- package/client/assets/dist-Bij44LdC.js +2 -0
- package/client/assets/echarts-GV3U9sVf.js +1 -0
- package/client/assets/echarts-gl-BXx4On0Y.js +4282 -0
- package/client/assets/echarts-wordcloud-_9uQUAfp.js +1 -0
- package/client/assets/emscripten-module-Q67P5WYC-BU3Hlttz.js +2160 -0
- package/client/assets/ffi-4V2b7xu3.js +1 -0
- package/client/assets/{index-lkpgbKtk.js → index-D6xzCZhP.js} +600 -589
- package/client/assets/index-DzMsXDnj.css +2 -0
- package/client/assets/{install-wxcddUsO.js → install-C1moNcrn.js} +1 -1
- package/client/assets/{marp-DcTywifN.js → marp-Du5Sym5b.js} +1 -1
- package/client/assets/module-ES6BEMUI-CDTIpMiA.js +6 -0
- package/client/index.html +4 -3
- package/package.json +5 -4
- package/server/api/routes/plugins.ts +8 -59
- package/server/build/mcp-server.mjs +609 -608
- package/server/index.ts +1 -0
- package/server/plugins/confined-file-ops.ts +136 -0
- package/server/plugins/runtime.ts +8 -115
- package/server/plugins/spreadsheet-builtin.ts +26 -0
- package/server/plugins/spreadsheet-save-outcome.ts +7 -0
- package/server/utils/files/store-resolvers.ts +1 -1
- package/src/lang/de.ts +0 -20
- package/src/lang/en.ts +0 -20
- package/src/lang/es.ts +0 -20
- package/src/lang/fr.ts +0 -20
- package/src/lang/ja.ts +0 -20
- package/src/lang/ko.ts +0 -20
- package/src/lang/pt-BR.ts +0 -20
- package/src/lang/zh.ts +0 -20
- package/src/plugins/spreadsheet/definition.ts +9 -127
- package/src/plugins/spreadsheet/index.ts +7 -4
- package/src/plugins/spreadsheet/meta.ts +2 -4
- package/client/assets/echarts-gl-Cq76Z6DA.js +0 -4282
- package/client/assets/index-Cz7JLE0k.css +0 -2
- package/server/utils/files/spreadsheet-store.ts +0 -26
- package/src/plugins/spreadsheet/Preview.vue +0 -31
- package/src/plugins/spreadsheet/View.vue +0 -1007
- package/src/plugins/spreadsheet/cellHighlights.ts +0 -78
- package/src/plugins/spreadsheet/engine/calculator.ts +0 -427
- package/src/plugins/spreadsheet/engine/cellBuilder.ts +0 -89
- package/src/plugins/spreadsheet/engine/cellEmpty.ts +0 -26
- package/src/plugins/spreadsheet/engine/cellFormatting.ts +0 -59
- package/src/plugins/spreadsheet/engine/coerce-boolean.ts +0 -26
- package/src/plugins/spreadsheet/engine/condition.ts +0 -182
- package/src/plugins/spreadsheet/engine/date-locale.ts +0 -47
- package/src/plugins/spreadsheet/engine/date-parser.ts +0 -234
- package/src/plugins/spreadsheet/engine/date-utils.ts +0 -73
- package/src/plugins/spreadsheet/engine/datedif.ts +0 -93
- package/src/plugins/spreadsheet/engine/engine.ts +0 -177
- package/src/plugins/spreadsheet/engine/evaluator.ts +0 -494
- package/src/plugins/spreadsheet/engine/financial-math.ts +0 -114
- package/src/plugins/spreadsheet/engine/formatter.ts +0 -158
- package/src/plugins/spreadsheet/engine/formulaError.ts +0 -67
- package/src/plugins/spreadsheet/engine/formulaRefs.ts +0 -202
- package/src/plugins/spreadsheet/engine/functions/date.ts +0 -212
- package/src/plugins/spreadsheet/engine/functions/financial.ts +0 -244
- package/src/plugins/spreadsheet/engine/functions/index.ts +0 -16
- package/src/plugins/spreadsheet/engine/functions/logical.ts +0 -221
- package/src/plugins/spreadsheet/engine/functions/lookup-math.ts +0 -28
- package/src/plugins/spreadsheet/engine/functions/lookup.ts +0 -257
- package/src/plugins/spreadsheet/engine/functions/mathematical.ts +0 -283
- package/src/plugins/spreadsheet/engine/functions/statistical-math.ts +0 -91
- package/src/plugins/spreadsheet/engine/functions/statistical.ts +0 -290
- package/src/plugins/spreadsheet/engine/functions/text.ts +0 -398
- package/src/plugins/spreadsheet/engine/index.ts +0 -34
- package/src/plugins/spreadsheet/engine/jsonCellLocator.ts +0 -111
- package/src/plugins/spreadsheet/engine/math-ops.ts +0 -98
- package/src/plugins/spreadsheet/engine/numericCoercion.ts +0 -61
- package/src/plugins/spreadsheet/engine/parser.ts +0 -40
- package/src/plugins/spreadsheet/engine/registry.ts +0 -201
- package/src/plugins/spreadsheet/engine/responseDecoder.ts +0 -68
- package/src/plugins/spreadsheet/engine/spreadsheet-errors.ts +0 -82
- package/src/plugins/spreadsheet/engine/textFormat.ts +0 -108
- package/src/plugins/spreadsheet/engine/translateFormula.ts +0 -69
- package/src/plugins/spreadsheet/engine/types.ts +0 -81
- package/src/plugins/spreadsheet/keyboardNav.ts +0 -38
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Formula errors as a distinct VALUE type.
|
|
3
|
-
*
|
|
4
|
-
* A `#NUM!` produced by `SQRT(-1)` and the text `"#NUM!"` produced by
|
|
5
|
-
* `CONCAT("#N","UM!")` used to be the same string, so IFERROR could not tell a
|
|
6
|
-
* real error from text that merely spells one (#2451). An error is now its own
|
|
7
|
-
* value carrying the code, which is what the error-aware functions check; the
|
|
8
|
-
* display pass renders it back to `#NUM!` so cells look unchanged.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
export const SPREADSHEET_ERRORS = ["#NULL!", "#DIV/0!", "#VALUE!", "#REF!", "#NAME?", "#NUM!", "#N/A", "#ERROR!"] as const;
|
|
12
|
-
|
|
13
|
-
export type SpreadsheetErrorCode = (typeof SPREADSHEET_ERRORS)[number];
|
|
14
|
-
|
|
15
|
-
/** A formula error, distinct from any string. `toString` yields the code so the
|
|
16
|
-
* value renders as `#NUM!` wherever the engine coerces a cell value to text. */
|
|
17
|
-
export class SpreadsheetError {
|
|
18
|
-
constructor(readonly code: SpreadsheetErrorCode) {}
|
|
19
|
-
|
|
20
|
-
toString(): string {
|
|
21
|
-
return this.code;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
toJSON(): string {
|
|
25
|
-
return this.code;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export const NULL_ERROR = new SpreadsheetError("#NULL!");
|
|
30
|
-
export const DIV_ZERO_ERROR = new SpreadsheetError("#DIV/0!");
|
|
31
|
-
export const VALUE_ERROR = new SpreadsheetError("#VALUE!");
|
|
32
|
-
export const REF_ERROR = new SpreadsheetError("#REF!");
|
|
33
|
-
export const NAME_ERROR = new SpreadsheetError("#NAME?");
|
|
34
|
-
export const NUM_ERROR = new SpreadsheetError("#NUM!");
|
|
35
|
-
export const NA_ERROR = new SpreadsheetError("#N/A");
|
|
36
|
-
export const UNKNOWN_ERROR = new SpreadsheetError("#ERROR!");
|
|
37
|
-
|
|
38
|
-
// One instance per code: two errors of the same kind compare equal, which is how
|
|
39
|
-
// error strings behaved and what the condition/comparison paths still rely on.
|
|
40
|
-
const ERROR_VALUES: Record<SpreadsheetErrorCode, SpreadsheetError> = {
|
|
41
|
-
"#NULL!": NULL_ERROR,
|
|
42
|
-
"#DIV/0!": DIV_ZERO_ERROR,
|
|
43
|
-
"#VALUE!": VALUE_ERROR,
|
|
44
|
-
"#REF!": REF_ERROR,
|
|
45
|
-
"#NAME?": NAME_ERROR,
|
|
46
|
-
"#NUM!": NUM_ERROR,
|
|
47
|
-
"#N/A": NA_ERROR,
|
|
48
|
-
"#ERROR!": UNKNOWN_ERROR,
|
|
49
|
-
};
|
|
50
|
-
|
|
51
|
-
/** The error value for a code. */
|
|
52
|
-
export const spreadsheetError = (code: SpreadsheetErrorCode): SpreadsheetError => ERROR_VALUES[code];
|
|
53
|
-
|
|
54
|
-
const errorSet: ReadonlySet<string> = new Set(SPREADSHEET_ERRORS);
|
|
55
|
-
|
|
56
|
-
/** Whether a value is one of the error CODES as a plain string. Text that spells
|
|
57
|
-
* an error is not an error value — see `isSpreadsheetErrorValue`. */
|
|
58
|
-
export function isSpreadsheetError(value: unknown): value is SpreadsheetErrorCode {
|
|
59
|
-
return typeof value === "string" && errorSet.has(value);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/** Whether a value IS a formula error, as opposed to text that spells one. */
|
|
63
|
-
export function isSpreadsheetErrorValue(value: unknown): value is SpreadsheetError {
|
|
64
|
-
return value instanceof SpreadsheetError;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** The code behind a value: an error value's own code, or the code a plain
|
|
68
|
-
* string spells. Used where a literal `#REF!` written into a cell must still
|
|
69
|
-
* poison the arithmetic that reads it, provenance aside. */
|
|
70
|
-
export function errorCodeOf(value: unknown): SpreadsheetErrorCode | null {
|
|
71
|
-
if (isSpreadsheetErrorValue(value)) return value.code;
|
|
72
|
-
return isSpreadsheetError(value) ? value : null;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/** Whether an evaluated result should be treated as an error: a formula error
|
|
76
|
-
* VALUE, a NaN / infinite number, or a missing value. This is what IFERROR
|
|
77
|
-
* catches — deliberately NOT a look-alike string, which is ordinary text. */
|
|
78
|
-
export function isErrorResult(value: unknown): boolean {
|
|
79
|
-
if (value === null || value === undefined) return true;
|
|
80
|
-
if (typeof value === "number") return Number.isNaN(value) || !Number.isFinite(value);
|
|
81
|
-
return isSpreadsheetErrorValue(value);
|
|
82
|
-
}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Excel number-format patterns for the TEXT function.
|
|
3
|
-
*
|
|
4
|
-
* Deliberately separate from `formatter.formatNumber`, which renders CELL
|
|
5
|
-
* display values: the two disagree on defaults — Excel's `TEXT(0.5,"0%")` is
|
|
6
|
-
* `"50%"`, while a cell carrying the format `0%` has always displayed
|
|
7
|
-
* `"50.00%"` — so sharing the whole path would move every formatted cell in
|
|
8
|
-
* every stored workbook. Only the digit-grouping primitive is shared.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { groupThousands } from "./formatter";
|
|
12
|
-
|
|
13
|
-
const PERCENT_SCALE = 100;
|
|
14
|
-
|
|
15
|
-
// Characters that introduce an Excel format feature this module does not
|
|
16
|
-
// render (quoted literals, fill/skip, fractions, dates, text placeholder,
|
|
17
|
-
// negative/zero sections). Seeing one means the caller keeps its own fallback.
|
|
18
|
-
const UNSUPPORTED_LITERAL_CHARS = `?\\*_[]"@;/`;
|
|
19
|
-
|
|
20
|
-
// One run of digit placeholders, optionally carrying grouping commas and a
|
|
21
|
-
// decimal point: the `#,##0.00` family.
|
|
22
|
-
const NUMERIC_CORE_RE = /[#0][#0,.]*/;
|
|
23
|
-
const PLACEHOLDER_RE = /[#0]/;
|
|
24
|
-
const TRAILING_ZEROS_RE = /0+$/;
|
|
25
|
-
|
|
26
|
-
export interface NumberPattern {
|
|
27
|
-
prefix: string;
|
|
28
|
-
suffix: string;
|
|
29
|
-
useGrouping: boolean;
|
|
30
|
-
integerMinDigits: number;
|
|
31
|
-
minDecimals: number;
|
|
32
|
-
maxDecimals: number;
|
|
33
|
-
isPercent: boolean;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const countOf = (text: string, chars: string): number => Array.from(text).filter((char) => chars.includes(char)).length;
|
|
37
|
-
|
|
38
|
-
const hasUnsupportedLiteral = (text: string): boolean => Array.from(text).some((char) => UNSUPPORTED_LITERAL_CHARS.includes(char));
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Split a format code into a leading literal, one numeric core and a trailing
|
|
42
|
-
* literal, or `null` when the code uses a feature this module does not render.
|
|
43
|
-
*/
|
|
44
|
-
export const parseNumberPattern = (pattern: string): NumberPattern | null => {
|
|
45
|
-
const core = NUMERIC_CORE_RE.exec(pattern);
|
|
46
|
-
if (!core) return null;
|
|
47
|
-
// A comma AFTER the last placeholder scales by a thousand in Excel.
|
|
48
|
-
if (core[0].endsWith(",")) return null;
|
|
49
|
-
|
|
50
|
-
const prefix = pattern.slice(0, core.index);
|
|
51
|
-
const suffix = pattern.slice(core.index + core[0].length);
|
|
52
|
-
// A second placeholder run (`0.00E+00`, `# ?/?`) is a format of its own kind.
|
|
53
|
-
if (PLACEHOLDER_RE.test(suffix)) return null;
|
|
54
|
-
if (hasUnsupportedLiteral(prefix) || hasUnsupportedLiteral(suffix)) return null;
|
|
55
|
-
|
|
56
|
-
const [integerPart, decimalPart = "", ...extraParts] = core[0].split(".");
|
|
57
|
-
if (integerPart === undefined || extraParts.length > 0) return null;
|
|
58
|
-
|
|
59
|
-
return {
|
|
60
|
-
prefix,
|
|
61
|
-
suffix,
|
|
62
|
-
useGrouping: integerPart.includes(","),
|
|
63
|
-
integerMinDigits: countOf(integerPart, "0"),
|
|
64
|
-
minDecimals: countOf(decimalPart, "0"),
|
|
65
|
-
maxDecimals: countOf(decimalPart, "#0"),
|
|
66
|
-
isPercent: prefix.includes("%") || suffix.includes("%"),
|
|
67
|
-
};
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
// `0` keeps a decimal digit, `#` drops it once it is a trailing zero — so
|
|
71
|
-
// `0.0#` renders 0.5 as "0.5" but 0.25 as "0.25".
|
|
72
|
-
const trimOptionalZeros = (decimals: string, minDecimals: number): string => {
|
|
73
|
-
const kept = decimals.replace(TRAILING_ZEROS_RE, "");
|
|
74
|
-
return kept.length >= minDecimals ? kept : decimals.slice(0, minDecimals);
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const renderDigits = (absValue: number, pattern: NumberPattern): string => {
|
|
78
|
-
const fixed = absValue.toFixed(pattern.maxDecimals);
|
|
79
|
-
const point = fixed.indexOf(".");
|
|
80
|
-
const wholeDigits = point === -1 ? fixed : fixed.slice(0, point);
|
|
81
|
-
const decimalDigits = point === -1 ? "" : fixed.slice(point + 1);
|
|
82
|
-
const padded = wholeDigits.padStart(pattern.integerMinDigits, "0");
|
|
83
|
-
const whole = pattern.useGrouping ? groupThousands(padded) : padded;
|
|
84
|
-
const decimals = trimOptionalZeros(decimalDigits, pattern.minDecimals);
|
|
85
|
-
return decimals ? `${whole}.${decimals}` : whole;
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
const isZeroText = (digits: string): boolean => Array.from(digits).every((char) => char === "0" || char === "," || char === ".");
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Render a number the way Excel's TEXT does for the common numeric format
|
|
92
|
-
* codes: `#,##0` grouping, `0.00` fixed decimals, `0.##` optional decimals, a
|
|
93
|
-
* literal prefix/suffix such as `$`, and a `%` that scales by 100.
|
|
94
|
-
*
|
|
95
|
-
* Returns `null` for a format code it does not render, so the caller can keep
|
|
96
|
-
* its own fallback rather than inventing a plausible wrong string.
|
|
97
|
-
*/
|
|
98
|
-
export const formatWithPattern = (value: number, pattern: string): string | null => {
|
|
99
|
-
if (!Number.isFinite(value)) return null;
|
|
100
|
-
const spec = parseNumberPattern(pattern);
|
|
101
|
-
if (!spec) return null;
|
|
102
|
-
|
|
103
|
-
const scaled = spec.isPercent ? value * PERCENT_SCALE : value;
|
|
104
|
-
const digits = renderDigits(Math.abs(scaled), spec);
|
|
105
|
-
// The sign follows the ROUNDED digits: -0.001 under "0.00" reads "0.00", not "-0.00".
|
|
106
|
-
const sign = scaled < 0 && !isZeroText(digits) ? "-" : "";
|
|
107
|
-
return `${sign}${spec.prefix}${digits}${spec.suffix}`;
|
|
108
|
-
};
|
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Excel-formula → JS-expression translation
|
|
3
|
-
*
|
|
4
|
-
* Pure string transforms that turn the Excel-operator form of an
|
|
5
|
-
* already-substituted expression (cell references resolved, functions replaced)
|
|
6
|
-
* into the JavaScript form handed to `new Function`, plus the character
|
|
7
|
-
* allowlists that gate that evaluation. No engine or evaluator state is
|
|
8
|
-
* captured — every function here is input → output only.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
/** Excel `^` exponentiation → JS `**`.
|
|
12
|
-
*
|
|
13
|
-
* Excel's `^` is left-associative (`2^3^2` = `(2^3)^2` = 64); JS `**` is
|
|
14
|
-
* right-associative (`2**3**2` = `2**(3**2)` = 512). This transform does not
|
|
15
|
-
* bridge that difference, so a chained `^` still evaluates the JS way — a known
|
|
16
|
-
* limitation tracked separately (#2359), pinned by the tests. */
|
|
17
|
-
export function caretToPow(expr: string): string {
|
|
18
|
-
return expr.replace(/\^/g, "**");
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** Move the "inside a string literal" marker across one quote character.
|
|
22
|
-
* Empty marker means "not in a literal"; otherwise it holds the opening quote. */
|
|
23
|
-
function toggleQuote(openQuote: string, char: string): string {
|
|
24
|
-
if (openQuote === "") return char;
|
|
25
|
-
return char === openQuote ? "" : openQuote;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
/** Excel `&` string concatenation → JS `+`, leaving any `&` inside a quoted
|
|
29
|
-
* string literal untouched. A quote toggles the "inside a literal" state only
|
|
30
|
-
* when it is not backslash-escaped. */
|
|
31
|
-
export function replaceConcatOperator(expr: string): string {
|
|
32
|
-
const chars = expr.split("");
|
|
33
|
-
const out: string[] = [];
|
|
34
|
-
let openQuote = "";
|
|
35
|
-
chars.forEach((char, index) => {
|
|
36
|
-
const escaped = index > 0 && chars[index - 1] === "\\";
|
|
37
|
-
if (!escaped && (char === '"' || char === "'")) {
|
|
38
|
-
openQuote = toggleQuote(openQuote, char);
|
|
39
|
-
}
|
|
40
|
-
out.push(char === "&" && openQuote === "" ? "+" : char);
|
|
41
|
-
});
|
|
42
|
-
return out.join("");
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/** Excel `=` equality → JS `==`, without disturbing `<=`, `>=` or `!=`. A single
|
|
46
|
-
* `=` is rewritten only when flanked by a non-`<>!` char on the left and a
|
|
47
|
-
* non-`=` on the right.
|
|
48
|
-
*
|
|
49
|
-
* Known limitations, pinned by the tests and tracked separately (#2359): the
|
|
50
|
-
* match consumes both flanking characters so replacements do not overlap
|
|
51
|
-
* (`5=6=7` → `5==6=7`, only the first rewritten); a `=` at either end of the
|
|
52
|
-
* expression has no left/right neighbour and is left alone (`5=` → `5=`); and a
|
|
53
|
-
* hand-typed `==` becomes `===` (its second `=` matches). Excel never emits the
|
|
54
|
-
* last two, so they only bite malformed input. */
|
|
55
|
-
export function rewriteComparisonEq(expr: string): string {
|
|
56
|
-
return expr.replace(/([^<>!])=([^=])/g, "$1==$2");
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/** Whether an expression contains only the characters an arithmetic evaluation
|
|
60
|
-
* may see: digits, ` + - * / ( ) . ` and spaces. Gates `new Function`. */
|
|
61
|
-
export function isSafeArithmetic(expr: string): boolean {
|
|
62
|
-
return /^[\d+\-*/(). ]+$/.test(expr);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/** Whether an expression contains only the characters a comparison evaluation
|
|
66
|
-
* may see: the arithmetic set plus ` < > ! = `. Gates `new Function`. */
|
|
67
|
-
export function isSafeComparison(expr: string): boolean {
|
|
68
|
-
return /^[\d+\-*/(). <>!=]+$/.test(expr);
|
|
69
|
-
}
|
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Spreadsheet Engine Type Definitions
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type { SpreadsheetError } from "./spreadsheet-errors";
|
|
6
|
-
|
|
7
|
-
/** A value as it is STORED in a cell and serialized to the workbook JSON. A
|
|
8
|
-
* formula error is never stored — it only exists as a computed result. */
|
|
9
|
-
export type StoredCellValue = number | string | boolean;
|
|
10
|
-
|
|
11
|
-
/** A value as the engine COMPUTES it: a stored value, or a formula error. */
|
|
12
|
-
export type CellValue = StoredCellValue | SpreadsheetError;
|
|
13
|
-
|
|
14
|
-
export interface SpreadsheetCell {
|
|
15
|
-
v: StoredCellValue; // Value or formula (formulas start with "=")
|
|
16
|
-
f?: string; // Format code (e.g., "$#,##0.00")
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface SheetData {
|
|
20
|
-
name: string;
|
|
21
|
-
data: SpreadsheetCell[][];
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface CalculatedSheet {
|
|
25
|
-
name: string;
|
|
26
|
-
data: CellValue[][]; // Calculated values
|
|
27
|
-
formulas: FormulaInfo[]; // Formula metadata
|
|
28
|
-
errors: CalculationError[]; // Any errors encountered
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface CellRef {
|
|
32
|
-
row: number;
|
|
33
|
-
col: number;
|
|
34
|
-
sheet?: string; // For cross-sheet refs
|
|
35
|
-
absolute?: {
|
|
36
|
-
// For $A$1 style
|
|
37
|
-
row: boolean;
|
|
38
|
-
col: boolean;
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export interface RangeRef {
|
|
43
|
-
start: CellRef;
|
|
44
|
-
end: CellRef;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export interface EvaluationContext {
|
|
48
|
-
currentSheet: string;
|
|
49
|
-
sheets: Map<string, SpreadsheetCell[][]>;
|
|
50
|
-
calculatedValues?: Map<string, CellValue>; // Cache
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export interface FormulaInfo {
|
|
54
|
-
cell: CellRef;
|
|
55
|
-
formula: string;
|
|
56
|
-
dependencies: CellRef[];
|
|
57
|
-
result: CellValue;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export interface CalculationError {
|
|
61
|
-
cell: CellRef;
|
|
62
|
-
formula: string;
|
|
63
|
-
error: string;
|
|
64
|
-
type: "circular" | "invalid_ref" | "div_zero" | "syntax" | "unknown";
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/** Per-calculation settings that change how cell CONTENT is read, as opposed
|
|
68
|
-
* to how the engine runs. Passed down explicitly so the engine stays pure —
|
|
69
|
-
* no ambient locale, no import-time state. */
|
|
70
|
-
export interface CalculateOptions {
|
|
71
|
-
/** Read an ambiguous `A/B/YYYY` date as day-first. Only affects dates whose
|
|
72
|
-
* two leading numbers are both 12 or under; anything else decides itself.
|
|
73
|
-
* See engine/date-locale.ts for how a locale maps onto this. */
|
|
74
|
-
preferDDMMYYYY?: boolean;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
export interface EngineOptions extends CalculateOptions {
|
|
78
|
-
maxIterations?: number; // For circular reference detection
|
|
79
|
-
enableCrossSheetRefs?: boolean; // Default: true
|
|
80
|
-
strictMode?: boolean; // Throw on errors vs. return 0
|
|
81
|
-
}
|
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
// Pure helpers behind the spreadsheet mini-editor's arrow-key
|
|
2
|
-
// navigation. Lifted out of View.vue so each rule can be unit-tested
|
|
3
|
-
// without spinning up Vue or a DOM.
|
|
4
|
-
|
|
5
|
-
export interface CellPosition {
|
|
6
|
-
row: number;
|
|
7
|
-
col: number;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
// Sheet shape we actually rely on — the mini-editor only reads
|
|
11
|
-
// `data` as a 2D array, so the type is intentionally loose to match
|
|
12
|
-
// what arrives from `JSON.parse(editableData.value)`.
|
|
13
|
-
export interface SheetLike {
|
|
14
|
-
data?: unknown[][];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function getArrowKeyOffset(key: string, row: number, col: number): CellPosition | null {
|
|
18
|
-
switch (key) {
|
|
19
|
-
case "ArrowUp":
|
|
20
|
-
return { row: Math.max(0, row - 1), col };
|
|
21
|
-
case "ArrowDown":
|
|
22
|
-
return { row: row + 1, col };
|
|
23
|
-
case "ArrowLeft":
|
|
24
|
-
return { row, col: Math.max(0, col - 1) };
|
|
25
|
-
case "ArrowRight":
|
|
26
|
-
return { row, col: col + 1 };
|
|
27
|
-
default:
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export function isWithinSheetBounds(sheet: SheetLike | null | undefined, row: number, col: number): boolean {
|
|
33
|
-
if (!sheet?.data) return false;
|
|
34
|
-
if (row < 0 || row >= sheet.data.length) return false;
|
|
35
|
-
const rowData = sheet.data[row];
|
|
36
|
-
if (!rowData) return false;
|
|
37
|
-
return col >= 0 && col < rowData.length;
|
|
38
|
-
}
|