Commit Graph
101 Commits
Author SHA1 Message Date
aiongg b0b6409112 Integration: track t03
# Conflicts:
#	ROADMAP.md
#	crates/ui-egui/tests/keyboard_shortcuts.rs
#	docs/file-format-parity.md
#	docs/localization-parity.md
#	docs/ui-parity.md
2026-10-22 03:00:13 +00:00
aiongg 54d0f7cfc0 Integration: track t02
# Conflicts:
#	ROADMAP.md
#	crates/core/src/lib.rs
#	crates/ui-egui/src/editor.rs
#	crates/ui-egui/src/grid.rs
#	crates/ui-egui/src/tabs.rs
#	docs/chart-parity.md
#	docs/localization-parity.md
#	docs/ui-parity.md
2026-10-22 02:59:55 +00:00
aiongg 1316d39865 Merge PR #1151: Charts: a huge trendline forecast no longer collapses the chart (#1148)
ROADMAP.md: all rows kept.
crates/chart/src/tests.rs: both tests kept.
2026-10-22 02:37:37 +00:00
aiongg 857bdae023 Merge PR #1692: XLSX: keep Japanese phonetic runs on open and save
Conflicts:
- crates/model/src/lib.rs, crates/xlsx/src/read.rs, crates/xlsx/src/write.rs: use lists unioned (ShowObjects and the phonetic types)
- docs/file-format-parity.md: main's header and rows kept (#1692 collapsed the three duplicate .xlsx rows; they stay), phonetic-runs text added to each; Last updated 2026-10-20
2026-10-22 02:36:58 +00:00
aiongg 8e2974cc65 Merge PR #1629: Memory: undo steps share picture and macro bytes instead of copying them
Conflicts:
- ROADMAP.md: both progress-log and revision rows kept
- crates/xlsx/src/sheet_write.rs: keeps #995's image_part call; drops #1629's to_vec line (image_part copies from &[u8])
2026-10-22 02:36:22 +00:00
aiongg 7fd1553a8b Merge PR #1626: Keyboard: Ctrl+6 hides and shows objects, saved as workbookPr showObjects
Conflicts:
- docs/file-format-parity.md: header and the three .xlsx rows keep main's text plus the show-objects entry; both log rows kept
- docs/ui-parity.md: header keeps main's text plus Ctrl+6; both log rows kept
2026-10-22 02:36:15 +00:00
aiongg c03d4a881b Merge PR #1570: XLSX: keep dynamic-array cell metadata (cm) on save
ROADMAP.md: kept both progress-log rows
2026-10-22 02:36:04 +00:00
aiongg 7768f56603 Merge PR #1580: Calc: CELL("filename") names the workbook's file 2026-10-22 02:35:56 +00:00
aiongg d26bbe3432 Merge PR #1085: Format: Row Height and Column Width say pixels, and take points like the reference app
Conflicts:
- crates/engine/src/cmd/format.rs: spec strings keep #885's sheet?/Sheet! forms with #1085's pixels/points wording; row_height and col_width keep #1085's unit logic with #885's (si, lines) = line_targets
2026-10-22 02:35:21 +00:00
aiongg 2c10a8da46 Merge PR #759: Names: a name over several ranges reads its top-level commas as unions
crates/formula/src/lib.rs: union pub use list (MAX_FORMULA_LEN and parse_name).
crates/engine/src/cmd/formulas.rs: keep both new tests.
2026-10-22 02:35:11 +00:00
aiongg 6735e9e7c0 Integration: #451 qualify the run-stop row through value_ref
A stored cell with no value and no formula falls through to spill in
value_ref; the scan now asks value_ref for the row where the stored run
stops. Docs corrected; tests cover a style-only stored cell over a spill
value.
2026-10-22 02:34:58 +00:00
aiongg 93de710d28 Merge PR #1306: Show dynamic array source formulas read-only in spill cells
docs/ui-parity.md: kept HEAD's header line, both change rows (newest first)
2026-10-22 02:34:52 +00:00
aiongg 8041fbfc22 Merge PR #451: perf: fill-handle double-click scans a dense neighbour column ~2.4x faster 2026-10-22 02:34:35 +00:00
midasdf 40803e6aca XLSX: keep Japanese phonetic runs (ふりがな) on open and save
xl/sharedStrings.xml can carry phonetic runs (<rPh sb="0" eb="2"><t>ふりがな</t></rPh>)
and a <phoneticPr> beside a string item. GridCraft read only the text and wrote every
string back as <si><t>…</t></si>, so a plain open + save deleted a Japanese workbook's
furigana.

