A `;`-separated CSV (the usual CSV where `,` is the decimal separator) was
rewritten with `,` on save: read_csv sniffed the delimiter but the result was
dropped, and save_bytes passed a hard-coded b','. file.save, Save As .csv,
file.exportCsv, file.saveBytes and gridcraft-cli convert all went through it.
The workbook now remembers the separator it was read with
(Workbook::csv_delimiter, set by read_csv) and a CSV save writes it back.
Workbooks not read from CSV, and .tsv/.txt sources saved as .csv, keep `,`.
Regression tests: `;`, `,` and tab .csv round trips byte for byte, new and
.xlsx-origin workbooks save with `,`, and open-by-path + edit + file.save on a
`;` file keeps `;` (also through exportCsv and saveBytes).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
gridcraft_xlsx::check_encryption parses EncryptionInfo without a
password, so RC4, Extensible, certificate-only and rights-management
(IRM) files, and damaged ones, get their real message instead of a
password prompt first.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Without a password, file.open returns an error carrying
io::PASSWORD_REQUIRED (never a dialog). A decrypted workbook gets no save
path, so Save never replaces the encrypted original.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Restores "Counts whole years, months or days between two dates." and
appends the units, in the multi-line form rustfmt gives a long entry.
Drops the 256-argument test case: the parser already refuses more than
255 arguments, so it never reached the entry check. (993c108's message
says entry "also refuses" them; it already did.)
Claude-Session: https://claude.ai/code/session_01BGBBGBC2gRpQ7GDuwQ6WPt
- Action::Pending now consults the engine catalog at fire time: silent
no-op while the command is missing, runs it once it exists - no table
edit needed when a future feature lands.
- Stripped stray backticks from 7 pending target ids so catalog lookup
can ever match them.
- gridcraft-engine: doc(hidden) register_test_command hook so UI tests
can prove the go-live path without touching production code.
- Tests: pending_sequences_are_silent_no_ops (no dialog/toast/journal),
pending_sequences_go_live_when_the_command_lands (fake command fires
via its reserved sequence), every_sequence_terminates_without_panicking
(all 413 sequences pressed end-to-end).
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 <noreply@anthropic.com>
FnSpec::accepts holds the arity check (including Excel's 255-argument
limit) that functions::call and the entry check both use, so entry also
refuses 256 arguments to a variadic function. The evaluator's own check
in calc is unchanged.
Claude-Session: https://claude.ai/code/session_01BGBBGBC2gRpQ7GDuwQ6WPt
XLSX headerFooter kept only oddHeader/oddFooter: a workbook whose header is on a
different first page (differentFirst + firstHeader) or on even pages showed no
header, and saving dropped it. The model now keeps the first/even headers and
footers and the differentFirst / differentOddEven / scaleWithDoc / alignWithMargins
flags; XLSX reads and writes them; the PDF and the Page Layout view use the right
pair per page; in-place editing edits the page's own pair, and the Header & Footer
tab has Different First Page and Different Odd & Even Pages. pageLayout.headerFooter
takes the variants and a page: odd|even|first for one-section edits.
Insert › Header & Footer now switches to Page Layout view and edits the first
page's centre header, as in Excel, instead of opening the codes dialog.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>