A reference box that had focus kept picking from the grid while Check, Run or a debugger action
ran. The selections those make landed in the box and dirtied the draft, and a later Check could
save that draft. The pick now ends when an action starts, and while a debug run is active a
selection on the grid is never taken as a pick.
Also: the parity assessment said an .xlsm's VBA project is dropped on open; it is kept unchanged
on save and never run.
New Macro (Automate › Macros) and Step Into, Edit and Create in the
Macros dialog open a resizable step editor. It works on a draft of the
macro and saves it with macro.save.
The step list shows each step's number, a breakpoint dot in the gutter
(click to set or clear), a category icon, a summary, an error marker
and an enabled checkbox; Repeat bodies and If branches are indented
under their step, and rows drag to reorder within their list. Add Step
opens a command picker (control steps first; commands a macro may
never run are left out) and fills in the command's required
parameters; Duplicate and Delete act on the selected step.
The inspector's Details tab edits the selected step. Reference
parameters are reference boxes that take a typed reference or a
selection on the grid, with a Rel/Abs switch between a token relative
to the example input and an absolute reference, and token suggestions.
The Inputs tab sets each input's shape, header and column types (Use
current selection as example infers them); the Output tab sets the
anchor and the conflict rule.
The debug bar has Check (a dry run that colours the grid: inputs blue,
writes green, writes over data red), Run, Step, Continue, Run to Here
and Stop, which asks whether to keep the changes or undo them. The
debugger receives the editor's breakpoints with breakpointsOnly, so a
dot cleared while paused takes effect at once; the current step is
selected, its parameters are shown as they will run, and its target is
outlined on the grid. While a macro is debugged, the status bar says so
and Show reopens the editor.
List drag and drop lives in a small dnd module. The debugger icons are
drawn in code, and the breakpoint and sheet overlay colours are theme
tokens covered by the contrast test.
The Automate tab gains a Macros group: Macros opens the Macros dialog,
Record Macro opens the Record Macro dialog (name, Ctrl+Shift shortcut
letter, description, Use Relative References) and turns into Stop
Recording while recording, Use Relative References sets how the next
recording starts, and Run Macro runs the macro run last; its menu lists
the recent macros, then every macro. While recording, the status bar
shows a red dot, "Recording · N steps" and Stop.
The Macros dialog (Automate › Macros, View › Macros, Alt+F8) lists the
workbook's macros with their shortcuts, then its Automate scripts, and
says when the file carries a VBA project, which is kept and not run.
Run, Delete (which asks first) and Options (shortcut letter and
description) act on the selected row. The dialog keeps the result of
view.macros, so the scripts and the VBA entry are listed as before.
A macro that needs approval asks with Run, and one whose output would
land on data asks with Overwrite, Shift Down and Shift Right; each
button runs the command again with its answer. Ctrl+Shift+letter
(Cmd+Shift on a Mac) runs the macro with that shortcut; the letters
GridCraft uses keep their meaning. The View › Macros keytips (WMR, WMV)
now open the Record Macro and Macros dialogs.
Icons for macros, recording and relative references are drawn in code,
the recording dot is a theme token covered by the contrast test, and
large and small ribbon buttons and icon buttons carry accessible names.
- target-app-parity.md: the summary table and the method say 276 / 292 and 292 unique ids, as
`cargo xtask parity` measures; Last updated and revision history.
- architecture.md: macros are kept as `_gc_macro_<n>_<k>` (CodeRabbit 4366060044).
- macros.md: the intro says the page covers the engine and its commands, without implying
later work.
* CI: dev builds keep line tables only
* CI: one integration-test binary per crate
* CI: xtask runs tests with nextest, doctests apart, --keep-going on clippy and wasm
cargo xtask test runs the workspace tests (--lib --bins --tests) with cargo-nextest when it is installed, else cargo test, then the doctests. cargo xtask ci uses the same two steps.
* CI: docs name cargo xtask test
* CI: agent guides name the moved fuzz test
* CI: tests added on main join their crate's test binary
- The workbook's active sheet is an atomic set in place (Workbook::active_sheet /
set_active_sheet), so a window swap or sheet switch never clones the
Arc<Workbook> or marks it dirty. Selections already live in DocState and the
window views. Save records the sheet of the most recently active window.
- A dialog taken out of its window while it runs goes back to that window when
a command run from it switched windows (dialogs::show, ui.dialog.confirm).
- view.arrangeAll {arrange?: tiled|horizontal|vertical|cascade, activeWorkbook?}
returns the visible windows, active first; the desktop moves and sizes the
native windows over the main window's monitor. New Window and Arrange All
buttons on the View ribbon; Arrange Windows dialog.
- Docs: ui-parity, hardware-parity, gaps #20, target-app-parity, ROADMAP.
The catalog went from 274 / 290 to 275 / 291 with View › Macros › Record Macro, and
parity-checklist.md says so, but the ROADMAP.md headline, ui-parity.md and target-app-parity.md
still said 274 / 290 (and 290 catalog ids). They now match, with revision rows.
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.
auto_row_height grew a row for every line break in a cell's text whether
or not the cell wrapped. Excel counts breaks only when Wrap Text is on:
with it off a multi-line value displays on one line and AutoFit gives a
one-line row. A file whose unwrapped cells hold pasted multi-line text
(the shape generating libraries write) opened with tall rows and nothing
visibly tall in them.
The fast path no longer treats a line break as a reason to measure, and
the measuring path lays text out over lines only for a wrapping cell.
Typing a break still turns wrap on (the Alt+Enter rule in cell entry) and
grows the row, as before.
Test: an unwrapped multi-line cell from a file keeps the default height
on open; wrap on grows the row; wrap off returns it; a typed break wraps
and grows. The open-time fit tests from #833 are unchanged.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
With two or more value fields the Σ Values pseudo-field can sit in Rows
as well as Columns, at any position among the row fields. The model
records it (`PivotTable.values_on_rows`, `values_position`) and the
engine lays it out like Excel in compact, outline and tabular form: one
row item per value field at the Σ Values level, per-value subtotals
("East Sum of Sales") and grand totals ("Total Sum of Sales").
XLSX reads `dataOnRows` and the `-2` field's place in rowFields or
colFields and writes them back; the "values on rows are shown on
columns" warning is gone. `pivot.moveField` moves "Σ Values" between
Rows and Columns or within one, axis field moves keep it in place, and
the PivotTable Fields pane lists it.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Builds on the Solver of #327. data.solver takes reports: [answer, limits]
and writes them on new sheets before the model's sheet once a solution
is found. The Answer report lists the objective and variable cells with
their original and final values and each constraint's value, formula,
status (Binding or Not Binding) and slack; cells are named by the
nearest labels to their left and above. The Limits report minimises and
maximises each variable with the others held at the solution, under the
model's constraints, and gives the objective at both ends (Simplex LP
and GRG Nonlinear, without integer constraints).
data.solverReport writes the reports for a given solution and model,
so data.solver's result (which now carries the original values and the
model) can be passed to it as is. The Solver Results dialog offers the
two reports; they are written after Keep Solver Solution or Restore
Original Values.
Builds on the Solver of #327. method: evolutionary runs a genetic search
for models that aren't smooth: tournament selection, blend crossover and
Gaussian mutation inside the variable bounds, feasibility-first ranking
and integer rounding, then a compass search around the best member. It
stops when the population converges, when the best member stops
improving for a number of generations, or at the evaluation budget. A
seeded generator makes runs repeatable. Population size, mutation rate,
random seed, convergence and Require Bounds are options; the method and
its options are saved in the hidden solver_* names. The Solver
Parameters dialog offers the method.
Data: Analysis ToolPak with 13 tools and the Data Analysis dialog (#312)
# Conflicts:
# crates/ui-egui/src/dialogs.rs
# docs/gaps.md
# docs/parity-checklist.md
View: Formula Bar and Zoom 100% as engine commands (#289)
# Conflicts:
# ROADMAP.md
# crates/ui-egui/src/control.rs
# crates/ui-egui/src/lib.rs
# docs/target-app-parity.md
View › Page Layout (and the status-bar button) now shows the sheet as printed
pages on a desk instead of overlaying page outlines on the grid.
- Model/XLSX: per-sheet view mode (normal / pageLayout / pageBreakPreview),
saved as <sheetView view="…"> and read back. view.* commands set it per
sheet ({sheet?}) without an undo step or dirtying a clean workbook.
- Engine: paper::PageGrid paginates the sheet from A1 with the print
pagination (print::split_bands, shared with PDF export and printing) and
maps cells to pages and back; header/footer section helpers
(split/join, &[Page] field names) shared with the PDF header code.
pageLayout.headerFooter takes {part, section, text} for one section and a
sheet; header/footer strings are capped at 255 characters.
view.ruler toggles the rulers.
- UI: Geo carries the page grid, so painting, hit-testing, selection,
editing, objects and scrolling work through the page transform; frozen
panes are ignored in this view. Pages with margins and gaps, "Click to add
data" pages, rulers in inches (English) or centimetres, headers/footers
drawn on printed pages, click a margin section to edit it in place; a
click elsewhere commits (one undo step), Escape cancels. Header & Footer
contextual tab: presets, Page Number, Number of Pages, Current Date/Time,
File Path, File Name, Sheet Name, Go to Header/Footer. View › Ruler.
- New strings in every catalog; new code-drawn icons; desk colour token.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
data.analysisToolPak runs Descriptive Statistics, Histogram, Correlation,
Covariance, Regression, Moving Average, Exponential Smoothing, Rank and
Percentile, the three t-Tests, F-Test and Anova: Single Factor, writing a
static report to an output range or a new sheet. Non-empty output cells are
refused unless overwrite is set; the dialog asks instead.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A chart linked to a PivotTable: series are built from the report
(innermost row items x column items/value fields, totals excluded) and
rebuilt on every refresh or layout change; c:pivotSource round trip;
chart series accept union references.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Model: Sheet.edit_ranges (title, ranges, optional password hash,
security descriptor) and gridcraft_model::password: the ECMA-376 legacy
16-bit hash and the salted, iterated SHA-2 hash (SHA-512/384/256; other
algorithms, damaged hashes and spin counts over 10,000,000 are refused).
New passwords use SHA-512, spin count 100000. Adds the sha2 crate.
- XLSX: <protectedRanges> read and written; an unreadable password keeps
the range locked.
- review.allowEditRanges {action: list|add|modify|delete|unlock, ...}: empty
params list; ranges can't change while the sheet is protected; unlock
verifies the password for the session. Not journaled (passwords).
- Locked-cell checks (commit guard, cell.set, picture in cell) let edits
through in open or unlocked ranges; ranges move with structural edits.
- Ribbon button and a form dialog (menu invocation only).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>