A workbook opened with GridCraft scripts (`_sc_scripts`) starts untrusted:
automate.runScript refuses them until the user trusts the workbook for the
session (Macros dialog > Trust Scripts, i.e. automate.trustScripts) or the
caller passes `trusted: true` for that one run. Workbooks without scripts,
and scripts written in the session, are unaffected.
Whatever the trust, a script whose steps reach outside the workbook
(file.*, link.*, or any `path` param: Get Data, From Text/CSV, Insert
Picture) is refused before any step runs.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Clipboard HTML is read only as far as the pasted text's shape (clamped
to the sheet) and at most 1,000,000 cells; colspan/rowspan are clamped
to the sheet extent before expanding, and rowspan now keeps later rows
in their columns.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
zip2 (binary operators, TEXT), zip3 (array IF) and MAP now compute the
broadcast cell count with checked multiplication and return #NUM! when it
exceeds MAX_CELLS, instead of allocating (abort) or wrapping on wasm32.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Parenthesised negatives were parsed by recursing once per level, so runtime
text such as REPT("(",32767)&"1"&REPT(")",32767) overflowed the stack. Strip
the parentheses in a loop, toggling the sign per level; results are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every area, array and value of one SUBTOTAL/AGGREGATE call is charged against MAX_CELLS before any cell is read; past it the call returns #NUM!. A value stored in XFD1048576 no longer makes SUBTOTAL(9,A:XFD) read ~17 billion cells into a vector.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
LINEST, LOGEST, TREND and GROWTH built k x k normal-equation matrices
(plus two copies in sym_inverse, O(k^3)) with k taken straight from the
known_xs shape, so one formula could allocate gigabytes and abort on open.
More than 255 predictors is now #VALUE!, and more than 1e9 rows x k^2
multiply-adds is #NUM!, both checked before any allocation.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The pane ran `pivot.fields` on every frame while the active cell was in a
PivotTable report. That command reads the whole source list and collects
distinct labels for every field, so a 250,000 x 20 source made each frame
cost ~2.4 s and the window looked frozen (#2113).
Keep the answer in GridState, keyed by (doc uid, revision, pivot), the way
Page Break Preview keeps its pages. Any undoable edit, undo or redo bumps
the revision and drops it.
Known limit: Calculate Now / Calculate Sheet don't bump the revision, so
volatile formulas in the source can leave the tooltip samples stale until
the next edit; the field list itself only changes through undoable edits.
VERIFIED
- New test pivot_fields_pane_reuses_its_field_list_until_the_workbook_changes
fails without the change and passes with it.
- Release build, 250,000 x 20 synthetic source, pane open (ui.inspect):
main ~2,400 ms per frame throughout; this branch 2,528 ms for the first
frame, then 0.4-0.9 ms.
- `cargo xtask ci` passes all 8 steps on macOS with the separate clippy
fix from #2141 applied locally (main fails clippy on macOS without it).
NOT IN THIS CHANGE
- The report is still empty after opening a file saved with
refreshOnLoad="1" (second part of #2113).
- `pivot.fields` itself is unchanged; the first frame still pays one scan.
長形4号 is a 90×205 mm envelope (90×225 mm is 長形40号, a different one), so
the paper table gave it a height 20 mm too tall — and XLSX paper id 74, the PDF
MediaBox and the print preview all followed it. Use 205 mm (581.10 pt) and pin
the corrected size with a dedicated assertion; the ROADMAP progress row is
corrected too. 長形40号 is not added: it is a different envelope.
The Page Layout ▸ Size menu stopped at `B5 (ISO)` and never offered
`B6 (ISO)`, which the engine and the command docs support; add it.
Japanese Excel's B4/B5/B6 are the JIS sizes (257×364, 182×257 and 128×182 mm),
but Page Setup's `b4`/`b5` were the ISO ones (250×353, 176×250): a Japanese B5
worksheet printed 8 mm narrow. `known_paper` now reads `B4`/`B5`/`B6` as JIS and
keeps the ISO sizes reachable as `B4 (ISO)`/`B5 (ISO)`/`B6 (ISO)`. This also
fixes the XLSX round-trip, whose `paperSize` ids 12/13 are the JIS sizes.
Adds the Japanese papers Page Setup lacked: Japanese Postcard (はがき,
100×148 mm), Japanese Double Postcard (往復はがき, 148×200 mm), Japanese
Envelope #3/#4 (長形3号/4号, 120×235 and 90×225 mm), Japanese Envelope Kaku
#2/#3 (角形2号/3号, 240×332 and 216×277 mm), A6, and aliases such as
`postcard`, `chou #3`, `kaku #2`; the xlsx paper table gains ids 42 (ISO B4) and
43/69–74/88 so these names survive save and reopen (the ISO B5/B6 names have no
ECMA-376 paper id and stay engine-side).
The pageSetup/pageLayout.size parameter docs list the names, the Page Layout
Size menu lists them, and tests pin the point sizes, the PDF MediaBox for A4
(unchanged), B5, postcard, double postcard and kaku #2, landscape and
fit-to-page, the unknown-name Letter fallback, and the XLSX round-trip.
Docs: docs/target-app-parity.md print row + revision history, and ROADMAP.md
progress log, dated 2026-10-21.
si_body returned as soon as no valid run was written, so a shared string with a
<phoneticPr> and no <rPh> — a shape §18.4.8 allows — lost its properties on
save even though the reader keeps them. The runs and the properties are now
written independently: valid runs as before, then the valid <phoneticPr> when
the workbook has one; with neither, the <si> is exactly the plain t_el output.
property_only_shared_string_survives_open_and_save covers read, save and
reopen, and the si_body damaged-data test now expects the property-only output.
The write-time checks the reader also runs (sb/eb clamped into the UTF-16
length, empty readings skipped, type/alignment filtered through the shared
PHONETIC_KINDS/PHONETIC_ALIGNMENTS) were already in the head; the tests above
pin them, so no further change there.
Docs: the .xlsx row and the revision history now state that the shared string
table holds each text once, so duplicate <si> items with the same text but
different readings keep only the first reading (reported as a warning on open),
and that a <phoneticPr> without runs is kept. Both parity docs' "Last updated"
headers match their 2026-10-20 revision rows; the earlier rows keep their dates.
## CI Validation
`cargo xtask ci` completed successfully.
<details>
<summary>Build Warning</summary>
```text
warning: variants `At` and `Unknown` are never constructed
--> crates\ui-egui\src\backstage.rs:416:5
```
This warning appears to be pre-existing and is unrelated to the changes introduced in this PR. As it falls outside the scope of the current work, no changes were made to address it.
</details>
The build completed successfully, including the `wasm32-unknown-unknown` target checks.
```text
wasm32-unknown-unknown check (gridcraft-calc, gridcraft-chart, gridcraft-core, gridcraft-engine,
gridcraft-formula, gridcraft-functions, gridcraft-model, gridcraft-numfmt,
gridcraft-pdf, gridcraft-web, gridcraft-xlsx): ok
```
<details>
<summary>CI Parity</summary>
```text
CI summary:
ok fmt
ok clippy
ok test
ok assets
ok layers
ok wasm
```
</details>
- English (and Auto where offered) stays first as the source language and fallback; the rest sort by native name with accents folded, so Čeština is under C and Magyar under M; Cyrillic and CJK names follow the Latin ones
- The list used the registry order, which is the order the languages were added
tr reads | as a context separator, so translating the Open Hyperlink note
cut a URL with a pipe down to the part after it. Notes carry user data
(URLs, file and sheet names); one with a pipe is now drawn verbatim.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JadgGo4G2GHaAMNb3qyygk