Skip to content

Charts: draw categories in reverse order when the axis says maxMin (#1687) - #4

Draft
isaac-levine wants to merge 422 commits into
mainfrom
chart-axis-reverse-order
Draft

isaac-levine wants to merge 422 commits into
mainfrom
chart-axis-reverse-order

Conversation

@isaac-levine

@isaac-levine isaac-levine commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

The category axis orientation (c:scaling/c:orientation) was never read, and the writer always emitted minMax. A bar chart over a ranked or task list drew its first row at the bottom, and "Categories in reverse order" was lost on save.

Chart gets an x_reverse flag (serde default, so old documents load). The XLSX reader sets it from the category axis, the writer emits maxMin and writes crosses=max on the value axis so it stays at the near edge. The renderer flips the category slots, the axis labels and trendline positions. chart.set takes reverseCategories. docs/chart-parity.md is updated.

Commands run:

  • cargo fmt --all clean
  • cargo clippy -p gridcraft-model -p gridcraft-chart -p gridcraft-xlsx -p gridcraft-engine --all-targets -- -D warnings clean
  • cargo test -p gridcraft-model -p gridcraft-chart -p gridcraft-xlsx -p gridcraft-engine all passed (engine lib 287, chart 35, xlsx 174), including two new tests: a render test for bar and column flipping, and an engine test that sets the flag, saves, reopens and clears it
  • cargo xtask layers ok, cargo xtask wasm ok, cargo check --workspace --all-targets ok

Not verified: I did not run the full cargo xtask ci, and I did not compare against Excel. In Excel a reversed column chart also moves the value axis to the other side unless "crosses at maximum" is set. Here the value axis stays at the near edge, which matches what the writer emits, but I did not check it black-box. The Format pane toggle and the value axis reverse flag from the issue are not part of this change.

The fork's main is behind storytold/gridcraft main, so the diff here lists many unrelated files. The branch is one commit on top of upstream f1894c6 and touches 15 files.

Upstream issue: storytold#1687


Generated by Claude Code

nihilist-narwhal and others added 30 commits October 12, 2026 19:52
A growth series with a step below 1 stopped at once when given a stop
value, since the stop was only checked for rising series. It now bounds
the series in the direction it runs.
A formula such as =A1+A2+…+A500 nests one level per operator, and the
evaluator recursed once per level, so past its depth limit (about 400
operators) the result was #CALC!. Excel allows such formulas up to its
8,192-character limit. Chains of arithmetic, comparison and & operators
are now folded in a loop.
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.
Merge & Center and Merge Cells kept only the upper-left value, but
Merge Across left the other values of each row in place, hidden under
the merge and still counted: with 1 2 / 3 4 in A1:B2, SUM(A1:B2) stayed
10 after Merge Across, where Excel gives 4. Each row's merged area now
keeps only its first value, as the other merges do.
 storytold#864)

[@col] is only Excel's display shorthand. Written verbatim into cell formulas and calculatedColumnFormula, it made Excel repair the file and drop the tables.
…part

Business Central's "Open in Excel" exports write booleans as
<c><f>TRUE()</f><v>1</v></c> with no t="b", so the cached value reads back
as the number 1 and the calc oracle reported every such cell as a
`number → bool` mismatch. A cached 1 or 0 now matches GridCraft's TRUE or
FALSE, and the cells are counted apart (`boolAsNumber` in --json, a note in
the text report) so a real number/logical disagreement stays visible.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
echelon and others added 30 commits October 13, 2026 01:16
# Conflicts:
#	crates/ui-egui/src/dialogs.rs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	docs/ui-parity.md
# Conflicts:
#	crates/ui-egui/tests/interaction.rs
# Conflicts:
#	crates/xlsx/src/tests/passthrough_tests.rs
Brings the branch up to upstream's main (59 more commits). One conflict:

- docs/ui-parity.md: every line of both sides is kept, as upstream's own
  merges keep them, in the order of their dates, newest first (upstream
  has a change of the 13th now, which goes above this one of the 12th).

Gate: all eight steps, 1580 tests.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
# Conflicts:
#	ROADMAP.md
#	docs/gaps.md
#	docs/target-app-parity.md
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: skylartaylor <26588250+skylartaylor@users.noreply.github.com>
…ver crash) (storytold#1330)

A hostile file could abort any process that opens it: a cell formula of a
million `%` (`1%%%…`) or a 200,000-term sum parsed into trees deep enough to
overflow the stack when printed, saved, evaluated or dropped, even in release
builds. Postfix operators (`%`, invoking a result) built their nesting in a
loop that the 256-level limit did not see.

- `parse` refuses formula text longer than Excel's 8,192 characters
  (`MAX_FORMULA_LEN`, counted in characters, the leading `=` excluded). This
  also bounds the depth of the left-deep trees long operator chains build.
- Each postfix `%` and each call of a result counts towards the nesting limit.
- A file's formula that is refused keeps its text and cached value, as other
  unparsable formulas do.

Regression tests: formula (small-stack thread: long `%`, invoke and operator
chains are errors; the limit counts characters, not bytes) and xlsx
(`hostile_formula_text_does_not_overflow_the_stack`: open, save and drop a
workbook with such formulas and a defined name on a 1 MiB stack). Both abort
with a stack overflow without the fix.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…nter-applies

AutoFilter: Enter in the filter menu applies the filter and closes it
…-workflow

ci: delegate FreeBSD workflow, add nix check, and remove flatpak-pages
…torytold#1687)

The category axis orientation was never read, and the writer always
emitted minMax, so a bar chart over a ranked list came out upside down
and the setting was lost on save.

Chart gets an x_reverse flag. The XLSX reader takes it from
c:scaling/c:orientation on the category axis, the writer emits maxMin
and keeps the value axis at the near edge with crosses=max. The
renderer flips category slots, labels and trendlines. chart.set takes
reverseCategories.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

10 participants