Skip to content

Integration: GridCraft parity and bugfix combined testing - #2

Closed
FredApps wants to merge 360 commits into
integration/base-20261030from
integration/parity-bugfixes-20261030
Closed

FredApps wants to merge 360 commits into
integration/base-20261030from
integration/parity-bugfixes-20261030

Conversation

@FredApps

Copy link
Copy Markdown
Owner

Draft fork test integration, not an upstream merge request.

Base: storytold/gridcraft main at 75f7056.

Initial checkpoint combines these 50 PRs:

Original authored commits are preserved as parents. Changes are adapted to the current module layout. The reviewed main egui/winit fork pins are retained; no workflow changes are introduced relative to upstream main.

Still being reconciled, not excluded merely because of conflicts: storytold#722, storytold#1822, storytold#2268, storytold#2271, storytold#2278, storytold#2371. Additional candidate assessment is ongoing.

Combined CI has not run yet. This checkpoint is not claimed ready to merge.

Higgs32584 and others added 30 commits October 21, 2026 20:22
…ld#2183)

precedents() handled a range operator with non-reference operands by recursing into both operands and then letting Expr::walk descend into them again, so a left-associative chain of k operands cost 2^k (a 60-operand INDEX(...):INDEX(...) chain would hang on open). It is now a hand-written collector that visits every node once, growing the per-sheet bounding boxes of the enclosing range operator as it goes. Past a depth (4096) or node (1,000,000) bound it marks the formula dynamic (always recalculated) instead of recursing further.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- XML: at most 1M children per element, 16,384 per worksheet row (TooLarge).
- Workbook-wide cap of 16M cell elements.
- Array-formula ranges indexed by row (budgeted), not scanned per cell.
- <cols> writes share a 4M budget.
- At most 4096 sheets; duplicate names deduplicated with a HashSet.
- Pivot: field lists capped, repeated fields used once, labels borrowed.
- CSV: fields past MAX_COLS dropped while reading the record.
- At most 4M shared strings.
- Relationships looked up by id through a HashMap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
link.open is reachable by the control channel, CLI, MCP and workbook
scripts, so it could open https://attacker/?d=<cell data> without the
user seeing it. By default (LinkPolicy::Confirm) it now refuses web and
e-mail addresses with an error and opens no dialog; references still
navigate and other schemes stay refused. A host may opt in with
LinkPolicy::Allow (Rust API only, no command sets it).

The UI's Open Hyperlink prompt no longer runs a command on OK: it calls
Session::open_confirmed_link with the address it showed, kept in a
private field, so ui.dialog.confirm can't press OK and ui.dialog.set
can't swap the address behind the prompt text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Explorer is now spawned as %SystemRoot%\explorer.exe (falling back to
windir, then C:\Windows; only local drive paths are accepted), so an
explorer.exe planted next to the app or in the CWD can't run. Space, ",
comma and ^ are percent-encoded before Explorer parses its command line.
macOS uses /usr/bin/open; a target starting with - or / is refused on
every platform so it can never become an opener option.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
std::time::Instant::now is a disallowed method (clippy.toml: traps on wasm).
The regression test only needs to finish; the old 2^k walk would never return.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`Os::MacOs` -> `Os::Mac` (clippy::enum_variant_names). Also give the
desktop-theme watcher's non-Linux `Link` a non-unit type so
clippy::let_unit_value stops firing on Windows/macOS.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ytold#1077)

Increase/Decrease Decimal and the ribbon's other number formats widened the format but never the
column, so the number turned into ####. A column nobody sized by hand now widens to the widest
number in the target, as observed in Excel 16.0: AutoFit counts as automatic, Column Width (or
dragging the border) as by hand; hidden columns, text, General numbers, merged and shrink-to-fit
cells don't widen; columns never narrow.

LineInfo::custom now marks columns set by hand too. XLSX reads and writes it as Excel does
(customWidth without bestFit = by hand; automatic widths get bestFit and customWidth), and .xls
reads COLINFO's fUserSet and fBestFit the same way.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts: AutoSum's and Recommended Charts' menu-style cases in
`run_or_alert` were both added after Export as PDF (kept both);
docs/ui-parity.md keeps main's rows with this change added, and main's
two Point mode rows are one again.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
File › Open read a `sep=|` first line as data and split the file by the detected delimiter, so a
`|` file stayed one column. When the delimiter is detected, a first line `sep=` plus one character
now names it and isn't data, as observed in Excel 16.0: spaces ignored, any case, the line may be
quoted; `sep=` alone (or a non-ASCII character) splits nothing; two characters or the line anywhere
but first is data; `sep="` splits at the quotes. A given delimiter wins. Saved again, the file keeps
`,` `;` or tab; any other named delimiter is written as commas, so it reopens in the same columns.

parse_records takes an optional delimiter and quote character for the two cases that split or
quote nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Main now carries the AutoSave labels and the per-language catalog modules; this keeps them and
re-applies the branch's tab names (Language::tr_tab, Tab|name keys) on top. Catalog conflicts are
resolved key by key: Russian keeps this branch's wording, other languages keep main's.
GridCraft aborted while File › Open or the save picker was up (storytold#1911,
storytold#2042). The blocking rfd picker runs NSSavePanel's modal loop inside
winit's event handler; an event delivered in that loop (a Finder drag)
re-enters winit, whose guard panics in a callback that can't unwind,
and the process aborts with the user's work.

On macOS the pickers are now begun as sheets with rfd's
AsyncFileDialog: AppKit builds the panel during the frame, as it
requires, a worker thread waits for the completion handler, and the
answer comes back over a channel. The frame loop keeps running, so
nothing re-enters.

- ui-egui: `Services::pick_async` (with `PathPick`) and
  `SheetApp::pick_path`, which runs the same follow-up (open, Save As,
  export PDF/CSV/HTML, Save a Copy) at once with a blocking picker or on
  the frame the answer arrives. One sheet at a time; a cancelled or
  dropped request frees the picker. Windows and Linux keep their
  blocking pickers.
- Insert Picture uses the existing `pick_picture_async` inbox on macOS,
  and the dialog now repaints while it waits for it.
- The open/save/picture file types live in one place for both pickers.

VERIFIED
- New test an_asynchronous_picker_runs_its_action_when_the_answer_arrives;
  the existing File tests (blocking pickers) pass unchanged.
- Release build on macOS 27 (aarch64), File › Open through the control
  channel: before, the panel is an app-modal window and the main thread
  stops answering; after, the panel is a sheet and the UI keeps
  answering while it is open.
- `cargo xtask ci` passes all 8 steps; 2120 tests.

NOT VERIFIED
- The abort itself needs a Finder drag over the panel and was not
  reproduced by script; a manual check on a Mac is pending.
@FredApps FredApps closed this Oct 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.