The grid's look() and print's cell_look() layered a rule's fill, font and
borders onto the cell style but not its num_fmt, so a cell under a rule with a
number format kept its own. Take the rule's code when it isn't General (in a
differential style General means "not set", as layer_under treats it).
Print also re-formatted every non-General cell from its own style through
display::cell_text; format with the look's code instead, through a new
display::value_text_as that value_text now calls (CSV/Text output unchanged).
Show Formulas keeps cell_text.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Crostini's cros-host-fonts mounts the host's fonts under
/usr/share/fonts/chromeos (noto/, notocjk/, crosextra/), which none of the
fixed Linux path lists reached: Thai, Hebrew, CJK and Symbols 2 fell back to
egui's Latin-only faces and drew tofu with the fonts installed, and the UI and
cell roles missed Noto Sans and Carlito. Add those folders to each list, with a
structural test that every list probes them.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The evaluator stopped at MAX_DEPTH (400) nested eval calls. A LAMBDA
call takes one of those levels but several stack frames (eval,
eval_inner, call, builtin, apply_lambda, call_lambda_values), so on the
1 MiB main thread of the Windows build
`=LET(f,LAMBDA(g,x,g(g,x)),f(f,1))` overflowed the stack before it
reached the limit, and the process ended.
eval now also stops once a formula has used 512 KiB of stack below its
first eval. Both limits answer #NUM!, the error Excel documents for too
many recursive calls, and a defined name that holds the recursion passes
that error on instead of answering #NAME?.
Fixes#1662
Adds Language::Vi (vi), following the per-language module pattern (crates/ui-egui/src/i18n/<lang>.rs). All 398 strings in the master table are covered, checked by the completeness test; vi-VN locale tags resolve to Vietnamese.
egui's bundled fonts have no Lao, and none of the script fallback faces
(CJK, Thai, Hebrew, symbols) carry it on macOS, so Lao text in a cell,
the formula bar or the interface drew as boxes unless the cell's own font
had Lao glyphs. Add a `sys-lao` fallback: Leelawadee UI / DokChampa on
Windows, Lao Sangam MN / Lao MN on macOS, Noto Sans Lao / Phetsarath OT
on Linux. `fallback_covers_its_script` now checks a Lao sample.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The sheet tab context menu buttons (Insert Sheet, Rename, Move or
Copy…, Duplicate, Tab Color, Hide, Unhide…, Protect/Unprotect Sheet,
Select All Sheets), the navigation tooltips (Previous/Next/All sheets,
New sheet), and the AutoSave toggle label and tooltip in the ribbon were
all hardcoded English — they were never routed through lang.tr().
Fix:
- tabs.rs: wrap every hardcoded UI string with app.ui.language.tr(…)
- ribbon.rs: wrap the AutoSave label and hover text with lang.tr(…)
- i18n/cs,de,es,fr,ko,pt,ru,sr,zh: add the 130 missing translation
entries (each language only gets the entries it was missing)
Translations produced from first-principles per language, following the
terminology used for other sheet/workbook strings already in each file.
Closes#1537
Found while testing crates/engine/src/cmd/sheet.rs:216-238 (rewrite never updates Chart.source); used by crates/engine/src/cmd/insert.rs:849 (chart_switch)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
forecast_grid re-added every squared residual in each window, O(n×window).
Keep a running sum instead: add the entering term, subtract the leaving one,
and restart at a point without a forecast so windows touching it stay #N/A.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Found while testing crates/engine/src/cmd/sheet.rs:115 (shift_sheet_features; it never touches sh.pivots)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
table.resize has existed in the engine since #589/#1706 (overlap checks, tests), and the
command catalog already promised it under Table Design > Properties, but no ribbon button ever
called it -- the only way to resize a table was scripting the command directly (control channel,
CLI, MCP). #935 (no drag handle) assumed this menu path worked; it doesn't.
Adds a "Resize Table" button next to the Table Name field (same Properties group the catalog
already declares), opening a small dialog pre-filled with the table's current range. Confirming
runs table.resize with the typed range, same validation as #589/#1706 (rejects overlap with
another table, matching Excel's "A table can't overlap another table.").
The drag-handle part of #935 is unaddressed; this only restores the menu/dialog path.
Verified manually: created two tables, used the new button to grow one onto the other (rejected),
then to an adjacent non-overlapping range (accepted, table intact).
auto_row_height estimated a line of a bigger font by scaling the sheet's
default row height by size / 11. When the sheet's default already fits
that font, the font counted twice: Excel for Mac saves rows of 16pt text
with defaultRowHeight="21" (28px) and no ht, and opening such a file
fitted every row to 40.7px, 1.45 times Excel's height. Rows mixing Middle
and Bottom aligned cells then looked misaligned.
The estimate now scales an 11pt row's height (DEFAULT_ROW_HEIGHT), so
those rows open at 29px, and rows on a 15pt-default sheet still grow to
their font (#833).
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Found while testing crates/engine/src/cmd/extra.rs:818-855 (table_resize, overlap check at ~832)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Fedora keeps each font family in its own package folder, and the cell, serif, mono, Thai and
Hebrew candidates only listed the Debian/Ubuntu/Arch folders, so no cell face was found there and
bold and italic cells drew as regular text in the built-in font. Add Fedora's folders after the
existing Linux entries for each font, as #54 did for the UI fonts, and let mono fall back to
Liberation Mono and then Fedora's default Noto Sans Mono, since DejaVu Sans Mono is optional there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A workbook GridCraft created had no <fileVersion> in workbook.xml; it now
names GridCraft, and keeps it on later saves.
A workbook read from an Excel file still keeps its <fileVersion> as it was,
appName="xl" included. Observed in Excel 16.114: with the calcFeatures
extension present, Excel uses the saved cell values only when appName is
"xl". appName="gridcraft", "Calc" or no appName makes it recalculate
everything on open, which brings back #799 (lookups into closed workbooks
open as #CALC!). The application that last saved the file is recorded in
docProps/app.xml <Application>GridCraft</Application>, which GridCraft
already writes on every save and which Excel's recalculation ignores.
Checked black-box in Excel for Windows (Microsoft 365) with synthetic
workbooks: a bare name in <c:f> draws the whole chart blank, without a
repair prompt, for workbook- and sheet-scoped names alike, while
`[0]!Name` and `Sheet1!Name` plot.
- Write: a sheet-scoped name is written `Sheet!Name` (quoted when
needed) instead of bare; an already qualified name keeps its sheet;
an undefined name gets `[0]!`. A reference to an unknown sheet is
kept as written. A qualified series name is written as a strRef.
- Resolve: a series name `Sheet1!Name` is looked up on that sheet, as
the evaluator does, instead of being shown as literal text.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Excel writes a workbook-scoped defined name in a chart reference as
`[0]!Name`. The reader kept that text, the lexer took `[0]` for a
structured reference, and the series evaluated to #NAME?: a chart over
dynamic (OFFSET) names opened blank with one #NAME? category.
- Read: `[0]!Name` becomes `Name` in c:f (cat, val, xVal, yVal,
bubbleSize, tx), chartex cx:f and _xlchart names. External books
([1]Sheet1!...), quoted sheet names and strings are untouched.
- Write: a bare workbook-scoped name is written back as `[0]!Name`; a
sheet-scoped name stays bare. A series name that is a defined name is
written as a strRef instead of literal text.
- Resolve: a series name that is a defined name is evaluated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A line that is not a JSON object with a string method, including an HTTP
request line, bytes that are not UTF-8 or a line over 64 MiB, gets one
error reply and the connection closes before anything after it is read.
Request lines are read with a bound instead of buffering them whole.
Floating pictures were decoded at full size, with no limits, into one
process-wide map of textures that never let go, not even when their
workbook closed. Its key was the picture's id and byte length, and ids
are per workbook, so a second open workbook could show the first one's
picture.
The new floating_pictures module, built like cell_pictures, keeps a
cache per egui context keyed by workbook, picture id and size:
- bounded at 128 MB, least recently drawn first, never evicting a
picture drawn in the last frame, so none turns into a placeholder;
- a workbook's textures go when it closes (checked every frame);
- a picture's header is read first (16,384 px a side, 64 MP at most),
it is decoded with limits, and to the next size step of what is
drawn (a power of two or three quarters of one, up to 4,096 px or the
GPU's limit); it is decoded again only when drawn larger;
- a picture that can't be decoded is remembered, not retried each frame.
Measured on macOS 27 (Apple M3 Max): 12 pictures of 2600 x 2600 opened
from an .xlsx and scrolled through, textures held by egui 258 -> 40 MB,
settled footprint 738 -> 509 MB; after closing the workbook, textures
258 -> 0 MB, footprint 504 -> 236 MB. 24 pictures: textures 490 -> 76 MB.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Every edit copies the workbook for its undo step (copy-on-write: sheets
and 64-row bands of cells are shared). A floating picture's bytes
(Image.data) and the VBA project (Workbook.vba_project) were plain
Vec<u8>, so each edit on a sheet with a picture, and every edit in a
workbook with macros, kept another full copy of them, up to the 100
steps of the history. They are Arc<Vec<u8>> now, as cell pictures
already are, with base64 helpers for serde (b64::shared, b64::opt). JSON
and XLSX are unchanged.
Measured on macOS 27 (Apple M3 Max), gridcraft-cli, kernel peak
footprint: a 19 MB picture and 50 cell edits on its sheet, 992 -> 22 MB;
10 edits, 215 -> 21 MB.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
egui copies an owned font's bytes into its text engine (epaint's
blob_from_font_data clones the Cow) and keeps the original FontData too,
so every interface and script fallback font was in memory twice: about
100 MB on a Mac, where the fallbacks alone (Hiragino Sans GB, Hiragino
Sans, Apple SD Gothic Neo, Thonburi...) are 89 MB of files. try_load now
keeps each file it reads for the rest of the run and hands egui borrowed
'static bytes, which it shares. These faces are loaded by every font
rebuild anyway, so keeping them costs nothing, and rebuilds no longer
read them from disk again. A missing or broken file is looked for again
next time. Which faces load, and their order, are unchanged; workbook
fonts (bind_faces) load as before.
Measured on macOS 27 (Apple M3 Max), offscreen snapshot held after its
picture, settled footprint: empty workbook 258 -> 158 MB, the sample
workbook 261 -> 160 MB, a cell in five scripts 258 -> 158 MB; the
rendered pictures are byte-for-byte the same.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Found while testing crates/engine/src/cmd/extra.rs:664 (command doc at extra.rs:114)
Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>