Commit Graph

422 Commits

Author SHA1 Message Date
Brandon Thomas 9da7acf846 Merge pull request #350 from LloydNicholson/fix/multi-select-resize
Issue #341: resizing Ctrl-selected rows or columns resizes all of them

# Conflicts:
#	crates/engine/src/tests.rs
2026-10-11 12:04:14 -04:00
Brandon Thomas 19b0dc6e97 Merge pull request #351 from LloydNicholson/fix/protect-password-mask
Issue #347: Protect Sheet and Unprotect Sheet mask the password
2026-10-11 12:04:13 -04:00
Brandon Thomas ea3e0ce0c5 Merge pull request #355 from LloydNicholson/fix/journal-redact-passwords
Journal: sheet protection passwords aren't recorded in plain text

# Conflicts:
#	crates/engine/src/tests.rs
2026-10-11 12:04:12 -04:00
Brandon Thomas 2f6c2cd486 Fix-up: Spanish and German strings for Interface size (#294 met #242/#271); rustfmt after merges
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:59:24 -04:00
Brandon Thomas 4f7144d753 Merge pull request #295 from vangel76/feat/view-formula-bar-zoom100
View: Formula Bar and Zoom 100% as engine commands (#289)

# Conflicts:
#	ROADMAP.md
#	crates/ui-egui/src/control.rs
#	crates/ui-egui/src/lib.rs
#	docs/target-app-parity.md
2026-10-11 11:45:32 -04:00
Brandon Thomas 8f3d13a115 Merge pull request #294 from vangel76/fix/interface-scale
View: Interface size preference scales the whole window (#230)

# Conflicts:
#	ROADMAP.md
2026-10-11 11:45:01 -04:00
Brandon Thomas 505d001ad4 Merge pull request #293 from vangel76/fix/name-manager-new
Name Manager: New… and Edit… open the name dialog and return to the manager (#238)

# Conflicts:
#	crates/engine/src/tests.rs
2026-10-11 11:45:00 -04:00
Brandon Thomas fb9266f479 Merge pull request #271 from arekown/german-translation
Add German (de) interface language

# Conflicts:
#	ROADMAP.md
#	crates/ui-egui/src/i18n.rs
2026-10-11 11:44:58 -04:00
Brandon Thomas ea0e971e27 Merge pull request #228 from webdevtodayjason/fix/200-formula-helpers
Keep formula helpers inside the worksheet when scrolling (#200)

# Conflicts:
#	contributors/people.toml
#	docs/ui-parity.md
2026-10-11 11:44:56 -04:00
Brandon Thomas a1f97c2f06 Merge pull request #218 from MarekSpodBiedry/issue-190
Resize charts from the corner handle and the Size fields, keep typed chart and table names (#190)
2026-10-11 11:44:55 -04:00
Brandon Thomas 0a65ffc31a Merge pull request #219 from Zarvinx/fix-keyboard-chart-delete
Delete selected charts with the keyboard

# Conflicts:
#	ROADMAP.md
2026-10-11 11:44:53 -04:00
Brandon Thomas ad8aeca851 Merge pull request #217 from MarekSpodBiedry/issue-198
Do not draw the selection over the frozen pane when it lies only in the scrolling pane (#198)
2026-10-11 11:44:51 -04:00
Brandon Thomas 90dcc2b329 Merge pull request #297 from vangel76/feat/trimrange
Functions: TRIMRANGE (#296)

# Conflicts:
#	README.md
#	ROADMAP.md
#	crates/xlsx/src/tables.rs
#	docs/function-parity.md
#	docs/gaps.md
#	docs/target-app-parity.md
2026-10-11 11:44:50 -04:00
Brandon Thomas c2c381d55d Merge pull request #292 from vangel76/feat/bahttext
Functions: BAHTTEXT (#291)

# Conflicts:
#	README.md
#	ROADMAP.md
#	docs/function-parity.md
#	docs/gaps.md
#	docs/target-app-parity.md
2026-10-11 11:44:48 -04:00
Brandon Thomas fa4d2e5741 Merge pull request #280 from vangel76/feat/percentof-euroconvert
Functions: PERCENTOF and EUROCONVERT (#279)

# Conflicts:
#	README.md
#	ROADMAP.md
#	crates/functions/src/financial.rs
#	docs/function-parity.md
#	docs/gaps.md
#	docs/target-app-parity.md
2026-10-11 11:44:46 -04:00
Losminek dd8016225d Merge origin/main into cs-pr
The only conflict was contributors/people.toml: other contributors appended
their entries at the end of the file in the meantime. All entries kept.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:44:24 +02:00
Brandon Thomas f91f7bc154 Merge pull request #276 from vangel76/feat/odd-bond-functions
Functions: ODDFPRICE, ODDFYIELD, ODDLPRICE, ODDLYIELD (#275)

# Conflicts:
#	ROADMAP.md
2026-10-11 11:43:49 -04:00
Brandon Thomas 59deadc4c3 Merge pull request #285 from vangel76/fix/currency-paste
Clipboard: pasted currency cells keep the number from the clipboard HTML (#257)
2026-10-11 11:43:48 -04:00
Brandon Thomas 96703b79d4 Merge pull request #278 from vangel76/fix/fill-handle-ctrl-series
Fill handle: Ctrl+drag swaps copying and extending a series (#264)

# Conflicts:
#	ROADMAP.md
2026-10-11 11:43:47 -04:00
Brandon Thomas 4ea12e967b Merge pull request #242 from diegozpy/i18n/es-interfaz
i18n: Spanish interface translation (the Español option only localized formulas)
2026-10-11 11:42:40 -04:00
Brandon Thomas f6628954fc Merge pull request #263 from alexanderalber/fix/201-filter-header-row
AutoFilter: a selected header row filters the data below it; search box in the filter menu (#201)
2026-10-11 11:42:38 -04:00
Brandon Thomas 3b0da1d760 Merge pull request #254 from nihilist-narwhal/fix-typed-dates-and-times
Typed dates before 1900 stay text; typed times past 24 hours keep their hours
2026-10-11 11:42:37 -04:00
Brandon Thomas 77e6a4a46d Merge pull request #251 from nihilist-narwhal/fix-conditional-format-layers
Conditional formatting: apply every matching rule; Cell Value rules as in Excel
2026-10-11 11:42:36 -04:00
Brandon Thomas 47dcd84e04 Merge pull request #282 from vangel76/fix/name-box-enter
UI: Name Box Enter goes to the typed cell, not the one below (#247)
2026-10-11 11:24:39 -04:00
Brandon Thomas 331a535d3c Merge pull request #277 from vangel76/fix/linux-font-panic
UI: skip system fonts egui can't parse instead of panicking at startup (#226)
2026-10-11 11:24:38 -04:00
Brandon Thomas c97f025459 Merge pull request #248 from MacJedi42/fix/excel-15-digit-display
Accuracy: round an exact tie at the 16th significant digit toward zero
2026-10-11 11:24:37 -04:00
Brandon Thomas 85f0320f5c Merge pull request #267 from alexanderalber/fix/64-write-at-as-single
XLSX: write `@` as `_xlfn.SINGLE` in a dynamic-array formula (#64)

# Conflicts:
#	crates/xlsx/src/tests/roundtrip.rs
2026-10-11 11:24:36 -04:00
Brandon Thomas 1eb7796724 Merge pull request #262 from alexanderalber/fix/235-icon-set-cfvo
XLSX: write one cfvo per icon in icon-set rules (#235)

# Conflicts:
#	contributors/people.toml
2026-10-11 11:24:22 -04:00
Brandon Thomas aa7c97adb9 Merge pull request #255 from nihilist-narwhal/fix-sort-and-duplicates
Sort and Remove Duplicates: compare text the way Excel does
2026-10-11 11:24:00 -04:00
Brandon Thomas d059a5b5a4 Merge pull request #252 from nihilist-narwhal/fix-choose-array-index
CHOOSE: pick element by element when the index is an array
2026-10-11 11:23:59 -04:00
Brandon Thomas 46cd4d7b72 Merge pull request #250 from nihilist-narwhal/fix-criteria-blank-cells
Criteria: text patterns don't match blank cells
2026-10-11 11:23:58 -04:00
Brandon Thomas e1939aac68 Merge pull request #272 from ttrudging/fix/paste-deadlock
Fix Paste button deadlock in ribbon
2026-10-11 11:23:32 -04:00
Brandon Thomas 96af804cb5 Merge pull request #223 from Dejan-Port/fix-web-edit-loss
fix: Save toolbar button and saveChanges dialog discard an in-progress cell edit
2026-10-11 11:23:30 -04:00
Brandon Thomas 1cd3d4af5d Merge pull request #231 from pkibiego/security/quick-xml-0.41
XLSX: bump quick-xml 0.38 -> 0.41 (RUSTSEC-2026-0195 namespace-declaration DoS)
2026-10-11 11:23:29 -04:00
lloyd c1a500c5b9 Journal: leave sheet protection passwords out
Protect Sheet and Unprotect Sheet were journaled with their `password`
param, so the password showed in plain text in the journal dialog and
`engine.journal`, and a recorded script (Automate > Record Actions)
saved it in the workbook's hidden `_sc_scripts` name.

Session::execute now journals the params without secret ones
(`password`, or a name ending in `Password`). A replayed entry runs
without that param: Protect Sheet protects with the same options but no
password, and the matching Unprotect Sheet still succeeds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 17:17:30 +02:00
lloyd d665499055 Clipboard: copy lined-up multiple selections; say why others can't be copied (#348)
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>
2026-10-11 17:02:39 +02:00
lloyd a8eb09604d Review: mask the Protect / Unprotect Sheet password (#347)
The password fields in Protect Sheet and Unprotect Sheet were plain
text fields, so the password was readable on screen and in the
accessibility tree. A new Password form field masks it as typed and adds
a "Show password" check box that reveals it on purpose (off each time
the dialog opens).

These are the only password fields in the UI: Protect Workbook runs
without a dialog and encrypted files are refused on open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:55:13 +02:00
Sasha Syrotenko f7f7769460 Fold 'SUM', 'COUNT' and 'AVERAGE' over ranges in place 2026-10-11 17:50:30 +03:00
lloyd 206d837639 Grid: header drags resize every Ctrl-selected row or column (#341)
Dragging a header boundary inside a selection of whole columns (rows)
sized only the current area, so with B, D and F Ctrl-selected only the
last-selected one changed. The drag now targets every whole-column
(whole-row) area of the selection, as Excel does; lines outside the
selection, and partial-cell areas, keep their size.

home.columnWidth / home.rowHeight accept a comma-separated list of
areas ("B:B,D:F") so the drag stays one command and one undo step.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:48:43 +02:00
Losminek 5f346dc3cc i18n (cs): terminology review fixes
An independent review of the table found two meaning errors:
"Enter" is both the status-bar mode and the tooltip of the confirm
button next to the formula bar, so "Zadat" fits both places better
than "Zadavani"; "Convert to Range" uses "oblast" (as "Oblast tisku"
does), not "rozsah".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:47:02 +02:00
Losminek f44b31e154 i18n: cover Czech in the interface-language tests
- The catalog-completeness test requires a non-empty Czech label for
  every key of the reference (Japanese) table.
- Czech context keys: `Page Layout|Orientation` and `Orientation` get
  different labels; unknown text such as `Sheet1!A1` passes through.
- `cs-CZ` / `cs_CZ.UTF-8` system tags and `parse("cs")` resolve to Czech.
- The ribbon renders after switching to Czech, and the choice persists
  in UiState.
- Czech keeps canonical formula entry (`=SUM(1.5,2.25)`), and `=SUMA(1;2)`
  is not accepted.
- The UI font draws every Czech accented letter, upper and lower case,
  without tofu; Czech shares the default Han font order.

Czech spells "Data" as English does, so the Czech checks probe
"Formulas" -> "Vzorce", where an English fallback would be caught.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:36:33 +02:00
Losminek c8ce333a74 i18n: Czech interface language (cs)
Add a Czech table with all 266 interface labels (the same key set as
Korean, Russian, Simplified Chinese and Brazilian Portuguese) and
register `cs` in Language: name "Čeština", code `cs`, system tags
`cs-*` / `cs_*`, appended to the end of the switcher. The language
command help, its error text and the snapshot example usage list `cs`.

Like the other ribbon tables, it covers the ribbon, sheet tabs and
widgets; dialogs, menus and messages stay English. Formulas stay
canonical English: Czech uses the English formula locale like every
non-Spanish language.

Translated from the meaning of each English label (clean-room, not from
another product's localization), with AI assistance.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:35:04 +02:00
vangel76 001f025119 View: Page Layout view with rulers and in-place headers and footers (#329)
View › Page Layout (and the status-bar button) now shows the sheet as printed
pages on a desk instead of overlaying page outlines on the grid.

- Model/XLSX: per-sheet view mode (normal / pageLayout / pageBreakPreview),
  saved as <sheetView view="…"> and read back. view.* commands set it per
  sheet ({sheet?}) without an undo step or dirtying a clean workbook.
- Engine: paper::PageGrid paginates the sheet from A1 with the print
  pagination (print::split_bands, shared with PDF export and printing) and
  maps cells to pages and back; header/footer section helpers
  (split/join, &[Page] field names) shared with the PDF header code.
  pageLayout.headerFooter takes {part, section, text} for one section and a
  sheet; header/footer strings are capped at 255 characters.
  view.ruler toggles the rulers.
- UI: Geo carries the page grid, so painting, hit-testing, selection,
  editing, objects and scrolling work through the page transform; frozen
  panes are ignored in this view. Pages with margins and gaps, "Click to add
  data" pages, rulers in inches (English) or centimetres, headers/footers
  drawn on printed pages, click a margin section to edit it in place; a
  click elsewhere commits (one undo step), Escape cancels. Header & Footer
  contextual tab: presets, Page Number, Number of Pages, Current Date/Time,
  File Path, File Name, Sheet Name, Go to Header/Footer. View › Ruler.
- New strings in every catalog; new code-drawn icons; desk colour token.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:24:06 +02:00
vangel76 ed62390244 XLSX: keep unmodelled parts on save (#330)
Pass-through of package content GridCraft does not model: on read the raw
parts are kept with their content types and relationship graph per owner
(package root, workbook, sheet); on write they get fresh part names where
needed and new relationship ids, and the r:ids in kept XML are rewritten.

Kept: custom document properties, ribbon customisation, add-in task panes,
custom XML, links to external workbooks with <externalReferences> (never
followed), connections, sheet query tables, printer settings with the
<pageSetup r:id> (#324), form and ActiveX controls, OLE objects and sheet
backgrounds with their VML shapes (merged with GridCraft's note shapes).
Content whose graph reaches parts GridCraft rewrites is dropped with a
warning; sheet content goes with its sheet and is not copied by Copy Sheet.

Fixes #324

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:12:56 +02:00
vangel76 bd5c5c1d26 XLSX/IO: read legacy .xls (BIFF8) workbooks (#121)
Clean-room import of Excel 97-2003 workbooks from the public [MS-CFB] and
[MS-XLS] specifications: compound-file streams (FAT, mini FAT, directory
tree), the BIFF8 Workbook stream with Continue records, shared strings,
all cell records, formulas decoded from Ptg tokens (shared and array
formulas, 3-D refs, names, add-in/newer functions by name) with cached
results, number formats, the 1904 date system, merges, column widths, row
heights, defined names and print areas/titles. Encrypted (FILEPASS) and
BIFF5 files are refused with a clear message; everything else is skipped
with a warning. .xls opens as a new unsaved workbook (Save As .xlsx).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:09:27 +02:00
vangel76 b4d823902a UI: File tab Backstage view (#174)
Clicking the File tab now opens a full-window Backstage view in place of the
ribbon, formula bar, grid and sheet tabs, like Excel's. Esc or the back arrow
return to the sheet. Pages: Home (new + recent), New (blank + samples), Open
(recent + Browse), Info (location, properties, Protect Workbook), Save, Save As,
Print (page preview, scope, fit to page, Page Setup), Export (PDF, CSV, web
page), Close and Options (theme, language, formula bar, AutoSave, calculation).
Every action runs an existing command or dialog.

Adds `file.options` (reports the workbook's calculation settings and asks the
interface for the Options page) and the UI command `ui.backstage {page}`.
Keyboard shortcuts and the macOS native menu are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:03:11 +02:00
vangel76 dc75b2cf5e Data: Solver with Simplex LP, integer branch and bound and a nonlinear method (#326)
data.solver maximises, minimises or targets a value in an objective cell by
changing variable cells under <=, >=, =, int and bin constraints. Simplex LP
linearises the model through recalculation, checks linearity and solves it
with a two-phase simplex; integers use branch and bound. The GRG Nonlinear
choice runs an augmented-Lagrangian projected-gradient method. The model is
saved as hidden solver_* names; Solver Parameters and Solver Results dialogs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:56:36 +02:00
vangel76 c2d2f22e6a Data: Forecast Sheet (data.forecastSheet) (#322)
New sheet before the active one with the sorted history, a FORECAST.ETS
forecast column, FORECAST.ETS.CONFINT lower/upper bounds, an optional
FORECAST.ETS.STAT table, an Excel table over the data and a line or column
chart. Parameters: timeline/values (default: the selection's first two
columns), end or periods, confidence interval, seasonality, missing-point
fill, duplicate aggregation, statistics, chart kind. Data ribbon button
opens a form dialog; programmatic calls never do. gridcraft-functions
exports Timeline to continue a timeline (numeric step or calendar months).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:54:30 +02:00
vangel76 46873b2826 macOS: centre the window buttons in the title bar (#170)
AppKit draws the traffic lights centred in its standard 28 pt title-bar
band, but GridCraft's title bar was 38 pt tall, so the lights sat above
its centre line. On macOS the title bar is now 28 pt with 22 pt controls,
and content starts after the lights with the same gap they keep from the
top. Other platforms are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:47:10 +02:00
vangel76 0dfd29efde Data: Analysis ToolPak with 13 tools and the Data Analysis dialog (#312)
data.analysisToolPak runs Descriptive Statistics, Histogram, Correlation,
Covariance, Regression, Moving Average, Exponential Smoothing, Rank and
Percentile, the three t-Tests, F-Test and Anova: Single Factor, writing a
static report to an output range or a new sheet. Non-empty output cells are
refused unless overwrite is set; the dialog asks instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:44:47 +02:00
vangel76 76c620fc32 Keyboard: Excel shortcut audit, add the missing ones (#110)
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>
2026-10-11 12:43:51 +02:00
vangel76 838304accf UI: title-bar light/dark switch, File › Display theme, system appearance on first run (#194, #239)
The theme could only be chosen in the View tab's Display theme box, and
the app always started light. Now:
- a title-bar button switches light/dark in one click (view.darkMode);
  right-click offers System / Light / Dark;
- File › Display theme offers the same three (Excel keeps it under
  File › Account › Office Theme);
- the desktop app follows the system appearance until a theme is chosen
  and saved in ui.json (UiState's default stays light for tests).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:43:47 +02:00
vangel76 a1dc03a484 Functions: FORECAST.ETS, .CONFINT, .SEASONALITY and .STAT (#314)
AAA exponential smoothing (Hyndman et al. 2008, ETS(A,A,A)) over an evenly
spaced timeline: documented seasonality, data completion and aggregation
arguments, calendar-month date timelines, autocorrelation seasonality
detection, state-space prediction intervals, statistic types 1-8.
Smoothing parameters minimise the in-sample SSE (grid + clamped
Nelder-Mead). XLSX export treats the family's range arguments as
array-taking (no spurious dynamic-array metadata), FORECAST/FORECAST.LINEAR
included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:42:52 +02:00
vangel76 1a7c16eb9a Charts: PivotCharts (insert.pivotChart) (#311)
A chart linked to a PivotTable: series are built from the report
(innermost row items x column items/value fields, totals excluded) and
rebuilt on every refresh or layout change; c:pivotSource round trip;
chart series accept union references.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:31:49 +02:00
vangel76 2d8c29dad2 Grid: mirror right-to-left sheets (#146)
Sheet Right-to-Left (view.rightToLeft, XLSX rightToLeft) was stored and
round-tripped but never rendered. The grid now lays out in logical
left-to-right coordinates and mirrors on screen: painted shapes are
reflected (text and pictures only moved), floating objects move as a
whole, pointer input is mirrored back for hit-testing, Left/Right arrows
follow the screen, and the cell editor, text box editor, popups and the
vertical scrollbar are placed on the mirrored side. Page Layout gets a
Sheet Right-to-Left button.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:30:25 +02:00
vangel76 33f10d0e6e Grid: freeze panes by dragging the freeze handles (#195)
Like Excel for the web, a bar in the upper part of the column-header strip
and one in the left part of the row-header strip sit on the frozen panes'
edges (the corner's edges when nothing is frozen). Dragging one previews
the line and, on release, freezes up to the nearest column or row boundary
through view.freezePanes; dragging back to the corner unfreezes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:28:02 +02:00
vangel76 3b918ada2c Review: Allow Edit Ranges with Excel-compatible password hashes (#307)
- 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>
2026-10-11 12:26:14 +02:00
vangel76 400ab591cb PivotTables: calculated fields (pivot.calculatedField) (#304)
A value field can be a calculated field whose formula is evaluated on
the sums of the fields it names for each report cell (Excel's documented
semantics). Command to add/change/delete, dialog from the field pane,
XLSX cacheField formula round trip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:24:50 +02:00
vangel76 ce411edc91 Checkboxes: ribbon button, Excel's value and Delete rules, XLSX cell controls (#196)
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>
2026-10-11 12:24:25 +02:00
vangel76 9262fba5ce Charts: Select Data edits the category axis labels; blank corner makes column 1 the categories (#287)
A line chart from numeric X values (years) plotted them as a series and
there was no way to set the axis labels. Now:
- a blank top-left cell over a header row makes the first column the
  category labels even when it holds numbers (the usual convention);
- chart.selectData takes categories (Horizontal (Category) Axis Labels,
  "" clears) and removeSeries, and range is optional;
- Chart Design gets a Select Data button opening a Select Data Source
  dialog: chart data range, legend entries with Remove, axis labels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:20:29 +02:00
vangel76 8e5b1ac8e3 XLSX: write threaded comments and persons parts, read note visibility (#300)
- Writer: one xl/threadedComments/threadedCommentN.xml per sheet with
  comments (thread, replies via parentId, done for resolved, dT when known)
  and xl/persons/person.xml (one person per author), with the [MS-XLSX]
  relationship and content types. Each thread keeps its legacy note, linked
  by the tc={id} author and xr:uid; its body is the text plus replies.
  Ids are GUID-shaped and deterministic.
- Reader: keeps dT timestamps (Comment.dates, JSON-compatible), drops
  replies whose thread is missing, and reads note visibility from the VML
  <x:Visible/> (best effort, broken VML skipped silently).
- Invalid dates are never written. Round-trip and hostile-input tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:15:45 +02:00
vangel76 f58b1234fb Charts: Recommended Charts opens a dialog of chart types with previews (#215)
The ribbon button inserted a column chart straight away. It now opens an
Insert Chart dialog: a Recommended tab (insert.chartRecommendations, a
heuristic over the data's shape: series, points, labels, negatives) and an
All Charts tab, each with a live preview of the chart for the selection.
OK inserts the chosen type and selects the chart.

insert.recommendedCharts stays dialog-free for programmatic calls: without
a type it inserts the first recommendation; type/subtype pick one.
insert_chart is split into build_chart (also used for previews).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:15:12 +02:00
vangel76 022aba41a8 UI fonts: symbol fallback faces so interface symbols aren't boxes (#197)
UI fonts such as Noto Sans, Cantarell and egui's built-in Ubuntu lack some
of the symbols the interface draws as text (▸ ⌃ ✓ ✕ ▲ ▼ arrows), so they
rendered as empty boxes. Two symbol faces now sit at the end of every font
chain: DejaVu Sans (Debian, Fedora and Arch paths; Segoe UI Symbol on
Windows, Apple Symbols on macOS) and Noto Sans Symbols 2. The UI font
candidates also gain Fedora's package folders (#54).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:14:16 +02:00
vangel76 52921512c7 Functions: TRIMRANGE (#296)
Evaluator special form returning the range (or array) without its blank
outer rows and columns; trim codes 0-3 as documented; written as
_xlfn.TRIMRANGE. The trim-reference operators are not parsed yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:11:25 +02:00
vangel76 be028cc6cc View: Formula Bar and Zoom 100% as engine commands (#289)
- view.zoom100 sets the active sheet's zoom to 100% (the ribbon's 100%
  button runs it).
- view.formulaBar {on?} toggles a formulaBar preference (Prefs, saved in
  prefs.json on desktop; an application option, not in the file). The UI
  shows the formula bar from it; the ribbon checkbox, native menu and
  ui.set {formulaBar} go through the command.
- The UI-only interceptions and their entries in engine.commands are gone,
  so the CLI and MCP can run both. Parity checklist regenerated (260 / 290).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:09:30 +02:00
vangel76 14ad23044a View: Interface size preference scales the whole window (#230)
UiState gains interfaceScale (percent, 100 = Auto, 75–200), saved in
ui.json and applied as egui's zoom factor on top of the desktop scale.
New UI command view.interfaceScale {percent | "auto"} clamps hostile
values; a malformed saved value reads as 100. View tab gets an
"Interface size" picker. egui's built-in keyboard zoom is turned off so the
preference owns the zoom.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:09:29 +02:00
vangel76 a11446f2f0 Name Manager: New… and Edit… open the name dialog and return to the manager (#238)
dialogs::show put the Name Manager back into app.dialog after its body
had opened the New Name dialog, so New… did nothing. A dialog opened from
another one now takes over and the opener waits behind it (back), coming
back when the child closes; Go To still hands over to Go To Special.

The Name Manager gets Excel's New…/Edit…/Delete buttons over a selectable
list. formulas.defineName takes oldName/oldScope to edit a name in place;
a renamed name is renamed in cell formulas and other names' definitions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:09:19 +02:00
vangel76 35d6c23fd1 Functions: BAHTTEXT (#291)
Thai number words with บาท / สตางค์ / ถ้วน, rounded to satang, ล้าน
repeating per six digits, ลบ for negatives.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:07:41 +02:00
vangel76 33afc7d3a5 Clipboard: Insert Copied Cells and Insert Cut Cells (#21)
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>
2026-10-11 12:04:52 +02:00
vangel76 6f47a8b463 Review: Previous/Next Comment, Show Comments, Show All Notes, Convert to Comments (#281)
- review.previousComment / review.nextComment select the previous/next
  threaded comment in reading order (row by row), across visible sheets in
  tab order, wrapping around; disabled when the workbook has no comments.
- review.showComments {on?} toggles the Comments pane as session state, so
  the CLI, MCP and control channel can drive it; the UI pane follows it.
- review.showAllNotes {on?} displays every note (session display setting,
  not saved in the file).
- review.convertToComments turns every note in the workbook into a threaded
  comment with the same author and text, as one undoable edit.
- Ribbon: Previous/Next and a Show Comments toggle in the Comments group;
  Show All Notes and Convert to Comments in the Notes menu. Strings in all
  catalogs. Parity checklist regenerated (263 / 290).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:04:03 +02:00
vangel76 b6618aee8e Clipboard: pasted currency cells keep the number from the clipboard HTML (#257)
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>
2026-10-11 12:03:46 +02:00
vangel76 5c614fca48 UI: Name Box Enter goes to the typed cell, not the one below (#247)
The Enter that commits the Name Box stayed in the frame's input; the grid,
drawn after the formula bar and focused again, read it as its own Enter
and moved the active cell down. The Name Box now consumes it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:57:26 +02:00
vangel76 a2894eff83 Functions: PERCENTOF and EUROCONVERT (#279)
PERCENTOF = SUM(subset) / SUM(all), written as _xlfn.PERCENTOF.
EUROCONVERT uses the fixed EU conversion rates for the currencies the
function documents, with currency rounding and triangulation precision.
New Add-in category for EUROCONVERT.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:56:18 +02:00
vangel76 1b9751ed82 Fill handle: Ctrl+drag swaps copying and extending a series (#264)
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>
2026-10-11 11:55:45 +02:00
vangel76 573cf92745 UI: skip system fonts egui can't parse instead of panicking at startup (#226)
epaint panics in set_fonts on a face skrifa can't open. Every system font
file is now checked with the same skrifa call (plus a readable cmap)
before it reaches FontDefinitions; a bad candidate is skipped with a
warning and the next one is tried, and a final pass drops any remaining
unparseable face from the definitions and every family.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:55:32 +02:00
vangel76 fed3996944 Functions: ODDFPRICE, ODDFYIELD, ODDLPRICE, ODDLYIELD (#275)
Odd first and last coupon periods via quasi-coupon periods, from the
formulas on Microsoft's public function pages. ODDLYIELD is closed form;
ODDFYIELD bisects ODDFPRICE (bounded). Financial is now 55 / 55.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:51:34 +02:00
mbi 17211dcb65 Fix Paste button deadlock in ribbon popup 2026-10-11 12:00:07 +03:00
arekown a44cc8540d Add German (de) interface language
New catalog crates/ui-egui/src/i18n/de.rs (266 entries) mirroring the ru.rs
key set exactly; original translations written from the meaning of the
English UI strings, MIT OR Apache-2.0.

- Language::De registered (code de, name Deutsch); de/de-DE/de_AT/de-CH
  resolve via from_tag
- Tests: German coverage in the i18n coverage loop, de/de-AT/de-CH locale
  tags, ribbon round-trip case, default CJK font-order assertion
- Docs: localization-parity.md row, ROADMAP.md languages section

cargo test -p gridcraft-ui-egui i18n: 5 passed; --lib: 21 passed;
interaction + formula_locale: 13 + 8 passed.
2026-10-11 10:57:27 +02:00
mbi 479185377e Fix worksheet tab scrolling with mouse wheel and touchpad 2026-10-11 11:09:24 +03:00
Alexander Alber 92adfea27c XLSX: write `@` as `_xlfn.SINGLE` in a dynamic-array formula (#64)
The file format has no `@` operator, but formulas with an explicit
implicit intersection were written with a literal `@`
(`<f>@A1:A3*2</f>`). Excel writes `@x` as `_xlfn.SINGLE(x)`, which only
dynamic-array formulas carry. The writer now does the same and marks such
formulas cm="1"; the parser reads `SINGLE(x)` back as `@x`, as it already
does for `ANCHORARRAY` and `#`.

Two cases in the #83 metadata test expected a redundant `@` (`SQRT(@A1:A3)`,
`@A1:A3*2`) to be written as an ordinary formula. Dropping the `@` there is
only lossless if the reader puts it back on legacy formulas, which it
doesn't yet (the read side of #64), so they now expect cm.

generated with Claude Code
2026-10-11 10:04:09 +02:00
Alexander Alber 4e6e0d6c31 AutoFilter: a selected header row filters the data below it; search box in the filter menu (#201)
Turning the filter on with a multi-cell selection used the selection as the
filter range. With only the header row selected, the filter had no data rows:
the dropdown list was empty and filtering hid nothing. A one-row selection
now extends down over the current region below it. An explicit `range`
parameter is still taken as given.

The filter menu gets a search box, as asked for in the issue. It narrows
the value list; Select All then reads "(Select All Search Results)" and
covers only the matches, and Apply keeps only the checked matches. The box
takes focus when the menu opens, so typing no longer lands in the active
cell.

generated with Claude Code
2026-10-11 09:49:46 +02:00
Alexander Alber 32d0a866f9 XLSX: write one cfvo per icon in icon-set rules (#235)
An icon-set rule without stored thresholds (every rule made by
home.conditionalFormat, and the sales sample) was written as an empty
<iconSet>, which CT_IconSet forbids (cfvo minOccurs 2); openpyxl drops it.
The writer now always emits one cfvo per icon: the stored thresholds when
there is one per icon, the lower bound prepended when it is missing, else
Excel's defaults (percent 0/33/67, 0/25/50/75, 0/20/40/60/80).

Evaluation read the thresholds as one fewer than the icons, but files
carry one per icon (the first is the lower bound), so thresholds read from
a file were ignored. Both now share icon_count and default_icon_thresholds
in gridcraft-model; icon_count is clamped to 3..=5 so a set name like
"0Foo" can't underflow k - 1.

generated with Claude Code
2026-10-11 09:26:00 +02:00
Bryan Mark 5967b5656f Glide the selection outline to a new selection; View > Animations turns it off
Excel's cell cursor doesn't jump: press an arrow key or click a cell and the
border slides over; extend the selection and it grows. The outline of the
current area now does the same, in 0.12 s with a decelerating curve, from
wherever it is drawn (a held arrow key reads as one motion), with its fill
and fill handle. The timing is an estimate, not yet measured against Excel.

The same cells moving on screen (scrolling, zoom, a resized column), another
sheet or workbook, an outline that wasn't drawn the frame before, and moves
that start or end off screen don't glide. Only painting waits: the
selection, hit testing and the cell editor use the real cells at once.

View > Animations (under Display theme) turns it off; view.animations {on?}
and ui.set {animations?} do the same for agents. Saved with the UI
preferences, on by default; translated in the five catalogs.

Scripted pictures never catch an outline in flight: it stays on its cells
while a ui.screenshot is on its way and when egui's animations are off (the
headless harness, so the offscreen snapshot). The marching ants around a
copied range now advance by the clock instead of once per painted frame,
since a glide repaints every frame and made them lurch.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:18:29 +09:00
AlissonFA 9518b041d5 M2: fix cell vertical alignment positioning and add keytips
Closes #241
2026-10-11 04:18:16 -03:00
Bryan Mark 4517d8be65 Leave the active cell unshaded inside a selected range
The active cell of a selected range was shaded like the rest of the range:
the line meant to clear it painted a fully transparent rectangle, which
changes nothing. The fill now goes around the active cell (the whole merged
cell when it is one) in every area of the selection, so it is plain where
typing goes. A lone active cell among several areas isn't shaded at all,
and neither is a single selected merged cell.

The fill is translucent, so the cell can't be cleared by painting over it:
each area is shaded as up to four rectangles around it that never overlap.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:15:57 +09:00
nihilist-narwhal 6e31ad9ea8 Typed times of 24 hours or more keep their hours
Typing 25:00 or 36:30:15 gave the right value but the h:mm format, so
the cell showed 1:00 and 12:30:15. Excel formats such entries as
[h]:mm:ss (25:00:00).
2026-10-10 22:52:59 -05:00
nihilist-narwhal ce83871f2b Remove Duplicates: compare the text cells show
Excel decides duplicates by what a cell displays, not the value stored:
the same date shown as 3/8/2006 and as Mar 8, 2006 is two values, while
1.01 and 1.02 both shown as 1.0 are duplicates. Remove Duplicates
compared stored values, so it did the opposite in both cases.
2026-10-10 22:52:59 -05:00
nihilist-narwhal 6fd41d626b Typed dates before 1900 stay text
7/4/1776 was entered as 7/4/3676 and DATEVALUE("12/31/1899") gave
693962: the text parser built dates with DATE()'s rule that adds 1900 to
years below 1900. Excel keeps such entries as text, and DATEVALUE gives
#VALUE!.
2026-10-10 22:52:59 -05:00
nihilist-narwhal 5e22f9b373 Sort: case-sensitive order puts lowercase before uppercase
With Case sensitive checked, Sort compared text by code point, so every
uppercase word came before every lowercase one (A, B, a, b). Excel
compares ignoring case first and only then puts lowercase before
uppercase: a, A, b, B.
2026-10-10 22:52:59 -05:00
nihilist-narwhal a8a4c5dab3 Conditional formatting: accept Excel's operator names, refuse unknown ones
home.conditionalFormat took any operator it didn't know as "greater",
so a Cell Value rule given Excel's own names (lessThan, greaterThan,
lessThanOrEqual, greaterThanOrEqual, as in the file format and VBA)
silently became "greater than". Those names are now accepted, as data
validation already does, and anything else is an error.
2026-10-10 22:52:58 -05:00
nihilist-narwhal 060889afb2 CHOOSE: pick element by element when the index is an array
The evaluator took a single index, so CHOOSE({1,2},"a","b") gave "a"
and VLOOKUP(x,CHOOSE({1,2},B1:B3,A1:A3),2,FALSE) gave #REF!. An array
index now goes through the library CHOOSE, which lifts over its
arguments as Excel does.
2026-10-10 22:52:58 -05:00
nihilist-narwhal d75b6b73b4 Criteria: text patterns don't match blank cells
COUNTIF(range,"*") counted empty cells and COUNTIF(range,"<>*") skipped
them. Excel matches text patterns against text only: an empty string is
text, a blank cell is not.
2026-10-10 22:52:58 -05:00
nihilist-narwhal 4e7379bd68 Conditional formatting: cell value rules on blank and error cells
Excel compares a blank cell as 0 (or empty text) in Cell Value rules,
so "less than 5" or "equal to 0" also formats blank cells, and a
cell holding an error never matches (the comparison is an error).
GridCraft skipped blank cells and matched errors as if they sorted
above every number ("greater than 5" formatted #N/A).

The No Blanks rule also counted a cell holding only spaces as not
blank, while the Blanks rule (like Excel's LEN(TRIM())) counted it as
blank, so such a cell matched both.
2026-10-10 22:52:58 -05:00
nihilist-narwhal 2b1f851d0a Conditional formatting: apply every matching rule's format
Only the first matching rule's format was used, so with a light red
fill for >300 and a red border for >500, 510 showed only the border.
Excel applies every matching rule; where two rules set the same part
(fill, font colour, a border edge…), the rule with the higher priority
wins, and Stop If True still ends the list.
2026-10-10 22:52:58 -05:00
Diego Ospina cbf56f0707 i18n: Spanish interface translation
Language::Es was registered and switches formulas to Spanish (SUMA, decimal comma, semicolon
arguments), but every interface label still fell back to English. This adds es.rs with all 266
labels in Excel's Spanish wording (Combinar y centrar, Inmovilizar paneles, Análisis de
hipótesis, Buscar objetivo, Rastrear precedentes…) and covers Spanish in the i18n tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012JC2QZ1Qc5KQMBswS38GKy
2026-10-10 22:36:49 -05:00
Sebastian Whincop aee898595f Accuracy: round an exact tie at the 16th significant digit toward zero
Excel shows 15 significant digits. When a number is an exact tie at the 16th
(its exact decimal expansion is 15 digits, a 5, then only zeros), Excel
rounds it toward zero: TEXT(1234567890123455,"0") is 1234567890123450.
GridCraft took Rust's `{:.14e}`, which rounds that tie to even, so any tie
with an odd 15th digit came out one unit too high (…460).

decimal_digits now formats 16 digits (correct unless the 16th is a 5),
and only on a 5 checks the exact expansion (a 30-digit screen, then the
full one) and truncates a true tie. Non-ties, including values just above
or below a tie, round as before. One format serves almost every number.

Tests: ties of every 15th-digit parity, negatives, fractional ties, the
neighbours just above and below a tie, the carry after fifteen 9s
(999999999999999.5 shows 999999999999999), and unchanged ordinary cases.

Evidence: Windows Excel 16.0.20430 TEXT() over a 174-row probe and a 24-row
carry probe agree on every row except those under a percent format and
General over negatives (the two bugs named below). The suite's number-format
golden goes from 183,843 to 184,900 of 187,810 passing with no row that
passed before failing now.

	modified:   crates/numfmt/src/decimal.rs
	modified:   crates/numfmt/src/tests.rs
2026-10-11 16:22:36 +13:00
webdevtodayjason fae90c8e4a selection.move: clamp extreme dr/dc to the sheet edge instead of overflowing 2026-10-10 21:05:58 -05:00
pkibiego 2f078962a0 xlsx: bump quick-xml to 0.41 for the namespace-declaration DoS (RUSTSEC-2026-0195)
The ODS and XLSB readers parse untrusted package metadata and content through
`quick_xml::NsReader`. In 0.38.4 that is RUSTSEC-2026-0195 (CVSS 7.5): the namespace
resolver pushes a binding for every `xmlns` / `xmlns:*` declaration on a start tag, with
no cap on how many there are, and `NsReader::read_event` reaches that path - not only
`read_resolved_event`.

None of our caps bound it. MAX_PART (512 MiB), MAX_TOTAL (1 GiB), MAX_ENTRIES,
MAX_CELLS, MAX_TEXT and MAX_DEPTH bound part size, entry count, cells, text and nesting,
never declarations per tag.

Measured with `gridcraft-cli info` on crafted .ods files, before this change:

     754 B, 0 declarations  ->    8.6 MB, exit 0
     24 KB, 1,000,000       ->   52.6 MB, exit 0
    187 KB, 8,000,000       ->  367.8 MB, exit 0
    770 KB, 33,000,000      ->    1.46 GB, exit 0

About 45 bytes of resident memory per declaration, roughly 1,900x the file size, and no
error at all. At MAX_PART a ~1 MB file reaches ~1.9 GB, and every part is held in memory
while the package is read. Both readers share the path, so a downloaded .ods or .xlsb
was enough. Nothing panicked, so `Session::execute` never saw it.

0.41.0 caps declarations per start tag at 256 (DEFAULT_MAX_DECLARATIONS_PER_ELEMENT,
raisable through NamespaceResolver::set_max_declarations_per_element) and returns
`NamespaceError::TooManyDeclarations` beyond it. The same fixtures after:

     754 B ->    8.6 MB, ok         24 KB ->   19 MB, error
    187 KB ->  101.8 MB, error     770 KB ->    397 MB, error

with "start tag declares more than 256 namespace bindings". The residual peak is the
part itself: MAX_PART still lets one part decompress to 256 MiB, the existing intended
bound.

API changes for 0.41 (0.x bumps may break; all four are in the xlsx crate):
- `resolve_element` and `resolve_attribute` moved off `NsReader` on to the resolver:
  `reader.resolver_mut().resolve_element(..)`. `attr` and `tag` in ods.rs take
  `&mut NsReader` for that.
- `Attribute::unescape_value` is deprecated in favour of `normalized_value`, which also
  applies the attribute-value normalization the XML specification requires.
- `BytesText::xml_content` now takes the document's XML version.

This also clears RUSTSEC-2026-0194, the quadratic duplicate-attribute check. xml.rs
already passed `with_checks(false)`, but ods.rs and xlsb.rs used the checked iterator, so
they were on the quadratic path as well.

Regression test: `ods_tests::ods_namespace_declaration_bomb_is_rejected` asserts that a
tag with thousands of declarations is an error and that a document with a handful still
reads. It fails on 0.38.4 (the tag is accepted) and passes on 0.41.0.

Verified: `cargo xtask ci`; the full xlsx suite (80 tests) plus the malformed-input
tests; and the four measurements above, before and after, with no panics either way.
2026-10-11 03:39:32 +03:00
webdevtodayjason a788c41faa #200: keep formula helpers inside the worksheet
Fixes #200
2026-10-10 18:29:25 -05:00
Dejan Kocić a0dc1abce1 fix: Save toolbar button and saveChanges dialog discard an in-progress cell edit
Clicking the Save icon in the toolbar, or the Save button in the "save
changes?" dialog, called file.save directly without first committing a
cell edit still in progress (e.g. double-click a cell, type a new value,
then click Save with the mouse instead of pressing Enter). The pending
text in EditState was never flushed into the document, so the saved
file still held the pre-edit value, silently.

The File > Save menu item already guards against this
(app.commit_edit(...) before running the command); this applies the
same guard to the toolbar icon and the saveChanges dialog's Save button.
2026-10-11 00:35:45 +02:00
Zarvinx ac87c269ff Fix #213: delete selected objects from keyboard 2026-10-11 01:02:18 +03:00
MarekSpodBiedry 36c591883b Resize charts from the corner handle and the Size fields, keep typed chart and table names (#190)
The corner drag now grabs the chart where the press began and the selected object's corner shows a resize cursor. The Format Chart Area Size fields, chart and axis title fields and the Table Name field reloaded their value from the workbook every frame, so typed or dragged edits were thrown away; they now keep a draft until the field loses focus (Escape cancels, like sheet tab rename).
2026-10-10 23:52:49 +02:00
MarekSpodBiedry 5722dc5355 Do not draw the selection over the frozen pane when it lies only in the scrolling pane (#198) 2026-10-10 23:52:14 +02:00
MarekSpodBiedry 27be1753c2 Read the browser clock for NOW(), TODAY() and year-less dates on wasm (#202) 2026-10-10 23:43:32 +02:00
Zarvinx 271119d9e3 Build: satisfy Rust 1.95 clippy 2026-10-11 00:30:51 +03:00
Zarvinx 7a7e23cab7 Grid: reorder selected columns by header drag 2026-10-11 00:30:24 +03:00
Dejan Kocić d27dec0312 web: print straight to the system print dialog, not just export
The web build had no way to actually print — Ctrl+P (Cmd+P, declared on
file.print's shortcut) did nothing at all, and the ribbon's "Print…" menu
item ran file.print and silently discarded the result. file.print already
builds a PDF and returns it as base64 specifically "for the UI to hand to
the system print dialog" per its own doc comment — this wires that up.

- ui-egui: new Services::print hook (web: hand PDF bytes to the browser's
  print flow directly, no download); SheetApp::print_now runs file.print,
  decodes the base64 result, and calls the hook.
- ui-egui: Key::P now dispatches to print_now (was unbound); the ribbon's
  "Print…" menu item calls print_now instead of discarding file.print's
  result.
- gridcraft-web: implements the hook (Blob -> object URL -> hidden iframe
  -> iframe.contentWindow.print() on load).

Desktop builds are unaffected (no Services::print set there; print_now
falls back to the existing alert-on-error behaviour with no UI action on
success, same as before this change).
2026-10-10 22:50:36 +02:00
Mikkel Georgsen c9525ab5fb Cell references: an invalid reference is an error instead of the active cell
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.
2026-10-10 22:41:40 +02:00
Mikkel Georgsen f69861f7e3 Cell: cell.set without an input is an error
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.
2026-10-10 22:41:40 +02:00
Brandon Thomas 90b3c41eec Fix-up: #83's array-export test expects #156's legacy-array read (cached values refilled on recalc)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 15:59:42 -04:00
GyuriX e8d935298f Linux: the display theme asks the desktop, not winit
winit reports no system theme on Linux — `ActiveEventLoop::system_theme()` is
`None` there on every desktop — so `System` in the appearance setting never saw
the desktop's choice and fell back to light. A new `desktop_theme` module asks
the XDG Desktop Portal's Settings interface for
`org.gnome.desktop.interface color-scheme` on a thread of its own, so no
D-Bus call is ever made from the frame loop, and the app follows the answer,
including a change made while it runs.

egui resolves `ThemePreference::System` from what winit reports and offers no
way to set that itself, so where a desktop answered, the app hands egui the
concrete theme instead. What is drawn is unchanged on macOS and Windows, where
winit answers and the same resolution happens a step earlier; the saved
preference is still `system`, so a file keeps meaning the same thing.

The colour scheme is asked for under its GNOME schema name first, which GNOME's
portal and XFCE's both answer, then under the older freedesktop one, which KDE
answers. A desktop that answers neither leaves the user's own choice alone.
2026-10-10 21:55:20 +02:00
Brandon Thomas c6392e6380 Fix-up: chartex frames use the (open, close) anchor tags after merging #161 onto the move-only anchor change
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 15:52:46 -04:00
Brandon Thomas 88d0c772fb Merge pull request #161 from nihilist-narwhal/fix-chart-kinds-roundtrip
Charts: histogram, box & whisker, waterfall, funnel, treemap and sunburst keep their kind through XLSX
2026-10-10 15:50:48 -04:00
Brandon Thomas f23281db8c Merge pull request #144 from harrisonoconnorhover/codex/cross-sheet-cut
Preserve formula references when cutting cells across sheets

# Conflicts:
#	ROADMAP.md
#	crates/formula/src/adjust.rs
#	crates/formula/src/tests.rs
2026-10-10 15:48:08 -04:00
Brandon Thomas 49fbae245a Merge pull request #156 from nihilist-narwhal/fix-legacy-array-formulas
Legacy array formulas: fill the range they were entered in

# Conflicts:
#	crates/engine/src/cmd/edit.rs
#	crates/ui-egui/src/formula_bar.rs
2026-10-10 15:47:56 -04:00
Brandon Thomas de0c715662 Merge pull request #186 from MarekSpodBiedry/issue-126
Write comment and hyperlink maps with A1 keys so JSON save works (#126)

# Conflicts:
#	crates/engine/src/tests.rs
2026-10-10 15:47:35 -04:00
Brandon Thomas b07249de75 Merge pull request #160 from itrycodinganditsbad/fix-month-fill-clamp
fill: clamp month and year date series to the month's last day

# Conflicts:
#	crates/engine/src/tests.rs
2026-10-10 15:47:32 -04:00
Brandon Thomas c15a51c959 Fix-up: move the #179 table-style test module to the end of tables.rs after merging #149
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 15:47:19 -04:00
Brandon Thomas dd185ff12f Merge pull request #157 from nihilist-narwhal/fix-blocked-spill-dependents
Recalc: formulas over a blocked spill see #SPILL!; no spilling in tables
2026-10-10 15:47:12 -04:00
Brandon Thomas c3cfc92186 Merge pull request #155 from nihilist-narwhal/fix-range-blank-cells
Ranges keep their size past the used range; spilled blanks show 0
2026-10-10 15:47:12 -04:00
Brandon Thomas 9fea6235f7 Merge pull request #148 from nihilist-narwhal/fix-stale-recalc-values
Recalc: stale spills and values after edits (fixes #133, #139)
2026-10-10 15:47:12 -04:00
Brandon Thomas 84a414a705 Merge pull request #158 from nihilist-narwhal/fix-chart-kinds-combo-stock
Charts: combo and stock charts keep their kind in XLSX
2026-10-10 15:47:11 -04:00
Brandon Thomas 1e5edbd43c Merge pull request #154 from nihilist-narwhal/fix-repeated-headers
Repeated headers: number pivot fields and table columns in linear time
2026-10-10 15:47:11 -04:00
Brandon Thomas 5c456df4b7 Merge pull request #152 from nihilist-narwhal/fix-whole-sheet-selection
Whole-sheet selection: Fill and Remove Duplicates no longer run out of memory or time
2026-10-10 15:47:10 -04:00
Brandon Thomas 92fb7cd1b3 Merge pull request #149 from nihilist-narwhal/fix-table-header-edits
Tables: typing in the header row renames the column; Go To a table selects its data

# Conflicts:
#	crates/engine/src/tables.rs
2026-10-10 15:47:10 -04:00
Brandon Thomas 5545e72944 Merge pull request #188 from MarekSpodBiedry/issue-145
Release focus when the sheet tab rename ends (#145)
2026-10-10 15:46:34 -04:00
Brandon Thomas 960c79a328 Merge pull request #187 from MarekSpodBiedry/issue-57
Compare numbers at 15 significant digits like Excel (#57)
2026-10-10 15:46:33 -04:00
Brandon Thomas 645782f5e3 Merge pull request #185 from MarekSpodBiedry/issue-135
Report an error when file commands get no file in MCP and CLI (#135)
2026-10-10 15:46:32 -04:00
Brandon Thomas 87d64f20e1 Merge pull request #184 from MarekSpodBiedry/issue-136
Honor search in the desktop engine.commands handler so connected list_commands filters (#136)
2026-10-10 15:46:32 -04:00
Brandon Thomas d031c8f37e Merge pull request #183 from MarekSpodBiedry/issue-140
Shrink 3-D sheet spans when an end sheet is deleted (#140)
2026-10-10 15:46:31 -04:00
Brandon Thomas cda29c5b73 Merge pull request #182 from MarekSpodBiedry/issue-141
Deliver Export as PDF as a browser download (#141)
2026-10-10 15:46:30 -04:00
Brandon Thomas c21123e375 Merge pull request #181 from MarekSpodBiedry/issue-127
Keep comma when a semicolon or tab is missing from some CSV lines (#127)
2026-10-10 15:46:29 -04:00
Brandon Thomas 75ede09917 Merge pull request #180 from MarekSpodBiedry/issue-129
Keep the untouched part of a data validation range when clearing or replacing part of it (#129)
2026-10-10 15:35:45 -04:00
Brandon Thomas 14402fc5a3 Merge pull request #179 from MarekSpodBiedry/issue-130
Clamp table style number to 1 so zero-numbered styles don't underflow (#130)
2026-10-10 15:35:44 -04:00
Brandon Thomas e9fb005eed Merge pull request #178 from MarekSpodBiedry/issue-134
Rename sheet-qualified defined names with their sheet (#134)
2026-10-10 15:35:44 -04:00
Brandon Thomas 5578c09e55 Merge pull request #159 from itrycodinganditsbad/fix-min-no-args
functions: MIN() accepts zero arguments
2026-10-10 15:35:43 -04:00
Brandon Thomas 00df1908a7 Merge pull request #153 from nihilist-narwhal/fix-jump-without-direction
selection.move: Ctrl+arrow without a direction no longer loops forever
2026-10-10 15:35:42 -04:00
Brandon Thomas c4e4aeb293 Merge pull request #151 from nihilist-narwhal/fix-current-region
Current region: grow past 10,000 rows, in linear time
2026-10-10 15:35:42 -04:00
Brandon Thomas 20aa531d8c Merge pull request #150 from nihilist-narwhal/fix-paste-special-operations
Paste Special operations: keep text, errors and formulas
2026-10-10 15:35:41 -04:00
Brandon Thomas 11ef0e6b04 Maintainer fix-up: compare clearly different numbers directly, skipping the 15-digit text round trip on the sort/lookup hot path
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 15:35:13 -04:00
Brandon Thomas dfd3675324 Maintainer fix-up: drop wall-clock assertions from the repeated-headers test (flaky under load; Instant::now is a disallowed method)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 15:35:10 -04:00
Brandon Thomas f75ded37e7 Merge pull request #105 from harrisonoconnorhover/codex/cell-pictures
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>
2026-10-10 15:02:44 -04:00
Brandon Thomas 59c8e36d36 Merge pull request #83 from harrisonoconnorhover/codex/nonspilling-array-export
Preserve array evaluation for non-spilling XLSX formulas

Rebased onto main with a maintainer fix-up (LET bindings via split_last/as_chunks for clippy, no slice indexing); the contributor's branch does not allow maintainer edits.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 15:02:43 -04:00
Brandon Thomas 5b15827ede Maintainer fix-up: leave English formula display untouched, match engine formula detection
English (and languages without a formula table) now shows stored formula
text verbatim instead of re-parsing it every frame; localized entry no longer
trims leading spaces before formula detection (" =1" stays text, as in the
engine). Adds a display round-trip test for string literals containing
separators, decimal commas and the intersection operator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:49:52 -04:00
Brandon Thomas 3e40014eb0 Merge pull request #98 from harrisonoconnorhover/codex/spanish-formulas
Support common Spanish formula names and semicolon arguments
2026-10-10 14:49:52 -04:00
Zaid Shurbaji fd2243df95 Add table slicers (insert.slicer / table.insertSlicer)
Implements the two slicer commands from the parity catalog as first-class
floating objects that filter a table column.

- model: Slicer struct on Sheet (#[serde(default)]), included in
  Workbook::next_object_id; slicer anchors use AnchorMode::MoveOnly.
- engine: cmd/slicer.rs with insert.slicer, table.insertSlicer, slicer.toggle
  (single + multi), slicer.selectAll, slicer.clear, slicer.delete. Selection is
  written as a FilterCriterion::Values into the sheet AutoFilter and applied via
  the existing apply_filter. object.move / object.delete handle "slicer".
- ui-egui: slicers rendered, hit-tested, selected and dragged like charts; value
  tiles toggle on click (Ctrl/Cmd = multi), funnel button clears. Ribbon buttons
  under Insert > Filters and Table Design > Tools.
- tests: slicer_filters_table; the every_command_survives_empty_params fuzz
  covers the new commands.

Native-only for now; .xlsx slicer-cache round-trip is a follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-10 21:27:13 +03:00
Brandon Thomas 60aa2dfe07
Progress docs: target-app parity, gaps, alpha gate, readiness by audience (#189)
Progress docs per craftrules standards/progress-docs.md: target-app parity, gaps, roadmap with alpha gate, readiness by audience (full / mainstream / essentials) with Opus 5.5 hours, and feature, format, hardware, UI and localization parity docs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:09:06 -04:00
Harrison O'Connor-Hoover ada33d1ad5 Fix #45: follow the selected language for formula editing 2026-10-10 13:48:20 -04:00
Brandon Thomas e72e16a0ec Merge pull request #82 from edurbs/feat/i18n-portuguese
i18n: Brazilian Portuguese interface language (#79)
2026-10-10 13:25:35 -04:00
Brandon Thomas 580b14eeb9 Merge pull request #104 from neko233-com/feature/i18n-zh-ko-ru
i18n: add Simplified Chinese, Korean and Russian UI
2026-10-10 13:25:34 -04:00