Commit Graph
2630 Commits
Author SHA1 Message Date
Brandon Thomas 4b8725b29b Merge PR #2314 2026-10-23 10:11:12 -04:00
Brandon Thomas e92d8b4e48 Merge PR #2295 2026-10-23 10:11:12 -04:00
Brandon Thomas 817cf80470 Merge PR #2292 2026-10-23 10:11:12 -04:00
Brandon Thomas 2d0fe1f01e Merge PR #2289 2026-10-23 10:11:12 -04:00
Brandon Thomas d1399ff2ca Merge PR #2279 2026-10-23 10:11:12 -04:00
Brandon Thomas 67f13d804c Merge PR #2275 2026-10-23 10:11:12 -04:00
Brandon Thomas 50e5a3e928 Merge PR #2267 2026-10-23 10:11:11 -04:00
Brandon Thomas a725f2dc67 Merge PR #2266 2026-10-23 10:11:11 -04:00
Brandon Thomas 27a33f1ee0 Merge PR #2260 2026-10-23 10:11:11 -04:00
Brandon Thomas 5d0fc0b06a Merge PR #2245 2026-10-23 10:11:11 -04:00
Brandon Thomas 3c62ae27fd Merge PR #2244 2026-10-23 10:11:11 -04:00
Brandon Thomas 073ae2df39 Merge PR #2233 2026-10-23 10:11:11 -04:00
RedBearAKandClaude Opus 5.5 8eda577545 Page Up into frozen rows scrolls the rows below back to the top
With row 1 frozen, Page Up could land on A1, which is always on screen, while the scrolled rows
stayed a page down (row 10, Page Down, Page Up, Page Up ends on row 1 with rows 2-9 hidden). The
same held for Ctrl+Up into frozen rows and Ctrl+Left / Alt+Page Up into frozen columns.

