Commit Graph
474 Commits
Author SHA1 Message Date
Harrison BoothandClaude Sonnet 5.5 70fc5e1c78 Merge main into fix/workbook-metadata-part
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-16 13:13:22 -05:00
webdevtodayjason ea3ef8faf7 Charts: honor box and whisker quartile methods
Keep exclusive and inclusive quartiles per series when drawing and saving XLSX, and expose the setting through chart.set.

Fixes #1453
2026-10-16 12:37:27 -05:00
midasmatterhorn 4ca967e114 Auto-fit: skip merged cells when computing row heights 2026-10-16 16:30:18 +03:00
Marco Arnulfo 295f7dde33 Fill handle: right-drag opens the fill menu (Copy Cells, Fill Series, Fill Formatting Only, Fill Without Formatting, Series…) 2026-10-16 14:20:48 +02:00
midasmatterhorn 86b124fca6 Charts: build multi-area series from every selected area 2026-10-16 15:17:19 +03:00
Bryan MarkandClaude Opus 5.5 a09d3f6934 Memory: undo steps share picture and macro bytes instead of copying them
Every edit copies the workbook for its undo step (copy-on-write: sheets
and 64-row bands of cells are shared). A floating picture's bytes
(Image.data) and the VBA project (Workbook.vba_project) were plain
Vec<u8>, so each edit on a sheet with a picture, and every edit in a
workbook with macros, kept another full copy of them, up to the 100
steps of the history. They are Arc<Vec<u8>> now, as cell pictures
already are, with base64 helpers for serde (b64::shared, b64::opt). JSON
and XLSX are unchanged.

