Commit Graph
765 Commits
Author SHA1 Message Date
vangel76 1da640abcc Merge upstream/main into fix/control-auth-777 2026-10-22 19:32:43 +02:00
aiongg 2ed3df2bd6 G4: two windows on one workbook without copying it; Arrange All
- 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.
2026-10-22 16:50:10 +00:00
Higgs32584andClaude Opus 5.5 b5b3969377 scripts: trust gate review fixes (#2178)
- automate.trustScripts: only a missing `trusted` defaults to true; a
  non-boolean value is an error and leaves the workbook untrusted.
- The refusal message points at Trust Scripts in View > Macros (Alt+F8)
  and at automate.trustScripts.
- Catalog row for automate.trustScripts; parity checklist regenerated.
- Script steps run inside an io::ScriptSandbox (thread-local) so
  read_file/write_file refuse whatever the parameter is called, and a
  step that raises OpenUrl is dropped and stops the script. The id/param
  deny list stays as the up-front refusal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 12:43:48 -04:00
aiongg 5bd5d241a0 G3: a native window per workbook window
Each engine window is drawn in its own native window through craft-window's
immediate-viewport driver (desktop only; the web app keeps one window). The
root viewport shows one window and adopts another when its window closes.

- WindowUi: the cell draft, text-box draft, dialog, Backstage, message,
  toast, notice, grid state, name box, key tips, selected chart, Tab run and
  edit mode are per window. SheetApp's fields hold the active window's; the
  others are parked and swapped in when a window is drawn or activated, and
  after any command that switched windows.
- Session::show_window makes a window current for drawing without counting
  as an activation.
- The ribbon in each window acts on it; menu bar commands and scripts go to
  the window focused last. Leaving a window enters its draft as Enter would.
- A window's close button runs window.close: one of several windows on a
  workbook closes without asking, a workbook's last asks when unsaved.
  SheetApp::request_quit (macOS Quit item, Dock quit) asks about each unsaved
  workbook in its window; Cancel stops. Save Changes answers moved to
  SheetApp::answer_save_changes.
- The last window closed quits on Windows and Linux; macOS hides the root
  and keeps running until New or Open.
- Fonts are bound for every open workbook, since each may be showing.

craft-window is pinned to the aiongg/craft-libs window-list branch until it
merges into storytold/craft-libs.
2026-10-22 16:21:59 +00:00
aiongg 45d1d1acc0 G2: windows on workbooks in the engine; New Window shares the workbook
Session keeps a window list (stable ids, per-window selection, active sheet
and per-sheet selections swapped into the workbook without dirtying it)
in place of the index-based hidden list and the copying New Window.
Titles read Name:1 / Name:2 for a workbook with several windows.
File > Close closes one window of several; window.close, window.list and
window ids for window.activate, view.switchWindows, view.hideWindow and
view.unhideWindow. Control channel and MCP tools take window/doc ids;
scroll positions and cell drafts belong to their window.
2026-10-22 14:50:53 +00:00
Dominik StrebelandClaude Opus 5.5 74f29a743f docs: Ctrl+Shift+A (argument names) is no longer listed as missing
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 16:18:45 +02:00
Dominik StrebelandClaude Opus 5.5 e6ffca60bb view: hidden outline symbols answer no clicks; parity rows updated
With headings hidden too, a level button sat across the empty column band
at the grid's top edge, so a click there still ran it. Outline hit testing
now stops when both bands are empty. The parity doc no longer lists Ctrl+8
as missing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 16:09:29 +02:00
Dominik StrebelandClaude Opus 5.5 840ba5c3a5 view: Show Outline Symbols (Ctrl+8), read and written
A sheet can hide its outline's level buttons and brackets while keeping
the outline (Excel's Ctrl+8, saved as showOutlineSymbols="0"). It wasn't
modelled, so such sheets showed a wide outline band Excel doesn't.

- model: Sheet::show_outline_symbols (default on, also for older JSON).
- xlsx: read and written on the sheet view.
- engine: view.showOutlineSymbols {on?} (Ctrl+8), undoable.
- ui: no outline bands while it's off.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 15:54:06 +02:00
Dominik StrebelandClaude Opus 5.5 843c9b8404 ui: a too-wide number in a repeat-fill format shows ####
A number too wide for its column is drawn as ####. With a repeat fill
(`*` in the format, as in the Accounting format) the fill step then
rebuilt the text from the formatted value, so it overflowed and, being
right-aligned, lost its leading digits: 2,509.00 CHF in a narrow column
was drawn as "509.00 CHF". The fill now leaves #### alone.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 15:00:06 +02:00
prxnvt 67fa9f7b4e Merge main into #2152
# Conflicts:
#	ROADMAP.md
#	docs/architecture.md
#	docs/target-app-parity.md
#	docs/ui-parity.md
2026-10-22 02:39:09 -04:00
prxnvt bca1483e2e Merge main into #2145 2026-10-22 02:37:04 -04:00
prxnvt 38ad2dba83 Merge main into #2144
# Conflicts:
#	crates/engine/src/tests.rs
2026-10-22 02:37:04 -04:00
prxnvt 3eed81318f Merge main into #2151 2026-10-22 02:36:09 -04:00
prxnvt faff24f3aa Merge main into #465
# Conflicts:
#	crates/engine/src/io.rs
2026-10-22 02:36:09 -04:00
prxnvt 66f7993ba5 Merge main into #462
# Conflicts:
#	crates/engine/src/lib.rs
#	crates/engine/src/tests.rs
#	docs/architecture.md
2026-10-22 02:30:25 -04:00
prxnvt bbf1bc441b Engine: stopping a recording waits while a macro in its workbook is being debugged
Stopping a recording saved the macro with a nested macro.save while a debugged run's undo group could be open on the same workbook. Stopping the run without keeping its changes then deleted the macro that macro.record had reported as saved. Stopping now fails with "Finish or stop the macro first" under macro.record before anything changes, and the recording goes on, as when a save fails for another reason, so stopping again after the run ends saves it.
2026-10-22 02:26:33 -04:00
prxnvt ea691f703c Engine: a stopped recording reports the steps cut at the macro step limit in dropped
A recording keeps up to 100,000 commands, and inlined macro and script runs can add more, but a macro holds at most 10,000 steps. Stopping truncated the converted steps to that limit without saying so: dropped counted only the recorder's own overflow, so a 10,005-step recording saved a 10,000-step macro and reported nothing missing. The steps cut at the macro limit are now added to dropped.
2026-10-22 02:26:33 -04:00
prxnvt 3f5f6ea4f1 Engine: a name given when a recording stops never replaces an existing macro
Stopping a recording with a name that a macro already has (in any case) saved over that macro's
steps, inputs and description, with only Undo to get them back. A taken name now gets a suffix
("Tidy (2)"), and the save passes replace: false as a backstop.
2026-10-22 02:26:33 -04:00
prxnvt 20eb282e75 Docs: catalog count 275 / 291 in the roadmap and parity summaries
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.
2026-10-22 02:26:33 -04:00
prxnvt c0079aabe9 Docs: a failed macro run rolls back workbook changes; a file it already wrote stays
The intro said a macro run rolls everything back on an error, but a step that exports or prints to a path writes the file before a later step can fail, and the Running section says that file stays. The intro now limits the guarantee to workbook changes.
2026-10-22 02:26:32 -04:00
prxnvt 8797b7183c Engine: stopping a macro recording saves it in the workbook where recording started
Stopping a recording converted and saved it against the active workbook. For example, starting a
recording in Book1 and switching to Book2 before stopping saved the macro into Book2, inferred the
input's header and column types from Book2's cells and looked up inlined macros and scripts
there. The recording is now converted and saved against the workbook where it started, and the
active workbook stays active. If that workbook was closed, stopping returns an error and ends the
recording.
2026-10-22 02:26:32 -04:00
prxnvt f553619b33 Engine: the macro debugger lock also refuses edits that another command runs
Session::execute checked the debugger lock only for top-level undoable commands. A command that is not undoable could run edits as nested commands and skip it: stopping Record Actions while a macro in the same workbook was being debugged saved the script inside the run's undo group, and stopping the run without keeping its changes deleted the script again. The lock now applies at every depth, and only the debugged run's own steps go ahead.
2026-10-22 02:26:32 -04:00
prxnvt f1a46a2b53 Docs: the macro policy summary lists the file commands that do run
The policy summary said file commands never run in a macro, but File >
Properties runs, and exports and printing run: without a path they only
produce bytes, and with one they need the permission and approval. The
summary now names what is denied and what runs, and the paragraph is
wrapped to the file's line length.
2026-10-22 02:26:32 -04:00
prxnvt 0ea5d3ee33 Engine: a Name: token reaches a defined name that holds periods
`Name:` read a name only up to the first period, so `Name:Sales.Q1` looked up `Sales` and
treated `Q1` as a method, and a workbook name called Sales.Q1 could not be used in a macro,
alone or as `Name:Sales.Q1.cell(1,1)`.

The name now runs through periods, and the longest defined name or table that exists and ends
at a period (or the end) is used; what follows is read as methods. So `Name:Data.row` is a
name called Data.row, and `Name:Sales.offset(1,0)` still moves Sales. When no name matches,
the error names the text before the first method, and a bad method after a dotted name is
still refused. Tests cover direct resolution, reference parameters, `$ref` and `{{...}}`,
with and without a chained method.
2026-10-22 02:26:32 -04:00
prxnvt efce63f75f Engine: macro recorder applies the macro policy to the steps of a recorded script run
An Automate script run while recording was replaced by all of its steps except automate.* ones,
without the policy check that top-level commands get. For example, a script that runs
window.activate or macro.list produced a recorded macro that the runner refused on playback. The
steps of a recorded script run, and of an inlined macro run, now go through the same policy as
top-level commands; refused ones are left out and listed as skipped.
2026-10-22 02:26:32 -04:00
prxnvt 62cececa63 Docs: debugging macros 2026-10-22 02:26:32 -04:00
prxnvt 77150be455 Engine: col("Header") is refused on a range without a header row
col("...") always matched against the first row of the range, whatever
its header flag. On an input declared without a header row,
Input.col("Amount") silently picked the first column whose first data
cell read "Amount". After .data, the natural Input.data.col("Amount")
failed, because the first row was then a data row. The header row is
now carried through data, and col("...") is refused with a message when
there is none (an input without a header, a Name: base, or after any
other method).
2026-10-22 02:26:32 -04:00
prxnvt 7d31c5a4ed Engine: a macro in a newer format is dropped with a warning, not saved back as version 1
Opening a workbook accepted a macro whose format version `v` was above MACRO_VERSION. Serde
dropped the fields this build doesn't know, and the next save wrote the reduced macro back
with `v: 2`, so a newer build would read it as a complete version 2 macro.

A version above MACRO_VERSION is now refused by Macro::check_limits, and parse_macro checks
the raw `v` before reading the steps. Such a macro takes the existing per-macro path: it is
left out with the usual single warning (which now names a newer format), and the rest of the
workbook opens. Tests cover JSON and xlsx hidden names, with current and missing versions as
controls.
2026-10-22 02:26:32 -04:00
prxnvt 3f9223c85f Engine: macro recorder inlines a recorded macro run only when its steps stand alone
A macro run while recording was replaced by every command in it, including both branches of an
If and the body of a Repeat, whenever it had no inputs. For example, recording a run of a macro
with an If played back both the then and the else commands without a condition, and commands
using Row, Column or Output failed on playback. The run is now inlined only when the macro has no
inputs, no output and only plain commands (its disabled steps are left out); otherwise it is left
out and listed as skipped.
2026-10-22 02:26:32 -04:00
prxnvt b51d588cac Engine: macros can't open, hide or show windows
The policy denies window.* commands, but the View tab's window commands
are view.*: a macro could run view.newWindow, which adds a window that
the run's rollback doesn't remove, or view.hideWindow and
view.unhideWindow, which change the session's hidden windows.

The policy now denies those three. Arrange All changes nothing in the
engine and Switch Windows only lists windows, so both stay allowed.
2026-10-22 02:26:32 -04:00
prxnvt ac7c1ae639 Engine: a reference on a sheet named like a token stays a reference
is_token looked only at the leading identifier, so on a workbook with a
sheet called Output (or Row, Column, or the name of a declared input),
"Output!A1:B2" was read as the Output token, and the step failed with
"the macro has no output anchor" or "unexpected '!A1:B2'". This hit
reference keys, $ref and {{...}} alike, including references the
recorder writes for other sheets. A base followed by '!' is now a sheet
name, and the text resolves as a plain reference.
2026-10-22 02:26:32 -04:00
prxnvt ee7040a340 Docs: macro format and storage 2026-10-22 02:26:32 -04:00
prxnvt c6dda8562f Engine: macro recorder keeps references on other sheets as they are
The recorder made every unqualified reference relative to the example input, whatever sheet the
step ran on. For example, starting a recording on Sheet1 with B2:C5 selected, activating Sheet2
and setting C4 recorded Input.cell(3,2), so playback wrote Sheet1 instead; a formula entered on
Sheet2 was changed to refer to Sheet1's cells. Each step now keeps the sheet it ran on, and only
references on the input's sheet become tokens.
2026-10-22 02:26:32 -04:00
prxnvt bf456c9ff8 Engine: a macro's footprint dry runs keep to the run's timeLimit
footprint always ran its dry run with the default 60 second limit, so
macro.run with timeLimit 5 could run for over two minutes (a dry run,
then another after a shift) and fail with "ran longer than 60 seconds".

footprint now takes the run's clock and limit. make_way starts the
run's clock before the dry runs, so the checks and the run share one
limit, and macro.check takes timeLimit too.
2026-10-22 02:26:32 -04:00
prxnvt 7a0f591712 Docs: macro tokens 2026-10-22 02:26:32 -04:00
prxnvt 252ffa95b6 Engine: macro recorder keeps a recording when saving it fails
Stopping a recording took the recorded commands and where recording started before saving the
macro, so a failed save threw both away. For example, stopping with a name that holds a control
character, or in a workbook at its macro limit, ended the recording, and stopping again with a
good name found nothing to save. A failed save now puts the recording back, so stopping again
saves what was recorded.
2026-10-22 02:26:32 -04:00
prxnvt 4c6962a97c Engine: a partial macro footprint no longer clears later writes; the run checks each step
The footprint dry run stops at an approved file step and at Solver, but
make_way took a partial footprint with no conflicts as proof that the
run was safe, also after a shift. A macro that exported a CSV and then
set B1 ran under onConflict fail or ask and overwrote B1 without asking.

When the footprint is partial and the rule is not overwrite, the run's
machine now checks every step as it runs: a step that writes over
cells, sheets or objects that held data when the run started stops the
run and rolls it back, with a confirmation under ask and an error
otherwise. make_way now returns the machine for the run along with
whether it shifted.
2026-10-22 02:26:32 -04:00
prxnvt 749558f978 Docs: recording macros and the management commands; roadmap, architecture and parity 2026-10-22 02:26:32 -04:00
prxnvt d305566e57 Docs: running and checking macros 2026-10-22 02:26:32 -04:00
prxnvt b2740bdf33 Engine: macro recorder and macro.list, get, save, delete, rename, record
Commands: macro.list, macro.get, macro.save (a macro object or its
fields; replace defaults to true), macro.delete, macro.rename and
macro.record, beside macro.run, macro.check and macro.debug. Saving,
renaming and deleting are undoable. The catalog gains View › Macros ›
Record Macro (parity 275 / 291).

The recorder builds on the recording buffer of top-level commands, so
nested commands are never recorded twice and trimming the journal loses
nothing. When it stops, commands a macro may not run are left out and
listed; a macro run while recording is replaced by its steps when it
has no inputs, and an Automate script run by the script's steps;
sheet.activate and selection.set steps are added where the sheet or
selection changed between commands without one, and runs of them
collapse to the last. With relative references (the default) the
selection when recording started is the example input: references in
and near it become tokens (Input.col(2), Input.rows(2..),
Input.col(2).below, Input.row(4).right, Input.cell(r,c)), exact
references in formulas become {{...}}, and the input's width, header
and column types are inferred from the example. A recording replayed on
a longer block of data writes in the matching places.

A toggle such as Bold is recorded with the state it set ("on": true),
as Excel records Bold = True, so a replay sets it instead of flipping
it again.

Automate scripts are unchanged: automate.* keep their commands,
parameters, storage and results, and View › Macros still lists the
scripts and a preserved VBA project.
2026-10-22 02:26:32 -04:00
aiongg 4bafd4e8ee Docs: Switch Windows and drafts kept in their workbook (UI parity, progress log) 2026-10-22 05:12:23 +00:00
aiongg d5cdd2ea2b Integration: track t15
# Conflicts:
#	contributors/people.toml
#	crates/engine/src/tests.rs
#	docs/file-format-parity.md
#	docs/localization-parity.md
2026-10-22 03:05:39 +00:00
aiongg 561382cc6d Integration: track t14
# Conflicts:
#	ROADMAP.md
#	contributors/people.toml
#	crates/formula/src/tests.rs
#	docs/file-format-parity.md
#	docs/localization-parity.md
#	docs/ui-parity.md
2026-10-22 03:05:30 +00:00
aiongg ca1cdd4229 Integration: track t13
# Conflicts:
#	ROADMAP.md
#	contributors/people.toml
#	crates/engine/src/cmd/data.rs
#	crates/xlsx/src/tests/mod.rs
#	docs/chart-parity.md
#	docs/file-format-parity.md
#	docs/function-parity.md
#	docs/gaps.md
#	docs/localization-parity.md
#	docs/target-app-parity.md
2026-10-22 03:05:18 +00:00
aiongg c7fd66d807 Integration: track t12
# Conflicts:
#	ROADMAP.md
#	crates/calc/src/tests.rs
#	crates/chart/src/tests.rs
#	crates/engine/src/tests.rs
#	crates/xlsx/src/tests/mod.rs
#	docs/chart-parity.md
2026-10-22 03:05:03 +00:00
aiongg 9350ab1def Integration: track t11
# Conflicts:
#	ROADMAP.md
#	apps/gridcraft-web/Cargo.toml
#	crates/ui-egui/src/i18n.rs
#	docs/control-protocol.md
#	docs/gaps.md
#	docs/target-app-parity.md
#	docs/ui-parity.md
2026-10-22 03:04:52 +00:00
aiongg 4c5c345c31 Integration: track t10
# Conflicts:
#	ROADMAP.md
#	crates/ui-egui/tests/sheet_tabs.rs
#	crates/xlsx/src/tests/roundtrip.rs
#	docs/gaps.md
#	docs/ui-parity.md
2026-10-22 03:04:30 +00:00
aiongg 6dda6be2c6 Integration: track t09
# Conflicts:
#	crates/engine/src/tests.rs
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/src/i18n/pt.rs
#	docs/file-format-parity.md
#	docs/target-app-parity.md
#	docs/ui-parity.md
2026-10-22 03:04:21 +00:00
aiongg d3201e7cea Integration: track t08
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/tests.rs
#	docs/file-format-parity.md
#	docs/localization-parity.md
2026-10-22 03:03:55 +00:00
aiongg a680ac59f0 Integration: track t07
# Conflicts:
#	crates/calc/src/tests.rs
#	docs/function-parity.md
2026-10-22 03:03:23 +00:00