All notable changes to the PDF Viewer PCF Control will be documented in this file.
- Fixed blank pages during rapid scrolling (complete fix) - Pages now re-render when re-entering virtual window
- Root cause: When pages exit/re-enter virtual window, React creates NEW canvas elements
- But
renderedPagesstill said 'done', so no re-render was triggered on the new blank canvas - Fix: Clear
renderedPagesentries for pages that exit the virtual window - When pages re-enter, they properly re-render on their new canvas element
- Partial fix for blank pages during rapid scrolling - Only clear canvas for fresh renders
- Root cause:
ctx.clearRect()was called before async render started, causing blank canvas if page exited virtual window mid-render - Fix: Only clear canvas for fresh renders (pages never rendered before), skip for re-renders
- PDF.js overwrites canvas content during render anyway, so clearing is only needed for initial placeholder removal
- Root cause:
-
Fixed sidebar/page mismatch - Sidebar thumbnail now correctly matches current page in main viewer
- Added
isNavigatingRefflag to prevent IntersectionObserver from overriding programmatic navigation - IntersectionObserver skips page updates during navigation operations
- Added
-
Fixed thumbnail click navigation - Clicking sidebar thumbnail now scrolls to correct page on first try
- Rewrote
goToPage()to use pending scroll pattern that waits for virtual window re-render - Added
useEffectthat scrolls AFTER state update ensures target page is in DOM - Uses
requestAnimationFrameto guarantee DOM has updated before scrolling - Changed scroll behavior to 'instant' for programmatic navigation (eliminates race conditions)
- Rewrote
-
Fixed text/canvas disappearing - Page content no longer flickers during re-rendering
- Added
everRenderedRefto track pages that have been rendered at least once - Canvas stays visible even during re-rendering if it was previously rendered
- Placeholder only shows for pages that have never been rendered
- Added
-
Fixed page number field navigation - Arrow keys and page input now work correctly on first try
- Expanded virtual window buffer - Increased from 3/5 pages to 5/8 pages for smoother navigation
- Increased virtual scroll threshold - Changed from 20 to 30 pages before enabling virtual scrolling
- Documents with <=30 pages now render all pages (no virtual scrolling needed)
- New refs:
isNavigatingRef,pendingScrollRef,everRenderedRef - Navigation flag prevents IntersectionObserver debounce from overriding programmatic navigation
- Pending scroll pattern ensures DOM is ready before attempting scroll
- New CONFIG constant:
VIRTUAL_SCROLL_THRESHOLD
-
Virtual scrolling for pages - Only renders pages near the current view
- Reduces DOM nodes from 100+ to ~9 for large documents
- Auto-activates for documents with >20 pages
- Configurable buffer: 3 pages before, 5 pages after current page
- Spacer divs maintain scroll height for smooth navigation
-
Thumbnail virtualization - Only renders visible thumbnails in sidebar
- Auto-activates for documents with >30 pages
- Scroll-based range tracking with requestAnimationFrame throttling
- Buffer of 5 thumbnails before and 10 after visible range
-
Parallel Dataverse requests - Column discovery now runs in parallel
- File and Image attribute queries execute simultaneously via Promise.all()
- Reduces initialization time by 100-200ms
-
Memoized search match filtering - O(1) lookup instead of O(n) filtering
- Pre-computed
matchesByPageMap using useMemo - Pre-computed
currentMatchfor instant comparison - Significant improvement for documents with many search matches
- Pre-computed
- New CONFIG constants:
VIRTUAL_SCROLL_BUFFER_BEFORE,VIRTUAL_SCROLL_BUFFER_AFTER - Virtual scrolling uses spacer divs to maintain correct scroll height
- Thumbnail virtualization tracks visible range via scroll events
- Search matches grouped by page number for O(1) page lookup
- IntersectionObserver for page visibility - Replaced manual scroll position calculations with native browser API
- Eliminates 60+ DOM queries per scroll event
- More accurate current page detection using intersection ratios
- Automatic pre-loading via rootMargin buffer
- requestIdleCallback for thumbnail rendering - Non-blocking thumbnail generation
- Thumbnails near current page render immediately (high priority)
- Remaining thumbnails render during browser idle time
- Falls back to setTimeout for Safari and older browsers
- Proper cleanup of idle callbacks on unmount
- Multiple intersection thresholds (0, 0.1, 0.25, 0.5, 0.75, 1.0) for smooth current page tracking
- Thumbnail rendering split into priority batches for better perceived performance
- Added AbortController to all DataverseService fetch calls for proper request cancellation on unmount
- Added render task timeout cleanup - stale tasks auto-cancel after 10 seconds
- Periodic cleanup interval checks for stuck render tasks every 5 seconds
- Fixed potential memory leak: Dataverse fetch requests now properly abort on component unmount
- Fixed potential stuck render tasks that could accumulate during rapid scrolling
- Added React.memo to ThumbnailPanel and OutlinePanel components (~20-30% render reduction)
- Wrapped all handler functions with useCallback to prevent cascading re-renders
- Added useMemo for columnsWithFiles computed value
- Removed redundant CSS will-change property
- Replaced magic numbers with CONFIG constants for better maintainability
- Fixed memory leak: Blob URLs are now revoked when imageUrl changes or component unmounts
- Fixed race condition: Document load now uses loadIdRef to prevent stale callbacks
- Fixed PDF.js version mismatch: Updated pdfjs-dist to 4.10.38 to match CDN worker
- Removed unused singleton exports from PdfService and DataverseService
- Removed console.error calls from production code
- Added CONFIG constants for thumbnail rendering delays and batch sizes
- Fixed rendering glitches where text from different pages would overlap/merge
- Added canvas clearing before each render to prevent ghosting
- Added rendering lock (ref-based) to prevent race conditions during concurrent renders
- Added scroll throttling to prevent excessive render calls during fast scrolling
- Proper cleanup of rendering state on component unmount or scale/rotation changes
- Page input no longer glitches when typing - uses local state until blur/Enter
- Page input now limits digit length to prevent excessively long numbers
- Input selects all text on focus for easy replacement
- Escape key cancels page input and resets to current page
- PDF now centers properly when sidebar is closed
- Improved sidebar collapse animation with smooth transitions
- Thumbnail images no longer appear stretched (added proper aspect ratio handling)
- File switching now works correctly - selecting a different file from dropdown loads the new document
- Fixed "Cannot use the same canvas during multiple render() operations" error by using React key props to force canvas remount
- Proper state reset before loading new documents (clears rendered pages, text content, viewports, etc.)
- Fixed pdfService.destroy() to be properly awaited
- Added
documentKeystate to force PdfCanvas and ThumbnailPanel remount on document switch - Added CSS
object-fit: containandmax-heightfor thumbnail images - Improved cleanup flow in loadDocument function
- Page caching in PdfService for improved performance
- Parallelized page viewport fetching (10 pages at a time)
- GPU acceleration hints for smoother scrolling
- Various performance optimizations
- Improved error handling
- Initial release of PDF Viewer PCF Control
- PDF rendering using PDF.js v4.10.38
- Image file support (JPG, PNG, GIF, BMP, WebP, SVG, TIFF)
- Zoom controls (25%-500%, fit page, fit width, auto)
- Page navigation with scroll tracking
- Text search with highlights (Ctrl+F)
- Thumbnails sidebar panel
- Outline/bookmarks navigation
- Document rotation (left/right)
- Dark mode toggle
- Fullscreen mode
- Print functionality
- Download functionality
- Keyboard shortcuts
- Auto-discovery of Dataverse file columns
- Default file column configuration
- Responsive design
- Test mode for development (URL input, file upload)
- Built with React and TypeScript
- PCF Virtual Control (React-based)
- Bundle size: ~524 KB (minified)
- PDF.js worker loaded from CDN
- Annotation support (highlights, comments)
- Form field support
- Drag-to-pan navigation
- Pinch-to-zoom on touch devices
- Multi-document tabs
- Recent documents list
- Document comparison view
- OCR for scanned PDFs (via Azure AI Document Intelligence)
- Digital signature verification
- Watermark overlay
- Custom stamp annotations