chore: sync upstream (602 Commits) #5
No reviewers
Labels
No labels
herkunft/alpha-feedback
status
blockiert
status
zu-testen
typ
bug
typ
docs
typ
enhancement
typ
feature
typ
idea
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
iris/gridcraft!5
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "chore/sync-upstream-20261022"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Upstream-Sync von
upstream/main(602 Commits). Fast-Forward.06920de8Merge pull request #2226 from aiongg/next6f10378bIntegration: #1320 font-measure AutoFit tests in their own test binaryc4c7f747Integration: keytip NR test gives the sheet data so the Insert Chart dialog opens818175f5Integration: Japanese catalog covers the reference catalog's new keys; clear the texture delta in the completion testc7016fb7Integration: fix struct literals and call signatures after combining tracks (Series.quartiles, Comment.note_box, Image data Arc, signature language)cde364f2Integration: cargo fmt after combining tracksd5cdd2eaIntegration: track t15561382ccIntegration: track t14ca1cdd42Integration: track t13c7fd66d8Integration: track t129350ab1dIntegration: track t114c5c345cIntegration: track t106dda6be2Integration: track t09d3201e7cIntegration: track t08a680ac59Integration: track t07home.rowHeight and home.columnWidth take pixels at 96 dpi, the model's unit (header drags and the Row Height dialog send pixels), but their parameter docs said points. An agent asking for a 30 pt row got `ht="22.5"` in the file, and the range check accepted 500 while its message said "between 0 and 409 points". - The params docs say pixels: `height: 20 (pixels) | points: 15`, `width: 64 (pixels) | chars: 8.43`, and `{width: 64 (pixels)}` for home.defaultWidth. Pixel values behave as before. - home.rowHeight takes `points`, Excel's unit (Row Height dialog, `ht` in the file), as home.columnWidth already takes `chars`. - Range errors give the limit in the unit passed: 546 pixels (409.5 points), 409.5 points, 1800 pixels, 255 characters. `chars` is checked against 255, Excel's limit and the widest column a file holds (it went to 256.4 through the pixel limit). - The model's size comments say pixels. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>home.conditionalFormat deserialized `style` straight into the model's Style, so a programmatic caller had to write colours as {"Rgb": 16773836}: "#FFF2CC", which format_range, colorScale and dataBar take, was refused with a serde message naming internal types. A key the struct doesn't have (`style: {preset: …}`, `fillColor`) was dropped, which saved a rule with an empty format that matches and shows nothing. And a cellIs `value` already written as a string literal ("\"Closed\"", as in the file and openpyxl) was quoted again into the text "Closed" with its quotes, so it never matched. `style` colours (font, fill fg/bg, borders) may now be "#RRGGBB", {theme, tint} or "auto", and `fill` may be a colour alone (a solid fill); a fill colour with no pattern is solid. Unknown keys and a non-object `style` are refused. A cellIs value that is one string literal is kept. The model's own shape keeps working. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>data.validation {type: "list", formula1: "Open,In progress,Closed"}, which is how Excel's Source box takes a list, worked inside GridCraft but was saved as <formula1>Open,In progress,Closed</formula1>. In a file an inline list is a string literal, "Open,In progress,Closed" (what Excel writes and what our own reader expects); unquoted, formula1 reads as a formula of names. - validation quotes such a source when the rule is created, doubling inner quotes, using the test list_items already used to tell a literal list from a reference or a name (now is_literal_list). A source typed with = (a formula), a reference, a name, a sheet-qualified range and an already quoted list are stored as before. - list_items reads a quoted list as a string literal: "" inside stands for one quote, so an item with a quote reads back the same after the change and from a file. Creating the rule is the one place that still knows whether the user typed =, so the XLSX writer is left alone: it can't tell OFFSET(...) entered as a formula from a literal list, and a rule read from a file is already quoted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>`style: {fill: "none"}` (or "auto") went through the colour pass and came out as a solid fill in the automatic colour. `home.fillColor` reads the same word as no fill, so the rule style does too now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>sheetis nosheet7b19b74a59`{"sheet": null, "cols": "Data!B:B"}` was refused as naming a sheet other than `sheet`, while `target_sheet` and the cell parameters read a null `sheet` as absent. The prefix now decides, as it does when `sheet` is left out. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>home.formatCells documents {style: {numFmt?, ...}}, but the style patch was merged with the style's snake_case field names, so numFmt (and borders.diagUp/diagDown) were silently ignored. camelCase keys in the patch are now renamed to the style's own; snake_case keys still work.home.insertRows {"rows":"3"} and home.insertColumns {"cols":"B"} failed with "not a range": only spans such as 3:3 or B:B parsed. The same went for deleting, hiding, sizing and grouping rows and columns. A single row number or column letter now means that one line.MAP, BYROW, BYCOL, SCAN and MAKEARRAY put one value in each cell, but a LAMBDA returning several values silently gave its first one: MAP({1,2},LAMBDA(x,{1,2})) was {1,1} and BYROW(…,LAMBDA(r,r)) the first column. As in Excel, the whole result is now #CALC!, as arrays can't nest (a single-value array is still fine). MAP with a LAMBDA taking a different number of parameters than it has arrays, and BYROW or BYCOL with one taking other than one, ran anyway; Excel gives #VALUE!.tableis found on any sheet 2cae3a22b1=it types (fixes #1327) 62729e3a99Adds Language::It ("it", native name "Italiano") with a new per-language module crates/ui-egui/src/i18n/it.rs, following the existing pattern (translate(text) -> Option<&'static str>). The catalog has 971 entries: every key the most complete catalog (pt) translates, plus the PivotTable Fields pane strings (from es) and Options|Editing. Labels follow Excel's Italian terminology (Foglio, Cartella di lavoro, Blocca riquadri, Unisci e allinea al centro, Somma automatica...). Function names are not touched. Also: it / it-IT / it_CH tags resolve to Italian (system-language default), Language::ALL and the coverage/tag/age tests include Italian, and docs/localization-parity.md lists the new language. Not compiled locally (no Rust toolchain on the author's machine); relying on CI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>print_titles read both rows and cols as Options and assigned both unconditionally, so {"rows":"2:2"} set title_cols = None and {"cols":"A:A"} set title_rows = None: the repeated rows or columns disappeared from every printed page with no message, and there was no way to set or clear just one axis. The sibling pageLayout.pageSetup already distinguishes "key absent" from "key present but empty"; read both axes the same way — absent leaves the axis alone, empty/null clears it, anything else that is not a range is an error rather than a silent wipe. Regression test: print_titles_keeps_the_axis_it_is_not_given — on main the rows-only call clears the columns; with the fix each axis is touched only when named, an explicit empty value still clears, and a bad range errors.A cut removed the source's cells and pictures and then re-created the block's merged areas at the destination and copied its notes there — but removed nothing from the source, so one merged block became two (an empty one left behind) and a note ended up on both the old and the new cell. Excel moves both and leaves the source clean. The cut now clears the source range's comments, hyperlinks and merges the same way edit.clear does. A second gap in the same path: paste never transferred hyperlinks at all, so clearing the source would have lost them — paste now carries hyperlinks (in What::All and a new What::Hyperlinks, so edit.pasteSpecial {"what":"hyperlinks"} works like "comments"). Regression test: a_cut_moves_the_sources_notes_hyperlinks_and_merges — on main the old merge stays behind and the note is on both cells; with the fix the merge, note and link all move and the source is clean.symbolis honoured 7776cfcc5c`home.accounting` documents `{range?, symbol?}` but ignored the symbol: the command always applied the `$` format, so a script or MCP client asking for lempiras (#1893) or any other currency silently got dollars. The symbol is now part of the code it writes, and a symbol that would break the format string (a quote or a backslash) is refused instead of being pasted in. The default is unchanged, character for character. Tests: `accounting_format_takes_the_symbol_it_is_given` (displayed text for `L` and the default, and the two refused symbols) and `the_default_symbol_reproduces_the_accounting_format` (the built code still equals the named `Accounting` format). `cargo xtask ci`: 8/8 steps, 0 failures.`C1#` gave `#REF!` while the dynamic-array formula in C1 happened to return a single value -- a FILTER matching one row, a SORT or UNIQUE over one item -- so `ROWS(C1#)` and `SUM(C1#)` failed and `COUNT(C1#)` gave 0. Every formula that counts or sums a filtered list therefore broke the moment the list had exactly one entry. In Excel the formula is the anchor of a 1 x 1 range and the three give 1, the value, and 1. A pass recorded the spill only for an array larger than one cell: if a.rows > 1 || a.cols > 1 { self.spills.insert(k, a.clone()); } so a 1 x 1 result left no range for `spill_range` to answer with, and `eval` turned the missing range into `#REF!`. It is recorded unconditionally now. The report suggested keying this on the cell carrying `cm`, which turned out not to be expressible: nothing in the model says "dynamic array" positively -- the reader tells one from a legacy CSE formula only by leaving `Formula::array` unset. The arm itself is the better test, because a formula whose result is a scalar never reaches it: `=1+1` does not become an anchor, which a test pins. It also matches Excel 365, where `=A1:A1` is a 1 x 1 anchor of its own.A request that waited more than 60 s was answered with a timeout and left in the queue, so `control::poll` ran it on the next UI pass and dropped the reply. The client was told its command had failed while the command was applied: an agent that retried after the error ran it twice, and one that gave up left an unexpected change in the workbook. `ControlRequest` now carries who has it -- pending, taken by the app, or abandoned by its sender -- as the report suggests. `poll` claims each request and skips one it cannot claim; the connection thread abandons the request when its reply times out, and says which happened: still queued timed out waiting for the UI (is the window minimised?) already started timed out waiting for the UI, which had already started the request, so it may still finish; its outcome is unknown The second case cannot be called a failure without guessing. This is the shape DeckCraft settled on for the same bug (its #719), so the two apps now agree. Fix: #1914It returned an empty string for every workbook, saved or not, so the classic sheet-name formula `MID(CELL("filename",A1),FIND("]",…)+1,31)` gave #VALUE!: with no `]` in the string there is nothing for FIND to find. Excel returns `<folder>\[<file>]<sheet>`, and empty only before the first save. The model had no file to report. `source_dir` could not serve: it names the folder relative links to other workbooks are relative to, and must keep naming it after a Save As into another folder, because the links held in memory are still written for the old one — only the saved copy is rebased. `source_path` is therefore its own field, with that distinction written down. It is set when a workbook is opened, from the path made absolute so the folder is the one the user sees, and when a full-fidelity save makes a file the document's own — before the `saved` snapshot, or the workbook would read as dirty the moment it was saved, and only when it changes, so an ordinary Save does not clone the workbook for nothing. A workbook that arrived as bytes knows its name but never a folder and reports `[name]Sheet`, which still splits on the `]`; an import reports the file it came from, as Excel does. The sheet is the one the reference is on, not the active one. CELL is volatile and already in the volatile sets, so a cell holding it answers from the next recalculation; after a Save As that means F9 rather than the save itself, which the engine test asserts rather than papers over. Making a save recalculate is a larger decision than this issue. `check` still skips CELL("filename") as volatile, as docs/cli.md says: the cached value in a file is the folder of the machine that saved it.`SheetProtection` carries eleven permissions. `format_cells` is checked in `apply_style`, and an edit to a locked cell is caught after the fact by `check_protection`, but `sort` and `autofilter` had no reader anywhere outside `sheet_read.rs`, `sheet_write.rs` and a round-trip test: they only ever travelled through the file. So a protected sheet could still be sorted, and Ctrl+Shift+L still put an AutoFilter on it. `refuse_unless_allowed` sits beside `locked_cell_error`, with three call sites: `do_sort` (which both Sort… and the A-to-Z buttons come through), `toggle_filter` and `filter_by`. Where the box decides is worth knowing. Sorting a range of locked cells was already refused, by the locked-cell guard rather than the permission, because a sort moves values between locked cells — which is Excel's rule too ("users cannot sort ranges that contain locked cells on a protected worksheet, regardless of this setting"). So the Sort box decides for a range that has been unlocked, while an AutoFilter changes no cell's contents and the box is the only thing that ever decided it. The test asserts both, and that an unprotected sheet still sorts and filters. Not fixed here: Delete Rows and Delete Columns are refused even when their boxes are ticked, because deleting removes locked cells and trips the same post-hoc guard, while Insert Rows and Insert Columns work. Letting the guard know which operation ran is a change to code every command passes through and wants its own PR.The French catalog once shipped with over a hundred translations sitting on their neighbours' keys ("Close" read "Société"); main has since fixed them. A catalog shifted that way still covers every key, so the existing coverage tests don't catch it. A new test checks every language's translations keep the shape of their English: the same {placeholders}, and no line break in a one-line label (the two big ribbon buttons that wrap are exempt). Plus spot checks of French labels that were once misplaced. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>`=1+` reported "unexpected `)`", although no `)` was typed: entry closes missing parentheses by appending up to eight `)`, and when every retry failed it reported the last retry's error. It now reports the error of the formula as typed ("unexpected end of formula"). `gridcraft_formula::parse_closing_parens` holds the convenience and takes the parse function, so cell entry and the UI's Spanish formula path share it instead of each having its own loop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>Numbers stored as text are skipped by SUM and friends, and GridCraft gave no hint. Like Excel's background error checking, a constant whose text would be a number if typed now gets a green triangle in its top-left corner; with such a cell active, a warning button beside it opens a menu naming the problem with Convert to Number, which converts every flagged cell in the selection as one undo step. - core: `parse::number_stored_as_text` (signs, thousands separators, currency, percent, exponent, one parenthesised negative). Dates, times and fractions kept as text are not flagged, as in Excel. - engine: `cmd::formulas::number_stored_as_text(cell)` (constants only, not formula results) and `formulas.convertToNumber {range?}`, which keeps each cell's format. - ui: the triangle in the grid and the warning button with its menu; pt-BR and German strings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>The Japanese catalog's ribbon labels the review flagged are already in the head ("Save (⌘S)" is 上書き保存 (⌘S), "Insert\nSlicer" is スライサーの\n挿入, and the catalog test asserts the new value), so only the docs needed work: docs/gaps.md had no revision row for the §11 Japanese-catalog update. Add one dated 2026-10-20, and set the "Last updated" header of ROADMAP.md, docs/gaps.md and docs/localization-parity.md to the same date as their new rows. Every existing revision row keeps its original date.Parenthesised negatives were parsed by recursing once per level, so runtime text such as REPT("(",32767)&"1"&REPT(")",32767) overflowed the stack. Strip the parentheses in a loop, toggling the sign per level; results are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>=it types 696555f484home.deleteRows {rows:"2"} and home.deleteColumns {cols:"B"} failed with "not a range"; delete_areas now parses with parse_lines. The PR's test covers both.symbolis honoured bdb03349f6[0]!Name) plot their data, and are saved qualified 6370fd2254