Faster, smoother PDF splitting for large documents in Mail (QA)
Executive summary
This QA release promotes a single feature from development: a performance overhaul of the PDF split tool in the Mail app. Splitting and previewing large PDFs no longer freezes the browser, and memory use during the process is dramatically reduced, making the feature usable on documents with hundreds of pages.
Why this was needed
The previous PDF splitter rendered every page thumbnail up front and ran the actual split work on the browser's main thread. For large documents this drove memory usage above 700MB and locked up the user interface while pages were processed, leading to sluggish scrolling and apparent hangs when working with big files.
Client / user impact
Users splitting large PDFs in Mail get a noticeably smoother experience: the page-thumbnail grid scrolls without stalling, thumbnails load as they come into view, and the browser stays responsive while a split runs. Internal testing targets reduce peak memory from 700MB+ to under 200MB for large PDFs. Small PDFs (20 pages or fewer) keep the simple, immediate grid with no added overhead.
Technical scope
Scoped entirely to the Mail app's PDF splitter (apps/mail), promoted via feature PR #760:
- Virtualized thumbnail grid:
PdfThumbnailGridnow usesreact-virtuoso'sVirtuosoGridto render only visible thumbnails once page count exceeds 20; smaller PDFs keep a plain static grid. - Lazy thumbnails: new
LazyThumbnailcomponent defers pdf.js page rendering until a thumbnail nears the viewport viaIntersectionObserver(400px root margin). - Scroll-seek skeletons: lightweight skeleton placeholders shown during fast scrolling (enter/exit velocity thresholds), replacing the old concurrent-render queue.
- Web Worker for splitting: new
pdfSplit.worker.tsruns pdf-lib split, page-extract, and page-count off the main thread using transferable ArrayBuffers, with progress messages. - Worker hook: new
usePdfSplitWorkermanages worker lifecycle and a request/response map;usePdfSplitteruses it when ready and falls back to the existing synchronous functions otherwise. - New tuning constants added (overscan, viewport padding, scroll-seek velocities, lazy root margin).
Risk & mitigation
Moderate, isolated to the Mail PDF splitter. New dependencies on react-virtuoso, Web Workers, and IntersectionObserver introduce browser/bundler-specific behavior (the worker is loaded via new URL(..., import.meta.url) for Next.js). Mitigations already in code: when the worker is not ready the hook transparently falls back to the existing synchronous split path, and lazy/virtualized rendering only activates above 20 pages so small documents are unaffected. Encrypted/password-protected PDFs are detected and surfaced as errors.
QA validation focus
- Split small (<20 page) and large (100+ page) PDFs; confirm output files and page ranges are correct in both grid modes.
- Verify the UI stays responsive during a large split (no freeze) and that thumbnails lazy-load while scrolling, with skeletons appearing during fast scroll.
- Confirm page-count detection, custom ranges, individual-page, and page-count split modes all still work.
- Test click and shift+click range selection of pages.
- Try an encrypted/password-protected PDF and confirm a clear error rather than a crash.
- Sanity-check the worker fallback path and watch the console for worker errors.