A reference over 16,384 cells was cut to the sheet's used range when it was
read into an array. Next to a full-size operand the two no longer lined up:
SUMPRODUCT(D3:AZ400*B3:B400,(A3:A400=1)*(D1:AZ1=2)) gave #VALUE!,
D3:AZ400*B3:B400 filled with #N/A, and ROWS, COUNTBLANK and --(r="") lost
the blanks past the data. Excel keeps the reference's size.
Bounded references now keep their full shape up to one column's worth of
cells (1,048,576). References that run to the edge of the sheet (A:A,
A2:A1048576, several whole rows) and larger blocks still stop at the used
range, so whole-column formulas stay fast.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A category range two or more columns wide beside a column of values (year beside month, Excel's
multiLvlStrRef) was read as one flat list: 36 rows in two columns became 72 categories, the
series sat in the left half of the plot and the years and months shared one row of labels.
Saving wrote the range as a plain strRef, so Excel lost the two-level axis too.
- gridcraft_core::category_levels: the shape of a category range beside its values (levels are
columns when the points run down the rows, rows when they run along the columns; up to three
outer levels), shared by the chart and xlsx crates.
- ChartData.category_levels: the outer levels, nearest the labels first, one entry per category,
non-empty where a group starts. resolve fills it; a single column is unchanged.
- render: the outer levels in rows under the labels, a group's name centred under its categories
with a separator at each boundary; in bar charts in columns beside the labels.
- xlsx: <c:cat> written as multiLvlStrRef with the level caches (innermost first), so the axis
survives a round trip.
Tests in core (shapes), chart (resolve and layout for columns, bars and a row of values) and
xlsx (written XML, read-back, stability, a single column stays strRef). chart-parity, gaps § 4
and the ROADMAP progress log updated.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
`formulas.evaluate` (behind `gridcraft-cli eval` and MCP's `evaluate_formula`) turned any
formula that didn't parse into #NAME?, so `=SUM(1;2)` looked like an unknown function. It now
parses the way `cell.set` does (missing closing parentheses are added) and returns the same
errors: "There's a problem with this formula: expected `,` or `)`, found Semicolon", or the
wrong-argument-count message. An unknown function still evaluates to #NAME?, as in Excel.
Brings the branch up to upstream's main (180 more commits). One conflict,
and one thing the merge needed that was no conflict:
- docs/ui-parity.md: every line of both sides is kept, as upstream's own
merges keep them (its heading has three lines now); this change's
heading line and history row come first.
- crates/ui-egui/src/i18n/pt.rs: upstream's Portuguese catalog gained a
"Yesterday" of its own, with the same word ("Ontem"). The one this change
added is left out: a second arm for the same text is unreachable.
Gate: all eight steps, 1548 tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings the branch up to upstream's main (132 commits). Two conflicts, and
one thing the merge needed that was no conflict:
- crates/ui-egui/src/i18n/fr.rs: upstream rewrote the French catalog's
lines this change sits among. Upstream's lines are kept, and the ten new
entries are put back where they were, with the plain apostrophe the
catalog now uses.
- docs/ui-parity.md: both changes are of the same day. Both rows of the
history are kept, and the heading line names this change.
- crates/ui-egui/src/i18n/sr.rs: upstream gained a Serbian catalog, and
every string must be in every catalog. The ten new strings are given
their Serbian (Latin script, as the catalog is). Weeks, months and years
are shortened, as in the Russian catalog: their endings change with the
number.
Gate: all eight steps, 1415 tests.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Business Central's "Open in Excel" exports write booleans as
<c><f>TRUE()</f><v>1</v></c> with no t="b", so the cached value reads back
as the number 1 and the calc oracle reported every such cell as a
`number → bool` mismatch. A cached 1 or 0 now matches GridCraft's TRUE or
FALSE, and the cells are counted apart (`boolAsNumber` in --json, a note in
the text report) so a real number/logical disagreement stays visible.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A legacy array formula in a .xls file ({=FREQUENCY(...)}, {=TREND(...)}) is
stored as one ARRAY record plus a FORMULA record per cell of its range, each
with the cached result. The importer attached the formula to the anchor cell
but kept the cached values in the other cells as plain constants. On
recalculation the formula then found its range occupied and returned #SPILL!.
The .xlsx reader already drops those cached results; the .xls reader now does
the same once the formula is attached, and keeps the values when the formula
could not be imported.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>