24 Commits
Author SHA1 Message Date
neilcawse 95012e03b4 UI: a cut's text pastes again once the clipboard has held other text 2026-10-12 21:19:05 -04:00
neilcawseandClaude Opus 5.5 317ec4388c XLS: array formulas fill their range on recalculation
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 <[email protected]>
2026-10-12 20:52:03 -04:00
neilcawseandClaude Opus 5.5 0485e7f4ba Packaging: offer GridCraft for .xlsb and .ods files on macOS
GridCraft imports XLSB (#101) and ODS (#100) files, and #321 added both
to the Linux and Windows "Open with" lists, but the macOS Info.plist
still declared only .xlsx, .xlsm, .csv and .tsv, so Finder's Open With
didn't offer GridCraft for them. Add two CFBundleDocumentTypes entries
(Editor, rank Alternate) with the UTIs macOS reports for these files,
plus an imported UTI for .xlsb like the .xlsm one. A test now checks
that the macOS and Windows packaging offer the same file types.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 20:52:03 -04:00
neilcawse 4d654c5185 UI: Cmd/Ctrl+V doesn't re-paste a pasted cut's clipboard text either 2026-10-12 20:51:39 -04:00
neilcawse fd1ed6649b UI: Paste doesn't re-paste a pasted cut's clipboard text; Paste keytip (H,V,P) pastes the system clipboard 2026-10-12 20:51:39 -04:00
neilcawse cc65915d16 UI: keytip Copy/Cut publish to the system clipboard; ribbon Paste keeps a newer programmatic copy 2026-10-12 20:51:39 -04:00
neilcawseandClaude Opus 5.5 fa1dcd73e2 UI: Paste button and menus paste the system clipboard; freeze regression test (#273)
In 0.5.0 a click anywhere on the Home ribbon's Paste button (label,
icon or dropdown arrow) froze the app for good. home() called
paste.secondary_clicked() inside ctx.data_mut(); after any click egui
answers that with ctx.input(), which takes the context lock the closure
already holds, so the UI thread waited on itself. Sampling the hung
0.5.0 process on macOS shows the main thread parked in
RawRwLock::lock_exclusive_slow under Context::write <- data_mut <-
ribbon::home. #272 moved the query out of the closure; it landed after
the 0.5.0 release, which is the build both reporters used.

No test ever clicked the Paste button itself, which is how this
shipped. The new test clicks it, then opens its menu with a secondary
click and picks Paste Values, on a worker thread with a timeout so a
regression fails instead of hanging the run. With #272 reverted it
fails (egui's debug lock check panics after 10 s); with it, it passes.
The Paste button gets an accessible "Paste" name so the test (and
screen readers) can find it, the same way Pictures and Border presets
are named.

With the freeze gone, the button still did not do what it says: it
pasted GridCraft's own copy only (GridState::system_clipboard was never
assigned), so text copied in another app gave "Nothing to paste." The
ribbon button, its options menu's Paste, the cell and header context
menus and the macOS Edit menu now read the system clipboard through a
new paste_text host service and paste it like Cmd/Ctrl+V does, falling
back to GridCraft's own copy when there is no text (or no service, as
on the web). Asking egui for the clipboard instead (RequestPaste) was
tried and stalled the macOS event loop until the next input event.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 20:51:39 -04:00
neilcawse ef1cd55d24 PivotTables: a collapsed item puts its value captions in the Values column (outline, tabular) 2026-10-12 16:29:02 -04:00
neilcawse e5b12dab06 Functions: COUNTIF family counts the 255 criterion limit in UTF-16 units, as in Excel 2026-10-12 16:09:52 -04:00
neilcawseandClaude Opus 5.5 aa2a580626 PivotTables: values on rows review fixes (Total header, Σ Values last in Columns)
With values on rows and no column field, the header cell above the data
column reads "Total", as Excel saves it (tabular checked against a real
file).

Σ Values in Columns always comes after the last column field: the column
layout doesn't place it anywhere else, so `pivot.moveField` no longer
stores a position there, the reader ignores where `-2` sits in colFields
and the writer puts it last.

