Skip to content

feat(html): report the sheet a view cut, and budget its cells - #757

Merged
andiwand merged 1 commit into
mainfrom
feat/spreadsheet-cut-reporting
Aug 29, 2026
Merged

feat(html): report the sheet a view cut, and budget its cells#757
andiwand merged 1 commit into
mainfrom
feat/spreadsheet-cut-reporting

Conversation

@andiwand

@andiwand andiwand commented Aug 29, 2026

Copy link
Copy Markdown
Member

🤖 Generated with Claude Code

Fixes #740.

spreadsheet_limit dropped every cell past it and wrote nothing in its place: the <table> simply ended, and nothing came back to say so. A reader saw a document that looked complete and was not, and an embedder could only find out by opening the document a second time to measure it — a second parse, for a read-only format, to re-derive what translation had just computed and thrown away.

The view property

struct HtmlSheetCut final {
  TableDimensions content;   // the extent the sheet's cells span
  TableDimensions rendered;  // the extent the markup carries
};

[[nodiscard]] const std::optional<HtmlSheetCut> &HtmlView::sheet_cut() const;

Nothing where the view writes every cell, and for a view that renders no sheet. Measured without rendering, and cached — so a host can decide before it renders, which is the point. A view writing several sheets into one file (document.html for a workbook) answers for the first it cut; the remedy — re-render with the limits dropped — is global anyway.

No sheet name in the struct: a sheet view's name() already is one.

The cell budget

HtmlConfig::spreadsheet_cell_limit, 500000 cells for one sheet, bounding the rows by how wide the sheet turns out to be:

columns = min(content.columns, limit.columns)
rows    = min(content.rows, limit.rows, cell_limit / columns)

As a rectangle alone the limit punished a tall-narrow sheet — 5 columns by 100000 rows is half a million cells and an ordinary export — to guard against a wide one, where 10000×500 is five million and no WebView survives it. With the budget doing the protecting, spreadsheet_limit rises from 10000 to 100000 rows. The trade is the other way at the wide end: a sheet 500 columns across keeps 1000 rows where it kept 10000 — which is the protection the rectangle was buying badly, and it is no longer silent.

Both halves need the same measurement, so they share one.

What is not changed

The renderer still measures its content extent within the window (Sheet::content(spreadsheet_limit)). Passing nullopt there instead looks like a simplification and is not: for a .ods whose cells stop at row 3 but which carries one cell far down, it turned a 3-row render into thousands of empty ones. The cut is measured separately, against the whole sheet — which is exactly what makes it able to say "you are seeing 3 rows of 65364".

Bindings

python, jni, wasm and apple. wasm gains spreadsheetLimit too — it was never exposed there, and without it a host cannot act on the report.

Test

  • Four C++ tests: no cut when the sheet fits, what a rectangle cut reports, the budget following the width (3 wide → 20 rows, 6 wide → 10, with the rectangle still capping), and a view that renders no sheet.
  • One JNI test and one python test, proving the pair crosses each boundary.
  • Full suite green. No reference output moves: the corpus's largest sheet is 427k cells, under the budget, and the output test keeps its own 4000×500 rectangle.

Base automatically changed from fix/link-scheme-allowlist to main August 29, 2026 08:15
@andiwand
andiwand force-pushed the feat/spreadsheet-cut-reporting branch 2 times, most recently from 4a6ed36 to a9e5ca9 Compare August 29, 2026 08:21
@andiwand
andiwand force-pushed the feat/spreadsheet-cut-reporting branch 3 times, most recently from 8424d2d to 183ab1f Compare August 29, 2026 08:41
`spreadsheet_limit` dropped every cell past it and wrote nothing in its
place: the `<table>` simply ended, and nothing came back to say so. A
reader saw a document that looked complete and was not, and an embedder
could only find out by opening the document a second time to measure it.

`HtmlView::sheet_cut` answers with the extent the sheet's cells span
against the extent the markup carries, or nothing where the view writes
every cell. It is measured without rendering and cached, so the host can
decide before it renders; a view writing several sheets into one file
answers for the first it cut.

`spreadsheet_cell_limit` is the second half. As a rectangle alone the
limit punished a tall-narrow sheet - 5 columns by 100000 rows is half a
million cells and an ordinary export - to guard against a wide one, where
10000x500 is five million and no webview survives it. Budgeting cells and
deriving the rows from the width the sheet turns out to have is the same
protection with the row cap raised tenfold.

Bound in python, jni, wasm and apple; wasm reaches `spreadsheetLimit` for
the first time, without which a host there cannot act on the report.

Fixes #740

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Re7MMYiM7fL58uELzKGy77
@andiwand
andiwand force-pushed the feat/spreadsheet-cut-reporting branch from 183ab1f to f552d1e Compare August 29, 2026 08:49
@andiwand
andiwand merged commit ff334be into main Aug 29, 2026
25 checks passed
@andiwand
andiwand deleted the feat/spreadsheet-cut-reporting branch August 29, 2026 08:50
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.

Spreadsheets are silently cut off at spreadsheet_limit

1 participant