Commit Graph

48 Commits

Author SHA1 Message Date
Brandon Thomas 1c3beb2a95 Merge branch 'land' into fix-401
# Conflicts:
#	ROADMAP.md
2026-10-11 16:09:03 -04:00
Brandon Thomas 4a23b9cf4b Merge pull request #404 from vangel76/fix/header-footer-variants
Headers and footers: first-page and even-page variants, Insert › Header & Footer edits in place (#344)

# Conflicts:
#	ROADMAP.md
2026-10-11 16:08:56 -04:00
vangel76 cdd71b4d5d Headers and footers: first-page and even-page variants, Insert › Header & Footer edits in place (#344)
XLSX headerFooter kept only oddHeader/oddFooter: a workbook whose header is on a
different first page (differentFirst + firstHeader) or on even pages showed no
header, and saving dropped it. The model now keeps the first/even headers and
footers and the differentFirst / differentOddEven / scaleWithDoc / alignWithMargins
flags; XLSX reads and writes them; the PDF and the Page Layout view use the right
pair per page; in-place editing edits the page's own pair, and the Header & Footer
tab has Different First Page and Different Odd & Even Pages. pageLayout.headerFooter
takes the variants and a page: odd|even|first for one-section edits.

Insert › Header & Footer now switches to Page Layout view and edits the first
page's centre header, as in Excel, instead of opening the codes dialog.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:44:49 +02:00
Brandon Thomas f173d6733d Merge pull request #387 from vangel76/fix/sheet-protection-password
Review: Protect Sheet passwords use Excel-compatible hashes, kept in XLSX (#346)

# Conflicts:
#	ROADMAP.md
2026-10-11 15:43:46 -04:00
vangel76 da1d291d93 Drawing: text box run formatting, alignment, anchor and no-outline (#377)
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>
2026-10-11 21:39:53 +02:00
vangel76 22978a7e62 Review: Protect Sheet passwords use Excel-compatible hashes, kept in XLSX
Sheet protection stored an internal FNV hash that was never written to
XLSX, and an Excel sheet password was dropped on import, so imported
protected sheets could be unprotected without it. Protect Sheet now
stores a salted SHA-512 hash (spin count 100000) via PasswordHash; the
XLSX sheetProtection password (iterated or legacy) is read and written,
and Unprotect Sheet really verifies it. Hashes from older GridCraft JSON
workbooks are still read and verified.

Refs #346

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:29:10 +02:00
Brandon Thomas 9bf5ecaf04 Merge main into VBA preservation; save macros only into .xlsm (an .xlsx never carries a VBA project); drop the shell round-trip script (review fix-up for #19)
The Rust round-trip test covers what scripts/check-vba-roundtrip.sh checked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 15:20:45 -04:00
vangel76 fbabac15d4 Review: Protect Workbook dialog with an Excel-compatible password (#345)
The ribbon button toggled structure protection silently. It now opens a
Protect Structure and Windows dialog with an optional password; when a
password was set, unprotecting asks for it and really verifies it
(SHA-512, spin count 100000, salted). The password hash is read from and
written to XLSX workbookProtection (workbookAlgorithmName/HashValue/
SaltValue/SpinCount or the legacy workbookPassword); an unreadable one
stays locked. File › Info uses the same flow.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:12:24 +02:00
Brandon Thomas 205981a52a Merge branch 'land' into fix-193
# Conflicts:
#	crates/engine/src/cmd/mod.rs
#	crates/ui-egui/src/chartview.rs
2026-10-11 14:48:14 -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 4a55aa3e34 Merge main into page-layout-view: pages keep the gliding selection (#266), frozen-pane clipping (#217), right-to-left mirroring and freeze handles (#310, #309); es/de/cs strings for headers and footers
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:39:07 -04:00
Brandon Thomas 79fce91359 Merge remote-tracking branch 'origin/main' into fix-333
# Conflicts:
#	ROADMAP.md
#	crates/model/src/lib.rs
#	crates/xlsx/src/sheet_read.rs
#	docs/file-format-parity.md
#	docs/gaps.md
2026-10-11 12:56:45 -04:00
Brandon Thomas 97bad77ef4 Merge pull request #313 from vangel76/feat/pivot-chart
Charts: PivotCharts (insert.pivotChart) (#311)

# Conflicts:
#	ROADMAP.md
#	crates/engine/src/cmd/pivot.rs
#	docs/gaps.md
#	docs/parity-checklist.md
#	docs/target-app-parity.md
2026-10-11 12:05:54 -04:00
Brandon Thomas 685802ee9f Merge pull request #306 from vangel76/feat/pivot-calculated-field
PivotTables: calculated fields (pivot.calculatedField) (#304)

# Conflicts:
#	ROADMAP.md
#	docs/gaps.md
#	docs/parity-checklist.md
#	docs/target-app-parity.md
2026-10-11 12:05:52 -04:00
Brandon Thomas 2af0984af0 Merge pull request #308 from vangel76/feat/review-allow-edit-ranges
Review: Allow Edit Ranges with Excel-compatible password hashes (#307)

# Conflicts:
#	ROADMAP.md
#	crates/xlsx/src/sheet_write.rs
#	crates/xlsx/src/tests/malformed.rs
#	docs/file-format-parity.md
#	docs/gaps.md
#	docs/parity-checklist.md
#	docs/target-app-parity.md
2026-10-11 12:05:30 -04:00
Brandon Thomas 1f99931dd3 Merge pull request #302 from vangel76/feat/xlsx-threaded-comments
XLSX: write threaded comments and persons parts, read note visibility (#300)

# Conflicts:
#	ROADMAP.md
#	crates/xlsx/src/tests/roundtrip.rs
2026-10-11 12:05:27 -04:00
vangel76 001f025119 View: Page Layout view with rulers and in-place headers and footers (#329)
View › Page Layout (and the status-bar button) now shows the sheet as printed
pages on a desk instead of overlaying page outlines on the grid.

- Model/XLSX: per-sheet view mode (normal / pageLayout / pageBreakPreview),
  saved as <sheetView view="…"> and read back. view.* commands set it per
  sheet ({sheet?}) without an undo step or dirtying a clean workbook.
- Engine: paper::PageGrid paginates the sheet from A1 with the print
  pagination (print::split_bands, shared with PDF export and printing) and
  maps cells to pages and back; header/footer section helpers
  (split/join, &[Page] field names) shared with the PDF header code.
  pageLayout.headerFooter takes {part, section, text} for one section and a
  sheet; header/footer strings are capped at 255 characters.
  view.ruler toggles the rulers.
- UI: Geo carries the page grid, so painting, hit-testing, selection,
  editing, objects and scrolling work through the page transform; frozen
  panes are ignored in this view. Pages with margins and gaps, "Click to add
  data" pages, rulers in inches (English) or centimetres, headers/footers
  drawn on printed pages, click a margin section to edit it in place; a
  click elsewhere commits (one undo step), Escape cancels. Header & Footer
  contextual tab: presets, Page Number, Number of Pages, Current Date/Time,
  File Path, File Name, Sheet Name, Go to Header/Footer. View › Ruler.
- New strings in every catalog; new code-drawn icons; desk colour token.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:24:06 +02:00
vangel76 ed62390244 XLSX: keep unmodelled parts on save (#330)
Pass-through of package content GridCraft does not model: on read the raw
parts are kept with their content types and relationship graph per owner
(package root, workbook, sheet); on write they get fresh part names where
needed and new relationship ids, and the r:ids in kept XML are rewritten.

Kept: custom document properties, ribbon customisation, add-in task panes,
custom XML, links to external workbooks with <externalReferences> (never
followed), connections, sheet query tables, printer settings with the
<pageSetup r:id> (#324), form and ActiveX controls, OLE objects and sheet
backgrounds with their VML shapes (merged with GridCraft's note shapes).
Content whose graph reaches parts GridCraft rewrites is dropped with a
warning; sheet content goes with its sheet and is not copied by Copy Sheet.

Fixes #324

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 13:12:56 +02:00
vangel76 1a7c16eb9a Charts: PivotCharts (insert.pivotChart) (#311)
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>
2026-10-11 12:31:49 +02:00
vangel76 3b918ada2c Review: Allow Edit Ranges with Excel-compatible password hashes (#307)
- 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>
2026-10-11 12:26:14 +02:00
vangel76 400ab591cb PivotTables: calculated fields (pivot.calculatedField) (#304)
A value field can be a calculated field whose formula is evaluated on
the sums of the fields it names for each report cell (Excel's documented
semantics). Command to add/change/delete, dialog from the field pane,
XLSX cacheField formula round trip.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 12:24:50 +02:00
vangel76 8e5b1ac8e3 XLSX: write threaded comments and persons parts, read note visibility (#300)
- 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>
2026-10-11 12:15:45 +02:00
Alexander Alber 32d0a866f9 XLSX: write one cfvo per icon in icon-set rules (#235)
An icon-set rule without stored thresholds (every rule made by
home.conditionalFormat, and the sales sample) was written as an empty
<iconSet>, which CT_IconSet forbids (cfvo minOccurs 2); openpyxl drops it.
The writer now always emits one cfvo per icon: the stored thresholds when
there is one per icon, the lower bound prepended when it is missing, else
Excel's defaults (percent 0/33/67, 0/25/50/75, 0/20/40/60/80).

Evaluation read the thresholds as one fewer than the icons, but files
carry one per icon (the first is the lower bound), so thresholds read from
a file were ignored. Both now share icon_count and default_icon_thresholds
in gridcraft-model; icon_count is clamped to 3..=5 so a set name like
"0Foo" can't underflow k - 1.

generated with Claude Code
2026-10-11 09:26:00 +02:00
Brandon Thomas 49fbae245a Merge pull request #156 from nihilist-narwhal/fix-legacy-array-formulas
Legacy array formulas: fill the range they were entered in

# Conflicts:
#	crates/engine/src/cmd/edit.rs
#	crates/ui-egui/src/formula_bar.rs
2026-10-10 15:47:56 -04:00
Brandon Thomas de0c715662 Merge pull request #186 from MarekSpodBiedry/issue-126
Write comment and hyperlink maps with A1 keys so JSON save works (#126)

# Conflicts:
#	crates/engine/src/tests.rs
2026-10-10 15:47:35 -04:00
Brandon Thomas f75ded37e7 Merge pull request #105 from harrisonoconnorhover/codex/cell-pictures
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>
2026-10-10 15:02:44 -04:00
Zaid Shurbaji fd2243df95 Add table slicers (insert.slicer / table.insertSlicer)
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>
2026-10-10 21:27:13 +03:00
Brandon Thomas f2ed982fdd Merge pull request #12 from justinjohn0306/fix/anchor-mode-sort
Objects: anchor modes — images/charts/shapes follow cells on sort (#5)

# Conflicts:
#	crates/engine/src/tests.rs
2026-10-10 13:17:30 -04:00
Brandon Thomas bd1be73f2d Merge pull request #62 from justinjohn0306/fix/smooth-line-charts
Line charts: smooth (spline) curves (issue #52)

# Conflicts:
#	crates/xlsx/src/tests/mod.rs
2026-10-10 13:17:07 -04:00
MarekSpodBiedry 1d33ea81be Write comment and hyperlink maps with A1 keys so JSON save works (#126) 2026-10-10 19:08:53 +02:00
Harrison O'Connor-Hoover 9dc3aaf9fa Support static pictures inside cells with XLSX roundtrip (#61) 2026-10-10 12:43:27 -04:00
Brandon Thomas 36723ec310 Merge remote-tracking branch 'origin/main' into pr-12 2026-10-10 12:35:42 -04:00
Brandon Thomas 435a750bba Merge remote-tracking branch 'origin/main' into pr-62 2026-10-10 12:16:17 -04:00
Brandon Thomas 11a921becb Merge remote-tracking branch 'origin/main' into pr-116 2026-10-10 12:04:22 -04:00
Brandon Thomas c30a720998 Merge pull request #114 from nihilist-narwhal/fix-defined-name-entry
Names: R1C1-style names, Create from Selection bottom/right, LAMBDA name case
2026-10-10 11:57:10 -04:00
nihilist-narwhal c650c2828f Legacy array formulas: fill the range they were entered in
A Ctrl+Shift+Enter array formula was evaluated like a dynamic array:

- Read from a file, the cached values in its range were kept as
  constants, so the formula showed #SPILL! (any multi-cell array
  formula saved by Excel or LibreOffice).
- Entered over C2:C4 with a two-value result, C4 stayed blank where
  Excel shows #N/A, and a single-cell array formula spilled.
- On save, the values of its other cells were not cached.

The result is now fitted to the range: a single row or column repeats
across it, cells past the result show #N/A, and a larger result is cut
off. Reading drops the cached values inside the range, entering clears
the range, and saving caches its values. The formula bar shows the
formula in braces, {=B2:B3*2}.
2026-10-10 01:53:55 -05:00
nihilist-narwhal bf1381253f Charts: scatter and bubble charts with text X values number the points
A scatter or bubble chart whose X values are text (a first column of
labels) drew no points, as none of its X values were numbers. Like
Excel, such X values are now ignored and the points numbered 1..n.
2026-10-09 23:31:26 -05:00
nihilist-narwhal d4d38fd224 Names: calls of LAMBDA names keep the name's case
The parser upper-cases function names, so =Double(4) showed as
=DOUBLE(4). Calls of LAMBDA names are now spelled like the name when
a formula is entered and when a file is read.
2026-10-09 23:27:39 -05:00
nihilist-narwhal 2a3d1ffd7f Protection: refuse every edit of a locked cell on a protected sheet
Only cell.set checked protection. The check now runs where every edit
is committed: an edit that changes the value or formula of a locked
cell on a protected sheet is refused with Excel's message, whichever
command made it (clear, paste, fills, setValues, replace, goal seek,
sort, insert/delete cells...).

Inserting and deleting rows and columns follow the protection's
options, and deleted lines must not hold locked cells.
2026-10-09 23:25:58 -05:00
justinjohn0306 69709bd458 Line charts: smooth (spline) curves (issue #52)
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.
2026-10-10 00:09:12 +05:30
justinjohn0306 726a31740e Preserve VBA macros on round-trip; never destroy a macro-enabled workbook (#6)
Opening an .xlsm and saving it silently stripped xl/vbaProject.bin, turning a
macro-enabled workbook into a macro-free one — data loss for anyone whose work
depends on macros. GridCraft still does not run VBA, but it now carries the
project through unchanged.

- model: `Workbook.vba_project: Option<Vec<u8>>` (base64 in JSON via a new
  `b64::opt` helper). `None` = no macros.
- xlsx read: capture the `vbaProject` relationship's bytes instead of warning
  and dropping them; warn that macros are kept but not executed.
- xlsx write: when present, emit `xl/vbaProject.bin` byte-identical, add the
  `.../relationships/vbaProject` relationship, switch the main part to the
  macro-enabled content type (`...sheet.macroEnabled.main+xml`) and add the
  `application/vnd.ms-office.vbaProject` override.
- engine: `view.macros` lists the preserved project alongside GridCraft's own
  scripts (`{"kind":"vba","executed":false}`), so callers can see it is there.
- ROADMAP: scope now "preserve, don't execute" (was "out of scope").

Tests: round-trip byte-identical blob + correct content types/relationship;
capture from a foreign-produced .xlsm; a macro-free workbook stays clean.
2026-10-09 00:54:10 +05:30
justinjohn0306 cbc2430f4f Objects: anchor modes — images/charts/shapes follow cells on sort (#5)
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.
2026-10-08 17:58:53 +05:30
Brandon Thomas 488e12de31 Rename SheetCraft to GridCraft everywhere (crates, apps, bundle ids, packaging, docs)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 01:39:08 -04:00
Brandon Thomas 2618a233bf Perf: prefix-sum LineIndex for grid geometry (O(log n) with many custom row heights)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 01:36:28 -04:00
Brandon Thomas f76a0beca3 M8: Draw tab — pen, highlighter, eraser, ink strokes and Ink to Shape
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 01:20:40 -04:00
Brandon Thomas 168b8ec8b7 M4–M10: PivotTables, PDF export & printing, what-if analysis, spelling, more commands
- PivotTables: engine (grouping, date groups, multiple values, show-as, filters, layouts,
  refresh, recommended pivots) + PivotTable Fields task pane
- sheetcraft-pdf (dependency-free PDF writer, core-14 fonts) + file.exportPdf/print/
  printPreview/pageLayout.pageSetup with pagination, headers/footers, charts, images
- What-if: Goal Seek, Scenario Manager, data tables, advanced filter, consolidate
- Page Break Preview rendering; view mode buttons
- Spelling (system dictionary), icons library, cell checkboxes, recorded scripts,
  chart design/format commands, arrange, custom views, watch window, Get Data from CSV/JSON
- Native macOS menu bar
- UI interaction tests (kittest): fixed arrow keys losing grid focus and Escape not cancelling
- Perf: FxHash in calc, sparse range reads, Arc'd formula ASTs, fast auto row heights
- Parity 254/290 (87.6%)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 01:04:46 -04:00
Brandon Thomas beb353943e M1: Excel-style egui UI, desktop app, chart renderer, MCP server, CLI, release packaging
- ui-egui: title bar with QAT, full ribbon (Home/Insert/Draw/Page Layout/Formulas/Data/
  Review/View/Automate + Table/Chart Design), formula bar with Name Box, virtualised grid
  (frozen panes, overflow, borders, CF data bars/scales/icons, tables, sparklines, filter
  buttons, charts/pictures/shapes), in-cell editor with point mode, reference colouring,
  autocomplete and argument hints, sheet tabs, status bar, dialogs, control channel
- apps/sheetcraft: desktop app (control server, file dialogs, prefs, drag & drop)
- chart: toolkit-free chart layout/rendering for 25 chart kinds + sparklines + rasteriser
- mcp + sheetcraft-cli: MCP server (headless or bridged) and CLI (info/convert/eval/cat/run/mcp)
- xtask (ci/layers/assets/wasm/version/parity), release workflows, packaging, placeholder icon
- automatic row heights

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 00:00:15 -04:00
Brandon Thomas 1ba9daa263 M0: foundation — core, numfmt, formula, functions (478), model, calc, xlsx/csv, engine
Clean-room spreadsheet engine: addresses/values/dates, Excel number formats,
formula parser/printer/reference adjustment, worksheet function library,
copy-on-write workbook model, dependency-driven recalculation with dynamic
arrays, XLSX/CSV I/O, and a command engine (cell entry, clipboard, fill,
formatting, sheets, sort/filter, tables, charts, CF, names, view, file).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 23:18:10 -04:00