Roots and parts
Root components create a state class and put it in context; the parts below read it. Nest them in this order:
Find, Annotations and Paper roots render no element; the order between them does not matter as long as they wrap the pages and the floating parts that use them.
Every part, the same contract
class,styleand any HTML attribute are merged with the part’s own; event handlers are chained, not replaced.ref(bindable) gives you the DOM element.childrenders your own element instead:{#snippet child({ props })}<MyButton {...props} />{/snippet}— spreadprops, which carry ARIA attributes, handlers and the internal attachment.childrenreceives the part’s snippet props (for exampleViewer.PagepassespageNumber,width,height,isVisible…).- Data attributes describe state for styling:
data-current,data-active,data-state="open",data-selected… See Data attributes & CSS variables.
Controlled and uncontrolled state
Every piece of view state works both ways:
With the state classes, pass a getter to control a value: new ViewerState({ document, zoom: () => z, onZoomChange: (v) => (z = v) }).
Coordinates
Everything stored — focus rects, annotations, citations, figures — is in PDF user space: points (1/72 inch), origin at the bottom-left, unrotated, pages numbered from 1. Zoom and rotation are view concerns, so stored geometry never changes. Overlays use a scale-1 pdf.js viewport as their SVG viewBox, so they follow zoom for free.
The text index
Each page’s text is indexed once (document.getPageText(n)): the raw text with item offsets, a search-normalised form (case, accents, ligatures and line-break hyphens folded) and quadsFor(start, end) to turn any character range into PDF quads. Find, selection, copy, annotations and the paper analysis all share it.
The render pipeline
Pages are laid out from their sizes (all page shells exist, so scroll height is exact), and only pages near the view are drawn. A priority scheduler renders visible pages first and cancels work that scrolls away. Zoom changes one CSS variable (--pdf-scale) per frame; the old bitmap stays, stretched, until the sharp one lands. See Performance.