Measured on macOS 27 (Apple M3 Max), gridcraft-cli, kernel peak
footprint: a 19 MB picture and 50 cell edits on its sheet, 992 -> 22 MB;
10 edits, 215 -> 21 MB.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 14:26:53 +09:00
Marco Arnulfo efc8171609 Keyboard: Ctrl+6 hides and shows objects, saved as workbookPr showObjects 2026-10-16 07:03:58 +02:00
derechopiano 214a67d53c feat(#1622): add Advanced Filter ribbon dialog 2026-10-16 05:57:03 +01:00
derechopiano 3379fb63a5 fix(#1031): constrain Advanced Filter copy output 2026-10-16 05:33:52 +01:00
NetterSilverandClaude Haiku 5.5 ad4422f5f6 Reject unsupported flip param in arrange.rotate
Found while testing crates/engine/src/cmd/extra.rs:664 (command doc at extra.rs:114)

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-16 05:59:22 +02:00
Harrison O'Connor-Hoover 79e85c1b80 fix(edit): recalculate every cell changed by large clears (#1032) (#1128) 2026-10-15 13:38:13 -06:00
Marco Arnulfo 7a352cee09 Grid: an armed Format Painter paints whole rows and columns from their headers (refs #1211) 2026-10-15 21:22:40 +02:00
Marco Arnulfo c0d6f290e7 Name Manager: preview a name over several ranges through parse_name 2026-10-15 21:20:52 +02:00
NetterSilverandClaude Haiku 5.5 2bb222aea6 Sort and de-duplicate data.textToColumns fixed-width breaks
Found while testing crates/engine/src/cmd/data.rs:826

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-15 17:48:47 +02:00
midasmatterhorn f8cbfb7cb6 engine: sort and remove duplicates move the rows' notes and hyperlinks
do_sort moved cells, formulas, cell pictures, row heights and floating
object anchors through the sort permutation, and remove_duplicates moved
cells and pictures through the compaction, but neither touched
Sheet::comments or Sheet::hyperlinks (keyed by CellRef): a note that
described one record stayed behind and annotated whatever landed on that
row — 'late payment' on a paid invoice — and a note on a row removed by
Remove Duplicates annotated blank cells. Sheet::comments/hyperlinks are
exactly the range-scoped side data that row and column inserts already
remap and edit.clear already clears, so the omission looks unintended.

Both maps now travel through the same permutation the cells use (the
sort collects before re-inserting, so a swap cannot resurrect an entry
from a destination that is also a source; Remove Duplicates compacts the
kept rows and drops the rest).

Regression test: sort_and_remove_duplicates_move_the_rows_notes_and_links
— on main the note and link stay on their old cells; with the fix they
travel with their record.
2026-10-15 17:40:18 +03:00
midasmatterhorn e6c0f92d59 engine: a cut takes the source's notes, hyperlinks and merged areas with it
A cut removed the source's cells and pictures and then re-created the
block's merged areas at the destination and copied its notes there — but
removed nothing from the source, so one merged block became two (an
empty one left behind) and a note ended up on both the old and the new
cell. Excel moves both and leaves the source clean.

The cut now clears the source range's comments, hyperlinks and merges
the same way edit.clear does. A second gap in the same path: paste never
transferred hyperlinks at all, so clearing the source would have lost
them — paste now carries hyperlinks (in What::All and a new
What::Hyperlinks, so edit.pasteSpecial {"what":"hyperlinks"} works
like "comments").

Regression test: a_cut_moves_the_sources_notes_hyperlinks_and_merges —
on main the old merge stays behind and the note is on both cells; with
the fix the merge, note and link all move and the source is clean.
2026-10-15 17:30:35 +03:00
midasmatterhorn 18fecf8e8c engine: pageLayout.printTitles only touches the axes it is given
print_titles read both rows and cols as Options and assigned both
unconditionally, so {"rows":"2:2"} set title_cols = None and
{"cols":"A:A"} set title_rows = None: the repeated rows or columns
disappeared from every printed page with no message, and there was no
way to set or clear just one axis. The sibling pageLayout.pageSetup
already distinguishes "key absent" from "key present but empty"; read
both axes the same way — absent leaves the axis alone, empty/null clears
it, anything else that is not a range is an error rather than a silent
wipe.

Regression test: print_titles_keeps_the_axis_it_is_not_given — on main
the rows-only call clears the columns; with the fix each axis is touched
only when named, an explicit empty value still clears, and a bad range
errors.
2026-10-15 17:18:19 +03:00
adrdsouza 7f9f73d6ba fix(numfmt): share legacy colors across grid and PDF rendering 2026-10-15 16:10:07 +05:30
Harrison BoothandClaude Opus 5.5 4567aed5fb fix(engine): AutoFill refuses targets that don't hold the source in a corner
edit.autoFill capped source and target at 5M cells separately, but fill()
writes one cell per source lane per step, and it never checked the target
held the source. Whole rows into whole columns (`2:3` into `A:A`) filled
every one of 16,384 columns down a million rows: ~17 billion writes, then
a 24-51 GB allocation abort that catch_unwind can't stop.

Excel (observed via COM on a synthetic workbook) refuses AutoFill when the
target doesn't contain the source or holds it away from every corner, and
accepts any corner-sharing target. edit.autoFill now applies that rule
(fill::holds_source_in_corner), and fill() bounds lanes x steps by
MAX_FILL_CELLS as a backstop. The 5M literals in auto_fill and fill_dir
use the shared constant.

Regression test: fill::tests::autofill_refuses_targets_that_do_not_extend_the_source.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 02:39:54 -05:00
euisuhandOpenAI Codex 370b220834 fix(solver): commit solutions and reports atomically
Prepare report data before editing so refused reports cannot leave solution cells or the saved model changed. Commit accepted reports with the optional solution and model, retaining array/protection checks and row autofit after sheet insertion.

Closes #1050

Co-Authored-By: OpenAI Codex <noreply@openai.com>
2026-10-15 15:23:19 +09:00
CouchRiv d59e1cf604 Merge remote-tracking branch 'up/main' into fix/sheet-param-honoured 2026-10-15 01:36:08 -04:00
CouchRiv bc524fa48e Merge remote-tracking branch 'up/main' into fix/conditional-format-style-colors 2026-10-15 01:36:05 -04:00
NetterSilverandClaude Haiku 5.5 1b4df8acc2 Refuse table names that clash with defined names or R1C1 references
Found while testing crates/engine/src/cmd/insert.rs:477

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 06:22:02 +02:00
Harrison BoothandClaude Opus 5.5 7ee11fe137 XLSX: keep dynamic-array cell metadata (cm) on save
A formula read with `cm` (Excel's XLDAPR dynamic-array record in
xl/metadata.xml) now carries `Formula::dynamic`, and the writer keeps the
`cm` and the metadata part for it even when the formula returns one value
and the export shape analysis would not need array evaluation. Before, such
formulas were saved as legacy formulas, so Excel recalculated some of them
with implicit intersection (SUMPRODUCT(IFERROR(range/range,0)) changed
value), and workbooks whose every dynamic formula was scalar lost
xl/metadata.xml altogether.

The mark survives moves, row and column insertion or deletion, sheet and
name renames and LAMBDA call-case fixes; retyping a formula drops it and
the writer decides from the formula's shape, as before. It is serialized
only when set, so saved documents without it are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:05:03 -05:00
vangel76andClaude Sonnet 5.5 0d18137114 Protection: enforce selectLockedCells/selectUnlockedCells and offer them in Protect Sheet (#1346)
selection.set refuses a cell the protection forbids selecting, selection.move steps on to the
next selectable cell, and review.protectSheet keeps the sheet's current selection rights unless
told otherwise. The dialog gains the two select options and insert/delete columns.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-14 23:00:50 +02:00
midasmatterhorn 2c23b61908 engine: subtotal honours header:false instead of dropping the first data row 2026-10-14 23:51:59 +03:00
midasmatterhorn 686c13c8b7 engine: keep case-insensitive replace from dropping text when folding shifts byte boundaries 2026-10-14 23:22:05 +03:00
MarekSpodBiedry ef894c20e5 Reject a list data validation that has no formula1 (#1303) 2026-10-14 19:31:01 +02:00
Marco Arnulfo f36bc100d4 AutoFit measures each distinct text once, and quickly on large columns (#1316) 2026-10-14 18:14:09 +02:00
Marco Arnulfo f92b4d30cb AutoFit Column Width measures text in the font the grid draws it with (fixes #1316) 2026-10-14 18:01:11 +02:00
midasmatterhorn 450ef1aea0 engine: refuse removeDuplicates with no valid column instead of deleting all rows 2026-10-14 18:01:42 +03:00
dev-exeandClaude Opus 5.5 2cae3a22b1 Slicers: a table named in table is found on any sheet
insert.slicer and table.insertSlicer looked for the table named by `table` only on the active
sheet. With another sheet active, the call failed with "Select a cell inside a table first, or
pass `table`" although `table` was passed. Other table commands find a table by name in the
whole workbook (Workbook::table).

The named table is now found on any sheet, and the slicer goes on that table's sheet. `at` is
read on that sheet, so "Sheet1!G2" names the table's own sheet. A name that matches no table
gives "no table named `X`".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:30:46 +05:30
Pranav Chandra 3177fbe03f ui: show spill-cell formula as greyed out 2026-10-14 20:23:44 +05:30
MarekSpodBiedry 139098e174 Filter: count empty text and ="" results as (Blanks) (#1294) 2026-10-14 15:44:31 +02:00
derechopiano 78ffd3b4c3 fix(pdf): preserve exported links and allow CLI PDF conversion (#954) 2026-10-14 13:40:16 +01:00
Marco Arnulfo 39602e6ce1 Filter and Subtotal: a Sheet! prefix on the range works on that sheet, not the active one (refs #1283) 2026-10-14 13:00:17 +02:00
Marco Arnulfo 13691e95e5 Remove Duplicates: a Sheet! prefix on the range works on that sheet, not the active one (refs #1283) 2026-10-14 12:51:05 +02:00
Marco Arnulfo be1af2d6ac Sort: a Sheet! prefix on the range sorts that sheet, not the active one (fixes #1283) 2026-10-14 12:08:41 +02:00
derechopiano 72db972de3 fix(engine): bound Undo history by retained cell storage (#1227) 2026-10-14 08:23:57 +01:00
vangel76andClaude Sonnet 5.5 568dd58264 Charts: a huge trendline forecast no longer collapses the chart (#1148)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 21:13:05 +02:00
MarekSpodBiedry 71437f23b1 Fix table without header: re-point references when the data moves down (#1030) 2026-10-13 18:06:31 +02:00
MarekSpodBiedry 2c8ff394cc Refuse a cut-paste whose destination does not fit instead of dropping cells (#997) 2026-10-13 18:04:55 +02:00
MarekSpodBiedry 3580a72c0d Respect workbook structure protection in sheet rename, move, copy, hide and unhide (#1049) 2026-10-13 18:04:32 +02:00
jonathanwxh-cell 18092672ee #701: integrate hidden-sheet navigation with current main 2026-10-14 00:01:18 +08:00
quantmind-br 482c3e9481 Engine: formulas.evaluate refuses what cell entry refuses, saying why
`formulas.evaluate` (behind `gridcraft-cli eval` and MCP's `evaluate_formula`) turned any
formula that didn't parse into #NAME?, so `=SUM(1;2)` looked like an unknown function. It now
parses the way `cell.set` does (missing closing parentheses are added) and returns the same
errors: "There's a problem with this formula: expected `,` or `)`, found Semicolon", or the
wrong-argument-count message. An unknown function still evaluates to #NAME?, as in Excel.
2026-10-13 09:38:41 -03:00
nihilist-narwhal 9939819ee3 rows/cols parameters: accept a single row or column
home.insertRows {"rows":"3"} and home.insertColumns {"cols":"B"}
failed with "not a range": only spans such as 3:3 or B:B parsed. The
same went for deleting, hiding, sizing and grouping rows and columns.
A single row number or column letter now means that one line.
2026-10-13 06:54:31 -05:00
nihilist-narwhal a98239e1d1 Format Cells: take the documented camelCase style keys
home.formatCells documents {style: {numFmt?, ...}}, but the style patch
was merged with the style's snake_case field names, so numFmt (and
borders.diagUp/diagDown) were silently ignored. camelCase keys in the
patch are now renamed to the style's own; snake_case keys still work.
2026-10-13 06:54:20 -05:00
Brandon Thomas ebe0138d30 Merge main into #855
# Conflicts:
#	ROADMAP.md
#	docs/gaps.md
#	docs/target-app-parity.md
2026-10-13 01:52:08 -04:00
Brandon Thomas be003d44af Merge main into #863
# Conflicts:
#	crates/xlsx/src/tests/passthrough_tests.rs
2026-10-13 01:40:22 -04:00
Brandon Thomas 2d0a3f8a47 Merge main into #780
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/insert.rs
#	crates/engine/src/tests.rs
2026-10-13 01:02:28 -04:00