Commit Graph
78 Commits
Author SHA1 Message Date
PranavandFredApps 71b130be2d Data: Solver Load/Save Model and Reset All (#3086)
* Data: Solver Load/Save Model and Reset All

data.solverSaveModel writes a model (the sheet's saved model, or one
given as data.solver params) down a column of its sheet as formulas:
the objective and goal (=MAX(...), =MIN(...) or =cell=value), the
variable cells (=COUNT(...)), one =AND(relation) per constraint, which
shows TRUE while the constraint holds, and the method and its options
as an array constant. It refuses a range that overlaps the model's
cells or runs off the sheet.

data.solverLoadModel reads such a range (or reads down from its first
cell), checks the model and makes it the active sheet's model.
data.solverReset (Reset All) removes the active sheet's model. Each is
one undo step.

The Solver Parameters dialog has Reset All and Load/Save buttons; the
dialog can also be opened with fields that go over the saved model.

* Data: Solver Reset All also clears a model read from workbook-level names

A sheet's Solver model can come from workbook-level names, which every
sheet without a model of its own reads. Reset All removed only the
sheet-level names, so such a model stayed and the dialog showed it
again.

Removing the workbook-level names would change every other sheet that
reads them. Reset All instead leaves them in place and writes an empty
sheet-level solver_opt over them on the active sheet only; the result
says masked: true. saved_model now reads a sheet's own names when the
sheet has a solver_opt, and the workbook-level names only otherwise,
never a mix of both, and treats an empty solver_opt as no model. The
mask survives XLSX and is replaced when a model is saved on the sheet.

* Data: Solver Save Model clears the rest of a longer model saved at the same cell

Saving a model over a longer one saved at the same cell left the old
model's last rows on the sheet, so Load Model from that cell read the new
options row followed by stale constraint rows and refused the range.
Save Model now clears what is left of a model saved there before (only
when those cells hold a complete model written by Save Model, and never
the new model's own cells), keeping the cells' formatting, in the same
undo step. Load Model reading down from one cell also stops at the
options row, so formulas just below a saved model no longer break it.

* Data: Solver Load Model keeps the Evolutionary convergence and generations

Save Model writes Convergence and the generations without improvement in
its options row and Load Model reads them, but the sheet's saved model
(hidden names) did not hold them, so after Load Model, or any solve,
they fell back to their defaults. The saved model now keeps them for the
Evolutionary method: Convergence as solver_cvg, which means the same in
Excel, and the generations as gridcraft_solver_stall, because Excel's
solver_mni is a time in seconds. Saving a model again and Reset All
remove the GridCraft name with the solver_* names.

* Data: Solver Load Model refuses a range with a gap

Load Model from a range of several cells kept only the cells that hold a
formula, so the rows below a cell without one moved up. A constraint cell
overwritten by a constant gave a model without that constraint, and an
empty cell before the options row gave the default options, both without
an error. Load Model now refuses a range in which a cell before the last
formula holds no formula, and names that cell. Empty cells after the
model are still allowed.

* Data: Solver removes only its own names when it saves or resets a model

Saving a model (by a solve or Load Model) and Reset All removed every
name of the sheet that starts with solver_, so a user's own name such as
solver_budget was deleted with the model. They now remove only the
Solver's names: the ones Excel's Solver writes (solver_opt, solver_adj,
solver_lhs1, solver_rel1, solver_rhs1 and so on) and GridCraft's own.
The names are matched by name rather than by the hidden flag, so a model
whose names lost that flag is still replaced in full.

* Data: Solver Save Model refuses a place where the options cannot spill

The options row that Save Model writes is an array constant of eight
values, which spills into the seven cells to the right of its first
cell. Content in any of those cells blocked the spill and the options
cell showed #SPILL!. Save Model now checks the whole spill range before
it writes anything and refuses, naming the cell, when a cell there holds
content or another formula's spilled values or is a cell of the model,
when merged cells or a table meet the row (a dynamic array cannot spill
into a table), or when the range runs past the last column. It also
refuses a place where the model's column would cover another formula's
spilled values, which that formula would lose to #SPILL!. The options of
a model saved at the same cell before may be replaced, as before.

* Data: Solver Reset All and Load Model drop the old model's options

Reset All cleared the model fields of the Solver Parameters form but
kept the Evolutionary options (Convergence, Mutation Rate, Population
Size, Random Seed, generations without improvement and Require Bounds),
and Load Model kept any of them that the loaded model omits, such as all
of them for a GRG Nonlinear or Simplex LP model. A new Evolutionary model
then solved with the old options, for example with Require Bounds off.
Both now remove the model's options from the form first (one list of
their keys), so an option the model doesn't set takes its default.

* Data: Solver Save Model asks before it replaces data

Save Model wrote its formulas over whatever the model range held.
It is now refused, with the sheet unchanged, when a cell of the
range holds a value or a formula (a model saved there before
included), unless `overwrite: true` is passed. The message names
the first such cell and carries the same "already holds data"
phrase as Data Analysis, so the Solver Parameters dialog asks
"Replace it with the model?" with Replace and Don't Replace. The
question belongs to the range it asked about: editing the range,
Load or Reset All drops it. The check runs after the spill checks,
so the question is asked only when replacing would succeed. A save
remains one undo step.

Tests: the engine test refuses a value, a formula and an earlier
model, accepts only a true boolean, and undoes and redoes the
replacing save in one step; the dialog test covers Don't Replace,
an edited range and Replace. Both fail without the change.

* Data: Solver Load Model and Reset All tests for limits and undo

Tests for behaviour already in place: reading down from one cell of
a column of 5,000 formulas stops after the most rows a model takes,
a range larger than a model is refused before any cell is read, and
malformed or hostile cells and ranges (unbalanced brackets, an
overflowing number, an unknown sheet, 10,000 nested brackets, a
non-text `from`) each give an error and leave no model. Undoing
Reset All brings the model back with its options (the population
size).

* Data: Solver Load Model refuses a model without its options row

Load Model from one cell read down to the first cell without a
formula, and a range dropped its trailing cells without formulas.
A constraint cleared in the middle of a saved model, or its options
row cleared or replaced by a constant, therefore left a prefix of
the model, which was loaded as the whole model with default options
and replaced the sheet's model. Every load now requires the model's
last row to be the options row ({...}) that Save Model writes last;
otherwise it is refused, naming the cell without a formula when
reading down from one cell, and the sheet's model stays as it was.

The test clears a middle constraint, clears the options row and
replaces it by a constant, loads from the first cell and as two
ranges, and checks after each refusal that the old model (three
constraints, Simplex LP) is unchanged. It fails without the change.

* Data: Solver Load Model refuses an options row cut short

The options row of a saved model was read leniently: a missing value
or one that isn't a number took its default, and values after the
sixteenth were not read at all. A row cut short or edited by hand,
such as ={1}, therefore loaded the model with default options in
place of the saved ones (here: GRG Nonlinear in place of Simplex
LP). The row must now hold at least the eight numbers Save Model
writes (method, non-negative, population size, mutation rate, random
seed, require bounds, convergence, generations without improvement),
and every value in it, however many, must be a number; otherwise
Load Model is refused and the sheet's model stays as it was. Only
the first sixteen values are kept. More numbers than eight are still
read, so a row with options a later version adds loads too.

The test loads rows of one value, seven values, a text value among
the eight, after them and after the sixteenth, and a TRUE, from the
first cell and as a range, checking the old model after each
refusal, and a row of ten numbers, which loads. It fails without the
change.

---------

Co-authored-by: FredApps <FredApps@users.noreply.github.com>
2026-11-01 20:13:00 +01:00
Harrison Booth 0e643e9026 Merge remote-tracking branch 'upstream/main' into gc-xls-cell-styles 2026-10-27 15:59:33 -05:00
aiongg f102fe29a1 Merge g3 into g4
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/edit.rs
#	crates/engine/src/cmd/formulas.rs
#	crates/engine/src/cmd/inspect.rs
#	crates/engine/src/cmd/pivot.rs
#	crates/ui-egui/src/lib.rs
#	crates/ui-egui/src/tabs.rs
#	docs/gaps.md
#	docs/ui-parity.md
2026-10-26 02:45:41 +00:00
Brandon Thomas 0665476b35 Merge pull request #2493 from Harrison-Booth:feat/options-iteration-fields
# Conflicts:
#	docs/function-parity.md
#	docs/ui-parity.md
2026-10-25 09:36:59 -04:00
aiongg cb1fb89549 Merge storytold/gridcraft#2467: PivotTables: right-clicking a value cell offers Show Details in the cell menu
# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/pivot.rs
#	docs/ui-parity.md
2026-10-25 02:46:38 +00:00
Harrison Booth bf85ba482c Align .xls estimate in gaps with file-format parity 2026-10-24 15:10:40 -05:00
dotCooCoo 30126a709a PivotTables: Enable show details turns Show Details off for a PivotTable
A PivotTable has an Enable show details option, set with the new
pivot.options command ({pivot?, enableShowDetails?: bool}). While it is
off, pivot.showDetails and double-clicking a value cell list no records:
the double-click shows a message and does not open the cell for editing.
Labels and headers still edit. Changing the option is one undo step and
does not refresh the report. pivot.fields reports the setting.

XLSX files keep it: an off option is written as enableDrill="0" on the
pivotTableDefinition, and enableDrill is read on open (on when absent).
2026-10-24 07:46:29 -07:00
Harrison Booth 2c052d03d5 Read cell formatting from .xls files
.xls (Excel 97-2003) import used only each cell's number format. It now
also reads the BIFF8 formatting records (Font, XF, Palette and Style),
so opened sheets keep their fonts, solid and pattern fills, borders
(including diagonals), alignment (rotation, indent, wrap, shrink),
locked and hidden protection, the workbook's custom colour palette, its
Normal font as the default, and its user-defined cell styles.

The decoding lives in a new xls/styles.rs; xls/mod.rs gains the record
arms and the build call. Short or malformed formatting records keep the
fields present and add one warning instead of refusing the workbook.
Tests build the records in code, including truncation and byte mutation.

Also merges origin/main, keeping its dialog-sheet, WINDOW2 and XF
number-format handling; the XF arm now goes through the new styles
module, which covers the number format too.
2026-10-23 16:32:15 -05:00
Harrison Booth ac890b839a Merge origin/main into gc-options-iteration-fields
# Conflicts:
#	ROADMAP.md
#	crates/ui-egui/src/backstage.rs
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/tests/it/file_menu.rs
#	docs/function-parity.md
#	docs/gaps.md
#	docs/ui-parity.md
2026-10-23 16:28:15 -05:00
Marco Arnulfo 0c14f59555 PivotTables: Show Details, double-click a value cell to list its source records on a new sheet (#2364) 2026-10-23 20:21:27 +02:00
aiongg 2ed3df2bd6 G4: two windows on one workbook without copying it; Arrange All
- The workbook's active sheet is an atomic set in place (Workbook::active_sheet /
  set_active_sheet), so a window swap or sheet switch never clones the
  Arc<Workbook> or marks it dirty. Selections already live in DocState and the
  window views. Save records the sheet of the most recently active window.
- A dialog taken out of its window while it runs goes back to that window when
  a command run from it switched windows (dialogs::show, ui.dialog.confirm).
- view.arrangeAll {arrange?: tiled|horizontal|vertical|cascade, activeWorkbook?}
  returns the visible windows, active first; the desktop moves and sizes the
  native windows over the main window's monitor. New Window and Arrange All
  buttons on the View ribbon; Arrange Windows dialog.
- Docs: ui-parity, hardware-parity, gaps #20, target-app-parity, ROADMAP.
2026-10-22 16:50:10 +00:00
aiongg ca1cdd4229 Integration: track t13
# Conflicts:
#	ROADMAP.md
#	contributors/people.toml
#	crates/engine/src/cmd/data.rs
#	crates/xlsx/src/tests/mod.rs
#	docs/chart-parity.md
#	docs/file-format-parity.md
#	docs/function-parity.md
#	docs/gaps.md
#	docs/localization-parity.md
#	docs/target-app-parity.md
2026-10-22 03:05:18 +00:00
aiongg 9350ab1def Integration: track t11
# Conflicts:
#	ROADMAP.md
#	apps/gridcraft-web/Cargo.toml
#	crates/ui-egui/src/i18n.rs
#	docs/control-protocol.md
#	docs/gaps.md
#	docs/target-app-parity.md
#	docs/ui-parity.md
2026-10-22 03:04:52 +00:00
aiongg 4c5c345c31 Integration: track t10
# Conflicts:
#	ROADMAP.md
#	crates/ui-egui/tests/sheet_tabs.rs
#	crates/xlsx/src/tests/roundtrip.rs
#	docs/gaps.md
#	docs/ui-parity.md
2026-10-22 03:04:30 +00:00
aiongg cfda01c831 Integration: track t06
# Conflicts:
#	ROADMAP.md
#	contributors/people.toml
#	crates/ui-egui/src/i18n.rs
#	crates/ui-egui/tests/interaction.rs
#	docs/gaps.md
#	docs/localization-parity.md
2026-10-22 03:03:17 +00:00
aiongg 78b7ef49fd Merge PR #1889: Docs: correct stale chart round-trip notes 2026-10-22 02:36:08 +00:00
midasdf 97aa04dc80 i18n(ja): add the §11 revision row and align the docs header dates
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.
2026-10-21 22:51:09 +09:00
midasdf ad32e6a847 i18n(ja): translate every dialog, message and menu string the pt-BR catalog covers
The Japanese catalog still lived inline in i18n.rs with its ribbon and widget
labels, so with the interface set to 日本語 the dialog titles and labels, message
boxes, Backstage/File-view items, template names and native menus showed in
English. This moves it to i18n/ja.rs and adds the 545 keys the Brazilian
Portuguese catalog translates beyond it: 943 keys, the coverage pt-BR has
(~43% of ~2,200 strings), in Excel for Mac 日本語版 wording.

CORE keeps the shared reference set every catalog covers, verbatim and in the
same order (the cross-catalog coverage test still points at it); DIALOGS holds
the new strings, and the lookup checks it first. One test parses pt.rs's source
so the two catalogs cannot drift apart again, one checks that every key is
unique across the two lists and every value is translated, and one spot-checks
the high-value strings.

Docs: the Japanese row and the "Where we are" bullet in
docs/localization-parity.md (943, ~43%), a revision row, the ROADMAP Languages
paragraph and progress log, and gaps.md §11.
2026-10-20 13:45:17 +09:00
Hamza 92e1e96997 Docs: §6's calculation bugs have all landed
gaps.md §6 still listed four things as missing and function-parity.md still
marked 15-significant-digit comparison as missing, citing #57 — all five
reports are closed and their PRs merged. The work list reads as the place to
pick work from, so a closed entry sends someone after work that is done.

Checked by behaviour, not by issue state, where that was possible:

  =0.1+0.2=0.3                                -> TRUE
  =(0.1+0.2)-0.3                              -> 5.55E-17  (full precision kept,
                                                 as in Excel: the 15-digit rule
                                                 is for comparison)
  =MATCH(0.3,{0.30000000000000004},0)         -> 1
  =COUNTIF({0.30000000000000004},0.3)         -> 1
  a spill blocked and then unblocked           -> #SPILL! then the spill again,
                                                 with its dependent back to 6

and by their regression tests for the other two:
`deleting_a_3d_span_end_sheet_keeps_the_rest` (#140) and
`sheet_operations_keep_names_on_their_sheets` (#134).

§6 is left open rather than deleted: it says wrong numbers are the worst kind of
bug, and five closed reports are not the same as the category being clear —
that needs §15's oracle suite. The function counts are untouched; this is
formula-language semantics, not the inventory.
2026-10-19 20:37:17 +05:00
Francisco Herrera 5f2fb705b8 Docs: correct stale chart round-trip notes 2026-10-18 16:42:19 -05:00
euisuhandOpenAI Codex 370b220834 fix(solver): commit solutions and reports atomically
Prepare report data before editing so refused reports cannot leave solution cells or the saved model changed. Commit accepted reports with the optional solution and model, retaining array/protection checks and row autofit after sheet insertion.

Closes #1050

Co-Authored-By: OpenAI Codex <noreply@openai.com>
2026-10-15 15:23:19 +09:00
Harrison BoothandClaude Opus 5.5 5bd6cc3d04 Options: Maximum Iterations and Maximum Change in File > Options > Formulas
The Formulas section had the calculation-mode radios and Enable iterative
calculation but not the two limits that formulas.calculationOptions already
takes. They now sit under the checkbox, disabled while iteration is off, and
commit when the field loses focus. Like Excel, out-of-range entries
(Maximum Iterations outside 1-32767, a negative or non-numeric Maximum
Change) are refused with a message instead of being clamped by the command;
the Enter that committed the field no longer also dismisses that message.

Labels and messages are translated for every interface language. A kittest
covers the disabled state, valid and invalid entries, and undo refreshing
the field. Drops the stale "no Options dialog yet" from gaps.md and
function-parity.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-15 01:19:03 -05:00
jonathanwxh-cell 18092672ee #701: integrate hidden-sheet navigation with current main 2026-10-14 00:01:18 +08:00
Lexx143andClaude Fable 5.1 209d2447d9 Charts: multi-level category axes, read, drawn and written back
A category range two or more columns wide beside a column of values (year beside month, Excel's
multiLvlStrRef) was read as one flat list: 36 rows in two columns became 72 categories, the
series sat in the left half of the plot and the years and months shared one row of labels.
Saving wrote the range as a plain strRef, so Excel lost the two-level axis too.

- gridcraft_core::category_levels: the shape of a category range beside its values (levels are
  columns when the points run down the rows, rows when they run along the columns; up to three
  outer levels), shared by the chart and xlsx crates.
- ChartData.category_levels: the outer levels, nearest the labels first, one entry per category,
  non-empty where a group starts. resolve fills it; a single column is unchanged.
- render: the outer levels in rows under the labels, a group's name centred under its categories
  with a separator at each boundary; in bar charts in columns beside the labels.
- xlsx: <c:cat> written as multiLvlStrRef with the level caches (innermost first), so the axis
  survives a round trip.

Tests in core (shapes), chart (resolve and layout for columns, bars and a row of values) and
xlsx (written XML, read-back, stability, a single column stays strRef). chart-parity, gaps § 4
and the ROADMAP progress log updated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-13 17:30:01 +03:00
Brandon ThomasandClaude Opus 5.5 589b59defc Docs: one Last updated line per doc after the merges
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 02:02:41 -04:00
Brandon Thomas ebe0138d30 Merge main into #855
# Conflicts:
#	ROADMAP.md
#	docs/gaps.md
#	docs/target-app-parity.md
2026-10-13 01:52:08 -04:00
Brandon Thomas a67b5d2960 Merge main into #794
# Conflicts:
#	ROADMAP.md
#	docs/gaps.md
#	docs/ui-parity.md
2026-10-13 00:08:22 -04:00
Brandon Thomas e5f31a02f3 Merge main into #786
# Conflicts:
#	ROADMAP.md
#	docs/gaps.md
2026-10-13 00:08:15 -04:00
Brandon Thomas 29775397ef Merge main into #712
# Conflicts:
#	ROADMAP.md
#	crates/xlsx/src/tests/mod.rs
#	docs/file-format-parity.md
#	docs/gaps.md
2026-10-13 00:07:50 -04:00
Brandon Thomas 98c85c6de4 Merge pull request #718 from neilcawse:fix/pivot-values-on-rows 2026-10-12 23:49:00 -04:00
Brandon Thomas 591040ce09 Merge pull request #475 from prxnvt:pr/solver-extras 2026-10-12 23:35:22 -04:00
Eduardo Soares dc3ccc4ea0 #833: docs — track row auto-fit for oversized fonts
# Conflicts:
#	ROADMAP.md
#	docs/gaps.md
2026-10-12 20:48:43 -03:00
vangel76andClaude Sonnet 5.5 339f8ed35b Page Layout view: drag the margins on the rulers (#792)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 20:57:02 +02:00
vangel76andClaude Sonnet 5.5 47c0af3f1b Data: Forecast Sheet Forecast Start option (#785)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-12 20:34:07 +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
jonathanwxh-cell d4874373a4 #551: preserve editable ink through XLSX round trips 2026-10-12 23:54:44 +08:00
jonathanwxh-cell 561299bfad sheet-navigation: keep boundary navigation on visible worksheets 2026-10-12 22:59:53 +08:00
prxnvt 019d5de98e Data: Solver Answer and Limits reports
Builds on the Solver of #327. data.solver takes reports: [answer, limits]
and writes them on new sheets before the model's sheet once a solution
is found. The Answer report lists the objective and variable cells with
their original and final values and each constraint's value, formula,
status (Binding or Not Binding) and slack; cells are named by the
nearest labels to their left and above. The Limits report minimises and
maximises each variable with the others held at the solution, under the
model's constraints, and gives the objective at both ends (Simplex LP
and GRG Nonlinear, without integer constraints).

data.solverReport writes the reports for a given solution and model,
so data.solver's result (which now carries the original values and the
model) can be passed to it as is. The Solver Results dialog offers the
two reports; they are written after Keep Solver Solution or Restore
Original Values.
2026-10-11 22:25:08 -04:00
prxnvt fa2b9566fb Data: Solver Evolutionary method
Builds on the Solver of #327. method: evolutionary runs a genetic search
for models that aren't smooth: tournament selection, blend crossover and
Gaussian mutation inside the variable bounds, feasibility-first ranking
and integer rounding, then a compass search around the best member. It
stops when the population converges, when the best member stops
improving for a number of generations, or at the evaluation budget. A
seeded generator makes runs repeatable. Population size, mutation rate,
random seed, convergence and Require Bounds are options; the method and
its options are saved in the hidden solver_* names. The Solver
Parameters dialog offers the method.
2026-10-11 22:09:10 -04:00
skylar taylor-barrick 587d879f13 Print: adopt craft-print with native settings and job feedback 2026-10-12 03:25:47 +02:00
Brandon Thomas a34fe4a7af Merge pull request #436 from LloydNicholson/feat/open-encrypted
Open password-encrypted workbooks (Agile and Standard encryption) (#11)

# Conflicts:
#	ROADMAP.md
#	docs/file-format-parity.md
#	docs/gaps.md
#	docs/target-app-parity.md
2026-10-11 18:06:04 -04: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 f170b89a92 Merge pull request #420 from neilcawse/fix/structref
Formulas: trim-reference operators (A1:.E10, A1.:E10, A1.:.E10); read unbracketed [[#This Row],Col]

# Conflicts:
#	docs/function-parity.md
#	docs/gaps.md
2026-10-11 18:05:40 -04:00
lloydandClaude Opus 5.5 8de8bc187a Docs: encrypted workbooks open with their password (#11)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 23:29:28 +02:00
neilcawseandClaude Opus 5.5 8ea2342703 Charts: read, write and draw trendlines
Model: Series.trendlines (Vec<Trendline>, serde default) with kind
(linear, exponential, logarithmic, polynomial 2..6, power, moving
average 2..255), name, forward/backward forecast, set intercept,
equation / R² display and line colour, width and dash.

XLSX: read and write <c:trendline> in CT_Trendline child order, placed
after dLbls and before the data references in line, bar, area, scatter,
bubble and stock series; unknown types are skipped.

Chart: least-squares fits (transformed for exp/log/power, skipping
non-positive values; polynomials via normal equations on centred,
scaled x with partial pivoting), moving averages, forecasts that widen
the category or x axis, clipping to the plot, equation and R² labels and
legend entries ("Linear (Sales)"). Degenerate data draws nothing.

Engine: chart.addElement element="trendline" adds (type, series, order,
period, forward, backward, displayEquation, displayRSquared) or removes
trendlines; document.inspect lists each chart's trendlines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:39:31 -04:00
neilcawseandClaude Opus 5.5 9ce910f87f Formulas: trim-reference operators (A1:.E10, A1.:E10, A1.:.E10); read unbracketed [[#This Row],Col]
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:39:31 -04:00
neilcawseandClaude Opus 5.5 fdb74c27dd Calc: iterative calculation for circular references (Maximum Iterations, Maximum Change)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 16:39:31 -04:00
Brandon Thomas 7a34692a0d Merge pull request #325 from vangel76/feat/forecast-sheet
Data: Forecast Sheet (data.forecastSheet) (#322)

# Conflicts:
#	ROADMAP.md
#	docs/gaps.md
#	docs/parity-checklist.md
#	docs/ui-parity.md
2026-10-11 13:52:00 -04:00
Brandon Thomas fda7f5bc14 Merge pull request #333 from vangel76/feat/xlsx-passthrough
XLSX: keep unmodelled parts on save (#330)

# Conflicts:
#	ROADMAP.md
#	crates/model/src/lib.rs
#	crates/model/src/sheet.rs
#	docs/file-format-parity.md
#	docs/gaps.md
2026-10-11 13:51:59 -04:00
Brandon Thomas c379da6ac9 Merge pull request #332 from vangel76/feat/xls-read
XLSX/IO: read legacy .xls (BIFF8) workbooks (#121)

# Conflicts:
#	docs/gaps.md
2026-10-11 13:51:48 -04:00