Arrow and paging keys now record the cell they moved from (`GridState::reveal_from`). When such a
move goes from the scrolled rows (columns) into the frozen ones, the scrolled area goes back to
its top (left). Other reveals (clicks, Find, Go To, Name Box, Undo, the control channel, formula
pointing) leave it unset and keep the view where the user scrolled it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 03:20:18 -08:00
xc4pr 7c1ec1df7d Fill handle: test the Auto Fill Options button on a right-to-left sheet, merge the duplicate fill handle row (#1403) 2026-10-23 02:37:49 +02:00
xc4pr 60776ff4de Hyperlinks: test that Ctrl+click beside the link text still extends the selection (#1198) 2026-10-23 02:23:37 +02:00
xc4pr d19f656014 Fill handle: test the Auto Fill Options button's workbook-changed rule, report an undo error, document it (#1403) 2026-10-23 02:17:38 +02:00
xc4pr 6febf5442f Fill handle: Auto Fill Options button after a drag offers Copy Cells, Fill Series and the formatting modes (#1403)
A left-drag of the fill handle filled at once and offered no choice. A button now appears below the filled range; its menu undoes that fill and reruns edit.autoFill in the chosen mode. It goes away when the workbook changes or the selection moves.
2026-10-23 01:25:21 +02:00
xc4pr d046ba6aa7 Hyperlinks: Ctrl+click on a link follows it instead of extending the selection (#1198)
A Ctrl+click went to the add-to-selection branch, so the link under the pointer was never followed. It now goes through follow_link like a plain click (asks before opening web addresses, jumps to references).
2026-10-23 00:44:34 +02:00
Brandon ThomasandClaude Opus 5.5 ac8da7d49c Release 0.11.1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
v0.11.1
2026-10-22 16:16:52 -04:00
Brandon ThomasandClaude Opus 5.5 10fb7d566e macOS packaging: retry stapler when Apple's CloudKit drops the connection
stapler staple/validate fetch the notarization ticket from CloudKit, which
sometimes fails with "The network connection was lost" (Error 68). That
failed this release's macOS job twice in a row. Both steps are idempotent,
so retry up to 5 times with backoff, like hdiutil_retry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 16:16:17 -04:00
Brandon ThomasandClaude Opus 5.5 73af8c6a6d Release 0.11.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 15:38:06 -04:00
D. Fender 09f78cb3a8 Tables: Format as Table inside a table restyles it
Home > Format as Table on a cell or range inside an existing table
failed with "A table can't overlap another table." Excel applies the
picked style to that table instead. The command now returns that table
and its range, as it does for a new table. A range crossing a table's
edge is still rejected.

Without a range, the active area of the selection decides, as it does
for Insert > Table. The native Format menu's Format as Table now runs
home.formatAsTable instead of insert.table.
2026-10-22 11:43:58 -07:00
dotCooCoo 450f457535 Calc: a formula calculated inside another one shares its stack budget
A cell that another cell's formula calculates on demand got its own
evaluator, which started a new 896 KiB stack budget wherever it was
called. Two cells that each count down from 131 with a LAMBDA, one read
at the deepest call of the other, overflowed a 1 MiB thread.

The budget of a formula calculated on demand is now counted from where
the outermost formula started. When it runs out there, the formula is
calculated again from the top, as a formula needed at the nesting limit
is, so its value does not depend on the order cells are calculated in.
2026-10-22 11:06:01 -07:00
coolvitto a8e2ed31b1 Add extra Japanese translations for UI elements
Added extra labels for ribbon menus and pop-ups in Japanese.
2026-10-23 03:05:42 +09:00
dotCooCoo a0fa6c6e6e Merge main into fix/lambda-recursion-stack 2026-10-22 10:57:09 -07:00
NetterSilverandClaude Haiku 5.5 9cbb674c55 Print numbers near f64::MAX in a form that parses back
Found while testing crates/formula/src/printer.rs:134

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 19:34:06 +02:00
vangel76 1da640abcc Merge upstream/main into fix/control-auth-777 2026-10-22 19:32:43 +02:00
BFlat d1db71d0f0 Merge pull request #2209 from Higgs32584/fix/2178-script-trust-gate
scripts: require workbook trust before running embedded scripts (#2178)
2026-10-23 00:25:36 +07: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
Higgs32584 6dedb9310b Merge remote-tracking branch 'origin/main' into fix/2178-script-trust-gate 2026-10-22 12:32:28 -04:00
aiongg d4db58cde4 G3: feedback from a command that switched windows shows in the window it left active
after_engine, show_open_warnings and alert bring the per-window state in line
with the engine's active window first, so a notice, message or toast from
opening a workbook (also by a script on the session) lands in the opened
workbook's window instead of being parked with the previous one.
2026-10-22 16:29:24 +00: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 5bcbaacce7 Snapshot: catalog installed fonts before rendering
The offscreen snapshot never started the background font scan the app runs
from its first frame, so a workbook's installed fonts (Helvetica Neue, say)
were drawn in the Calibri substitute. Catalog them up front and run a few more
frames so they bind, as in the app.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 16:02:58 +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
Hamza 99f92f4a28 range.setValues: parse: false stores a string as the text it is (#2255)
Every string in `values` went through typed input, which reads it with en-US
rules, and nothing said so:

    "05.04.2026"  (dd.mm.yyyy)  → the date 4 May 2026
    "02/11/2026"  (dd/mm/yyyy)  → the date 11 February 2026
    "2.500"       (thousands)   → the number 2.5
    "4.25 %"      (text)        → 0.0425, formatted 0.00%

A workbook built from Turkish-format exports therefore held different values
than the script put in it, and the only way to notice was to read the saved
file back cell by cell.

`parse: false` stores every string as the text it is. A formula is still a
formula — `=` means a formula in a cell, and the ticket asks for that — and
numbers, booleans and null are unaffected either way. A leading apostrophe is
part of the text rather than the marker that makes it text, since nothing is
being marked.

The default is unchanged: `parse` is absent in every existing call, and those
calls behave exactly as before. A `parse` that is neither true nor false is a
usage error rather than a quiet default.

The test pins both sides — the swapped date, the decimal point and the
percentage under today's default; each of the six strings stored verbatim with
`parse: false`, with `=1+1` still calculating 2; numbers and booleans either
way; and the refused `parse` values. Without the change the first string comes
back as a date.
2026-10-22 18:05:53 +05: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
Hamza 56207f2d3c run --script: a line's unknown key stops the run (#2256)
A script line whose parameters sat under a key the runner does not read ran the
command with no parameters at all, and the run still exited 0:

    {"command":"home.insertSheet","args":{"name":"Report"}}
    {"command":"home.insertSheet","name":"Report2"}

made Sheet2 and Sheet3 instead of Report and Report2, and said nothing. A typo
in a script produced a valid, wrong workbook.

A line is read for `command` (or `id`) and `params`; any other key is a usage
error now, naming the keys and the line, and the run stops before the workbook
is written:

    gridcraft-cli: s.jsonl:2: unknown key `args`: a line is {"command": "id", "params": {…}}

The usage text says what a line looks like, which is the other half of the
report — the format was not written down anywhere, so the reporter found it by
trying `args`, a flat object and `id` in turn.

Tested at both levels: `parse_command` for the keys, their sorted list when
there are several, and the forms that are read (`id`, `params`, `id={json}`,
comments); and a run of a four-line script that stops at line 2 without writing
the file, then the same script corrected, whose sheets come out named. Without
the check the mistaken line parses again.

An unknown key *inside* `params` is still ignored — the rest of #2256 — and
needs the command's parameter list, which the engine does not expose yet. Noted
on the issue.
2026-10-22 17:50:09 +05:00
BFlat a1c84547e3 Merge pull request #2249 from storytold/readme/app-icon
README: the app icon above the title
2026-10-22 18:01:55 +07:00
bflatastic 7ed494691c README: the app icon above the title 2026-10-22 17:57:56 +07:00
NetterSilverandClaude Opus 5.5 8307d3cb05 Treat empty instance_num in TEXTBEFORE/TEXTAFTER as 1
Found while testing crates/functions/src/text.rs:553

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 08:25:54 +00:00
NetterSilverandClaude Opus 5.5 c39d2ad197 Keep '!' in structured-reference columns intact on XLSX save
Found while testing crates/xlsx/src/fmla.rs:101 (with crates/formula/src/printer.rs:181)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-22 09:47:21 +02:00
Tyrel HavemanandClaude Opus 5.5 742a6e8535 functions: REGEXREPLACE bounds each replacement as it expands it (#2162)
From the local CodeRabbit review: the result was bounded per match, but
one match's expansion was built whole first, so a replacement repeating
$0 10,000 times over a 32,767-character match allocated about 330 MB
before the check. The replacement is now split into pieces as regex's
Captures::expand reads it (template_expands_like_regex checks they agree)
and appended piece by piece within the bound: 327 MB peak became 7 MB
(regexreplace_bounds_its_result).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Nnsfu3QGqjD1aFZbBgUWP
2026-10-21 23:45:50 -07:00
Tyrel HavemanandClaude Opus 5.5 e177bf1c44 calc: hardening: overlong digits, NaN indexes, REGEXREPLACE output (#2163, #2162)
- Text coercion: digits too many for a finite number are not a number,
  and a fraction must come out finite, so "1 <400 digits>/<400 digits>"
  (inf/inf) no longer coerces to NaN (test overlong_digits_stay_text).
- VLOOKUP/HLOOKUP and the D-functions' field index refuse NaN, which
  passed their "< 1" checks and became index 0, then underflowed
  (test nan_index_is_a_value_error; hostile_inputs_never_panic now also
  passes NaN and infinity to every function).
- REGEXREPLACE replaces match by match and gives up once the result can
  only be too long, instead of building every replacement first: the
  32,767 x 32,767 worst case went from 1.05 GB peak to 7 MB
  (test regexreplace_bounds_its_result).

#2165 (MAP's size check) is already fixed on main by the broadcast cap
(836b2698, #2182), whose test covers MAP.

Also adds my contributors/people.toml entry.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019Nnsfu3QGqjD1aFZbBgUWP
2026-10-21 23:41:28 -07: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