range.fill parsed the input once at the active cell, then put back the old
style of every target that already held a cell, so 5%, $5, 3/4/2026 or
=DATE(...) lost their automatic format there (0.05, 5, 46085), and empty
targets took the active cell's style. Each target now gets what
input_to_cell gives for it (parsed once per distinct style), as typing does.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Undo stored the selection at the moment of undoing (wherever the user had
clicked) in the redo entry, so redo jumped back there instead of to the
cells it changed. The redo entry now keeps the undone step's selection.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
selection.set refuses a cell the protection forbids selecting, selection.move steps on to the
next selectable cell, and review.protectSheet keeps the sheet's current selection rights unless
told otherwise. The dialog gains the two select options and insert/delete columns.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Ctrl+Shift+Arrow (and Shift+Arrow, Shift+PageUp/PageDown, Alt+Shift+PageUp/PageDown,
Ctrl+Shift+Home/End, Shift+Home) extend the selection by moving its far corner while the
active cell stays on the anchor. After the move the grid set `ensure_visible`, but the
scroll-into-view step in `grid::show` always targeted `selection.active`, which is the
anchor and is already on screen. So the view never followed the extension past the last
visible row or column.
The keyboard extension paths now also set `GridState::reveal_moving_end`, and the
ensure-visible step then scrolls to `Selection::moving_end()`, the corner opposite the
anchor. `selection.move` now uses that same helper (it had the logic inline) to find where
an extension starts. Other ensure-visible callers (Go To, Name Box, plain moves) still
reveal the active cell.
Regression test: crates/ui-egui/tests/selection_scroll.rs drives Ctrl+Shift+Up/Down/
Left/Right, Shift+Down/Right and Ctrl+Shift+Home/End through real egui key events and
checks that the moving end ends up on screen while the anchor and active cell stay put.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`input` is documented as "text, number or =formula", but only a string
was read; a JSON number was dropped and the command failed (or, on 0.5.0,
silently cleared the cell). The undocumented `value` alias already went
through `json_to_input`. Both keys now share that converter, so numbers
and booleans are entered as typed. The MCP `set_cell` schema types
`input` as string, number or boolean to match its description.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
FnSpec::accepts holds the arity check (including Excel's 255-argument
limit) that functions::call and the entry check both use, so entry also
refuses 256 arguments to a variadic function. The evaluator's own check
in calc is unchanged.
Claude-Session: https://claude.ai/code/session_01BGBBGBC2gRpQ7GDuwQ6WPt
A formula calling a built-in function with too few or too many
arguments (=DATEDIF(a,b), =SUM(), =DATE(2024,1)) was stored and showed
#VALUE! with no hint about what was wrong. Entry now refuses it with
the function's signature, e.g. "DATEDIF has too few arguments. Use
DATEDIF(start_date, end_date, unit)", and leaves the cell unchanged.
Only registered functions are checked: special forms the evaluator
handles itself (IF, LET, LAMBDA...), LAMBDA names and unknown functions
are skipped. Paste and Find & Replace keep turning input that fails
entry into text. DATEDIF's description now lists its units.
Fixes#363
Claude-Session: https://claude.ai/code/session_01BGBBGBC2gRpQ7GDuwQ6WPt
Issue #348: copying multiple selections works when the areas line up, and explains when it can't
# Conflicts:
# crates/engine/src/tests.rs
# crates/ui-egui/tests/clipboard.rs
Copying a multi-area selection always failed in the engine, and the UI
dropped the error, so Ctrl+C did nothing and Ctrl+V had nothing to
paste. Now, as in Excel:
- Areas that cover the same rows (or the same columns) and don't
overlap are copied as one block, slid together left to right (top to
bottom). The clipboard keeps a snapshot with the block packed at the
first area's place; formulas travel as their values, and column
widths or row heights, comments, pictures and merges come along.
- Other layouts, Cut of several areas and Paste Link of a multi-area
copy are refused with "This action won't work on multiple
selections."; Copy and Cut from the keyboard, ribbon and context menus
now show that message instead of failing silently.
- The marching ants go around each copied area.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Audited the grid, ribbon and editor key handling against Microsoft's
published Windows and Mac shortcut lists. New keymap.rs binds the ones
whose feature already exists: Ctrl+Shift+~!@#$%^&_ number formats and
borders, Ctrl+2-5, Ctrl+Shift+: time, Ctrl+' / Ctrl+Shift+" from the cell
above, Ctrl+L, Ctrl+Shift+L, Ctrl+P, Ctrl+Alt+V, Alt+=, Alt+Down,
Alt+Page Down/Up, Ctrl+Shift+Home/End, Shift+Home, the F-key family
(Shift+F2/F3/F9/F10/F11, F5, F7, F12, Ctrl+F1/F3/F4, Alt+F1, Ctrl+Alt+F9)
and the Excel for Mac variants (Cmd+L/E alignment, Control+E Flash Fill,
Cmd+Option borders, Cmd+Shift+K/J/H/R). Number-format keys match the
physical key; AltGr text never triggers a Ctrl+Alt shortcut. Fixes the
command catalog's wrong shortcut labels and documents the audit.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Model: Sheet.edit_ranges (title, ranges, optional password hash,
security descriptor) and gridcraft_model::password: the ECMA-376 legacy
16-bit hash and the salted, iterated SHA-2 hash (SHA-512/384/256; other
algorithms, damaged hashes and spin counts over 10,000,000 are refused).
New passwords use SHA-512, spin count 100000. Adds the sha2 crate.
- XLSX: <protectedRanges> read and written; an unreadable password keeps
the range locked.
- review.allowEditRanges {action: list|add|modify|delete|unlock, ...}: empty
params list; ranges can't change while the sheet is protected; unlock
verifies the password for the session. Not journaled (passwords).
- Locked-cell checks (commit guard, cell.set, picture in cell) let edits
through in open or unlocked ranges; ranges move with structural edits.
- Ribbon button and a form dialog (menu invocation only).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
insert.checkbox existed but had no ribbon button, replaced existing values
with FALSE and was written to XLSX as an invalid number format. Now:
Insert > Checkbox (Cell Controls); empty cells start FALSE, other values
stay and display as General; toggling an emptied box checks it and only
checkbox cells toggle; Delete unchecks first, then removes the boxes; XLSX
reads and writes the checkbox as Excel's feature-property-bag cell control
([MS-XLSX] 2.1.22, 2.3.9, 2.4.99).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
New command home.insertCopiedCells: makes room at the active cell and
pastes the clipboard there, as one undo step. Whole copied rows or columns
insert whole rows or columns; a copied block shifts the cells down or right
(Insert Paste dialog from the menu). A cut of whole rows or columns moves
them and closes the gap (Insert Cut Cells). The cell and header context
menus show the entry while the clipboard holds a copy.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Spreadsheets put an HTML table next to the tab-separated text; Excel
(x:num) and LibreOffice Calc (sdval) record each cell's number there.
Paste now reads the HTML flavour (new paste_html host service, arboard on
desktop) and edit.paste takes an optional html param: a field that would
paste as text becomes the recorded number when the HTML cell shows the
same text, with an inferred format ("Rp"#,##0.00) that renders it back
identically.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A plain drag copies a lone number and extends every other series; Ctrl+drag
(Cmd on macOS) now does the opposite, as in Excel: a lone number counts up
by 1, and numbers, dates, lists and `Item 1`-style text are copied.
edit.autoFill gains mode "alternate" for this.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cell_param returned None for any cell argument it could not parse, so every
command that falls back to the selection (cell.set, view.freezePanes,
formulas.trace*, insert.* and others) silently used the active cell instead:
set_cell with cell "ZZZZ0" wrote A1 and reported success. It also dropped a
sheet prefix, so "Other!B2" was applied to the active sheet.
cell_param now returns Result<Option<CellRef>>. An absent or null value is still
None, so the selection fallback is unchanged; an unparsable or non-string value
is an error; a sheet prefix naming a sheet other than the one the command works
on is an error.
cell_param_on is for the commands that resolve their own sheet (cell.get, links,
comments, notes, checkboxes, chart placement, paste): they accept a prefix naming
that sheet. cell.set resolves its own target sheet and still writes to another
existing sheet.
cell.set treated a missing input as an empty string, so a call that forgot the
input (or sent it under the wrong key) cleared the cell and reported success.
It now returns an error when neither `input` nor `value` is given. An explicit
empty string still clears the cell.
Add static pictures inside cells with XLSX roundtrip
Rebased onto main (conflicts with #83/#98 and main resolved by maintainer); the contributor's branch does not allow maintainer edits.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
edit.copy and edit.cut build and return the HTML table only with
{"html": true}; the UI asks for it when the host can publish HTML, so
MCP and control-channel responses are not bloated by up to 4 MB. Tests
cover the default, html:false and html:true.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>