craft-update, craft-window, craft-print and craft-ui move from craft-libs
c5a1cbfb to c7c84230, a branch with storytold/craft-libs#687 and #696
merged into craft-libs main.
- The check result window shows whenever the updater's status is not
idle, so the first start after an update reports that it installed or
why it failed. Restart to Update asks the app to quit once nothing is
unsaved.
- An update's close request quits the app (Quit asks about each unsaved
workbook), so every workbook window closes.
- on_exit starts the install helper of a ready update before the macOS
process exit.
Keep the complete reviewed matrix bound together so main never receives the known newline-row bypass. This merge tree is identical to the validated #3328 head 83670dfe3a.
rewrite_all_formulas borrows each tree with Formula::with_expr and calls
adjust_if_changed, so a formula the edit leaves alone is neither copied
nor printed, and a changed one is printed once. A legacy array whose
range moves while its formula stays the same still gets the new range.
Sheet rename and delete compare the adjusted tree with the old one
instead of printed text, and replace only formulas that changed. Delete
no longer rebuilds every formula in the workbook, so unrelated formulas
keep their array range and dynamic-array mark.
A stored non-canonical formula text is no longer re-printed by an edit
that does not change the formula; a test pins this.
Part of #3576
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CellStore::shift_rows, shift_cols, shift_cols_in_rows and take_range removed
and re-set every affected cell one by one. They now take whole row maps (or
the split-off part of one) out of the bands and put them back at their new
position, so a row shift costs a few map operations per row instead of a
band lookup, a remove and a set per cell. take_range does this when the
block reaches the last column.
Behaviour is unchanged, including blank cells left behind by get_mut, cells off the grid (they
are still dropped, and still clear the cell they land on), cells shifted off
the sheet, and copy-on-write: bands without moved cells stay shared with undo
snapshots. A differential test runs 12,000 random operations against the old
per-cell code and compares contents, length, iteration order and band sharing.
shift_rows_in_cols, and take_range on a block with columns to its right, keep
moving cell by cell: the row-map version measured no faster there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
adjust had no way to report that an edit does not affect a formula, so
callers copied, rebuilt and printed every formula in the workbook.
adjust_if_changed borrows the formula and returns None when nothing
moves. Its check reuses adjust_ref on each reference into the edited
sheet, on a copy without the sheet name, so it cannot disagree with
adjust. adjust_ref and shift_ref take the reference by value and no
longer clone sheet names.
An integration test keeps the previous implementation as an oracle
over every edit kind and a randomized property test.
Fixes#3576
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A Formula read from JSON had no cached tree (`expr` is not saved), so
every graph rebuild and every recalculation parsed each formula again.
Reading one now parses its text once and keeps the tree, as
Formula::new does. The saved text is kept as written.
Fixes#3577
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* Paste, Ctrl+Enter and Fill skip rows hidden by a filter (#1101)
Repeating one cell or row down a filtered selection (paste of a single
cell or row, Ctrl+Enter, Fill Down/Up/Right/Left) now leaves the rows an
applied filter hides alone, as Excel does. A taller pasted block still
goes straight down, and rows hidden by hand are still written, also as
in Excel. Each case was measured in Excel for Microsoft 365.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A2bJ3LxrZ6YnmppgrfwHwr
* Copying a filtered range takes only the rows showing (#1101)
A copied range with rows hidden by an applied filter is split into its
runs of showing rows and packed like a multi-area copy (formulas as
values), so pasting it inside GridCraft no longer brings the hidden rows
back. One run is copied as a plain range, keeping formulas. Cut still
moves every row. All as measured in Excel for Microsoft 365.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01A2bJ3LxrZ6YnmppgrfwHwr
* Find the rows a filter hides without rescanning every table per row
filtered_rows asks the sheet for its filter row spans once instead of
checking every table for each hidden row.
* Paste over filtered rows leaves their validation rules alone
When one row is tiled over filtered rows, paste_validations no longer
clears or copies rules on the rows the filter hides.
* Filtered copy packs links and validation with the rows showing
slide_together now moves hyperlinks and validation rules with their rows, and a
filtered whole-row copy records the packed rows as its selection so Insert
Copied Cells inserts only the rows copied.
* Group validation paste options and format the new tests
* Pack a filtered copy in one pass over notes, merges, links and spills
slide_together finds each entry's area by search instead of scanning every map
once per area, so a filter leaving thousands of separate runs no longer costs
runs times map size on every copy.
---------
Co-authored-by: Richard <richard1912@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: skylar <hi@skylar.cc>
The width of a prefix plus an ellipsis is not monotone when the text has a
line break, so the binary search could drop a label that fits. Also pass the
series index to plot_kind in the data table (upstream/main does not compile
without it).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Read only the cells the category levels use and cut the category dimension
at the per-series cap, so a multi-level axis keeps all its levels.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
When the edge sheet was already active nothing changed, so the strip was not
scrolled back to it. A Ctrl-click on the arrow now asks for the reveal itself.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The small-k product also needs (1 - p)^(n - k) to stay a normal number;
otherwise it returned 0 for BINOM.DIST(15,1100,0.5,FALSE) (exact
2.1367646606291324e-298) and the cumulative form built on it. Those cases
now use the logarithmic formula. Reference grid gets k = 10..25 at n = 1100.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A name such as align\@char{97}t reads as alignat to the parser but split at
the first } looked like another environment, so the pair limit was skipped.
Names that contain a backslash or brace, or do not close in the window, are
now refused like an unreadable count.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
COUNT never reads the running sum, so the zero-sum gate that keeps SUM
and AVERAGE additions in order does not apply to it.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>