fix(pdf-server): render page before O(numPages) annotation scans#581
Merged
fix(pdf-server): render page before O(numPages) annotation scans#581
Conversation
PR #506 added loadBaselineAnnotations() and buildFieldNameMap() between showViewer() and renderPage(). Both iterate every page calling getPage()+getAnnotations(), so the viewer sat with an unsized empty canvas for ~400ms (15-page doc, scales linearly) before painting. Neither scan blocks the canvas — page.render() only needs canvasContext + viewport. renderAnnotationsForPage() reads annotationMap (graceful empty), AnnotationLayer.render() takes cachedFieldObjects (graceful null). So: render first, scan after, re-render to overlay annotations + form values. Same fix at the reload path.
@modelcontextprotocol/ext-apps
@modelcontextprotocol/server-basic-preact
@modelcontextprotocol/server-basic-react
@modelcontextprotocol/server-basic-solid
@modelcontextprotocol/server-basic-svelte
@modelcontextprotocol/server-basic-vanillajs
@modelcontextprotocol/server-basic-vue
@modelcontextprotocol/server-budget-allocator
@modelcontextprotocol/server-cohort-heatmap
@modelcontextprotocol/server-customer-segmentation
@modelcontextprotocol/server-debug
@modelcontextprotocol/server-map
@modelcontextprotocol/server-pdf
@modelcontextprotocol/server-scenario-modeler
@modelcontextprotocol/server-shadertoy
@modelcontextprotocol/server-sheet-music
@modelcontextprotocol/server-system-monitor
@modelcontextprotocol/server-threejs
@modelcontextprotocol/server-transcript
@modelcontextprotocol/server-video-resource
@modelcontextprotocol/server-wiki-explorer
commit: |
ochafik
added a commit
that referenced
this pull request
Apr 1, 2026
Changes since 1.3.2: - feat: add addEventListener/removeEventListener with DOM-model on* semantics (#573) - feat(pdf-server): add save_as interact action (#580) - fix(pdf-server): viewer liveness, 1:1 batch results, fullscreen jitter (#579) - fix(pdf-server): render page before O(numPages) annotation scans (#581) - fix(pdf-server): radio + dropdown in fill_form/save (#577) - fix(deps): bump path-to-regexp 8.3.0 → 8.4.1 to patch ReDoS CVEs (#576)
ochafik
added a commit
that referenced
this pull request
Apr 2, 2026
Changes since 1.3.2: - feat: add addEventListener/removeEventListener with DOM-model on* semantics (#573) - feat(pdf-server): add save_as interact action (#580) - feat(pdf-server): fit-to-page on fullscreen + pinch-to-zoom (#583) - fix(pdf-server): viewer liveness, 1:1 batch results, fullscreen jitter (#579) - fix(pdf-server): render page before O(numPages) annotation scans (#581) - fix(pdf-server): radio + dropdown in fill_form/save (#577) - fix(deps): bump path-to-regexp 8.3.0 → 8.4.1 to patch ReDoS CVEs (#576)
ochafik
added a commit
that referenced
this pull request
Apr 2, 2026
Changes since 1.3.2: - feat: add addEventListener/removeEventListener with DOM-model on* semantics (#573) - feat(pdf-server): add save_as interact action (#580) - feat(pdf-server): fit-to-page on fullscreen + pinch-to-zoom (#583) - fix(pdf-server): npx DOMMatrix crash + broken MCPB bundle (#584) - fix(pdf-server): viewer liveness, 1:1 batch results, fullscreen jitter (#579) - fix(pdf-server): render page before O(numPages) annotation scans (#581) - fix(pdf-server): radio + dropdown in fill_form/save (#577) - fix(deps): bump path-to-regexp 8.3.0 → 8.4.1 to patch ReDoS CVEs (#576)
ochafik
added a commit
that referenced
this pull request
Apr 2, 2026
Changes since 1.3.2: - feat: add addEventListener/removeEventListener with DOM-model on* semantics (#573) - feat(pdf-server): add save_as interact action (#580) - feat(pdf-server): fit-to-page on fullscreen + pinch-to-zoom (#583) - fix(pdf-server): npx DOMMatrix crash + broken MCPB bundle (#584) - fix(pdf-server): viewer liveness, 1:1 batch results, fullscreen jitter (#579) - fix(pdf-server): render page before O(numPages) annotation scans (#581) - fix(pdf-server): radio + dropdown in fill_form/save (#577) - fix(deps): bump path-to-regexp 8.3.0 → 8.4.1 to patch ReDoS CVEs (#576) - chore(deps): npm audit fix — sdk 1.29.0, systeminformation 5.31.5, +13 transitives (#585, #586)
ochafik
pushed a commit
that referenced
this pull request
Apr 2, 2026
Changes since 1.3.2: SDK - feat: add addEventListener/removeEventListener with DOM-model on* semantics (#573) pdf-server - feat: add save_as interact action (#580) - feat: fit-to-page on fullscreen + pinch-to-zoom (#583) - fix: npx DOMMatrix crash + broken MCPB bundle (#584) - fix: viewer liveness, 1:1 batch results, fullscreen jitter (#579) - fix: render page before O(numPages) annotation scans (#581) - fix: radio + dropdown in fill_form/save (#577) Dependencies - chore: npm audit fix — sdk 1.29.0, systeminformation 5.31.5, +13 transitives (#585, #586) - fix: bump path-to-regexp 8.3.0 → 8.4.1 to patch ReDoS CVEs (#576)
ochafik
added a commit
that referenced
this pull request
Apr 2, 2026
Changes since 1.3.2: SDK - feat: add addEventListener/removeEventListener with DOM-model on* semantics (#573) pdf-server - feat: add save_as interact action (#580) - feat: fit-to-page on fullscreen + pinch-to-zoom (#583) - fix: npx DOMMatrix crash + broken MCPB bundle (#584) - fix: viewer liveness, 1:1 batch results, fullscreen jitter (#579) - fix: render page before O(numPages) annotation scans (#581) - fix: radio + dropdown in fill_form/save (#577) Dependencies - chore: npm audit fix — sdk 1.29.0, systeminformation 5.31.5, +13 transitives (#585, #586) - fix: bump path-to-regexp 8.3.0 → 8.4.1 to patch ReDoS CVEs (#576)
ochafik
added a commit
that referenced
this pull request
Apr 2, 2026
* chore: bump ext-apps to 1.4.0 Changes since 1.3.2: SDK - feat: add addEventListener/removeEventListener with DOM-model on* semantics (#573) pdf-server - feat: add save_as interact action (#580) - feat: fit-to-page on fullscreen + pinch-to-zoom (#583) - fix: npx DOMMatrix crash + broken MCPB bundle (#584) - fix: viewer liveness, 1:1 batch results, fullscreen jitter (#579) - fix: render page before O(numPages) annotation scans (#581) - fix: radio + dropdown in fill_form/save (#577) Dependencies - chore: npm audit fix — sdk 1.29.0, systeminformation 5.31.5, +13 transitives (#585, #586) - fix: bump path-to-regexp 8.3.0 → 8.4.1 to patch ReDoS CVEs (#576) * chore: update e2e snapshots [skip ci] --------- Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.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.
Problem
After the loading spinner clears, the viewer briefly shows an empty document — toolbar visible, page counter reads "1 of 1", canvas is a tiny blank white rectangle at default size — before the real page paints. ~400ms with a 15-page doc, scales linearly with page count.
Root cause
#506 added
loadBaselineAnnotations()andbuildFieldNameMap()betweenshowViewer()andrenderPage(). Both iterate every page callinggetPage()+getAnnotations(). The canvas isn't sized untilrenderPage()runs, so the viewer sits empty for the duration of those scans.Same pattern at the reload path.
Fix
Render first, scan after. Neither scan blocks the canvas —
page.render()only needscanvasContext+viewport.renderAnnotationsForPage()readsannotationMap(graceful empty),AnnotationLayer.render()takescachedFieldObjects(graceful null). A secondrenderPage()after the scans overlays baseline annotations and restored form values; for PDFs with neither, the canvas content is identical so there's no flicker.Restores pre-#506 timing: page visible <50ms after
showViewer().Before/after (1706.03762, 15 pages)