Skip to content

Resolve current-main integration of unsaved-workbook quit protection (#496) - #1

Closed
scottcampbelldata wants to merge 414 commits into
prxnvt:pr/quit-promptfrom
scottcampbelldata:fix/pr-496-main-integration
Closed

scottcampbelldata wants to merge 414 commits into
prxnvt:pr/quit-promptfrom
scottcampbelldata:fix/pr-496-main-integration

Conversation

@scottcampbelldata

@scottcampbelldata scottcampbelldata commented Oct 13, 2026 •

Copy link
Copy Markdown

Resolves the current-main roadmap/UI documentation conflicts in storytold#496, retaining the author's close/quit protection entries alongside incoming upstream changes and current Page Layout details. The original unsaved-workbook prompt implementation and regression suite remain intact with main's current startup, printing and macOS event handling.

This integrates original head 3feb5033e73538e80e8d7a28a69c773c80cd359c with main 84acf6293b1dc1fc09837524e035d674eb4d6741. All eight cargo xtask ci gates passed on Ubuntu 24.04 / Rust 1.99.0, including synthetic close/save/cancel regressions, strict Clippy and wasm compilation. Exact staged tree 0baead2038c777bd8a9f3e43334b133087522d92 matched the validation checkout; both remained unchanged.

The existing Save Changes prompt was rendered and reviewed from that same tested tree under the shared validation lock. This is automated/offscreen evidence; actual macOS Quit/Dock and Windows desktop close behavior have not been independently exercised. Multi-window storytold#171 remains separate and should continue coordinating with this flow.

marcoarnulfo and others added 30 commits October 13, 2026 02:45
…ion test (storytold#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. storytold#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 storytold#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 <noreply@anthropic.com>
GridCraft imports XLSB (storytold#101) and ODS (storytold#100) files, and storytold#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 <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>
Following Excel's AutoFill series:
- a single time steps by an hour (9:00, 10:00, 11:00); it was copied.
- Qtr3 and Quarter3 wrap after 4 like Q3; they counted on to Qtr5.
- 1st Period continues as 2nd Period, 3rd Period; it gave 2st, 3st.
- a source without a common pattern (Product 1, On backorder) continues
  each cell's own series; it was copied as is.
A growth series with a step below 1 stopped at once when given a stop
value, since the stop was only checked for rising series. It now bounds
the series in the direction it runs.
A formula such as =A1+A2+…+A500 nests one level per operator, and the
evaluator recursed once per level, so past its depth limit (about 400
operators) the result was #CALC!. Excel allows such formulas up to its
8,192-character limit. Chains of arithmetic, comparison and & operators
are now folded in a loop.
home.manageRules took the number of rules once, before deleting, so
{"delete":0,"moveUp":1} with two rules swapped past the end of the
shortened list and panicked (index out of bounds; the session's guard
kept the workbook). The count is now taken after each step.
Merge & Center and Merge Cells kept only the upper-left value, but
Merge Across left the other values of each row in place, hidden under
the merge and still counted: with 1 2 / 3 4 in A1:B2, SUM(A1:B2) stayed
10 after Merge Across, where Excel gives 4. Each row's merged area now
keeps only its first value, as the other merges do.
 storytold#864)

[@col] is only Excel's display shorthand. Written verbatim into cell formulas and calculatedColumnFormula, it made Excel repair the file and drop the tables.
…part

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>
echelon and others added 27 commits October 13, 2026 01:07
# Conflicts:
#	crates/ui-egui/src/grid.rs
#	crates/ui-egui/src/tabs.rs
#	docs/ui-parity.md
…r track; drop the unused Color32 import

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	crates/ui-egui/src/dialogs.rs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	docs/ui-parity.md
# Conflicts:
#	crates/ui-egui/tests/interaction.rs
# Conflicts:
#	crates/xlsx/src/tests/passthrough_tests.rs
Brings the branch up to upstream's main (59 more commits). One conflict:

- docs/ui-parity.md: every line of both sides is kept, as upstream's own
  merges keep them, in the order of their dates, newest first (upstream
  has a change of the 13th now, which goes above this one of the 12th).

Gate: all eight steps, 1580 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	ROADMAP.md
#	docs/gaps.md
#	docs/target-app-parity.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@prxnvt

prxnvt commented Oct 14, 2026

Copy link
Copy Markdown
Owner

Thanks for taking the time to do this, and for the careful write-up. I looked it over: the code is unchanged from storytold#496 and the two doc conflicts are resolved the way I would have done it.

That said, I am not going to merge it here. This fork's branches only exist to back the open upstream pull requests, and I would rather leave them as they were submitted and let the storytold maintainers merge main into them as they have been doing. So I will leave this one unmerged. If it is useful to them, feel free to point to it from storytold#496.

Appreciate the effort all the same.

@scottcampbelldata scottcampbelldata closed this by deleting the head repository Oct 22, 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.

8 participants