Repository navigation
feat(html): lay out only the pdf pages near the viewport - #1248
Merged
Merged
Conversation
A pdf of 2985 pages took 12 s to its first frame in Chrome, and its renderer held 5.2 GB. Now it takes 1.8 s and 2.0 GB. Each page was `content-visibility: auto`, and Chrome lays out every `auto` page once on the first frame. Now a page is an `odr-chunk`, as the lines of a text file are: - `write_chunk_style` writes the chunk rules inline for both views. A chunk is `hidden` until it is near, `visible` in print, and `auto` where no script runs. - `viewport.js` marks the chunks near the viewport. The observer moves there from `text.js`, because every view loads `viewport.js`. The reference output changes for every pdf and txt page, and for `text.css`, `text.js`, `search.js` and `viewport.js`. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017QtawyvLP2SCnoBtnVfFed
`write_chunk_style` stood between `write_search_style` and `write_search_dark_style`. It now comes after them, in the order of the header. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YHNzLmVv1mFWfPitHASucN
andiwand
force-pushed
the
feat/pdf-chunks
branch
from
October 24, 2026 18:47
7b794c2 to
786c7bd
Compare
andiwand
added a commit
that referenced
this pull request
Oct 24, 2026
The status said that nothing of the design has landed, but #1247 and #1248 did. Also say how `spreadsheet.js` learns that a chunk became near: `viewport.js` dispatches an event on it. A change of the class alone tells no script. 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
* docs: design the chunks of a sheet for lazy loading `content-visibility` has no effect on a table row, so a chunk of a sheet is a table of its own (decision 6): - A `div.odr-sheet` takes the attributes of the sheet. - The ruler gets a sticky table of its own. - Every column gets a fixed width. A column that states none gets the width of its longest text. - A chunk ends only where no merge is open. - Each chunk states the estimated height of its rows. - The first chunk stays near where shapes hang from A1. For each script that reads the rows or the geometry, the design states how it works with chunks. Level 2 of a text file drops out of the order of work, because it gained little when measured. * docs: state that level 1 of lazy loading has landed for text and pdf The status said that nothing of the design has landed, but #1247 and #1248 did. Also say how `spreadsheet.js` learns that a chunk became near: `viewport.js` dispatches an event on it. A change of the class alone tells no script. Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
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
Step 3 of
docs/design/lazy-loading.md: pdf at level 1. WithCore_v5.1.pdf(2,985 pages, a 127 MBdocument.html) in headless Chrome:.p{content-visibility:auto})A pdf page already had
content-visibility: auto. That is the trap that #1247 measured: on the first frame, Chrome lays out everyautoelement once. So a page is now anodr-chunk, as the lines of a text file are:write_chunk_stylewrites the chunk rules inline for both views:hiddenuntil it is near the viewportvisiblein print, which also covers WebKit's blank print of a skipped page<noscript>, a chunk isautoon screenA page states its own size, so it needs no
contain-intrinsic-size.viewport.jsnow holds theIntersectionObserver, moved fromtext.js, because every view loads it. A browser withoutIntersectionObservershows every chunk.The first page is near in the markup, so the first frame does not wait for the observer.
The reference output changes on both output repos'
main, and both pins are advanced:text.css,text.js,viewport.jssearch.js,viewport.jsThe copy is surgical. I checked each changed page: after this change's edits are reverted on both sides (the chunk classes, the chunk style,
content-visibilityon.p), it is byte-identical to the reference.Test
test/browser/plaintextpass.Core_v5.1.pdfrenders after a scroll.printToPDFof a 208-page pdf gives 208 sheets, and sheet 150 has its content.Review fixes
write_chunk_stylestood between the two search styles infrontend.cpp. It now comes after them.test/browserviewport (17), plaintext (54) and annotation (86) pass. The public txt and pdf pages are byte-identical to f3724ee. Both new pins descend from the pins on main.