Both are now read — offsets validated against the base text, runs with bad or missing
offsets, sb >= eb or no reading dropped with a warning, and never used to index — and
kept on the workbook as Workbook::phonetics, one entry per string text. The writer
emits them next to the <si> of the same text. The saved table holds each text once, so
two identical texts cannot keep two different readings: the reader keeps the first and
warns. Strings without phonetic data are written byte for byte as before.

Behaviour reference: ISO/IEC 29500-1 §18.4.6 rPh and §18.4.7 phoneticPr (Microsoft
Learn PhoneticRun/PhoneticProperties). Tests: crates/xlsx/src/tests/phonetic_tests.rs
(round trip, plain string, hostile offsets, shared text, unused data, comments and
inline strings). Docs: file-format-parity.md, localization-parity.md.
2026-10-20 13:39:48 +09:00
Hamza aff9153dac Calc: CELL("filename") names the workbook's file (#1489)
It 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.
2026-10-19 22:29:37 +05:00
Mouhand-Kaddo 90344445d7 M12.AR10: keep right-to-left paragraph direction in text boxes and shapes 2026-10-19 15:46:21 +04:00
Bryan MarkandClaude Opus 5.5 a09d3f6934 Memory: undo steps share picture and macro bytes instead of copying them
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>
2026-10-16 14:26:53 +09:00
Marco Arnulfo efc8171609 Keyboard: Ctrl+6 hides and shows objects, saved as workbookPr showObjects 2026-10-16 07:03:58 +02:00
Harrison BoothandClaude Opus 5.5 7ee11fe137 XLSX: keep dynamic-array cell metadata (cm) on save
A formula read with `cm` (Excel's XLDAPR dynamic-array record in
xl/metadata.xml) now carries `Formula::dynamic`, and the writer keeps the
`cm` and the metadata part for it even when the formula returns one value
and the export shape analysis would not need array evaluation. Before, such
formulas were saved as legacy formulas, so Excel recalculated some of them
with implicit intersection (SUMPRODUCT(IFERROR(range/range,0)) changed
value), and workbooks whose every dynamic formula was scalar lost
xl/metadata.xml altogether.