`pivot.changeSource` removes dropped row and column fields one by one so
Σ Values keeps its place ahead of the field it preceded.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 15:05:59 -04:00
neilcawse 4c2bcd637c Functions: *IFS with two or more criteria pairs match the first 255 characters of a longer criterion, as in Excel 2026-10-12 14:34:18 -04:00
neilcawse caf3193261 Functions: *IFS with two or more criteria pairs accept criteria over 255 characters, as in Excel 2026-10-12 14:19:29 -04:00
neilcawseandClaude Opus 5.5 5524e2520a Functions: COUNTIF family returns #VALUE! for criteria over 255 characters
Excel rejects a text criterion longer than 255 characters in COUNTIF(S),
SUMIF(S), AVERAGEIF(S), MAXIFS and MINIFS with #VALUE!; GridCraft matched
it anyway. Found by the calc oracle on an Excel-saved report where a
268-character concatenated criterion caches #VALUE!.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 14:19:29 -04:00
neilcawseandClaude Opus 5.5 1f7fd07c0d PivotTables: values on rows (Σ Values in Rows, dataOnRows)
With two or more value fields the Σ Values pseudo-field can sit in Rows
as well as Columns, at any position among the row fields. The model
records it (`PivotTable.values_on_rows`, `values_position`) and the
engine lays it out like Excel in compact, outline and tabular form: one
row item per value field at the Σ Values level, per-value subtotals
("East Sum of Sales") and grand totals ("Total Sum of Sales").

XLSX reads `dataOnRows` and the `-2` field's place in rowFields or
colFields and writes them back; the "values on rows are shown on
columns" warning is gone. `pivot.moveField` moves "Σ Values" between
Rows and Columns or within one, axis field moves keep it in place, and
the PivotTable Fields pane lists it.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-12 12:06:30 -04:00
neilcawseandClaude Opus 5.5 828efab385 Engine: command-sequence fuzz harness (undo/redo, XLSX round-trip, full-recalc invariants) and the bugs it found
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:39:31 -04:00
neilcawseandClaude Opus 5.5 8ea2342703 Charts: read, write and draw trendlines
Model: Series.trendlines (Vec<Trendline>, serde default) with kind
(linear, exponential, logarithmic, polynomial 2..6, power, moving
average 2..255), name, forward/backward forecast, set intercept,
equation / R² display and line colour, width and dash.

XLSX: read and write <c:trendline> in CT_Trendline child order, placed
after dLbls and before the data references in line, bar, area, scatter,
bubble and stock series; unknown types are skipped.

Chart: least-squares fits (transformed for exp/log/power, skipping
non-positive values; polynomials via normal equations on centred,
scaled x with partial pivoting), moving averages, forecasts that widen
the category or x axis, clipping to the plot, equation and R² labels and
legend entries ("Linear (Sales)"). Degenerate data draws nothing.

Engine: chart.addElement element="trendline" adds (type, series, order,
period, forward, backward, displayEquation, displayRSquared) or removes
trendlines; document.inspect lists each chart's trendlines.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:39:31 -04:00
neilcawseandClaude Opus 5.5 53690bace0 Functions: oracle fixes - MODE/VALUE/SUMPRODUCT text args, ADDRESS [Book]Sheet, Excel-style lookup bisection, SUBTOTAL skips filtered rows (table filter criteria kept)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:39:31 -04:00
neilcawseandClaude Opus 5.5 9ce910f87f Formulas: trim-reference operators (A1:.E10, A1.:E10, A1.:.E10); read unbracketed [[#This Row],Col]
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:39:31 -04:00
neilcawseandClaude Opus 5.5 fdb74c27dd Calc: iterative calculation for circular references (Maximum Iterations, Maximum Change)
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:39:31 -04:00
neilcawseandClaude Opus 5.5 eecfa4074d File formats: CSV/TSV export writes displayed values; templates and .xlsm saved with their own content types
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:39:31 -04:00
neilcawseandClaude Opus 5.5 13afa8c773 XLSX: read cell formulas via file→model conversion (_xlpm), keep line breaks in table columns, allow leading backslash in table names
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:39:31 -04:00
neilcawseandClaude Opus 5.5 f7bc97b378 CLI: check subcommand - cached-value calc oracle and XLSX round-trip fidelity on any corpus
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:39:30 -04:00
neilcawseandClaude Opus 5.5 ba830cacf8 CI: run cargo xtask ci on pull requests and pushes to main
Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:39:30 -04:00
neilcawseandClaude Opus 5.5 737999f182 Numfmt: fraction literals and zero fractions, ? pads thousands separator, TEXT booleans through text section
Checked against Excel-cached TEXT() results in number- and text-format test workbooks: 226 of 352 TEXT cells mismatched before, 0 after.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-11 16:39:30 -04:00