Repository navigation
feat(html): lay out only the chunks of a sheet near the viewport - #1250
Merged
Merged
Conversation
A sheet is now the ruler and a table for each chunk of rows (`lazy-loading.md` decision 6), because `content-visibility` has no effect on a table row. The sheet of 500 K cells in `efficiency-big-1.ods` took 7.0 s to its first frame in Chrome. Now it takes 4.0 s. - A `div.odr-sheet` takes the attributes of the sheet. The ruler is a sticky table of its own. - A chunk holds up to 256 rows or 4096 cells, at least 8 rows, and ends only where no merge is open. It states the estimated height of its rows. The first chunk of a sheet with shapes stays near. - Every table states the width of the sheet, so `table-layout: fixed` takes the columns from the `col`s alone. A column that states no width gets the width of its longest text in its first 1000 rows. A cell that wraps counts its longest word. - `spreadsheet.js` reads the rows of every chunk. A sort puts the rows back with the count of each chunk, row edits work in the chunk of the row, and column edits change every table and the sheet width. A `reflow` in a far chunk waits until `viewport.js` says it is near. - `viewport.js` gets `odr.chunks.show` and `observe`. The editor and `odr.search` show the chunk of a cell before they measure or scroll. The reference output changes for every sheet page. A column that stated no width took its content's width before, and now takes the estimate. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017QtawyvLP2SCnoBtnVfFed
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017QtawyvLP2SCnoBtnVfFed
The writer has no C++ test for where a chunk ends. A sheet of 300 rows with a merge over rows 251 to 260 now checks that the first chunk takes the merge and the second chunk the 40 rows left, with the heights of both. Also correct the comment of `fods_columns`: a column without a width now takes the width of its longest text. In `spreadsheet.js`, declare `odr` before the helpers that read it. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHNzLmVv1mFWfPitHASucN
andiwand
added a commit
that referenced
this pull request
Oct 24, 2026
The views lay out only the parts near the viewport since #1247, #1248 and #1250, and this PR adds `HtmlConfig::lazy_layout`. A consumer notices both, but the changelog had no entry. Also check the round trip of the jni test with `assertFalse`. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHNzLmVv1mFWfPitHASucN
andiwand
added a commit
that referenced
this pull request
Oct 24, 2026
* feat(html): add a switch for lazy layout `HtmlConfig::lazy_layout` is on by default. Off, no view writes a chunk or the style that hides one, so the browser lays out everything. Its own find and a screen reader then reach every line, row and page. A host can take that where a file is small. - The text and sheet views write all their lines or rows as one block. The pdf pages get no chunk class. - `chunk_classes` gives the classes of a chunk, empty where the switch is off, and `write_chunk_style` writes nothing then. - The text styles move from `.odr-chunk` to `.odr-text-lines`, so a text page without chunks keeps its line numbers. - The apple, jni and python bindings carry the switch. * test: advance the reference output to the lazy layout switch The design names the switch, and the pins move to the reference output of the renamed text class and the reordered pdf page classes. * docs: add the lazy layout and its switch to the changelog The views lay out only the parts near the viewport since #1247, #1248 and #1250, and this PR adds `HtmlConfig::lazy_layout`. A consumer notices both, but the changelog had no entry. Also check the round trip of the jni test with `assertFalse`. Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤖 Generated with Claude Code
Refs #764.
Summary
This implements decision 6 of
docs/design/lazy-loading.md(#1249): a sheet is the ruler and a<table>for each chunk of rows. With the 500 K-cell sheet ofefficiency-big-1.odsin headless Chrome:About 2 s of the rest is
viewport.js, which existed before this change. It sets the body zoom twice to probe text-size adjustment, and each set restyles and relays out the whole document. That is a follow-up.Writer (
translate_sheet)div.odr-sheettakes the sheet's attributes, and the ruler is a stickytable.odr-sheet-head.rowspanis open.contain-intrinsic-height: autofrom its rows' heights, 20px where a row states none.data-odr-chunk-held.--odr-sheet-width), and the gutter and every column are in px, sotable-layout: fixedtakes the widths from thecols alone.Scripts
spreadsheet.jsreads the rows of every chunk.--odr-sheet-width. A new column is 64px.reflowin a far chunk waits for the chunk'sodr-chunk-nearevent.viewport.jsraisesodr-chunk-near, skips held chunks, and getsodr.chunks.showandobserve. The editor'splaceandodr.searchshow the chunk before they measure or scroll.References. Every sheet page changes. I checked the change by screenshot A/B:
sample.xlsx: column D is wider.Konzentration.xls: the headers wrap where they did before.ruski.xlsx: a long number in a wide file font spills past its column. That is the limit of an estimate that does not know the font.Copied to output
mainwith the pins advanced:spreadsheet.jsandsheet-editing.jsalso carry earlier PRs' changes that the references had missed.Test
test/browser/sheetpass. That includes the newchunks.html(15 checks): positions across chunks, row edits into and out of a chunk, an emptied chunk, a column in every table, a deferred spill, and a sort over chunks. The text, plaintext, viewport, bridge and annotation pages pass too.Review fixes
html.a_sheet_ends_a_chunk_only_where_no_merge_is_open: the writer had no C++ test for where a chunk ends. A sheet of 300 rows with a merge over rows 251 to 260 checks that the first chunk takes the merge, and that the second chunk holds the 40 rows left and states 800px.fods_columnssaid that a column without a width is exactly its content's width. It now says that such a column takes the width of its longest text.spreadsheet.jsdeclaredodrbelow the helpers that read it. It now comes first.test/browsersheet, text, plaintext, viewport, bridge and annotation pass, with the formula pages run against the wasm module.