Skip to content

feat(html): load the later chunks of a sheet on demand - #1255

Merged
andiwand merged 3 commits into
mainfrom
feat/sheet-chunks-on-demand
Oct 24, 2026
Merged

andiwand merged 3 commits into
mainfrom
feat/sheet-chunks-on-demand

Conversation

@andiwand

@andiwand andiwand commented Oct 24, 2026 •

Copy link
Copy Markdown
Member

🤖 Generated with Claude Code

Refs #764.

Summary

This is level 3 of docs/design/lazy-loading.md for sheets, the page side only, as decided. The decode stays as it is.

  • HtmlConfig::lazy_loading now also writes each chunk of rows after the first as a script, sheet<n>-chunks/<m>.js. It applies to a read-only render with lazy_layout. The script calls odr.chunks.fill("<n>-<m>", html), and its html is the chunk's cols and tbody.
  • translate_sheet splits into three parts:
    • SheetRows: the rows' layout, shared with the chunk resources.
    • write_row: one row, which carries the running merge state in a RowCursor.
    • sheet_chunk: a row range alone. A chunk starts where no merge is open, so a fresh cursor at its first row is right.
  • The page writes the rows of a loading chunk to a null writer, because that is how it finds where the chunk ends. Where the rows would go, it writes an empty table with --odr-chunk-height, which keeps the height of the rows.
  • On request, the rows render again with inline styles, because the page's head is already written by then.
  • Serving: the document service answers the scripts from the resources that warmup collects, and bring_offline writes them beside the page. No server change.
  • Scripts:
    • viewport.js raises odr-chunk-filled after a fill.
    • spreadsheet.js builds its position map again after a fill, keeps a chunk that is still to load, and offers no sort while rows are still to load, because a sort needs every row.
  • With the default config, every sheet page is byte for byte what it was: a full regen differs only in spreadsheet.css, spreadsheet.js and viewport.js.
  • References: pins public cce7c60 and private 0661e57. The output commits name OpenDocument.core#1254, because that number was taken while I worked; they belong to this PR.

Measured: the 500 K-cell sheet of efficiency-big-1.ods with lazy_loading, from file:// in headless Chrome, is a page of 370 KB (14.9 MB inline) and 122 scripts. It paints its first frame after 107 ms. After a scroll to the middle, rows 3996–4022 have loaded and line up with the ruler. The decode still peaks at 669 MB, which is the core question deferred in the design doc.

Test

  • html.a_sheet_loads_its_later_chunks, a 300 × 20 csv:
    • the first 204 rows are inline, with the placeholder and resource of chunk 1
    • the script serves rows 205–300
    • an editable render keeps every row inline
  • All tests pass. All 20 sheet check pages and both viewport check pages pass.

Review fixes

  • Images of rows that load later: the page writes those rows to a null writer, to find where a chunk ends. Their resources went into a list that nothing read. So with embed_images off, or with an external image, bring_offline did not write the image, and the chunk script linked a file that did not exist. The null writer now adds to the page's resources. html.a_sheet_lists_the_images_of_a_chunk_that_loads_later fails without the fix.
  • -Werror: SheetRows rows{.sheet = sheet} left members out of a designated initializer, which fails with -Wmissing-designated-field-initializers (Homebrew clang, as CI). Every member of SheetRows now has a default value.
  • All the chunks of a sheet share one HtmlConfig, where each chunk resource held a copy.
  • Checked again:
    • 592 C++ tests pass.
    • All the sheet and viewport check pages pass, with the formula pages run against the wasm module.
    • The -Werror check and the local clang-tidy are clean on document_element.cpp.
    • Both pins are on the remote main of their output repos.

andiwand and others added 2 commits October 24, 2026 23:43
`HtmlConfig::lazy_loading` now also writes each chunk of rows of a
sheet after the first as a script of its own,
`sheet<n>-chunks/<m>.js`. It applies to a read-only render, on the
decode as it is.

- `translate_sheet` splits into the layout of the rows (`SheetRows`),
  one row (`write_row`), and a chunk alone (`sheet_chunk`).
- The page writes the rows of a loading chunk to nothing, because that
  is where it finds the chunk to end, and writes an empty table there.
  A request renders the rows again, with inline styles, because the
  head of the page has gone out by then.
- The document service serves the scripts from the resources that
  `warmup` collects. `bring_offline` writes them beside the page.
- `spreadsheet.js` builds its position map again after a chunk fills.
  It offers no sort while rows are still to load.

With the default config, every sheet page is byte for byte what it
was. The sheet of 500 K cells in `efficiency-big-1.ods` becomes a page
of 370 KB and 122 scripts, and paints after 107 ms.

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 page writes the rows of a chunk that loads later to a null writer, to
find where the chunk ends. Their resources went into a list that nothing
read. So with `embed_images` off, or an external image, `bring_offline`
did not write an image of such a row, and the chunk script linked a file
that did not exist. The null writer now adds to the page's resources.

Also:
- All the chunks of a sheet share one copy of `HtmlConfig`.
- Give every member of `SheetRows` a default value. A designated
  initializer that left them out failed -Werror with
  -Wmissing-designated-field-initializers.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YHNzLmVv1mFWfPitHASucN
@andiwand
andiwand merged commit d84ddf4 into main Oct 24, 2026
27 checks passed
@andiwand
andiwand deleted the feat/sheet-chunks-on-demand branch October 24, 2026 21:58
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.

1 participant