mirror of
https://github.com/storytold/gridcraft.git
synced 2026-10-31 06:06:48 -06:00
Merge main into function updates
This commit is contained in:
+14
-3
@@ -1,6 +1,6 @@
|
||||
# Architecture
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · ** Last updated:** 2026-10-24 · **Change:** minor (engine building blocks for multi-command actions) · **Target:** Microsoft Excel (Microsoft 365)
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-25 · **Change:** trivial (the hidden names that hold macros; earlier: macro engine on the engine building blocks) · **Target:** Microsoft Excel (Microsoft 365)
|
||||
|
||||
How GridCraft is built today. Rules for contributors are in [`AGENTS.md`](../AGENTS.md).
|
||||
|
||||
@@ -98,12 +98,15 @@ Actions made of many commands (macros, a debugger that runs one step per call) u
|
||||
every group it opened, as `group_rollback` does. `Session::abort_groups(doc)`, and the command
|
||||
`automate.stopAction` on the active workbook, roll back the groups left open on a document (a
|
||||
debugger the person abandons, a caller that dropped its token), so Undo works there again. The
|
||||
command runs on its own: a step of a script or macro may not stop the action it is part of, and
|
||||
a recording leaves it out, so playing the recording back never stops the run. The
|
||||
history limit is applied when the outermost command finishes; until then the steps made inside
|
||||
it are bounded on their own.
|
||||
- **Recording buffer** (`record.rs`). `Session::start_recording` collects each successful
|
||||
top-level command with the document, sheet and selection it ran in, without secret parameters.
|
||||
Commands run by other commands are not recorded, nor is a menu-style call that only opened a
|
||||
dialog (the dialog's confirming call is the step), and the buffer is independent of the journal
|
||||
dialog (the dialog's confirming call is the step; a run that changed the workbook before a step
|
||||
of it opened a dialog is recorded), and the buffer is independent of the journal
|
||||
(Automate › Record Actions still records through the journal).
|
||||
- **Document trust.** `DocState::trust` is `Local` for workbooks made in the session and
|
||||
`FromFile` for workbooks opened from a file. `Trust` has no default: `DocState::new` takes it,
|
||||
@@ -120,11 +123,17 @@ Actions made of many commands (macros, a debugger that runs one step per call) u
|
||||
other than tab, line feed and carriage return, U+FFFE, U+FFFF) are stored as the noncharacter
|
||||
U+FDD0 and four hex digits, as is U+FDD0 itself, and read back as they were. `stash::items`
|
||||
and `stash::keys` list the items under a key prefix in one pass over the names, at most
|
||||
10,000 of them (`stash::MAX_ITEMS`). The older single-name `_sc_<key>` items are read as well.
|
||||
10,000 of them (`stash::MAX_ITEMS`) with a count of the rest, so a caller can say what it did
|
||||
not list. The older single-name `_sc_<key>` items are read as well.
|
||||
`formulas.defineName` and `formulas.deleteName` refuse a name the workbook holds such an item
|
||||
under (a hidden workbook-scope name of either shape; a visible one is the person's own), and
|
||||
`formulas.createFromSelection` skips such labels, unless `force: true` is passed.
|
||||
|
||||
The macro engine (`engine/src/macros/`, commands in `cmd/macros.rs`, described in
|
||||
[`macros.md`](macros.md)) is built on these: a run is an undo group, the recorder reads the
|
||||
recording buffer, the policy asks before a macro in a `FromFile` workbook runs, and xlsx keeps
|
||||
macros as chunked hidden names (`_gc_macro_<n>_<k>`: macro `n`, chunk `k`).
|
||||
|
||||
## Agent control
|
||||
|
||||
- Desktop control channel: `gridcraft --control 7979`, JSON lines on localhost
|
||||
@@ -150,6 +159,8 @@ rpm, tarball), FreeBSD, and web (WASM; WebGPU or WebGL2). Release workflows in
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-25 | trivial | The hidden names that hold macros are `_gc_macro_<n>_<k>` |
|
||||
| 2026-10-24 | minor | Macro engine on the building blocks (`macros/`, `cmd/macros.rs`) |
|
||||
| 2026-10-24 | minor | Engine building blocks for multi-command actions: undo groups, recording buffer, document trust, chunked hidden-name storage |
|
||||
| 2026-10-14 | minor | Document Undo's retained-cell-storage budget and latest-step guarantee |
|
||||
| 2026-10-10 | major | Created from the code on main: crates, layers, data flow, agent control, known gaps |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Chart parity
|
||||
|
||||
> **Last reviewed:** 2026-10-18 · **Last updated:** 2026-10-24 · **Change:** minor (100% stacked area charts) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
|
||||
> **Last reviewed:** 2026-10-18 · **Last updated:** 2026-10-29 · **Change:** minor (selected chart source outlines stay beneath floating objects) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
|
||||
|
||||
Excel's chart types and chart elements against GridCraft. Part of
|
||||
[target-app-parity.md](target-app-parity.md). **Breadth ~50%, ready ~30%, 40–60 h** (estimated).
|
||||
@@ -38,6 +38,7 @@ Sources: `crates/model/src/features.rs` (`ChartKind`, `Chart`, `Series`), `crate
|
||||
| Element | Excel | GridCraft |
|
||||
|---|---|---|
|
||||
| Move and resize the chart object | yes | yes (corner handle, Format Chart Area Size fields) |
|
||||
| Selected chart source-data outline | visible on uncovered cells, beneath floating objects | painted above cells and below charts, shapes, pictures and slicers; retains pane clipping and right-to-left cell geometry (#2861) |
|
||||
| Chart title, axis titles | text, linked to cells, full formatting | text only |
|
||||
| Legend | position, overlay, formatting, per-entry delete | position only |
|
||||
| Data labels | value, category, series, percentage, from cells, callouts, position | one on/off flag |
|
||||
@@ -90,6 +91,7 @@ are (#1686). Such series get no `numCache`/`strCache` on save (#1690).
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-29 | minor | Selected charts highlight their source cells beneath floating objects, so the blue outline no longer crosses the chart; uncovered source cells remain highlighted (#2861) |
|
||||
| 2026-10-24 | minor | 100% Stacked Area: inserted from the ribbon and All Charts, drawn as shares of each category's total, read and written as `percentStacked` |
|
||||
| 2026-10-18 | trivial | Stale notes: stock/combo, treemap/sunburst, box and waterfall kinds survive XLSX round-trip (PRs #158, #161) |
|
||||
| 2026-10-16 | trivial | Series that refer to defined names (`[0]!Name`, `Sheet!Name`) resolve instead of showing `#NAME?`, and are saved qualified, as Excel needs (#1686) |
|
||||
|
||||
@@ -23,7 +23,8 @@ The control port is reachable by every program on the machine, and by a web page
|
||||
`{"id":0,"ok":true,"result":{"authenticated":true}}`. Any other first line (a wrong or missing
|
||||
token, another method, text that isn't JSON such as an HTTP request, no line within 10 s, or
|
||||
one longer than 4 KiB) gets `{"ok":false,"error":"authentication required"}` and the
|
||||
connection is closed. After that, a line that isn't JSON also closes the connection.
|
||||
connection is closed. At most 16 connections may be waiting to send their token at once; past that a new
|
||||
connection is closed before anything is read. After that, a line that isn't JSON also closes the connection.
|
||||
- `gridcraft-cli send`/`mcp --connect` and `gridcraft_mcp::Remote` do this themselves: they use
|
||||
`GRIDCRAFT_CONTROL_TOKEN` if set, else the token file of that port (so they work for the same
|
||||
user on the same machine without setup). Your own client reads the file or the variable.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# File-format parity
|
||||
|
||||
> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-21 · **Change:** minor (.xls dialog sheets are skipped instead of read as worksheets; `.xls` import reads each sheet's display options from WINDOW2: right-to-left, gridlines, headings, zeros, formulas; XLSX text boxes and shapes keep their paragraphs' right-to-left direction on save; CSV/TSV/TXT can open with every field kept as text via `file.open {csvAsText}`; formulas over Excel's 8,192 characters, and `%` or call chains past the nesting limit, are parse errors instead of stack overflows when a file is opened; XLSX PivotTables read and write `rowGrandTotals`/`colGrandTotals` the way Excel does, so a table saved with only one grand total opens with the right one; HTML export keeps right-to-left; CSV and text import detects likely Windows-1256 Arabic when invalid UTF-8 has Arabic byte runs; other invalid UTF-8 remains Windows-1252; HTML export places pictures and charts, as inline SVG, at their anchors) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
|
||||
> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-26 · **Change:** minor (`file.exportCsv {values: true}` writes raw values for automation; .xls dialog sheets are skipped instead of read as worksheets; `.xls` import reads each sheet's display options from WINDOW2: right-to-left, gridlines, headings, zeros, formulas; XLSX text boxes and shapes keep their paragraphs' right-to-left direction on save; CSV/TSV/TXT can open with every field kept as text via `file.open {csvAsText}`; formulas over Excel's 8,192 characters, and `%` or call chains past the nesting limit, are parse errors instead of stack overflows when a file is opened; XLSX PivotTables read and write `rowGrandTotals`/`colGrandTotals` the way Excel does, so a table saved with only one grand total opens with the right one; HTML export keeps right-to-left; CSV and text import detects likely Windows-1256 Arabic when invalid UTF-8 has Arabic byte runs; other invalid UTF-8 remains Windows-1252; HTML export places pictures and charts, as inline SVG, at their anchors) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
|
||||
|
||||
Every format Excel opens or saves, with GridCraft's read and write support, fidelity and tests.
|
||||
Part of [target-app-parity.md](target-app-parity.md). **File formats: ~50% ready, 80–120 h**
|
||||
@@ -66,9 +66,9 @@ values) is still dropped.
|
||||
|
||||
| Format | Excel | GridCraft read | GridCraft write | Notes |
|
||||
|---|---|---|---|---|
|
||||
| `.csv` UTF-8 / Windows / Mac / MS-DOS | R/W | yes (UTF-8 and UTF-16 BOMs, UTF-8, likely Windows-1256 Arabic detection, otherwise Windows-1252 fallback; delimiter detection) | yes (UTF-8 without BOM, CRLF; a file opened as CSV is saved with the separator it was read with, so a `;` file stays `;`; otherwise `,`) | Excel writes the **displayed** values; we write raw values. No encoding choice on save. Re-import of a single-column export splits text (#127) A used range that would write more than 512 MiB (two far-apart cells span the whole rectangle) is refused with an error instead of being expanded (#528). |
|
||||
| `.csv` UTF-8 / Windows / Mac / MS-DOS | R/W | yes (UTF-8 and UTF-16 BOMs, UTF-8, likely Windows-1256 Arabic detection, otherwise Windows-1252 fallback; delimiter detection) | yes (UTF-8 without BOM, CRLF; a file opened as CSV is saved with the separator it was read with, so a `;` file stays `;`; otherwise `,`) | Excel writes the **displayed** values, and so do we; `file.exportCsv {values: true}` writes raw values (full precision, ISO 8601 dates) for automation. No encoding choice on save. Re-import of a single-column export splits text (#127) A used range that would write more than 512 MiB (two far-apart cells span the whole rectangle) is refused with an error instead of being expanded (#528). |
|
||||
| `.txt` tab-delimited, Unicode text | R/W | yes (`.txt`, `.tsv`, `.tab`; likely Windows-1256 Arabic detection for invalid UTF-8) | yes | Text Import Wizard options partly via Text to Columns |
|
||||
| `.csv` UTF-8 / Windows / Mac / MS-DOS | R/W | yes (UTF-8 and UTF-16 BOMs, UTF-8, likely Windows-1256 Arabic detection, otherwise Windows-1252 fallback; delimiter detection) | yes (UTF-8 without BOM, CRLF) | Writes the **displayed** values like Excel (number formats applied, formula results, never column-width `#####`). No encoding choice on save. Re-import of a single-column export splits text (#127) A used range that would write more than 512 MiB (two far-apart cells span the whole rectangle) is refused with an error instead of being expanded (#528). |
|
||||
| `.csv` UTF-8 / Windows / Mac / MS-DOS | R/W | yes (UTF-8 and UTF-16 BOMs, UTF-8, likely Windows-1256 Arabic detection, otherwise Windows-1252 fallback; delimiter detection) | yes (UTF-8 without BOM, CRLF) | Writes the **displayed** values like Excel (number formats applied, formula results, never column-width `#####`); `file.exportCsv {values: true}` writes raw values instead. No encoding choice on save. Re-import of a single-column export splits text (#127) A used range that would write more than 512 MiB (two far-apart cells span the whole rectangle) is refused with an error instead of being expanded (#528). |
|
||||
| `.txt` tab-delimited, Unicode text | R/W | yes (`.txt`, `.tsv`, `.tab`; likely Windows-1256 Arabic detection for invalid UTF-8) | yes (displayed values) | Text Import Wizard options partly via Text to Columns |
|
||||
| `.prn` formatted text (space-delimited) | R/W | no | no | |
|
||||
| `.dif`, `.slk` (SYLK) | R/W | no | no | Small, legacy |
|
||||
@@ -115,6 +115,7 @@ refresh. Counted under features (Get & Transform, ~10% ready), not here.
|
||||
| 2026-10-20 | minor | A workbook theme's major/minor per-script typefaces (`<a:font script="Arab" typeface="…"/>`, also Hebr, Jpan and the rest) are read and written back in file order after `<a:cs>`; they were dropped before. A hostile file is held to 64 entries of 256 characters |
|
||||
| 2026-10-20 | trivial | A workbook theme's major/minor East Asian (`<a:ea>`) and complex-script (`<a:cs>`) typefaces are read and written back, so a Japanese theme's fonts (e.g. 游ゴシック) survive open + save; they were deleted before because the model had nowhere to keep them. Rendering still resolves fonts without them (follow-up) |
|
||||
| 2026-10-19 | minor | XLSX font charset and family values are preserved on save, retaining the character set and family used for substitute font selection |
|
||||
| 2026-10-26 | trivial | `file.exportCsv {values: true}` writes raw cell values (full-precision numbers, ISO 8601 dates and times) for automation; the default stays Excel's displayed text (#2648) |
|
||||
| 2026-10-19 | minor | CSV and text import decodes likely Windows-1256 Arabic; other invalid UTF-8 retains the Windows-1252 fallback |
|
||||
| 2026-10-19 | minor | .xls import skips dialog sheets (WsBool `fDialog`), which share the worksheet BOF type and were read as empty worksheets |
|
||||
| 2026-10-19 | minor | `.xls` import reads WINDOW2: right-to-left sheets (common in older Arabic and Hebrew files) open right-to-left, and hidden gridlines, headings and zero values and Show Formulas carry over |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Function parity: worksheet functions and formulas
|
||||
|
||||
> **Last reviewed:** 2026-10-31 · **Last updated:** 2026-10-31 · **Change:** minor (Student-t density uses a stable large-degrees-of-freedom gamma ratio, matching Excel at degrees through 10 billion; earlier: the LAMBDA row lists the recursion limit and its `#NUM!`, and the error a defined name holding an error gives when called; earlier: number text with accounting parentheses coerces like Excel 365; INDEX with one index on a 2-D reference is #REF! as in Excel; earlier: SUBTOTAL and AGGREGATE cap the cells they visit, #2175) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
|
||||
> **Last reviewed:** 2026-10-31 · **Last updated:** 2026-10-31 · **Change:** minor (Student-t density uses a stable large-degrees-of-freedom gamma ratio, matching Excel at degrees through 10 billion; earlier: ECMA.CEILING listed with USDOLLAR as not on Microsoft's list; IMSQRT reproduces the stray real part Excel gives for negative reals, and the other IM* differences from Excel are listed; the LAMBDA row lists the recursion limit and its `#NUM!`, and the error a defined name holding an error gives when called; number text with accounting parentheses coerces like Excel 365; INDEX with one index on a 2-D reference is #REF! as in Excel; SUBTOTAL and AGGREGATE cap the cells they visit, #2175) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
|
||||
|
||||
Excel's worksheet functions, category by category, against GridCraft's function library. Part of
|
||||
[target-app-parity.md](target-app-parity.md); gaps feed [gaps.md](gaps.md).
|
||||
@@ -20,8 +20,8 @@ Excel's worksheet functions, category by category, against GridCraft's function
|
||||
evaluator's special forms in `crates/calc/src/eval.rs` (`SPECIAL_FUNCTIONS`: IF, IFS, CHOOSE,
|
||||
OFFSET, INDIRECT, INDEX, SUBTOTAL, AGGREGATE, TEXT, LET, LAMBDA, MAP, REDUCE, SCAN, BYROW, BYCOL,
|
||||
MAKEARRAY…), excluding the test-only `ADD2`. Every one is an Excel name.
|
||||
- **Not on Microsoft's list:** USDOLLAR, a legacy function Excel still evaluates (like DOLLAR),
|
||||
is in our library as a Compatibility function for files that use it. It is not counted above.
|
||||
- **Not on Microsoft's list:** USDOLLAR and ECMA.CEILING, legacy functions Excel still evaluates (like DOLLAR),
|
||||
are in our library as Compatibility functions for files that use them. They are not counted above.
|
||||
- **Working** excludes stubs that return an error by design: the 7 CUBE functions (`#N/A`, no
|
||||
cube connections) and WEBSERVICE and FILTERXML (`#VALUE!`). Excel for Mac also lacks
|
||||
WEBSERVICE and FILTERXML; Excel for Windows has them.
|
||||
@@ -90,6 +90,7 @@ This improves series preparation; it does not change the model-fitting or season
|
||||
| LET, LAMBDA, named LAMBDAs, ISOMITTED, MAP/REDUCE/SCAN/BYROW/BYCOL/MAKEARRAY | done. A LAMBDA that calls itself more than 140 times, or a formula nested past the depth limit, gives `#NUM!` (the target app allows deeper recursion, #1581). Calling a defined name that holds an error value gives that error, not `#NAME?` |
|
||||
| Iterative calculation for circular references | done: with Enable Iterative Calculation on (`formulas.calculationOptions` `{iterative, maxIterations, maxChange}`, saved as `<calcPr iterate iterateCount iterateDelta>`), loops are evaluated again from their previous values (blank = 0) until no cell on a loop changes by Maximum Change or Maximum Iterations is reached; off, cycles give `#CIRC!`. Set in File › Options › Formulas (Enable iterative calculation, Maximum Iterations 1 to 32767, Maximum Change 0 or more; the two fields are disabled while iteration is off). Differences: GridCraft recalculates on open, so a loop that never settles (`=A1+1`) advances again each time the file is opened
|
||||
| 15-significant-digit comparison semantics | done (#57): `=0.1+0.2=0.3` is TRUE, and MATCH and COUNTIF compare the same way. Arithmetic keeps full precision, as Excel does — `=(0.1+0.2)-0.3` is 5.55E-17 |
|
||||
| IMSQRT and the other IM* functions against Excel's text | IMSQRT follows Excel's polar route, so a negative real gives Excel's stray real part: `IMSQRT("-4")` is `1.22514845490862E-16+2i` (checked in Excel 16.0 on 79 inputs). Differences kept: IMSQRT of a number with a part above about 1E154 or below about 1E-155 gives the exact root (Excel gives `#NUM!` or 0); IMPOWER, IMEXP, IMSIN, IMCOS, IMSINH and IMCOSH keep exact results where Excel shows the same kind of noise (`IMPOWER("-4",2)` is `16-3.92047505570758E-15i`) or different trailing digits near multiples of pi/2; IMCSC and IMCSCH differ by one unit in the 15th digit; IMARGUMENT returns 16–17 digits where Excel returns 15; the text of IM* and COMPLEX results differs for some magnitudes (Excel writes `COMPLEX(1E-10,1)` as `0.0000000001+i` but `COMPLEX(0.000000123456789012345,1)` as `1.23456789012345E-07+i`, and an integer past 15 digits rounded and zero-padded) |
|
||||
| External workbook references (`[Book.xlsx]Sheet1!A1`) | parsed; links dropped on open, no update |
|
||||
| Localized function names and `;` separators | **missing** (#45); en-US only |
|
||||
| Long flat operator chains (`=A1+A2+…`, `-`, `*`, `/`, `^`, `&`, comparisons) | done: no operand limit below Excel's 8,192-character formula limit (Excel, observed: `=1+1+…` with 4,096 operands is exactly 8,192 characters with the `=` and calculates; one more character is refused). Evaluated (#484), printed (#628), stored, copied, adjusted, saved and reopened without recursing once per operand, on a 1 MiB stack (#916); written to XLSX as plain formulas, not dynamic arrays; Spanish entry accepts them |
|
||||
@@ -124,6 +125,8 @@ workbooks; results checked in as data, not Excel files) and fixing what it finds
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-31 | minor | Student-t density uses an asymptotic log-gamma ratio for large degrees of freedom, avoiding cancellation while preserving bounded work; checked against Excel at 100 million and 10 billion degrees |
|
||||
| 2026-10-27 | minor | ECMA.CEILING added to the functions that are in our library but not on Microsoft's list (with USDOLLAR), so the counts don't change |
|
||||
| 2026-10-26 | minor | IMSQRT uses Excel's polar route with its cosine reduction, so `IMSQRT("-4")` is `1.22514845490862E-16+2i` as in Excel (exact `2i` before); the rows list the IM* differences still open |
|
||||
| 2026-10-25 | minor | LAMBDA row: a LAMBDA that calls itself more than 140 times, and a formula nested past the depth limit, give `#NUM!` (#1809; nesting gave `#CALC!` before). `=Foo(1)` where the defined name Foo holds `#N/A` gives `#N/A`, not `#NAME?` |
|
||||
| 2026-10-23 | minor | Number text coercion (VALUE, arithmetic, typed entry): a dollar sign before accounting parentheses (`$(1,234)`, `$ (5)`) and a percent sign after them (`(5)%`) read as negatives; `(-5)`, `((5))`, `($-5)`, `$5%`, `$(5)%` and dates or times in parentheses are `#VALUE!`, as observed in Excel 365 |
|
||||
| 2026-10-23 | minor | INDEX with only a row index on a reference of several rows and columns (`INDEX(A1:C3,2)`, `INDEX(A1:C3,0)`) is `#REF!`, as observed in Excel, so it also fails as a range endpoint and inside other functions; an empty column argument (`INDEX(A1:C3,2,)`) still gives the row. Checked that a column array with one index stays a 1×1 array (`TYPE` 64), which is what Excel returns |
|
||||
|
||||
+326
@@ -0,0 +1,326 @@
|
||||
# Macros
|
||||
|
||||
> **Last updated:** 2026-10-25 · **Change:** minor (recording macros and the management commands; Record Macro takes a name, shortcut and description; the macro interface: Automate › Macros, the Macros and Record Macro dialogs, the step editor, shortcuts)
|
||||
|
||||
GridCraft macros are typed, declarative lists of commands, not VBA. Every step runs a GridCraft
|
||||
command (the same ids the ribbon, CLI and MCP use) or one of three bounded control steps. A macro
|
||||
declares the inputs it works on, runs as one undo step, rolls all workbook changes back on an
|
||||
error (a file it already wrote stays), checks where its results will land before it writes them,
|
||||
and does not touch files without permission.
|
||||
Agents run macros with `macro.run` like any other command.
|
||||
|
||||
This page describes the engine, its commands and the macro interface in the window.
|
||||
|
||||
|
||||
## A macro
|
||||
|
||||
```json
|
||||
{
|
||||
"name": "Totals",
|
||||
"description": "Adds a total next to the amounts",
|
||||
"inputs": [{"name": "Input", "header": true, "cols": [2, 2], "colTypes": ["text", "number"]}],
|
||||
"output": {"anchor": "Input.right(2).cell(1,2)", "onConflict": "ask"},
|
||||
"steps": [
|
||||
{"type": "command", "id": "cell.set", "params": {"cell": "Output", "input": "Total"}},
|
||||
{"type": "command", "id": "cell.set", "params": {"cell": "Output.offset(1,0)", "input": "=SUM({{Input.data.col(2)}})"}},
|
||||
{"type": "repeat", "over": "Input.data", "of": "rows", "max": 1000, "body": [
|
||||
{"type": "if", "cell": "Row.cell(1,2)", "op": "gt", "value": 100,
|
||||
"then": [{"type": "command", "id": "home.bold", "params": {"range": "Row"}}]}
|
||||
]},
|
||||
{"type": "stop", "message": "Done."}
|
||||
],
|
||||
"permissions": []
|
||||
}
|
||||
```
|
||||
|
||||
- **Steps**: `command` (`id`, `params`), `repeat` (each row or column of `over`, at most `max`
|
||||
times), `if` (`cell` compared with `value` by `eq`, `ne`, `gt`, `ge`, `lt`, `le`, `empty`,
|
||||
`notEmpty` or `contains`; `then` and `else` lists) and `stop` (`message`; `rollback: true`
|
||||
undoes the run). Every step can carry `enabled`, `breakpoint` and `note`. The short form
|
||||
`{"command": "cell.set", "params": {…}}` is accepted for a command step.
|
||||
- **Inputs**: `kind` (`cell` or `range`), `rows` and `cols` as `[min, max]` (0 for no maximum;
|
||||
rows exclude the header), `header`, `colTypes` (`number`, `text`, `date`, `bool`, `any`),
|
||||
`nonEmpty` and `defaultRef`. These declare what a range must look like for the macro to work
|
||||
on it. An input's name is letters, digits and `_`, starting with a letter, and is neither a
|
||||
token of its own (`Output`, `Row`, `Column`, `Name`) nor something that reads as a cell
|
||||
reference (`Q1`), so a step written with plain A1 text keeps meaning the cell. The declaration
|
||||
is checked as a whole: neither `rows` nor `cols` may have a least above its most, a `cell` input
|
||||
has one row and one column (`[1, 1]`, or the default `[1, 0]`), no header and at most one column
|
||||
type, and no input lists more column types than it allows columns. A run binds each input
|
||||
to the range it is given, its default, or (for the first input) the selection, and refuses
|
||||
with messages that name the cells: "Input column 2 (Amount) must hold numbers; C4 holds text."
|
||||
- **Limits**: 1,000 macros a workbook, 10,000 steps a macro, Repeat and If nested 4 deep, and 64
|
||||
inputs a macro. Names (of macros and inputs) and command ids are at most 255 characters;
|
||||
descriptions, step notes, Stop messages and If values (as JSON) 4,096; references and tokens
|
||||
(`output.anchor`, `defaultRef`, a Repeat's `over`, an If's `cell`) 1,024; a shortcut 64. A
|
||||
macro lists at most 256 permissions, each a command id, and an input at most 16,384 column
|
||||
types. One-line fields hold no line breaks or other control characters, and a command id is
|
||||
letters, digits, `.` and `_`. A command step's `params` are bounded only by the size of the
|
||||
whole macro (4,194,304 characters of JSON). A macro read from a file that breaks any of these is
|
||||
left out with a warning; it is never changed to fit. When a macro runs, a Repeat whose range has
|
||||
more rows or columns than its `max` is refused, and a run executes at most 100,000 steps (each
|
||||
pass through a Repeat counts), passes its commands at most 256 MB of parameters, all steps
|
||||
together, and takes at most 60 seconds, all three shared by the footprint dry runs and the run
|
||||
itself (`timeLimit` to change the time; the web build has no clock, so there only the step and
|
||||
parameter limits apply). The debugger gives each of its actions the time limit on its own.
|
||||
|
||||
## Tokens
|
||||
|
||||
References in step parameters can be tokens, worked out when the step runs:
|
||||
|
||||
| Token | Means |
|
||||
|---|---|
|
||||
| `Input`, or another input's name | the range the input is bound to |
|
||||
| `Output` | the output rule's anchor |
|
||||
| `Row`, `Column` | the current row or column of the Repeat around the step |
|
||||
| `Name:Total` | a defined name, or a table's data |
|
||||
|
||||
Methods narrow or move a range and chain: `col(2)` or `col("Amount")`, `row(1)`, `cols(2..3)`,
|
||||
`rows(2..)`, `cell(r,c)` (relative to the top-left cell; `cell(0,1)` is the cell above),
|
||||
`offset(r,c)`, `resize(h,w)`, `data` (without the header row), `below(n)` and `right(n)`.
|
||||
`col("Amount")` looks the name up in an input's header row, directly or after `data`; on a range
|
||||
without a header row it is refused. A defined name can hold periods: `Name:Sales.Q1` is the name
|
||||
`Sales.Q1`, and `Name:Sales.Q1.cell(1,1)` its first cell, because the longest name or table that
|
||||
exists is used and methods follow it.
|
||||
|
||||
Write a token as the whole value of a reference parameter (`cell`, `range`, `target`, keys ending
|
||||
in `Cell` or `Range`…), as `{"$ref": "Input.col(2)"}` anywhere, or as `{{Input.col(2)}}` inside
|
||||
any string, such as a formula; `{{{{` in a string is a literal `{{`, and `{"$text": "Output"}`
|
||||
anywhere is the text as it is, never read as a token or for `{{…}}` (a defined name called
|
||||
`Output`, or `Dear {{name}}`). Under the general keys that
|
||||
other commands use for plain words (`to`, `from`, `at`, `area`, `source`, `target`,
|
||||
`destination`, `location`), a bare `Row` or `Column` stays text (a replacement word, a pivot
|
||||
area); write `Row.col(2)`, `{{Row}}` or `{"$ref": "Row"}` there. Plain A1 references stay
|
||||
absolute; a `Name:` anchor follows inserted rows. A reference with a sheet name is never a token,
|
||||
so `Output!A1` is a cell on a sheet called Output, even when a token or input has that name.
|
||||
`Input.data` on a range that is only a header row, and `Name:` of a table with no data rows, are
|
||||
refused rather than read as the header.
|
||||
|
||||
## Running
|
||||
|
||||
`macro.run {name, inputs?, onConflict?, overwrite?, approve?, timeLimit?}`:
|
||||
|
||||
1. **Policy.** File commands other than Properties, exports and printing (such as Open, Save
|
||||
and Close), app, window (including New Window, Hide and Unhide), automate and macro commands,
|
||||
and Undo and Redo, never run in a macro. File › Properties runs, and so do exports and
|
||||
printing (`file.exportPdf`, `exportCsv`, `exportHtml`, `print`, `printPreview`, `saveBytes`)
|
||||
that only produce bytes. Reading or writing a file (`data.getData`, `data.fromTextCsv`,
|
||||
`insert.picture` or `pageLayout.background` with a `path`, or an export or print with a
|
||||
`path`) needs the command in the macro's `permissions`. Any `path` other than `null` counts,
|
||||
including a blank one and a token, and the policy is checked again on the parameters as they
|
||||
are when the step runs. A macro that touches files, or that came with a workbook opened from
|
||||
a file, runs once the person approves it: the call fails with `approve: …` and runs with
|
||||
`approve: true`. Approval lasts for the session, until the macro changes. Every step other
|
||||
than an approved file step runs inside the file sandbox that scripts use, so no other
|
||||
parameter reaches a file either.
|
||||
2. **Footprint.** Unless the conflict rule is `overwrite`, the macro first runs in dry-run mode on
|
||||
the real workbook, and everything is put back. Cells it would write outside its inputs that
|
||||
hold data, and sheets and objects it would delete, are conflicts, handled by `onConflict` (or
|
||||
the macro's `output.onConflict`): `ask` (the call fails with `confirm: …`; `overwrite: true`
|
||||
answers it), `fail`, `overwrite`, `shiftDown` or `shiftRight` (Insert Cells makes room, then
|
||||
the footprint is checked again). Steps that touch files, and Solver, end the dry run early;
|
||||
the footprint then says it is partial. Unless the rule is `overwrite`, the run also checks
|
||||
every step as it goes, since the footprint can miss a write: it is partial, or a value such as
|
||||
RAND or NOW sends the run another way than the dry run. A step that writes over cells, sheets
|
||||
or objects that held data when the run started stops the run and rolls the workbook back (a
|
||||
file already written stays): it asks (`confirm: …`) under `ask` and fails otherwise, since
|
||||
shifting can't make room that late. Moving cells that hold data, as inserting or deleting rows
|
||||
or cells above them does, counts as writing over them, in the footprint and in this check, and
|
||||
so does a formula whose references such a move rewrites: both compare cells by place, so they
|
||||
may ask about data that only moved, never the other way.
|
||||
3. **Run.** One undo step, "Run Macro: Totals". Any error rolls the whole run back and names the
|
||||
step: `Step 3.1 (cell.set): …` (paths are 1-based; `2.1` is inside a Repeat, `4.then.1` inside
|
||||
an If). The message ends "Nothing was changed.", or names the files the run already wrote,
|
||||
which stay. An input the macro doesn't have, an input that isn't reference text, and
|
||||
`onConflict`, `overwrite`, `approve` or `timeLimit` of the wrong kind are refused rather than
|
||||
ignored. When a step inserts, deletes or moves sheets, rows, columns or cells, the inputs, the
|
||||
output and the range of a Repeat in progress move with their cells, as references in formulas
|
||||
do; a step that deletes one of them, or changes the size of a range a Repeat is going over,
|
||||
stops the run.
|
||||
|
||||
`macro.check {name, inputs?, onConflict?, overwrite?, timeLimit?}` reports the bound inputs, the
|
||||
output anchor, any approval needed and the footprint, and puts back everything the dry run
|
||||
changed. `ok` says whether `macro.run` with the same parameters would go ahead without a question;
|
||||
when it wouldn't, `errors` says why: the run would fail at a step, a Stop step rolls it back, it
|
||||
would write over data under `ask` or `fail`, shifting can't make room (the check shifts and
|
||||
checks again, then puts everything back), or the run would go past
|
||||
the step or parameter limit counting the dry runs before it. The random numbers RAND gives move
|
||||
on, as after any recalculation.
|
||||
|
||||
## Debugging
|
||||
|
||||
`macro.debug {action, …}` runs a macro a step at a time. The actions are:
|
||||
|
||||
- `start` with `name` and the options of `macro.run` (`inputs`, `onConflict`, `overwrite`,
|
||||
`approve`, `timeLimit`), refused as `macro.run` refuses them when they are of the wrong kind. The
|
||||
checks, the approval and the footprint come first, as for `macro.run`; then the run pauses
|
||||
before its first step.
|
||||
- `step` runs the next step; `continue` runs to the next breakpoint or the end; `runTo` (`step`:
|
||||
a path) runs to that step, stopping at a breakpoint on the way.
|
||||
- `stop` ends the run: `keep: true` keeps the changes as one undo step, otherwise they are rolled
|
||||
back.
|
||||
- `state` answers without running anything (the default while a macro is being debugged).
|
||||
|
||||
`breakpoints` (a list of step paths of the macro, at most 10,000) can be given with any action and
|
||||
replaces the list set before; `breakpointsOnly: true` makes the list replace the steps' own
|
||||
breakpoint flags, also when it is given without a list. An unknown action, a breakpoint or `runTo`
|
||||
path the macro doesn't have, and `keep` or `breakpointsOnly` that isn't `true` or `false` are
|
||||
refused before anything changes. The time limit applies to each action on its own (`start` with
|
||||
its dry runs, and each `step`, `continue` or `runTo`); the step and parameter limits apply to the
|
||||
whole run, counting the dry runs.
|
||||
|
||||
Each answer gives the next step with its parameters as they would run (a Repeat over whole rows or
|
||||
columns shows the part of the sheet in use, as the run goes over it), the parameters of the step
|
||||
that just ran, the steps the run has executed (the dry runs not counted), and what the run has
|
||||
changed in its workbook: `changed` lists at most 500 entries (cell ranges, and sheets added,
|
||||
deleted or renamed) and `changedCells` counts the changed cells on the sheets it had at the start. `shifted` names the rule that made
|
||||
room at the start, and `wroteFiles` the files the run wrote, which stay when the run is rolled
|
||||
back; the answer to a rolled-back run says so in `message`. A run that ends (its last step, a
|
||||
Stop step, or a failed step, which rolls it back) or is stopped answers with `state` `finished`,
|
||||
`stopped` or `failed`.
|
||||
|
||||
While a macro is being debugged, its workbook is locked between steps: only `macro.debug`, queries
|
||||
(commands that only read), commands that look around (`selection.*`, Select All, `sheet.activate`,
|
||||
`sheet.next` and `sheet.previous`, Go To, Go To Special, Find, Copy and the zoom commands), Switch
|
||||
Window, Close (which ends the run with its workbook) and Automate › Stop Action run there.
|
||||
Everything else, undoable or not, saving included, is refused with "Finish or stop the macro
|
||||
first", also when another command runs it (stopping Record Actions saves a script), and so is
|
||||
`macro.check`, whose dry run runs the macro's steps. Other
|
||||
workbooks stay editable. The person may look around between steps: before the next step runs, the
|
||||
debugger puts the active sheet and the selection back where the run left them, so the run goes on
|
||||
as `macro.run` would run it. A zoom change made meanwhile is part of the run, and is undone with
|
||||
it. Stop Action rolls the run back and ends it, and its answer names the macro. The debugger runs
|
||||
on its own, never as a step of a script or macro, and is never recorded.
|
||||
|
||||
## Recording
|
||||
|
||||
`macro.record` toggles recording. Starting takes `relative` (default true; `false` for absolute
|
||||
references) and, from the Record Macro dialog, `name`, `shortcut` and `description`: the macro's
|
||||
name, its Ctrl+Shift shortcut letter (`"Ctrl+Shift+M"` or `"M"`) and its description. They are
|
||||
checked at once, so a name that is taken (a kept one included), too long or holds line breaks, or a
|
||||
letter that GridCraft or another macro uses, is refused before anything is recorded. Without a name
|
||||
the macro is called `Macro1`, `Macro2` and so on. A `name` or `description` given when stopping wins
|
||||
over the one given when starting; a name that another macro has by then gets a suffix, such as `Tidy
|
||||
(2)`, so stopping never replaces a macro, and a shortcut another macro took meanwhile is left off,
|
||||
which the result says in `shortcutDropped`. `cancel: true` ends a recording without saving it, the
|
||||
way out of one whose macro can't be saved. A parameter the call would ignore is refused: `relative`
|
||||
or `shortcut` when stopping, `stop: false` while recording, anything beside `cancel: true`, and one
|
||||
of the wrong kind. The macro is saved in the workbook where recording started, even if another one
|
||||
is active; if that workbook was closed, stopping ends the recording without saving it. When saving
|
||||
the recording fails (a name the check refuses, a workbook at its macro limit, a macro being debugged
|
||||
in that workbook), recording goes on, so stopping again keeps what was recorded. The person recorded
|
||||
the macro, so in a workbook opened from a file it runs without the approval a macro that came with
|
||||
the file needs, until it changes.
|
||||
|
||||
A recording keeps up to 100,000 commands and 64 MB of them as JSON, and a macro holds up to
|
||||
10,000 steps and 4,194,304 characters of JSON. The steps past any of these limits, and steps
|
||||
recorded in another workbook, are left out and counted in `dropped`.
|
||||
|
||||
With relative references the selection when recording starts is the example input: references in
|
||||
and near it on its sheet become tokens (on another sheet `C4` stays that sheet's `C4`), and the
|
||||
input's shape, header and column types are inferred from it as it was when recording started,
|
||||
before the recorded steps changed it. Recorded text that playback would read as a token stays
|
||||
text: a reference parameter that reads as one (a defined name called `Output`) is written
|
||||
`{"$text": "Output"}`, and `{{` in a string is written `{{{{`. Toggles such as Bold are recorded
|
||||
with the state they set (`"on": true`), also inside a script run, so playing them back sets them
|
||||
rather than flipping them.
|
||||
|
||||
Commands a macro may not run are left out and listed in `skipped`, also from the steps of a macro
|
||||
or script run while recording. A macro run while recording is replaced by its steps when it has
|
||||
no inputs, no output and only plain commands (otherwise it is left out and listed), and an
|
||||
Automate script run by the steps it ran, without a step that only opened a dialog (a menu-style
|
||||
call without its parameters). Both are kept as they ran, so editing or deleting the macro or
|
||||
script before the recording stops changes nothing; a run whose steps did not fit in the
|
||||
recording's 64 MB is left out and listed.
|
||||
|
||||
Undo while recording works as in Excel: `edit.undo` is left out (a macro never runs it), and the
|
||||
step it undid stays in the recording, so playing the recording back makes that edit again.
|
||||
|
||||
GridCraft keeps the Ctrl+Shift letters it uses itself on some platform: F, G, H, J, K, L, R, S, T
|
||||
and Z (`macros::RESERVED_SHORTCUTS`). A shortcut a file brings with it on one of these letters is
|
||||
kept but does not run the macro.
|
||||
|
||||
## Other commands and storage
|
||||
|
||||
`macro.list`, `macro.get`, `macro.save {macro, replace?}`, `macro.delete` and `macro.rename
|
||||
{name, to}`. Saving under a name a macro has is refused unless `replace: true` is passed, and
|
||||
renaming to one is refused; names compare ignoring the case of the letters A to Z. A renamed macro is checked
|
||||
against every limit again, its size included. Saving, renaming and deleting are undoable.
|
||||
|
||||
Macros are saved in the workbook: as a field in GridCraft's JSON format, and in xlsx as hidden
|
||||
defined names (`_gc_macro_<n>_<k>`, the macro's JSON split into string literals within Excel's
|
||||
limits), which other spreadsheet applications keep but do not run. A visible name of that shape
|
||||
is someone's own name: opening a file leaves it alone, and saving stores the macros under indices
|
||||
it does not use.
|
||||
|
||||
Each macro carries its format version, `v` (1 in this version; a missing `v` reads as 1). When a
|
||||
file is opened, a macro in a newer format (a `v` above 1) is kept unread, with a warning: this
|
||||
version can't show or run it, and saving writes it back unchanged, so the newer
|
||||
version gets it back whole. It is never read in part. Kept macros count toward the 1,000-macro
|
||||
limit after the readable ones, and one too large for an xlsx file is left out. They count when a
|
||||
macro is added too (saving, or stopping a recording, is refused once readable and kept macros
|
||||
together reach 1,000, and the message says how many are kept), so a save holds no more macros
|
||||
than the next open keeps. Their names are taken: saving or renaming a macro to one is refused, and
|
||||
a recording gets a suffix. A macro that is
|
||||
damaged or over the limits is left out with one warning, and the rest of the workbook and its
|
||||
other macros open; a macro that was left out is not in the saved file.
|
||||
|
||||
## In the window
|
||||
|
||||
- **Automate › Macros group.** Macros opens the Macros dialog. Record Macro opens the Record
|
||||
Macro dialog (name, Ctrl+Shift shortcut letter, description, Use Relative References); OK
|
||||
starts recording and stays off while the name or the letter cannot be used, with the reason
|
||||
under the fields. While recording, the button reads Stop Recording, the status bar shows a red
|
||||
dot, "Recording · N steps" and Stop, and Use Relative References is locked. Run Macro runs the
|
||||
macro run last (or the only one); its menu lists the recent macros, then every macro. New
|
||||
Macro opens the step editor on a new macro.
|
||||
- **Macros dialog** (Automate › Macros, View › Macros, Alt+F8). It lists the workbook's macros
|
||||
(with their shortcuts), then its Automate scripts, and says when the file carries a VBA project.
|
||||
Scripts that came with an opened file stay disabled until Trust Scripts (`automate.trustScripts`)
|
||||
trusts them for the session. Run runs the selected macro on the selection or the selected
|
||||
script; Step Into, Edit and Create open the step editor; Delete asks first; Options sets a
|
||||
macro's shortcut letter and description. The dialog keeps the result of `view.macros`, as before
|
||||
macros existed.
|
||||
- **Step editor.** A resizable window that works on a draft and saves it with `macro.save`; in a
|
||||
narrow app window it is no larger than the window and its toolbar wraps onto a second row. 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); Duplicate and
|
||||
Delete act on the selected step. The inspector's Details tab edits the selected step, with
|
||||
reference boxes that take a typed reference or a selection on the grid and switch between a
|
||||
token relative to the example input (Rel) and an absolute reference (Abs); the Inputs tab sets
|
||||
each input's shape, header and column types, and Use current selection as example infers them;
|
||||
the Output tab sets the anchor and what to do when results would land on data. The editor
|
||||
belongs to the workbook it opened in: while another workbook is active it says so and Save,
|
||||
Check, Run and Debug wait until that workbook is active again, and closing the workbook closes
|
||||
the editor. It keeps its window until it closes itself: another dialog asked for meanwhile is
|
||||
refused with a message, and closing the app first asks about the editor's debug run and unsaved
|
||||
steps, then about unsaved workbooks.
|
||||
- **Debug bar.** Check dry-runs the macro and colours the grid: inputs blue, the cells it writes
|
||||
green, the cells among them that hold data red. Run runs it as one undo step. Step, Continue and
|
||||
Run to Here start the debugger when needed (`macro.debug` with the editor's breakpoints and
|
||||
`breakpointsOnly: true`, 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.
|
||||
Stop asks whether to keep the changes so far or undo them. While a macro is debugged the status
|
||||
bar says "Debugging" with the macro's name, and Show reopens the editor.
|
||||
- **Questions.** A macro that needs approval offers Run; a macro whose output would land on data
|
||||
offers Overwrite, Shift Down and Shift Right. Each button runs the command again with
|
||||
`approve: true` or the chosen `onConflict`, in the workbook that asked: an answer given while
|
||||
another workbook is active does nothing and says why.
|
||||
- **Shortcuts.** Ctrl+Shift+letter (⌘⇧letter on a Mac) on the grid runs the macro with that
|
||||
letter. The letters GridCraft uses itself keep their own meaning.
|
||||
|
||||
Commands with empty parameters never open these dialogs: `macro.record {}` starts recording
|
||||
under the next free name, and the dialogs are opened only from the ribbon, the menus and keys.
|
||||
|
||||
## Automate scripts and VBA
|
||||
|
||||
Automate scripts (`automate.recordActions`, `newScript`, `allScripts`, `runScript`,
|
||||
`deleteScript`) are an older, separate feature. They keep their commands, parameters, storage
|
||||
(`_sc_scripts`) and results, and View › Macros (`view.macros`) still lists them. Macro commands
|
||||
neither read nor change their storage; a recording keeps the steps a script run ran while it
|
||||
recorded. A workbook can hold a script and a macro with the same name.
|
||||
|
||||
A VBA project in an `.xlsm` or `.xltm` file is preserved on save and never run. GridCraft macros
|
||||
are stored apart from it.
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
Each entry is an Excel ribbon or menu command (feature names only). It counts as implemented when GridCraft registers a command with the same id. This measures breadth only; the overall assessment (depth, fidelity, effort) is in [target-app-parity.md](target-app-parity.md).
|
||||
|
||||
**Overall: 275 / 291 commands implemented (94.5%).**
|
||||
**Overall: 276 / 292 commands implemented (94.5%).**
|
||||
|
||||
| Tab | Implemented | Total | Parity |
|
||||
|---|---|---|---|
|
||||
@@ -16,7 +16,7 @@ Each entry is an Excel ribbon or menu command (feature names only). It counts as
|
||||
| Formulas | 23 | 23 | 100% |
|
||||
| Data | 23 | 28 | 82% |
|
||||
| Review | 15 | 18 | 83% |
|
||||
| View | 20 | 20 | 100% |
|
||||
| View | 21 | 21 | 100% |
|
||||
| Automate | 4 | 4 | 100% |
|
||||
| Table Design | 13 | 13 | 100% |
|
||||
| Chart Design | 8 | 8 | 100% |
|
||||
@@ -299,7 +299,7 @@ Missing:
|
||||
- `review.translate` Translate (Language)
|
||||
- `review.showChanges` Show Changes (Changes)
|
||||
|
||||
## View (20 / 20, 100%)
|
||||
## View (21 / 21, 100%)
|
||||
|
||||
Implemented:
|
||||
|
||||
@@ -323,6 +323,7 @@ Implemented:
|
||||
- `view.unhideWindow` Unhide (Window)
|
||||
- `view.switchWindows` Switch Windows (Window)
|
||||
- `view.macros` Macros (Macros)
|
||||
- `macro.record` Record Macro (Macros)
|
||||
|
||||
## Automate (4 / 4, 100%)
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Target-app parity: GridCraft vs Microsoft Excel
|
||||
|
||||
> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-22 · **Change:** minor (several windows: a native window per workbook window, New Window on the same workbook, Arrange All; no score change) · **Target:** Microsoft Excel (Microsoft 365)
|
||||
> **Last reviewed:** 2026-10-11 · **Last updated:** 2026-10-25 · **Change:** minor (automation: macro interface; earlier: catalog count 276 / 292, as `cargo xtask parity` measures; automation: typed macros in the engine; several windows: a native window per workbook window, New Window on the same workbook, Arrange All; no score change) · **Target:** Microsoft Excel (Microsoft 365)
|
||||
|
||||
This is the authoritative parity assessment. [`ROADMAP.md`](../ROADMAP.md) summarizes it,
|
||||
[`gaps.md`](gaps.md) lists every shortfall, and the area checklists hold the detail:
|
||||
@@ -13,7 +13,7 @@ generated ribbon checklist is [`parity-checklist.md`](parity-checklist.md).
|
||||
| Number | Value | Kind |
|
||||
|---|---|---|
|
||||
| Feature breadth (weighted by use) | **~75%** | estimated from the area table below; anchored on two measurements |
|
||||
| Ribbon and menu catalog | **274 / 290 (94.5%)** | measured: `cargo xtask parity` (`crates/engine/src/catalog.rs` ∩ registered command ids) |
|
||||
| Ribbon and menu catalog | **276 / 292 (94.5%)** | measured: `cargo xtask parity` (`crates/engine/src/catalog.rs` ∩ registered command ids) |
|
||||
| Worksheet functions, by name | **513 / 523 (98.1%)** | measured: registered functions vs Microsoft's published function list (method below) |
|
||||
| Worksheet functions that do real work | **504 / 523 (96.4%)** | measured: excludes 9 stubs (7 CUBE functions, WEBSERVICE, FILTERXML) |
|
||||
| **Ready for real work** | **~50%** | estimated; written weights below (51.6 computed) |
|
||||
@@ -54,7 +54,7 @@ long `.xls` track, so about half parallelizes (≈30–50 wall-clock hours with
|
||||
COPILOT and PY: **523 functions**. Ours: every `f!(…)` spec in `crates/functions/src/*.rs` plus
|
||||
`SPECIAL_FUNCTIONS` in `crates/calc/src/eval.rs` (evaluator special forms), minus the test-only
|
||||
`ADD2`: **505**, all of them Excel names.
|
||||
- **Ribbon catalog:** `crates/engine/src/catalog.rs` (290 unique ids across 15 tabs) against the
|
||||
- **Ribbon catalog:** `crates/engine/src/catalog.rs` (292 unique ids across 15 tabs) against the
|
||||
313 command ids registered in `crates/engine/src/cmd/*.rs`, recomputed from source by script and
|
||||
identical to the committed `parity-checklist.md`. The catalog is our own transcription of
|
||||
Excel's main ribbon tabs; it leaves out most contextual tabs (Picture Format, Shape Format,
|
||||
@@ -93,7 +93,7 @@ performance 8, stability 8, localization 4, hardware 2, platforms 2, ecosystem 2
|
||||
| Get & Transform, external data, data model | 5 | ~15% | ~10% | 60–100 | Import of CSV/TSV, JSON, HTML. No Power Query editor, no From Web, database or folder sources, no connections or refresh, no relationships or data model, no external workbook links (dropped with a warning) |
|
||||
| Insert objects | 3 | ~55% | ~45% | 25–40 | Pictures, 8 shape kinds (Excel has ~170), text boxes, own icon set, ink with Ink to Shape, hyperlinks, checkboxes. Missing: SmartArt, WordArt, equations, signature line, objects, 3-D models, screenshot, pictures in cells (PR #105) |
|
||||
| Review, comments, protection, collaboration | 4 | ~55% | ~40% | 35–55 | Threaded comments, notes, spelling (system dictionary), thesaurus, sheet and workbook protection, Allow Edit Ranges with Excel-compatible password hashes (#307). Missing: comment navigation, Show Changes, notes-to-comments conversion, accessibility checker, co-authoring, opening password-encrypted files (refused with a message since #17) |
|
||||
| Automation | 4 | ~30% | ~20% | 12–20 | Recorded, editable GridCraft scripts (Automate tab). VBA is out of scope by owner decision and is **dropped** when an `.xlsm` is opened (warning shown). No Office Scripts compatibility |
|
||||
| Automation | 4 | ~30% | ~20% | 12–20 | Recorded, editable GridCraft scripts (Automate tab). Typed macros in the engine (`macro.*`: recorder, runner with input checks and a footprint dry run, debugger; [macros.md](macros.md)), recorded from Automate › Macros, edited and debugged in a step editor, and run from the Macros dialog or a Ctrl+Shift shortcut. VBA is out of scope by owner decision: an `.xlsm` keeps its VBA project unchanged on save and never runs it. No Office Scripts compatibility |
|
||||
| **Weighted** | 100 | **~75%** | **~59%** | **300–485** | |
|
||||
|
||||
## Dimensions
|
||||
@@ -334,11 +334,14 @@ The inventory carried over from the 2026-10-07 ROADMAP.md, updated for what land
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-25 | minor | Automation: macro interface (Automate › Macros, the Macros and Record Macro dialogs, the step editor, shortcuts) (no score change) |
|
||||
| 2026-10-25 | trivial | Catalog count 276 / 292 in the summary table and the method, as `cargo xtask parity` measures (no score change) |
|
||||
| 2026-10-22 | minor | Several windows on the desktop: a native window per workbook window, New Window on the same workbook, Switch Windows and Arrange All; "one window" removed from UI/UX fidelity, Hardware and the interaction-fidelity discount; no score change |
|
||||
| 2026-10-21 | minor | Page Setup paper sizes: `B4`/`B5`/`B6` are the JIS sizes Japanese Excel uses (ISO B4/B5/B6 by name), and Japanese Postcard, Japanese Double Postcard, the chou/kaku envelopes and A6 are added; PDF MediaBox and `file.print` follow, and the Size menu lists them |
|
||||
| 2026-10-18 | trivial | Stale chart notes: all chart kinds survive XLSX round-trip (PRs #158, #161) and trendlines landed |
|
||||
| 2026-10-16 | minor | Advanced Filter copy-to clears only its actual output and rejects overflow without changing cells (#1031) |
|
||||
| 2026-10-15 | minor | Row auto-fit: line breaks count only in wrapping cells (Excel: wrap off shows one line, AutoFit gives one line); unwrapped multi-line cells from files no longer grow rows on open (no score change) |
|
||||
| 2026-10-21 | minor | Automation: typed macros in the engine and `macro.*` commands, no interface yet (no score change); catalog 274 / 290 → 275 / 291 (View › Macros › Record Macro) |
|
||||
| 2026-10-12 | minor | Essentials AutoFit evidence: rows auto-fit oversized fonts on open and row formatting (#833) |
|
||||
| 2026-10-12 | minor | Sheet tabs: reveal the selected tab on navigation and resize while preserving manual scrolling (#681); no score change |
|
||||
| 2026-10-12 | minor | PivotTables: values on rows (Σ Values in Rows at any position, all three layouts; `pivot.moveField` and the field pane move it; `dataOnRows` round-trips) (no score change) |
|
||||
|
||||
+18
-5
@@ -1,10 +1,10 @@
|
||||
# UI parity
|
||||
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-25 · **Change:** minor (AutoSum over a selected row puts its total to the right) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
|
||||
> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-29 · **Change:** minor (Ctrl+arrow and Ctrl+Shift+arrow while pointing a formula reference; earlier: editing a hyperlink loads its existing fields; earlier: AutoSum over a selected row puts its total to the right; ribbon catalog count 276 / 292) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4
|
||||
|
||||
Interaction, controls and feel, against Excel. Part of [target-app-parity.md](target-app-parity.md).
|
||||
**UI/UX fidelity: ~55% ready, 50–80 h** (estimated). Ribbon command coverage is measured
|
||||
separately in [parity-checklist.md](parity-checklist.md) (274 / 290).
|
||||
separately in [parity-checklist.md](parity-checklist.md) (276 / 292).
|
||||
|
||||
Sources: `crates/ui-egui/src` (ribbon, grid, editor, formula bar, dialogs, panes),
|
||||
`crates/engine/src/cmd` (shortcuts in command specs), the owner's black-box notes
|
||||
@@ -106,7 +106,7 @@ otherwise. Number-format and border keys match the physical key (Ctrl+Shift+4, w
|
||||
| Ctrl+S, Ctrl+Shift+S, Ctrl+O, Ctrl+N, Ctrl+W | Cmd | save, save as, open, new, close | present |
|
||||
| Ctrl+F, Ctrl+H, Ctrl+G, Ctrl+K, Ctrl+T | Cmd (Ctrl+H) | find, replace, go to, hyperlink, table | present |
|
||||
| Ctrl+D, Ctrl+R, Ctrl+E, Ctrl+1, Ctrl+9 / 0, Ctrl+Shift+9 / 0 | Cmd (Ctrl+E on Mac: see below) | fill down/right, Flash Fill, Format Cells, hide/unhide | present |
|
||||
| Ctrl+- , Ctrl+Shift+= | Cmd | Delete / Insert cells dialog | present |
|
||||
| Ctrl+- , Ctrl+Shift+= , Ctrl+numpad + | Cmd | Delete / Insert cells dialog | present |
|
||||
| Ctrl+; , Ctrl+` | Control+; | current date, show formulas | present (Mac Control+; added) |
|
||||
| Arrows, Shift / Ctrl / Ctrl+Shift+arrows, Home, Ctrl+Home / End, Page Up / Down, Ctrl+Page Up / Down | Cmd+arrows | navigation and extension | present |
|
||||
| Tab, Shift+Tab, Enter, Shift+Enter, Ctrl+Space, Shift+Space, Delete, Esc | same | entry and selection | present |
|
||||
@@ -142,7 +142,8 @@ otherwise. Number-format and border keys match the physical key (Ctrl+Shift+4, w
|
||||
| Ctrl+Q, Ctrl+A after a function name, Ctrl+Alt+P | same | Quick Analysis, Function Arguments, tooltips | missing |
|
||||
| Ctrl+Shift+A after a function name | Control+Shift+A | argument names: `=SUMIF(` becomes `=SUMIF(range,criteria,sum_range)`, the `(` added when missing; one Undo takes them back | **added** |
|
||||
| Ctrl+Shift+U | Control+Shift+U | expand or collapse the formula bar (`view.expandFormulaBar`), on the grid and while editing; on Windows and Linux it no longer underlines | **added** |
|
||||
| Ctrl+Shift+Page Up / Down, Ctrl+Alt+5, Ctrl+Shift+F1, Ctrl+F5–F10, Alt+F8 / F11 / F12 | same | group sheets, shape cycling, full screen, window keys, macros, VBA, Power Query | missing |
|
||||
| Alt+F8, Ctrl+Shift+letter | Option+F8, Cmd+Shift+letter | Macros dialog, a macro's own shortcut (letters GridCraft uses keep their meaning) | **added** |
|
||||
| Ctrl+Shift+Page Up / Down, Ctrl+Alt+5, Ctrl+Shift+F1, Ctrl+F5–F10, Alt+F11 / F12 | same | group sheets, shape cycling, full screen, window keys, VBA, Power Query | missing |
|
||||
| — | Cmd+= (calculate), Cmd+T (references while editing; F4 works), Shift+F7, Cmd+Shift+L | Mac-only extras | missing (Cmd+= is egui's zoom key; no thesaurus or Modify Cell Style) |
|
||||
|
||||
On Windows, AltGr arrives as Ctrl+Alt: a Ctrl+Alt key that also types a character is treated as
|
||||
@@ -151,9 +152,15 @@ a character (e.g. Ctrl+Alt+A for `ä`) never trigger a shortcut, Ctrl+letter one
|
||||
|
||||
## Dialogs
|
||||
|
||||
Insert Hyperlink loads an existing cell link's address, display text and ScreenTip for editing
|
||||
(#2876). Keeping the display text unchanged preserves the cell's number or formula; Cancel
|
||||
keeps the original link and an edit can be undone in one step.
|
||||
|
||||
Format Cells, Insert Function/Formula Builder, Find and Replace, Go To and Go To Special, Sort,
|
||||
Name Manager, Data Validation, Paste Special, Conditional Formatting manager, Page Setup, Goal
|
||||
Seek, Scenario Manager, Data Analysis, Forecast Sheet and others exist. Missing or partial: Options,
|
||||
Seek, Scenario Manager, Data Analysis, Forecast Sheet, Macros (macros, Automate scripts and a
|
||||
note on a VBA project; Run, Step Into, Edit, Create, Delete, Options), Record Macro, the macro step
|
||||
editor (Excel's VBA editor has no counterpart here) and others exist. Missing or partial: Options,
|
||||
Solver, Power Query editor, Insert Object, Equation editor, Protect/Allow Edit Ranges,
|
||||
Workbook Links, Accessibility checker. Like Excel's, every dialog opens centred and moves by its
|
||||
title bar, keeping its place while it is open (#1250).
|
||||
@@ -187,8 +194,14 @@ scrollbar bugs 2–4 · pixel/feel pass against observed Excel 7–12.
|
||||
|
||||
| Date | Change | Summary |
|
||||
|---|---|---|
|
||||
| 2026-10-29 | minor | Formula pointing from the keyboard: Ctrl+arrow (⌘arrow) moves the pointed cell to the edge of its data and Ctrl+Shift+arrow (⌘⇧arrow) extends the pointed range there, as the same keys move and extend the selection, so typing `=COUNTIF(`, clicking the top of a column and pressing ⌘⇧↓ points at the whole column of data; the keys used to move or extend by one cell. A range reaching the sheet edge is written as whole columns or rows (`A:B`). The editor reads the modifiers of the arrow's own key event, so a keystroke pressed and released within one frame (a control client's `ui.key`, an automation tool) keeps its Shift and Ctrl |
|
||||
| 2026-10-29 | minor | Insert Hyperlink opens with an existing link's address, display text and ScreenTip (#2876). An address-only edit preserves the cell's content and formula; Cancel leaves the link unchanged, and Undo restores the edit |
|
||||
| 2026-10-27 | minor | Ctrl with the numpad's + inserts rows, columns or cells as Ctrl+Shift+= does, as in Excel; it did nothing because the numpad key arrives without Shift |
|
||||
| 2026-10-25 | minor | AutoSum (Alt+=, Home › AutoSum) over a selected row puts its total to the right of the row, or in its last cell when that is empty, as in Excel; it wrote a one-cell total under each cell. A selection's empty last column takes each row's total and its empty last row each column's, with the block's total in the corner when both are empty |
|
||||
| 2026-10-25 | minor | Paste Name lays out only the names in view, so a workbook with thousands of names opens it at once; the arrow keys still scroll the chosen name into view. Go To matches a `Sheet2!Name` reference and hides a workbook name behind the active sheet's own name in any case and script (`Größe` = `GRÖSSE`), as formulas do. The two Page Layout view rows are one; the 0.7 in side margin used when Align with Page Margins is off is marked unverified |
|
||||
| 2026-10-24 | minor | Macro step editor: step list with breakpoints, drag to reorder and a command picker, an inspector with reference boxes, a debug bar and grid overlays |
|
||||
| 2026-10-24 | minor | Automate › Macros group (Record Macro, Use Relative References, Run Macro), recording status in the status bar, Macros dialog with macros beside scripts, Record Macro dialog, Ctrl+Shift+letter macro shortcuts; View › Macros keytips open the dialogs |
|
||||
| 2026-10-24 | minor | Ribbon catalog count 275 / 291 → 276 / 292 (View › Macros › Record Macro) |
|
||||
| 2026-10-24 | minor | Go To lists the named ranges on every sheet, as Excel's does ("The Go to popup window shows named ranges on every worksheet in your workbook"): a name scoped to another sheet appears as `Sheet2!Name`, and hidden names, constants and formulas are left out. It keeps the references entered (newest first, ten at most), and a double-click on an entry goes there. The Reference box and `edit.goTo` take several names or references separated by commas (`Price, Type`, `B14:C22,F19:G30,H21:H29`) and select them all; `edit.goTo {list: true}` returns the list |
|
||||
| 2026-10-24 | minor | Right-clicking the status bar opens a menu of its statistics, as in Excel ("If you want to customize the status bar, right-click it, and then click the options that you want"): Average, Count and Sum are on by default, and Numerical Count, Minimum and Maximum are new and off by default. The choice is a preference (`view.statusBar`), kept in `prefs.json` |
|
||||
| 2026-10-24 | minor | Ctrl+Shift+U (Control+Shift+U on a Mac) expands or collapses the formula bar, as in Excel ("Expand or collapse the formula bar. Ctrl+Shift+U"), on the grid and in the cell editor or formula bar while editing. On Windows and Linux it used to underline the selection, as Ctrl+U does. The command is `view.expandFormulaBar` (`{on?}`, toggles without it) |
|
||||
|
||||
Reference in New Issue
Block a user