home.manageRules took the number of rules once, before deleting, so
{"delete":0,"moveUp":1} with two rules swapped past the end of the
shortened list and panicked (index out of bounds; the session's guard
kept the workbook). The count is now taken after each step.
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>
Text boxes ignored a:rPr (size, bold, italic, typeface, fill, outline),
a:pPr algn, a:bodyPr anchor and spPr/a:ln/a:noFill. They are now read into
Shape::format, drawn on screen and in PDF, and written back.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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>
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>
- 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>
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>
- 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>
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>
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.
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.
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>
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>
Re-anchor loop zips objects with their measured corners instead of indexing; a sort moves
only objects anchored inside the sorted block on the other axis too (a picture at Z3 stays
put when A1:B4 is sorted). insert.picture defaults to Move but don't size (Excel's
editAs="oneCell", which the writer emits for it), and object.setAnchorMode errors on an
unknown or empty kind. Tests for each.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
shape.setText refuses edits on a protected sheet (drawing objects are
locked; there is no edit-objects allowance yet) and text longer than
32,767 characters, like a cell. The editor doesn't open on a protected
sheet and its TextEdit carries the same character limit. Tests for both.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Clip a file chart's series block to the sheet's used range and refuse blocks over the
insert.chart cell cap, so whole-column references can't build ~1M series.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Each header was checked against every name before it, once per suffix
tried, so a source whose headers repeat (a row of thousands of equal
labels, such as check boxes) took cubic time: 4,000 columns took 28 s
in Recommended PivotTables, and a row of 16,384 hung the app, in
PivotTables, in Insert Table and in Get Data from Table/Range. Taken
names and the next suffix of each header are now kept in hash maps, in
the pivot source, in table creation and in the XLSX pivot cache; the
names are the same as before.
chart.switchRowColumn did nothing on a chart read from a file: only
charts made by insert.chart keep their source range. Without one, the
command now takes the block the series' names, categories and values
span (when they're all on one sheet), and keeps it.
Add support for Excel's "smoothed line" chart style. The smooth flag flows
model -> resolve -> chart render, persists through XLSX (<c:smooth val="1"/>),
and is toggled via the chart.addElement "smooth" element.
Rendering interpolates each gap-free run with a uniform Catmull-Rom spline
(0.5 tangent scale, 100 sub-segments per data segment), so the curve passes
through every data point and rounds the vertices — matching Excel's observed
behaviour. Long runs are still decimated before smoothing.
- model: Series.smooth (#[serde(default)], so old workbooks load unchanged)
- chart: SeriesData.smooth; smooth_run + catmull in render; wired through
resolve and the sanitize() copy; draw_runs takes the flag (line + scatter)
- engine: chart.addElement element="smooth"
- xlsx: read/write <c:smooth val> instead of hard-coding val="0"
- tests: smooth_line_adds_interpolated_points_and_keeps_endpoints,
smooth_line_splits_at_gaps, smooth_line_setting_round_trips
- examples/smooth_demo.rs: generates a straight+smooth side-by-side workbook
cargo xtask ci: all 7 steps pass.
Excel anchors a floating object to the grid in one of three ways, stored in the
file as the drawing anchor's `editAs`: "move and size with cells" (twoCellAnchor,
the default), "move but don't size with cells" (oneCellAnchor / editAs="oneCell")
and "don't move or size with cells" (editAs="absolute"). GridCraft had no such
concept: xlsx import dropped `editAs`, export always wrote a plain twoCellAnchor,
and sorting/filtering never moved an object — so pictures stayed put when their
rows were reordered.
Model: add `AnchorMode` on `Anchor` (serde default = MoveAndSize, so existing
documents round-trip unchanged).
xlsx: read the anchor kind and `editAs` into the mode; write it back out
(editAs="oneCell"/"absolute" as Excel does).
Engine: sorting now moves objects anchored inside the sorted body with their
lines, re-fitting "move and size" objects, keeping "move but don't size"
objects' size, and leaving absolute ones pinned. Structural insert/delete
honours the same rule (previously it moved every object's cell unconditionally
and ignored shapes). New `object.setAnchorMode` command sets an object's mode;
`arrange.selectionPane` reports it.
Tests: sort moves images with their rows; absolute objects stay put across a
sort and a row delete; move-only keeps its size; the mode survives an xlsx
round-trip; the drawing fixture parses all three anchor kinds.