The mark survives moves, row and column insertion or deletion, sheet and
name renames and LAMBDA call-case fixes; retyping a formula drops it and
the writer decides from the formula's shape, as before. It is serialized
only when set, so saved documents without it are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:05:03 -05:00
Pranav Chandra 3177fbe03f ui: show spill-cell formula as greyed out 2026-10-14 20:23:44 +05:30
vangel76andClaude Sonnet 5.5 568dd58264 Charts: a huge trendline forecast no longer collapses the chart (#1148)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-13 21:13:05 +02:00
scottcampbelldata 00fb1147c3 Fix PR 451: integrate main and validate existing behavior 2026-10-13 10:03:52 -04:00
Brandon Thomas be003d44af Merge main into #863
# Conflicts:
#	crates/xlsx/src/tests/passthrough_tests.rs
2026-10-13 01:40:22 -04:00
Brandon Thomas 9972a75f10 Merge main into #824
# Conflicts:
#	crates/engine/src/cmd/file.rs
#	crates/engine/src/tests.rs
#	crates/xlsx/src/tests/passthrough_tests.rs
2026-10-13 00:53:41 -04:00
Brandon Thomas 3c4556bb04 Merge main into #856
# Conflicts:
#	ROADMAP.md
#	crates/model/src/lib.rs
#	crates/xlsx/src/tests/passthrough_tests.rs
#	docs/file-format-parity.md
2026-10-13 00:52:57 -04:00
Marco Arnulfo c7cb91ea53 Merge remote-tracking branch 'origin/main' into fix/xlsx-xml-maps 2026-10-13 06:33:03 +02:00
Marco Arnulfo d231823e1c Merge remote-tracking branch 'origin/main' into fix/name-union-definition 2026-10-13 06:22:09 +02:00
Brandon Thomas 62b7ba1e49 Merge main into #823
# Conflicts:
#	crates/xlsx/src/tests/roundtrip.rs
2026-10-13 00:08:44 -04:00
Brandon Thomas d2bd177b98 Merge main into #753
# Conflicts:
#	crates/calc/src/tests.rs
2026-10-13 00:08:09 -04:00
Brandon Thomas efed7caa01 Merge pull request #826 from marcoarnulfo:fix/xlsx-external-file-hyperlinks 2026-10-12 23:49:53 -04:00
Brandon Thomas a76759cba6 Merge pull request #822 from marcoarnulfo:fix/workbook-fileversion-calcfeatures 2026-10-12 23:49:47 -04:00
CouchRivandClaude Opus 5.5 9a816f80f7 Format: Row Height and Column Width say pixels, and take points like Excel
home.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>
2026-10-12 22:54:25 -04:00
Marco Arnulfo 2dd832e2d5 XLSX: XML maps survive a save, with their table columns and mapped cells (part of #860) 2026-10-13 02:45:40 +02:00
NetterSilver c5c1763774 XLSX: keep chart sheets on open and save (#727)
Chart sheets (a sheet that holds one chart; Move Chart > New sheet, F11) were
skipped on open, and their chart sheet, drawing, chart, style and colour parts
were dropped with them because the pass-through treats those kinds as content
GridCraft rewrites. Saving a workbook lost every chart sheet.

They are now kept as they were: the <sheet> entry (name, visibility and its
place among the worksheets), the chartsheet part and everything behind it,
byte for byte, written back in place. The workbook's other sheet positions
(active tab, print areas, scoped names) are computed with the chart sheets
counted. A chart sheet is left out on save when the chart is no longer
valid for the workbook: a sheet it plots was deleted or renamed, or a
worksheet took its name.
2026-10-13 01:45:40 +02:00
Marco Arnulfo 52b4f873c7 XLSX: a hyperlink to another file is saved as an external relationship (fixes #800) 2026-10-12 22:57:20 +02:00
Marco Arnulfo 5406fc5629 XLSX: Save As into another folder keeps links on the same workbook (fixes #801)
Excel stores the path of a linked workbook relative to the file that uses it, and rewrites it when the file is saved into another folder. The path was kept as text, so after Save As it named a file next to the new copy: a missing link, or another workbook with the same name. Relative externalLinkPath targets are now resolved against the folder the workbook was read from and written relative to the folder it is saved into (absolute when there is no relative path, on another drive).
2026-10-12 22:55:53 +02:00
Marco Arnulfo 75305dfedf XLSX: keep the workbook's default font on save (fixes #805)
Cell format 0 always mapped to the model's built-in Calibri 11 default, so the file's Normal font was replaced on save and every default-formatted cell changed size. The Normal style now takes the font of cell format 0, and a Normal font in the theme's body face is written with scheme="minor" again.
2026-10-12 22:51:41 +02:00
Marco Arnulfo 930fa1cada XLSX: keep fileVersion and calcFeatures in workbook.xml on save (fixes #799)
Excel uses the cached cell values of a saved file only when workbook.xml has both <fileVersion> and the calcFeatures extension; without them it recalculates everything on open, and lookups into closed workbooks come back as #CALC!. Both are now read into the workbook's kept content and written back in schema order: fileVersion first, the calcFeatures ext in <extLst> last. A workbook that had neither gets neither.
2026-10-12 22:49:34 +02:00
neilcawseandClaude Opus 5.5 aa2a580626 PivotTables: values on rows review fixes (Total header, Σ Values last in Columns)
With values on rows and no column field, the header cell above the data
column reads "Total", as Excel saves it (tabular checked against a real
file).

Σ Values in Columns always comes after the last column field: the column
layout doesn't place it anywhere else, so `pivot.moveField` no longer
stores a position there, the reader ignores where `-2` sits in colFields
and the writer puts it last.

`pivot.changeSource` removes dropped row and column fields one by one so
Σ Values keeps its place ahead of the field it preceded.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 15:05:59 -04:00
Marco Arnulfo cf587ffe3b Names: a name over several ranges reads its top-level commas as unions (fixes #728) 2026-10-12 19:16:49 +02:00
Marco Arnulfo f968bb3269 CELL("width") returns the Column Width dialog value, without the padding (#735) 2026-10-12 19:05:41 +02:00
neilcawseandClaude Opus 5.5 1f7fd07c0d PivotTables: values on rows (Σ Values in Rows, dataOnRows)
With two or more value fields the Σ Values pseudo-field can sit in Rows
as well as Columns, at any position among the row fields. The model
records it (`PivotTable.values_on_rows`, `values_position`) and the
engine lays it out like Excel in compact, outline and tabular form: one
row item per value field at the Σ Values level, per-value subtotals
("East Sum of Sales") and grand totals ("Total Sum of Sales").

XLSX reads `dataOnRows` and the `-2` field's place in rowFields or
colFields and writes them back; the "values on rows are shown on
columns" warning is gone. `pivot.moveField` moves "Σ Values" between
Rows and Columns or within one, axis field moves keep it in place, and
the PivotTable Fields pane lists it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 12:06:30 -04:00
Dejan PortandClaude Sonnet 5 e7d590c8a4 style: satisfy cargo fmt and clippy on the fill-handle perf change
cargo fmt reformatted the new example's while-loop condition onto its
own line; clippy's nonminimal_bool caught a negated is_some_and that
reads more plainly as is_none_or.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-12 02:07:45 +02:00
Dejan PortandClaude Sonnet 5 43a118c543 perf: fill-handle double-click scans a dense neighbour column ~2.4x faster
Follow-up to #210, which the reviewer correctly rejected: the stated
cause (a sparse column with a stray distant value) doesn't hold, since
the scan already stops at the first empty cell. This is a different,
narrower fix that doesn't change behaviour for any input — it never
truncates a long fill, unlike #210's cap — only how fast a genuinely
long, densely-populated run is scanned.

grid.rs's fill-handle double-click called Sheet::value_ref once per
row to find where the neighbouring column's data ends. Each call does
a band lookup + row lookup + column lookup (three BTreeMap probes) via
CellStore::get. For a real dense run (e.g. a filled column near the
1,048,576-row sheet limit) that's a lot of point lookups on the UI
thread in one frame: ~59ms for a 1,000,000-row run in a release build
(measured with the new fill_handle_perf example), likely worse under
wasm where GridCraft actually runs in the browser.

- crates/model/src/store.rs: CellStore::contiguous_value_run walks
  each band's row BTreeMap with `range` (one in-order pass per band)
  instead of one `get` per row, so a dense run costs roughly one
  tree-traversal step per row instead of one binary search per row.
  Deliberately narrower than `has`/`get`: a stored cell always decides
  the row on its own (even with an empty value), matching what
  `value_ref`'s callers actually test for, not `has`'s "formula counts
  regardless of its cached value" (used for Ctrl+arrow navigation).
- crates/model/src/sheet.rs: Sheet::last_contiguous_nonempty layers
  `spill` back in wherever contiguous_value_run finds no stored cell,
  exactly where plain `value_ref` would fall through to it.
- crates/ui-egui/src/grid.rs: the fill-handle double-click now calls
  this once instead of looping over value_ref.

Result: 999,999-row dense run, 59.2ms -> 24.7ms (2.40x), same answer
(asserted in the example). Four new tests in sheet.rs, including a
300-trial property test against a naive value_ref-loop reference
(spills, formula-with-empty-value, gaps, blanks) — all pass, plus the
full existing suite (`cargo test -p gridcraft-model -p gridcraft-ui-egui
-p gridcraft-engine`; the only failures are #210-era gridcraft-engine
CSV tests already failing on main, unrelated to this change).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-12 02:01:49 +02:00
Brandon Thomas 69b5f78e86 Merge pull request #424 from neilcawse/fix/trendlines
Charts: read, write and draw trendlines

# Conflicts:
#	docs/gaps.md
2026-10-11 18:06:01 -04:00
Brandon Thomas dcd469d12e Merge pull request #419 from neilcawse/fix/function-fixes
Functions: MODE/VALUE/SUMPRODUCT text args, ADDRESS [Book]Sheet, Excel-style lookup bisection, SUBTOTAL skips filtered rows

# Conflicts:
#	docs/file-format-parity.md
#	docs/function-parity.md
2026-10-11 18:05:41 -04:00
Brandon Thomas 80c2efe0f1 Merge pull request #421 from neilcawse/fix/iterative-calc
Calc: iterative calculation for circular references (Maximum Iterations, Maximum Change)

# Conflicts:
#	docs/function-parity.md
2026-10-11 18:05:39 -04:00
Brandon Thomas 5432419cd5 Merge pull request #427 from vedran-dobos/csv-keep-delimiter
CSV: saving keeps the separator the file was opened with (#426)
2026-10-11 18:05:14 -04:00
Vedran DobosandClaude Opus 5.5 b13ea3e2f3 CSV: saving keeps the separator the file was opened with (#426)
A `;`-separated CSV (the usual CSV where `,` is the decimal separator) was
rewritten with `,` on save: read_csv sniffed the delimiter but the result was
dropped, and save_bytes passed a hard-coded b','. file.save, Save As .csv,
file.exportCsv, file.saveBytes and gridcraft-cli convert all went through it.

The workbook now remembers the separator it was read with
(Workbook::csv_delimiter, set by read_csv) and a CSV save writes it back.
Workbooks not read from CSV, and .tsv/.txt sources saved as .csv, keep `,`.

Regression tests: `;`, `,` and tab .csv round trips byte for byte, new and
.xlsx-origin workbooks save with `,`, and open-by-path + edit + file.save on a
`;` file keeps `;` (also through exportCsv and saveBytes).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 23:46:27 -07:00
derechopiano 1a6c864235 home.autofitColumnWidth: reserve filter dropdown space 2026-10-11 22:09:51 +01:00