* E37: migrate GridCraft to egui 0.37
Pin the reviewed fork and shared dependencies, adapt input and rendering to the
new frame lifecycle, and preserve bounded capture handling and font notices.
Add representative accessibility checks and meaningful control labels.
Validation: full repository CI passed all ten stages, with 2,249 tests passed,
zero failed and one existing timing test ignored. Native workflows and paired
performance qualification remain documented separately.
* egui037: record final published pins and local qualification
* M0.37: fix RTL clipping and preserve screenshot budgets
* test: share migrated GridCraft UI harness setup
* egui037: document migrated UI and regression contracts
* egui-037: align popup test fonts and avoid redundant tooltip probes
AutoSum over any selection of more than one cell wrote a total under each column, so a row
selected (B2:E2) got four one-cell totals in row 3 instead of =SUM(B2:E2) in F2.
As Excel does, totals now go in the selection's empty last row or column, and otherwise past
it:
- a single row: its total in its last cell when that is empty, else just to its right;
- a block whose last column is empty: each row's total there; whose last row is empty: each
column's; both: both, and the block's total in the corner;
- otherwise totals under each column, as before.
A whole-row selection still stops at the data's last column, and all totals are one undo step.
Tests: autosum_over_a_row_totals_it_to_the_right,
autosum_over_a_whole_row_totals_it_after_the_data and
autosum_fills_a_selections_empty_last_column_and_row; each fails without the change.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`macro.check` is a query, so the debugger's lock let it through, but its dry run runs the macro's
steps, which the lock refuses: the check reported the lock as the failure of a step. It now waits
with "Finish or stop the macro first", as other commands that change the workbook do.
- Test: `between_steps_only_looking_around_runs_and_the_run_continues_where_it_was`.
Under `shiftDown` or `shiftRight` with conflicts, `macro.check` looked only for the refusals made
before a shift, so it said `ok` for a run the second footprint stops (an anchor that moves with
the shifted data). It now shifts the data inside an undo group, takes the footprint again, rolls
both back and puts the session back, and reports what still stands in the way.
- Test: the `Named` case and a shift that makes room in `shifting_makes_room_and_is_checked_again`.
- The check for writes over data compared the inputs' sheet with the sheet's index when the run
started, while the inputs follow their cells: after a step inserted a sheet in front, a step
writing into its own input on the moved sheet was stopped as writing over data. It now uses the
sheet's index now. Test: the input case in
`inputs_and_the_output_follow_their_cells_when_a_step_moves_them`.
- Docs: the page says `macro.run` checks the macro, its inputs and parameters and the policy
before it runs, and that a formula whose references a move rewrites counts as changed too.
- The start options are those of `macro.run`, refused as it refuses them.
- The time limit applies to each debugger action; the step and parameter limits to the whole run.
- The lock lists what runs between steps, and says the sheet and selection are put back.
- Stop Action ends the run; the debugger is never a step and never recorded.
- The answers' fields, including `changedCells`, `shifted`, `wroteFiles` and `message`.
`automate.stopAction` rolls back the undo groups open on the workbook. While a macro was being
debugged it rolled back the debugger's own group and left the debugger set, pointing at a group
that was gone. It is now refused under the debugger's lock with "Finish or stop the macro first",
as edits are; `macro.debug {action: "stop"}` ends the run.
- Test: `stop_action_waits_while_a_macro_is_being_debugged`.
- `ok` in `macro.check` meant only that the dry run hit no error. It now says whether
`macro.run` with the same parameters would go ahead without a question, and `errors` says
why not: a failing step, a Stop step that rolls back, data written over under `ask` or `fail`,
a shift that can't make room (the refusals `make_room` makes, now in `shift_refusal`), or a
run past the step or parameter limit once the dry runs before it count. `macro.check` takes
`onConflict` and `overwrite` as `macro.run` does, reports the rule it used, and refuses the
parameters `macro.run` refuses.
- After a partial footprint, the question under `ask` says where the check stopped and that
overwriting also lets the later steps write over data unchecked.
- The dry runs and the run share the parameter budget as they share the step budget.
- A dry run puts back the Go To list (`edit.goTo` with `record: true`).
- The footprint groups the touch log by sheet once, instead of filtering a million entries once
per sheet.
- Docs: what `ok` means; RAND's sequence moves on after a check, as after any recalculation.
- Tests: `the_check_says_whether_the_run_would_go_ahead`,
`the_question_after_a_partial_footprint_says_what_the_answer_covers`,
`a_dry_run_puts_back_the_go_to_list`, the shared parameter budget, and `ok` under `ask` in
`conflicts_ask_fail_or_overwrite`.
- A run that rolls back after writing a file no longer says "Nothing was changed.": the message
says the workbook was put back and names the step whose file stays (`StepError::wrote_files`,
also after a Stop with rollback).
- `macro.run` refuses an input the macro doesn't have (a misspelt input bound the selection),
an input that isn't reference text, and `onConflict`, `overwrite`, `approve` or `timeLimit` of
the wrong kind, which it used to ignore; `null` still counts as not given.
- A step no longer copies itself, and Repeat and If no longer copy their bodies, on every pass:
a body is copied once per run. A run passes its commands at most 256 MB of parameters, all
steps together, which bounds a run's work on the web build, where there is no clock.
- Every step except an approved file step runs inside the scripts' file sandbox, so a command
that touches a file under a parameter other than `path` can't do so from a macro.
- Docs: moving cells that hold data counts as writing over them.
- Tests: `run_parameters_that_would_be_ignored_are_refused`,
`a_run_passes_a_bounded_amount_of_parameters`, and the rollback message in
`each_step_is_checked_for_writes_over_data`.
Inputs, the output and a Repeat's range were bound once, by sheet index and coordinates. A step
that inserted a sheet in front of an input's sheet sent every later token to the new sheet, and
rows inserted above an input left the tokens on the cells that moved into its place; the run
wrote into the wrong cells and reported success.
- Commands record the structural edits they make as they rewrite references (`Ctx::edits`; while
a macro step runs, `DocState::edits` collects them).
- After each step the machine matches the sheets as the run's guard does and moves the inputs,
the output and the ranges of the Repeats in progress by those edits, with the formula engine's
own adjustment.
- A step that deletes an input's or the output's cells, or their sheet, or changes the size of a
range a Repeat is going over, stops the run, which rolls back.
- Test: `inputs_and_the_output_follow_their_cells_when_a_step_moves_them` (a sheet inserted in
front, rows inserted above, an input deleted, a Repeat's range grown).
A footprint is not proof of what the run will do. RAND gives the run other values than the dry
run before it (the recalculation's random sequence goes on), and NOW moves on, so an If can take
another branch in the run and write over data the dry run never reached; under `fail` the run
went ahead and wrote over it.
- Unless the rule is `overwrite`, every run checks each step as it goes, as a run after a
partial footprint already did; a write the footprint didn't see stops the run, which asks under
`ask` and fails otherwise, and rolls back.
- Test: `a_run_that_takes_another_path_than_its_check_is_still_checked` (an If on `=RAND()` over
40 runs under `fail`; before, one of them wrote over B1).
Split from the pull request below at the review's request; the code is the runner's footprint
as reviewed there, unchanged.
- `macro.run` first runs the macro in dry-run mode on the real workbook (unless the rule is
`overwrite`) and puts everything back: the workbook, its selection and history through the
dry run's undo group, and the session state a dry run may touch (`Saved`: the active
workbook, open documents, the saved mark, selections, unlocked ranges, trust, clipboard,
preferences, mode, journal and recording, queued requests, format painter, find, ruler,
hidden windows, drawing tool and the comment panes).
- Cells it would write outside its inputs that hold data, and sheets and objects it would
delete, are conflicts: `ask` asks before anything runs, `fail` fails, and `shiftDown` and
`shiftRight` insert cells over the block of every written cell and check again. A shift is
refused when it would move an input, when the macro deletes sheets or objects, or when the
touch log is full.
- A dry run stops before a file step and before Solver; the footprint is then partial, and the
run checks each step as it goes.
- The dry runs and the run share one clock, one time limit and one step budget.
- `macro.check` reports the inputs, the output anchor, approval needed and the footprint.
- Tests: the footprint tests that moved with this code.
The review asked for the runner to be split. This pull request keeps the policy, the input
checks, the step machine and `macro.run`; working out before a run where a macro writes (the
footprint dry run, shifting data to make room, `macro.check`) moves to the pull request above it.
- Unless the conflict rule is `overwrite`, every run now checks each step as it runs, which the
run did before only after a partial footprint: a step that writes over cells outside the inputs
that held data when the run started, or deletes sheets or objects that were there, stops the
run, which asks (`ask`) or fails, and the run rolls back.
- `shiftDown` and `shiftRight` need the footprint, so here they stop the run as `fail` does.
- Moved: `Footprint`, `Saved`, `footprint`, `diff`, `make_room`, `macro.check`, the dry-run
branches of the machine, `policy::previewable`, `DocState::touched` and the touch log,
`Session::journal_dropped` and `truncate_journal`, and the tests of those.
- Test: `each_step_is_checked_for_writes_over_data` (the review's counterexample: a permitted
file step or Solver, then a write over B1, under `fail` and `ask`).
Main adds UiRequest::InsertName (Paste Name, Use in Formula), which puts a name in the cell
editor; a macro step drops it, as it drops a request to start editing a cell.
* Go To: names match in any case and script, as formulas match them
* Paste Name: only the names in view are laid out
* Docs: LAMBDA recursion limit and called error names; one Page Layout view row with the side margin marked unverified
* Go To and Paste Name: cargo fmt
A macro whose format version is above this build's was dropped on open,
so the next save removed it from the file, and the warning said only
that it couldn't be opened. It is now kept as the file held it
(Workbook::macros_unread, shared between snapshots), never read, listed
or run, and written back unchanged by JSON and xlsx saves, numbered
after the readable macros and around visible names of the storage
shape. Kept macros count toward the 1,000-macro limit after the
readable ones; one too large for an xlsx item is left out. The warning
says it is kept and saved back; damaged and over-limit macros keep
their own warning.
check_limits capped the name, description, notes, step count, depth and
input names, and left the shortcut, permissions, output anchor, default
range, column types, command ids, Repeat ranges, If cells and values and
Stop messages bounded only by the item size; the input declaration was
stored unchecked (rows [5, 2], a cell input with cols [2, 5]).
Now each of them has a limit (docs/macros.md lists them), one-line
fields refuse control characters, and a command id is letters, digits,
. and _ (every registered id passes, tested). An If value may be null
(Empty and NotEmpty ignore it); Repeat.max stays the runner's to bound.
An input's least rows and columns can't pass its most, a cell input is
one row and one column with no header and at most one column type, and
no input lists more column types than columns. check_limits takes every
struct apart field by field, so a new field doesn't compile there until
its limit is decided. A macro from a file that breaks a rule is left
out with the usual warning, never changed to fit.
with_names numbered macros 0..N, and stash::set_many refuses to write a
hidden name beside a visible one of the same name, so a visible
_gc_macro_0_0 (from a file, or defined after the file opened, when the
stored names are no longer in the workbook) made every xlsx save of a
workbook with macros fail. The index is GridCraft's choice: macros now
take the free indices from 0 up, skipping every key a visible
workbook-scope name of the storage shape uses (compared as the stash
reads names), in their order. No arithmetic depends on a number from
the file.