Repository navigation
UN-4503 [DEV] Render v1 PDF pages in a windowed, scrolling list - #2405
muhammad-ali-e wants to merge 13 commits into
Conversation
- PdfPageList lays every page out at its own size from the page sizes read at load, so the scrollbar is right from the start, and gives a canvas only to the pages in view plus two on each side. - usePageWindow tracks pages in view with one IntersectionObserver on the scroll container; lib/pdf/windowing holds the window, pixel ratio and CSS size arithmetic. - PdfPage renders a frame after it enters the window, so a fast scroll draws nothing for pages it passes. Leaving the window or a zoom change cancels the render in flight, cleans up the page and zeroes the canvas. The pixel ratio is capped at 2 and lowered further for pages that would exceed the largest canvas Safari allocates. - Page geometry is set as custom properties read by ui: classes, since v1 bans the style attribute. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R7trgcAzu6jxDnit3eeozm
|
The failing hook is There's no fix to port yet. The fix is a one-line change on #2399: Generated by Claude Code |
S1 now allows a style attribute whose keys are all custom properties, so the page size is set directly instead of through a ref and layout effect. React's CSSProperties gains a --* index signature so this typechecks. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R7trgcAzu6jxDnit3eeozm
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R7trgcAzu6jxDnit3eeozm
# Conflicts: # frontend/src/v1/lib/css-custom-properties.d.ts
…ive tech - A page whose getPage resolves after it left the render window is now cleaned up; the effect cleanup had already run with no page to free. - Pages are figures instead of role="img", whose children are presentational and would hide the highlight buttons. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R7trgcAzu6jxDnit3eeozm
Covers a render task that rejects with a real error and a canvas with no 2D context, so a regression that leaves the page in rendering fails. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R7trgcAzu6jxDnit3eeozm
[Low impact] The PR appears safe to merge as part of the reviewed stack, including PR #2408's document-switch protection.
|
A page whose render failed was left blank, so it could not be told from an empty page. It now says it didn't load, with a Retry for that page. Raised by Greptile on #2405. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R7trgcAzu6jxDnit3eeozm
praveen-formido
left a comment
There was a problem hiding this comment.
Windowing and the page component. Two visible problems, both in PdfPage.tsx/PdfPageList.tsx: the canvas is blanked before the replacement render starts, so every page in the window flashes on each zoom step, and the overlay children defeat the memo on every window change. The rest are smaller.
…let pages stay memoised From review on #2405: - A page draws off screen and copies the result in one step, so a zoom change keeps the old picture up instead of blanking every page; the canvas is still zeroed when the page leaves the window or unmounts. - useDevicePixelRatio follows a move to another screen or a browser zoom, and a change redraws the pages. - PdfPage calls renderOverlay itself, so a window change re-renders only the pages that entered or left it. - A page is 'rendering' from the moment it asks for the page, and a failed render is logged with its page number. - usePageWindow skips an element without data-page-index instead of reading it as page 0. - isRenderCancelled and pdfErrorName live in lib/pdf/pdfjs.ts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01R7trgcAzu6jxDnit3eeozm
Dismissed because a newer commit was pushed; Greptile will re-review the current head.
# Conflicts: # frontend/src/v1/lib/pdf/pdfjs.test.ts
Dismissed because a newer commit was pushed; Greptile will re-review the current head.
|



What
New files in the v1 DocumentViewer (P1-T21, UN-4390):
components/patterns/document-viewer/PdfPageList.tsx— shows every page of a document in one scrolling column.components/patterns/document-viewer/PdfPage.tsx— one page: a placeholder at the page's real size, plus a canvas.hooks/usePageWindow.ts— tracks which pages are in view.lib/pdf/windowing.ts— the arithmetic: which pages to render, the pixel ratio, and page size at a zoom.Why
The same viewer has to open 200-page documents. Rendering every page uses far too much memory, so only the pages near the viewport get a canvas.
How
Page sizes and scrollbar: each page is laid out at its own size, using the sizes
usePdfDocumentreads at load (UN-4502). The scrollbar is therefore right before anything renders.Render window: one IntersectionObserver on the scroll container tracks which pages are in view. Those pages plus 2 on each side get a canvas. When nothing is in view yet, nothing renders.
Fast scrolling: a page starts rendering one frame after it enters the window. The UN-4501 spike flagged fast scrolling at 2× zoom as the main risk, and with this delay a fast scroll draws nothing for the pages it passes.
Cancelling and freeing: a page leaving the window, or a zoom change, does four things:
RenderTaskin flightpage.cleanup()Zeroing the canvas first matters because Safari keeps a canvas's memory until garbage collection, and caps the total.
Pixel ratio: the screen's ratio, capped at 2. It is lowered further when a canvas would exceed 16,777,216 px, the largest canvas Safari allocates.
Zoom: zoom 1 means 100%, i.e. the printed size (96/72 CSS px per PDF point).
Sizing without inline style: v1 bans the
styleattribute (S1), so page sizes are set as--page-width/--page-heightcustom properties through a ref, andui:w-(--page-width)/ui:h-(--page-height)read them. I've asked Praveen in #engg-unstract-frontend-v1-migration whether they'd prefer an explicit S1 escape for this kind of computed geometry.Layout: pages are centred while they fit, and scroll both ways once a zoomed page is wider than the viewer.
Overlays:
renderPageOverlay(index)draws over each page, at the page's size. The highlight layer in UN-4504 uses it.Can this PR break any existing features. If yes, please list possible items. If no, please explain why. (PS: Admins do not merge the PR without this section filled)
src/v1/only, and nothing imports them yet. There are no package, config or v0 changes.Database Migrations
Env Config
Relevant Docs
Related Issues or PRs
Dependencies Versions
Notes on Testing
bun run typecheck:v1,lint:v1andtest:v1pass: 79 unit tests and 56 boundary fixtures.ui:w-(--page-width)/ui:h-(--page-height)utilities are generated?urlimportevalin pdf.js. That is the code pathisEvalSupported: falseturns off.Screenshots
Checklist
I have read and understood the Contribution Guidelines.
Generated by Claude Code