@embedpdf/web 3.0.0-next.13 → 3.0.0-next.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +3697 -381
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2812 -137
- package/dist/index.d.ts +2812 -137
- package/dist/index.js +3570 -382
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["w","firstFamily"],"sources":["../src/file-picker.ts","../src/clipboard.ts","../src/anchored-position.ts","../src/cursor.ts","../src/external-uri.ts","../src/actions-ui.ts","../src/painted-image.ts","../src/feedback.ts","../src/wheel.ts","../src/stage-gestures.ts","../src/selection-handles.ts","../src/stage-surface.ts","../src/byte-store.ts","../src/web-font.ts","../src/web-font-metrics.ts","../src/rich-text-editor.ts"],"sourcesContent":["/**\n * A one-shot file dialog. Opens a throwaway `<input type=file>`, resolves the\n * chosen `File`, or `null` when the dialog is dismissed. MUST be called\n * synchronously from a user gesture (a click) or the browser refuses to open it.\n *\n * A `File` is a `Blob`, which is a `BinarySource` everywhere in the stack, so the\n * caller can hand the result straight to the engine — this module stays a pure\n * DOM utility with no EmbedPDF types.\n */\nconst IMAGE_ACCEPT = 'image/png,image/jpeg,application/pdf';\n\nexport interface PickFileOptions {\n /** The dialog's `accept` filter. Defaults to PNG / JPEG / PDF. */\n accept?: string;\n /** Allow multiple selection (returns the first file). Defaults to false. */\n multiple?: boolean;\n}\n\n/** Open the file dialog; resolve the picked file, or null if cancelled. */\nexport function pickImageFile(options: PickFileOptions = {}): Promise<File | null> {\n const { accept = IMAGE_ACCEPT, multiple = false } = options;\n return new Promise((resolve) => {\n const input = document.createElement('input');\n input.type = 'file';\n input.accept = accept;\n input.multiple = multiple;\n input.style.display = 'none';\n\n let settled = false;\n const finish = (value: File | null): void => {\n if (settled) return;\n settled = true;\n window.removeEventListener('focus', onFocus);\n input.remove();\n resolve(value);\n };\n\n // A window refocus means the dialog closed. Give a queued `change` a tick to\n // win, then treat a bare refocus as a cancel — the fallback for browsers that\n // predate the `cancel` event.\n const onFocus = (): void => {\n setTimeout(() => finish(null), 300);\n };\n\n input.addEventListener('change', () => finish(input.files?.[0] ?? null), { once: true });\n input.addEventListener('cancel', () => finish(null), { once: true });\n window.addEventListener('focus', onFocus, { once: true });\n\n document.body.appendChild(input);\n input.click();\n });\n}\n\n/** The same dialog with NO type filter — any file (attachments). */\nexport const pickFile = (options: PickFileOptions = {}): Promise<File | null> =>\n pickImageFile({ accept: '*/*', ...options });\n\n/**\n * Hand bytes to the user as a browser download (a one-shot `<a download>`).\n * The DOM half of \"download this attachment\" — pair with the annotation\n * capability's `downloadAttachment` or the document `attachments.download`.\n */\nexport function saveFile(bytes: Uint8Array | Blob, name: string, mimeType?: string): void {\n const blob =\n bytes instanceof Blob\n ? bytes\n : new Blob([bytes as BlobPart], { type: mimeType ?? 'application/octet-stream' });\n const url = URL.createObjectURL(blob);\n const a = document.createElement('a');\n a.href = url;\n a.download = name;\n a.style.display = 'none';\n document.body.appendChild(a);\n a.click();\n a.remove();\n setTimeout(() => URL.revokeObjectURL(url), 1000);\n}\n","/**\n * Clipboard wiring for text selection — the environmental half of copy.\n *\n * The selection plugin is deliberately DOM-free: its public capability ends\n * at `readText()` (data). Everything that touches `navigator.clipboard` or\n * the native `copy` event lives HERE, structurally typed so this module\n * stays a pure DOM utility with no EmbedPDF dependencies (the selection\n * capability satisfies {@link ClipboardSelectionSource} as-is).\n */\n\n/** The slice of the selection capability clipboard wiring needs. */\nexport interface ClipboardSelectionSource {\n hasSelection(): boolean;\n canCopy(): boolean;\n readText(): Promise<string>;\n onChange(cb: () => void): () => void;\n onCommit(cb: () => void): () => void;\n}\n\nexport interface SelectionClipboardOptions {\n /**\n * Prefetch the selected text when the selection settles (default true).\n * This is what makes the NATIVE copy path work: a `copy` event handler\n * must call `clipboardData.setData` synchronously — awaiting a page-text\n * read inside it is too late — and it also keeps the async path instant.\n * The fetch is one versioned, cached, permission-gated read per page;\n * pass false to make copying strictly on-demand.\n */\n prefetch?: boolean;\n /** Event target to listen on. Defaults to `document`. */\n target?: Document;\n}\n\nconst PREFETCH_DEBOUNCE_MS = 150;\n\n/**\n * Wire clipboard copy for a selection. Two delivery paths, both fed by the\n * prefetched text:\n *\n * - the native `copy` event (menu Edit→Copy, or ctrl/cmd+C while the page\n * has a DOM selection or focused editable — e.g. the viewer shell's\n * focus sink): answered SYNCHRONOUSLY from the cache;\n * - ctrl/cmd+C with no DOM selection (canvas-rendered viewers usually\n * have none, and browsers don't dispatch `copy` then): a keydown\n * fallback writes via the async Clipboard API inside the keystroke's\n * user-activation window.\n *\n * Returns the unwire function. Mount once per document view (not per page).\n */\nexport function wireSelectionClipboard(\n selection: ClipboardSelectionSource,\n options: SelectionClipboardOptions = {},\n): () => void {\n const target = options.target ?? document;\n const prefetch = options.prefetch ?? true;\n let cached: string | null = null;\n let generation = 0;\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const refresh = (): void => {\n cached = null;\n if (!prefetch || !selection.hasSelection() || !selection.canCopy()) return;\n const gen = ++generation;\n selection.readText().then(\n (text) => {\n if (gen === generation) cached = text;\n },\n () => {}, // denied / failed — the async path will surface it if used\n );\n };\n\n // Commit (gesture end) refreshes immediately; change (drag, programmatic\n // select) debounces so a drag doesn't fetch per pointer move.\n const offCommit = selection.onCommit(() => {\n if (timer) clearTimeout(timer);\n timer = null;\n refresh();\n });\n const offChange = selection.onChange(() => {\n generation++;\n cached = null;\n if (timer) clearTimeout(timer);\n timer = setTimeout(refresh, PREFETCH_DEBOUNCE_MS);\n });\n\n const onCopy = (e: ClipboardEvent): void => {\n if (cached == null || cached === '' || !selection.hasSelection()) return;\n e.clipboardData?.setData('text/plain', cached);\n e.preventDefault();\n };\n\n const onKeyDown = (e: KeyboardEvent): void => {\n if (!(e.metaKey || e.ctrlKey) || e.key.toLowerCase() !== 'c' || e.shiftKey || e.altKey) {\n return;\n }\n if (!selection.hasSelection() || !selection.canCopy()) return;\n // A live DOM selection means the browser will dispatch `copy` itself and\n // our synchronous handler answers — don't double-write.\n const dom = target.getSelection?.();\n if (dom && !dom.isCollapsed) return;\n void copySelection(selection); // instant when prefetched; still inside user activation\n };\n\n target.addEventListener('copy', onCopy);\n target.addEventListener('keydown', onKeyDown);\n return () => {\n offCommit();\n offChange();\n if (timer) clearTimeout(timer);\n target.removeEventListener('copy', onCopy);\n target.removeEventListener('keydown', onKeyDown);\n };\n}\n\n/**\n * Toolbar/menu path: read the selected text and write it to the clipboard\n * via the async Clipboard API. Call from a user-gesture handler (browsers\n * require a secure context + user activation for clipboard writes). Resolves\n * with what was copied ('' when nothing was — nothing is written then, so an\n * empty selection never clobbers the user's clipboard).\n */\nexport async function copySelection(selection: ClipboardSelectionSource): Promise<string> {\n const text = await selection.readText();\n if (text !== '') await navigator.clipboard.writeText(text);\n return text;\n}\n","/**\n * Anchored-overlay geometry for UI floating over page content (selection\n * menus, draft menus, popovers): the projector SNAPSHOT contract, the\n * anchor shape, and the placement math. Pure values and pure functions over\n * structural rect/point shapes — no EmbedPDF types, no framework, no\n * reactive lifecycle — so every framework adapter (React, Vue, Svelte,\n * Angular) shares ONE implementation and binds it with its NATIVE\n * reactivity (context revision, signals, computed, $derived).\n *\n * The boundary laws:\n * - Placement policy lives HERE, above the projector seam: a projector\n * only knows geometry — never what is being positioned nor where it\n * prefers to sit.\n * - A projector describes the CURRENT projection; it never implements a\n * framework-style reactive lifecycle. State-driven changes (the Stage\n * camera) must reach consumers through the framework's own render\n * cycle so surface and overlay commit TOGETHER; only genuinely\n * browser-driven changes (a PageView moving because the document\n * scrolled) use an external listener ({@link observeClientGeometry}).\n */\n\nexport interface AnchoredRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface AnchoredPoint {\n x: number;\n y: number;\n}\n\nexport type AnchoredPlacement = 'top' | 'right' | 'bottom' | 'left';\n\n/** CSS-ready output: absolute/fixed `left`/`top` plus a centering transform. */\nexport interface AnchoredPosition {\n left: number;\n top: number;\n transform: string;\n}\n\n/**\n * Place an upright element around `box` (screen px). `avoid` is a screen\n * point the element must clear (e.g. the rotate knob): the element extends\n * ONLY the edge it sits on, and ONLY when the point protrudes past that\n * edge — so it clears the obstacle without ever shifting off-centre on the\n * other axis. When the point is on another side (e.g. a 90° shape, knob at\n * mid-height for a `top` placement) the edge is untouched and the element\n * stays centred on `box`.\n */\n/** What anchored UI attaches to: a content-space rect on a page, plus\n * optional content-space points the UI must clear (e.g. a rotate knob).\n * Structural — plugin anchor reads satisfy it without importing this\n * package. */\nexport interface AnchorTarget {\n pon: number;\n bounds: AnchoredRect;\n avoid?: AnchoredPoint[];\n}\n\n/**\n * A page surface's PROJECTION SNAPSHOT: how a content-space rect on a page\n * becomes screen coordinates, right now. Provided by `<Stage>`\n * (camera-driven, pure state, no DOM reads) and `<PageView>` (DOM-measured).\n * Deliberately NO subscribe here — when projection changes is a framework\n * binding concern (see the module doc), not part of the snapshot.\n */\nexport interface ViewProjector {\n /**\n * Which coordinate space `toScreen` speaks, and therefore how anchored\n * content mounts:\n * - 'overlay': coords are relative to the surface's own positioned\n * container → rendered in place, position:absolute. (Stage)\n * - 'client': coords are viewport px → portalled to the document body,\n * position:fixed, so no ancestor overflow can clip. (PageView)\n */\n space: 'overlay' | 'client';\n /** Content-space rect on a page → coords in `space`. Null: not projectable\n * right now (page not shown / not measurable yet). */\n toScreen(pon: number, rect: AnchoredRect): AnchoredRect | null;\n toScreenPoint(pon: number, at: AnchoredPoint): AnchoredPoint | null;\n /** The page's live view facts (for anchor reads that need them — e.g. the\n * screen-constant rotate-knob stalk), or null when the page isn't shown. */\n viewEnv(pon: number): {\n scale: number;\n rotation: 0 | 90 | 180 | 270;\n zoom: number;\n } | null;\n}\n\n/**\n * THE anchored projection: anchor → screen position, in one pure call —\n * projection, avoid-point transform, and the shared placement policy.\n * Frameworks recompute this inside their native reactive primitive\n * (a React render, a Vue `computed`, an Angular signal, a Svelte\n * `$derived`); nothing here schedules anything.\n */\nexport function projectAnchoredTarget(\n projector: ViewProjector,\n anchor: AnchorTarget,\n placement: AnchoredPlacement,\n gap: number,\n): AnchoredPosition | null {\n const box = projector.toScreen(anchor.pon, anchor.bounds);\n if (!box) return null;\n const avoid = anchor.avoid?.length\n ? projector.toScreenPoint(anchor.pon, anchor.avoid[0])\n : null;\n return positionAnchoredRect(box, placement, gap, avoid);\n}\n\n/**\n * The one genuinely BROWSER-driven invalidation: an element in normal\n * document flow moves when the document scrolls or the window resizes —\n * no state change announces it. `<PageView>`'s binding registers this;\n * the Stage never does (its camera is state, delivered through render).\n */\nexport function observeClientGeometry(callback: () => void): () => void {\n window.addEventListener('scroll', callback, true);\n window.addEventListener('resize', callback);\n return () => {\n window.removeEventListener('scroll', callback, true);\n window.removeEventListener('resize', callback);\n };\n}\n\nexport function positionAnchoredRect(\n box: AnchoredRect,\n placement: AnchoredPlacement,\n gap: number,\n avoid?: AnchoredPoint | null,\n): AnchoredPosition {\n const cx = box.x + box.width / 2;\n const cy = box.y + box.height / 2;\n\n switch (placement) {\n case 'bottom': {\n const edge = Math.max(box.y + box.height, avoid ? avoid.y : -Infinity);\n return { left: cx, top: edge + gap, transform: 'translate(-50%, 0)' };\n }\n case 'left': {\n const edge = Math.min(box.x, avoid ? avoid.x : Infinity);\n return { left: edge - gap, top: cy, transform: 'translate(-100%, -50%)' };\n }\n case 'right': {\n const edge = Math.max(box.x + box.width, avoid ? avoid.x : -Infinity);\n return { left: edge + gap, top: cy, transform: 'translate(0, -50%)' };\n }\n case 'top':\n default: {\n const edge = Math.min(box.y, avoid ? avoid.y : Infinity);\n return { left: cx, top: edge - gap, transform: 'translate(-50%, -100%)' };\n }\n }\n}\n","/**\n * SVG → CSS cursor string. The armed-tool indicator is the CURSOR itself (the\n * only zero-latency pointer-locked pixel the web has — the OS composites it),\n * so an app hands its toolbar icon here and assigns the result via the\n * interaction hub's `setToolCursor`.\n *\n * Browser rules baked into this module:\n * - The SVG must carry explicit `width`/`height` attributes (not just a\n * viewBox) or Chromium/WebKit ignore it — enforced here.\n * - The hard ceiling is 128×128 CSS px (bigger falls back to the keyword).\n * Above 32×32, Chromium temporarily resets the cursor to default while it\n * would overlap browser UI (anti-spoofing) — fine mid-viewport, so ~40px\n * compositions are common; stay ≤128.\n * - The keyword fallback covers browsers that refuse SVG cursor images.\n *\n * Pure string work with no EmbedPDF types — like the file picker, this module\n * is a plain web utility.\n */\n\nexport interface SvgCursorOptions {\n /** Complete `<svg>` markup, colors baked in (no `currentColor` — a cursor\n * image has no cascade to inherit from). */\n svg: string;\n /** The click point inside the image, CSS px from the top-left. Default 0,0\n * (arrow-style: the image's corner is the tip). */\n hotspot?: { x: number; y: number };\n /** Keyword cursor when the image can't render. Default `'crosshair'`. */\n fallback?: string;\n}\n\n/** Build the CSS cursor value: `url(\"data:image/svg+xml,…\") x y, fallback`. */\nexport function svgCursor({ svg, hotspot, fallback = 'crosshair' }: SvgCursorOptions): string {\n // Scan only the opening `<svg …>` tag (bounded slice) rather than running\n // `/<svg[^>]*\\swidth\\s*=/` over the whole string — the unbounded form is\n // quadratic on pathological inputs (many `<svg` fragments with no `>`),\n // while accepting exactly the same markup: `[^>]*` could never cross a\n // `>` either.\n const tagStart = svg.indexOf('<svg');\n const tagEnd = tagStart >= 0 ? svg.indexOf('>', tagStart) : -1;\n const openTag = tagEnd >= 0 ? svg.slice(tagStart, tagEnd + 1) : '';\n if (!/\\swidth\\s*=/.test(openTag) || !/\\sheight\\s*=/.test(openTag)) {\n throw new Error(\n '[web] svgCursor: the <svg> needs explicit width/height attributes — ' +\n 'browsers ignore cursor images without an intrinsic size',\n );\n }\n const uri = `data:image/svg+xml,${encodeURIComponent(svg)}`;\n const spot = hotspot ? ` ${Math.round(hotspot.x)} ${Math.round(hotspot.y)}` : '';\n return `url(\"${uri}\")${spot}, ${fallback}`;\n}\n","/**\n * External-URI hygiene for PDF link annotations — written ONCE here so every\n * framework adapter (react, vue, svelte, angular) shares the same policy.\n *\n * PDFs are untrusted input: a link's `/URI` can carry anything, including\n * `javascript:` payloads. The default allowlist covers what a document link\n * legitimately opens; everything else (javascript:, file:, data:, vbscript:,\n * custom app schemes) resolves to `null` and the adapters render the link\n * WITHOUT an href — activation still reports through the plugin, so an\n * embedder that needs a custom scheme can handle it in `onActivate`.\n */\nconst DEFAULT_ALLOWED_SCHEMES = new Set(['http:', 'https:', 'mailto:', 'tel:']);\n\n/**\n * Validate a link URI for opening in the browser. Returns the URI when its\n * scheme is allowed (scheme-relative and bare URIs resolve as https), else\n * `null`. Never throws — a malformed URI is just an unopenable one.\n */\nexport function sanitizeExternalUri(\n uri: string,\n allowedSchemes: Iterable<string> = DEFAULT_ALLOWED_SCHEMES,\n): string | null {\n const trimmed = uri.trim();\n if (!trimmed) return null;\n const allowed = new Set(\n [...allowedSchemes].map((s) => (s.endsWith(':') ? s.toLowerCase() : `${s.toLowerCase()}:`)),\n );\n try {\n // Base resolves scheme-relative (\"//host\") and bare (\"example.com/x\" is a\n // relative path → https) forms; an absolute scheme wins over the base.\n const url = new URL(trimmed, 'https://invalid.example/');\n return allowed.has(url.protocol) ? trimmed : null;\n } catch {\n return null;\n }\n}\n","/**\n * The DEFAULT actions UI adapter — the origin×phase visibility matrix plus\n * the browser fallbacks, written ONCE here so every framework binding\n * (react, angular, …) ships the SAME policy instead of forking it per\n * framework. Types are structural twins of `@embedpdf/plugin-actions`'\n * adapter contract (declared here so this package stays plugin-free, per\n * the layering law); assignability is checked where a binding installs it.\n *\n * The matrix (defaults only — an embedder override receives EVERY effect\n * with its context attached and decides for itself):\n * - URIs open through `sanitizeExternalUri` in a new tab (the dispatcher\n * already policy-gated the node; blocked schemes are dropped here).\n * - Print: hover/lifecycle-origin requests never open the dialog; user\n * requests fall back to `globalThis.print`. The `doc.print` AUTHORITY\n * gate is upstream in the plugin and not overridable.\n * - Alert: document-open nags (lifecycle origin, boot phase) never reach\n * `window.alert` — the boot-nag gap, closed.\n * - gotoPage: delegates to the binding-supplied navigator (the stage).\n */\nimport { sanitizeExternalUri } from './external-uri';\n\n/** Structural twin of plugin-actions' `ActionOrigin`. */\nexport type ActionsUiOrigin = 'user' | 'hover' | 'lifecycle';\n\n/** Structural twin of plugin-actions' `ActionUiContext`. */\nexport interface ActionsUiEffectContext {\n origin: ActionsUiOrigin;\n phase: 'boot' | 'user';\n}\n\n/** Structural twin of plugin-actions' `ActionUiAdapter`. */\nexport interface ActionsUiAdapterShape {\n openUri(uri: string, opts: { isMap: boolean; origin: ActionsUiOrigin }): void;\n print(opts?: ActionsUiEffectContext): void;\n alert?(message: string, opts: ActionsUiEffectContext & { icon: number; title?: string }): void;\n gotoPage?(page: number, opts: ActionsUiEffectContext): void;\n}\n\nexport interface DefaultActionsUiAdapterOptions {\n /**\n * Per-handler overrides. Pass a FUNCTION for late binding (a binding's\n * mutable handlers ref) — it is consulted on every effect, so a fresh\n * closure never needs a reinstall.\n */\n overrides?: Partial<ActionsUiAdapterShape> | (() => Partial<ActionsUiAdapterShape> | undefined);\n /** Navigation default for script `this.pageNum = n` requests. */\n goToPage?: (page: number) => void;\n}\n\nexport const createDefaultActionsUiAdapter = (\n options: DefaultActionsUiAdapterOptions = {},\n): ActionsUiAdapterShape => {\n const overridesOf = (): Partial<ActionsUiAdapterShape> | undefined =>\n typeof options.overrides === 'function' ? options.overrides() : options.overrides;\n return {\n openUri: (uri, opts) => {\n const current = overridesOf();\n if (current?.openUri) {\n current.openUri(uri, opts);\n return;\n }\n const href = sanitizeExternalUri(uri);\n if (href && typeof window !== 'undefined') {\n window.open(href, '_blank', 'noopener,noreferrer');\n }\n },\n print: (opts) => {\n const current = overridesOf();\n if (current?.print) current.print(opts);\n else if (opts && opts.origin !== 'user') {\n // Hover/lifecycle scripts never open the print dialog by default.\n } else if (typeof globalThis.print === 'function') globalThis.print();\n },\n alert: (message, opts) => {\n const current = overridesOf();\n if (current?.alert) current.alert(message, opts);\n else if (opts.origin === 'lifecycle' || opts.phase === 'boot') {\n // Document-open nags (Adobe version checks, lifecycle scripts)\n // never alert by default — the boot-nag gap, closed.\n } else if (typeof globalThis.alert === 'function') globalThis.alert(message);\n },\n gotoPage: (page, opts) => {\n const current = overridesOf();\n if (current?.gotoPage) current.gotoPage(page, opts);\n else options.goToPage?.(page);\n },\n };\n};\n","/** A browser image source whose object URL has an explicit lifetime. */\nexport interface ObjectUrlImageSource {\n objectUrl(signal?: AbortSignal): Promise<{ url: string; revoke(): void }>;\n}\n\nexport interface PaintedImageCallbacks {\n onPainted(): void;\n onUnpainted(): void;\n}\n\n/**\n * Bind a raster source to an <img> without ever painting its incomplete-image\n * placeholder. The source becomes visible on load; retained coverage is\n * released only after the browser has had one full frame to present it.\n *\n * Call this from the framework's pre-paint mount hook and invoke the returned\n * cleanup on unmount. Keeping this DOM lifecycle here lets every framework\n * adapter remain a ref plus one bind/unbind hook.\n */\nexport function bindPaintedImage(\n image: HTMLImageElement,\n source: ObjectUrlImageSource,\n callbacks: PaintedImageCallbacks,\n): () => void {\n const controller = new AbortController();\n let revoke: (() => void) | undefined;\n let frame = 0;\n let painted = false;\n\n // The element is already committed when a mount hook runs. Hide it before\n // that commit can paint, then reveal only once the bitmap has loaded.\n image.style.visibility = 'hidden';\n\n const onLoad = () => {\n image.style.visibility = 'visible';\n\n // rAF callbacks run before a frame paints. The first frame presents the\n // newly visible image while retained coverage remains; the second reports\n // that presentation opportunity and permits the retained source to leave.\n frame = requestAnimationFrame(() => {\n frame = requestAnimationFrame(() => {\n frame = 0;\n painted = true;\n callbacks.onPainted();\n });\n });\n };\n\n image.addEventListener('load', onLoad, { once: true });\n\n void source.objectUrl(controller.signal).then(\n (objectUrl) => {\n if (controller.signal.aborted) {\n objectUrl.revoke();\n return;\n }\n revoke = objectUrl.revoke;\n image.src = objectUrl.url;\n },\n () => {\n // Aborted or unavailable: remain hidden and retain fallback coverage.\n },\n );\n\n return () => {\n controller.abort();\n if (frame) cancelAnimationFrame(frame);\n image.removeEventListener('load', onLoad);\n image.style.visibility = 'hidden';\n image.removeAttribute('src');\n revoke?.();\n if (painted) callbacks.onUnpainted();\n };\n}\n","/**\n * Platform-feedback providers — the browser implementations of the interaction\n * hub's haptic port (structurally `PlatformFeedback` from\n * `@embedpdf/plugin-interaction`; declared structurally here so this package\n * stays plugin-free, per the layering law).\n *\n * Honesty about reach: `navigator.vibrate` works on Android browsers and is\n * NOT implemented in iOS Safari — there the default provider is a silent\n * no-op until Apple ships a haptics API. (The community switch-control hacks\n * were evaluated and deliberately NOT shipped: Apple patched programmatic\n * triggering in iOS 26.5, leaving only tap-on-control tricks that don't fit a\n * viewer's gesture moments.) The real iPhone haptic today comes through a\n * native shell: a WKWebView embedder installs a script message handler and\n * passes {@link wkFeedback} (or their own provider) instead.\n */\n\n/** Structural twin of plugin-interaction's `PlatformFeedback`. */\nexport interface WebPlatformFeedback {\n selection(): void;\n impact(weight?: 'light' | 'medium' | 'heavy'): void;\n notify(kind: 'success' | 'warning' | 'error'): void;\n}\n\nconst vibrate = (pattern: number | number[]): void => {\n if (typeof navigator !== 'undefined') navigator.vibrate?.(pattern);\n};\n\n/**\n * The default web provider: the Vibration API, mapped onto the three haptic\n * families (selection = the shortest tick; impact scales with weight; notify\n * uses patterns). A no-op wherever vibration is unsupported — notably iOS\n * Safari — so it is always safe to install.\n */\nexport const vibrationFeedback: WebPlatformFeedback = {\n selection: () => vibrate(8),\n impact: (weight = 'light') => vibrate(weight === 'heavy' ? 30 : weight === 'medium' ? 20 : 10),\n notify: (kind) =>\n vibrate(kind === 'error' ? [12, 60, 12, 60, 12] : kind === 'warning' ? [15, 70, 15] : [10, 50, 10]),\n};\n\ninterface WKMessageHandler {\n postMessage(message: unknown): void;\n}\n\n/**\n * A provider for native iOS shells: forwards each call to a WKWebView script\n * message handler the HOST APP installed under `handlerName` — an explicit\n * contract between the embedder and their own app, never a global this library\n * sniffs on its own. The message shape is\n * `{ family: 'selection' } | { family: 'impact', weight } | { family: 'notify', kind }`;\n * the native side maps families onto the matching `UIFeedbackGenerator`.\n * Calls fall back to `fallback` (default: {@link vibrationFeedback}) when the\n * handler is absent, so one provider serves a hybrid web/app deployment.\n */\nexport const wkFeedback = (\n handlerName: string,\n fallback: WebPlatformFeedback = vibrationFeedback,\n): WebPlatformFeedback => {\n const handler = (): WKMessageHandler | undefined => {\n if (typeof window === 'undefined') return undefined;\n const w = window as Window & {\n webkit?: { messageHandlers?: Record<string, WKMessageHandler | undefined> };\n };\n return w.webkit?.messageHandlers?.[handlerName];\n };\n return {\n selection: () => {\n const h = handler();\n if (h) h.postMessage({ family: 'selection' });\n else fallback.selection();\n },\n impact: (weight = 'light') => {\n const h = handler();\n if (h) h.postMessage({ family: 'impact', weight });\n else fallback.impact(weight);\n },\n notify: (kind) => {\n const h = handler();\n if (h) h.postMessage({ family: 'notify', kind });\n else fallback.notify(kind);\n },\n };\n};\n","/**\n * Wheel-input classification for ambient zoom — pure, so every framework\n * adapter shares one feel and the mapping is unit-testable.\n *\n * A ctrl/cmd wheel event is one of three physically different inputs that\n * happen to share an event type, each with its own unit system:\n *\n * • trackpad PINCH — browsers (Chrome/Edge/Firefox) synthesize ctrl+wheel\n * px-deltas scaled to the de-facto convention that `exp(-Δ/100)` tracks\n * the physical finger scale 1:1. Anything weaker feels dead (the content\n * must track the fingers — the Figma/maps feel).\n * • mouse NOTCHES — coarse steps: ±100/±120 px in Chrome, lines/pages mode\n * in Firefox. The pinch mapping would triple per click; a notch instead\n * steps by 1.2, the same ratio as the zoom buttons.\n * • cmd + continuous SCROLL — real scroll px, big and streaming (with\n * momentum): a gentle scrub coefficient. A cmd+mouse notch (±120 px)\n * lands within 0.3% of the 1.2 button step, so it needs no special case.\n *\n * (Safari never synthesizes ctrl+wheel for pinch — it fires proprietary\n * gesture events with an absolute scale; that wiring lives in the adapters.)\n */\nexport interface WheelSample {\n deltaY: number;\n /** WheelEvent.deltaMode: 0 = pixels, 1 = lines, 2 = pages. */\n deltaMode: number;\n ctrlKey: boolean;\n metaKey: boolean;\n}\n\n/** The per-notch step for discrete wheels — the zoom buttons' ratio. */\nconst NOTCH = 1.2;\n\n/** Zoom factor for a ctrl/cmd wheel event (the caller decides zoom vs pan). */\nexport function wheelZoomFactor(e: WheelSample): number {\n // Lines/pages mode (Firefox mouse wheels): one button-step per notch.\n if (e.deltaMode !== 0) return Math.pow(NOTCH, -Math.sign(e.deltaY));\n if (e.ctrlKey) {\n // px deltas with ctrl: a real mouse notch arrives big (±100/±120) and\n // steps like the buttons; anything smaller is a synthesized pinch.\n if (Math.abs(e.deltaY) >= 100) return Math.pow(NOTCH, -Math.sign(e.deltaY));\n return Math.exp(-e.deltaY / 100);\n }\n // cmd + continuous scroll: the gentle scrub.\n return Math.exp(-e.deltaY * 0.0015);\n}\n","/**\n * Stage gesture controller — the ONE DOM input binding for a Stage surface,\n * shared by every framework adapter (React, Angular, …) so the feel can never\n * drift between them.\n *\n * The premise (see the stage plugin's camera doctrine): desktop inputs arrive\n * with physics already applied by the OS — momentum lives in the wheel stream,\n * a trackpad pinch is one pre-arbitrated gesture stream. Touch arrives RAW:\n * with `touch-action: none` the platform scroller is out of the loop, so the\n * ballistics (velocity → fling) and the arbitration (is this contact a scroll,\n * a pinch, a tap, or a tool gesture?) must be synthesized here.\n *\n * Arbitration is MODALITY-AWARE:\n * - touch — navigation-first: one finger pans (whatever tool is armed), two\n * fingers pinch-zoom around their centroid, release velocity flings, a tap\n * forwards as a click, a double-tap zoom-toggles, a long-press hands the\n * gesture to the interaction hub (text selection), and a second finger\n * landing mid-tool-gesture CANCELS it into a pinch (the Notes/Procreate\n * convention).\n * - mouse/pen — tool-first, exactly the pre-existing behavior: with a hub,\n * every down/move/up forwards (pan is the pan tool's job); without one,\n * dragging pans. Wheel and Safari-trackpad gesture events are unchanged.\n *\n * Camera writes are rAF-COALESCED: pointer events only update gesture state;\n * one animation-frame tick applies at most one pan and one zoom per frame,\n * inside the host's begin/endGesture transaction. Events may arrive at 120 Hz;\n * the camera moves at display rate.\n *\n * Dependency note: this module speaks to the stage through the STRUCTURAL\n * {@link StageGestureHost} interface (satisfied by `StageCapability`) and to\n * the interaction hub through {@link StageGestureSink} (a closure the adapter\n * builds) — @embedpdf/web stays free of plugin imports, per the layering law.\n */\n\nimport { wheelZoomFactor } from './wheel';\n\nexport type StagePointerKind = 'mouse' | 'pen' | 'touch';\n\n/** What the controller needs from the camera — `StageCapability` satisfies it. */\nexport interface StageGestureHost {\n panBy(dxScreen: number, dyScreen: number): void;\n zoomAround(screenPt: { x: number; y: number }, factor: number): void;\n beginGesture(options?: { elastic?: boolean }): void;\n endGesture(): void;\n /** Momentum pan from a release velocity in screen px/s. */\n fling(velocityX: number, velocityY: number): void;\n /** True while a tween/fling runs — a touch-down then is a \"catch\", not a tap. */\n cameraInMotion(): boolean;\n doubleTapZoom(screenPt: { x: number; y: number }): void;\n}\n\n/**\n * Where non-navigation gestures go — the adapter's bridge to the interaction\n * hub. Every callback receives the ORIGINAL PointerEvent so the adapter can\n * resolve pages/points exactly as it always has. Omit the sink entirely for a\n * hub-less (built-in pan) stage.\n */\nexport interface StageGestureSink {\n down(e: PointerEvent, clickCount: number): void;\n move(e: PointerEvent): void;\n up(e: PointerEvent): void;\n /** The gesture was taken over by navigation (second finger → pinch) or\n * cancelled by the system — abort, don't commit. */\n cancel(e: PointerEvent): void;\n /** Pointer travel with no gesture in flight — cursor feedback. */\n hover(e: PointerEvent): void;\n /** A touch press held still: hand the gesture to the hub (the adapter\n * typically forwards it as a word-select down). Subsequent move/up arrive\n * via {@link move}/{@link up}. */\n longPress(e: PointerEvent): void;\n /**\n * Touch-consent pre-flight, asked at touch-down BEFORE the contact is\n * classified: does a tool have standing to own this contact? True when the\n * armed tool takes fingers wholesale (a drawing tool) or something under\n * the point claims its drags (a selected annotation's body or handles).\n * True routes the whole contact to {@link down}/{@link move}/{@link up} —\n * where a second finger still cancels it into a pinch. Must be a pure\n * read. Absent = never; the contact navigates.\n */\n claimsPoint?(e: PointerEvent): boolean;\n}\n\n/** The wheel fields the zoom classifier reads (see `./wheel`'s `WheelSample`). */\nexport interface StageWheelSample {\n deltaY: number;\n deltaMode: number;\n ctrlKey: boolean;\n metaKey: boolean;\n}\n\nexport interface StageGestureOptions {\n /** Ambient zoom (wheel-zoom, pinch-zoom, double-tap zoom). Off: zoom wheels\n * fall through to pan and pinches pan without zooming — but are still\n * swallowed, never page-zooming the browser. Default true. */\n zoomGestures?: boolean;\n /** The wheel → zoom-factor classifier. Defaults to this package's\n * `wheelZoomFactor` (browser wheel classification lives HERE, with the rest\n * of the browser input handling); inject to override or to fake in tests. */\n wheelZoomFactor?: (sample: StageWheelSample) => number;\n /** Tool routing for non-navigation gestures; omit for built-in-pan stages. */\n sink?: StageGestureSink | null;\n /** Touch press duration that becomes a long-press (ms). Default 450. */\n longPressMs?: number;\n /** Finger travel below which a touch stays a tap/press (px). Default 10. */\n tapSlopPx?: number;\n /** Max gap between taps for a double-tap (ms). Default 300. */\n doubleTapMs?: number;\n /** Release speed below which no fling starts (px/s). Default 50. */\n flingMinVelocity?: number;\n}\n\n/**\n * Release velocity from a trail of pointer samples: the mean velocity over the\n * trailing `windowMs` (first-to-last inside the window). Null when the trail is\n * too thin or too stale to trust — the standard \"held still, then let go\"\n * case, which must NOT fling. Pure; exported for tests.\n */\nexport function computeReleaseVelocity(\n samples: ReadonlyArray<{ t: number; x: number; y: number }>,\n now: number,\n windowMs = 100,\n): { vx: number; vy: number } | null {\n let firstIdx = -1;\n for (let i = 0; i < samples.length; i++) {\n if (now - samples[i].t <= windowMs) {\n firstIdx = i;\n break;\n }\n }\n if (firstIdx < 0 || firstIdx === samples.length - 1) return null;\n const first = samples[firstIdx];\n const last = samples[samples.length - 1];\n const dt = last.t - first.t;\n if (dt < 10) return null;\n return { vx: ((last.x - first.x) / dt) * 1000, vy: ((last.y - first.y) / dt) * 1000 };\n}\n\ninterface Tracked {\n id: number;\n kind: StagePointerKind;\n x: number;\n y: number;\n downX: number;\n downY: number;\n}\n\ntype Mode = 'idle' | 'pending' | 'pan' | 'pinch' | 'tool' | 'mousedrag';\n\n/** Attach the gesture controller to a Stage container. Returns the detach fn. */\nexport function createStageGestureController(\n el: HTMLElement,\n host: StageGestureHost,\n options: StageGestureOptions,\n): () => void {\n const zoomGestures = options.zoomGestures ?? true;\n const wheelZoom = options.wheelZoomFactor ?? wheelZoomFactor;\n const sink = options.sink ?? null;\n const LONG_PRESS_MS = options.longPressMs ?? 450;\n const SLOP = options.tapSlopPx ?? 10;\n const DOUBLE_TAP_MS = options.doubleTapMs ?? 300;\n const DOUBLE_TAP_RADIUS = 25;\n const FLING_MIN = options.flingMinVelocity ?? 50;\n const MIN_PINCH_SPAN = 20; // px — below this a span ratio is mostly noise\n const PINCH_RELEASE_GRACE_MS = 160; // leftover finger must OUTLIVE the release\n const SETTLE_SPEED = 0.12; // px/ms — below this the leftover finger has SETTLED\n\n const pointers = new Map<number, Tracked>();\n let mode: Mode = 'idle';\n let began = false; // a host gesture transaction is open\n let suppressTap = false; // this contact CAUGHT a moving camera — never a tap\n let touchToolGesture = false; // 'tool' mode entered via touch long-press\n let downEvent: PointerEvent | null = null; // first touch's down, for tap/long-press forwarding\n let longPressTimer: ReturnType<typeof setTimeout> | null = null;\n let panId = -1;\n // A pan is ARMED once its finger has crossed slop. Fresh touches arm on the\n // pending→pan transition; the finger LEFT OVER when a pinch ends starts\n // DISARMED — a gesture's identity persists through its release, so the\n // leftover contact must re-earn pan-hood exactly like a new finger would.\n // While disarmed, its micro-rolls neither pan nor pollute the velocity\n // trail (which still holds the pinch's CENTROID motion — the momentum a\n // compound gesture actually has). For a GRACE window after the transition\n // the slop base FOLLOWS the finger: in a fast release the leftover finger\n // is still genuinely moving, and distance alone cannot tell that from a\n // deliberate continuation — only outliving the release window can.\n let panArmed = true;\n let panGraceUntil = 0;\n let graceSample = { t: 0, x: 0, y: 0 }; // leftover finger's last observed sample\n // iOS-visible seam: real WebKit can deliver a trailing gesturechange after\n // the last pointerup of a pinch — with no touches left, the desktop-trackpad\n // path would apply a stray end-of-pinch zoom. Any RECENT touch activity\n // therefore suppresses gesture events; desktop trackpads never have any.\n let lastTouchAt = -Infinity;\n\n // rAF application state\n let frame = 0;\n let dirty = false;\n let lastApplied = { x: 0, y: 0 }; // pan focal point, client px\n let lastSpan = 0;\n\n // velocity trail of the pan focal point (finger, or pinch centroid)\n let trail: Array<{ t: number; x: number; y: number }> = [];\n // the pinch's SPAN trail, sampled beside the centroid: at release it decides\n // the gesture's CHARACTER. A fast pinch release has asymmetric finger\n // speeds (the lifting finger flicks away), which moves the TRUE centroid at\n // hundreds of px/s — physically real, but zoom-release noise the platform\n // ignores. Fling only when the centroid rate DOMINATES the span rate\n // (a two-finger pan); a zoom-dominant release ends still.\n let spanTrail: Array<{ t: number; x: number; y: number }> = [];\n\n // tap-pair state for double-tap\n let lastTapT = 0;\n let lastTapX = 0;\n let lastTapY = 0;\n\n // multi-click counter for mouse/pen tool downs (parity with the adapters'\n // previous createClickCounter: 400 ms / 6 px)\n let mLast = 0;\n let mX = 0;\n let mY = 0;\n let mCount = 0;\n const clickCount = (e: PointerEvent): number => {\n const now = Date.now();\n mCount =\n now - mLast <= 400 && Math.hypot(e.clientX - mX, e.clientY - mY) <= 6 ? mCount + 1 : 1;\n mLast = now;\n mX = e.clientX;\n mY = e.clientY;\n return mCount;\n };\n\n const vpt = (clientX: number, clientY: number) => {\n const r = el.getBoundingClientRect();\n return { x: clientX - r.left, y: clientY - r.top };\n };\n const touches = (): Tracked[] => {\n const out: Tracked[] = [];\n pointers.forEach((p) => {\n if (p.kind === 'touch') out.push(p);\n });\n return out;\n };\n\n const clearLongPress = () => {\n if (longPressTimer !== null) {\n clearTimeout(longPressTimer);\n longPressTimer = null;\n }\n };\n const stopFrames = () => {\n if (frame) {\n cancelAnimationFrame(frame);\n frame = 0;\n }\n };\n const begin = (elastic = false) => {\n if (!began) {\n began = true;\n // touch contacts are elastic (rubber-band past the clamp); mouse drags\n // stay rigid — the desktop convention\n host.beginGesture(elastic ? { elastic: true } : undefined);\n }\n };\n const end = () => {\n if (began) {\n began = false;\n host.endGesture();\n }\n };\n const toIdle = () => {\n mode = 'idle';\n downEvent = null;\n touchToolGesture = false;\n trail = [];\n spanTrail = [];\n dirty = false;\n panId = -1;\n panArmed = true;\n panGraceUntil = 0;\n clearLongPress();\n stopFrames();\n };\n const pushSample = (x: number, y: number) => {\n trail.push({ t: performance.now(), x, y });\n if (trail.length > 16) trail.shift();\n };\n const maybeFling = () => {\n const v = computeReleaseVelocity(trail, performance.now());\n if (v && Math.hypot(v.vx, v.vy) >= FLING_MIN) host.fling(v.vx, v.vy);\n };\n\n // ── the application step, shared by the frame loop AND the release paths —\n // a release must flush whatever the last tick hadn't applied yet, or a fast\n // flick loses its final sub-frame of travel and a pinch its last span step.\n const flushPanTo = (x: number, y: number) => {\n const dx = x - lastApplied.x;\n const dy = y - lastApplied.y;\n if (dx !== 0 || dy !== 0) host.panBy(dx, dy);\n lastApplied = { x, y };\n dirty = false;\n };\n const flushPinch = (ax: number, ay: number, bx: number, by: number) => {\n const cx = (ax + bx) / 2;\n const cy = (ay + by) / 2;\n const span = Math.hypot(ax - bx, ay - by);\n const dx = cx - lastApplied.x;\n const dy = cy - lastApplied.y;\n if (dx !== 0 || dy !== 0) host.panBy(dx, dy);\n if (zoomGestures && span > MIN_PINCH_SPAN && lastSpan > MIN_PINCH_SPAN && span !== lastSpan) {\n host.zoomAround(vpt(cx, cy), span / lastSpan);\n }\n // The centroid + span trails sample HERE — once per applied frame, where\n // both fingers are read coherently. Per-event sampling zig-zags (fingers\n // report sequentially, so each single-finger move fakes a half-step of\n // centroid motion) and manufactures phantom release velocity. The span\n // trail is the release gate's evidence of the gesture's CHARACTER.\n pushSample(cx, cy);\n spanTrail.push({ t: performance.now(), x: span, y: 0 });\n if (spanTrail.length > 16) spanTrail.shift();\n lastApplied = { x: cx, y: cy };\n lastSpan = span;\n dirty = false;\n };\n\n // ── the per-frame application (one camera write per frame) ────────────────\n const tick = () => {\n frame = 0;\n if (mode === 'pan' || mode === 'mousedrag') {\n const p = pointers.get(panId);\n if (p && dirty) flushPanTo(p.x, p.y);\n frame = requestAnimationFrame(tick);\n } else if (mode === 'pinch') {\n const [a, b] = touches();\n if (a && b && dirty) flushPinch(a.x, a.y, b.x, b.y);\n frame = requestAnimationFrame(tick);\n }\n };\n const ensureFrames = () => {\n if (!frame) frame = requestAnimationFrame(tick);\n };\n\n const enterPinch = () => {\n const [a, b] = touches();\n if (!a || !b) return;\n mode = 'pinch';\n lastApplied = { x: (a.x + b.x) / 2, y: (a.y + b.y) / 2 };\n lastSpan = Math.hypot(a.x - b.x, a.y - b.y);\n trail = [];\n dirty = false;\n ensureFrames();\n };\n\n const onLongPress = () => {\n longPressTimer = null;\n if (mode !== 'pending' || !downEvent || !sink) return;\n // The camera transaction closes; the tool owns the rest of this contact.\n end();\n mode = 'tool';\n touchToolGesture = true;\n sink.longPress(downEvent);\n };\n\n // ── listeners ─────────────────────────────────────────────────────────────\n const onDown = (e: PointerEvent) => {\n const kind = (e.pointerType || 'mouse') as StagePointerKind;\n if (kind === 'touch') lastTouchAt = performance.now();\n if (kind !== 'touch') {\n if (kind === 'mouse' && e.button !== 0) return;\n if (mode !== 'idle') return; // an active gesture owns the surface\n pointers.set(e.pointerId, {\n id: e.pointerId,\n kind,\n x: e.clientX,\n y: e.clientY,\n downX: e.clientX,\n downY: e.clientY,\n });\n if (sink) {\n // tool-first, exactly the pre-existing hub behavior\n mode = 'tool';\n sink.down(e, clickCount(e));\n } else {\n mode = 'mousedrag';\n begin();\n panId = e.pointerId;\n lastApplied = { x: e.clientX, y: e.clientY };\n ensureFrames();\n }\n return;\n }\n\n // touch\n switch (mode) {\n case 'idle': {\n pointers.set(e.pointerId, {\n id: e.pointerId,\n kind,\n x: e.clientX,\n y: e.clientY,\n downX: e.clientX,\n downY: e.clientY,\n });\n // Consent pre-flight — but a MOVING camera always catches first: while\n // content flies under the finger, the touch means \"stop\", never \"grab\n // whatever happens to pass beneath it\".\n const moving = host.cameraInMotion();\n if (!moving && sink?.claimsPoint?.(e)) {\n // A tool owns this contact from the first pixel (selected-annotation\n // drag, or an armed drawing tool). No camera transaction — this is\n // not a navigation gesture; a second finger converts it to a pinch\n // via the 'tool' branch (sink.cancel), exactly like a long-press.\n mode = 'tool';\n touchToolGesture = true;\n sink.down(e, 1);\n break;\n }\n mode = 'pending';\n downEvent = e;\n suppressTap = moving; // a catch, not a tap\n begin(true); // stops any fling/tween under the finger\n trail = [];\n pushSample(e.clientX, e.clientY);\n clearLongPress();\n if (sink && !suppressTap) longPressTimer = setTimeout(onLongPress, LONG_PRESS_MS);\n break;\n }\n case 'pending':\n case 'pan': {\n pointers.set(e.pointerId, {\n id: e.pointerId,\n kind,\n x: e.clientX,\n y: e.clientY,\n downX: e.clientX,\n downY: e.clientY,\n });\n clearLongPress();\n enterPinch();\n break;\n }\n case 'tool': {\n // A second finger during a TOUCH tool gesture cancels it into a pinch\n // (the platform convention). Mouse tool gestures ignore stray touches.\n if (!touchToolGesture) return;\n sink?.cancel(e);\n touchToolGesture = false;\n pointers.set(e.pointerId, {\n id: e.pointerId,\n kind,\n x: e.clientX,\n y: e.clientY,\n downX: e.clientX,\n downY: e.clientY,\n });\n begin(true);\n enterPinch();\n break;\n }\n case 'pinch':\n case 'mousedrag':\n return; // ignore extra contacts\n }\n };\n\n const onWindowMove = (e: PointerEvent) => {\n const p = pointers.get(e.pointerId);\n if (!p) return;\n if (p.kind === 'touch') lastTouchAt = performance.now();\n p.x = e.clientX;\n p.y = e.clientY;\n switch (mode) {\n case 'pending': {\n pushSample(p.x, p.y);\n if (Math.hypot(p.x - p.downX, p.y - p.downY) > SLOP) {\n clearLongPress();\n mode = 'pan';\n panId = p.id;\n lastApplied = { x: p.x, y: p.y }; // absorb the slop, like a scroller\n ensureFrames();\n }\n break;\n }\n case 'pan':\n if (p.id !== panId) break;\n if (!panArmed) {\n // pinch-leftover contact. Inside the release-grace window the slop\n // base follows the finger — a fast release keeps moving and must\n // never accumulate distance; only a contact that OUTLIVES the\n // window can re-earn panning by crossing slop from where it\n // settled.\n const nowT = performance.now();\n if (nowT < panGraceUntil) {\n // a finger that SETTLES (speed drops) inside the window is a\n // continuation taking hold — end the grace early so a deliberate\n // pause-then-drag stays responsive\n const dt = nowT - graceSample.t;\n if (dt >= 8) {\n const speed = Math.hypot(p.x - graceSample.x, p.y - graceSample.y) / dt;\n graceSample = { t: nowT, x: p.x, y: p.y };\n if (speed < SETTLE_SPEED) panGraceUntil = 0;\n }\n p.downX = p.x;\n p.downY = p.y;\n break;\n }\n if (Math.hypot(p.x - p.downX, p.y - p.downY) <= SLOP) break;\n panArmed = true;\n lastApplied = { x: p.x, y: p.y }; // absorb, like any fresh pan\n trail = [];\n spanTrail = [];\n }\n pushSample(p.x, p.y);\n dirty = true;\n break;\n case 'pinch':\n // centroid samples are taken per applied FRAME (see flushPinch) —\n // per-event sampling here would zig-zag between the two fingers\n dirty = true;\n break;\n case 'tool':\n sink?.move(e);\n break;\n case 'mousedrag':\n if (p.id === panId) dirty = true;\n break;\n case 'idle':\n break;\n }\n };\n\n // Hover (cursor feedback) — only with no gesture in flight, and only from\n // the element itself, matching the previous adapters.\n const onHoverMove = (e: PointerEvent) => {\n if (mode === 'idle' && sink) sink.hover(e);\n };\n\n const backToSingleFinger = (): boolean => {\n const rest = touches();\n if (rest.length !== 1) return false;\n mode = 'pan';\n panId = rest[0].id;\n panArmed = false; // leftover contact re-earns pan-hood via slop\n panGraceUntil = performance.now() + PINCH_RELEASE_GRACE_MS;\n graceSample = { t: performance.now(), x: rest[0].x, y: rest[0].y };\n rest[0].downX = rest[0].x;\n rest[0].downY = rest[0].y;\n lastApplied = { x: rest[0].x, y: rest[0].y };\n trail = [];\n dirty = false;\n return true;\n };\n\n const onUp = (e: PointerEvent) => {\n const p = pointers.get(e.pointerId);\n if (!p) return;\n if (p.kind === 'touch') lastTouchAt = performance.now();\n pointers.delete(e.pointerId);\n switch (mode) {\n case 'pending': {\n // Slop never exceeded, timer never fired: a tap (or a catch).\n clearLongPress();\n end();\n const now = performance.now();\n const isDouble =\n now - lastTapT <= DOUBLE_TAP_MS &&\n Math.hypot(e.clientX - lastTapX, e.clientY - lastTapY) <= DOUBLE_TAP_RADIUS;\n if (suppressTap) {\n lastTapT = 0; // a catch never counts toward a double-tap\n } else if (zoomGestures && isDouble) {\n lastTapT = 0;\n host.doubleTapZoom(vpt(e.clientX, e.clientY));\n } else {\n lastTapT = now;\n lastTapX = e.clientX;\n lastTapY = e.clientY;\n if (sink && downEvent) {\n // The whole click, delivered at release — tools see exactly the\n // down/up pair they would from a mouse.\n sink.down(downEvent, 1);\n sink.up(e);\n }\n }\n toIdle();\n break;\n }\n case 'pan': {\n if (!panArmed) {\n // The pinch's OTHER finger leaving: the gesture ends as a pinch.\n // No flush (release-rolls are noise). Glide is CHARACTER-gated: a\n // human fast release moves the true centroid (the lifting finger\n // flicks away), so centroid velocity alone lies — the glide fires\n // only when the centroid rate DOMINATES the span rate (a\n // two-finger pan), never on a zoom-dominant release.\n const now = performance.now();\n const v = computeReleaseVelocity(trail, now);\n const vs = computeReleaseVelocity(spanTrail, now);\n end(); // close the bracket first — endGesture owns the elastic settle\n if (v) {\n const speed = Math.hypot(v.vx, v.vy);\n if (speed >= FLING_MIN && speed > Math.abs(vs?.vx ?? 0)) host.fling(v.vx, v.vy);\n }\n toIdle();\n break;\n }\n flushPanTo(e.clientX, e.clientY); // apply the final sub-frame of travel\n pushSample(e.clientX, e.clientY);\n end();\n maybeFling();\n toIdle();\n break;\n }\n case 'pinch': {\n const rest = touches();\n if (rest.length === 1) {\n // The pinch ends AT ITS LAST COHERENT FRAME. No flush here: the\n // lifting finger's release position is fresh but the other one's is\n // stale (its move for this window may not have arrived), and a\n // centroid of two instants is fiction — in a fast pinch that skewed\n // write was the visible end-of-pinch hop, and its poisoned trail\n // sample the phantom fling. Only coherent finger-pairs write the\n // camera; the sub-frame remainder is discarded, as the platform\n // recognizers do. The remaining contact gets a DISARMED pan with\n // the centroid trail preserved and a release-grace window armed.\n mode = 'pan';\n panId = rest[0].id;\n panArmed = false;\n panGraceUntil = performance.now() + PINCH_RELEASE_GRACE_MS;\n graceSample = { t: performance.now(), x: rest[0].x, y: rest[0].y };\n rest[0].downX = rest[0].x;\n rest[0].downY = rest[0].y;\n lastApplied = { x: rest[0].x, y: rest[0].y };\n dirty = false;\n break;\n }\n end();\n maybeFling();\n toIdle();\n break;\n }\n case 'tool': {\n sink?.up(e);\n toIdle();\n break;\n }\n case 'mousedrag': {\n if (p.id !== panId) break;\n flushPanTo(e.clientX, e.clientY);\n end();\n toIdle();\n break;\n }\n case 'idle':\n break;\n }\n };\n\n const onCancel = (e: PointerEvent) => {\n const p = pointers.get(e.pointerId);\n if (!p) return;\n pointers.delete(e.pointerId);\n if (mode === 'tool') {\n sink?.cancel(e);\n toIdle();\n return;\n }\n if (mode === 'pinch' && backToSingleFinger()) return;\n end();\n toIdle();\n };\n\n // Wheel is ambient navigation in BOTH modes: ctrl/meta zooms (classified per\n // input by the injected wheelZoomFactor), else scrolls. With zoom gestures\n // off, a zoom-wheel falls through to ordinary pan.\n const onWheel = (e: WheelEvent) => {\n e.preventDefault();\n if (zoomGestures && (e.ctrlKey || e.metaKey)) {\n host.zoomAround(vpt(e.clientX, e.clientY), wheelZoom(e));\n } else {\n const dx = e.shiftKey ? e.deltaY : e.deltaX;\n const dy = e.shiftKey ? e.deltaX : e.deltaY;\n host.panBy(-dx, -dy);\n }\n };\n\n // Safari's proprietary gesture events. On DESKTOP Safari they are the only\n // trace of a trackpad pinch — convert the absolute scale to per-event ratios.\n // On iOS they fire ALONGSIDE per-finger pointer events; there the pointer\n // path owns the pinch and these are preventDefault-ed only (a pinch over the\n // stage must never page-zoom Safari). The guard is live touch contacts.\n let lastScale = 1;\n const onGestureStart = (e: Event) => {\n e.preventDefault();\n lastScale = (e as unknown as { scale?: number }).scale ?? 1;\n };\n const onGestureChange = (e: Event) => {\n e.preventDefault();\n // iOS: the pointer path owns touch pinches — and a TRAILING gesturechange\n // can arrive after the last pointerup, so suppression keys on RECENT\n // touch activity, not just live contacts. Desktop trackpads have none.\n if (touches().length > 0 || performance.now() - lastTouchAt < 500) return;\n const g = e as unknown as { scale?: number; clientX: number; clientY: number };\n const scale = g.scale ?? 1;\n if (zoomGestures && scale > 0) {\n host.zoomAround(vpt(g.clientX, g.clientY), scale / lastScale);\n }\n lastScale = scale;\n };\n\n el.addEventListener('pointerdown', onDown);\n el.addEventListener('pointermove', onHoverMove);\n window.addEventListener('pointermove', onWindowMove);\n window.addEventListener('pointerup', onUp);\n window.addEventListener('pointercancel', onCancel);\n el.addEventListener('wheel', onWheel, { passive: false });\n const hasGestureEvents = 'GestureEvent' in window;\n if (hasGestureEvents) {\n el.addEventListener('gesturestart', onGestureStart);\n el.addEventListener('gesturechange', onGestureChange);\n el.addEventListener('gestureend', onGestureStart); // reset the base\n }\n\n return () => {\n el.removeEventListener('pointerdown', onDown);\n el.removeEventListener('pointermove', onHoverMove);\n window.removeEventListener('pointermove', onWindowMove);\n window.removeEventListener('pointerup', onUp);\n window.removeEventListener('pointercancel', onCancel);\n el.removeEventListener('wheel', onWheel);\n if (hasGestureEvents) {\n el.removeEventListener('gesturestart', onGestureStart);\n el.removeEventListener('gesturechange', onGestureChange);\n el.removeEventListener('gestureend', onGestureStart);\n }\n clearLongPress();\n stopFrames();\n end(); // balance an open transaction on unmount\n };\n}\n","/**\n * Selection-handle DOM binding — the listener mechanics of dragging one\n * handle element, shared by every framework adapter (the policy — geometry\n * and the drag session — lives in `@embedpdf/plugin-selection`'s pure\n * `handles` module; this file never imports it, per the layering law:\n * structural interfaces only).\n *\n * The load-bearing subtlety this module owns: the pointer-DOWN shield must be\n * a NATIVE listener. The stage's gesture controller listens natively on the\n * container, so a framework-synthetic stopPropagation (React runs its\n * handlers at the root, AFTER the container's native ones) would be too late\n * — the stage would already be panning underneath the handle drag. Once the\n * down is captured here, movement tracks by CLIENT DELTAS from the handle's\n * own base point: no DOM geometry reads, and the client↔overlay conversion\n * cancels out.\n */\n\ninterface SurfacePoint {\n x: number;\n y: number;\n}\n\n/** The armed drag the adapter hands back — plugin-selection's\n * `SelectionHandleDragSession` satisfies it. */\nexport interface SelectionHandleSession {\n move(overlay: SurfacePoint): void;\n end(): void;\n}\n\nexport interface AttachSelectionHandleOptions {\n /**\n * Called at pointer-down to arm a drag: return the drag session plus the\n * handle's current BASE point in overlay space (the bar's midpoint — the\n * point the user grabbed). Return null to decline (endpoint vanished, page\n * not laid out) — the press then does nothing and nothing is captured.\n */\n arm(): { base: SurfacePoint; session: SelectionHandleSession } | null;\n}\n\n/** Bind one handle element. Returns the detach fn. */\nexport function attachSelectionHandle(\n el: HTMLElement,\n options: AttachSelectionHandleOptions,\n): () => void {\n let active: {\n session: SelectionHandleSession;\n base: SurfacePoint;\n baseClient: SurfacePoint;\n pointerId: number;\n } | null = null;\n\n const onMove = (e: PointerEvent) => {\n if (!active || e.pointerId !== active.pointerId) return;\n active.session.move({\n x: active.base.x + (e.clientX - active.baseClient.x),\n y: active.base.y + (e.clientY - active.baseClient.y),\n });\n };\n // Release and system-cancel settle identically: the selection's own commit\n // semantics live in the session; there is no half-state to revert here.\n const onUp = (e: PointerEvent) => {\n if (!active || e.pointerId !== active.pointerId) return;\n const s = active.session;\n active = null;\n s.end();\n };\n const onDown = (e: PointerEvent) => {\n if (active) return;\n const armed = options.arm();\n if (!armed) return;\n e.preventDefault();\n e.stopPropagation(); // native: fires BEFORE the stage controller's listener\n try {\n el.setPointerCapture(e.pointerId);\n } catch {\n // best-effort, like the scrollbar: an already-released pointer\n // (pen/touch races, synthetic events in tests) throws — the drag must\n // still arm, and the window-less capture path still delivers to `el`.\n }\n active = {\n session: armed.session,\n base: armed.base,\n baseClient: { x: e.clientX, y: e.clientY },\n pointerId: e.pointerId,\n };\n };\n\n el.addEventListener('pointerdown', onDown);\n el.addEventListener('pointermove', onMove);\n el.addEventListener('pointerup', onUp);\n el.addEventListener('pointercancel', onUp);\n return () => {\n el.removeEventListener('pointerdown', onDown);\n el.removeEventListener('pointermove', onMove);\n el.removeEventListener('pointerup', onUp);\n el.removeEventListener('pointercancel', onUp);\n active = null;\n };\n}\n","/**\n * Stage surface binding — everything DOM about attaching one element to one\n * stage lens, shared by every framework adapter (React, Angular, …) so the\n * binding can never drift between them:\n *\n * • ResizeObserver → `setViewport` (the shell only reports; placement is the\n * stage plugin's job)\n * • device-pixel-ratio observation → `setDevicePixelRatio` (dppx changes\n * re-subscribe, since the media query's value itself moves)\n * • pointer-sample normalization — the ONE place a PointerEvent becomes a\n * page-resolved, source-stamped sample for the interaction hub\n * • the gesture controller (wheel, Safari trackpad gestures, synthesized\n * touch physics) from `./stage-gestures`\n *\n * The framework adapters keep only their reactive glue: refs/effects, page\n * surfaces, cursor-style subscription, overlay slots.\n *\n * Dependency note: like the gesture controller, this module speaks to the\n * stage and the hub through STRUCTURAL interfaces ({@link StageSurfaceHost},\n * {@link StageSurfaceHub}) — satisfied by `StageCapability` and\n * `InteractionCapability`, imported by neither. @embedpdf/web stays free of\n * plugin imports, per the layering law.\n *\n * Browser-only by nature (ResizeObserver, matchMedia): call from a mounted\n * effect / browser platform check, exactly like the gesture controller.\n */\nimport { createStageGestureController } from './stage-gestures';\nimport type { StageGestureHost, StageGestureSink, StageWheelSample } from './stage-gestures';\n\ninterface SurfacePoint {\n x: number;\n y: number;\n}\n\n/** What the binding needs from the stage — `StageCapability` satisfies it. */\nexport interface StageSurfaceHost extends StageGestureHost {\n setViewport(size: { width: number; height: number }): void;\n setDevicePixelRatio(ratio: number): void;\n /** Viewport point → the page under it (with per-page display context), or null over a gap. */\n pageAt(screen: SurfacePoint): {\n pon: number;\n point: SurfacePoint;\n scale?: number;\n rotation?: 0 | 90 | 180 | 270;\n zoom?: number;\n } | null;\n /** Viewport point → a SPECIFIC page's content space, unclamped (frame-stable projection). */\n pointOnPage(pon: number, screen: SurfacePoint): SurfacePoint | null;\n}\n\n/**\n * The page-resolved sample the binding hands the hub — structurally a\n * `PointerSample` (plugin-interaction). `source` carries the lens identity so\n * the hub can route lens-scoped handlers; with several stages on one document,\n * a drag on one lens must never be captured by another lens's handler.\n */\nexport interface StageSurfaceSample {\n phase: 'down' | 'move' | 'up' | 'cancel';\n viewport: SurfacePoint;\n page?: {\n pon: number;\n point: SurfacePoint;\n scale?: number;\n rotation?: 0 | 90 | 180 | 270;\n zoom?: number;\n };\n project: (pon: number) => SurfacePoint | null;\n modifiers: { shift: boolean; alt: boolean; ctrl: boolean; meta: boolean };\n clickCount: number;\n pointerType: 'mouse' | 'pen' | 'touch';\n gesture?: 'long-press';\n source?: string;\n}\n\n/** What the binding needs from the interaction hub — `InteractionCapability` satisfies it. */\nexport interface StageSurfaceHub {\n dispatch(sample: StageSurfaceSample): void;\n activeTool(): { touchDirect?: boolean };\n wouldClaimTouch(sample: StageSurfaceSample): boolean;\n}\n\nexport interface StageSurfaceOptions {\n /** Route pointer input to the interaction hub (page-resolved samples) instead\n * of built-in drag-to-pan. Omit/null for a hub-less stage. */\n hub?: StageSurfaceHub | null;\n /** The lens identity stamped on every sample (`StageCapability.lensId()`),\n * so lens-scoped handlers only see their own stage's input. */\n source?: string;\n /** Ambient zoom gestures (see {@link StageGestureOptions}). Default true. */\n zoomGestures?: boolean;\n /** Override the wheel → zoom-factor classifier (defaults to `./wheel`). */\n wheelZoomFactor?: (sample: StageWheelSample) => number;\n}\n\n/** Attach the full surface binding. Returns the detach fn. */\nexport function createStageSurface(\n el: HTMLElement,\n stage: StageSurfaceHost,\n options: StageSurfaceOptions = {},\n): () => void {\n const hub = options.hub ?? null;\n const cleanups: Array<() => void> = [];\n\n // Only report the viewport size. Initial placement (home) is the stage\n // plugin's job — it places when it first learns a real size (and a\n // higher-priority initial-view provider can override). The shell stays dumb.\n const setViewport = () => stage.setViewport({ width: el.clientWidth, height: el.clientHeight });\n const ro = new ResizeObserver(setViewport);\n ro.observe(el);\n setViewport();\n cleanups.push(() => ro.disconnect());\n\n // Report the device pixel ratio so page transforms render crisp. dppx changes\n // (browser zoom, dragging between monitors) fire the media query;\n // re-subscribe each time since the query value itself moves.\n let mq: MediaQueryList | null = null;\n const reportDpr = () => {\n stage.setDevicePixelRatio(window.devicePixelRatio || 1);\n mq?.removeEventListener('change', reportDpr);\n mq = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);\n mq.addEventListener('change', reportDpr);\n };\n reportDpr();\n cleanups.push(() => mq?.removeEventListener('change', reportDpr));\n\n // The sink is the hub bridge: it converts events to page-resolved samples\n // (`pageAt` per event, so a drag can cross pages) and stamps the lens source.\n const sampleOf = (\n phase: StageSurfaceSample['phase'],\n e: PointerEvent,\n clickCount = 1,\n gesture?: StageSurfaceSample['gesture'],\n ): StageSurfaceSample => {\n const r = el.getBoundingClientRect();\n const viewport = { x: e.clientX - r.left, y: e.clientY - r.top };\n return {\n phase,\n viewport,\n page: stage.pageAt(viewport) ?? undefined,\n // Page-anchored gestures (annotation move/resize) track the origin\n // page's frame through this even when the cursor is off that page.\n project: (pon) => stage.pointOnPage(pon, viewport),\n modifiers: { shift: e.shiftKey, alt: e.altKey, ctrl: e.ctrlKey, meta: e.metaKey },\n clickCount,\n pointerType: (e.pointerType || 'mouse') as StageSurfaceSample['pointerType'],\n ...(gesture ? { gesture } : {}),\n ...(options.source ? { source: options.source } : {}),\n };\n };\n const forward = (\n phase: StageSurfaceSample['phase'],\n e: PointerEvent,\n clickCount = 1,\n gesture?: StageSurfaceSample['gesture'],\n ) => {\n hub?.dispatch(sampleOf(phase, e, clickCount, gesture));\n };\n const sink: StageGestureSink | null = hub\n ? {\n down: (e, clickCount) => forward('down', e, clickCount),\n move: (e) => forward('move', e),\n up: (e) => forward('up', e),\n cancel: (e) => forward('cancel', e),\n hover: (e) => forward('move', e), // no owner → the hub routes to onHover\n // Touch long-press = a word-select down (clickCount 2 keeps the\n // word-selection contract; the `gesture` marker is the honest\n // long-press signal for haptics/pickup handlers).\n longPress: (e) => forward('down', e, 2, 'long-press'),\n // Touch consent: an armed drawing/markup tool takes fingers wholesale;\n // otherwise per-point claims (a selected annotation's body or handles)\n // decide. A pure pre-flight — nothing captures.\n claimsPoint: (e) =>\n !!hub.activeTool().touchDirect || hub.wouldClaimTouch(sampleOf('down', e)),\n }\n : null;\n cleanups.push(\n createStageGestureController(el, stage, {\n zoomGestures: options.zoomGestures,\n wheelZoomFactor: options.wheelZoomFactor,\n sink,\n }),\n );\n\n return () => {\n for (const fn of cleanups) fn();\n };\n}\n","/**\n * A bytes-by-id store over IndexedDB — the browser's answer to \"keep these\n * blobs for me\". Structurally the store port a plugin declares for its own\n * persistence (e.g. `StampLibraryStore` in `@embedpdf/plugin-stamp`); declared\n * here as a twin so this package stays plugin-free, per the layering law.\n *\n * IndexedDB rather than localStorage: a library or an image is binary, and a\n * localStorage string store caps at a few MB. One database, one object store,\n * keys are the caller's ids, values are the bytes as stored.\n */\nexport interface ByteStore {\n list(): Promise<Array<{ id: string; bytes: Uint8Array }>>;\n put(id: string, bytes: Uint8Array): Promise<void>;\n delete(id: string): Promise<void>;\n}\n\nexport interface IndexedDbByteStoreOptions {\n /** Object store name inside the database. Default `'bytes'`. */\n storeName?: string;\n}\n\n/** Open (creating on first use) one IndexedDB object store keyed by id. */\nexport function indexedDbByteStore(\n dbName: string,\n opts: IndexedDbByteStoreOptions = {},\n): ByteStore {\n const storeName = opts.storeName ?? 'bytes';\n const open = (): Promise<IDBDatabase> =>\n new Promise((resolve, reject) => {\n const request = indexedDB.open(dbName, 1);\n request.onupgradeneeded = () => {\n if (!request.result.objectStoreNames.contains(storeName)) {\n request.result.createObjectStore(storeName);\n }\n };\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error ?? new Error('indexedDB.open failed'));\n });\n const run = async <T>(\n mode: IDBTransactionMode,\n body: (store: IDBObjectStore) => IDBRequest<T>,\n ): Promise<T> => {\n const db = await open();\n try {\n return await new Promise<T>((resolve, reject) => {\n const request = body(db.transaction(storeName, mode).objectStore(storeName));\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error ?? new Error('indexedDB request failed'));\n });\n } finally {\n db.close();\n }\n };\n return {\n list: async () => {\n const keys = await run<IDBValidKey[]>('readonly', (store) => store.getAllKeys());\n const values = await run<Uint8Array[]>('readonly', (store) => store.getAll());\n return keys.map((key, i) => ({ id: String(key), bytes: new Uint8Array(values[i]) }));\n },\n put: (id, bytes) =>\n run('readwrite', (store) => store.put(new Uint8Array(bytes), id)).then(() => undefined),\n delete: (id) => run('readwrite', (store) => store.delete(id)).then(() => undefined),\n };\n}\n","/**\n * Mount a registered font for the DOM: the engine lays text out with the\n * font's bytes, the browser needs the same face as a `@font-face` so the\n * live editor and the vector renderer show the glyphs the appearance stream\n * will bake. The family name is the font's KEY — the CSS family the\n * annotation plugin emits for a registered font (`cssFontFamily`) — so\n * mounting under the key is what makes the two agree.\n *\n * Returns the unmount. Idempotent per (document, key): a second mount of a\n * key already in the document's font set is a no-op that still returns an\n * unmount of its own (the face leaves when the LAST holder unmounts).\n */\nexport async function mountWebFont(\n key: string,\n data: ArrayBuffer | Uint8Array,\n options: { document?: Document; weight?: number | string; style?: 'normal' | 'italic' } = {},\n): Promise<() => void> {\n const doc = options.document ?? document;\n const fonts = doc.fonts;\n if (!fonts || typeof FontFace === 'undefined') return () => {};\n const holders = holdersOf(doc);\n const existing = holders.get(key);\n if (existing) {\n existing.count++;\n return () => release(holders, key);\n }\n const buffer =\n data instanceof Uint8Array\n ? (data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) as ArrayBuffer)\n : data;\n const face = new FontFace(key, buffer, {\n weight: options.weight !== undefined ? String(options.weight) : undefined,\n style: options.style,\n });\n holders.set(key, { face, doc, count: 1 });\n try {\n await face.load();\n fonts.add(face);\n } catch {\n holders.delete(key);\n return () => {};\n }\n notifyFontChange(doc);\n return () => release(holders, key);\n}\n\ninterface Holder {\n face: FontFace;\n doc: Document;\n count: number;\n}\n\nconst HOLDERS = new WeakMap<Document, Map<string, Holder>>();\nconst LISTENERS = new WeakMap<Document, Set<() => void>>();\n\n/** Internal: refresh editor styles for CSS loads and explicitly mounted faces.\n * Adding an already loaded FontFace does not trigger `loadingdone`. */\nexport function observeWebFonts(doc: Document, onChange: () => void): () => void {\n let listeners = LISTENERS.get(doc);\n if (!listeners) {\n listeners = new Set();\n LISTENERS.set(doc, listeners);\n }\n listeners.add(onChange);\n doc.fonts?.addEventListener('loadingdone', onChange);\n doc.fonts?.addEventListener('loadingerror', onChange);\n return () => {\n listeners.delete(onChange);\n doc.fonts?.removeEventListener('loadingdone', onChange);\n doc.fonts?.removeEventListener('loadingerror', onChange);\n };\n}\n\nfunction notifyFontChange(doc: Document): void {\n for (const listener of LISTENERS.get(doc) ?? []) listener();\n}\n\nfunction holdersOf(doc: Document): Map<string, Holder> {\n let map = HOLDERS.get(doc);\n if (!map) {\n map = new Map();\n HOLDERS.set(doc, map);\n }\n return map;\n}\n\nfunction release(holders: Map<string, Holder>, key: string): void {\n const holder = holders.get(key);\n if (!holder) return;\n holder.count--;\n if (holder.count > 0) return;\n holders.delete(key);\n try {\n holder.doc.fonts.delete(holder.face);\n } catch {\n // a detached document: nothing to remove from\n }\n notifyFontChange(holder.doc);\n}\n","/**\n * Acrobat's line model, stated in CSS — private to the rich text binding.\n *\n * The engine lays a line out the way Acrobat does: the line box is the\n * face's ascent + descent (its `hhea` metrics) + a constant 0.2 × size of\n * leading, with the baseline at the ascent and the leading entirely below.\n * The standard 14 carry no font program, so the engine uses the metrics of\n * Acrobat's bundled substitutes (Helvetica 0.83/0.17, Times 0.784/0.216,\n * Courier 0.627/0.373); any other face — a registered font, an embedded\n * one — uses its own `hhea` table.\n *\n * Canvas provides browser metrics, which can be rounded or differ from the\n * font program's metrics. They approximate the registered font's line model;\n * standard families use the engine's known values below. CSS still chooses\n * wrapping and combines mixed runs differently from the engine.\n *\n * One thing CSS cannot express: it centres a line's leading (half above the\n * glyphs), and the substitute face's ascent differs from the engine's, so\n * the first baseline can land lower by the half-leading plus that ascent\n * gap. The binding compensates using the body's face and size; mixed runs\n * remain approximate.\n */\n\nexport interface WebFontMetrics {\n /** Ascent above the baseline, per em. */\n ascent: number;\n /** Descent below the baseline, per em. */\n descent: number;\n}\n\n/** A face's approximate CSS line model. */\nexport interface LineModel {\n ascent: number;\n descent: number;\n /** Unitless CSS line height: ascent + descent + Acrobat's 0.2 leading. */\n lineHeight: number;\n}\n\n/** Acrobat's leading: a constant share of the size, never the font's lineGap. */\nexport const ACROBAT_LEADING = 0.2;\n\n/** The engine's metrics for the standard families (Acrobat's substitutes'\n * `hhea`), keyed the engine's way: no case, spaces, hyphens, quotes. */\nconst STANDARD_METRICS: Record<string, WebFontMetrics> = {\n helvetica: { ascent: 0.83, descent: 0.17 },\n arial: { ascent: 0.83, descent: 0.17 },\n arialmt: { ascent: 0.83, descent: 0.17 },\n times: { ascent: 0.784, descent: 0.216 },\n timesroman: { ascent: 0.784, descent: 0.216 },\n timesnewroman: { ascent: 0.784, descent: 0.216 },\n courier: { ascent: 0.627, descent: 0.373 },\n couriernew: { ascent: 0.627, descent: 0.373 },\n};\n\n/** What the browser resolves when nothing can be measured: a one-em content\n * area, the ascent of the engine's Helvetica. */\nconst FALLBACK_METRICS: WebFontMetrics = { ascent: 0.83, descent: 0.17 };\n\nfunction familyKey(family: string): string {\n return family.toLowerCase().replace(/[\\s\\-_'\"]/g, '');\n}\n\n/** The first family of a CSS family list, unquoted. */\nexport function firstFamily(cssFamily: string): string {\n return (cssFamily.split(',')[0] ?? '').trim().replace(/^[\"']|[\"']$/g, '');\n}\n\n// Reuse the canvas, never its measurements: a font can be registered,\n// replaced or removed after a fallback face was measured. FontFaceSet.check\n// also returns true for missing fonts, so it cannot validate a result cache.\nconst CONTEXTS = new WeakMap<Document, CanvasRenderingContext2D>();\n\n/**\n * The browser's own ascent and descent for a CSS family list, per em,\n * measured through a canvas. `null` when the platform cannot measure\n * (no canvas, no `fontBoundingBoxAscent`). Reads the currently resolved face\n * on every call, including while the intended font is loading.\n */\nexport function webFontMetrics(\n cssFamily: string,\n doc: Document | undefined = typeof document === 'undefined' ? undefined : document,\n face: { weight?: number | string; style?: string } = {},\n): WebFontMetrics | null {\n if (!doc) return null;\n let ctx = CONTEXTS.get(doc);\n if (!ctx) {\n const context = doc.createElement('canvas').getContext('2d');\n if (!context) return null;\n ctx = context;\n CONTEXTS.set(doc, ctx);\n }\n ctx.font = `${face.style ?? 'normal'} ${face.weight ?? 400} 100px ${cssFamily}`;\n const m = ctx.measureText('Hg') as TextMetrics & {\n fontBoundingBoxAscent?: number;\n fontBoundingBoxDescent?: number;\n };\n if (typeof m.fontBoundingBoxAscent !== 'number' || typeof m.fontBoundingBoxDescent !== 'number') {\n return null;\n }\n return {\n ascent: m.fontBoundingBoxAscent / 100,\n descent: m.fontBoundingBoxDescent / 100,\n };\n}\n\n/**\n * The standard families' engine metrics, or a browser approximation for\n * other families. `measure` is injectable for tests and resolved face styles.\n */\nexport function lineModelFor(\n cssFamily: string,\n measure: (cssFamily: string) => WebFontMetrics | null = webFontMetrics,\n): LineModel {\n const metrics =\n STANDARD_METRICS[familyKey(firstFamily(cssFamily))] ?? measure(cssFamily) ?? FALLBACK_METRICS;\n return {\n ascent: metrics.ascent,\n descent: metrics.descent,\n lineHeight: Math.round((metrics.ascent + metrics.descent + ACROBAT_LEADING) * 1000) / 1000,\n };\n}\n\n/**\n * Screen px to raise a box's first line by so its baseline matches the\n * engine's: CSS puts half the leading above the glyphs and draws them with\n * the browser's face, whose ascent differs from the engine's for the\n * standard families. `fontSize` in screen px.\n */\nexport function firstLineShiftFor(\n cssFamily: string,\n fontSize: number,\n measure: (cssFamily: string) => WebFontMetrics | null = webFontMetrics,\n): number {\n const model = lineModelFor(cssFamily, measure);\n const web = measure(cssFamily) ?? { ascent: model.ascent, descent: model.descent };\n const cssBaseline = (model.lineHeight - (web.ascent + web.descent)) / 2 + web.ascent;\n return Math.round((cssBaseline - model.ascent) * fontSize * 100) / 100;\n}\n","/**\n * Rich text editor binding — the ONE contentEditable mechanics module for a\n * FreeText annotation, shared by every framework adapter (React, Angular,\n * Vue, Svelte…) so editing can never drift between them:\n *\n * • render a rich document (runs of style deltas over a body) into the\n * element as paragraphs of inline-styled spans\n * • serialise the element back into runs on `input` — inline styles only,\n * never computed styles, so the editor cannot invent properties\n * • map the DOM selection to flat offsets over the plain projection (and\n * back), and keep the caret through a model-driven re-render\n * • hold re-renders during IME composition, paste as plain text, forward\n * Cmd/Ctrl+B/I/U as commands\n *\n * The browser owns typing; this module only translates. The POLICY — what a\n * command does to the document, when to commit, what a family maps to —\n * belongs to the host (the annotation plugin), reached through the\n * structural {@link RichTextEditorHost}: @embedpdf/web imports no plugin,\n * per the layering law. The return shape is a Svelte action; the other\n * frameworks call the same two functions from their effects.\n *\n * Offsets: positions in the plain projection — paragraphs joined by `\\r`,\n * runs concatenated, a hard break inside a run is a `\\r` too. The same\n * arithmetic the core's run algebra uses.\n *\n * Known limits (by design, recorded in the plan): the browser's native undo\n * stack does not span a host-driven restyle; DOM line wrapping approximates\n * the engine's layout, the appearance stream is the truth.\n */\n\nimport { observeWebFonts } from './web-font';\nimport {\n firstLineShiftFor,\n lineModelFor,\n webFontMetrics,\n type WebFontMetrics,\n} from './web-font-metrics';\n\nexport type RichTextEditorDecoration = 'underline' | 'line-through' | 'word';\nexport type RichTextEditorScript = 'normal' | 'sub' | 'super';\nexport type RichTextEditorAlign = 'left' | 'center' | 'right' | 'justify';\n\n/** A run's style delta — the engine's `RichTextRunStyle`, structurally. */\nexport interface RichTextEditorStyle {\n family?: string;\n weight?: number;\n italic?: boolean;\n size?: number;\n color?: string;\n decoration?: RichTextEditorDecoration[];\n script?: RichTextEditorScript;\n letterSpacing?: number;\n horizontalScale?: number;\n unknown?: string;\n}\n\nexport interface RichTextEditorRun {\n text: string;\n style?: RichTextEditorStyle;\n}\n\nexport interface RichTextEditorParagraph {\n align?: RichTextEditorAlign;\n dir?: 'ltr' | 'rtl';\n runs: RichTextEditorRun[];\n}\n\n/** The document the editor renders and produces (the engine's input shape). */\nexport interface RichTextEditorDocument {\n paragraphs: RichTextEditorParagraph[];\n}\n\nexport interface RichTextEditorRange {\n start: number;\n end: number;\n}\n\nexport type RichTextEditorCommand = 'bold' | 'italic' | 'underline';\n\n/** What the binding needs from the plugin — structural, no plugin import. */\nexport interface RichTextEditorHost {\n /** The element's content changed by typing: here is the serialised document. */\n onInput(doc: RichTextEditorDocument): void;\n /** The selection inside the editor moved (null: the editor has none). */\n onSelectionChange(range: RichTextEditorRange | null): void;\n /** A keyboard command the host applies to the current selection. */\n onCommand(command: RichTextEditorCommand): void;\n /** The CSS family list for a PDF family (\"Helvetica\" → a web stack, a\n * registered family → itself, mounted as a @font-face). */\n cssFontFamily(family: string): string;\n}\n\nexport interface RichTextEditorProps {\n document: RichTextEditorDocument;\n /** Screen px per content unit (the page scale): sizes are content points. */\n scale: number;\n}\n\nexport interface RichTextEditorBinding {\n update(props: RichTextEditorProps): void;\n /** The current selection as flat offsets, or null when not in the editor. */\n selection(): RichTextEditorRange | null;\n /** Place the selection (after the host restyled the document). */\n select(range: RichTextEditorRange): void;\n detach(): void;\n}\n\n// ---- Minimal DOM shapes (so the pure parts test with fake nodes) ---------------\n\nconst ELEMENT_NODE = 1;\nconst TEXT_NODE = 3;\n\nexport interface EditorStyle {\n marginTop?: string;\n lineHeight?: string;\n fontWeight: string;\n fontStyle: string;\n textDecoration: string;\n textDecorationLine?: string;\n color: string;\n fontSize: string;\n fontFamily: string;\n verticalAlign: string;\n letterSpacing: string;\n textAlign: string;\n direction: string;\n}\n\nexport interface EditorNode {\n nodeType: number;\n nodeName: string;\n nodeValue?: string | null;\n childNodes: ArrayLike<EditorNode>;\n parentNode?: EditorNode | null;\n style?: EditorStyle;\n}\n\nexport interface EditorElement extends EditorNode {\n style: EditorStyle;\n appendChild(node: EditorNode): unknown;\n}\n\nexport interface EditorDocumentFactory {\n createElement(tag: string): EditorElement;\n createTextNode(text: string): EditorNode;\n}\n\nexport interface EditorRoot extends EditorNode {\n ownerDocument: EditorDocumentFactory | null;\n appendChild(node: EditorNode): unknown;\n replaceChildren?(...nodes: EditorNode[]): void;\n removeChild?(node: EditorNode): unknown;\n firstChild?: EditorNode | null;\n}\n\nfunction isBlock(node: EditorNode): boolean {\n if (node.nodeType !== ELEMENT_NODE) return false;\n const name = node.nodeName.toUpperCase();\n return name === 'DIV' || name === 'P';\n}\n\nfunction isBreak(node: EditorNode): boolean {\n return node.nodeType === ELEMENT_NODE && node.nodeName.toUpperCase() === 'BR';\n}\n\nfunction lastChild(node: EditorNode): EditorNode | undefined {\n return node.childNodes[node.childNodes.length - 1];\n}\n\n/**\n * A `<br>` that nothing follows inside its block (or the root) is the\n * browser's caret placeholder, not content — even wrapped in a span, as\n * Chrome does for a new styled line. Every other break is a hard line break.\n */\nfunction isTrailingBreak(node: EditorNode, root: EditorNode): boolean {\n for (let n: EditorNode = node; ; ) {\n const parent = n.parentNode;\n if (!parent) return false;\n if (lastChild(parent) !== n) return false;\n if (parent === root || isBlock(parent)) return true;\n n = parent;\n }\n}\n\nfunction contains(root: EditorNode, node: EditorNode | null | undefined): boolean {\n for (let n = node; n; n = n.parentNode) if (n === root) return true;\n return false;\n}\n\n// ---- Style ↔ CSS ---------------------------------------------------------------\n\nconst HEX = /^#([0-9a-f]{6})$/i;\nconst RGB = /^rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)/i;\n\nfunction cssColorToHex(css: string): string | undefined {\n const trimmed = css.trim();\n if (HEX.test(trimmed)) return trimmed.toUpperCase();\n const m = RGB.exec(trimmed);\n if (!m) return undefined;\n const hex = (n: string) =>\n Math.max(0, Math.min(255, Number(n)))\n .toString(16)\n .padStart(2, '0');\n return `#${hex(m[1]!)}${hex(m[2]!)}${hex(m[3]!)}`.toUpperCase();\n}\n\nfunction firstFamily(css: string): string | undefined {\n const first = css\n .split(',')[0]\n ?.trim()\n .replace(/^[\"']|[\"']$/g, '');\n return first ? first : undefined;\n}\n\nfunction round2(n: number): number {\n return Math.round(n * 100) / 100;\n}\n\n/** The inline style declarations a span carries, as a style delta. Only what\n * the span itself sets: an unset property means \"inherit\", never a value. */\nexport function styleDeltaOf(style: EditorStyle | undefined, scale: number): RichTextEditorStyle {\n const delta: RichTextEditorStyle = {};\n if (!style) return delta;\n const weight = style.fontWeight;\n if (weight) {\n const parsed = weight === 'bold' ? 700 : weight === 'normal' ? 400 : Number(weight);\n if (Number.isFinite(parsed) && parsed > 0) delta.weight = parsed;\n }\n if (style.fontStyle) delta.italic = style.fontStyle === 'italic' || style.fontStyle === 'oblique';\n const decoration = style.textDecorationLine || style.textDecoration;\n if (decoration) {\n delta.decoration = decoration\n .split(/\\s+/)\n .filter((p): p is 'underline' | 'line-through' => p === 'underline' || p === 'line-through');\n }\n if (style.color) {\n const hex = cssColorToHex(style.color);\n if (hex) delta.color = hex;\n }\n const script = style.verticalAlign;\n if (script === 'sub' || script === 'super') delta.script = script;\n if (style.fontSize && !delta.script) {\n const px = parseFloat(style.fontSize);\n if (Number.isFinite(px) && scale > 0) delta.size = round2(px / scale);\n }\n if (style.fontFamily) {\n const family = firstFamily(style.fontFamily);\n if (family) delta.family = family;\n }\n if (style.letterSpacing) {\n const px = parseFloat(style.letterSpacing);\n if (Number.isFinite(px) && scale > 0) delta.letterSpacing = round2(px / scale);\n }\n return delta;\n}\n\nfunction applyStyleDelta(\n style: EditorStyle,\n delta: RichTextEditorStyle,\n scale: number,\n cssFontFamily: (family: string) => string,\n): void {\n if (delta.weight !== undefined) style.fontWeight = String(delta.weight);\n if (delta.italic !== undefined) style.fontStyle = delta.italic ? 'italic' : 'normal';\n if (delta.decoration !== undefined) {\n const lines = delta.decoration.map((d) => (d === 'word' ? 'underline' : d));\n style.textDecoration = lines.length ? [...new Set(lines)].join(' ') : 'none';\n }\n if (delta.color !== undefined) style.color = delta.color;\n if (delta.script !== undefined && delta.script !== 'normal') {\n // The engine's sub/superscript ratio; the size itself stays inherited.\n style.verticalAlign = delta.script;\n style.fontSize = '0.66em';\n } else if (delta.size !== undefined) {\n style.fontSize = `${delta.size * scale}px`;\n }\n if (delta.family !== undefined) style.fontFamily = cssFontFamily(delta.family);\n if (delta.letterSpacing !== undefined) style.letterSpacing = `${delta.letterSpacing * scale}px`;\n}\n\nfunction mergeDelta(base: RichTextEditorStyle, over: RichTextEditorStyle): RichTextEditorStyle {\n return { ...base, ...over };\n}\n\nfunction sameDelta(\n a: RichTextEditorStyle | undefined,\n b: RichTextEditorStyle | undefined,\n): boolean {\n const da = (a ?? {}) as Record<string, unknown>;\n const db = (b ?? {}) as Record<string, unknown>;\n const keys = new Set([...Object.keys(da), ...Object.keys(db)]);\n for (const key of keys) {\n const va = da[key];\n const vb = db[key];\n if (Array.isArray(va) || Array.isArray(vb)) {\n const sa = [...((va as string[] | undefined) ?? [])].sort().join(' ');\n const sb = [...((vb as string[] | undefined) ?? [])].sort().join(' ');\n if (sa !== sb) return false;\n } else if (va !== vb) {\n return false;\n }\n }\n return true;\n}\n\n// ---- Render -------------------------------------------------------------------\n\n/** Render a document into the element: one block per paragraph, one span per\n * styled run, `<br>` for a hard break, a placeholder `<br>` at the end of a\n * block that is empty or ends on a break so the caret has a home. */\nexport function renderRichText(\n root: EditorRoot,\n doc: RichTextEditorDocument,\n options: { scale: number; cssFontFamily: (family: string) => string },\n): void {\n const factory = root.ownerDocument;\n if (!factory) return;\n const blocks: EditorElement[] = [];\n const paragraphs = doc.paragraphs.length ? doc.paragraphs : [{ runs: [{ text: '' }] }];\n for (const paragraph of paragraphs) {\n const block = factory.createElement('div');\n if (paragraph.align) block.style.textAlign = paragraph.align;\n if (paragraph.dir) block.style.direction = paragraph.dir;\n let endsWithBreak = false;\n for (const run of paragraph.runs) {\n // A styled run keeps its hard breaks inside its span, so the break\n // inherits the run's style and serialises back into the same run.\n let container: EditorElement = block;\n if (run.style && Object.keys(run.style).length) {\n container = factory.createElement('span');\n applyStyleDelta(container.style, run.style, options.scale, options.cssFontFamily);\n block.appendChild(container);\n }\n const pieces = run.text.split(/\\r\\n|\\r|\\n/);\n for (let i = 0; i < pieces.length; i++) {\n if (i > 0) {\n container.appendChild(factory.createElement('br'));\n endsWithBreak = true;\n }\n const piece = pieces[i]!;\n if (!piece) continue;\n container.appendChild(factory.createTextNode(piece));\n endsWithBreak = false;\n }\n }\n if (endsWithBreak || !lastChild(block)) block.appendChild(factory.createElement('br'));\n blocks.push(block);\n }\n if (root.replaceChildren) {\n root.replaceChildren(...blocks);\n } else {\n while (root.firstChild && root.removeChild) root.removeChild(root.firstChild);\n for (const block of blocks) root.appendChild(block);\n }\n}\n\n/** Keep the baseline adjustment on the first block after Enter clones it. */\nexport function applyFirstLineShift(root: EditorNode, shift: number | undefined): void {\n for (let i = 0; i < root.childNodes.length; i++) {\n const node = root.childNodes[i]!;\n if (!node.style) continue;\n node.style.marginTop = i === 0 && shift ? `${-shift}px` : '';\n }\n}\n\n// ---- Serialise ----------------------------------------------------------------\n\ninterface Walk {\n root: EditorNode;\n scale: number;\n paragraphs: RichTextEditorParagraph[];\n current: RichTextEditorParagraph | null;\n}\n\nfunction pushText(walk: Walk, text: string, style: RichTextEditorStyle): void {\n if (!walk.current) {\n walk.current = { runs: [] };\n walk.paragraphs.push(walk.current);\n }\n const runs = walk.current.runs;\n const delta = Object.keys(style).length ? style : undefined;\n const last = runs[runs.length - 1];\n if (last && sameDelta(last.style, delta)) {\n last.text += text;\n } else {\n runs.push(delta ? { text, style: delta } : { text });\n }\n}\n\nfunction walkNode(node: EditorNode, inherited: RichTextEditorStyle, walk: Walk): void {\n if (node.nodeType === TEXT_NODE) {\n const text = (node.nodeValue ?? '').replace(/\\r\\n|\\n/g, '\\r');\n if (text) pushText(walk, text, inherited);\n return;\n }\n if (node.nodeType !== ELEMENT_NODE) return;\n if (isBreak(node)) {\n if (!isTrailingBreak(node, walk.root)) pushText(walk, '\\r', inherited);\n return;\n }\n const name = node.nodeName.toUpperCase();\n let style = inherited;\n if (name === 'B' || name === 'STRONG') style = mergeDelta(style, { weight: 700 });\n if (name === 'I' || name === 'EM') style = mergeDelta(style, { italic: true });\n if (name === 'U') style = mergeDelta(style, { decoration: ['underline'] });\n if (name === 'SUB') style = mergeDelta(style, { script: 'sub' });\n if (name === 'SUP') style = mergeDelta(style, { script: 'super' });\n style = mergeDelta(style, styleDeltaOf(node.style, walk.scale));\n\n if (isBlock(node)) {\n // A block starts a paragraph: the top-level ones by design, nested ones\n // because the browser wrapped a line during an edit.\n const paragraph: RichTextEditorParagraph = { runs: [] };\n const align = node.style?.textAlign;\n if (align === 'left' || align === 'center' || align === 'right' || align === 'justify') {\n paragraph.align = align;\n }\n if (node.style?.direction === 'rtl') paragraph.dir = 'rtl';\n walk.paragraphs.push(paragraph);\n walk.current = paragraph;\n for (let i = 0; i < node.childNodes.length; i++) walkNode(node.childNodes[i]!, style, walk);\n walk.current = null; // inline content after a block starts a new paragraph\n return;\n }\n for (let i = 0; i < node.childNodes.length; i++) walkNode(node.childNodes[i]!, style, walk);\n}\n\n/** The element's content as a document. Inline styles (and the semantic\n * b/i/u/sub/sup tags a browser may insert) only. */\nexport function serializeRichText(root: EditorNode, scale: number): RichTextEditorDocument {\n const walk: Walk = { root, scale, paragraphs: [], current: null };\n for (let i = 0; i < root.childNodes.length; i++) walkNode(root.childNodes[i]!, {}, walk);\n for (const paragraph of walk.paragraphs) {\n if (paragraph.runs.length === 0) paragraph.runs.push({ text: '' });\n }\n if (walk.paragraphs.length === 0) walk.paragraphs.push({ runs: [{ text: '' }] });\n return { paragraphs: walk.paragraphs };\n}\n\n// ---- Offsets ------------------------------------------------------------------\n\nexport interface EditorPosition {\n node: EditorNode;\n offset: number;\n}\n\ninterface OffsetEvent {\n kind: 'enter' | 'exit' | 'text' | 'break';\n node: EditorNode;\n /** Flat offset where this node's content starts (text/break) or where the\n * element begins/ends. */\n at: number;\n length: number;\n}\n\n/** The element in document order, each node with its flat offset — counted\n * like the projection: text length, 1 per hard break, 1 between blocks. */\nfunction collectOffsets(root: EditorNode): OffsetEvent[] {\n const events: OffsetEvent[] = [];\n let offset = 0;\n let blocks = 0;\n const visit = (node: EditorNode): void => {\n if (node.nodeType === TEXT_NODE) {\n const length = (node.nodeValue ?? '').length;\n events.push({ kind: 'text', node, at: offset, length });\n offset += length;\n return;\n }\n if (node.nodeType !== ELEMENT_NODE) return;\n if (isBreak(node)) {\n if (isTrailingBreak(node, root)) return;\n events.push({ kind: 'break', node, at: offset, length: 1 });\n offset += 1;\n return;\n }\n if (isBlock(node)) {\n if (blocks > 0) offset += 1; // the paragraph separator\n blocks++;\n }\n events.push({ kind: 'enter', node, at: offset, length: 0 });\n for (let i = 0; i < node.childNodes.length; i++) visit(node.childNodes[i]!);\n events.push({ kind: 'exit', node, at: offset, length: 0 });\n };\n for (let i = 0; i < root.childNodes.length; i++) visit(root.childNodes[i]!);\n return events;\n}\n\n/** The flat offset of a DOM position (a node and an offset within it). */\nexport function offsetOfPosition(root: EditorNode, position: EditorPosition): number {\n if (!contains(root, position.node)) return 0;\n const events = collectOffsets(root);\n if (position.node.nodeType === TEXT_NODE) {\n const event = events.find((e) => e.kind === 'text' && e.node === position.node);\n return event ? event.at + Math.min(position.offset, event.length) : 0;\n }\n if (isBreak(position.node)) {\n const event = events.find((e) => e.kind === 'break' && e.node === position.node);\n if (event) return event.at + (position.offset > 0 ? 1 : 0);\n const exit = events.find((e) => e.kind === 'exit' && e.node === position.node.parentNode);\n return exit ? exit.at : 0;\n }\n // An element position: before its Nth child, or past its last child.\n const child = position.node.childNodes[position.offset];\n if (child) {\n const event = events.find(\n (e) => e.node === child || (e.kind !== 'exit' && contains(child, e.node)),\n );\n if (event) return event.at;\n }\n if (position.node === root) {\n const last = events[events.length - 1];\n return last ? last.at + last.length : 0;\n }\n const exit = events.find((e) => e.kind === 'exit' && e.node === position.node);\n return exit ? exit.at : 0;\n}\n\nfunction indexIn(parent: EditorNode, node: EditorNode): number {\n for (let i = 0; i < parent.childNodes.length; i++) if (parent.childNodes[i] === node) return i;\n return 0;\n}\n\n/** The DOM position of a flat offset: inside a text node when one covers it,\n * else the empty block / the slot after a hard break the offset names. */\nexport function positionOfOffset(root: EditorNode, offset: number): EditorPosition | null {\n const events = collectOffsets(root);\n const text = events.find((e) => e.kind === 'text' && offset >= e.at && offset <= e.at + e.length);\n if (text) return { node: text.node, offset: offset - text.at };\n const block = events.find((e) => e.kind === 'enter' && isBlock(e.node) && e.at === offset);\n if (block) return { node: block.node, offset: 0 };\n const after = events.find((e) => e.kind === 'break' && e.at + 1 === offset);\n if (after && after.node.parentNode) {\n return { node: after.node.parentNode, offset: indexIn(after.node.parentNode, after.node) + 1 };\n }\n for (let i = events.length - 1; i >= 0; i--) {\n const e = events[i]!;\n if (e.kind === 'text') return { node: e.node, offset: e.length };\n }\n const first = root.childNodes[0];\n return first ? { node: first, offset: 0 } : null;\n}\n\n// ---- The binding --------------------------------------------------------------\n\n/** What the binding reads off a live DOM range (a `Range` or a `StaticRange`). */\ninterface RangeLike {\n startContainer: Node;\n startOffset: number;\n endContainer: Node;\n endOffset: number;\n}\n\n/** The selection's range as the shadow tree sees it: the standard\n * `getComposedRanges` (options form, then the earlier positional form). */\nfunction composedRange(sel: Selection, shadowRoot: ShadowRoot): RangeLike | null {\n const s = sel as Selection & { getComposedRanges?: (...args: unknown[]) => RangeLike[] };\n if (typeof s.getComposedRanges !== 'function') return null;\n for (const args of [[{ shadowRoots: [shadowRoot] }], [shadowRoot]]) {\n try {\n const ranges = s.getComposedRanges(...args);\n if (ranges.length) return ranges[0]!;\n } catch {\n // an engine with the other signature\n }\n }\n return null;\n}\n\nfunction documentsEqual(a: RichTextEditorDocument, b: RichTextEditorDocument): boolean {\n if (a.paragraphs.length !== b.paragraphs.length) return false;\n for (let i = 0; i < a.paragraphs.length; i++) {\n const pa = a.paragraphs[i]!;\n const pb = b.paragraphs[i]!;\n if ((pa.align ?? 'left') !== (pb.align ?? 'left') || (pa.dir ?? 'ltr') !== (pb.dir ?? 'ltr')) {\n return false;\n }\n if (pa.runs.length !== pb.runs.length) return false;\n for (let j = 0; j < pa.runs.length; j++) {\n const ra = pa.runs[j]!;\n const rb = pb.runs[j]!;\n if (ra.text !== rb.text || !sameDelta(ra.style, rb.style)) return false;\n }\n }\n return true;\n}\n\n/** Bind one contentEditable element. Returns the Svelte-action-shaped handle. */\nexport function attachRichTextEditor(\n el: HTMLElement,\n host: RichTextEditorHost,\n initial: RichTextEditorProps,\n): RichTextEditorBinding {\n const root = el as unknown as EditorRoot;\n const doc = el.ownerDocument;\n // Inside a shadow tree (a custom-element viewer) the document's own\n // `activeElement` and selection are retargeted to the HOST: focus and the\n // selection have to be read from the shadow root instead.\n const rootNode = el.getRootNode() as Document | ShadowRoot;\n const activeElement = (): Element | null => rootNode.activeElement;\n const liveRange = (): RangeLike | null => {\n const sel = doc.getSelection();\n if (!sel) return null;\n if (rootNode !== doc) {\n const composed = composedRange(sel, rootNode as ShadowRoot);\n if (composed) return composed;\n const shadowSel = (\n rootNode as ShadowRoot & { getSelection?(): Selection | null }\n ).getSelection?.();\n if (shadowSel && shadowSel.rangeCount > 0) return shadowSel.getRangeAt(0);\n }\n return sel.rangeCount > 0 ? sel.getRangeAt(0) : null;\n };\n let props = initial;\n let rendered: RichTextEditorDocument | null = null;\n let composing = false;\n\n const currentSelection = (): RichTextEditorRange | null => {\n const range = liveRange();\n if (!range) return null;\n const anchor = range.startContainer as unknown as EditorNode;\n const focus = range.endContainer as unknown as EditorNode;\n if (!contains(root, anchor) || !contains(root, focus)) return null;\n const start = offsetOfPosition(root, { node: anchor, offset: range.startOffset });\n const end = offsetOfPosition(root, { node: focus, offset: range.endOffset });\n return { start: Math.min(start, end), end: Math.max(start, end) };\n };\n\n const select = (range: RichTextEditorRange) => {\n const sel = doc.getSelection();\n if (!sel) return;\n const start = positionOfOffset(root, range.start);\n const end = positionOfOffset(root, range.end);\n if (!start || !end) return;\n // `setBaseAndExtent` places a selection inside a shadow tree in every\n // engine; a Range added from outside is retargeted to the host in some.\n if (typeof sel.setBaseAndExtent === 'function') {\n sel.setBaseAndExtent(\n start.node as unknown as Node,\n start.offset,\n end.node as unknown as Node,\n end.offset,\n );\n return;\n }\n const domRange = doc.createRange();\n domRange.setStart(start.node as unknown as Node, start.offset);\n domRange.setEnd(end.node as unknown as Node, end.offset);\n sel.removeAllRanges();\n sel.addRange(domRange);\n };\n\n // Read resolved faces in the element's own document (including iframe\n // fonts). Refresh styles without replacing nodes, selection or composition.\n const refreshLineModel = () => {\n const bodyStyle = doc.defaultView?.getComputedStyle(el) ?? el.style;\n const body = {\n family: bodyStyle.fontFamily || 'Helvetica',\n weight: bodyStyle.fontWeight || '400',\n style: bodyStyle.fontStyle || 'normal',\n };\n const size = parseFloat(bodyStyle.fontSize || '16') || 16;\n // Deduplicate measurements only within this synchronous refresh. They\n // must be measured again after fonts or their descriptors change.\n const measured = new Map<string, WebFontMetrics | null>();\n const measureFor = (face: typeof body) => (family: string) => {\n const key = JSON.stringify([family, face.weight, face.style]);\n if (!measured.has(key)) measured.set(key, webFontMetrics(family, doc, face));\n return measured.get(key)!;\n };\n // Read all computed styles before writing line heights to avoid forcing\n // a style recalculation for each run. Native editing may nest spans.\n const runs = Array.from(el.querySelectorAll<HTMLElement>('span, b, strong, i, em')).map(\n (node) => {\n const style = doc.defaultView?.getComputedStyle(node) ?? node.style;\n const face = {\n family: style.fontFamily || body.family,\n weight: style.fontWeight || body.weight,\n style: style.fontStyle || body.style,\n };\n return { node, lineHeight: lineModelFor(face.family, measureFor(face)).lineHeight };\n },\n );\n const measure = measureFor(body);\n const lineHeight = lineModelFor(body.family, measure).lineHeight;\n const shift = firstLineShiftFor(body.family, size, measure);\n el.style.lineHeight = String(lineHeight);\n for (const run of runs) run.node.style.lineHeight = String(run.lineHeight);\n applyFirstLineShift(root, shift);\n };\n\n const render = (document: RichTextEditorDocument, keepSelection: boolean) => {\n const selection = keepSelection ? currentSelection() : null;\n renderRichText(root, document, {\n scale: props.scale,\n cssFontFamily: host.cssFontFamily,\n });\n refreshLineModel();\n rendered = document;\n if (selection) select(selection);\n };\n\n const serialiseAndReport = () => {\n // Enter splits a block by CLONING its style attribute, so the first\n // block's shift would ride onto the new paragraph: keep it on the first\n // block alone, whatever the browser produced.\n refreshLineModel();\n const document = serializeRichText(root, props.scale);\n rendered = document;\n host.onInput(document);\n };\n\n const onInput = () => {\n if (!composing) serialiseAndReport();\n };\n const onCompositionStart = () => {\n composing = true;\n };\n const onCompositionEnd = () => {\n composing = false;\n serialiseAndReport();\n };\n const onKeyDown = (e: KeyboardEvent) => {\n if (!(e.metaKey || e.ctrlKey) || e.altKey) return;\n const key = e.key.toLowerCase();\n const command: RichTextEditorCommand | null =\n key === 'b' ? 'bold' : key === 'i' ? 'italic' : key === 'u' ? 'underline' : null;\n if (!command) return;\n e.preventDefault();\n host.onCommand(command);\n };\n const onPaste = (e: ClipboardEvent) => {\n e.preventDefault();\n const text = e.clipboardData?.getData('text/plain') ?? '';\n // execCommand keeps the browser's own undo stack and caret handling for\n // the insertion; the resulting `input` event serialises as usual.\n doc.execCommand('insertText', false, text);\n };\n const onSelectionChange = () => {\n if (activeElement() !== el) return;\n host.onSelectionChange(currentSelection());\n };\n\n el.addEventListener('input', onInput);\n el.addEventListener('compositionstart', onCompositionStart);\n el.addEventListener('compositionend', onCompositionEnd);\n el.addEventListener('keydown', onKeyDown);\n el.addEventListener('paste', onPaste);\n doc.addEventListener('selectionchange', onSelectionChange);\n\n render(initial.document, false);\n const stopObservingFonts = observeWebFonts(doc, refreshLineModel);\n\n return {\n update(next: RichTextEditorProps) {\n const scaleChanged = next.scale !== props.scale;\n props = next;\n // Our own input echoes back through the host unchanged: no re-render,\n // the caret stays where the browser put it. Anything else (a restyle,\n // a remote edit, a scale change) re-renders and restores the caret.\n if (composing) return;\n if (!rendered || scaleChanged || !documentsEqual(next.document, rendered)) {\n render(next.document, activeElement() === el);\n return;\n }\n // The element's font may have changed under an unchanged document (a\n // body restyle): the line model follows it without touching the DOM text.\n refreshLineModel();\n },\n selection: currentSelection,\n select,\n detach() {\n stopObservingFonts();\n el.removeEventListener('input', onInput);\n el.removeEventListener('compositionstart', onCompositionStart);\n el.removeEventListener('compositionend', onCompositionEnd);\n el.removeEventListener('keydown', onKeyDown);\n el.removeEventListener('paste', onPaste);\n doc.removeEventListener('selectionchange', onSelectionChange);\n },\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["webkitWindow","firstFamily"],"sources":["../src/file-picker.ts","../src/clock.ts","../src/css-text.ts","../src/focusable.ts","../src/text-field-event.ts","../src/clipboard.ts","../src/command-shortcuts.ts","../src/anchored-position.ts","../src/annotation-frame.ts","../src/cursor.ts","../src/external-uri.ts","../src/actions-ui.ts","../src/painted-image.ts","../src/feedback.ts","../src/wheel.ts","../src/stage-gestures.ts","../src/selection-handles.ts","../src/stage-surface.ts","../src/byte-store.ts","../src/web-font.ts","../src/web-font-metrics.ts","../src/rich-text-editor.ts","../src/picture-or-painting.ts","../src/theme.ts","../src/page-pixels.ts","../src/event-isolation.ts","../src/outside-press.ts","../src/anchor-equality.ts","../src/annotation-scene.ts","../src/annotation-chrome.ts","../src/text-box-editor.ts","../src/annotation-renderers.ts","../src/object-urls.ts","../src/comment-threads.ts","../src/form-field.ts","../src/form-styles.ts","../src/link-anchor.ts","../src/link-events.ts","../src/search-highlights.ts","../src/press-click.ts","../src/page-projector.ts","../src/page-context.ts","../src/page-layers.ts","../src/page-surface.ts","../src/page-pointer.ts","../src/scrollbar.ts","../src/toolbar-measure.ts","../src/orientation.ts","../src/roving-focus.ts","../src/toolbar-focus.ts","../src/menu-keys.ts","../src/focus-trap.ts","../src/surface-focus.ts","../src/standard-commands-platform.ts"],"sourcesContent":["/**\n * A one-shot file dialog. Opens a throwaway `<input type=file>`, resolves the\n * chosen `File`, or `null` when the dialog is dismissed. Must be called\n * synchronously from a user gesture (a click) or the browser refuses to open it.\n *\n * A `File` is a `Blob`, which is a `BinarySource` everywhere in the stack, so the\n * caller can hand the result straight to the engine — this module stays a pure\n * DOM utility with no EmbedPDF types. It also holds the default provider for\n * the annotation plugin's file prompt ({@link pickRequestedFile}), and its\n * one install per document ({@link installFilePickerProvider}).\n */\nconst IMAGE_ACCEPT = 'image/png,image/jpeg,application/pdf';\n\nexport interface PickFileOptions {\n /** The dialog's `accept` filter. Defaults to PNG / JPEG / PDF. */\n accept?: string;\n /** Allow multiple selection (returns the first file). Defaults to false. */\n multiple?: boolean;\n}\n\n/** Open the file dialog; resolve the picked file, or null if cancelled. */\nexport function pickImageFile(options: PickFileOptions = {}): Promise<File | null> {\n const { accept = IMAGE_ACCEPT, multiple = false } = options;\n return new Promise((resolve) => {\n const input = document.createElement('input');\n input.type = 'file';\n input.accept = accept;\n input.multiple = multiple;\n input.style.display = 'none';\n\n let settled = false;\n const finish = (value: File | null): void => {\n if (settled) return;\n settled = true;\n input.remove();\n resolve(value);\n };\n\n // The dialog says how it closed: `change` with the picked file, or `cancel`, which every\n // browser the viewer runs in fires. Nothing else is a sign: the window can get its focus back\n // before the picked file arrives (a file in iCloud Drive or on a network drive, an app's\n // embedded browser), and taking that for a cancel would drop the user's pick.\n input.addEventListener('change', () => finish(input.files?.[0] ?? null), { once: true });\n input.addEventListener('cancel', () => finish(null), { once: true });\n\n document.body.appendChild(input);\n input.click();\n });\n}\n\n/** The same dialog with no type filter — any file (attachments). */\nexport const pickFile = (options: PickFileOptions = {}): Promise<File | null> =>\n pickImageFile({ accept: '*/*', ...options });\n\n/**\n * Hand bytes to the user as a browser download (a one-shot `<a download>`).\n * The DOM half of \"download this attachment\" — pair with the annotation\n * capability's `downloadAttachment` or the document `attachments.download`.\n */\nexport function saveFile(bytes: Uint8Array | Blob, name: string, mimeType?: string): void {\n const blob =\n bytes instanceof Blob\n ? bytes\n : new Blob([bytes as BlobPart], { type: mimeType ?? 'application/octet-stream' });\n const url = URL.createObjectURL(blob);\n const anchor = document.createElement('a');\n anchor.href = url;\n anchor.download = name;\n anchor.style.display = 'none';\n document.body.appendChild(anchor);\n anchor.click();\n anchor.remove();\n setTimeout(() => URL.revokeObjectURL(url), 1000);\n}\n\n/**\n * The browser's default for a plugin's file prompt (the annotation plugin's\n * `FilePickerProvider`): the file dialog, honouring the request's `accept`\n * filter. A picked `File` carries its own name and type, so it goes straight\n * through as the engine's file source; a dismissed dialog answers `null`.\n */\nexport async function pickRequestedFile(request: {\n accept?: string | null;\n}): Promise<{ data: File } | null> {\n const file = await pickFile({ accept: request.accept ?? '*/*' });\n return file ? { data: file } : null;\n}\n\n/** Where a provider goes: the annotation plugin's port (its capability satisfies it). */\nexport interface FilePickerPort<Provider> {\n /** Install the provider, `null` to make click-then-pick tools do nothing. Returns its removal. */\n setFilePickerProvider(provider: Provider | null): () => void;\n}\n\n/** How many mounted installs each document's port has: one is the rule. */\nconst filePickerInstalls = new WeakMap<object, number>();\n\n/**\n * Install `provider` as a document's file picker, until the call it returns\n * removes it. A document has one port, so a second install while the first\n * is there silently replaces it: `onRepeat` hears of it (an adapter warns in\n * development, saying where to install it once).\n */\nexport function installFilePickerProvider<Provider>(\n annotation: FilePickerPort<Provider>,\n provider: Provider | null,\n onRepeat: () => void,\n): () => void {\n const installed = (filePickerInstalls.get(annotation) ?? 0) + 1;\n filePickerInstalls.set(annotation, installed);\n if (installed > 1) onRepeat();\n const remove = annotation.setFilePickerProvider(provider);\n return () => {\n filePickerInstalls.set(annotation, (filePickerInstalls.get(annotation) ?? 1) - 1);\n remove();\n };\n}\n","/**\n * The browser's time, for the kernel: `createKernel({ clock: browserClock() })`.\n * Timers, and frames from `requestAnimationFrame`, so plugins can animate and\n * merge their wake-ups into one per painted frame. Structurally `HostClock`\n * from `@embedpdf/core`, declared here so this package stays dependency-free.\n */\n\n/** Structural twin of `HostClock` (`@embedpdf/core`). */\nexport interface WebClock {\n now(): number;\n after(ms: number, run: () => void): () => void;\n nextFrame?(run: (timeMs: number) => void): () => void;\n}\n\n/**\n * The browser's clock. A page without `requestAnimationFrame` (some test\n * environments) gets a clock without frames, and plugins then skip animation.\n */\nexport function browserClock(): WebClock {\n const clock: WebClock = {\n now: () => performance.now(),\n after(ms, run) {\n const timer = setTimeout(run, ms);\n return () => clearTimeout(timer);\n },\n };\n if (typeof requestAnimationFrame === 'function') {\n clock.nextFrame = (run) => {\n const frame = requestAnimationFrame(run);\n return () => cancelAnimationFrame(frame);\n };\n }\n return clock;\n}\n","/**\n * Style records as CSS text. This package hands styles out as records with\n * camelCase names (`strokeWidth`, `fontFamily`) and lengths with their units,\n * the shape a style object takes in React, Vue and Angular. A framework whose\n * `style` is a string (Svelte) turns a record into CSS text with\n * {@link cssText}: `stroke-width: …; font-family: …`. Paint values are\n * `var(--epdf-…, …)`, which only work in `style`, never in an SVG\n * presentation attribute.\n */\n\n/** `strokeWidth` → `stroke-width`. */\nconst cssNameOf = (name: string): string =>\n name.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);\n\n/** A style record: CSS property names in camelCase, each a value or left out. */\nexport type StyleRecord<Style> = {\n readonly [Name in keyof Style]?: string | number | null;\n};\n\n/** A style record as CSS text, leaving out what is `undefined` or `null`. */\nexport function cssText<Style extends StyleRecord<Style>>(style: Style): string {\n return Object.entries(style as Record<string, string | number | null | undefined>)\n .filter((entry): entry is [string, string | number] => entry[1] != null)\n .map(([name, value]) => `${cssNameOf(name)}: ${value}`)\n .join('; ');\n}\n","/**\n * Which elements take keyboard focus, for the keyboard models (a toolbar's\n * stops, a menu's items, a dialog's tab order). Decided from the markup only,\n * never from layout, so it answers the same before the first paint: `hidden`,\n * `inert` and `disabled` count; a `display: none` from a stylesheet doesn't.\n */\n\n/** Elements that can take focus, before {@link canTakeFocus} rules out the disabled and the hidden. */\nconst FOCUSABLE_SELECTOR = [\n 'a[href]',\n 'area[href]',\n 'button',\n 'input:not([type=\"hidden\"])',\n 'select',\n 'textarea',\n 'iframe',\n 'summary',\n '[tabindex]',\n '[contenteditable]:not([contenteditable=\"false\"])',\n].join(', ');\n\n/** Form controls the `disabled` attribute takes focus from. */\nconst DISABLEABLE = new Set([\n 'BUTTON',\n 'INPUT',\n 'SELECT',\n 'TEXTAREA',\n 'FIELDSET',\n 'OPTGROUP',\n 'OPTION',\n]);\n\n/** Whether an element can take focus now: focusable markup, not disabled, not hidden or inert. */\nexport function canTakeFocus(element: Element): boolean {\n if (!element.matches(FOCUSABLE_SELECTOR)) return false;\n if (DISABLEABLE.has(element.tagName) && (element as HTMLButtonElement).disabled) return false;\n return element.closest('[hidden], [inert]') === null;\n}\n\n/** Whether Tab stops at an element: it takes focus and its `tabIndex` isn't negative. */\nexport function isTabbable(element: Element): element is HTMLElement {\n return canTakeFocus(element) && (element as HTMLElement).tabIndex >= 0;\n}\n\n/**\n * The elements `root` shows, in order: a `<slot>` that has elements assigned shows them, and what\n * is inside them, in place of its own content. A slot in a hidden or inert part shows nothing.\n */\nfunction shownElementsIn(root: ParentNode): Element[] {\n const shown: Element[] = [];\n const visit = (parent: ParentNode) => {\n for (const child of parent.children) {\n if (child.localName === 'slot') {\n const assigned = (child as HTMLSlotElement).assignedElements({ flatten: true });\n if (assigned.length > 0) {\n if (child.closest('[hidden], [inert]') !== null) continue;\n for (const element of assigned) {\n shown.push(element);\n visit(element);\n }\n continue;\n }\n }\n shown.push(child);\n visit(child);\n }\n };\n visit(root);\n return shown;\n}\n\n/**\n * The elements inside `root` that can take focus, in the order they show: through a `<slot>`,\n * the page's elements the browser shows there (a web component's slotted children).\n */\nexport function focusablesIn(root: ParentNode): HTMLElement[] {\n if (root.querySelector('slot') === null) {\n return [...root.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTOR)].filter(canTakeFocus);\n }\n return shownElementsIn(root).filter(\n (element): element is HTMLElement => element instanceof HTMLElement && canTakeFocus(element),\n );\n}\n\n/** The elements inside `root` that Tab stops at, in document order. */\nexport function tabbablesIn(root: ParentNode): HTMLElement[] {\n return focusablesIn(root).filter((element) => element.tabIndex >= 0);\n}\n\n/**\n * The element that has focus, looking into shadow roots: the document's\n * `activeElement` is only the outermost shadow host. Null when nothing has\n * focus (the body).\n */\nexport function deepActiveElementOf(doc: Document): Element | null {\n let active: Element | null = doc.activeElement;\n while (active?.shadowRoot?.activeElement) active = active.shadowRoot.activeElement;\n return active === doc.body ? null : active;\n}\n\n/**\n * Whether the element with focus is `container` or inside it, as the page shows it: through\n * shadow roots, and from an element slotted into a shadow root to its slot, so a control\n * slotted into a viewer counts as inside the viewer, as its keys do.\n */\nexport function containsFocus(container: Element): boolean {\n let node: Node | null = deepActiveElementOf(container.ownerDocument);\n while (node) {\n if (node === container) return true;\n node =\n (node as Partial<Element>).assignedSlot ??\n node.parentNode ??\n (node as Partial<ShadowRoot>).host ??\n null;\n }\n return false;\n}\n\n/**\n * Whether a control says it can't be used now, through `disabled` or\n * `aria-disabled=\"true\"`: the keyboard models pass over it.\n */\nexport function isUnavailable(element: Element): boolean {\n if (DISABLEABLE.has(element.tagName) && (element as HTMLButtonElement).disabled) return true;\n return element.getAttribute('aria-disabled') === 'true';\n}\n\n/** Input types that type text, so they keep Left, Right, Home and End for the caret. */\nconst TEXT_INPUT_TYPES = new Set(['', 'text', 'search', 'url', 'tel', 'email', 'password']);\n\n/** Input types that change their value with the arrow keys, both ways. */\nconst STEPPED_INPUT_TYPES = new Set([\n 'number',\n 'range',\n 'radio',\n 'date',\n 'datetime-local',\n 'month',\n 'time',\n 'week',\n]);\n\n/** Roles of widgets that move with the arrow keys themselves. */\nconst ARROW_ROLES = new Set([\n 'slider',\n 'spinbutton',\n 'combobox',\n 'textbox',\n 'listbox',\n 'tree',\n 'grid',\n]);\n\n/**\n * Whether a focused control uses `key` itself, so a keyboard model around it\n * must leave the key alone: a text field moves its caret with Left, Right,\n * Home and End; a select changes with Up and Down; a slider takes them all.\n */\nexport function usesKey(element: Element, key: string): boolean {\n const horizontal = key === 'ArrowLeft' || key === 'ArrowRight' || key === 'Home' || key === 'End';\n const vertical = key === 'ArrowUp' || key === 'ArrowDown';\n if (!horizontal && !vertical) return false;\n if ((element as HTMLElement).isContentEditable || element.tagName === 'TEXTAREA') return true;\n if (element.tagName === 'SELECT') return vertical;\n if (element.tagName === 'INPUT') {\n const type = (element.getAttribute('type') ?? '').toLowerCase();\n return STEPPED_INPUT_TYPES.has(type) || (TEXT_INPUT_TYPES.has(type) && horizontal);\n }\n const role = element.getAttribute('role');\n return role !== null && ARROW_ROLES.has(role);\n}\n","/** Telling a key or a copy meant for a text field from one meant for the viewer around it. */\n\n/**\n * Whether an event comes from a text field: an input, a text area, a select or editable\n * content. Read from the event's first target, so a field inside a shadow root counts too\n * (a listener on the document sees the shadow host as the target).\n */\nexport function isFromTextField(event: Event): boolean {\n const element = (event.composedPath?.()[0] ?? event.target) as HTMLElement | null;\n if (!element || !element.tagName) return false;\n return (\n element.tagName === 'INPUT' ||\n element.tagName === 'TEXTAREA' ||\n element.tagName === 'SELECT' ||\n element.isContentEditable\n );\n}\n","/**\n * Clipboard wiring for text selection — the environmental half of copy.\n *\n * The selection plugin is deliberately DOM-free: its public capability ends\n * at `readText()` (data). Everything that touches `navigator.clipboard` or\n * the native `copy` event lives here, structurally typed so this module\n * stays a pure DOM utility with no EmbedPDF dependencies (the selection\n * capability satisfies {@link ClipboardSelectionSource} as-is).\n */\nimport { containsFocus } from './focusable';\nimport { isFromTextField } from './text-field-event';\n\n/** The slice of the selection capability clipboard wiring needs. */\nexport interface ClipboardSelectionSource {\n hasSelection(): boolean;\n canCopy(): boolean;\n readText(): Promise<string>;\n onChanged(listener: () => void): () => void;\n onCommitted(listener: () => void): () => void;\n}\n\nexport interface SelectionClipboardOptions {\n /**\n * Prefetch the selected text when the selection settles (default true).\n * This is what makes the native copy path work: a `copy` event handler\n * must call `clipboardData.setData` synchronously — awaiting a page-text\n * read inside it is too late — and it also keeps the async path instant.\n * The fetch is one versioned, cached, permission-gated read per page;\n * pass false to make copying strictly on-demand.\n */\n prefetch?: boolean;\n /**\n * Whose keys and copies these are (default `document`, the whole page). An element takes\n * them only while focus is inside it, for a page with more than one viewer; a copy from the\n * browser's Edit menu included, which reaches the page's body when nothing is selected there.\n */\n target?: Document | HTMLElement;\n /**\n * Called when Ctrl/Cmd+C or a copy (Edit → Copy) meets a selection the user may not copy.\n * The browser's own copy is prevented either way; this is where to say so.\n */\n onRefused?: () => void;\n}\n\nconst PREFETCH_DEBOUNCE_MS = 150;\n\n/**\n * Wire clipboard copy for a selection. Two delivery paths, both fed by the\n * prefetched text:\n *\n * - the native `copy` event (menu Edit→Copy, or ctrl/cmd+C while the page\n * has a DOM selection): answered synchronously from the cache;\n * - ctrl/cmd+C with no DOM selection (canvas-rendered viewers usually\n * have none, and browsers don't dispatch `copy` then): a keydown\n * fallback writes via the async Clipboard API inside the keystroke's\n * user-activation window.\n *\n * A selection the user may not copy is never copied by either path, nor by\n * the browser: the key and the copy are prevented, and `onRefused` says so.\n * Keys and copies in a text field are the field's, and left alone. With an\n * element as `target`, only the keys and copies made while focus is inside it\n * are its own.\n *\n * Returns the unwire function. Mount once per document view (not per page).\n */\nexport function wireSelectionClipboard(\n selection: ClipboardSelectionSource,\n options: SelectionClipboardOptions = {},\n): () => void {\n const target = options.target ?? document;\n // The document hears every copy, Edit → Copy's among them, which goes to the body when the\n // page has no DOM selection (the viewer draws its own); `isOurs` keeps the target's own.\n const doc = target.ownerDocument ?? (target as Document);\n const isOurs = (): boolean => target === doc || containsFocus(target as HTMLElement);\n const prefetch = options.prefetch ?? true;\n let cached: string | null = null;\n let generation = 0;\n let timer: ReturnType<typeof setTimeout> | null = null;\n\n const refresh = (): void => {\n cached = null;\n if (!prefetch || !selection.hasSelection() || !selection.canCopy()) return;\n const gen = ++generation;\n selection.readText().then(\n (text) => {\n if (gen === generation) cached = text;\n },\n () => {}, // denied / failed — the async path will surface it if used\n );\n };\n\n // Commit (gesture end) refreshes immediately; change (drag, programmatic\n // select) debounces so a drag doesn't fetch per pointer move.\n const offCommit = selection.onCommitted(() => {\n if (timer) clearTimeout(timer);\n timer = null;\n refresh();\n });\n const offChange = selection.onChanged(() => {\n generation++;\n cached = null;\n if (timer) clearTimeout(timer);\n timer = setTimeout(refresh, PREFETCH_DEBOUNCE_MS);\n });\n\n /** Whether there is a selection the user may not copy: the copy is then refused. */\n const isRefused = (): boolean => selection.hasSelection() && !selection.canCopy();\n const refuse = (event: Event): void => {\n event.preventDefault();\n options.onRefused?.();\n };\n\n // A copy or a key while focus is elsewhere is someone else's; one in a text field (the\n // search box, a comment) is the field's own.\n const onCopy = (event: ClipboardEvent): void => {\n if (!isOurs() || isFromTextField(event)) return;\n if (isRefused()) return refuse(event);\n if (cached == null || cached === '' || !selection.hasSelection()) return;\n event.clipboardData?.setData('text/plain', cached);\n event.preventDefault();\n };\n\n const onKeyDown = (event: KeyboardEvent): void => {\n if (\n !(event.metaKey || event.ctrlKey) ||\n event.key.toLowerCase() !== 'c' ||\n event.shiftKey ||\n event.altKey ||\n // A command shortcut for the same key took it already.\n event.defaultPrevented ||\n !isOurs() ||\n isFromTextField(event)\n ) {\n return;\n }\n // Preventing the key also keeps the browser from dispatching `copy` for it.\n if (isRefused()) return refuse(event);\n if (!selection.hasSelection() || !selection.canCopy()) return;\n // A live DOM selection means the browser will dispatch `copy` itself and\n // our synchronous handler answers — don't double-write.\n const dom = domSelectionOf(target);\n if (dom && !dom.isCollapsed) return;\n void copySelection(selection); // instant when prefetched; still inside user activation\n };\n\n doc.addEventListener('copy', onCopy);\n doc.addEventListener('keydown', onKeyDown);\n return () => {\n offCommit();\n offChange();\n if (timer) clearTimeout(timer);\n doc.removeEventListener('copy', onCopy);\n doc.removeEventListener('keydown', onKeyDown);\n };\n}\n\n/**\n * The DOM selection the browser copies from: the target's root's (its shadow root, where the\n * browser keeps one there, or its document), else its owner document's.\n */\nfunction domSelectionOf(target: Document | HTMLElement): Selection | null {\n const root = target.getRootNode() as Partial<Pick<Document, 'getSelection'>>;\n if (typeof root.getSelection === 'function') return root.getSelection();\n return target.ownerDocument?.getSelection() ?? null;\n}\n\n/**\n * Toolbar/menu path: read the selected text and write it to the clipboard\n * via the async Clipboard API. Call from a user-gesture handler (browsers\n * require a secure context + user activation for clipboard writes). Resolves\n * with what was copied ('' when nothing was — nothing is written then, so an\n * empty selection never clobbers the user's clipboard).\n */\nexport async function copySelection(\n selection: Pick<ClipboardSelectionSource, 'readText'>,\n): Promise<string> {\n const text = await selection.readText();\n if (text !== '') await navigator.clipboard.writeText(text);\n return text;\n}\n","/**\n * Keyboard shortcuts for commands: one `keydown` listener runs the command a\n * key matches. Matching is the commands plugin's; what this module adds is the\n * browser's part: the platform, whether a key was typed into a text field, and\n * the default action a shortcut replaces. Structurally typed, so it needs no EmbedPDF\n * package (the commands host capability satisfies {@link ShortcutCommands}).\n */\nimport { isFromTextField } from './text-field-event';\n\n/** Why a matched command won't run whatever the moment, as the commands plugin says it. */\nexport interface ShortcutRefusal {\n /** Turned off in this viewer, or not allowed by the scope. */\n readonly reason: 'turned-off' | 'not-allowed';\n /** A sentence for the user. */\n readonly message: string;\n}\n\n/** The part of the commands capability shortcuts need. */\nexport interface ShortcutCommands {\n /**\n * The command a keystroke's shortcut names, with its refusal when it's refused, or `null`.\n * With `inTextField`, only a command that runs from text fields.\n */\n matchStroke(\n stroke: KeyboardEvent,\n options: { isMac: boolean; inTextField: boolean },\n ): { readonly commandId: string; readonly refusal: ShortcutRefusal | null } | null;\n canExecute(id: string): boolean;\n execute(id: string): Promise<unknown>;\n}\n\nexport interface CommandShortcutOptions {\n /** Whether `Mod` is Cmd (a Mac) or Ctrl. Default: what the browser says. */\n isMac?: boolean;\n /** Where to listen. Default: `window`. */\n target?: Pick<Window, 'addEventListener' | 'removeEventListener'>;\n /** Called when a key's command is turned off or not allowed, with why: say so to the user. */\n onRefused?: (commandId: string, refusal: ShortcutRefusal) => void;\n}\n\n/** Whether this is an Apple platform, where `Mod` is Cmd and shortcuts show as `⌘K`. */\nexport const isMacPlatform = (): boolean =>\n typeof navigator !== 'undefined' && /Mac|iP(hone|ad|od)/.test(navigator.platform);\n\n/**\n * Run a command when its shortcut is pressed, and return the function that stops listening.\n * Keys typed into a text field are left alone, except a command's that says it runs from text\n * fields (`shortcutInTextFields`, as Find does). A command that's turned off or not allowed\n * still takes its key, so the browser doesn't do something else with it (Cmd+P printing the\n * page), and `onRefused` says why. A key whose command only can't run right now keeps its\n * browser default.\n */\nexport function bindCommandShortcuts(\n commands: ShortcutCommands,\n options: CommandShortcutOptions = {},\n): () => void {\n const isMac = options.isMac ?? isMacPlatform();\n const target = options.target ?? window;\n const onKeyDown = (event: Event) => {\n const stroke = event as KeyboardEvent;\n if (stroke.defaultPrevented) return;\n const match = commands.matchStroke(stroke, { isMac, inTextField: isFromTextField(stroke) });\n if (!match) return;\n if (match.refusal) {\n stroke.preventDefault();\n options.onRefused?.(match.commandId, match.refusal);\n return;\n }\n if (!commands.canExecute(match.commandId)) return;\n stroke.preventDefault();\n // A failing command reports itself through the capability's onExecutionFailed.\n commands.execute(match.commandId).catch(() => {});\n };\n target.addEventListener('keydown', onKeyDown);\n return () => target.removeEventListener('keydown', onKeyDown);\n}\n","/**\n * Anchored-overlay geometry for UI floating over page content (selection\n * menus, draft menus, popovers): the projector snapshot contract, the\n * anchor shape, and the placement math. Pure values and pure functions over\n * structural rect/point shapes — no EmbedPDF types, no framework, no\n * reactive lifecycle — so every framework adapter (React, Vue, Svelte,\n * Angular) shares one implementation and binds it with its native\n * reactivity (context revision, signals, computed, $derived).\n *\n * The boundary laws:\n * - Placement policy lives here, above the projector seam: a projector\n * only knows geometry — never what is being positioned nor where it\n * prefers to sit.\n * - A projector describes the current projection; it never implements a\n * framework-style reactive lifecycle. State-driven changes (the Stage\n * camera) must reach consumers through the framework's own render\n * cycle so surface and overlay commit together; only genuinely\n * browser-driven changes (a PageView moving because the document\n * scrolled) use an external listener ({@link observeClientGeometry}).\n */\n\nimport type { PageRef } from './page-ref';\n\nexport interface AnchoredRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\nexport interface AnchoredPoint {\n x: number;\n y: number;\n}\n\n/** A side of the box. */\nexport type AnchoredSide = 'top' | 'right' | 'bottom' | 'left';\n\n/**\n * Where anchored UI sits: a side of the box, centred on it, or lined up with\n * the side's start or end (`'top-end'`: above the box, right edges lined up).\n */\nexport type AnchoredPlacement = AnchoredSide | `${AnchoredSide}-${'start' | 'end'}`;\n\n/** How anchored UI is placed: where, how far from the box, and whether it may move to fit. */\nexport interface AnchoredOptions {\n placement: AnchoredPlacement;\n /** Screen px between the box and the UI; negative overlaps the box. */\n gap: number;\n /** Stays where `placement` puts it: never flips, never moves to stay in view. */\n pinned?: boolean;\n}\n\n/** CSS-ready output: absolute/fixed `left`/`top` plus a centering transform. */\nexport interface AnchoredPosition {\n left: number;\n top: number;\n transform: string;\n /** Where it sits: the placement asked for, or its opposite side when that had no room. */\n placement: AnchoredPlacement;\n}\n\nexport interface AnchoredSize {\n width: number;\n height: number;\n}\n\n/**\n * What placement needs to keep anchored UI on screen: the element's size, and\n * the area it must stay inside, in the projector's space (the Stage's own box,\n * or the browser window).\n */\nexport interface AnchoredFit {\n size: AnchoredSize;\n view: AnchoredRect;\n}\n\n/** How a page is shown right now: its view scale, its rotation on screen, and its zoom (1 = 100%). */\nexport interface PageViewEnv {\n scale: number;\n rotation: 0 | 90 | 180 | 270;\n zoom: number;\n}\n\n/** What anchored UI attaches to: a page-space rect on a page, plus\n * optional page-space points the UI must clear (e.g. a rotate knob).\n * Structural — plugin anchor reads satisfy it without importing this\n * package. */\nexport interface AnchorTarget {\n page: PageRef;\n bounds: AnchoredRect;\n /**\n * Where the box is in a given view, for a box whose size or orientation\n * depends on it (a note that keeps its size on screen). The projection\n * calls it, so the anchor itself never has to follow the view.\n */\n boundsIn?: (view: PageViewEnv) => AnchoredRect | null;\n avoid?: AnchoredPoint[];\n}\n\n/**\n * A page surface's projection snapshot: how a page-space rect on a page\n * becomes screen coordinates, right now. Provided by `<Stage>`\n * (camera-driven, pure state, no DOM reads) and `<PageView>` (DOM-measured).\n * Deliberately no subscribe here — when projection changes is a framework\n * binding concern (see the module doc), not part of the snapshot.\n */\nexport interface ViewProjector {\n /**\n * Which coordinate space `toScreen` speaks, and therefore how anchored\n * content mounts:\n * - 'overlay': coords are relative to the surface's own positioned\n * container → rendered in place, position:absolute. (Stage)\n * - 'client': coords are viewport px → portalled to the document body,\n * position:fixed, so no ancestor overflow can clip. (PageView)\n */\n space: 'overlay' | 'client';\n /** Page-space rect on a page → coords in `space`. Null: not projectable\n * right now (page not shown / not measurable yet). */\n toScreen(page: PageRef, rect: AnchoredRect): AnchoredRect | null;\n toScreenPoint(page: PageRef, at: AnchoredPoint): AnchoredPoint | null;\n /** The page's live view facts (for anchor reads that need them — e.g. the\n * screen-constant rotate-knob stalk), or null when the page isn't shown. */\n viewEnv(page: PageRef): PageViewEnv | null;\n /** The area anchored UI can be seen in, in `space`: the surface's box, or\n * the window. Null while it isn't measurable yet. */\n view(): AnchoredRect | null;\n}\n\n/**\n * The anchored projection: anchor → screen position, in one pure call —\n * projection, avoid-point transform, and the shared placement policy.\n * Frameworks recompute this inside their native reactive primitive\n * (a React render, a Vue `computed`, an Angular signal, a Svelte\n * `$derived`); nothing here schedules anything.\n *\n * Null when there is nothing to show: the page isn't shown, or the box is so\n * far out of view that the UI around it can't reach into it.\n */\nexport function projectAnchoredTarget(\n projector: ViewProjector,\n anchor: AnchorTarget,\n { placement, gap, pinned = false }: AnchoredOptions,\n fit?: AnchoredFit | null,\n): AnchoredPosition | null {\n const env = anchor.boundsIn ? projector.viewEnv(anchor.page) : null;\n const bounds = env && anchor.boundsIn ? anchor.boundsIn(env) : anchor.bounds;\n if (!bounds) return null;\n const box = projector.toScreen(anchor.page, bounds);\n if (!box) return null;\n const view = projector.view();\n // Until its size is measured, assume the UI reaches no further than the gap.\n const reach = Math.abs(gap) + (fit ? Math.max(fit.size.width, fit.size.height) : 0);\n if (view && !overlaps(box, view, reach)) return null;\n const avoid = anchor.avoid?.length ? projector.toScreenPoint(anchor.page, anchor.avoid[0]) : null;\n return fit && !pinned\n ? fitAnchoredRect(box, placement, gap, fit, avoid)\n : positionAnchoredRect(box, placement, gap, avoid);\n}\n\n/** Whether `box`, grown by `margin` on every side, touches `view`. */\nfunction overlaps(box: AnchoredRect, view: AnchoredRect, margin: number): boolean {\n return (\n box.x - margin < view.x + view.width &&\n box.x + box.width + margin > view.x &&\n box.y - margin < view.y + view.height &&\n box.y + box.height + margin > view.y\n );\n}\n\n/**\n * The one genuinely browser-driven invalidation: an element in normal\n * document flow moves when the document scrolls or the window resizes —\n * no state change announces it. `<PageView>`'s binding registers this;\n * the Stage never does (its camera is state, delivered through render).\n */\nexport function observeClientGeometry(callback: () => void): () => void {\n window.addEventListener('scroll', callback, true);\n window.addEventListener('resize', callback);\n return () => {\n window.removeEventListener('scroll', callback, true);\n window.removeEventListener('resize', callback);\n };\n}\n\n/**\n * The area anchored UI stays inside: the surface's own box (`'overlay'` space, the element's\n * positioned parent) or the window (`'client'` space). Null while the element has no\n * positioned parent to measure.\n */\nexport function anchoredViewOf(\n element: HTMLElement,\n space: ViewProjector['space'],\n): AnchoredRect | null {\n if (space === 'client') {\n const { clientWidth, clientHeight } = element.ownerDocument.documentElement;\n return { x: 0, y: 0, width: clientWidth, height: clientHeight };\n }\n const container = element.offsetParent;\n return container\n ? { x: 0, y: 0, width: container.clientWidth, height: container.clientHeight }\n : null;\n}\n\n/** Whether two measurements give the same sizes, so a placement made from the first still holds. */\nexport function sameAnchoredFit(left: AnchoredFit | null, right: AnchoredFit): boolean {\n return (\n left !== null &&\n left.size.width === right.size.width &&\n left.size.height === right.size.height &&\n left.view.width === right.view.width &&\n left.view.height === right.view.height\n );\n}\n\n/**\n * Measure anchored UI's `element` and the area it stays inside ({@link anchoredViewOf}) now, and\n * again whenever either resizes: `report` hears every measurement (keep it when\n * {@link sameAnchoredFit} says it changed). A camera move changes neither, so it measures nothing.\n * Returns the function that stops measuring.\n */\nexport function observeAnchoredFit(\n element: HTMLElement,\n space: ViewProjector['space'],\n report: (fit: AnchoredFit) => void,\n): () => void {\n const measure = () => {\n const view = anchoredViewOf(element, space);\n if (!view) return;\n report({ size: { width: element.offsetWidth, height: element.offsetHeight }, view });\n };\n measure();\n const observer = new ResizeObserver(measure);\n observer.observe(element);\n if (space === 'overlay' && element.offsetParent) observer.observe(element.offsetParent);\n else window.addEventListener('resize', measure);\n return () => {\n observer.disconnect();\n window.removeEventListener('resize', measure);\n };\n}\n\n/** A placement's side and how it lines up along that side. */\nfunction splitPlacement(placement: AnchoredPlacement): {\n side: AnchoredSide;\n align: 'start' | 'center' | 'end';\n} {\n const [side, align] = placement.split('-') as [AnchoredSide, 'start' | 'end' | undefined];\n return { side, align: align ?? 'center' };\n}\n\n/**\n * The point an element is placed at, and how far its own size shifts it from\n * there (`0` keeps its left or top edge on the point, `1` its right or bottom\n * edge, `0.5` its middle).\n */\ninterface Placed {\n x: number;\n y: number;\n shiftX: number;\n shiftY: number;\n}\n\nfunction placeAround(\n box: AnchoredRect,\n placement: AnchoredPlacement,\n gap: number,\n avoid?: AnchoredPoint | null,\n): Placed {\n const { side, align } = splitPlacement(placement);\n // Along the side: the box's start edge, middle or end edge.\n const along = (start: number, length: number) =>\n align === 'start' ? start : align === 'end' ? start + length : start + length / 2;\n const alongShift = align === 'start' ? 0 : align === 'end' ? 1 : 0.5;\n switch (side) {\n case 'bottom': {\n const edge = Math.max(box.y + box.height, avoid ? avoid.y : -Infinity);\n return { x: along(box.x, box.width), y: edge + gap, shiftX: alongShift, shiftY: 0 };\n }\n case 'left': {\n const edge = Math.min(box.x, avoid ? avoid.x : Infinity);\n return { x: edge - gap, y: along(box.y, box.height), shiftX: 1, shiftY: alongShift };\n }\n case 'right': {\n const edge = Math.max(box.x + box.width, avoid ? avoid.x : -Infinity);\n return { x: edge + gap, y: along(box.y, box.height), shiftX: 0, shiftY: alongShift };\n }\n case 'top':\n default: {\n const edge = Math.min(box.y, avoid ? avoid.y : Infinity);\n return { x: along(box.x, box.width), y: edge - gap, shiftX: alongShift, shiftY: 1 };\n }\n }\n}\n\nconst shiftCss = (shift: number): string => (shift === 0 ? '0' : `${-shift * 100}%`);\n\n/**\n * Place an upright element around `box` (screen px). `avoid` is a screen\n * point the element must clear (e.g. the rotate knob): the element extends\n * only the edge it sits on, and only when the point protrudes past that\n * edge — so it clears the obstacle without ever shifting along the side.\n * When the point is on another side (e.g. a 90° shape, knob at mid-height\n * for a `top` placement) the edge is untouched.\n */\nexport function positionAnchoredRect(\n box: AnchoredRect,\n placement: AnchoredPlacement,\n gap: number,\n avoid?: AnchoredPoint | null,\n): AnchoredPosition {\n const placed = placeAround(box, placement, gap, avoid);\n return {\n left: placed.x,\n top: placed.y,\n transform: `translate(${shiftCss(placed.shiftX)}, ${shiftCss(placed.shiftY)})`,\n placement,\n };\n}\n\nconst OPPOSITE: Readonly<Record<AnchoredSide, AnchoredSide>> = {\n top: 'bottom',\n bottom: 'top',\n left: 'right',\n right: 'left',\n};\n\n/** The same placement on the other side of the box: `'top-end'` becomes `'bottom-end'`. */\nconst opposite = (placement: AnchoredPlacement): AnchoredPlacement => {\n const { side, align } = splitPlacement(placement);\n const flipped = OPPOSITE[side];\n return align === 'center' ? flipped : `${flipped}-${align}`;\n};\n\n/**\n * Place an element of a known size around `box`, inside `fit.view`. It sits\n * where `placement` says when it fits there; otherwise on the opposite side,\n * lined up the same way, when that fits or has more room. Then, while `box`\n * is in view, it moves along both axes to stay inside the view, `gap` from\n * its edges; UI whose box scrolled out of view goes with it. The result is\n * the element's top-left corner, with no transform.\n */\nexport function fitAnchoredRect(\n box: AnchoredRect,\n placement: AnchoredPlacement,\n gap: number,\n fit: AnchoredFit,\n avoid?: AnchoredPoint | null,\n): AnchoredPosition {\n const { size, view } = fit;\n const inner = {\n left: view.x + gap,\n top: view.y + gap,\n right: view.x + view.width - gap,\n bottom: view.y + view.height - gap,\n };\n const cornerOn = (at: AnchoredPlacement): AnchoredPoint => {\n const placed = placeAround(box, at, gap, avoid);\n return { x: placed.x - placed.shiftX * size.width, y: placed.y - placed.shiftY * size.height };\n };\n const fits = (at: AnchoredPlacement, corner: AnchoredPoint): boolean => {\n const { side } = splitPlacement(at);\n if (side === 'top') return corner.y >= inner.top;\n if (side === 'bottom') return corner.y + size.height <= inner.bottom;\n if (side === 'left') return corner.x >= inner.left;\n return corner.x + size.width <= inner.right;\n };\n /** The space between the box and the view's edge on a placement's side. */\n const roomOn = (at: AnchoredPlacement): number => {\n const { side } = splitPlacement(at);\n if (side === 'top') return box.y - view.y;\n if (side === 'bottom') return view.y + view.height - (box.y + box.height);\n if (side === 'left') return box.x - view.x;\n return view.x + view.width - (box.x + box.width);\n };\n\n let chosen = placement;\n let corner = cornerOn(chosen);\n if (!fits(chosen, corner)) {\n const other = opposite(chosen);\n const otherCorner = cornerOn(other);\n if (fits(other, otherCorner) || roomOn(other) > roomOn(chosen)) {\n chosen = other;\n corner = otherCorner;\n }\n }\n\n const boxInView =\n box.x < view.x + view.width &&\n box.x + box.width > view.x &&\n box.y < view.y + view.height &&\n box.y + box.height > view.y;\n if (boxInView) {\n // Larger than the view: its top-left edge stays in view.\n const clamp = (value: number, min: number, max: number) => Math.max(min, Math.min(value, max));\n corner = {\n x: clamp(corner.x, inner.left, inner.right - size.width),\n y: clamp(corner.y, inner.top, inner.bottom - size.height),\n };\n }\n return { left: corner.x, top: corner.y, transform: 'none', placement: chosen };\n}\n","/**\n * An annotation's frame in pixels. The annotation plugin hands out each\n * render item's frame (a box, its turn, and how large the page draws it) and\n * the raster inside it, in page points; these put them in the page layer's\n * pixels, with the CSS turn about the middle. Plain geometry over structural\n * shapes, the same for every framework adapter, so none of them converts,\n * turns or scales anything itself.\n */\n\n/** A box in page points and its turn, degrees clockwise about its middle. */\nexport interface TurnedBox {\n box: { x: number; y: number; width: number; height: number };\n rotation: number;\n}\n\n/** A frame: its box and turn, and how large the page draws it relative to its own size. */\nexport interface FrameBox extends TurnedBox {\n scale: number;\n}\n\n/** What a page layer converts with: its transform's `toPixels`, its turn, and its zoom (1 = 100%). */\nexport interface PageLayerTransform {\n toPixels(point: { x: number; y: number }): { x: number; y: number };\n rotation: number;\n zoom: number;\n}\n\n/** A box in pixels and the CSS that turns it about its middle (`undefined` for no turn). */\nexport interface PixelBox {\n left: number;\n top: number;\n width: number;\n height: number;\n transform: string | undefined;\n}\n\n/** A frame in the page layer's pixels, and what a look drawn inside it needs. */\nexport interface FramePixels extends PixelBox {\n /** Its turn on screen: its own and the page's, degrees clockwise. */\n rotationOnScreen: number;\n /**\n * How large the frame is on screen relative to the annotation at its 100%\n * size: the zoom, or for a body that keeps its size on screen, the zoom\n * capped at 1. A look drawn at `design` size is scaled by this.\n */\n scale: number;\n /** The frame's size at the annotation's 100% size, in pixels: the size a look is drawn at. */\n design: { width: number; height: number };\n}\n\n/** A box inside its frame as CSS percentages of the frame, so it lands in place at any size. */\nexport interface FrameFraction {\n left: string;\n top: string;\n width: string;\n height: string;\n transform: string | undefined;\n}\n\nconst turnCss = (rotation: number): string | undefined =>\n rotation ? `rotate(${rotation}deg)` : undefined;\n\n/** A frame in the page layer's pixels: where it is, its turn, and the size and scale a look is drawn at. */\nexport function frameInPixels(frame: FrameBox, page: PageLayerTransform): FramePixels {\n const { box } = frame;\n const topLeft = page.toPixels({ x: box.x, y: box.y });\n const bottomRight = page.toPixels({ x: box.x + box.width, y: box.y + box.height });\n const width = bottomRight.x - topLeft.x;\n const height = bottomRight.y - topLeft.y;\n const scale = frame.scale * page.zoom || 1;\n return {\n left: topLeft.x,\n top: topLeft.y,\n width,\n height,\n transform: turnCss(frame.rotation),\n rotationOnScreen: (((frame.rotation + page.rotation) % 360) + 360) % 360,\n scale,\n design: { width: width / scale, height: height / scale },\n };\n}\n\n/**\n * A raster inside its frame, as CSS percentages of the frame. `raster`'s box\n * is relative to the frame's box, as the annotation plugin hands it out.\n */\nexport function rasterInFrame(raster: TurnedBox, frame: TurnedBox): FrameFraction {\n const percent = (part: number, whole: number) => `${whole > 0 ? (part / whole) * 100 : 0}%`;\n return {\n left: percent(raster.box.x, frame.box.width),\n top: percent(raster.box.y, frame.box.height),\n width: percent(raster.box.width, frame.box.width),\n height: percent(raster.box.height, frame.box.height),\n transform: turnCss(raster.rotation),\n };\n}\n\n/** The frame a renderer's look draws into: the size to draw at, its turn on screen, and its scale. */\nexport interface AnnotationLookFrame {\n width: number;\n height: number;\n /** Its turn on screen, the page's and its own, degrees clockwise. */\n rotation: number;\n /** How much the layer scales what the look draws. */\n scale: number;\n}\n\n/**\n * The frame a look draws into. A `scaled` look (the default) draws at the\n * annotation's 100% size (`design`) inside an element of that size, which\n * the layer scales by `scale` from its top-left corner; an unscaled look\n * draws at the frame's size on screen.\n */\nexport function lookFrameOf(frame: FramePixels, scaled: boolean): AnnotationLookFrame {\n return {\n width: scaled ? frame.design.width : frame.width,\n height: scaled ? frame.design.height : frame.height,\n rotation: frame.rotationOnScreen,\n scale: frame.scale,\n };\n}\n","/**\n * SVG → CSS cursor string. The armed-tool indicator is the cursor itself (the\n * only zero-latency pointer-locked pixel the web has — the OS composites it),\n * so an app hands its toolbar icon here and assigns the result via the\n * interaction hub's `setToolCursor`.\n *\n * Browser rules baked into this module:\n * - The SVG must carry explicit `width`/`height` attributes (not just a\n * viewBox) or Chromium/WebKit ignore it — enforced here.\n * - The hard ceiling is 128×128 CSS px (bigger falls back to the keyword).\n * Above 32×32, Chromium temporarily resets the cursor to default while it\n * would overlap browser UI (anti-spoofing) — fine mid-viewport, so ~40px\n * compositions are common; stay ≤128.\n * - The keyword fallback covers browsers that refuse SVG cursor images.\n *\n * Pure string work with no EmbedPDF types — like the file picker, this module\n * is a plain web utility.\n */\n\nexport interface SvgCursorOptions {\n /** Complete `<svg>` markup, colors baked in (no `currentColor` — a cursor\n * image has no cascade to inherit from). */\n svg: string;\n /** The click point inside the image, CSS px from the top-left. Default 0,0\n * (arrow-style: the image's corner is the tip). */\n hotspot?: { x: number; y: number };\n /** Keyword cursor when the image can't render. Default `'crosshair'`. */\n fallback?: string;\n}\n\n/** Build the CSS cursor value: `url(\"data:image/svg+xml,…\") x y, fallback`. */\nexport function svgCursor({ svg, hotspot, fallback = 'crosshair' }: SvgCursorOptions): string {\n // Scan only the opening `<svg …>` tag (bounded slice) rather than running\n // `/<svg[^>]*\\swidth\\s*=/` over the whole string — the unbounded form is\n // quadratic on pathological inputs (many `<svg` fragments with no `>`),\n // while accepting exactly the same markup: `[^>]*` could never cross a\n // `>` either.\n const tagStart = svg.indexOf('<svg');\n const tagEnd = tagStart >= 0 ? svg.indexOf('>', tagStart) : -1;\n const openTag = tagEnd >= 0 ? svg.slice(tagStart, tagEnd + 1) : '';\n if (!/\\swidth\\s*=/.test(openTag) || !/\\sheight\\s*=/.test(openTag)) {\n throw new Error(\n '[web] svgCursor: the <svg> needs explicit width/height attributes — ' +\n 'browsers ignore cursor images without an intrinsic size',\n );\n }\n const uri = `data:image/svg+xml,${encodeURIComponent(svg)}`;\n const spot = hotspot ? ` ${Math.round(hotspot.x)} ${Math.round(hotspot.y)}` : '';\n return `url(\"${uri}\")${spot}, ${fallback}`;\n}\n\n/** One cursor slot: an SVG image ({@link SvgCursorOptions}) or a plain CSS cursor ('crosshair', 'url(…) 4 4, copy'). */\nexport type ToolCursorImage = SvgCursorOptions | string;\n\n/**\n * A tool's cursors: the tool, and its cursors by keyword. Build `svg` from the same icon your\n * toolbar shows, so the cursor and the button never drift apart.\n */\nexport interface ToolCursorSpec {\n toolId: string;\n /**\n * keyword → cursor: replace the keywords this tool can show (its own cursor, 'crosshair' or\n * 'copy', and hover claims, 'text' over text) with the tool's look. Keywords left out show as\n * they are: a markup tool's 'default' stays the bare arrow, a 'move' claim drops the icon. An\n * SVG value's `fallback` defaults to the keyword it replaces.\n */\n cursors: Record<string, ToolCursorImage>;\n}\n\n/**\n * A tool's cursors as CSS cursor values, for the interaction hub's `setToolCursor(toolId,\n * cursors)`: a string stays as it is, an SVG becomes its `url(…)` cursor, falling back to the\n * keyword it replaces unless it names its own fallback.\n */\nexport function toolCursorsOf(cursors: Record<string, ToolCursorImage>): Record<string, string> {\n return Object.fromEntries(\n Object.entries(cursors).map(([keyword, image]) => [\n keyword,\n typeof image === 'string'\n ? image\n : svgCursor(image.fallback === undefined ? { ...image, fallback: keyword } : image),\n ]),\n );\n}\n","/**\n * External-URI hygiene for PDF link annotations — written once here so every\n * framework adapter (react, vue, svelte, angular) shares the same policy.\n *\n * PDFs are untrusted input: a link's `/URI` can carry anything, including\n * `javascript:` payloads. The default allowlist covers what a document link\n * legitimately opens; everything else (javascript:, file:, data:, vbscript:,\n * custom app schemes) resolves to `null` and the adapters render the link\n * without an href — activation still reports through the plugin, so an\n * embedder that needs a custom scheme can handle it in `onActivate`.\n */\nconst DEFAULT_ALLOWED_SCHEMES = new Set(['http:', 'https:', 'mailto:', 'tel:']);\n\n/** A URI that names its scheme (RFC 3986: a letter, then letters, digits, `+`, `-` or `.`). */\nconst SCHEME = /^[a-z][a-z\\d+.-]*:/i;\n\n/**\n * The absolute address a link means, never read against the page around the viewer: one with\n * a scheme as written; a scheme-relative `//host` on https; a bare `www.` host on https, as an\n * address bar reads it. Anything else is relative to the document (`/help`, `../x`, `page2.html`)\n * and has no document address to resolve against here, so it means nothing: read against the\n * host page, it would open the app's own pages.\n */\nfunction absoluteUri(uri: string): string | null {\n if (SCHEME.test(uri)) return uri;\n if (uri.startsWith('//')) return `https:${uri}`;\n if (/^www\\./i.test(uri)) return `https://${uri}`;\n return null;\n}\n\n/**\n * Validate a link URI for opening in the browser. Returns the absolute URI to open when its\n * scheme is allowed (scheme-relative and bare `www.` URIs become https), else `null`. Never\n * throws — a malformed URI is just an unopenable one.\n */\nexport function sanitizeExternalUri(\n uri: string,\n allowedSchemes: Iterable<string> = DEFAULT_ALLOWED_SCHEMES,\n): string | null {\n const absolute = absoluteUri(uri.trim());\n if (!absolute) return null;\n const allowed = new Set(\n [...allowedSchemes].map((scheme) =>\n scheme.endsWith(':') ? scheme.toLowerCase() : `${scheme.toLowerCase()}:`,\n ),\n );\n try {\n return allowed.has(new URL(absolute).protocol) ? absolute : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Open a link target in a new tab when its address is allowed (`http`,\n * `https`, `mailto`, `tel`); anything else is refused. True when it opened.\n * Call it inside the user's click (the link plugin's `activate()` does), so\n * the click is still the gesture that opens the tab.\n */\nexport function openExternalUri(uri: string): boolean {\n const href = sanitizeExternalUri(uri);\n if (!href || typeof window === 'undefined') return false;\n window.open(href, '_blank', 'noopener,noreferrer');\n return true;\n}\n","/**\n * The default actions UI adapter — the origin×phase visibility matrix plus\n * the browser fallbacks, written once here so every framework binding\n * (react, angular, …) ships the same policy instead of forking it per\n * framework. Types are structural twins of `@embedpdf/plugin-actions`'\n * adapter contract (declared here so this package stays plugin-free, per\n * the layering law); assignability is checked where a binding installs it.\n *\n * The matrix (defaults only — an embedder override receives every effect\n * with its context attached and decides for itself):\n * - URIs open through `sanitizeExternalUri` in a new tab (the dispatcher\n * already policy-gated the node; blocked schemes are dropped here).\n * - Print: hover/lifecycle-origin requests never open the dialog; user\n * requests fall back to `globalThis.print`. The `doc.print` authority\n * gate is upstream in the plugin and not overridable.\n * - Alert: document-open nags (lifecycle origin, boot phase) never reach\n * `window.alert` — the boot-nag gap, closed.\n * - gotoPage: delegates to the binding-supplied navigator (the stage).\n */\nimport { sanitizeExternalUri } from './external-uri';\n\n/** Structural twin of plugin-actions' `ActionOrigin`. */\nexport type ActionsUiOrigin = 'user' | 'hover' | 'lifecycle';\n\n/** Structural twin of plugin-actions' `ActionUiContext`. */\nexport interface ActionsUiEffectContext {\n origin: ActionsUiOrigin;\n phase: 'boot' | 'user';\n}\n\n/** Structural twin of plugin-actions' `ActionUiAdapter`. */\nexport interface ActionsUiAdapterShape {\n openUri(uri: string, options: { isMap: boolean; origin: ActionsUiOrigin }): void;\n print(options?: ActionsUiEffectContext): void;\n alert?(message: string, options: ActionsUiEffectContext & { icon: number; title?: string }): void;\n gotoPage?(page: number, options: ActionsUiEffectContext): void;\n}\n\nexport interface DefaultActionsUiAdapterOptions {\n /**\n * Per-handler overrides. Pass a function for late binding (a binding's\n * mutable handlers ref) — it is consulted on every effect, so a fresh\n * closure never needs a reinstall.\n */\n overrides?: Partial<ActionsUiAdapterShape> | (() => Partial<ActionsUiAdapterShape> | undefined);\n /** Navigation default for script `this.pageNum = n` requests. */\n goToPage?: (page: number) => void;\n}\n\nexport const createDefaultActionsUiAdapter = (\n options: DefaultActionsUiAdapterOptions = {},\n): ActionsUiAdapterShape => {\n const overridesOf = (): Partial<ActionsUiAdapterShape> | undefined =>\n typeof options.overrides === 'function' ? options.overrides() : options.overrides;\n return {\n openUri: (uri, options) => {\n const current = overridesOf();\n if (current?.openUri) {\n current.openUri(uri, options);\n return;\n }\n const href = sanitizeExternalUri(uri);\n if (href && typeof window !== 'undefined') {\n window.open(href, '_blank', 'noopener,noreferrer');\n }\n },\n print: (options) => {\n const current = overridesOf();\n if (current?.print) current.print(options);\n else if (options && options.origin !== 'user') {\n // Hover/lifecycle scripts never open the print dialog by default.\n } else if (typeof globalThis.print === 'function') globalThis.print();\n },\n alert: (message, options) => {\n const current = overridesOf();\n if (current?.alert) current.alert(message, options);\n else if (options.origin === 'lifecycle' || options.phase === 'boot') {\n // Document-open nags (Adobe version checks, lifecycle scripts)\n // never alert by default — the boot-nag gap, closed.\n } else if (typeof globalThis.alert === 'function') globalThis.alert(message);\n },\n gotoPage: (page, effectContext) => {\n const current = overridesOf();\n if (current?.gotoPage) current.gotoPage(page, effectContext);\n else options.goToPage?.(page);\n },\n };\n};\n","/**\n * A browser image source whose object URL has an explicit lifetime: an\n * engine `PageImageHandle`, whose `objectUrl()` is cancellable and follows an\n * `AbortSignal` through `abortWith()`.\n */\nexport interface ObjectUrlImageSource {\n objectUrl(): {\n abortWith(signal: AbortSignal): PromiseLike<{ url: string; revoke(): void }>;\n };\n}\n\nexport interface PaintedImageCallbacks {\n onPainted(): void;\n onUnpainted(): void;\n}\n\n/**\n * Bind a raster source to an <img> without ever painting its incomplete-image\n * placeholder. The source becomes visible on load; retained coverage is\n * released only after the browser has had one full frame to present it.\n *\n * Call this from the framework's pre-paint mount hook and invoke the returned\n * cleanup on unmount. Keeping this DOM lifecycle here lets every framework\n * adapter remain a ref plus one bind/unbind hook.\n */\nexport function bindPaintedImage(\n image: HTMLImageElement,\n source: ObjectUrlImageSource,\n callbacks: PaintedImageCallbacks,\n): () => void {\n const controller = new AbortController();\n let revoke: (() => void) | undefined;\n let frame = 0;\n let painted = false;\n\n // The element is already committed when a mount hook runs. Hide it before\n // that commit can paint, then reveal only once the bitmap has loaded.\n image.style.visibility = 'hidden';\n\n const onLoad = () => {\n image.style.visibility = 'visible';\n\n // rAF callbacks run before a frame paints. The first frame presents the\n // newly visible image while retained coverage remains; the second reports\n // that presentation opportunity and permits the retained source to leave.\n frame = requestAnimationFrame(() => {\n frame = requestAnimationFrame(() => {\n frame = 0;\n painted = true;\n callbacks.onPainted();\n });\n });\n };\n\n image.addEventListener('load', onLoad, { once: true });\n\n void source\n .objectUrl()\n .abortWith(controller.signal)\n .then(\n (objectUrl) => {\n if (controller.signal.aborted) {\n objectUrl.revoke();\n return;\n }\n revoke = objectUrl.revoke;\n image.src = objectUrl.url;\n },\n () => {\n // Aborted or unavailable: remain hidden and retain fallback coverage.\n },\n );\n\n return () => {\n controller.abort();\n if (frame) cancelAnimationFrame(frame);\n image.removeEventListener('load', onLoad);\n image.style.visibility = 'hidden';\n image.removeAttribute('src');\n revoke?.();\n if (painted) callbacks.onUnpainted();\n };\n}\n","/**\n * Platform-feedback providers — the browser implementations of the interaction\n * hub's haptic port (structurally `PlatformFeedback` from\n * `@embedpdf/plugin-interaction`; declared structurally here so this package\n * stays plugin-free, per the layering law).\n *\n * Honesty about reach: `navigator.vibrate` works on Android browsers and is\n * not implemented in iOS Safari — there the default provider is a silent\n * no-op until Apple ships a haptics API. (The community switch-control hacks\n * were evaluated and deliberately not shipped: Apple patched programmatic\n * triggering in iOS 26.5, leaving only tap-on-control tricks that don't fit a\n * viewer's gesture moments.) The real iPhone haptic today comes through a\n * native shell: a WKWebView embedder installs a script message handler and\n * passes {@link wkFeedback} (or their own provider) instead.\n */\n\n/** Structural twin of plugin-interaction's `PlatformFeedback`. */\nexport interface WebPlatformFeedback {\n selection(): void;\n impact(weight?: 'light' | 'medium' | 'heavy'): void;\n notify(kind: 'success' | 'warning' | 'error'): void;\n}\n\nconst vibrate = (pattern: number | number[]): void => {\n if (typeof navigator !== 'undefined') navigator.vibrate?.(pattern);\n};\n\n/**\n * The default web provider: the Vibration API, mapped onto the three haptic\n * families (selection = the shortest tick; impact scales with weight; notify\n * uses patterns). A no-op wherever vibration is unsupported — notably iOS\n * Safari — so it is always safe to install.\n */\nexport const vibrationFeedback: WebPlatformFeedback = {\n selection: () => vibrate(8),\n impact: (weight = 'light') => vibrate(weight === 'heavy' ? 30 : weight === 'medium' ? 20 : 10),\n notify: (kind) =>\n vibrate(\n kind === 'error' ? [12, 60, 12, 60, 12] : kind === 'warning' ? [15, 70, 15] : [10, 50, 10],\n ),\n};\n\ninterface WKMessageHandler {\n postMessage(message: unknown): void;\n}\n\n/**\n * A provider for native iOS shells: forwards each call to a WKWebView script\n * message handler the host app installed under `handlerName` — an explicit\n * contract between the embedder and their own app, never a global this library\n * sniffs on its own. The message shape is\n * `{ family: 'selection' } | { family: 'impact', weight } | { family: 'notify', kind }`;\n * the native side maps families onto the matching `UIFeedbackGenerator`.\n * Calls fall back to `fallback` (default: {@link vibrationFeedback}) when the\n * handler is absent, so one provider serves a hybrid web/app deployment.\n */\nexport const wkFeedback = (\n handlerName: string,\n fallback: WebPlatformFeedback = vibrationFeedback,\n): WebPlatformFeedback => {\n const handler = (): WKMessageHandler | undefined => {\n if (typeof window === 'undefined') return undefined;\n const webkitWindow = window as Window & {\n webkit?: { messageHandlers?: Record<string, WKMessageHandler | undefined> };\n };\n return webkitWindow.webkit?.messageHandlers?.[handlerName];\n };\n return {\n selection: () => {\n const messageHandler = handler();\n if (messageHandler) messageHandler.postMessage({ family: 'selection' });\n else fallback.selection();\n },\n impact: (weight = 'light') => {\n const messageHandler = handler();\n if (messageHandler) messageHandler.postMessage({ family: 'impact', weight });\n else fallback.impact(weight);\n },\n notify: (kind) => {\n const messageHandler = handler();\n if (messageHandler) messageHandler.postMessage({ family: 'notify', kind });\n else fallback.notify(kind);\n },\n };\n};\n","/**\n * Wheel-input classification for ambient zoom — pure, so every framework\n * adapter shares one feel and the mapping is unit-testable.\n *\n * A ctrl/cmd wheel event is one of three physically different inputs that\n * happen to share an event type, each with its own unit system:\n *\n * • trackpad pinch — browsers (Chrome/Edge/Firefox) synthesize ctrl+wheel\n * px-deltas scaled to the de-facto convention that `exp(-Δ/100)` tracks\n * the physical finger scale 1:1. Anything weaker feels dead (the content\n * must track the fingers — the Figma/maps feel).\n * • mouse notches — coarse steps: ±100/±120 px in Chrome, lines/pages mode\n * in Firefox. The pinch mapping would triple per click; a notch instead\n * steps by 1.2, the same ratio as the zoom buttons.\n * • cmd + continuous scroll — real scroll px, big and streaming (with\n * momentum): a gentle scrub coefficient. A cmd+mouse notch (±120 px)\n * lands within 0.3% of the 1.2 button step, so it needs no special case.\n *\n * (Safari never synthesizes ctrl+wheel for pinch — it fires proprietary\n * gesture events with an absolute scale; that wiring lives in the adapters.)\n */\nexport interface WheelSample {\n deltaY: number;\n /** WheelEvent.deltaMode: 0 = pixels, 1 = lines, 2 = pages. */\n deltaMode: number;\n ctrlKey: boolean;\n metaKey: boolean;\n}\n\n/** The per-notch step for discrete wheels — the zoom buttons' ratio. */\nconst NOTCH = 1.2;\n\n/** Zoom factor for a ctrl/cmd wheel event (the caller decides zoom vs pan). */\nexport function wheelZoomFactor(sample: WheelSample): number {\n // Lines/pages mode (Firefox mouse wheels): one button-step per notch.\n if (sample.deltaMode !== 0) return Math.pow(NOTCH, -Math.sign(sample.deltaY));\n if (sample.ctrlKey) {\n // px deltas with ctrl: a real mouse notch arrives big (±100/±120) and\n // steps like the buttons; anything smaller is a synthesized pinch.\n if (Math.abs(sample.deltaY) >= 100) return Math.pow(NOTCH, -Math.sign(sample.deltaY));\n return Math.exp(-sample.deltaY / 100);\n }\n // cmd + continuous scroll: the gentle scrub.\n return Math.exp(-sample.deltaY * 0.0015);\n}\n","/**\n * Stage gesture controller — the one DOM input binding for a Stage surface,\n * shared by every framework adapter (React, Angular, …) so the feel can never\n * drift between them.\n *\n * The premise (see the stage plugin's camera doctrine): desktop inputs arrive\n * with physics already applied by the OS — momentum lives in the wheel stream,\n * a trackpad pinch is one pre-arbitrated gesture stream. Touch arrives raw:\n * with `touch-action: none` the platform scroller is out of the loop, so the\n * ballistics (velocity → fling) and the arbitration (is this contact a scroll,\n * a pinch, a tap, or a tool gesture?) must be synthesized here.\n *\n * Arbitration is modality-aware:\n * - touch — navigation-first: one finger pans (whatever tool is armed), two\n * fingers pinch-zoom around their centroid, release velocity flings, a tap\n * forwards as a click, a double-tap zoom-toggles, a long-press hands the\n * gesture to the interaction hub (text selection), and a second finger\n * landing mid-tool-gesture cancels it into a pinch (the Notes/Procreate\n * convention).\n * - mouse/pen — tool-first, exactly the pre-existing behavior: with a hub,\n * every down/move/up forwards (pan is the pan tool's job); without one,\n * dragging pans. Wheel and Safari-trackpad gesture events are unchanged.\n *\n * Camera writes are rAF-coalesced: pointer events only update gesture state;\n * one animation-frame tick applies at most one pan and one zoom per frame,\n * inside the host's begin/endGesture transaction. Events may arrive at 120 Hz;\n * the camera moves at display rate.\n *\n * Dependency note: this module speaks to the stage through the structural\n * {@link StageGestureHost} interface (satisfied by `StageHostCapability`) and to\n * the interaction hub through {@link StageGestureSink} (a closure the adapter\n * builds) — @embedpdf/web stays free of plugin imports, per the layering law.\n */\n\nimport { wheelZoomFactor } from './wheel';\n\nexport type StagePointerKind = 'mouse' | 'pen' | 'touch';\n\n/** What the controller needs from the camera — `StageHostCapability` satisfies it. */\nexport interface StageGestureHost {\n panBy(dxScreen: number, dyScreen: number): void;\n zoomAround(screenPt: { x: number; y: number }, factor: number): void;\n beginGesture(options?: { elastic?: boolean }): void;\n endGesture(): void;\n /** Momentum pan from a release velocity in screen px/s. */\n fling(velocityX: number, velocityY: number): void;\n /** True while a tween/fling runs — a touch-down then is a \"catch\", not a tap. */\n isMoving(): boolean;\n doubleTapZoom(screenPt: { x: number; y: number }): void;\n}\n\n/**\n * Where non-navigation gestures go — the adapter's bridge to the interaction\n * hub. Every callback receives the original PointerEvent so the adapter can\n * resolve pages/points exactly as it always has. Omit the sink entirely for a\n * hub-less (built-in pan) stage.\n */\nexport interface StageGestureSink {\n down(event: PointerEvent, clickCount: number): void;\n move(event: PointerEvent): void;\n up(event: PointerEvent): void;\n /** The gesture was taken over by navigation (second finger → pinch) or\n * cancelled by the system — abort, don't commit. */\n cancel(event: PointerEvent): void;\n /** Pointer travel with no gesture in flight — cursor feedback. */\n hover(event: PointerEvent): void;\n /** A touch press held still: hand the gesture to the hub (the adapter\n * typically forwards it as a word-select down). Subsequent move/up arrive\n * via {@link move}/{@link up}. */\n longPress(event: PointerEvent): void;\n /**\n * Touch-consent pre-flight, asked at touch-down before the contact is\n * classified: does a tool have standing to own this contact? True when the\n * armed tool takes fingers wholesale (a drawing tool) or something under\n * the point claims its drags (a selected annotation's body or handles).\n * True routes the whole contact to {@link down}/{@link move}/{@link up} —\n * where a second finger still cancels it into a pinch. Must be a pure\n * read. Absent = never; the contact navigates.\n */\n claimsPoint?(event: PointerEvent): boolean;\n}\n\n/** The wheel fields the zoom classifier reads (see `./wheel`'s `WheelSample`). */\nexport interface StageWheelSample {\n deltaY: number;\n deltaMode: number;\n ctrlKey: boolean;\n metaKey: boolean;\n}\n\nexport interface StageGestureOptions {\n /** Ambient zoom (wheel-zoom, pinch-zoom, double-tap zoom). Off: zoom wheels\n * fall through to pan and pinches pan without zooming — but are still\n * swallowed, never page-zooming the browser. Default true. */\n zoomGestures?: boolean;\n /** The wheel → zoom-factor classifier. Defaults to this package's\n * `wheelZoomFactor` (browser wheel classification lives here, with the rest\n * of the browser input handling); inject to override or to fake in tests. */\n wheelZoomFactor?: (sample: StageWheelSample) => number;\n /** Tool routing for non-navigation gestures; omit for built-in-pan stages. */\n sink?: StageGestureSink | null;\n /** Touch press duration that becomes a long-press (ms). Default 450. */\n longPressMs?: number;\n /** Finger travel below which a touch stays a tap/press (px). Default 10. */\n tapSlopPx?: number;\n /** Max gap between taps for a double-tap (ms). Default 300. */\n doubleTapMs?: number;\n /** Release speed below which no fling starts (px/s). Default 50. */\n flingMinVelocity?: number;\n}\n\n/**\n * Release velocity from a trail of pointer samples: the mean velocity over the\n * trailing `windowMs` (first-to-last inside the window). Null when the trail is\n * too thin or too stale to trust — the standard \"held still, then let go\"\n * case, which must not fling. Pure; exported for tests.\n */\nexport function computeReleaseVelocity(\n samples: ReadonlyArray<{ t: number; x: number; y: number }>,\n now: number,\n windowMs = 100,\n): { vx: number; vy: number } | null {\n let firstIdx = -1;\n for (let i = 0; i < samples.length; i++) {\n if (now - samples[i].t <= windowMs) {\n firstIdx = i;\n break;\n }\n }\n if (firstIdx < 0 || firstIdx === samples.length - 1) return null;\n const first = samples[firstIdx];\n const last = samples[samples.length - 1];\n const dt = last.t - first.t;\n if (dt < 10) return null;\n return { vx: ((last.x - first.x) / dt) * 1000, vy: ((last.y - first.y) / dt) * 1000 };\n}\n\ninterface Tracked {\n id: number;\n kind: StagePointerKind;\n x: number;\n y: number;\n downX: number;\n downY: number;\n}\n\ntype Mode = 'idle' | 'pending' | 'pan' | 'pinch' | 'tool' | 'mousedrag';\n\n/** Attach the gesture controller to a Stage container. Returns the detach fn. */\nexport function createStageGestureController(\n element: HTMLElement,\n host: StageGestureHost,\n options: StageGestureOptions,\n): () => void {\n const zoomGestures = options.zoomGestures ?? true;\n const wheelZoom = options.wheelZoomFactor ?? wheelZoomFactor;\n const sink = options.sink ?? null;\n const LONG_PRESS_MS = options.longPressMs ?? 450;\n const SLOP = options.tapSlopPx ?? 10;\n const DOUBLE_TAP_MS = options.doubleTapMs ?? 300;\n const DOUBLE_TAP_RADIUS = 25;\n const FLING_MIN = options.flingMinVelocity ?? 50;\n const MIN_PINCH_SPAN = 20; // px — below this a span ratio is mostly noise\n const PINCH_RELEASE_GRACE_MS = 160; // leftover finger must outlive the release\n const SETTLE_SPEED = 0.12; // px/ms — below this the leftover finger has settled\n\n const pointers = new Map<number, Tracked>();\n let mode: Mode = 'idle';\n let began = false; // a host gesture transaction is open\n let suppressTap = false; // this contact caught a moving camera — never a tap\n let touchToolGesture = false; // 'tool' mode entered via touch long-press\n let downEvent: PointerEvent | null = null; // first touch's down, for tap/long-press forwarding\n let longPressTimer: ReturnType<typeof setTimeout> | null = null;\n let panId = -1;\n // A pan is armed once its finger has crossed slop. Fresh touches arm on the\n // pending→pan transition; the finger left over when a pinch ends starts\n // disarmed — a gesture's identity persists through its release, so the\n // leftover contact must re-earn pan-hood exactly like a new finger would.\n // While disarmed, its micro-rolls neither pan nor pollute the velocity\n // trail (which still holds the pinch's centroid motion — the momentum a\n // compound gesture actually has). For a grace window after the transition\n // the slop base follows the finger: in a fast release the leftover finger\n // is still genuinely moving, and distance alone cannot tell that from a\n // deliberate continuation — only outliving the release window can.\n let panArmed = true;\n let panGraceUntil = 0;\n let graceSample = { t: 0, x: 0, y: 0 }; // leftover finger's last observed sample\n // iOS-visible seam: real WebKit can deliver a trailing gesturechange after\n // the last pointerup of a pinch — with no touches left, the desktop-trackpad\n // path would apply a stray end-of-pinch zoom. Any recent touch activity\n // therefore suppresses gesture events; desktop trackpads never have any.\n let lastTouchAt = -Infinity;\n\n // rAF application state\n let frame = 0;\n let dirty = false;\n let lastApplied = { x: 0, y: 0 }; // pan focal point, client px\n let lastSpan = 0;\n\n // velocity trail of the pan focal point (finger, or pinch centroid)\n let trail: Array<{ t: number; x: number; y: number }> = [];\n // the pinch's span trail, sampled beside the centroid: at release it decides\n // the gesture's character. A fast pinch release has asymmetric finger\n // speeds (the lifting finger flicks away), which moves the true centroid at\n // hundreds of px/s — physically real, but zoom-release noise the platform\n // ignores. Fling only when the centroid rate dominates the span rate\n // (a two-finger pan); a zoom-dominant release ends still.\n let spanTrail: Array<{ t: number; x: number; y: number }> = [];\n\n // tap-pair state for double-tap\n let lastTapT = 0;\n let lastTapX = 0;\n let lastTapY = 0;\n\n // multi-click counter for mouse/pen tool downs (parity with the adapters'\n // previous createClickCounter: 400 ms / 6 px)\n let mLast = 0;\n let mX = 0;\n let mY = 0;\n let mCount = 0;\n const clickCount = (event: PointerEvent): number => {\n const now = Date.now();\n mCount =\n now - mLast <= 400 && Math.hypot(event.clientX - mX, event.clientY - mY) <= 6\n ? mCount + 1\n : 1;\n mLast = now;\n mX = event.clientX;\n mY = event.clientY;\n return mCount;\n };\n\n const vpt = (clientX: number, clientY: number) => {\n const rect = element.getBoundingClientRect();\n return { x: clientX - rect.left, y: clientY - rect.top };\n };\n const touches = (): Tracked[] => {\n const out: Tracked[] = [];\n pointers.forEach((pointer) => {\n if (pointer.kind === 'touch') out.push(pointer);\n });\n return out;\n };\n\n const clearLongPress = () => {\n if (longPressTimer !== null) {\n clearTimeout(longPressTimer);\n longPressTimer = null;\n }\n };\n const stopFrames = () => {\n if (frame) {\n cancelAnimationFrame(frame);\n frame = 0;\n }\n };\n const begin = (elastic = false) => {\n if (!began) {\n began = true;\n // touch contacts are elastic (rubber-band past the clamp); mouse drags\n // stay rigid — the desktop convention\n host.beginGesture(elastic ? { elastic: true } : undefined);\n }\n };\n const end = () => {\n if (began) {\n began = false;\n host.endGesture();\n }\n };\n const toIdle = () => {\n mode = 'idle';\n downEvent = null;\n touchToolGesture = false;\n trail = [];\n spanTrail = [];\n dirty = false;\n panId = -1;\n panArmed = true;\n panGraceUntil = 0;\n clearLongPress();\n stopFrames();\n };\n const pushSample = (x: number, y: number) => {\n trail.push({ t: performance.now(), x, y });\n if (trail.length > 16) trail.shift();\n };\n const maybeFling = () => {\n const velocity = computeReleaseVelocity(trail, performance.now());\n if (velocity && Math.hypot(velocity.vx, velocity.vy) >= FLING_MIN)\n host.fling(velocity.vx, velocity.vy);\n };\n\n // ── the application step, shared by the frame loop and the release paths —\n // a release must flush whatever the last tick hadn't applied yet, or a fast\n // flick loses its final sub-frame of travel and a pinch its last span step.\n const flushPanTo = (x: number, y: number) => {\n const dx = x - lastApplied.x;\n const dy = y - lastApplied.y;\n if (dx !== 0 || dy !== 0) host.panBy(dx, dy);\n lastApplied = { x, y };\n dirty = false;\n };\n const flushPinch = (ax: number, ay: number, bx: number, by: number) => {\n const cx = (ax + bx) / 2;\n const cy = (ay + by) / 2;\n const span = Math.hypot(ax - bx, ay - by);\n const dx = cx - lastApplied.x;\n const dy = cy - lastApplied.y;\n if (dx !== 0 || dy !== 0) host.panBy(dx, dy);\n if (zoomGestures && span > MIN_PINCH_SPAN && lastSpan > MIN_PINCH_SPAN && span !== lastSpan) {\n host.zoomAround(vpt(cx, cy), span / lastSpan);\n }\n // The centroid + span trails sample here — once per applied frame, where\n // both fingers are read coherently. Per-event sampling zig-zags (fingers\n // report sequentially, so each single-finger move fakes a half-step of\n // centroid motion) and manufactures phantom release velocity. The span\n // trail is the release gate's evidence of the gesture's character.\n pushSample(cx, cy);\n spanTrail.push({ t: performance.now(), x: span, y: 0 });\n if (spanTrail.length > 16) spanTrail.shift();\n lastApplied = { x: cx, y: cy };\n lastSpan = span;\n dirty = false;\n };\n\n // ── the per-frame application (one camera write per frame) ────────────────\n const tick = () => {\n frame = 0;\n if (mode === 'pan' || mode === 'mousedrag') {\n const pointer = pointers.get(panId);\n if (pointer && dirty) flushPanTo(pointer.x, pointer.y);\n frame = requestAnimationFrame(tick);\n } else if (mode === 'pinch') {\n const [first, second] = touches();\n if (first && second && dirty) flushPinch(first.x, first.y, second.x, second.y);\n frame = requestAnimationFrame(tick);\n }\n };\n const ensureFrames = () => {\n if (!frame) frame = requestAnimationFrame(tick);\n };\n\n const enterPinch = () => {\n const [first, second] = touches();\n if (!first || !second) return;\n mode = 'pinch';\n lastApplied = { x: (first.x + second.x) / 2, y: (first.y + second.y) / 2 };\n lastSpan = Math.hypot(first.x - second.x, first.y - second.y);\n trail = [];\n dirty = false;\n ensureFrames();\n };\n\n const onLongPress = () => {\n longPressTimer = null;\n if (mode !== 'pending' || !downEvent || !sink) return;\n // The camera transaction closes; the tool owns the rest of this contact.\n end();\n mode = 'tool';\n touchToolGesture = true;\n sink.longPress(downEvent);\n };\n\n // ── listeners ─────────────────────────────────────────────────────────────\n const onDown = (event: PointerEvent) => {\n const kind = (event.pointerType || 'mouse') as StagePointerKind;\n if (kind === 'touch') lastTouchAt = performance.now();\n if (kind !== 'touch') {\n if (kind === 'mouse' && event.button !== 0) return;\n if (mode !== 'idle') return; // an active gesture owns the surface\n pointers.set(event.pointerId, {\n id: event.pointerId,\n kind,\n x: event.clientX,\n y: event.clientY,\n downX: event.clientX,\n downY: event.clientY,\n });\n if (sink) {\n // tool-first, exactly the pre-existing hub behavior\n mode = 'tool';\n sink.down(event, clickCount(event));\n } else {\n mode = 'mousedrag';\n begin();\n panId = event.pointerId;\n lastApplied = { x: event.clientX, y: event.clientY };\n ensureFrames();\n }\n return;\n }\n\n // touch\n switch (mode) {\n case 'idle': {\n pointers.set(event.pointerId, {\n id: event.pointerId,\n kind,\n x: event.clientX,\n y: event.clientY,\n downX: event.clientX,\n downY: event.clientY,\n });\n // Consent pre-flight — but a moving camera always catches first: while\n // content flies under the finger, the touch means \"stop\", never \"grab\n // whatever happens to pass beneath it\".\n const moving = host.isMoving();\n if (!moving && sink?.claimsPoint?.(event)) {\n // A tool owns this contact from the first pixel (selected-annotation\n // drag, or an armed drawing tool). No camera transaction — this is\n // not a navigation gesture; a second finger converts it to a pinch\n // via the 'tool' branch (sink.cancel), exactly like a long-press.\n mode = 'tool';\n touchToolGesture = true;\n sink.down(event, 1);\n break;\n }\n mode = 'pending';\n downEvent = event;\n suppressTap = moving; // a catch, not a tap\n begin(true); // stops any fling/tween under the finger\n trail = [];\n pushSample(event.clientX, event.clientY);\n clearLongPress();\n if (sink && !suppressTap) longPressTimer = setTimeout(onLongPress, LONG_PRESS_MS);\n break;\n }\n case 'pending':\n case 'pan': {\n pointers.set(event.pointerId, {\n id: event.pointerId,\n kind,\n x: event.clientX,\n y: event.clientY,\n downX: event.clientX,\n downY: event.clientY,\n });\n clearLongPress();\n enterPinch();\n break;\n }\n case 'tool': {\n // A second finger during a touch tool gesture cancels it into a pinch\n // (the platform convention). Mouse tool gestures ignore stray touches.\n if (!touchToolGesture) return;\n sink?.cancel(event);\n touchToolGesture = false;\n pointers.set(event.pointerId, {\n id: event.pointerId,\n kind,\n x: event.clientX,\n y: event.clientY,\n downX: event.clientX,\n downY: event.clientY,\n });\n begin(true);\n enterPinch();\n break;\n }\n case 'pinch':\n case 'mousedrag':\n return; // ignore extra contacts\n }\n };\n\n const onWindowMove = (event: PointerEvent) => {\n const pointer = pointers.get(event.pointerId);\n if (!pointer) return;\n if (pointer.kind === 'touch') lastTouchAt = performance.now();\n pointer.x = event.clientX;\n pointer.y = event.clientY;\n switch (mode) {\n case 'pending': {\n pushSample(pointer.x, pointer.y);\n if (Math.hypot(pointer.x - pointer.downX, pointer.y - pointer.downY) > SLOP) {\n clearLongPress();\n mode = 'pan';\n panId = pointer.id;\n lastApplied = { x: pointer.x, y: pointer.y }; // absorb the slop, like a scroller\n ensureFrames();\n }\n break;\n }\n case 'pan':\n if (pointer.id !== panId) break;\n if (!panArmed) {\n // pinch-leftover contact. Inside the release-grace window the slop\n // base follows the finger — a fast release keeps moving and must\n // never accumulate distance; only a contact that outlives the\n // window can re-earn panning by crossing slop from where it\n // settled.\n const nowT = performance.now();\n if (nowT < panGraceUntil) {\n // a finger that settles (speed drops) inside the window is a\n // continuation taking hold — end the grace early so a deliberate\n // pause-then-drag stays responsive\n const dt = nowT - graceSample.t;\n if (dt >= 8) {\n const speed = Math.hypot(pointer.x - graceSample.x, pointer.y - graceSample.y) / dt;\n graceSample = { t: nowT, x: pointer.x, y: pointer.y };\n if (speed < SETTLE_SPEED) panGraceUntil = 0;\n }\n pointer.downX = pointer.x;\n pointer.downY = pointer.y;\n break;\n }\n if (Math.hypot(pointer.x - pointer.downX, pointer.y - pointer.downY) <= SLOP) break;\n panArmed = true;\n lastApplied = { x: pointer.x, y: pointer.y }; // absorb, like any fresh pan\n trail = [];\n spanTrail = [];\n }\n pushSample(pointer.x, pointer.y);\n dirty = true;\n break;\n case 'pinch':\n // centroid samples are taken per applied frame (see flushPinch) —\n // per-event sampling here would zig-zag between the two fingers\n dirty = true;\n break;\n case 'tool':\n sink?.move(event);\n break;\n case 'mousedrag':\n if (pointer.id === panId) dirty = true;\n break;\n case 'idle':\n break;\n }\n };\n\n // Hover (cursor feedback) — only with no gesture in flight, and only from\n // the element itself, matching the previous adapters.\n const onHoverMove = (event: PointerEvent) => {\n if (mode === 'idle' && sink) sink.hover(event);\n };\n\n const backToSingleFinger = (): boolean => {\n const rest = touches();\n if (rest.length !== 1) return false;\n mode = 'pan';\n panId = rest[0].id;\n panArmed = false; // leftover contact re-earns pan-hood via slop\n panGraceUntil = performance.now() + PINCH_RELEASE_GRACE_MS;\n graceSample = { t: performance.now(), x: rest[0].x, y: rest[0].y };\n rest[0].downX = rest[0].x;\n rest[0].downY = rest[0].y;\n lastApplied = { x: rest[0].x, y: rest[0].y };\n trail = [];\n dirty = false;\n return true;\n };\n\n const onUp = (event: PointerEvent) => {\n const pointer = pointers.get(event.pointerId);\n if (!pointer) return;\n if (pointer.kind === 'touch') lastTouchAt = performance.now();\n pointers.delete(event.pointerId);\n switch (mode) {\n case 'pending': {\n // Slop never exceeded, timer never fired: a tap (or a catch).\n clearLongPress();\n end();\n const now = performance.now();\n const isDouble =\n now - lastTapT <= DOUBLE_TAP_MS &&\n Math.hypot(event.clientX - lastTapX, event.clientY - lastTapY) <= DOUBLE_TAP_RADIUS;\n if (suppressTap) {\n lastTapT = 0; // a catch never counts toward a double-tap\n } else if (zoomGestures && isDouble) {\n lastTapT = 0;\n host.doubleTapZoom(vpt(event.clientX, event.clientY));\n } else {\n lastTapT = now;\n lastTapX = event.clientX;\n lastTapY = event.clientY;\n if (sink && downEvent) {\n // The whole click, delivered at release — tools see exactly the\n // down/up pair they would from a mouse.\n sink.down(downEvent, 1);\n sink.up(event);\n }\n }\n toIdle();\n break;\n }\n case 'pan': {\n if (!panArmed) {\n // The pinch's other finger leaving: the gesture ends as a pinch.\n // No flush (release-rolls are noise). Glide is character-gated: a\n // human fast release moves the true centroid (the lifting finger\n // flicks away), so centroid velocity alone lies — the glide fires\n // only when the centroid rate dominates the span rate (a\n // two-finger pan), never on a zoom-dominant release.\n const now = performance.now();\n const velocity = computeReleaseVelocity(trail, now);\n const vs = computeReleaseVelocity(spanTrail, now);\n end(); // close the bracket first — endGesture owns the elastic settle\n if (velocity) {\n const speed = Math.hypot(velocity.vx, velocity.vy);\n if (speed >= FLING_MIN && speed > Math.abs(vs?.vx ?? 0))\n host.fling(velocity.vx, velocity.vy);\n }\n toIdle();\n break;\n }\n flushPanTo(event.clientX, event.clientY); // apply the final sub-frame of travel\n pushSample(event.clientX, event.clientY);\n end();\n maybeFling();\n toIdle();\n break;\n }\n case 'pinch': {\n const rest = touches();\n if (rest.length === 1) {\n // The pinch ends at its last coherent frame. No flush here: the\n // lifting finger's release position is fresh but the other one's is\n // stale (its move for this window may not have arrived), and a\n // centroid of two instants is fiction — in a fast pinch that skewed\n // write was the visible end-of-pinch hop, and its poisoned trail\n // sample the phantom fling. Only coherent finger-pairs write the\n // camera; the sub-frame remainder is discarded, as the platform\n // recognizers do. The remaining contact gets a disarmed pan with\n // the centroid trail preserved and a release-grace window armed.\n mode = 'pan';\n panId = rest[0].id;\n panArmed = false;\n panGraceUntil = performance.now() + PINCH_RELEASE_GRACE_MS;\n graceSample = { t: performance.now(), x: rest[0].x, y: rest[0].y };\n rest[0].downX = rest[0].x;\n rest[0].downY = rest[0].y;\n lastApplied = { x: rest[0].x, y: rest[0].y };\n dirty = false;\n break;\n }\n end();\n maybeFling();\n toIdle();\n break;\n }\n case 'tool': {\n sink?.up(event);\n toIdle();\n break;\n }\n case 'mousedrag': {\n if (pointer.id !== panId) break;\n flushPanTo(event.clientX, event.clientY);\n end();\n toIdle();\n break;\n }\n case 'idle':\n break;\n }\n };\n\n const onCancel = (event: PointerEvent) => {\n const pointer = pointers.get(event.pointerId);\n if (!pointer) return;\n pointers.delete(event.pointerId);\n if (mode === 'tool') {\n sink?.cancel(event);\n toIdle();\n return;\n }\n if (mode === 'pinch' && backToSingleFinger()) return;\n end();\n toIdle();\n };\n\n // Wheel is ambient navigation in both modes: ctrl/meta zooms (classified per\n // input by the injected wheelZoomFactor), else scrolls. With zoom gestures\n // off, a zoom-wheel falls through to ordinary pan.\n const onWheel = (event: WheelEvent) => {\n event.preventDefault();\n if (zoomGestures && (event.ctrlKey || event.metaKey)) {\n host.zoomAround(vpt(event.clientX, event.clientY), wheelZoom(event));\n } else {\n const dx = event.shiftKey ? event.deltaY : event.deltaX;\n const dy = event.shiftKey ? event.deltaX : event.deltaY;\n host.panBy(-dx, -dy);\n }\n };\n\n // Safari's proprietary gesture events. On desktop Safari they are the only\n // trace of a trackpad pinch — convert the absolute scale to per-event ratios.\n // On iOS they fire alongside per-finger pointer events; there the pointer\n // path owns the pinch and these are preventDefault-ed only (a pinch over the\n // stage must never page-zoom Safari). The guard is live touch contacts.\n let lastScale = 1;\n const onGestureStart = (event: Event) => {\n event.preventDefault();\n lastScale = (event as unknown as { scale?: number }).scale ?? 1;\n };\n const onGestureChange = (event: Event) => {\n event.preventDefault();\n // iOS: the pointer path owns touch pinches — and a trailing gesturechange\n // can arrive after the last pointerup, so suppression keys on recent\n // touch activity, not just live contacts. Desktop trackpads have none.\n if (touches().length > 0 || performance.now() - lastTouchAt < 500) return;\n const gesture = event as unknown as { scale?: number; clientX: number; clientY: number };\n const scale = gesture.scale ?? 1;\n if (zoomGestures && scale > 0) {\n host.zoomAround(vpt(gesture.clientX, gesture.clientY), scale / lastScale);\n }\n lastScale = scale;\n };\n\n element.addEventListener('pointerdown', onDown);\n element.addEventListener('pointermove', onHoverMove);\n window.addEventListener('pointermove', onWindowMove);\n window.addEventListener('pointerup', onUp);\n window.addEventListener('pointercancel', onCancel);\n element.addEventListener('wheel', onWheel, { passive: false });\n const hasGestureEvents = 'GestureEvent' in window;\n if (hasGestureEvents) {\n element.addEventListener('gesturestart', onGestureStart);\n element.addEventListener('gesturechange', onGestureChange);\n element.addEventListener('gestureend', onGestureStart); // reset the base\n }\n\n return () => {\n element.removeEventListener('pointerdown', onDown);\n element.removeEventListener('pointermove', onHoverMove);\n window.removeEventListener('pointermove', onWindowMove);\n window.removeEventListener('pointerup', onUp);\n window.removeEventListener('pointercancel', onCancel);\n element.removeEventListener('wheel', onWheel);\n if (hasGestureEvents) {\n element.removeEventListener('gesturestart', onGestureStart);\n element.removeEventListener('gesturechange', onGestureChange);\n element.removeEventListener('gestureend', onGestureStart);\n }\n clearLongPress();\n stopFrames();\n end(); // balance an open transaction on unmount\n };\n}\n","/**\n * Selection-handle DOM binding — the listener mechanics of dragging one\n * handle element, shared by every framework adapter (the policy — geometry\n * and the drag session — lives in `@embedpdf/plugin-selection`'s pure\n * `handles` module; this file never imports it, per the layering law:\n * structural interfaces only).\n *\n * The load-bearing subtlety this module owns: the pointer-down shield must be\n * a native listener. The stage's gesture controller listens natively on the\n * container, so a framework-synthetic stopPropagation (React runs its\n * handlers at the root, after the container's native ones) would be too late\n * — the stage would already be panning underneath the handle drag. Once the\n * down is captured here, movement tracks by client deltas from the handle's\n * own base point: no DOM geometry reads, and the client↔overlay conversion\n * cancels out.\n */\n\ninterface SurfacePoint {\n x: number;\n y: number;\n}\n\n/** The armed drag the adapter hands back — plugin-selection's\n * `SelectionHandleDragSession` satisfies it. */\nexport interface SelectionHandleSession {\n move(overlay: SurfacePoint): void;\n end(): void;\n}\n\nexport interface AttachSelectionHandleOptions {\n /**\n * Called at pointer-down to arm a drag: return the drag session plus the\n * handle's current base point in overlay space (the bar's midpoint — the\n * point the user grabbed). Return null to decline (endpoint vanished, page\n * not laid out) — the press then does nothing and nothing is captured.\n */\n arm(): { base: SurfacePoint; session: SelectionHandleSession } | null;\n}\n\n/** Bind one handle element. Returns the detach fn. */\nexport function attachSelectionHandle(\n element: HTMLElement,\n options: AttachSelectionHandleOptions,\n): () => void {\n let active: {\n session: SelectionHandleSession;\n base: SurfacePoint;\n baseClient: SurfacePoint;\n pointerId: number;\n } | null = null;\n\n const onMove = (event: PointerEvent) => {\n if (!active || event.pointerId !== active.pointerId) return;\n active.session.move({\n x: active.base.x + (event.clientX - active.baseClient.x),\n y: active.base.y + (event.clientY - active.baseClient.y),\n });\n };\n // Release and system-cancel settle identically: the selection's own commit\n // semantics live in the session; there is no half-state to revert here.\n const onUp = (event: PointerEvent) => {\n if (!active || event.pointerId !== active.pointerId) return;\n const session = active.session;\n active = null;\n session.end();\n };\n const onDown = (event: PointerEvent) => {\n if (active) return;\n const armed = options.arm();\n if (!armed) return;\n event.preventDefault();\n event.stopPropagation(); // native: fires before the stage controller's listener\n try {\n element.setPointerCapture(event.pointerId);\n } catch {\n // best-effort, like the scrollbar: an already-released pointer\n // (pen/touch races, synthetic events in tests) throws — the drag must\n // still arm, and the window-less capture path still delivers to `el`.\n }\n active = {\n session: armed.session,\n base: armed.base,\n baseClient: { x: event.clientX, y: event.clientY },\n pointerId: event.pointerId,\n };\n };\n\n element.addEventListener('pointerdown', onDown);\n element.addEventListener('pointermove', onMove);\n element.addEventListener('pointerup', onUp);\n element.addEventListener('pointercancel', onUp);\n return () => {\n element.removeEventListener('pointerdown', onDown);\n element.removeEventListener('pointermove', onMove);\n element.removeEventListener('pointerup', onUp);\n element.removeEventListener('pointercancel', onUp);\n active = null;\n };\n}\n","/**\n * Stage surface binding — everything DOM about attaching one element to one\n * stage lens, shared by every framework adapter (React, Angular, …) so the\n * binding can never drift between them:\n *\n * • ResizeObserver → `setViewport` (the shell only reports; placement is the\n * stage plugin's job)\n * • device-pixel-ratio observation → `setDevicePixelRatio` (dppx changes\n * re-subscribe, since the media query's value itself moves)\n * • pointer-sample normalization — the one place a PointerEvent becomes a\n * page-resolved, source-stamped sample for the interaction hub\n * • the gesture controller (wheel, Safari trackpad gestures, synthesized\n * touch physics) from `./stage-gestures`\n *\n * The framework adapters keep only their reactive glue: refs/effects, page\n * surfaces, cursor-style subscription, overlay slots.\n *\n * Dependency note: like the gesture controller, this module speaks to the\n * stage and the hub through structural interfaces ({@link StageSurfaceHost},\n * {@link StageSurfaceHub}) — satisfied by `StageCapability` and\n * `InteractionHostCapability`, imported by neither. @embedpdf/web stays free of\n * plugin imports, per the layering law.\n *\n * Browser-only by nature (ResizeObserver, matchMedia): call from a mounted\n * effect / browser platform check, exactly like the gesture controller.\n */\nimport { createStageGestureController } from './stage-gestures';\nimport type { StageGestureHost, StageGestureSink, StageWheelSample } from './stage-gestures';\nimport type { PageRef } from './page-ref';\n\ninterface SurfacePoint {\n x: number;\n y: number;\n}\n\n/** What the binding needs from the stage — `StageHostCapability` satisfies it. */\nexport interface StageSurfaceHost extends StageGestureHost {\n setViewportSize(size: { width: number; height: number }): void;\n setDevicePixelRatio(ratio: number): void;\n /** Viewport point → the page under it (with per-page display context), or null over a gap. */\n getPageAt(screen: SurfacePoint): {\n ref: PageRef;\n point: SurfacePoint;\n scale?: number;\n rotation?: 0 | 90 | 180 | 270;\n zoom?: number;\n } | null;\n /** Viewport point → a specific page's page space, unclamped (frame-stable projection). */\n viewportToPage(page: PageRef, screen: SurfacePoint): SurfacePoint | null;\n}\n\n/**\n * The page-resolved sample the binding hands the hub — structurally a\n * `PointerSample` (plugin-interaction). `source` carries the lens identity so\n * the hub can route lens-scoped handlers; with several stages on one document,\n * a drag on one lens must never be captured by another lens's handler.\n */\nexport interface StageSurfaceSample {\n phase: 'down' | 'move' | 'up' | 'cancel';\n viewport: SurfacePoint;\n page?: {\n ref: PageRef;\n point: SurfacePoint;\n scale?: number;\n rotation?: 0 | 90 | 180 | 270;\n zoom?: number;\n };\n project: (page: PageRef) => SurfacePoint | null;\n modifiers: { shift: boolean; alt: boolean; ctrl: boolean; meta: boolean };\n clickCount: number;\n pointerType: 'mouse' | 'pen' | 'touch';\n gesture?: 'long-press';\n source?: string;\n}\n\n/** What the binding needs from the interaction hub — `InteractionHostCapability` satisfies it. */\nexport interface StageSurfaceHub {\n dispatchPointer(sample: StageSurfaceSample): void;\n getActiveTool(): { touch?: 'draw' | 'tap' };\n wouldClaimTouch(sample: StageSurfaceSample): boolean;\n}\n\nexport interface StageSurfaceOptions {\n /** Route pointer input to the interaction hub (page-resolved samples) instead\n * of built-in drag-to-pan. Omit/null for a hub-less stage. */\n hub?: StageSurfaceHub | null;\n /** The lens identity stamped on every sample (`StageHostCapability.getLensId()`),\n * so lens-scoped handlers only see their own stage's input. */\n source?: string;\n /** Ambient zoom gestures (see {@link StageGestureOptions}). Default true. */\n zoomGestures?: boolean;\n /** Override the wheel → zoom-factor classifier (defaults to `./wheel`). */\n wheelZoomFactor?: (sample: StageWheelSample) => number;\n}\n\n/** Attach the full surface binding. Returns the detach fn. */\nexport function createStageSurface(\n element: HTMLElement,\n stage: StageSurfaceHost,\n options: StageSurfaceOptions = {},\n): () => void {\n const hub = options.hub ?? null;\n const cleanups: Array<() => void> = [];\n\n // Only report the viewport size. Initial placement (home) is the stage\n // plugin's job — it places when it first learns a real size (and a\n // higher-priority initial-view provider can override). The shell stays dumb.\n const setViewport = () =>\n stage.setViewportSize({ width: element.clientWidth, height: element.clientHeight });\n const ro = new ResizeObserver(setViewport);\n ro.observe(element);\n setViewport();\n cleanups.push(() => ro.disconnect());\n\n // Report the device pixel ratio so page transforms render crisp. dppx changes\n // (browser zoom, dragging between monitors) fire the media query;\n // re-subscribe each time since the query value itself moves.\n let mq: MediaQueryList | null = null;\n const reportDpr = () => {\n stage.setDevicePixelRatio(window.devicePixelRatio || 1);\n mq?.removeEventListener('change', reportDpr);\n mq = window.matchMedia(`(resolution: ${window.devicePixelRatio}dppx)`);\n mq.addEventListener('change', reportDpr);\n };\n reportDpr();\n cleanups.push(() => mq?.removeEventListener('change', reportDpr));\n\n // The sink is the hub bridge: it converts events to page-resolved samples\n // (`pageAt` per event, so a drag can cross pages) and stamps the lens source.\n const sampleOf = (\n phase: StageSurfaceSample['phase'],\n event: PointerEvent,\n clickCount = 1,\n gesture?: StageSurfaceSample['gesture'],\n ): StageSurfaceSample => {\n const rect = element.getBoundingClientRect();\n const viewport = { x: event.clientX - rect.left, y: event.clientY - rect.top };\n return {\n phase,\n viewport,\n page: stage.getPageAt(viewport) ?? undefined,\n // Page-anchored gestures (annotation move/resize) track the origin\n // page's frame through this even when the cursor is off that page.\n project: (page) => stage.viewportToPage(page, viewport),\n modifiers: {\n shift: event.shiftKey,\n alt: event.altKey,\n ctrl: event.ctrlKey,\n meta: event.metaKey,\n },\n clickCount,\n pointerType: (event.pointerType || 'mouse') as StageSurfaceSample['pointerType'],\n ...(gesture ? { gesture } : {}),\n ...(options.source ? { source: options.source } : {}),\n };\n };\n const forward = (\n phase: StageSurfaceSample['phase'],\n event: PointerEvent,\n clickCount = 1,\n gesture?: StageSurfaceSample['gesture'],\n ) => {\n hub?.dispatchPointer(sampleOf(phase, event, clickCount, gesture));\n };\n const sink: StageGestureSink | null = hub\n ? {\n down: (event, clickCount) => forward('down', event, clickCount),\n move: (event) => forward('move', event),\n up: (event) => forward('up', event),\n cancel: (event) => forward('cancel', event),\n hover: (event) => forward('move', event), // no owner → the hub routes to onHover\n // Touch long-press = a word-select down (clickCount 2 keeps the\n // word-selection contract; the `gesture` marker is the honest\n // long-press signal for haptics/pickup handlers).\n longPress: (event) => forward('down', event, 2, 'long-press'),\n // Touch consent: an armed drawing/markup tool takes fingers wholesale;\n // otherwise per-point claims (a selected annotation's body or handles)\n // decide. A pure pre-flight — nothing captures.\n claimsPoint: (event) =>\n hub.getActiveTool().touch === 'draw' || hub.wouldClaimTouch(sampleOf('down', event)),\n }\n : null;\n cleanups.push(\n createStageGestureController(element, stage, {\n zoomGestures: options.zoomGestures,\n wheelZoomFactor: options.wheelZoomFactor,\n sink,\n }),\n );\n\n return () => {\n for (const fn of cleanups) fn();\n };\n}\n","/**\n * A bytes-by-id store over IndexedDB — the browser's answer to \"keep these\n * blobs for me\". Structurally the store port a plugin declares for its own\n * persistence (e.g. `StampLibraryStore` in `@embedpdf/plugin-stamp`); declared\n * here as a twin so this package stays plugin-free, per the layering law.\n *\n * IndexedDB rather than localStorage: a library or an image is binary, and a\n * localStorage string store caps at a few MB. One database, one object store,\n * keys are the caller's ids, values are the bytes as stored.\n */\nexport interface ByteStore {\n list(): Promise<Array<{ id: string; bytes: Uint8Array }>>;\n put(id: string, bytes: Uint8Array): Promise<void>;\n delete(id: string): Promise<void>;\n}\n\nexport interface IndexedDbByteStoreOptions {\n /** Object store name inside the database. Default `'bytes'`. */\n storeName?: string;\n}\n\n/** Open (creating on first use) one IndexedDB object store keyed by id. */\nexport function indexedDbByteStore(\n dbName: string,\n options: IndexedDbByteStoreOptions = {},\n): ByteStore {\n const storeName = options.storeName ?? 'bytes';\n const open = (): Promise<IDBDatabase> =>\n new Promise((resolve, reject) => {\n const request = indexedDB.open(dbName, 1);\n request.onupgradeneeded = () => {\n if (!request.result.objectStoreNames.contains(storeName)) {\n request.result.createObjectStore(storeName);\n }\n };\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error ?? new Error('indexedDB.open failed'));\n });\n const run = async <T>(\n mode: IDBTransactionMode,\n body: (store: IDBObjectStore) => IDBRequest<T>,\n ): Promise<T> => {\n const db = await open();\n try {\n return await new Promise<T>((resolve, reject) => {\n const request = body(db.transaction(storeName, mode).objectStore(storeName));\n request.onsuccess = () => resolve(request.result);\n request.onerror = () => reject(request.error ?? new Error('indexedDB request failed'));\n });\n } finally {\n db.close();\n }\n };\n return {\n list: async () => {\n const keys = await run<IDBValidKey[]>('readonly', (store) => store.getAllKeys());\n const values = await run<Uint8Array[]>('readonly', (store) => store.getAll());\n return keys.map((key, i) => ({ id: String(key), bytes: new Uint8Array(values[i]) }));\n },\n put: (id, bytes) =>\n run('readwrite', (store) => store.put(new Uint8Array(bytes), id)).then(() => undefined),\n delete: (id) => run('readwrite', (store) => store.delete(id)).then(() => undefined),\n };\n}\n","/** What every family {@link mountWebFont} mounts starts with. */\nconst FAMILY_PREFIX = 'epdf-';\n\n/**\n * The CSS family a registered font's key is mounted under: the key in the\n * viewer's own namespace, so a family of the same name on the page (its own\n * `@font-face`, or an installed font) never stands in for it. The annotation\n * plugin's CSS for a registered font names the same family.\n */\nexport function webFontFamily(key: string): string {\n return `${FAMILY_PREFIX}${key}`;\n}\n\n/** The key a family from {@link webFontFamily} was made from; any other family as it is. */\nexport function fontKeyOfWebFamily(family: string): string {\n return family.startsWith(FAMILY_PREFIX) ? family.slice(FAMILY_PREFIX.length) : family;\n}\n\n/**\n * Mount a registered font for the DOM: the engine lays text out with the\n * font's bytes, the browser needs the same face as a `@font-face` so the\n * live editor and the vector renderer show the glyphs the appearance stream\n * will bake. The face's family is {@link webFontFamily} of the key.\n *\n * Returns the unmount. Idempotent per (document, key): a second mount of a\n * key already in the document's font set is a no-op that still returns an\n * unmount of its own (the face leaves when the last holder unmounts).\n */\nexport async function mountWebFont(\n key: string,\n data: ArrayBuffer | Uint8Array,\n options: { document?: Document; weight?: number | string; style?: 'normal' | 'italic' } = {},\n): Promise<() => void> {\n return mountFace(webFontFamily(key), data, options);\n}\n\ninterface FaceOptions {\n readonly document?: Document;\n readonly weight?: number | string;\n readonly style?: 'normal' | 'italic';\n}\n\n/** Mount one face under `family`, refcounted per (document, family). */\nasync function mountFace(\n family: string,\n data: ArrayBuffer | Uint8Array,\n options: FaceOptions,\n): Promise<() => void> {\n const doc = options.document ?? document;\n const fonts = doc.fonts;\n if (!fonts || typeof FontFace === 'undefined') return () => {};\n const holders = holdersOf(doc);\n const existing = holders.get(family);\n if (existing) {\n existing.count++;\n return () => release(holders, family);\n }\n const buffer =\n data instanceof Uint8Array\n ? (data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength) as ArrayBuffer)\n : data;\n const face = new FontFace(family, buffer, {\n weight: options.weight !== undefined ? String(options.weight) : undefined,\n style: options.style,\n });\n holders.set(family, { face, doc, count: 1 });\n try {\n await face.load();\n fonts.add(face);\n } catch {\n holders.delete(family);\n return () => {};\n }\n notifyFontChange(doc);\n return () => release(holders, family);\n}\n\n/**\n * A font program to mount: its CSS family, its style, and its bytes. The\n * structural twin of `@embedpdf/core-fonts`' `FontProgram`, whose family is\n * named by the program's hash, so it never shadows another font.\n */\nexport interface MountableFontProgram {\n readonly family: string;\n readonly weight: number;\n readonly style: 'normal' | 'italic';\n load(): Promise<Uint8Array>;\n}\n\n/**\n * Mount a program under its own family, as it is (already in the viewer's\n * namespace), in its own weight and style, so CSS asking for that style takes\n * it as it is and never fakes a bold or an italic. Refcounted per document\n * like {@link mountWebFont}; resolves with the unmount.\n */\nexport async function mountFontProgram(\n program: MountableFontProgram,\n options: { document?: Document } = {},\n): Promise<() => void> {\n const bytes = await program.load();\n return mountFace(program.family, bytes, {\n ...options,\n weight: program.weight,\n style: program.style,\n });\n}\n\n/**\n * The programs one layer has mounted: each mounted once, however often it's\n * asked for, and all let go together when the layer goes (a program another\n * layer holds stays, refcounted per document). A mount after the release\n * holds again, for a layer that comes back (React's strict mode runs effects\n * twice).\n */\nexport function createFontMounts(options: { document?: Document } = {}) {\n const held = new Map<string, Promise<() => void>>();\n return {\n mount(programs: readonly MountableFontProgram[]): void {\n for (const program of programs) {\n if (held.has(program.family)) continue;\n held.set(\n program.family,\n mountFontProgram(program, options).catch(() => () => {}),\n );\n }\n },\n release(): void {\n for (const unmount of held.values()) void unmount.then((off) => off());\n held.clear();\n },\n };\n}\n\ninterface Holder {\n face: FontFace;\n doc: Document;\n count: number;\n}\n\nconst HOLDERS = new WeakMap<Document, Map<string, Holder>>();\nconst LISTENERS = new WeakMap<Document, Set<() => void>>();\n\n/** Internal: refresh editor styles for CSS loads and explicitly mounted faces.\n * Adding an already loaded FontFace does not trigger `loadingdone`. */\nexport function observeWebFonts(doc: Document, onChange: () => void): () => void {\n let listeners = LISTENERS.get(doc);\n if (!listeners) {\n listeners = new Set();\n LISTENERS.set(doc, listeners);\n }\n listeners.add(onChange);\n doc.fonts?.addEventListener('loadingdone', onChange);\n doc.fonts?.addEventListener('loadingerror', onChange);\n return () => {\n listeners.delete(onChange);\n doc.fonts?.removeEventListener('loadingdone', onChange);\n doc.fonts?.removeEventListener('loadingerror', onChange);\n };\n}\n\nfunction notifyFontChange(doc: Document): void {\n for (const listener of LISTENERS.get(doc) ?? []) listener();\n}\n\nfunction holdersOf(doc: Document): Map<string, Holder> {\n let map = HOLDERS.get(doc);\n if (!map) {\n map = new Map();\n HOLDERS.set(doc, map);\n }\n return map;\n}\n\nfunction release(holders: Map<string, Holder>, family: string): void {\n const holder = holders.get(family);\n if (!holder) return;\n holder.count--;\n if (holder.count > 0) return;\n holders.delete(family);\n try {\n holder.doc.fonts.delete(holder.face);\n } catch {\n // a detached document: nothing to remove from\n }\n notifyFontChange(holder.doc);\n}\n","/**\n * Acrobat's line model, stated in CSS — private to the rich text binding.\n *\n * The engine lays a line out the way Acrobat does: the line box is the\n * face's ascent + descent (its `hhea` metrics) + a constant 0.2 × size of\n * leading, with the baseline at the ascent and the leading entirely below.\n * The standard 14 carry no font program, so the engine uses the metrics of\n * Acrobat's bundled substitutes (Helvetica 0.83/0.17, Times 0.784/0.216,\n * Courier 0.627/0.373); any other face — a registered font, an embedded\n * one — uses its own `hhea` table.\n *\n * Canvas provides browser metrics, which can be rounded or differ from the\n * font program's metrics. They approximate the registered font's line model;\n * standard families use the engine's known values below. CSS still chooses\n * wrapping and combines mixed runs differently from the engine.\n *\n * One thing CSS cannot express: it centres a line's leading (half above the\n * glyphs), and the substitute face's ascent differs from the engine's, so\n * the first baseline can land lower by the half-leading plus that ascent\n * gap. The binding compensates using the body's face and size; mixed runs\n * remain approximate.\n */\n\nexport interface WebFontMetrics {\n /** Ascent above the baseline, per em. */\n ascent: number;\n /** Descent below the baseline, per em. */\n descent: number;\n}\n\n/** A face's approximate CSS line model. */\nexport interface LineModel {\n ascent: number;\n descent: number;\n /** Unitless CSS line height: ascent + descent + Acrobat's 0.2 leading. */\n lineHeight: number;\n}\n\n/** Acrobat's leading: a constant share of the size, never the font's lineGap. */\nexport const ACROBAT_LEADING = 0.2;\n\n/** The engine's metrics for the standard families (Acrobat's substitutes'\n * `hhea`), keyed the engine's way: no case, spaces, hyphens, quotes. */\nconst STANDARD_METRICS: Record<string, WebFontMetrics> = {\n helvetica: { ascent: 0.83, descent: 0.17 },\n arial: { ascent: 0.83, descent: 0.17 },\n arialmt: { ascent: 0.83, descent: 0.17 },\n times: { ascent: 0.784, descent: 0.216 },\n timesroman: { ascent: 0.784, descent: 0.216 },\n timesnewroman: { ascent: 0.784, descent: 0.216 },\n courier: { ascent: 0.627, descent: 0.373 },\n couriernew: { ascent: 0.627, descent: 0.373 },\n};\n\n/** What the browser resolves when nothing can be measured: a one-em content\n * area, the ascent of the engine's Helvetica. */\nconst FALLBACK_METRICS: WebFontMetrics = { ascent: 0.83, descent: 0.17 };\n\nfunction familyKey(family: string): string {\n return family.toLowerCase().replace(/[\\s\\-_'\"]/g, '');\n}\n\n/** The first family of a CSS family list, unquoted. */\nexport function firstFamily(cssFamily: string): string {\n return (cssFamily.split(',')[0] ?? '').trim().replace(/^[\"']|[\"']$/g, '');\n}\n\n// Reuse the canvas, never its measurements: a font can be registered,\n// replaced or removed after a fallback face was measured. FontFaceSet.check\n// also returns true for missing fonts, so it cannot validate a result cache.\nconst CONTEXTS = new WeakMap<Document, CanvasRenderingContext2D>();\n\n/**\n * The browser's own ascent and descent for a CSS family list, per em,\n * measured through a canvas. `null` when the platform cannot measure\n * (no canvas, no `fontBoundingBoxAscent`). Reads the currently resolved face\n * on every call, including while the intended font is loading.\n */\nexport function webFontMetrics(\n cssFamily: string,\n doc: Document | undefined = typeof document === 'undefined' ? undefined : document,\n face: { weight?: number | string; style?: string } = {},\n): WebFontMetrics | null {\n if (!doc) return null;\n let ctx = CONTEXTS.get(doc);\n if (!ctx) {\n const context = doc.createElement('canvas').getContext('2d');\n if (!context) return null;\n ctx = context;\n CONTEXTS.set(doc, ctx);\n }\n ctx.font = `${face.style ?? 'normal'} ${face.weight ?? 400} 100px ${cssFamily}`;\n const metrics = ctx.measureText('Hg') as TextMetrics & {\n fontBoundingBoxAscent?: number;\n fontBoundingBoxDescent?: number;\n };\n if (\n typeof metrics.fontBoundingBoxAscent !== 'number' ||\n typeof metrics.fontBoundingBoxDescent !== 'number'\n ) {\n return null;\n }\n return {\n ascent: metrics.fontBoundingBoxAscent / 100,\n descent: metrics.fontBoundingBoxDescent / 100,\n };\n}\n\n/**\n * The standard families' engine metrics, or a browser approximation for\n * other families. `measure` is injectable for tests and resolved face styles.\n */\nexport function lineModelFor(\n cssFamily: string,\n measure: (cssFamily: string) => WebFontMetrics | null = webFontMetrics,\n): LineModel {\n const metrics =\n STANDARD_METRICS[familyKey(firstFamily(cssFamily))] ?? measure(cssFamily) ?? FALLBACK_METRICS;\n return {\n ascent: metrics.ascent,\n descent: metrics.descent,\n lineHeight: Math.round((metrics.ascent + metrics.descent + ACROBAT_LEADING) * 1000) / 1000,\n };\n}\n\n/**\n * Screen px to raise a box's first line by so its baseline matches the\n * engine's: CSS puts half the leading above the glyphs and draws them with\n * the browser's face, whose ascent differs from the engine's for the\n * standard families. `fontSize` in screen px.\n */\nexport function firstLineShiftFor(\n cssFamily: string,\n fontSize: number,\n measure: (cssFamily: string) => WebFontMetrics | null = webFontMetrics,\n): number {\n const model = lineModelFor(cssFamily, measure);\n const web = measure(cssFamily) ?? { ascent: model.ascent, descent: model.descent };\n const cssBaseline = (model.lineHeight - (web.ascent + web.descent)) / 2 + web.ascent;\n return Math.round((cssBaseline - model.ascent) * fontSize * 100) / 100;\n}\n","/**\n * Rich text editor binding — the one contentEditable mechanics module for a\n * FreeText annotation, shared by every framework adapter (React, Angular,\n * Vue, Svelte…) so editing can never drift between them:\n *\n * • render a rich document (runs of style deltas over a body) into the\n * element as paragraphs of inline-styled spans\n * • serialise the element back into runs on `input` — inline styles only,\n * never computed styles, so the editor cannot invent properties\n * • map the DOM selection to flat offsets over the plain projection (and\n * back), and keep the caret through a model-driven re-render\n * • hold re-renders during IME composition, paste as plain text, forward\n * Cmd/Ctrl+B/I/U as commands\n *\n * The browser owns typing; this module only translates. The policy — what a\n * command does to the document, when to commit, what a family maps to —\n * belongs to the host (the annotation plugin), reached through the\n * structural {@link RichTextEditorHost}: @embedpdf/web imports no plugin,\n * per the layering law. The return shape is a Svelte action; the other\n * frameworks call the same two functions from their effects.\n *\n * Offsets: positions in the plain projection — paragraphs joined by `\\r`,\n * runs concatenated, a hard break inside a run is a `\\r` too. The same\n * arithmetic the core's run algebra uses.\n *\n * Known limits, by design: the browser's native undo stack does not span a\n * host-driven restyle; DOM line wrapping approximates the engine's layout,\n * the appearance stream is the truth.\n */\n\nimport { fontKeyOfWebFamily, observeWebFonts } from './web-font';\nimport {\n firstLineShiftFor,\n lineModelFor,\n webFontMetrics,\n type WebFontMetrics,\n} from './web-font-metrics';\n\nexport type RichTextEditorDecoration = 'underline' | 'line-through' | 'word';\nexport type RichTextEditorScript = 'normal' | 'sub' | 'super';\nexport type RichTextEditorAlign = 'left' | 'center' | 'right' | 'justify';\n\n/** A run's style delta — the engine's `RichTextRunStyle`, structurally. */\nexport interface RichTextEditorStyle {\n family?: string;\n weight?: number;\n italic?: boolean;\n size?: number;\n color?: string;\n decoration?: RichTextEditorDecoration[];\n script?: RichTextEditorScript;\n letterSpacing?: number;\n horizontalScale?: number;\n unknown?: string;\n}\n\nexport interface RichTextEditorRun {\n text: string;\n style?: RichTextEditorStyle;\n}\n\nexport interface RichTextEditorParagraph {\n align?: RichTextEditorAlign;\n dir?: 'ltr' | 'rtl';\n runs: RichTextEditorRun[];\n}\n\n/** The document the editor renders and produces (the engine's input shape). */\nexport interface RichTextEditorDocument {\n paragraphs: RichTextEditorParagraph[];\n}\n\nexport interface RichTextEditorRange {\n start: number;\n end: number;\n}\n\nexport type RichTextEditorCommand = 'bold' | 'italic' | 'underline';\n\n/** What the binding needs from the plugin — structural, no plugin import. */\nexport interface RichTextEditorHost {\n /** The element's content changed by typing: here is the serialised document. */\n onInput(doc: RichTextEditorDocument): void;\n /** The selection inside the editor moved (null: the editor has none). */\n onSelectionChange(range: RichTextEditorRange | null): void;\n /** A keyboard command the host applies to the current selection. */\n onCommand(command: RichTextEditorCommand): void;\n /** The CSS family list for a PDF family (\"Helvetica\" → a web stack, a\n * registered family → the family its face is mounted under, see `mountWebFont`). */\n cssFontFamily(family: string): string;\n}\n\nexport interface RichTextEditorProps {\n document: RichTextEditorDocument;\n /** Screen px per page unit (the page scale): sizes are page points. */\n scale: number;\n}\n\nexport interface RichTextEditorBinding {\n update(props: RichTextEditorProps): void;\n /** The current selection as flat offsets, or null when not in the editor. */\n selection(): RichTextEditorRange | null;\n /** Place the selection (after the host restyled the document). */\n select(range: RichTextEditorRange): void;\n detach(): void;\n}\n\n// ---- Minimal DOM shapes (so the pure parts test with fake nodes) ---------------\n\nconst ELEMENT_NODE = 1;\nconst TEXT_NODE = 3;\n\nexport interface EditorStyle {\n marginTop?: string;\n lineHeight?: string;\n fontWeight: string;\n fontStyle: string;\n textDecoration: string;\n textDecorationLine?: string;\n color: string;\n fontSize: string;\n fontFamily: string;\n verticalAlign: string;\n letterSpacing: string;\n textAlign: string;\n direction: string;\n}\n\nexport interface EditorNode {\n nodeType: number;\n nodeName: string;\n nodeValue?: string | null;\n childNodes: ArrayLike<EditorNode>;\n parentNode?: EditorNode | null;\n style?: EditorStyle;\n}\n\nexport interface EditorElement extends EditorNode {\n style: EditorStyle;\n appendChild(node: EditorNode): unknown;\n}\n\nexport interface EditorDocumentFactory {\n createElement(tag: string): EditorElement;\n createTextNode(text: string): EditorNode;\n}\n\nexport interface EditorRoot extends EditorNode {\n ownerDocument: EditorDocumentFactory | null;\n appendChild(node: EditorNode): unknown;\n replaceChildren?(...nodes: EditorNode[]): void;\n removeChild?(node: EditorNode): unknown;\n firstChild?: EditorNode | null;\n}\n\nfunction isBlock(node: EditorNode): boolean {\n if (node.nodeType !== ELEMENT_NODE) return false;\n const name = node.nodeName.toUpperCase();\n return name === 'DIV' || name === 'P';\n}\n\nfunction isBreak(node: EditorNode): boolean {\n return node.nodeType === ELEMENT_NODE && node.nodeName.toUpperCase() === 'BR';\n}\n\nfunction lastChild(node: EditorNode): EditorNode | undefined {\n return node.childNodes[node.childNodes.length - 1];\n}\n\n/**\n * A `<br>` that nothing follows inside its block (or the root) is the\n * browser's caret placeholder, not content — even wrapped in a span, as\n * Chrome does for a new styled line. Every other break is a hard line break.\n */\nfunction isTrailingBreak(node: EditorNode, root: EditorNode): boolean {\n for (let current: EditorNode = node; ; ) {\n const parent = current.parentNode;\n if (!parent) return false;\n if (lastChild(parent) !== current) return false;\n if (parent === root || isBlock(parent)) return true;\n current = parent;\n }\n}\n\nfunction contains(root: EditorNode, node: EditorNode | null | undefined): boolean {\n for (let current = node; current; current = current.parentNode) if (current === root) return true;\n return false;\n}\n\n// ---- Style ↔ CSS ---------------------------------------------------------------\n\nconst HEX = /^#([0-9a-f]{6})$/i;\nconst RGB = /^rgba?\\(\\s*(\\d+)\\s*,\\s*(\\d+)\\s*,\\s*(\\d+)/i;\n\nfunction cssColorToHex(css: string): string | undefined {\n const trimmed = css.trim();\n if (HEX.test(trimmed)) return trimmed.toUpperCase();\n const match = RGB.exec(trimmed);\n if (!match) return undefined;\n const hex = (channel: string) =>\n Math.max(0, Math.min(255, Number(channel)))\n .toString(16)\n .padStart(2, '0');\n return `#${hex(match[1]!)}${hex(match[2]!)}${hex(match[3]!)}`.toUpperCase();\n}\n\nfunction firstFamily(css: string): string | undefined {\n const first = css\n .split(',')[0]\n ?.trim()\n .replace(/^[\"']|[\"']$/g, '');\n return first ? first : undefined;\n}\n\nfunction round2(value: number): number {\n return Math.round(value * 100) / 100;\n}\n\n/** The inline style declarations a span carries, as a style delta. Only what\n * the span itself sets: an unset property means \"inherit\", never a value. */\nexport function styleDeltaOf(style: EditorStyle | undefined, scale: number): RichTextEditorStyle {\n const delta: RichTextEditorStyle = {};\n if (!style) return delta;\n const weight = style.fontWeight;\n if (weight) {\n const parsed = weight === 'bold' ? 700 : weight === 'normal' ? 400 : Number(weight);\n if (Number.isFinite(parsed) && parsed > 0) delta.weight = parsed;\n }\n if (style.fontStyle) delta.italic = style.fontStyle === 'italic' || style.fontStyle === 'oblique';\n const decoration = style.textDecorationLine || style.textDecoration;\n if (decoration) {\n delta.decoration = decoration\n .split(/\\s+/)\n .filter(\n (keyword): keyword is 'underline' | 'line-through' =>\n keyword === 'underline' || keyword === 'line-through',\n );\n }\n if (style.color) {\n const hex = cssColorToHex(style.color);\n if (hex) delta.color = hex;\n }\n const script = style.verticalAlign;\n if (script === 'sub' || script === 'super') delta.script = script;\n if (style.fontSize && !delta.script) {\n const px = parseFloat(style.fontSize);\n if (Number.isFinite(px) && scale > 0) delta.size = round2(px / scale);\n }\n if (style.fontFamily) {\n // A registered font is drawn in the family it is mounted under; the run names its key.\n const family = firstFamily(style.fontFamily);\n if (family) delta.family = fontKeyOfWebFamily(family);\n }\n if (style.letterSpacing) {\n const px = parseFloat(style.letterSpacing);\n if (Number.isFinite(px) && scale > 0) delta.letterSpacing = round2(px / scale);\n }\n return delta;\n}\n\nfunction applyStyleDelta(\n style: EditorStyle,\n delta: RichTextEditorStyle,\n scale: number,\n cssFontFamily: (family: string) => string,\n): void {\n if (delta.weight !== undefined) style.fontWeight = String(delta.weight);\n if (delta.italic !== undefined) style.fontStyle = delta.italic ? 'italic' : 'normal';\n if (delta.decoration !== undefined) {\n const lines = delta.decoration.map((decoration) =>\n decoration === 'word' ? 'underline' : decoration,\n );\n style.textDecoration = lines.length ? [...new Set(lines)].join(' ') : 'none';\n }\n if (delta.color !== undefined) style.color = delta.color;\n if (delta.script !== undefined && delta.script !== 'normal') {\n // The engine's sub/superscript ratio; the size itself stays inherited.\n style.verticalAlign = delta.script;\n style.fontSize = '0.66em';\n } else if (delta.size !== undefined) {\n style.fontSize = `${delta.size * scale}px`;\n }\n if (delta.family !== undefined) style.fontFamily = cssFontFamily(delta.family);\n if (delta.letterSpacing !== undefined) style.letterSpacing = `${delta.letterSpacing * scale}px`;\n}\n\nfunction mergeDelta(base: RichTextEditorStyle, over: RichTextEditorStyle): RichTextEditorStyle {\n return { ...base, ...over };\n}\n\nfunction sameDelta(\n left: RichTextEditorStyle | undefined,\n right: RichTextEditorStyle | undefined,\n): boolean {\n const da = (left ?? {}) as Record<string, unknown>;\n const db = (right ?? {}) as Record<string, unknown>;\n const keys = new Set([...Object.keys(da), ...Object.keys(db)]);\n for (const key of keys) {\n const va = da[key];\n const vb = db[key];\n if (Array.isArray(va) || Array.isArray(vb)) {\n const sa = [...((va as string[] | undefined) ?? [])].sort().join(' ');\n const sb = [...((vb as string[] | undefined) ?? [])].sort().join(' ');\n if (sa !== sb) return false;\n } else if (va !== vb) {\n return false;\n }\n }\n return true;\n}\n\n// ---- Render -------------------------------------------------------------------\n\n/** Render a document into the element: one block per paragraph, one span per\n * styled run, `<br>` for a hard break, a placeholder `<br>` at the end of a\n * block that is empty or ends on a break so the caret has a home. */\nexport function renderRichText(\n root: EditorRoot,\n doc: RichTextEditorDocument,\n options: { scale: number; cssFontFamily: (family: string) => string },\n): void {\n const factory = root.ownerDocument;\n if (!factory) return;\n const blocks: EditorElement[] = [];\n const paragraphs = doc.paragraphs.length ? doc.paragraphs : [{ runs: [{ text: '' }] }];\n for (const paragraph of paragraphs) {\n const block = factory.createElement('div');\n if (paragraph.align) block.style.textAlign = paragraph.align;\n if (paragraph.dir) block.style.direction = paragraph.dir;\n let endsWithBreak = false;\n for (const run of paragraph.runs) {\n // A styled run keeps its hard breaks inside its span, so the break\n // inherits the run's style and serialises back into the same run.\n let container: EditorElement = block;\n if (run.style && Object.keys(run.style).length) {\n container = factory.createElement('span');\n applyStyleDelta(container.style, run.style, options.scale, options.cssFontFamily);\n block.appendChild(container);\n }\n const pieces = run.text.split(/\\r\\n|\\r|\\n/);\n for (let i = 0; i < pieces.length; i++) {\n if (i > 0) {\n container.appendChild(factory.createElement('br'));\n endsWithBreak = true;\n }\n const piece = pieces[i]!;\n if (!piece) continue;\n container.appendChild(factory.createTextNode(piece));\n endsWithBreak = false;\n }\n }\n if (endsWithBreak || !lastChild(block)) block.appendChild(factory.createElement('br'));\n blocks.push(block);\n }\n if (root.replaceChildren) {\n root.replaceChildren(...blocks);\n } else {\n while (root.firstChild && root.removeChild) root.removeChild(root.firstChild);\n for (const block of blocks) root.appendChild(block);\n }\n}\n\n/** Keep the baseline adjustment on the first block after Enter clones it. */\nexport function applyFirstLineShift(root: EditorNode, shift: number | undefined): void {\n for (let i = 0; i < root.childNodes.length; i++) {\n const node = root.childNodes[i]!;\n if (!node.style) continue;\n node.style.marginTop = i === 0 && shift ? `${-shift}px` : '';\n }\n}\n\n// ---- Serialise ----------------------------------------------------------------\n\ninterface Walk {\n root: EditorNode;\n scale: number;\n paragraphs: RichTextEditorParagraph[];\n current: RichTextEditorParagraph | null;\n}\n\nfunction pushText(walk: Walk, text: string, style: RichTextEditorStyle): void {\n if (!walk.current) {\n walk.current = { runs: [] };\n walk.paragraphs.push(walk.current);\n }\n const runs = walk.current.runs;\n const delta = Object.keys(style).length ? style : undefined;\n const last = runs[runs.length - 1];\n if (last && sameDelta(last.style, delta)) {\n last.text += text;\n } else {\n runs.push(delta ? { text, style: delta } : { text });\n }\n}\n\nfunction walkNode(node: EditorNode, inherited: RichTextEditorStyle, walk: Walk): void {\n if (node.nodeType === TEXT_NODE) {\n const text = (node.nodeValue ?? '').replace(/\\r\\n|\\n/g, '\\r');\n if (text) pushText(walk, text, inherited);\n return;\n }\n if (node.nodeType !== ELEMENT_NODE) return;\n if (isBreak(node)) {\n if (!isTrailingBreak(node, walk.root)) pushText(walk, '\\r', inherited);\n return;\n }\n const name = node.nodeName.toUpperCase();\n let style = inherited;\n if (name === 'B' || name === 'STRONG') style = mergeDelta(style, { weight: 700 });\n if (name === 'I' || name === 'EM') style = mergeDelta(style, { italic: true });\n if (name === 'U') style = mergeDelta(style, { decoration: ['underline'] });\n if (name === 'SUB') style = mergeDelta(style, { script: 'sub' });\n if (name === 'SUP') style = mergeDelta(style, { script: 'super' });\n style = mergeDelta(style, styleDeltaOf(node.style, walk.scale));\n\n if (isBlock(node)) {\n // A block starts a paragraph: the top-level ones by design, nested ones\n // because the browser wrapped a line during an edit.\n const paragraph: RichTextEditorParagraph = { runs: [] };\n const align = node.style?.textAlign;\n if (align === 'left' || align === 'center' || align === 'right' || align === 'justify') {\n paragraph.align = align;\n }\n if (node.style?.direction === 'rtl') paragraph.dir = 'rtl';\n walk.paragraphs.push(paragraph);\n walk.current = paragraph;\n for (let i = 0; i < node.childNodes.length; i++) walkNode(node.childNodes[i]!, style, walk);\n walk.current = null; // inline content after a block starts a new paragraph\n return;\n }\n for (let i = 0; i < node.childNodes.length; i++) walkNode(node.childNodes[i]!, style, walk);\n}\n\n/** The element's content as a document. Inline styles (and the semantic\n * b/i/u/sub/sup tags a browser may insert) only. */\nexport function serializeRichText(root: EditorNode, scale: number): RichTextEditorDocument {\n const walk: Walk = { root, scale, paragraphs: [], current: null };\n for (let i = 0; i < root.childNodes.length; i++) walkNode(root.childNodes[i]!, {}, walk);\n for (const paragraph of walk.paragraphs) {\n if (paragraph.runs.length === 0) paragraph.runs.push({ text: '' });\n }\n if (walk.paragraphs.length === 0) walk.paragraphs.push({ runs: [{ text: '' }] });\n return { paragraphs: walk.paragraphs };\n}\n\n// ---- Offsets ------------------------------------------------------------------\n\nexport interface EditorPosition {\n node: EditorNode;\n offset: number;\n}\n\ninterface OffsetEvent {\n kind: 'enter' | 'exit' | 'text' | 'break';\n node: EditorNode;\n /** Flat offset where this node's content starts (text/break) or where the\n * element begins/ends. */\n at: number;\n length: number;\n}\n\n/** The element in document order, each node with its flat offset — counted\n * like the projection: text length, 1 per hard break, 1 between blocks. */\nfunction collectOffsets(root: EditorNode): OffsetEvent[] {\n const events: OffsetEvent[] = [];\n let offset = 0;\n let blocks = 0;\n const visit = (node: EditorNode): void => {\n if (node.nodeType === TEXT_NODE) {\n const length = (node.nodeValue ?? '').length;\n events.push({ kind: 'text', node, at: offset, length });\n offset += length;\n return;\n }\n if (node.nodeType !== ELEMENT_NODE) return;\n if (isBreak(node)) {\n if (isTrailingBreak(node, root)) return;\n events.push({ kind: 'break', node, at: offset, length: 1 });\n offset += 1;\n return;\n }\n if (isBlock(node)) {\n if (blocks > 0) offset += 1; // the paragraph separator\n blocks++;\n }\n events.push({ kind: 'enter', node, at: offset, length: 0 });\n for (let i = 0; i < node.childNodes.length; i++) visit(node.childNodes[i]!);\n events.push({ kind: 'exit', node, at: offset, length: 0 });\n };\n for (let i = 0; i < root.childNodes.length; i++) visit(root.childNodes[i]!);\n return events;\n}\n\n/** The flat offset of a DOM position (a node and an offset within it). */\nexport function offsetOfPosition(root: EditorNode, position: EditorPosition): number {\n if (!contains(root, position.node)) return 0;\n const events = collectOffsets(root);\n if (position.node.nodeType === TEXT_NODE) {\n const event = events.find((event) => event.kind === 'text' && event.node === position.node);\n return event ? event.at + Math.min(position.offset, event.length) : 0;\n }\n if (isBreak(position.node)) {\n const event = events.find((event) => event.kind === 'break' && event.node === position.node);\n if (event) return event.at + (position.offset > 0 ? 1 : 0);\n const exit = events.find(\n (event) => event.kind === 'exit' && event.node === position.node.parentNode,\n );\n return exit ? exit.at : 0;\n }\n // An element position: before its Nth child, or past its last child.\n const child = position.node.childNodes[position.offset];\n if (child) {\n const event = events.find(\n (event) => event.node === child || (event.kind !== 'exit' && contains(child, event.node)),\n );\n if (event) return event.at;\n }\n if (position.node === root) {\n const last = events[events.length - 1];\n return last ? last.at + last.length : 0;\n }\n const exit = events.find((event) => event.kind === 'exit' && event.node === position.node);\n return exit ? exit.at : 0;\n}\n\nfunction indexIn(parent: EditorNode, node: EditorNode): number {\n for (let i = 0; i < parent.childNodes.length; i++) if (parent.childNodes[i] === node) return i;\n return 0;\n}\n\n/** The DOM position of a flat offset: inside a text node when one covers it,\n * else the empty block / the slot after a hard break the offset names. */\nexport function positionOfOffset(root: EditorNode, offset: number): EditorPosition | null {\n const events = collectOffsets(root);\n const text = events.find(\n (event) => event.kind === 'text' && offset >= event.at && offset <= event.at + event.length,\n );\n if (text) return { node: text.node, offset: offset - text.at };\n const block = events.find(\n (event) => event.kind === 'enter' && isBlock(event.node) && event.at === offset,\n );\n if (block) return { node: block.node, offset: 0 };\n const after = events.find((event) => event.kind === 'break' && event.at + 1 === offset);\n if (after && after.node.parentNode) {\n return { node: after.node.parentNode, offset: indexIn(after.node.parentNode, after.node) + 1 };\n }\n for (let i = events.length - 1; i >= 0; i--) {\n const event = events[i]!;\n if (event.kind === 'text') return { node: event.node, offset: event.length };\n }\n const first = root.childNodes[0];\n return first ? { node: first, offset: 0 } : null;\n}\n\n// ---- The binding --------------------------------------------------------------\n\n/** What the binding reads off a live DOM range (a `Range` or a `StaticRange`). */\ninterface RangeLike {\n startContainer: Node;\n startOffset: number;\n endContainer: Node;\n endOffset: number;\n}\n\n/** The selection's range as the shadow tree sees it: the standard\n * `getComposedRanges` (options form, then the earlier positional form). */\nfunction composedRange(selection: Selection, shadowRoot: ShadowRoot): RangeLike | null {\n const composable = selection as Selection & {\n getComposedRanges?: (...args: unknown[]) => RangeLike[];\n };\n if (typeof composable.getComposedRanges !== 'function') return null;\n for (const args of [[{ shadowRoots: [shadowRoot] }], [shadowRoot]]) {\n try {\n const ranges = composable.getComposedRanges(...args);\n if (ranges.length) return ranges[0]!;\n } catch {\n // an engine with the other signature\n }\n }\n return null;\n}\n\nfunction documentsEqual(left: RichTextEditorDocument, right: RichTextEditorDocument): boolean {\n if (left.paragraphs.length !== right.paragraphs.length) return false;\n for (let i = 0; i < left.paragraphs.length; i++) {\n const pa = left.paragraphs[i]!;\n const pb = right.paragraphs[i]!;\n if ((pa.align ?? 'left') !== (pb.align ?? 'left') || (pa.dir ?? 'ltr') !== (pb.dir ?? 'ltr')) {\n return false;\n }\n if (pa.runs.length !== pb.runs.length) return false;\n for (let j = 0; j < pa.runs.length; j++) {\n const ra = pa.runs[j]!;\n const rb = pb.runs[j]!;\n if (ra.text !== rb.text || !sameDelta(ra.style, rb.style)) return false;\n }\n }\n return true;\n}\n\n/** Bind one contentEditable element. Returns the Svelte-action-shaped handle. */\nexport function attachRichTextEditor(\n element: HTMLElement,\n host: RichTextEditorHost,\n initial: RichTextEditorProps,\n): RichTextEditorBinding {\n const root = element as unknown as EditorRoot;\n const doc = element.ownerDocument;\n // Inside a shadow tree (a custom-element viewer) the document's own\n // `activeElement` and selection are retargeted to the host: focus and the\n // selection have to be read from the shadow root instead.\n const rootNode = element.getRootNode() as Document | ShadowRoot;\n const activeElement = (): Element | null => rootNode.activeElement;\n const liveRange = (): RangeLike | null => {\n const selection = doc.getSelection();\n if (!selection) return null;\n if (rootNode !== doc) {\n const composed = composedRange(selection, rootNode as ShadowRoot);\n if (composed) return composed;\n const shadowSel = (\n rootNode as ShadowRoot & { getSelection?(): Selection | null }\n ).getSelection?.();\n if (shadowSel && shadowSel.rangeCount > 0) return shadowSel.getRangeAt(0);\n }\n return selection.rangeCount > 0 ? selection.getRangeAt(0) : null;\n };\n let props = initial;\n let rendered: RichTextEditorDocument | null = null;\n let composing = false;\n\n const currentSelection = (): RichTextEditorRange | null => {\n const range = liveRange();\n if (!range) return null;\n const anchor = range.startContainer as unknown as EditorNode;\n const focus = range.endContainer as unknown as EditorNode;\n if (!contains(root, anchor) || !contains(root, focus)) return null;\n const start = offsetOfPosition(root, { node: anchor, offset: range.startOffset });\n const end = offsetOfPosition(root, { node: focus, offset: range.endOffset });\n return { start: Math.min(start, end), end: Math.max(start, end) };\n };\n\n const select = (range: RichTextEditorRange) => {\n const selection = doc.getSelection();\n if (!selection) return;\n const start = positionOfOffset(root, range.start);\n const end = positionOfOffset(root, range.end);\n if (!start || !end) return;\n // `setBaseAndExtent` places a selection inside a shadow tree in every\n // engine; a Range added from outside is retargeted to the host in some.\n if (typeof selection.setBaseAndExtent === 'function') {\n selection.setBaseAndExtent(\n start.node as unknown as Node,\n start.offset,\n end.node as unknown as Node,\n end.offset,\n );\n return;\n }\n const domRange = doc.createRange();\n domRange.setStart(start.node as unknown as Node, start.offset);\n domRange.setEnd(end.node as unknown as Node, end.offset);\n selection.removeAllRanges();\n selection.addRange(domRange);\n };\n\n // Read resolved faces in the element's own document (including iframe\n // fonts). Refresh styles without replacing nodes, selection or composition.\n const refreshLineModel = () => {\n const bodyStyle = doc.defaultView?.getComputedStyle(element) ?? element.style;\n const body = {\n family: bodyStyle.fontFamily || 'Helvetica',\n weight: bodyStyle.fontWeight || '400',\n style: bodyStyle.fontStyle || 'normal',\n };\n const size = parseFloat(bodyStyle.fontSize || '16') || 16;\n // Deduplicate measurements only within this synchronous refresh. They\n // must be measured again after fonts or their descriptors change.\n const measured = new Map<string, WebFontMetrics | null>();\n const measureFor = (face: typeof body) => (family: string) => {\n const key = JSON.stringify([family, face.weight, face.style]);\n if (!measured.has(key)) measured.set(key, webFontMetrics(family, doc, face));\n return measured.get(key)!;\n };\n // Read all computed styles before writing line heights to avoid forcing\n // a style recalculation for each run. Native editing may nest spans.\n const runs = Array.from(element.querySelectorAll<HTMLElement>('span, b, strong, i, em')).map(\n (node) => {\n const style = doc.defaultView?.getComputedStyle(node) ?? node.style;\n const face = {\n family: style.fontFamily || body.family,\n weight: style.fontWeight || body.weight,\n style: style.fontStyle || body.style,\n };\n return { node, lineHeight: lineModelFor(face.family, measureFor(face)).lineHeight };\n },\n );\n const measure = measureFor(body);\n const lineHeight = lineModelFor(body.family, measure).lineHeight;\n const shift = firstLineShiftFor(body.family, size, measure);\n element.style.lineHeight = String(lineHeight);\n for (const run of runs) run.node.style.lineHeight = String(run.lineHeight);\n applyFirstLineShift(root, shift);\n };\n\n const render = (document: RichTextEditorDocument, keepSelection: boolean) => {\n const selection = keepSelection ? currentSelection() : null;\n renderRichText(root, document, {\n scale: props.scale,\n cssFontFamily: host.cssFontFamily,\n });\n refreshLineModel();\n rendered = document;\n if (selection) select(selection);\n };\n\n const serialiseAndReport = () => {\n // Enter splits a block by cloning its style attribute, so the first\n // block's shift would ride onto the new paragraph: keep it on the first\n // block alone, whatever the browser produced.\n refreshLineModel();\n const document = serializeRichText(root, props.scale);\n rendered = document;\n host.onInput(document);\n };\n\n const onInput = () => {\n if (!composing) serialiseAndReport();\n };\n const onCompositionStart = () => {\n composing = true;\n };\n const onCompositionEnd = () => {\n composing = false;\n serialiseAndReport();\n };\n const onKeyDown = (event: KeyboardEvent) => {\n if (!(event.metaKey || event.ctrlKey) || event.altKey) return;\n const key = event.key.toLowerCase();\n const command: RichTextEditorCommand | null =\n key === 'b' ? 'bold' : key === 'i' ? 'italic' : key === 'u' ? 'underline' : null;\n if (!command) return;\n event.preventDefault();\n host.onCommand(command);\n };\n const onPaste = (event: ClipboardEvent) => {\n event.preventDefault();\n const text = event.clipboardData?.getData('text/plain') ?? '';\n // execCommand keeps the browser's own undo stack and caret handling for\n // the insertion; the resulting `input` event serialises as usual.\n doc.execCommand('insertText', false, text);\n };\n const onSelectionChange = () => {\n if (activeElement() !== element) return;\n host.onSelectionChange(currentSelection());\n };\n\n element.addEventListener('input', onInput);\n element.addEventListener('compositionstart', onCompositionStart);\n element.addEventListener('compositionend', onCompositionEnd);\n element.addEventListener('keydown', onKeyDown);\n element.addEventListener('paste', onPaste);\n doc.addEventListener('selectionchange', onSelectionChange);\n\n render(initial.document, false);\n const stopObservingFonts = observeWebFonts(doc, refreshLineModel);\n\n return {\n update(next: RichTextEditorProps) {\n const scaleChanged = next.scale !== props.scale;\n props = next;\n // Our own input echoes back through the host unchanged: no re-render,\n // the caret stays where the browser put it. Anything else (a restyle,\n // a remote edit, a scale change) re-renders and restores the caret.\n if (composing) return;\n if (!rendered || scaleChanged || !documentsEqual(next.document, rendered)) {\n render(next.document, activeElement() === element);\n return;\n }\n // The element's font may have changed under an unchanged document (a\n // body restyle): the line model follows it without touching the DOM text.\n refreshLineModel();\n },\n selection: currentSelection,\n select,\n detach() {\n stopObservingFonts();\n element.removeEventListener('input', onInput);\n element.removeEventListener('compositionstart', onCompositionStart);\n element.removeEventListener('compositionend', onCompositionEnd);\n element.removeEventListener('keydown', onKeyDown);\n element.removeEventListener('paste', onPaste);\n doc.removeEventListener('selectionchange', onSelectionChange);\n },\n };\n}\n","/**\n * How a layer draws an item now, the same rule for annotations and form\n * widgets: the engine's picture of it, or its own drawing (an annotation's\n * scene, a widget's painter).\n *\n * The picture shows while nothing about the item is pending and the picture\n * is of the item's version (its owner's `pictureVersion`). Otherwise the item\n * is drawn live: a value or a restyle shows at once, and a page's refetch\n * never shows an older look. An item with no live drawing (a stamp, a link)\n * keeps the newest picture there is, so nothing is ever blank. Until the\n * page's pictures have loaded once only what's pending is drawn: a picture\n * the engine drew is never swapped in over a live drawing at the first load.\n */\nimport type { AppearanceUrl } from './object-urls';\n\nexport type PictureOrPainting<Painting> =\n | { readonly kind: 'picture'; readonly picture: AppearanceUrl }\n | { readonly kind: 'painting'; readonly painting: Painting }\n | null;\n\nexport interface LookState<Painting> {\n /** The owner draws the item live now: a pending change, a widget's look not confirmed yet. */\n readonly pending: boolean;\n /** Its picture, for the state it shows; `null` when there is none. */\n readonly picture: AppearanceUrl | null;\n /** The item's picture version now. */\n readonly version: number;\n /** The page's pictures have loaded at least once. */\n readonly loaded: boolean;\n /** Its live drawing; `null` for an item that has none. */\n readonly paint: () => Painting | null;\n}\n\nexport function pictureOrPainting<Painting>(\n look: LookState<Painting>,\n): PictureOrPainting<Painting> {\n if (!look.pending && !look.loaded) return null;\n if (look.picture && look.picture.version === look.version && !look.pending) {\n return { kind: 'picture', picture: look.picture };\n }\n const painting = look.paint();\n if (painting) return { kind: 'painting', painting };\n return look.picture ? { kind: 'picture', picture: look.picture } : null;\n}\n","/**\n * The `--epdf-*` CSS variables that theme EmbedPDF, and the CSS value each\n * painted part uses. Every paint setting (a color, a line width, a dash, a\n * shadow, an opacity) can also come from a variable, and any variable wins over\n * any setting: a part looks for its own variable, then the variables of the\n * parts its default follows (another part, its plugin's accent, the viewer's\n * accent), and only then uses its setting's value. Parts you style yourself\n * have no setting, only a variable and a built-in look.\n *\n * {@link EPDF_VARIABLES} is plain data with no imports, so a script can read the\n * list without a browser. {@link epdfThemeVariables} turns a theme written with\n * setting names into the variables.\n */\n\n/**\n * A part other parts' defaults follow: an accent, or a part whose setting is\n * another part's default, such as the handles the rotation handle looks like.\n */\nexport type EpdfFollowedVariable =\n | 'accent'\n | 'annotation-accent'\n | 'annotation-handle-fill'\n | 'annotation-handle-stroke';\n\n/** One themable part, named by its variable without the `--epdf-` prefix. */\nexport interface EpdfVariableDefinition {\n /**\n * The setting the variable overrides, as `<plugin>.<path>`: `viewer.` for the\n * viewer's own settings, `[]` for every entry of a list. Absent for a part you\n * style yourself, which only has CSS.\n */\n readonly setting?: string;\n /**\n * The part whose setting is this part's default, looked up right after the\n * part's own variable, and followed in turn: the rotation handle's stroke\n * follows the handles' stroke, which follows the annotation accent, which\n * follows the viewer's. So `--epdf-annotation-handle-stroke` reaches the\n * rotation handle before any accent does.\n */\n readonly follows?: EpdfFollowedVariable;\n /** A translucent default, the accent it follows at this percent: see {@link mixAccent}. */\n readonly accentPercent?: number;\n /**\n * The CSS value of each keyword, for a setting that takes keywords instead of\n * a CSS value.\n */\n readonly keywords?: Readonly<Record<string, string>>;\n /** What the part paints when neither its setting nor a variable is set. */\n readonly default: string;\n}\n\n/**\n * A dash variable holds an SVG dash array, while its setting says `'solid'` or\n * `'dashed'`: solid has no dashes, and dashed is 4 pixels on, 3 off.\n */\nconst DASH_KEYWORDS = { solid: 'none', dashed: '4 3' } as const;\n\n/** Every theming variable, in the order the theming page lists them. */\nexport const EPDF_VARIABLES = {\n // ── the viewer ──\n accent: { setting: 'viewer.accent', default: '#3858e9' },\n 'page-background': { setting: 'viewer.page.background', default: '#ffffff' },\n 'page-shadow': { setting: 'viewer.page.shadow', default: '0 6px 18px rgb(0 0 0 / 0.18)' },\n\n // ── annotations: annotationPlugin({ chrome }) and each tool ──\n 'annotation-accent': {\n setting: 'annotation.chrome.accent',\n follows: 'accent',\n default: '#3858e9',\n },\n 'annotation-outline': {\n setting: 'annotation.chrome.outline.color',\n follows: 'annotation-accent',\n default: '#3858e9',\n },\n 'annotation-outline-width': { setting: 'annotation.chrome.outline.width', default: '1' },\n 'annotation-outline-dash': {\n setting: 'annotation.chrome.outline.style',\n keywords: DASH_KEYWORDS,\n default: 'none',\n },\n 'annotation-handle-fill': { setting: 'annotation.chrome.handles.fill', default: '#ffffff' },\n 'annotation-handle-stroke': {\n setting: 'annotation.chrome.handles.stroke',\n follows: 'annotation-accent',\n default: '#3858e9',\n },\n 'annotation-rotation-handle-fill': {\n setting: 'annotation.chrome.rotationHandle.fill',\n follows: 'annotation-handle-fill',\n default: '#ffffff',\n },\n 'annotation-rotation-handle-stroke': {\n setting: 'annotation.chrome.rotationHandle.stroke',\n follows: 'annotation-handle-stroke',\n default: '#3858e9',\n },\n 'annotation-guide': { setting: 'annotation.chrome.guides.color', default: '#e91e63' },\n 'annotation-rotation-guide': {\n setting: 'annotation.chrome.guides.rotationColor',\n follows: 'annotation-accent',\n default: '#3858e9',\n },\n 'annotation-guide-width': { setting: 'annotation.chrome.guides.width', default: '1.5' },\n 'annotation-guide-dash': {\n setting: 'annotation.chrome.guides.style',\n keywords: DASH_KEYWORDS,\n default: 'none',\n },\n 'annotation-marquee-fill': {\n setting: 'annotation.chrome.marquee.fill',\n follows: 'annotation-accent',\n accentPercent: 8,\n default: 'color-mix(in srgb, #3858e9 8%, transparent)',\n },\n 'annotation-marquee-stroke': {\n setting: 'annotation.chrome.marquee.stroke',\n follows: 'annotation-accent',\n default: '#3858e9',\n },\n 'annotation-text-outline': {\n setting: 'annotation.chrome.textOutline',\n follows: 'annotation-accent',\n default: '#3858e9',\n },\n 'readout-background': {\n setting: 'annotation.chrome.readout.background',\n default: 'rgb(0 0 0 / 0.8)',\n },\n 'readout-color': { setting: 'annotation.chrome.readout.color', default: '#ffffff' },\n 'ghost-opacity': { setting: 'annotation.tools[].ghost.opacity', default: '0.5' },\n\n // ── text selection: selectionPlugin({ color, handles }) ──\n 'text-selection': {\n setting: 'selection.color',\n follows: 'accent',\n accentPercent: 35,\n default: 'color-mix(in srgb, #3858e9 35%, transparent)',\n },\n 'text-selection-handle': {\n setting: 'selection.handles.color',\n follows: 'accent',\n default: '#3858e9',\n },\n 'text-selection-handle-shadow': {\n setting: 'selection.handles.shadow',\n default: '0 1px 4px rgb(0 0 0 / 0.35)',\n },\n\n // ── search: searchPlugin({ highlight }) ──\n 'search-highlight': { setting: 'search.highlight.color', default: '#ffd500' },\n 'search-highlight-active': { setting: 'search.highlight.activeColor', default: '#ff9632' },\n 'search-blend-mode': { setting: 'search.highlight.blendMode', default: 'multiply' },\n\n // ── forms: formPlugin({ focus, fields }) ──\n 'form-focus': { setting: 'form.focus.color', follows: 'accent', default: '#3858e9' },\n 'form-field-border': {\n setting: 'form.fields.border',\n follows: 'accent',\n accentPercent: 55,\n default: 'color-mix(in srgb, #3858e9 55%, transparent)',\n },\n 'form-field-background': {\n setting: 'form.fields.background',\n default: 'rgb(255 255 255 / 0.92)',\n },\n 'form-field-color': { setting: 'form.fields.color', default: '#1f2a44' },\n\n // ── parts you style yourself: CSS only ──\n 'scrollbar-thumb': { default: 'rgb(0 0 0 / 0.4)' },\n 'toolbar-surface': { default: '#ffffff' },\n 'toolbar-border': { default: 'rgb(0 0 0 / 0.15)' },\n 'toolbar-active': { default: 'rgb(0 0 0 / 0.12)' },\n 'font-mono': { default: 'ui-monospace, monospace' },\n} as const satisfies Record<string, EpdfVariableDefinition>;\n\ntype Definitions = typeof EPDF_VARIABLES;\n\n/** A theming variable's name, without its `--epdf-` prefix. */\nexport type EpdfVariable = keyof Definitions;\n\n/** A variable that overrides a setting: its part paints with {@link paint}. */\nexport type EpdfSettingVariable = {\n [Name in EpdfVariable]: Definitions[Name] extends { readonly setting: string } ? Name : never;\n}[EpdfVariable];\n\n/** A part you style yourself, which has no setting: it paints with {@link paintDefault}. */\nexport type EpdfCssOnlyVariable = Exclude<EpdfVariable, EpdfSettingVariable>;\n\n/** A part whose default is the accent at a percent: that default comes from {@link mixAccent}. */\nexport type EpdfTranslucentVariable = {\n [Name in EpdfVariable]: Definitions[Name] extends { readonly accentPercent: number }\n ? Name\n : never;\n}[EpdfVariable];\n\n/**\n * The CSS value for a painted part: its own variable, then the variables of the\n * parts its default follows (the part it follows, its plugin's accent,\n * `--epdf-accent`), then the setting's value. Put it in a style (`style.stroke`),\n * not an SVG attribute, because attributes don't read `var()`.\n *\n * `paint('annotation-outline', '#3858e9')` is\n * `var(--epdf-annotation-outline, var(--epdf-annotation-accent, var(--epdf-accent, #3858e9)))`,\n * `paint('annotation-rotation-handle-fill', '#ffffff')` is\n * `var(--epdf-annotation-rotation-handle-fill, var(--epdf-annotation-handle-fill, #ffffff))`,\n * and `paint('search-highlight', '#ffd500')` is `var(--epdf-search-highlight, #ffd500)`.\n */\nexport function paint(name: EpdfSettingVariable, settingValue: string | number): string {\n const definition: EpdfVariableDefinition = EPDF_VARIABLES[name];\n const value = String(settingValue);\n const css = definition.keywords?.[value] ?? value;\n // A translucent part meets its accent inside its default instead: see mixAccent().\n const followed = definition.accentPercent === undefined ? definition.follows : undefined;\n return `var(--epdf-${name}, ${followed ? paint(followed, css) : css})`;\n}\n\n/**\n * The default of a translucent part, such as \"the accent at 35%\": the accent it\n * follows, variables included, mixed with transparent. Use it as the setting's\n * value while the part's own setting is unset.\n *\n * The accent variables sit inside the mix, not in {@link paint}'s lookup: there\n * a set `--epdf-accent` would paint at full strength, and CSS can't mix a\n * variable only when it's set. So a part's own setting, when you give one, wins\n * over the accent variables, though not over the part's own variable.\n *\n * `mixAccent('text-selection', '#3858e9')` is\n * `color-mix(in srgb, var(--epdf-accent, #3858e9) 35%, transparent)`.\n */\nexport function mixAccent(name: EpdfTranslucentVariable, accentValue: string): string {\n const { follows, accentPercent } = EPDF_VARIABLES[name];\n return `color-mix(in srgb, ${paint(follows, accentValue)} ${accentPercent}%, transparent)`;\n}\n\n/**\n * The CSS value for a part you style yourself, which has no setting: its\n * variable, then its built-in look.\n *\n * `paintDefault('scrollbar-thumb')` is `var(--epdf-scrollbar-thumb, rgb(0 0 0 / 0.4))`.\n */\nexport function paintDefault(name: EpdfCssOnlyVariable): string {\n return `var(--epdf-${name}, ${EPDF_VARIABLES[name].default})`;\n}\n\n// ── a theme as setting names ──\n\n/** A setting's path as a theme writes it: the viewer's own at the top, a list's entries as one. */\ntype ThemePath<Setting extends string> = Setting extends `viewer.${infer Rest}`\n ? Rest\n : Setting extends `${infer Head}[]${infer Tail}`\n ? `${Head}${Tail}`\n : Setting;\n\n/** What a part takes: one of its keywords, or a CSS value. */\ntype ThemeValue<Name extends EpdfSettingVariable> = Definitions[Name] extends {\n readonly keywords: infer Keywords;\n}\n ? keyof Keywords & string\n : string | number;\n\n/** One setting as a nested object: `'page.shadow'` is `{ page?: { shadow?: … } }`. */\ntype ThemeBranch<Path extends string, Value> = Path extends `${infer Head}.${infer Rest}`\n ? { readonly [Key in Head]?: ThemeBranch<Rest, Value> }\n : { readonly [Key in Path]?: Value };\n\ntype Intersection<Union> = (Union extends unknown ? (value: Union) => void : never) extends (\n value: infer Both,\n) => void\n ? Both\n : never;\n\n/**\n * A theme by setting names, every part optional: `{ accent: '#e91e63', search: { highlight:\n * { color: '#7dd3fc' } } }`. The viewer's own settings sit at the top; each plugin's under its\n * name, as the theming page lists them.\n */\nexport type EpdfTheme = Intersection<\n {\n [Name in EpdfSettingVariable]: ThemeBranch<\n ThemePath<Definitions[Name]['setting']>,\n ThemeValue<Name>\n >;\n }[EpdfSettingVariable]\n>;\n\n/** The CSS variables of a theme, by their full name: `{ '--epdf-accent': '#e91e63' }`. */\nexport type EpdfThemeVariables = Readonly<Record<`--epdf-${EpdfVariable}`, string>>;\n\n/** The value at a dotted path of a theme, or `undefined`. */\nfunction valueAt(theme: EpdfTheme, path: readonly string[]): unknown {\n let node: unknown = theme;\n for (const key of path) {\n if (typeof node !== 'object' || node === null) return undefined;\n node = (node as Record<string, unknown>)[key];\n }\n return node;\n}\n\n/**\n * The CSS variables that set a theme, for a style on an element around the viewer: CSS variables\n * win over settings, and reach everything inside the element. A part left out keeps its own\n * look, so the parts that follow the accent follow a new `accent`.\n *\n * `epdfThemeVariables({ accent: '#e91e63', page: { shadow: 'none' } })` is\n * `{ '--epdf-accent': '#e91e63', '--epdf-page-shadow': 'none' }`.\n */\nexport function epdfThemeVariables(theme: EpdfTheme): Partial<EpdfThemeVariables> {\n const variables: Partial<Record<`--epdf-${EpdfVariable}`, string>> = {};\n for (const [name, definition] of Object.entries(EPDF_VARIABLES) as [\n EpdfVariable,\n EpdfVariableDefinition,\n ][]) {\n if (!definition.setting) continue;\n const path = definition.setting\n .replace(/^viewer\\./, '')\n .replace('[]', '')\n .split('.');\n const value = valueAt(theme, path);\n if (value === undefined || value === null) continue;\n const css = String(value);\n variables[`--epdf-${name}`] = definition.keywords?.[css] ?? css;\n }\n return variables;\n}\n","/**\n * Page-space shapes in a page layer's pixels. Every layer over a page (the\n * annotation, form, link, selection and search layers) gets boxes and quads\n * in page points and places its elements in the page layer's own pixels,\n * always through the page's `toPixels`: never by multiplying with a scale of\n * its own. Plain geometry over structural shapes, the same for every\n * framework adapter.\n */\n\n/** A point in page points or in pixels. */\nexport interface PagePoint {\n x: number;\n y: number;\n}\n\n/** What a page layer converts with: its transform's `toPixels`. */\nexport interface PageToPixels {\n toPixels(point: PagePoint): PagePoint;\n}\n\n/** A box in a page layer's pixels, the shape a positioned element takes. */\nexport interface PixelRect {\n left: number;\n top: number;\n width: number;\n height: number;\n}\n\n/** A quad's four corners, as the engine names them. */\nexport interface PageQuad {\n upperLeft: PagePoint;\n upperRight: PagePoint;\n lowerRight: PagePoint;\n lowerLeft: PagePoint;\n}\n\n/** A box in page points as a box in the page layer's pixels. */\nexport function rectInPixels(\n rect: { x: number; y: number; width: number; height: number },\n page: PageToPixels,\n): PixelRect {\n const topLeft = page.toPixels({ x: rect.x, y: rect.y });\n const bottomRight = page.toPixels({ x: rect.x + rect.width, y: rect.y + rect.height });\n return {\n left: topLeft.x,\n top: topLeft.y,\n width: bottomRight.x - topLeft.x,\n height: bottomRight.y - topLeft.y,\n };\n}\n\n/**\n * A quad's corners in the page layer's pixels, in drawing order (upper left,\n * upper right, lower right, lower left). The map is affine, so mapping the\n * corners is exact, for turned text too.\n */\nexport function quadInPixels(quad: PageQuad, page: PageToPixels): PagePoint[] {\n return [quad.upperLeft, quad.upperRight, quad.lowerRight, quad.lowerLeft].map((point) =>\n page.toPixels(point),\n );\n}\n\n/** Points as an SVG `points` attribute: `\"x,y x,y …\"`. */\nexport function svgPoints(points: readonly PagePoint[]): string {\n return points.map((point) => `${point.x},${point.y}`).join(' ');\n}\n\n/** Whether a quad is an upright box: drawn as a positioned box instead of a polygon. */\nexport function isUprightQuad(quad: PageQuad): boolean {\n return (\n quad.upperLeft.y === quad.upperRight.y &&\n quad.lowerLeft.y === quad.lowerRight.y &&\n quad.upperLeft.x === quad.lowerLeft.x\n );\n}\n","/**\n * Keeping a page's own listeners out of controls drawn over it. The Stage\n * listens natively on an ancestor of every page: a press that bubbles to it\n * starts a tool gesture or reads as a click outside (ending an edit), and a\n * wheel pans or zooms the page. A control that owns its gestures stops them\n * with a native listener on itself, which runs during real DOM bubbling,\n * before the Stage's. A framework's own event system may run too late: React\n * dispatches from its root, above the Stage.\n */\n\n/**\n * Stop presses that begin inside `element` from reaching the page, and tell `onPress` about each\n * one. A control that needs the press itself (a link or a form field sending its PDF \"mouse down\"\n * action) must take it here: once the press stops at the element, a framework that dispatches\n * from its root (React) never sees it. Returns the detach.\n */\nexport function isolatePointerDown(\n element: HTMLElement,\n onPress?: (event: PointerEvent) => void,\n): () => void {\n const stop = (event: PointerEvent) => {\n event.stopPropagation();\n onPress?.(event);\n };\n element.addEventListener('pointerdown', stop);\n return () => element.removeEventListener('pointerdown', stop);\n}\n\n/**\n * Stop wheel events inside `element` from reaching the page, so the browser\n * scrolls the element itself (a list box) instead of the Stage panning or\n * zooming. Returns the detach.\n */\nexport function isolateWheel(element: HTMLElement): () => void {\n const stop = (event: Event) => event.stopPropagation();\n element.addEventListener('wheel', stop);\n return () => element.removeEventListener('wheel', stop);\n}\n","/**\n * Call `onPress` when a pointer press starts anywhere in `element`'s document\n * outside `element`: how a menu closes when the user presses somewhere else,\n * in the viewer or on the page around it. Where the press landed is read from\n * its composed path: at the document, an event from inside a shadow root is\n * retargeted to the shadow host, so `element.contains(event.target)` would\n * call every press inside a shadow-rooted menu an outside one. Listens in the\n * capture phase, so a handler that stops the press can't keep a menu open.\n * Returns the stop.\n */\nexport function observeOutsidePress(\n element: Element,\n onPress: (event: PointerEvent) => void,\n): () => void {\n const doc = element.ownerDocument;\n const listener = (event: PointerEvent) => {\n if (!event.composedPath().includes(element)) onPress(event);\n };\n doc.addEventListener('pointerdown', listener, true);\n return () => doc.removeEventListener('pointerdown', listener, true);\n}\n","/**\n * Equality for the anchors plugins hand out. An anchor read (where a menu, a\n * badge or a selection handle attaches) returns a new object every time; a\n * framework adapter compares reads with these, so the UI attached to an\n * anchor renders again only when the anchor moved. The anchors are mirrored\n * structurally, so this package stays free of EmbedPDF imports.\n */\n\ninterface AnchorPoint {\n readonly x: number;\n readonly y: number;\n}\n\ninterface AnchorBox {\n readonly x: number;\n readonly y: number;\n readonly width: number;\n readonly height: number;\n}\n\ninterface AnchorPage {\n readonly objectNumber: number;\n}\n\nconst sameBox = (left: AnchorBox, right: AnchorBox): boolean =>\n left.x === right.x &&\n left.y === right.y &&\n left.width === right.width &&\n left.height === right.height;\n\n/** A box on a page: a text selection's anchor, and the core of every other one. */\nexport interface PageBoundsAnchor {\n readonly page: AnchorPage;\n readonly bounds: AnchorBox;\n}\n\n/** The same page and box (a text selection's menu anchor). */\nexport function samePageBounds(\n left: PageBoundsAnchor | null,\n right: PageBoundsAnchor | null,\n): boolean {\n if (left === right) return true;\n if (!left || !right) return false;\n return left.page.objectNumber === right.page.objectNumber && sameBox(left.bounds, right.bounds);\n}\n\n/** The annotation selection's anchor: its page, its box, and its rotation handle when it has one. */\nexport interface SelectionAnchorShape extends PageBoundsAnchor {\n readonly rotationHandle?: AnchorPoint | null;\n}\n\n/** The same annotation selection anchor: page, box and rotation handle. */\nexport function sameSelectionAnchor(\n left: SelectionAnchorShape | null,\n right: SelectionAnchorShape | null,\n): boolean {\n if (left === right) return true;\n if (!left || !right) return false;\n return (\n samePageBounds(left, right) &&\n left.rotationHandle?.x === right.rotationHandle?.x &&\n left.rotationHandle?.y === right.rotationHandle?.y\n );\n}\n\n/** One annotation's anchor: page, box, and the view-dependent box of one that keeps its size on screen. */\nexport interface AnnotationAnchorShape extends PageBoundsAnchor {\n readonly boundsIn?: unknown;\n}\n\n/** The same annotation anchor: page, box, and how it follows the view. */\nexport function sameAnnotationAnchor(\n left: AnnotationAnchorShape | null,\n right: AnnotationAnchorShape | null,\n): boolean {\n if (left === right) return true;\n if (!left || !right) return false;\n return samePageBounds(left, right) && left.boundsIn === right.boundsIn;\n}\n\n/** A shape being drawn click by click (a polygon): where it is and how far it got. */\nexport interface CreationDraftAnchorShape extends PageBoundsAnchor {\n readonly kind: string;\n readonly subtype: string;\n readonly pointCount: number;\n readonly minPoints: number;\n readonly canFinish: boolean;\n}\n\n/** The same creation draft: what it draws, its points so far, whether it can finish, and its box. */\nexport function sameCreationDraftAnchor(\n left: CreationDraftAnchorShape | null,\n right: CreationDraftAnchorShape | null,\n): boolean {\n if (left === right) return true;\n if (!left || !right) return false;\n return (\n left.kind === right.kind &&\n left.subtype === right.subtype &&\n left.pointCount === right.pointCount &&\n left.minPoints === right.minPoints &&\n left.canFinish === right.canFinish &&\n samePageBounds(left, right)\n );\n}\n\n/** A turn in progress: the pointer's page point and the angle. */\nexport interface RotationAnchorShape {\n readonly page: AnchorPage;\n readonly at: AnchorPoint;\n readonly angle: number;\n}\n\n/** The same turn in progress: page, pointer and angle. */\nexport function sameRotationAnchor(\n left: RotationAnchorShape | null,\n right: RotationAnchorShape | null,\n): boolean {\n if (left === right) return true;\n if (!left || !right) return false;\n return (\n left.page.objectNumber === right.page.objectNumber &&\n left.at.x === right.at.x &&\n left.at.y === right.at.y &&\n left.angle === right.angle\n );\n}\n\ninterface AnchorQuad {\n readonly upperLeft: AnchorPoint;\n readonly upperRight: AnchorPoint;\n readonly lowerLeft: AnchorPoint;\n readonly lowerRight: AnchorPoint;\n}\n\nconst samePoint = (left: AnchorPoint, right: AnchorPoint): boolean =>\n left.x === right.x && left.y === right.y;\n\nconst sameQuad = (left: AnchorQuad, right: AnchorQuad): boolean =>\n samePoint(left.upperLeft, right.upperLeft) &&\n samePoint(left.upperRight, right.upperRight) &&\n samePoint(left.lowerLeft, right.lowerLeft) &&\n samePoint(left.lowerRight, right.lowerRight);\n\n/** One end of a text selection: its page, the boundary glyph's quad, and the side of the glyph. */\nexport interface SelectionEndpointShape {\n readonly page: AnchorPage;\n readonly glyphQuad: AnchorQuad;\n readonly advance: unknown;\n}\n\n/** Both ends of a text selection, where its handles sit. */\nexport interface SelectionEndpointsShape {\n readonly start: SelectionEndpointShape;\n readonly end: SelectionEndpointShape;\n}\n\n/**\n * The same selection ends. The glyph quads compare corner by corner, so an\n * end that turns without moving its bounding box still counts as moved.\n */\nexport function sameSelectionEndpoints(\n left: SelectionEndpointsShape | null,\n right: SelectionEndpointsShape | null,\n): boolean {\n if (left === right) return true;\n if (!left || !right) return false;\n return (\n left.start.page.objectNumber === right.start.page.objectNumber &&\n left.end.page.objectNumber === right.end.page.objectNumber &&\n left.start.advance === right.start.advance &&\n left.end.advance === right.end.advance &&\n sameQuad(left.start.glyphQuad, right.start.glyphQuad) &&\n sameQuad(left.end.glyphQuad, right.end.glyphQuad)\n );\n}\n","/**\n * An annotation's painted scene as SVG elements. The annotation core turns a\n * render item into its scene (`scene(item)` in `@embedpdf/core-annotation`):\n * rects, ellipses, lines, polygons, paths and text, each with its paint.\n * {@link svgShapesOf} turns that scene into one SVG element description per\n * node (its tag, its attributes under their SVG names, and its text), so a\n * framework adapter draws it with one loop and no per-kind logic. The scene's\n * types are mirrored structurally, so this package stays free of EmbedPDF\n * imports.\n */\nimport { paint } from './theme';\nimport type { MountableFontProgram } from './web-font';\n\n/** How a scene node paints: the structural twin of core-annotation's `Paint`. */\nexport interface ScenePaint {\n fill?: string;\n stroke?: string;\n /** Stroke width, in page points. */\n width?: number;\n opacity?: number;\n /** Stroke dash, in page points. */\n dash?: number[];\n blend?: string;\n lineCap?: 'round';\n join?: 'round';\n fillRule?: 'evenodd';\n}\n\ninterface SceneBox {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\ninterface ScenePoint {\n x: number;\n y: number;\n}\n\n/** One painted node: the structural twin of core-annotation's `SceneNode`. */\nexport type SceneShapeNode = (\n | { kind: 'rect'; rect: SceneBox; paint: ScenePaint }\n | { kind: 'ellipse'; rect: SceneBox; paint: ScenePaint }\n | { kind: 'line'; a: ScenePoint; b: ScenePoint; paint: ScenePaint }\n | { kind: 'poly'; points: ScenePoint[]; closed: boolean; paint: ScenePaint }\n | { kind: 'path'; d: string; paint: ScenePaint }\n | {\n kind: 'text';\n at: ScenePoint;\n text: string;\n fontSize: number;\n fontFamily?: string;\n rotation?: number;\n paint: ScenePaint;\n }\n | {\n kind: 'glyphs';\n at: ScenePoint;\n text: string;\n offsets: number[];\n fontSize: number;\n fontFamily: string;\n fontWeight?: number;\n fontStyle?: 'normal' | 'italic';\n rotation?: number;\n /** The face it draws in: whatever draws it mounts it. */\n program?: MountableFontProgram;\n paint: ScenePaint;\n }\n) & {\n /** Drawn only inside this upright box. */\n clip?: SceneBox;\n};\n\n/** The SVG elements a scene draws with; a nested `svg` clips what's inside it to its box. */\nexport type SvgShapeTag =\n | 'rect'\n | 'ellipse'\n | 'line'\n | 'path'\n | 'text'\n | 'polygon'\n | 'polyline'\n | 'svg';\n\n/**\n * One SVG element: its tag, its attributes under their SVG names\n * (`stroke-width`, `fill-rule`; an attribute left out is not set), the text\n * inside it, and its blend mode, which is CSS (`mix-blend-mode` in its\n * `style`), never an attribute.\n */\nexport interface SvgShape {\n tag: SvgShapeTag;\n attributes: Readonly<Record<string, string | number>>;\n text?: string;\n blend?: string;\n /** What a clipping `svg` holds, in drawing order. */\n children?: SvgShape[];\n /** The face a `text` draws in: the renderer mounts it (`fontProgramsOf`). */\n program?: MountableFontProgram;\n}\n\n/** The faces shapes draw their text in, each once: a renderer mounts them while it shows the shapes. */\nexport function fontProgramsOf(shapes: readonly SvgShape[]): MountableFontProgram[] {\n const programs = new Map<string, MountableFontProgram>();\n const walk = (each: readonly SvgShape[]) => {\n for (const shape of each) {\n if (shape.program) programs.set(shape.program.family, shape.program);\n if (shape.children) walk(shape.children);\n }\n };\n walk(shapes);\n return [...programs.values()];\n}\n\nexport interface SvgShapesOptions {\n /**\n * The stroke miter limit, core-annotation's `MITER_LIMIT`: it must be the\n * one the core's bounds math uses, so a sharp corner spikes or bevels the\n * same way in both.\n */\n miterLimit: number;\n}\n\n/** A node's paint as SVG presentation attributes; everything else about its look is the core's. */\nfunction paintAttributes(\n nodePaint: ScenePaint,\n miterLimit: number,\n): Record<string, string | number> {\n const attributes: Record<string, string | number> = {\n fill: nodePaint.fill ?? 'none',\n stroke: nodePaint.stroke ?? 'none',\n // Unset joins are sharp miters; round joins and caps are only for ink.\n 'stroke-linejoin': nodePaint.join ?? 'miter',\n 'stroke-miterlimit': miterLimit,\n };\n // Even-odd punches the holes of an icon; unset is SVG's nonzero.\n if (nodePaint.fillRule !== undefined) attributes['fill-rule'] = nodePaint.fillRule;\n if (nodePaint.width !== undefined) attributes['stroke-width'] = nodePaint.width;\n if (nodePaint.opacity !== undefined) attributes.opacity = nodePaint.opacity;\n if (nodePaint.lineCap !== undefined) attributes['stroke-linecap'] = nodePaint.lineCap;\n if (nodePaint.dash) attributes['stroke-dasharray'] = nodePaint.dash.join(' ');\n return attributes;\n}\n\n/**\n * Each scene node as one SVG element, in drawing order, in the scene's own\n * page-point units. A clipped node comes inside an `svg` of its clip box,\n * which shows only what's inside it (no ids, so every framework draws it the\n * same).\n */\nexport function svgShapesOf(\n nodes: readonly SceneShapeNode[],\n options: SvgShapesOptions,\n): SvgShape[] {\n return nodes.map((node) => {\n const shape = svgShapeOf(node, options);\n if (!node.clip) return shape;\n const { x, y, width, height } = node.clip;\n return {\n tag: 'svg',\n attributes: {\n x,\n y,\n width,\n height,\n viewBox: `${x} ${y} ${width} ${height}`,\n overflow: 'hidden',\n },\n children: [shape],\n };\n });\n}\n\nfunction svgShapeOf(node: SceneShapeNode, options: SvgShapesOptions): SvgShape {\n const painted = paintAttributes(node.paint, options.miterLimit);\n const blend = node.paint.blend ? { blend: node.paint.blend } : {};\n switch (node.kind) {\n case 'rect':\n return {\n tag: 'rect',\n attributes: {\n x: node.rect.x,\n y: node.rect.y,\n width: node.rect.width,\n height: node.rect.height,\n ...painted,\n },\n ...blend,\n };\n case 'ellipse':\n return {\n tag: 'ellipse',\n attributes: {\n cx: node.rect.x + node.rect.width / 2,\n cy: node.rect.y + node.rect.height / 2,\n rx: node.rect.width / 2,\n ry: node.rect.height / 2,\n ...painted,\n },\n ...blend,\n };\n case 'line':\n return {\n tag: 'line',\n attributes: { x1: node.a.x, y1: node.a.y, x2: node.b.x, y2: node.b.y, ...painted },\n ...blend,\n };\n case 'path':\n return { tag: 'path', attributes: { d: node.d, ...painted }, ...blend };\n case 'text':\n return {\n tag: 'text',\n attributes: {\n x: node.at.x,\n y: node.at.y,\n ...(node.rotation\n ? { transform: `rotate(${node.rotation} ${node.at.x} ${node.at.y})` }\n : {}),\n 'font-size': node.fontSize,\n ...(node.fontFamily ? { 'font-family': node.fontFamily } : {}),\n ...painted,\n },\n text: node.text,\n ...blend,\n };\n case 'poly':\n return {\n tag: node.closed ? 'polygon' : 'polyline',\n attributes: {\n points: node.points.map((point) => `${point.x},${point.y}`).join(' '),\n ...painted,\n },\n ...blend,\n };\n case 'glyphs': {\n // One x per character: each glyph starts where the PDF starts it. A space draws nothing and\n // SVG would collapse it (and an x with it), so it's left out.\n const drawn = node.text\n .split('')\n .flatMap((character, index) =>\n /\\s/.test(character) ? [] : [{ character, x: node.at.x + (node.offsets[index] ?? 0) }],\n );\n return {\n tag: 'text',\n attributes: {\n x: drawn.map((glyph) => glyph.x).join(' '),\n y: node.at.y,\n ...(node.rotation\n ? { transform: `rotate(${node.rotation} ${node.at.x} ${node.at.y})` }\n : {}),\n 'font-size': node.fontSize,\n 'font-family': node.fontFamily,\n ...(node.fontWeight !== undefined ? { 'font-weight': node.fontWeight } : {}),\n ...(node.fontStyle !== undefined ? { 'font-style': node.fontStyle } : {}),\n ...painted,\n },\n text: drawn.map((glyph) => glyph.character).join(''),\n ...(node.program ? { program: node.program } : {}),\n ...blend,\n };\n }\n }\n}\n\n/**\n * The `viewBox` an item's scene draws in: its box, in page points. `null`\n * while the box has no area (a drawing at its first press): draw nothing then.\n *\n * The `<svg>` fills the item's frame, so the viewBox and the element's size\n * stay proportional (their ratio is the zoom). Never clamp either one: a\n * clamped element with a shrinking viewBox scales its content up, and a\n * cloudy border's scallops flood the page.\n */\nexport function sceneViewBox(box: SceneBox): string | null {\n if (box.width <= 0 || box.height <= 0) return null;\n return `${box.x} ${box.y} ${box.width} ${box.height}`;\n}\n\n/** How opaque a tool's ghost paints: `--epdf-ghost-opacity` wins over the tool's own. */\nexport function ghostOpacity(opacity: number): string {\n return paint('ghost-opacity', opacity);\n}\n","/**\n * The selection's chrome, ready to draw: its colors, widths and dashes as CSS\n * ({@link annotationChromePaint}), and each chrome node in the page layer's\n * pixels ({@link chromeInPixels}). The annotation plugin lists the chrome\n * nodes (outline, handles, the rotation handle, guides, the marquee) in page\n * points; a framework adapter only draws what these hand it. The settings and\n * the nodes are mirrored structurally, so this package stays free of EmbedPDF\n * imports.\n */\nimport type { PagePoint, PageToPixels, PixelRect } from './page-pixels';\nimport { rectInPixels, svgPoints } from './page-pixels';\nimport { mixAccent, paint } from './theme';\n\n/** The chrome settings the paint reads: the structural twin of the annotation plugin's `ChromeSettings`. */\nexport interface ChromePaintSettings {\n readonly accent: string | null;\n readonly outline: {\n readonly color: string | null;\n readonly style: string;\n readonly width: number;\n };\n readonly handles: { readonly fill: string; readonly stroke: string | null };\n readonly rotationHandle: { readonly fill: string | null; readonly stroke: string | null };\n readonly guides: {\n readonly color: string;\n readonly rotationColor: string | null;\n readonly style: string;\n readonly width: number;\n };\n readonly marquee: { readonly fill: string | null; readonly stroke: string | null };\n readonly textOutline: string | null;\n}\n\n/** A line's paint as CSS: its color, width and dash. */\nexport interface ChromeLinePaint {\n readonly stroke: string;\n readonly strokeWidth: string;\n readonly strokeDasharray: string;\n}\n\n/** A shape's paint as CSS: its fill and its stroke color. */\nexport interface ChromeShapePaint {\n readonly fill: string;\n readonly stroke: string;\n}\n\n/**\n * Every chrome color, width and dash as CSS, each its `--epdf-annotation-*`\n * variable first, then the setting. Put them in `style`: an SVG attribute\n * doesn't read `var()`.\n */\nexport interface AnnotationChromePaint {\n /** The selection box. */\n readonly outline: ChromeLinePaint;\n /** The resize and point handles. */\n readonly handle: ChromeShapePaint;\n /** The rotation handle; its stalk takes the `stroke`. */\n readonly rotationHandle: ChromeShapePaint;\n /** A snapped move's alignment guide. */\n readonly guide: ChromeLinePaint;\n /** The reference cross and the angle line while turning. */\n readonly rotationGuide: ChromeLinePaint;\n /** The box dragged to select. */\n readonly marquee: ChromeShapePaint;\n /** The outline color of a text box while someone types in it. */\n readonly textOutline: string;\n}\n\n/**\n * The chrome settings as paint. A color left `null` follows the part it\n * names, and in the end the accent: the chrome's own, else the viewer's.\n */\nexport function annotationChromePaint(\n chrome: ChromePaintSettings,\n viewerAccent: string,\n): AnnotationChromePaint {\n const accent = chrome.accent ?? viewerAccent;\n const guideLine = {\n strokeWidth: paint('annotation-guide-width', chrome.guides.width),\n strokeDasharray: paint('annotation-guide-dash', chrome.guides.style),\n };\n return {\n outline: {\n stroke: paint('annotation-outline', chrome.outline.color ?? accent),\n strokeWidth: paint('annotation-outline-width', chrome.outline.width),\n strokeDasharray: paint('annotation-outline-dash', chrome.outline.style),\n },\n handle: {\n fill: paint('annotation-handle-fill', chrome.handles.fill),\n stroke: paint('annotation-handle-stroke', chrome.handles.stroke ?? accent),\n },\n rotationHandle: {\n fill: paint(\n 'annotation-rotation-handle-fill',\n chrome.rotationHandle.fill ?? chrome.handles.fill,\n ),\n stroke: paint(\n 'annotation-rotation-handle-stroke',\n chrome.rotationHandle.stroke ?? chrome.handles.stroke ?? accent,\n ),\n },\n guide: { stroke: paint('annotation-guide', chrome.guides.color), ...guideLine },\n rotationGuide: {\n stroke: paint('annotation-rotation-guide', chrome.guides.rotationColor ?? accent),\n ...guideLine,\n },\n marquee: {\n fill: paint(\n 'annotation-marquee-fill',\n chrome.marquee.fill ?? mixAccent('annotation-marquee-fill', accent),\n ),\n stroke: paint('annotation-marquee-stroke', chrome.marquee.stroke ?? accent),\n },\n textOutline: paint('annotation-text-outline', chrome.textOutline ?? accent),\n };\n}\n\n/**\n * One chrome node as the annotation plugin lists it: the structural twin of\n * core-annotation's `ChromeNode`. `Role` is what a handle reshapes.\n */\nexport type ChromeNodeShape<Role extends string = string> =\n | { kind: 'outline'; rect: { x: number; y: number; width: number; height: number } }\n | { kind: 'obb'; corners: readonly PagePoint[] }\n | { kind: 'handle'; at: PagePoint; rot?: number; role: Role; active: boolean }\n | { kind: 'rotate-knob'; at: PagePoint; from: PagePoint }\n | { kind: 'guide'; axis: 'x' | 'y'; at: number; lo: number; hi: number }\n | {\n kind: 'rotate-guides';\n lines: ReadonlyArray<{ a: PagePoint; b: PagePoint; role: 'axis' | 'indicator' }>;\n }\n | { kind: 'marquee'; rect: { x: number; y: number; width: number; height: number } };\n\n/** A line in pixels. */\nexport interface ChromeLine {\n from: PagePoint;\n to: PagePoint;\n}\n\n/**\n * A chrome node in the page layer's pixels. What each one draws:\n * - `outline`, `marquee`: a rectangle, `box` (the marquee is always dashed `4 3`);\n * - `turned-outline`: a closed polygon through `points`, the outline of a turned selection;\n * - `handle`: a handle centred on `at`, turned by `rotation` (degrees clockwise) when square;\n * - `rotation-handle`: a round handle at `at`, its stalk from `from` on the box;\n * - `guide`: a crisp line, a snapped move's alignment;\n * - `rotation-guides`: the lines of a turn in progress, each at its `opacity`.\n */\nexport type ChromeInPixels<Role extends string = string> =\n | { kind: 'outline'; box: PixelRect }\n | { kind: 'marquee'; box: PixelRect }\n | { kind: 'turned-outline'; points: string }\n | { kind: 'handle'; at: PagePoint; rotation: number; role: Role; active: boolean }\n | { kind: 'rotation-handle'; at: PagePoint; from: PagePoint }\n | ({ kind: 'guide' } & ChromeLine)\n | { kind: 'rotation-guides'; lines: Array<ChromeLine & { opacity: number }> };\n\n/**\n * Each chrome node in the page layer's pixels, in drawing order. The plugin\n * already sized the screen-constant parts (handle grab zones, the rotation\n * handle's offset) for the page's zoom; this only places them.\n */\nexport function chromeInPixels<Role extends string>(\n nodes: readonly ChromeNodeShape<Role>[],\n page: PageToPixels,\n): ChromeInPixels<Role>[] {\n return nodes.map((node): ChromeInPixels<Role> => {\n switch (node.kind) {\n case 'handle':\n return {\n kind: 'handle',\n at: page.toPixels(node.at),\n rotation: node.rot ?? 0,\n role: node.role,\n active: node.active,\n };\n case 'guide': {\n // A through-line at the snapped edge or centre, spanning both shapes.\n const start = node.axis === 'x' ? { x: node.at, y: node.lo } : { x: node.lo, y: node.at };\n const end = node.axis === 'x' ? { x: node.at, y: node.hi } : { x: node.hi, y: node.at };\n return { kind: 'guide', from: page.toPixels(start), to: page.toPixels(end) };\n }\n case 'obb':\n // A turned selection: the closed quad through its four corners, in\n // place of the upright outline.\n return {\n kind: 'turned-outline',\n points: svgPoints(node.corners.map((corner) => page.toPixels(corner))),\n };\n case 'rotate-guides':\n // A faint 0°/90° reference cross and the line at the angle, already cut to the page.\n return {\n kind: 'rotation-guides',\n lines: node.lines.map((chord) => ({\n from: page.toPixels(chord.a),\n to: page.toPixels(chord.b),\n opacity: chord.role === 'axis' ? 0.35 : 0.8,\n })),\n };\n case 'rotate-knob':\n return {\n kind: 'rotation-handle',\n at: page.toPixels(node.at),\n from: page.toPixels(node.from),\n };\n case 'marquee':\n return { kind: 'marquee', box: rectInPixels(node.rect, page) };\n case 'outline':\n return { kind: 'outline', box: rectInPixels(node.rect, page) };\n }\n });\n}\n","/**\n * A free text box's editor on an element: the controller behind the\n * annotation layer's own text boxes and behind a renderer's element that\n * draws one itself. The shared rich-text binding ({@link attachRichTextEditor})\n * renders the runs as inline-styled spans, turns typing back into runs, maps\n * the selection to flat offsets and keeps the caret through a restyle; the\n * browser owns layout, the caret, input methods and the clipboard; the\n * annotation plugin owns the text, the range routing and the write after a\n * pause. This module adds what makes the element a text box of the layer:\n * focus and `contentEditable` follow the plugin's `editing`, never the other\n * way, and a press inside the box stays inside it. A framework binding keeps\n * one {@link createTextBoxEditorFollower} per element and hands it what\n * changed; {@link textBoxEditorScaleOf} is the scale of an editor element a\n * renderer draws itself.\n *\n * The plugin's types are mirrored structurally, so this package stays free of\n * EmbedPDF imports.\n */\nimport type { PageToPixels } from './page-pixels';\nimport { rectInPixels } from './page-pixels';\nimport {\n attachRichTextEditor,\n type RichTextEditorBinding,\n type RichTextEditorCommand,\n type RichTextEditorDocument,\n type RichTextEditorHost,\n type RichTextEditorRange,\n} from './rich-text-editor';\n\n/** A text box as the annotation plugin lists it (`listTextItems`): the fields the editor reads. */\nexport interface TextBoxEditorItem<Ref> {\n readonly id: string;\n /** `null` for a box that isn't written yet: nothing to route typing to. */\n readonly ref: Ref | null;\n readonly richText: RichTextEditorDocument;\n /** True while someone types in this box. */\n readonly editing: boolean;\n}\n\n/** The annotation plugin's text-editing calls (its host capability satisfies it). */\nexport interface TextBoxEditorAnnotation<Ref> {\n draftRichText(ref: Ref, document: RichTextEditorDocument): void;\n setTextSelection(ref: Ref, range: RichTextEditorRange | null): void;\n readonly text: { toggleFormat(format: RichTextEditorCommand): unknown };\n getCssFontFamily(family: string): string;\n getEditingId(): string | null;\n}\n\n/** An element made a text box's editor. */\nexport interface TextBoxEditor<Ref> {\n /**\n * The box as the plugin has it now, at `scale` pixels per point (or `null`\n * while there is none). Call it whenever either changes: the binding\n * redraws only when the text differs (so the caret never jumps while\n * typing), and focus follows `editing`.\n */\n update(item: TextBoxEditorItem<Ref> | null, scale: number): void;\n /** Remove the listeners and the rich-text binding. */\n detach(): void;\n}\n\n/**\n * Make `element` the editor of a text box. Typing shows at once and reaches\n * the engine in one write after a pause (or when the edit ends), not one\n * write per keystroke.\n */\nexport function attachTextBoxEditor<Ref>(\n element: HTMLElement,\n annotation: TextBoxEditorAnnotation<Ref>,\n): TextBoxEditor<Ref> {\n let item: TextBoxEditorItem<Ref> | null = null;\n let binding: RichTextEditorBinding | null = null;\n // What the element shows: `null` until the first update states it.\n let shownEditing: boolean | null = null;\n\n const host: RichTextEditorHost = {\n onInput: (document) => {\n if (item?.ref) annotation.draftRichText(item.ref, document);\n },\n onSelectionChange: (range) => {\n if (item?.ref) annotation.setTextSelection(item.ref, range);\n },\n onCommand: (command) => void annotation.text.toggleFormat(command),\n cssFontFamily: (family) => annotation.getCssFontFamily(family),\n };\n\n // A press inside the editor stays there: it must not reach the Stage's\n // listener, which reads it as a click outside (and ends the edit). The\n // browser then owns the caret and the drag inside the box.\n const stopPress = (event: Event) => event.stopPropagation();\n // The gesture that opens the editor blurs it to <body> right after it\n // takes focus; while the plugin still edits this box, take focus back. A\n // real click away ends the edit through the interaction hub first.\n const refocus = (event: FocusEvent) => {\n if (\n event.relatedTarget == null &&\n element.isConnected &&\n item &&\n annotation.getEditingId() === item.id\n ) {\n element.focus();\n }\n };\n element.addEventListener('pointerdown', stopPress);\n element.addEventListener('blur', refocus);\n\n return {\n update(next, scale) {\n item = next;\n if (next && !binding) {\n binding = attachRichTextEditor(element, host, { document: next.richText, scale });\n } else if (!next && binding) {\n binding.detach();\n binding = null;\n }\n // Model → DOM: the binding skips its own echo and redraws, caret kept,\n // for a restyle, a remote edit or a zoom; otherwise it states the line\n // model for the (maybe restyled) font again.\n if (next) binding?.update({ document: next.richText, scale });\n\n const editing = next?.editing ?? false;\n if (editing === shownEditing) return;\n shownEditing = editing;\n element.contentEditable = editing ? 'true' : 'false';\n if (editing) {\n element.focus();\n // Enter at the top, where the baked appearance anchors the text, so\n // baked → edit → baked never jumps.\n element.scrollTop = 0;\n }\n },\n detach() {\n binding?.detach();\n binding = null;\n element.removeEventListener('pointerdown', stopPress);\n element.removeEventListener('blur', refocus);\n },\n };\n}\n\n/** Keeps an element the editor of a text box while what it edits with changes. */\nexport interface TextBoxEditorFollower<Ref> {\n /**\n * The element, the plugin, the box and the scale as they are now. Call it\n * whenever any of them changes: the editor attaches when there are an\n * element and a plugin, attaches again when either is another one (a\n * document that opens anew), and gets the box and the scale every time.\n */\n follow(\n element: HTMLElement | null,\n annotation: TextBoxEditorAnnotation<Ref> | null,\n item: TextBoxEditorItem<Ref> | null,\n scale: number,\n ): void;\n /** Let go of the element, when the component that drew it goes away. */\n detach(): void;\n}\n\n/**\n * A text box editor that follows its inputs: what each framework's binding\n * holds between renders. The update runs right after an attach, in the same\n * call, so the element never shows an editor without its text.\n */\nexport function createTextBoxEditorFollower<Ref>(): TextBoxEditorFollower<Ref> {\n let attached: {\n element: HTMLElement;\n annotation: TextBoxEditorAnnotation<Ref>;\n editor: TextBoxEditor<Ref>;\n } | null = null;\n const detach = () => {\n attached?.editor.detach();\n attached = null;\n };\n return {\n follow(element, annotation, item, scale) {\n const same =\n attached !== null && attached.element === element && attached.annotation === annotation;\n if (!same) {\n detach();\n if (element && annotation) {\n attached = { element, annotation, editor: attachTextBoxEditor(element, annotation) };\n }\n }\n attached?.editor.update(item, scale);\n },\n detach,\n };\n}\n\n/**\n * Pixels per point for an editor element you draw yourself (a renderer's own\n * text box): the box's width on the page over its width in points, or the\n * page's view scale while there is no box. Inside a look the layer scales\n * (`lookScale`, 1 outside one), the element draws at the annotation's 100%\n * size and the look's scale does the rest.\n */\nexport function textBoxEditorScaleOf(\n item: { box: { x: number; y: number; width: number; height: number } } | null,\n page: PageToPixels & { viewScale: number },\n lookScale: number,\n): number {\n const onPage =\n item && item.box.width > 0\n ? rectInPixels(item.box, page).width / item.box.width\n : page.viewScale;\n return onPage / lookScale;\n}\n\n/** A free text body's look, as the plugin hands it (`TextItem.css`). */\nexport interface TextBoxCss {\n fontFamily: string;\n /** In page points. */\n fontSize: number;\n color: string;\n fontWeight: number;\n fontStyle: string;\n textDecoration: string;\n align: 'left' | 'center' | 'right';\n /** The plate's inset from the box, in page points. */\n padding: number;\n}\n\n/** A text box's body style as CSS: put it on the editor element. */\nexport interface TextBoxStyle {\n fontFamily: string;\n /** In pixels, with its unit. */\n fontSize: string;\n color: string;\n fontWeight: number;\n fontStyle: string;\n textDecoration: string;\n textAlign: 'left' | 'center' | 'right';\n}\n\n/**\n * A text box's body style at `scale` pixels per point: font, size, color and\n * alignment. No line height: the editor binding states the engine's line\n * model per face on the element itself.\n */\nexport function textBoxStyleOf(css: TextBoxCss, scale: number): TextBoxStyle {\n return {\n fontFamily: css.fontFamily,\n fontSize: `${css.fontSize * scale}px`,\n color: css.color,\n fontWeight: css.fontWeight,\n fontStyle: css.fontStyle,\n textDecoration: css.textDecoration,\n textAlign: css.align,\n };\n}\n\n/** Where a free text's editor element goes, and the scale it draws at. */\nexport interface TextPlatePixels {\n left: number;\n top: number;\n width: number;\n height: number;\n /** Pixels per page point. */\n scale: number;\n}\n\n/**\n * The editor element of a free text box in the page layer's pixels: the\n * engine's text plate (`SetPlateRect` and its `re W n` clip), the box inset\n * by its padding. Place it there with no CSS padding, so its edge is the\n * plate's: CSS padding doesn't clip overflow, and scrolled lines would paint\n * over the border band. The box never grows with its text.\n */\nexport function textPlateInPixels(\n item: { box: { x: number; y: number; width: number; height: number }; css: { padding: number } },\n page: PageToPixels,\n): TextPlatePixels {\n const box = rectInPixels(item.box, page);\n const scale = item.box.width > 0 ? box.width / item.box.width : 1;\n const inset = item.css.padding * scale;\n return {\n left: box.left + inset,\n top: box.top + inset,\n width: Math.max(0, box.width - 2 * inset),\n height: Math.max(0, box.height - 2 * inset),\n scale,\n };\n}\n","/**\n * The annotation layer's renderers: which drawing each annotation gets, and\n * the behaviors that `interactive` renderers register. A renderer is a rule\n * (\"draw these annotations with this component\"); a framework adapter keeps\n * the component, and these decide everything around it, the same for every\n * framework:\n *\n * - {@link registerRendererBehaviors}: an `interactive` renderer takes the\n * pointer through a behavior the annotation plugin knows, registered once\n * per capability and renderer entry however many pages mount the layer.\n * - {@link annotationDrawingOf}: per annotation, the layer's own drawing, a\n * look of yours, or a sibling plugin's control (ownership beats looks).\n * - {@link editingTextKeyOf} and {@link layerTextBoxesOf}: which text box is\n * typed in, and which text boxes the layer draws rather than a look.\n *\n * The plugin's types are mirrored structurally, so this package stays free of\n * EmbedPDF imports.\n */\n\n/** A sibling plugin's renderer: it draws what the behavior `behavior` owns (the form plugin's controls). */\nexport interface BehaviorRendererEntry {\n behavior: string;\n}\n\n/** What an `interactive` function is asked: the annotation, and the active tool. */\nexport interface RendererInteractiveContext<Annotation> {\n annotation: Annotation;\n toolId: string;\n}\n\n/** A look of yours: draws the annotations `for` matches. */\nexport interface LookRendererEntry<Annotation> {\n /** A stable id for the registration (optional; generated). */\n id?: string;\n for(annotation: Annotation): boolean;\n /** Takes the pointer: always, or when the function says so (asked whenever it matters). */\n interactive?: boolean | ((context: RendererInteractiveContext<Annotation>) => boolean);\n}\n\nexport type RendererEntry<Annotation> = BehaviorRendererEntry | LookRendererEntry<Annotation>;\n\nconst isLook = <Annotation>(\n renderer: RendererEntry<Annotation>,\n): renderer is LookRendererEntry<Annotation> => 'for' in renderer;\n\n/** The annotation plugin's behavior registry (its host capability satisfies it). */\nexport interface RendererBehaviorRegistry<Annotation> {\n registerBehavior(behavior: {\n id: string;\n matches(annotation: Annotation): boolean;\n engaged(annotation: Annotation): boolean;\n }): () => void;\n getBehaviorFor(annotation: Annotation): { id: string } | null;\n}\n\ninterface RegisteredBehavior {\n id: string;\n count: number;\n unregister: () => void;\n}\n\n/**\n * The behaviors `interactive` renderers registered, counted per capability\n * and renderer entry, because the layer mounts once per page: the first page\n * registers, the last one unregisters. The entry's identity is the key, so a\n * renderer list must be defined once, never anew on every render.\n */\nconst registered = new WeakMap<object, Map<object, RegisteredBehavior>>();\nlet generatedIds = 0;\n\n/**\n * Register a behavior for each `interactive` look in `renderers`, so the\n * plugin lets that renderer take the pointer. Call it when a layer mounts (and\n * again when its renderer list changes); call what it returns when it\n * unmounts. `activeToolId` is read whenever an `interactive` function is asked.\n */\nexport function registerRendererBehaviors<Annotation>(\n annotation: RendererBehaviorRegistry<Annotation>,\n renderers: readonly RendererEntry<Annotation>[],\n activeToolId: () => string,\n): () => void {\n const releases: Array<() => void> = [];\n for (const renderer of renderers) {\n if (!isLook(renderer) || !renderer.interactive) continue;\n let perCapability = registered.get(annotation);\n if (!perCapability) registered.set(annotation, (perCapability = new Map()));\n let entry = perCapability.get(renderer);\n if (!entry) {\n const id = renderer.id ?? `renderer:${++generatedIds}`;\n const interactive = renderer.interactive;\n const engaged =\n typeof interactive === 'function'\n ? (record: Annotation) => interactive({ annotation: record, toolId: activeToolId() })\n : () => true;\n entry = {\n id,\n count: 0,\n unregister: annotation.registerBehavior({ id, matches: renderer.for, engaged }),\n };\n perCapability.set(renderer, entry);\n }\n entry.count++;\n const owned = entry;\n releases.push(() => {\n owned.count--;\n if (owned.count === 0) {\n owned.unregister();\n registered.get(annotation)?.delete(renderer);\n }\n });\n }\n return () => releases.forEach((release) => release());\n}\n\n/** The behavior id a renderer entry answers for: a sibling plugin's own, or the one registered for it. */\nexport function rendererBehaviorId<Annotation>(\n annotation: object,\n renderer: RendererEntry<Annotation>,\n): string | null {\n if ('behavior' in renderer) return renderer.behavior;\n return registered.get(annotation)?.get(renderer)?.id ?? null;\n}\n\n/** The first of your looks that draws this annotation, or `null`. */\nexport function lookRendererFor<Annotation, Entry extends RendererEntry<Annotation>>(\n record: Annotation,\n renderers: readonly Entry[] | undefined,\n): Extract<Entry, { for: unknown }> | null {\n const look = renderers?.find((renderer) => isLook<Annotation>(renderer) && renderer.for(record));\n return (look ?? null) as Extract<Entry, { for: unknown }> | null;\n}\n\n/**\n * How the layer draws one annotation:\n * - `native`: its own drawing (the scene or the engine's raster). `inert`\n * when an engaged behavior's plugin owns the input with no renderer of\n * yours (a link's anchor takes the click).\n * - `owned`: a sibling plugin's renderer draws it and takes the input (the\n * form plugin's fill controls); it places its own controls.\n * - `look`: your renderer draws it, in the annotation's frame. `interactive`\n * when an engaged behavior gave it the pointer; otherwise it only draws,\n * and the frame is `inert` (no pointer, no focus) unless its text box is\n * being typed in, where the renderer's editor takes the keys.\n */\nexport type AnnotationDrawing<Entry> =\n | { kind: 'native'; inert: boolean }\n | { kind: 'owned'; entry: Extract<Entry, { behavior: string }> }\n | { kind: 'look'; entry: Extract<Entry, { for: unknown }>; interactive: boolean; inert: boolean };\n\n/**\n * The drawing an annotation gets. Ownership beats looks: an engaged\n * behavior's renderer is authoritative; `for` rules apply only to what the\n * layer owns, and draw without the pointer. `typing` is whether this\n * annotation's text box is being typed in.\n */\nexport function annotationDrawingOf<Annotation, Entry extends RendererEntry<Annotation>>(\n record: Annotation,\n annotation: RendererBehaviorRegistry<Annotation>,\n renderers: readonly Entry[] | undefined,\n typing: boolean,\n): AnnotationDrawing<Entry> {\n const behavior = annotation.getBehaviorFor(record);\n if (behavior) {\n const entry = renderers?.find(\n (renderer) => rendererBehaviorId(annotation, renderer) === behavior.id,\n );\n if (entry && 'behavior' in entry) {\n return { kind: 'owned', entry: entry as Extract<Entry, { behavior: string }> };\n }\n if (entry) {\n return {\n kind: 'look',\n entry: entry as Extract<Entry, { for: unknown }>,\n interactive: true,\n inert: false,\n };\n }\n return { kind: 'native', inert: true };\n }\n const look = lookRendererFor<Annotation, Entry>(record, renderers);\n if (!look) return { kind: 'native', inert: false };\n return { kind: 'look', entry: look, interactive: false, inert: !typing };\n}\n\n// ── text boxes ───────────────────────────────────────────────────────────────\n\n/**\n * The key of the text box being typed in (the one that is `editing` and has a\n * ref), or `null`. An annotation with this key is `typing` for\n * {@link annotationDrawingOf}: while it is, a look's own editor takes the keys.\n */\nexport function editingTextKeyOf<Ref>(\n texts: readonly { readonly editing: boolean; readonly ref: Ref | null }[],\n keyOf: (ref: Ref) => string,\n): string | null {\n const editing = texts.find((text) => text.editing && text.ref !== null);\n return editing?.ref ? keyOf(editing.ref) : null;\n}\n\n/**\n * The text boxes the layer draws itself: all of them but the ones whose\n * annotation a look of yours draws, which that look edits with its own editor\n * (a renderer's rich text editor). `items` are the page's render items, which\n * carry each annotation; the same array comes back when there is no look.\n */\nexport function layerTextBoxesOf<Annotation, Ref, Text extends { readonly ref: Ref | null }>(\n texts: readonly Text[],\n items: readonly { readonly ref: Ref | null; readonly annotation?: Annotation | null }[],\n renderers: readonly RendererEntry<Annotation>[] | undefined,\n keyOf: (ref: Ref) => string,\n): readonly Text[] {\n const looks = renderers?.filter((renderer) => isLook<Annotation>(renderer)) ?? [];\n if (looks.length === 0) return texts;\n const annotations = new Map<string, Annotation>();\n for (const item of items) {\n if (item.ref !== null && item.annotation) annotations.set(keyOf(item.ref), item.annotation);\n }\n return texts.filter((text) => {\n const annotation = text.ref !== null ? annotations.get(keyOf(text.ref)) : undefined;\n return !annotation || lookRendererFor(annotation, looks) === null;\n });\n}\n","/**\n * Object URLs for the pictures plugins hand out, with their lifetime. The\n * plugins hold bytes and image handles; a URL is a browser resource, made\n * here when a view shows the picture and revoked when it lets go:\n *\n * - {@link objectUrlOf}: bytes (a stamp's gallery preview) as a URL, now;\n * - {@link loadObjectUrl}: bytes still to load (the armed stamp's ghost);\n * - {@link loadAppearanceUrls}: a page's pictures of its annotations or its\n * form widgets, every state; {@link shownAppearance} picks the one an item\n * shows, and {@link bakedAppearanceOf} gives it as a renderer's `appearance`.\n *\n * A layer's pictures are loaded again whenever its page changes, and the set\n * it shows stays valid until the next one is shown ({@link createShownUrls}).\n */\nimport type { ObjectUrlImageSource } from './painted-image';\n\n/** Bytes and their type, as the stamp and annotation plugins hand out previews. */\nexport interface PictureBytes {\n readonly bytes: Uint8Array;\n readonly mimeType?: string | null;\n}\n\n/** An object URL, and how to release it. */\nexport interface ObjectUrl {\n readonly url: string;\n revoke(): void;\n}\n\n/** An object URL for bytes. Revoke it when nothing shows it any more. */\nexport function objectUrlOf(picture: PictureBytes): ObjectUrl {\n // Copy into an exact ArrayBuffer (the engine idiom): a Uint8Array view may\n // sit on a larger or shared buffer, which Blob won't accept.\n const body = new ArrayBuffer(picture.bytes.byteLength);\n new Uint8Array(body).set(picture.bytes);\n const blob = new Blob([body], picture.mimeType ? { type: picture.mimeType } : {});\n const url = URL.createObjectURL(blob);\n return { url, revoke: () => URL.revokeObjectURL(url) };\n}\n\n/**\n * Load a picture and hand its object URL to `onUrl` once it's there (nothing\n * for `null`). What it returns cancels: a load still running is dropped, and\n * the URL is revoked. Keep showing the previous URL until `onUrl` brings the\n * next one, so a swap never flickers.\n */\nexport function loadObjectUrl(\n load: () => Promise<PictureBytes | null>,\n onUrl: (url: string) => void,\n): () => void {\n let cancelled = false;\n let made: ObjectUrl | null = null;\n void load().then((picture) => {\n if (cancelled || !picture) return;\n made = objectUrlOf(picture);\n onUrl(made.url);\n });\n return () => {\n cancelled = true;\n made?.revoke();\n };\n}\n\n/**\n * One picture as its owner hands it out (the annotation plugin's or the form\n * plugin's `renderAppearances`): an item in one of its states, at its version.\n */\nexport interface AppearancePicture<Ref> {\n readonly ref: Ref;\n /** The state it draws (`/AS`), or `null` for an item without states. */\n readonly state: string | null;\n /** The box it was rendered into, in page points. */\n readonly rect: { x: number; y: number; width: number; height: number };\n /** The version of its item it shows: an item at another version looks different now. */\n readonly version: number;\n readonly image: ObjectUrlImageSource;\n}\n\n/** A picture's URL, the box it was rendered into, and the version of its item it shows. */\nexport interface AppearanceUrl {\n url: string;\n box: { x: number; y: number; width: number; height: number };\n version: number;\n}\n\n/** A picture's key: its item's key and the state it draws. */\nconst pictureKey = (itemKey: string, state: string | null): string => `${itemKey}|${state ?? ''}`;\n\n/**\n * The picture an item shows, from {@link loadAppearanceUrls}: the one of the\n * state it's in (`'Off'` when it names none), or its only picture when it has\n * no states; `null` until it's loaded.\n */\nexport function shownAppearance(\n urls: Readonly<Record<string, AppearanceUrl>>,\n itemKey: string,\n appearanceState: string | null,\n): AppearanceUrl | null {\n return (\n urls[pictureKey(itemKey, appearanceState ?? 'Off')] ?? urls[pictureKey(itemKey, null)] ?? null\n );\n}\n\n/**\n * The engine's picture of an annotation as a renderer gets it (its\n * `appearance`): `{ url }` once {@link loadAppearanceUrls} has loaded the one\n * of the state it shows, else `null`.\n */\nexport function bakedAppearanceOf(\n urls: Readonly<Record<string, AppearanceUrl>>,\n itemKey: string,\n appearanceState: string | null,\n): { url: string } | null {\n const baked = shownAppearance(urls, itemKey, appearanceState);\n return baked ? { url: baked.url } : null;\n}\n\n/**\n * The URLs a layer shows, kept from one load to the next: a set stays valid\n * until the set after it is shown, so a picture that shows again before the\n * next set arrives (an annotation an undo brings back) still has a live URL.\n * One per layer, passed to every load; `release()` when the layer goes.\n */\nexport interface ShownUrls {\n /** Revoke the URLs shown now: the layer is going. A later load starts afresh. */\n release(): void;\n}\n\n/** The set of URLs a layer shows, and the one place it is replaced. */\ninterface ShownUrlsHolder extends ShownUrls {\n /** `revokers` are shown now: revoke the set before them. */\n replace(revokers: readonly (() => void)[]): void;\n}\n\n/** What a layer shows, for {@link loadAppearanceUrls}. */\nexport function createShownUrls(): ShownUrls {\n let shown: readonly (() => void)[] = [];\n const holder: ShownUrlsHolder = {\n replace(revokers) {\n const before = shown;\n shown = revokers;\n before.forEach((revoke) => revoke());\n },\n release: () => holder.replace([]),\n };\n return holder;\n}\n\n/**\n * Load a page's pictures, every state of every item, and hand their URLs to\n * `onLoaded` once all of them are there; the set `shown` held before is\n * revoked then. {@link shownAppearance} picks an item's: a check box that\n * changes shows its other picture at once. What it returns cancels: the load\n * is aborted and the URLs it made are revoked, unless it already handed them\n * over. Start it again when the page's appearance epoch or the appearance\n * scale changes, never mid-gesture.\n */\nexport function loadAppearanceUrls<Ref>(\n shown: ShownUrls,\n load: (signal: AbortSignal) => Promise<readonly AppearancePicture<Ref>[]>,\n keyOf: (ref: Ref) => string,\n onLoaded: (urls: Record<string, AppearanceUrl>) => void,\n): () => void {\n return loadPictureUrls(\n shown,\n load,\n (picture) => pictureKey(keyOf(picture.ref), picture.state),\n onLoaded,\n );\n}\n\nfunction loadPictureUrls<Picture extends AppearancePicture<unknown>>(\n shown: ShownUrls,\n load: (signal: AbortSignal) => Promise<readonly Picture[]>,\n keyOf: (picture: Picture) => string,\n onLoaded: (urls: Record<string, AppearanceUrl>) => void,\n): () => void {\n const controller = new AbortController();\n /** The URLs this load made and hasn't handed over. */\n let made: (() => void)[] = [];\n const drop = () => {\n made.forEach((revoke) => revoke());\n made = [];\n };\n const urlOf = async (picture: Picture) => {\n const url = await picture.image.objectUrl().abortWith(controller.signal);\n // Arrived after the load was dropped: nobody else will revoke it.\n if (controller.signal.aborted) {\n url.revoke();\n throw controller.signal.reason;\n }\n made.push(url.revoke);\n return url.url;\n };\n void (async () => {\n try {\n const pictures = await load(controller.signal);\n const minted = await Promise.all(pictures.map(urlOf));\n if (controller.signal.aborted) return;\n const urls: Record<string, AppearanceUrl> = {};\n // Placed by its own rect (the box it was rendered into), never a recomputed bound.\n pictures.forEach((picture, index) => {\n urls[keyOf(picture)] = { url: minted[index]!, box: picture.rect, version: picture.version };\n });\n onLoaded(urls);\n (shown as ShownUrlsHolder).replace(made);\n made = [];\n } catch {\n // Aborted, or the page has no appearances to render: what is still on\n // its way is dropped with what was made.\n controller.abort();\n drop();\n }\n })();\n return () => {\n controller.abort();\n drop();\n };\n}\n","/**\n * Comment threads with where their page is shown: the join of the annotation\n * plugin's threads and the document's page list that a comments panel\n * prints. A thread's identity stays its page's address; the index and the\n * label are presentation, following page moves and deletes.\n */\n\n/** Where a thread's page is shown now. */\nexport interface CommentThreadPage {\n /** Current 0-based display index of the thread's page; `-1` when the page is no longer in the document. */\n pageIndex: number;\n /**\n * The page's `/PageLabels` label when the PDF declares one (\"iv\", \"A-2\"),\n * else the 1-based position as a string; `'?'` for a page that's gone.\n * Print it as it is.\n */\n pageLabel: string;\n}\n\n/** A page as the document's page list has it: its address, display index and label. */\nexport interface CommentPageLayout {\n readonly ref: { readonly objectNumber: number };\n readonly index: number;\n readonly label?: string | null;\n}\n\n/**\n * Each thread with its page's display index and label. A thread whose page\n * was just deleted reads `-1` and `'?'` for the one render before it goes.\n */\nexport function enrichCommentThreads<\n Thread extends { readonly page: { readonly objectNumber: number } },\n>(\n threads: readonly Thread[],\n pages: readonly CommentPageLayout[],\n): Array<Thread & CommentThreadPage> {\n const byPageObjectNumber = new Map(pages.map((page) => [page.ref.objectNumber, page] as const));\n return threads.map((thread) => {\n const page = byPageObjectNumber.get(thread.page.objectNumber);\n return {\n ...thread,\n pageIndex: page ? page.index : -1,\n pageLabel: page ? (page.label ?? String(page.index + 1)) : '?',\n };\n });\n}\n","/**\n * What every framework's form layer shares: the colors the viewer draws a\n * field with, a field's text look as CSS, the widget's PDF events from DOM\n * events, the toggle and text-field policies, and the optimistic selection of\n * a list box. A framework adapter keeps the markup (a positioned box per\n * widget with a native control inside) and its bindings. The form plugin's\n * types are mirrored structurally, so this package stays free of EmbedPDF\n * imports.\n */\nimport type { PixelRect } from './page-pixels';\nimport { mixAccent, paint } from './theme';\n\n// ── colors and looks ─────────────────────────────────────────────────────────\n\n/** The form settings the colors read: the structural twin of the form plugin's `focus` and `fields`. */\nexport interface FormColorSettings {\n readonly focus: { readonly color: string | null };\n readonly fields: {\n readonly border: string | null;\n readonly background: string;\n readonly color: string;\n };\n}\n\n/** The colors the viewer draws fields with, each its `--epdf-form-*` variable first, then the setting. */\nexport interface FormColors {\n /** The focus ring around the field being filled in. */\n readonly focus: string;\n /** The edge of a field without a border of its own. */\n readonly border: string;\n /** Inside a field without a background of its own, while it's filled in. */\n readonly background: string;\n /** The text of a field without a color of its own. */\n readonly text: string;\n}\n\n/** The form settings as colors: an unset focus color is the accent, an unset edge the accent at 55%. */\nexport function formColorsOf(settings: FormColorSettings, accent: string): FormColors {\n return {\n focus: paint('form-focus', settings.focus.color ?? accent),\n border: paint(\n 'form-field-border',\n settings.fields.border ?? mixAccent('form-field-border', accent),\n ),\n background: paint('form-field-background', settings.fields.background),\n text: paint('form-field-color', settings.fields.color),\n };\n}\n\n/** How a widget looks in the PDF: the structural twin of the form plugin's `FormWidgetLook`. */\nexport interface FieldLook {\n readonly border: string | null;\n /** In page points. */\n readonly borderWidth: number;\n readonly borderStyle: string;\n readonly background: string | null;\n readonly color: string | null;\n /** One of the 14 standard PDF fonts (`'helvetica-bold'`), or `null`. */\n readonly fontFamily: string | null;\n /** In page points; `0` or `null` fits the text to the box. */\n readonly fontSize: number | null;\n readonly textAlign: 'left' | 'center' | 'right';\n}\n\n// The style records below are type aliases, not interfaces: an object type\n// alias fits a framework's style type with an index signature for custom\n// properties (Vue's `CSSProperties`), which an interface never does.\n\n/** A standard PDF font as CSS. */\nexport type CssFont = {\n fontFamily: string;\n fontWeight: number;\n fontStyle: 'normal' | 'italic';\n};\n\n/** One of the 14 standard PDF fonts as CSS: a family that looks like it, its weight and style. */\nexport function cssFontOf(pdfFont: string | null): CssFont {\n const name = pdfFont ?? 'helvetica';\n const fontFamily = name.startsWith('courier')\n ? '\"Courier New\", Courier, monospace'\n : name.startsWith('times')\n ? '\"Times New Roman\", Times, serif'\n : 'Helvetica, Arial, sans-serif';\n return {\n fontFamily,\n fontWeight: name.includes('bold') ? 700 : 400,\n fontStyle: name.includes('italic') || name.includes('oblique') ? 'italic' : 'normal',\n };\n}\n\n/** A field's text as CSS: its font, size, alignment and colors, the settings where it has none. */\nexport type FieldTextStyle = CssFont & {\n /** In pixels, with its unit. */\n fontSize: string;\n textAlign: 'left' | 'center' | 'right';\n color: string;\n background: string;\n};\n\n/** Pixels per page point where the widget is drawn. */\nexport const scaleOf = (field: { box: { width: number } }, frame: PixelRect): number =>\n field.box.width > 0 ? frame.width / field.box.width : 1;\n\n/** A text field as the layer's editor shows it while focused, in the field's own font. */\nexport type TextFieldStyle = FieldTextStyle & {\n /** A comb field's spacing (its box split into `maxLength` cells), in pixels with its unit. */\n letterSpacing?: string;\n};\n\n/**\n * A text field's editor style. A font size of 0 means \"fit the box\" in PDF:\n * the box height for one line, Acrobat's 12 pt for several.\n */\nexport function textFieldStyleOf(\n field: {\n box: { width: number };\n look: FieldLook;\n multiline: boolean;\n comb: boolean;\n maxLength: number | null;\n },\n frame: PixelRect,\n colors: FormColors,\n): TextFieldStyle {\n const scale = scaleOf(field, frame);\n const fontSize = field.look.fontSize\n ? field.look.fontSize * scale\n : field.multiline\n ? 12 * scale\n : Math.max(6, frame.height * 0.72);\n return {\n ...cssFontOf(field.look.fontFamily),\n fontSize: `${fontSize}px`,\n textAlign: field.look.textAlign,\n color: field.look.color ?? colors.text,\n background: field.look.background ?? colors.background,\n ...(field.comb && field.maxLength\n ? { letterSpacing: `${frame.width / field.maxLength / 2}px` }\n : {}),\n };\n}\n\n/** A list box as the layer draws it: the field's text look, and its border. */\nexport type ListBoxStyle = FieldTextStyle & {\n /** In pixels, with its unit; never thinner than one pixel. */\n borderWidth: string;\n borderStyle: 'solid' | 'dashed';\n borderColor: string;\n};\n\n/** A list box's style: the field's own look, the settings where it has none (12 pt for a fitted size). */\nexport function listBoxStyleOf(\n field: { box: { width: number }; look: FieldLook },\n frame: PixelRect,\n colors: FormColors,\n): ListBoxStyle {\n const scale = scaleOf(field, frame);\n return {\n ...cssFontOf(field.look.fontFamily),\n fontSize: `${(field.look.fontSize || 12) * scale}px`,\n textAlign: field.look.textAlign,\n color: field.look.color ?? colors.text,\n background: field.look.background ?? colors.background,\n borderWidth: `${Math.max(1, field.look.borderWidth * scale)}px`,\n borderStyle: field.look.borderStyle === 'dashed' ? 'dashed' : 'solid',\n borderColor: field.look.border ?? colors.border,\n };\n}\n\n// ── the widget's events ──────────────────────────────────────────────────────\n\n/** The PDF events a widget's `/AA` actions answer, as the actions plugin names them. */\nexport type WidgetEventKind =\n | 'cursorEnter'\n | 'cursorExit'\n | 'mouseDown'\n | 'mouseUp'\n | 'focus'\n | 'blur';\n\n/**\n * Send a widget's pointer and focus events (`/AA`: enter, exit, down, up,\n * focus, blur) to `notify`, from native listeners on the widget's box. Put\n * them on the always-active box, never on the control inside: \"may edit this\n * field\" and \"may receive PDF action events\" are different rights, and a\n * session that may not fill still sees hover tooltips. Focus is `focusin` and\n * `focusout`, so the control's focus reaches the box, and a blur arrives\n * after the control's own blur handler started its commit. Returns the\n * detach.\n */\nexport function bindWidgetEvents(\n box: HTMLElement,\n notify: (event: WidgetEventKind) => void,\n): () => void {\n const listeners: Array<[string, WidgetEventKind]> = [\n ['pointerenter', 'cursorEnter'],\n ['pointerleave', 'cursorExit'],\n ['pointerdown', 'mouseDown'],\n ['pointerup', 'mouseUp'],\n ['focusin', 'focus'],\n ['focusout', 'blur'],\n ];\n const handlers = listeners.map(([type, kind]) => {\n const handler = () => notify(kind);\n box.addEventListener(type, handler);\n return [type, handler] as const;\n });\n return () => handlers.forEach(([type, handler]) => box.removeEventListener(type, handler));\n}\n\n/** What a toggle press writes with: the form plugin's `setValue`. */\nexport interface ToggleForm<Field> {\n setValue(field: Field, value: { value: string | null }): Promise<unknown>;\n}\n\n/**\n * A checkbox or radio button pressed: a checked checkbox clears, a radio\n * button always selects its own value. The value is written first, then the\n * widget's action runs (Acrobat's order), so an action script reads the new\n * state; a read-only toggle writes nothing and still runs its action.\n */\nexport function pressToggle<Field>(\n form: ToggleForm<Field>,\n toggle: {\n fieldRef: Field;\n kind: 'checkbox' | 'radio';\n checked: boolean;\n exportValue: string;\n disabled: boolean;\n },\n activate: () => void,\n): void {\n const written = toggle.disabled\n ? undefined\n : form.setValue(toggle.fieldRef, {\n value: toggle.kind === 'checkbox' && toggle.checked ? null : toggle.exportValue,\n });\n void Promise.resolve(written).then(activate, activate);\n}\n\n// ── a text field being typed in ──────────────────────────────────────────────\n\n/** The form plugin's draft calls for a text field (its host capability satisfies it). */\nexport interface TextFieldDrafts<Field> {\n draftText(field: Field, text: string): void;\n /** Resolves with how the write ended (`'rejected'`: the form's validation refused it), or `null`. */\n commitDraftText(field: Field): Promise<{ readonly status: string } | null>;\n discardDraftText(field: Field): void;\n}\n\n/** What a text field's editor shows: whether it has focus, and the text in it. */\nexport interface TextFieldEditorState {\n readonly focused: boolean;\n readonly draft: string;\n}\n\n/**\n * A text field being typed in, framework-free: the editor's `value` is\n * `draft`, and its events call the methods. Read the state with `getState`\n * and re-render on `subscribe`; the state object stays the same until it\n * changes.\n */\nexport interface TextFieldEditor {\n getState(): TextFieldEditorState;\n subscribe(listener: () => void): () => void;\n /** The field's value as the form has it now. The draft follows it, except while focused. */\n setValue(value: string): void;\n /** The editor took focus. */\n focus(): void;\n /** Someone typed: the draft shows it, and the plugin keeps it so a download writes it. */\n input(text: string): void;\n /**\n * A key went down. True when the editor should blur now: Escape (which\n * puts the value back) and, in a one-line field, Enter (which commits).\n */\n keyDown(key: string, multiline: boolean): boolean;\n /** The editor lost focus: commit what changed, discard what didn't or was cancelled. */\n blur(): void;\n}\n\n/**\n * The editing policy of one text field: typing drafts, blur or Enter\n * commits, Escape discards. A commit keeps the typed text until the field\n * shows it (at once, as the form shows a fill before the engine answers), so\n * a blur never shows the old value for a moment. A value the form's\n * validation refuses stays typed, as in Acrobat; a write the engine refuses\n * puts the field's value back.\n */\nexport function createTextFieldEditor<Field>(\n form: TextFieldDrafts<Field>,\n field: Field,\n value: string,\n): TextFieldEditor {\n let state: TextFieldEditorState = { focused: false, draft: value };\n let fieldValue = value;\n let cancelled = false;\n /** The text of the commit on its way, until the field shows it or the commit settles. */\n let committing: string | null = null;\n const listeners = new Set<() => void>();\n const set = (patch: Partial<TextFieldEditorState>) => {\n const next = { ...state, ...patch };\n if (next.focused === state.focused && next.draft === state.draft) return;\n state = next;\n listeners.forEach((listener) => listener());\n };\n const commit = (typed: string) => {\n committing = typed;\n const settled = (keepTyped: boolean) => {\n if (committing !== typed) return;\n committing = null;\n if (!state.focused) set({ draft: keepTyped ? typed : fieldValue });\n };\n form.commitDraftText(field).then(\n (result) => settled(result?.status === 'rejected'),\n () => settled(false),\n );\n };\n return {\n getState: () => state,\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n setValue(next) {\n fieldValue = next;\n // Never mid-edit: what's typed stays until the edit ends. A commit's text stays until the\n // field shows it; from then on the field's value is what shows.\n if (state.focused || (committing !== null && next !== committing)) return;\n committing = null;\n set({ draft: next });\n },\n focus: () => set({ focused: true }),\n input(text) {\n set({ draft: text });\n form.draftText(field, text);\n },\n keyDown(key, multiline) {\n if (key === 'Escape') {\n cancelled = true;\n return true;\n }\n // A field with several lines takes Enter as a new line.\n return key === 'Enter' && !multiline;\n },\n blur() {\n const typed = state.draft;\n if (cancelled || typed === fieldValue) {\n cancelled = false;\n form.discardDraftText(field);\n set({ focused: false, draft: fieldValue });\n return;\n }\n commit(typed);\n set({ focused: false, draft: typed });\n },\n };\n}\n\n// ── a list box's selection ───────────────────────────────────────────────────\n\n/**\n * A list box's selection, shown at once while its write is on its way. The\n * native control must stay the same element (never remounted), so its own\n * scroll position stays where the user left it.\n */\nexport interface OptimisticSelection {\n /** The selection the control shows. */\n get(): readonly string[];\n subscribe(listener: () => void): () => void;\n /** The selection the engine has now: the control shows it once it differs from the last one. */\n setConfirmed(values: readonly string[]): void;\n /** Show `values` now and write them; a write that fails puts the engine's selection back. */\n choose(values: string[], write: (values: string[]) => void | Promise<unknown>): void;\n}\n\n/** A list box's optimistic selection, starting at the engine's. */\nexport function createOptimisticSelection(confirmed: readonly string[]): OptimisticSelection {\n let shown: readonly string[] = [...confirmed];\n let engine: readonly string[] = confirmed;\n let engineSignature = JSON.stringify(confirmed);\n const listeners = new Set<() => void>();\n const show = (values: readonly string[]) => {\n shown = [...values];\n listeners.forEach((listener) => listener());\n };\n const rollback = () => show(engine);\n return {\n get: () => shown,\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n setConfirmed(values) {\n engine = values;\n const signature = JSON.stringify(values);\n if (signature === engineSignature) return;\n engineSignature = signature;\n show(values);\n },\n choose(values, write) {\n show(values);\n try {\n const result = write(values);\n if (result) void result.then(undefined, rollback);\n } catch {\n rollback();\n }\n },\n };\n}\n\n/** A native `<select>`, as {@link showSelectedOptions} reads it: its options. */\nexport interface SelectOptions {\n readonly options: ArrayLike<{ readonly value: string; selected: boolean }>;\n}\n\n/**\n * Show `values` as a native `<select>`'s selection, set on each option. A\n * `value` binding can't hold several values, and setting the options also\n * puts a choice back when its write fails before the framework renders\n * again. Setting the same selection again changes nothing the user sees,\n * scroll position included.\n */\nexport function showSelectedOptions(select: SelectOptions, values: readonly string[]): void {\n for (const option of Array.from(select.options)) {\n option.selected = values.includes(option.value);\n }\n}\n","/**\n * The styles of the form layer's elements, the same in every framework: the\n * box over each widget, the text field's editor, the list box and the focus\n * ring. Each is a style record (camelCase names, lengths with their units),\n * the shape a style object takes in React, Vue and Angular; Svelte writes one\n * as CSS text with `cssText`. Each is a type alias, so it fits Vue's\n * `CSSProperties` (see the note in `form-field`). The field's own look inside them comes from\n * `form-field` ({@link textFieldStyleOf}, {@link listBoxStyleOf}); its colors\n * from the form settings ({@link formColorsOf}).\n */\nimport { listBoxStyleOf, scaleOf, textFieldStyleOf } from './form-field';\nimport type { FieldLook, FormColors, ListBoxStyle, TextFieldStyle } from './form-field';\nimport type { PixelRect } from './page-pixels';\n\n/** A control that fills the widget's box it sits in: every native control the layer draws. */\nexport const FORM_CONTROL_FILL = Object.freeze({\n position: 'absolute',\n inset: '0',\n width: '100%',\n height: '100%',\n boxSizing: 'border-box',\n margin: '0',\n} as const);\n\n/** {@link FORM_CONTROL_FILL}'s shape. */\nexport type FormControlFill = typeof FORM_CONTROL_FILL;\n\n/** The box a widget's control sits in, placed over the widget on the page. */\nexport type WidgetBoxStyle = {\n position: 'absolute';\n left: string;\n top: string;\n width: string;\n height: string;\n /** Always the widget's event surface: the control inside gates the edits. */\n pointerEvents: 'auto';\n /** The edge of a field without a border of its own, so people see where to fill in. */\n boxShadow?: string;\n};\n\n/**\n * The style of a widget's box at `frame` (the widget's box in the page\n * layer's pixels). `edge: false` leaves the edge out, for a control that draws\n * its own (a list box) or never has one (a button).\n */\nexport function widgetBoxStyleOf(\n widget: { look: Pick<FieldLook, 'border'> },\n frame: PixelRect,\n colors: FormColors,\n { edge = true }: { edge?: boolean } = {},\n): WidgetBoxStyle {\n return {\n position: 'absolute',\n left: `${frame.left}px`,\n top: `${frame.top}px`,\n width: `${frame.width}px`,\n height: `${frame.height}px`,\n pointerEvents: 'auto',\n ...(edge && widget.look.border === null\n ? { boxShadow: `inset 0 0 0 1px ${colors.border}` }\n : {}),\n };\n}\n\n/**\n * Where a text field's editor shows the text typed in it, so it sits where the engine will draw\n * it (the form plugin's `getTextEditorLayout`), in page points.\n */\nexport interface TextEditorLayout {\n readonly fontFamily: string;\n readonly fontWeight: number;\n readonly fontStyle: 'normal' | 'italic';\n readonly fontSize: number;\n readonly lineHeight: number;\n readonly padding: {\n readonly top: number;\n readonly right: number;\n readonly bottom: number;\n readonly left: number;\n };\n /** A comb's room between characters; it starts at the left padding, whatever the alignment. */\n readonly letterSpacing: number;\n readonly comb: boolean;\n}\n\n/** A text field's editor: see-through at rest, the field's own look while focused. */\nexport type TextFieldEditorStyle = FormControlFill &\n TextFieldStyle & {\n border: 'none';\n outline: string;\n outlineOffset: string;\n padding: string;\n /** In pixels, with its unit: one line of the engine's layout. */\n lineHeight?: string;\n resize: 'none';\n cursor: 'text';\n /** 1 while focused, 0 at rest, where the field's picture shows through. */\n opacity: number;\n /** `none` for a disabled field: the box below runs the widget's action. */\n pointerEvents?: 'none';\n };\n\n/**\n * The style of a text field's editor, the `<input>` or `<textarea>` that is\n * always there: focused, it shows the field's text in its own font with a\n * focus ring; at rest it is see-through over the field's picture. A disabled\n * control would swallow clicks, and the box below runs the widget's action\n * (the read-only \"button\" pattern), so a disabled one lets them through.\n *\n * With the engine's `layout` of the text being typed, the editor writes it\n * where the engine will draw it: in the engine's face (mounted), at the size\n * it draws (a fitted size resolved), in the box inside the border, a line of\n * the face's height (one line centred, as the engine centres it).\n */\nexport function textFieldEditorStyleOf(\n field: Parameters<typeof textFieldStyleOf>[0] & { disabled: boolean },\n frame: PixelRect,\n colors: FormColors,\n focused: boolean,\n layout: TextEditorLayout | null = null,\n): TextFieldEditorStyle {\n const scale = scaleOf(field, frame);\n const pixels = (points: number) => `${points * scale}px`;\n return {\n ...FORM_CONTROL_FILL,\n border: 'none',\n outline: focused ? `2px solid ${colors.focus}` : 'none',\n outlineOffset: '-2px',\n padding: '0 2px',\n ...textFieldStyleOf(field, frame, colors),\n ...(layout\n ? {\n fontFamily: layout.fontFamily,\n fontWeight: layout.fontWeight,\n fontStyle: layout.fontStyle,\n fontSize: pixels(layout.fontSize),\n lineHeight: pixels(layout.lineHeight),\n padding: [\n layout.padding.top,\n layout.padding.right,\n layout.padding.bottom,\n layout.padding.left,\n ]\n .map(pixels)\n .join(' '),\n ...(layout.comb\n ? { letterSpacing: pixels(layout.letterSpacing), textAlign: 'left' as const }\n : {}),\n }\n : {}),\n resize: 'none',\n cursor: 'text',\n opacity: focused ? 1 : 0,\n ...(field.disabled ? { pointerEvents: 'none' as const } : {}),\n };\n}\n\n/** A list box: the visible native `<select>` in the field's own look. */\nexport type ListBoxControlStyle = FormControlFill &\n ListBoxStyle & {\n padding: string;\n borderRadius: string;\n outline: 'none';\n cursor: 'default' | 'pointer';\n /** `none` for a disabled field: the box below runs the widget's action. */\n pointerEvents?: 'none';\n };\n\n/**\n * The style of a list box's `<select>`, filling the widget's box: the field's\n * own look ({@link listBoxStyleOf}) with its own border, so the box adds no\n * edge. The focus ring is drawn above it ({@link formFocusRingStyleOf}).\n */\nexport function listBoxControlStyleOf(\n field: Parameters<typeof listBoxStyleOf>[0] & { disabled: boolean },\n frame: PixelRect,\n colors: FormColors,\n): ListBoxControlStyle {\n return {\n ...FORM_CONTROL_FILL,\n padding: '0',\n borderRadius: '0',\n outline: 'none',\n ...listBoxStyleOf(field, frame, colors),\n cursor: field.disabled ? 'default' : 'pointer',\n ...(field.disabled ? { pointerEvents: 'none' as const } : {}),\n };\n}\n\n/** The focus ring of a field whose control is see-through, or is the box itself. */\nexport type FormFocusRingStyle = {\n position: 'absolute';\n inset: string;\n zIndex: number;\n boxSizing: 'border-box';\n outline: string;\n outlineOffset: string;\n pointerEvents: 'none';\n};\n\n/**\n * The style of a field's focus ring in `color` (the form's focus color): an\n * element of its own above the field's picture and its control. An outline on\n * the box would sit under an opaque child, so a rectangular checkbox or a\n * dropdown would look unfocused while it has the focus.\n */\nexport function formFocusRingStyleOf(color: string): FormFocusRingStyle {\n return {\n position: 'absolute',\n inset: '0',\n zIndex: 1,\n boxSizing: 'border-box',\n outline: `2px solid ${color}`,\n outlineOffset: '-2px',\n pointerEvents: 'none',\n };\n}\n","/**\n * A page link's anchor: which links a page layer shows, where each anchor\n * sits and what it does natively. Links are real `<a>` elements on purpose: a website\n * link gets an `href` (middle-click, copy link, the status-bar preview and\n * keyboard focus come free); every other click follows the link through the\n * link plugin's `activate()`. The plugin's link type is mirrored\n * structurally, so this package stays free of EmbedPDF imports.\n */\nimport { sanitizeExternalUri } from './external-uri';\nimport { rectInPixels } from './page-pixels';\nimport type { PageToPixels, PixelRect } from './page-pixels';\n\n/** A link as the link plugin lists it: the fields the anchor reads. */\nexport interface LinkAnchorShape {\n readonly target: { readonly kind: string; readonly uri?: string };\n /** The full `/A` tree; a `/Next` after its root chains more actions. */\n readonly activate?: { readonly root: { readonly next: readonly unknown[] } | null } | null;\n /** A link riding an editable annotation (its parent owns its pixels while annotations are edited). */\n readonly attached: boolean;\n}\n\n/**\n * The links a page layer shows anchors for. One owner per pixel: while the\n * active tool edits annotations, a link attached to an annotation stands\n * down so its parent can be selected and moved; a document's own links\n * navigate under every tool that enables link navigation.\n */\nexport function navigableLinksOf<Link extends LinkAnchorShape>(\n links: readonly Link[],\n annotationEditEnabled: boolean,\n): readonly Link[] {\n return annotationEditEnabled ? links.filter((link) => !link.attached) : links;\n}\n\n/**\n * The anchor's native `href`: only for a website the browser may open and\n * nothing chained after it. Blocked schemes, targets inside the document and\n * action chains activate through the plugin instead: a native navigation\n * would run the first action and silently drop the rest.\n */\nexport function linkHrefOf(link: LinkAnchorShape): string | null {\n const chained = (link.activate?.root?.next.length ?? 0) > 0;\n if (link.target.kind !== 'uri' || chained || link.target.uri === undefined) return null;\n return sanitizeExternalUri(link.target.uri);\n}\n\n/** A link's anchor, ready to draw. */\nexport interface LinkAnchor<Link> {\n readonly link: Link;\n /** Where the anchor sits, in the page layer's pixels. */\n readonly box: PixelRect;\n /** Its native `href` ({@link linkHrefOf}), or null. */\n readonly href: string | null;\n /** Its `title` and `aria-label`. */\n readonly label: string;\n}\n\n/**\n * The anchor for `link` on a page layer: its box from the link's `bounds`,\n * its `href`, and the label `labelOf` (the link plugin's `getLabel`) gives it.\n */\nexport function linkAnchorOf<\n Link extends LinkAnchorShape & {\n readonly bounds: { x: number; y: number; width: number; height: number };\n },\n>(link: Link, page: PageToPixels, labelOf: (link: Link) => string): LinkAnchor<Link> {\n return {\n link,\n box: rectInPixels(link.bounds, page),\n href: linkHrefOf(link),\n label: labelOf(link),\n };\n}\n\n/**\n * Whether a click on an anchor with an `href` stays the browser's: a click\n * with a modifier key opens a background tab or a new window. Every other\n * click is prevented and follows the link through the plugin.\n */\nexport function isModifiedClick(event: {\n metaKey: boolean;\n ctrlKey: boolean;\n shiftKey: boolean;\n altKey: boolean;\n}): boolean {\n return event.metaKey || event.ctrlKey || event.shiftKey || event.altKey;\n}\n","/**\n * What a page link's anchor tells the plugins: the context a click or a key\n * follows the link with (the link plugin's `activate()`), and the link's own\n * PDF events (`/AA`): entering and leaving through the layer's hover pump,\n * the press, the release, focus and blur through the actions plugin's\n * `dispatch()`. Only a link that is an annotation has events of its own. The\n * plugins' types are mirrored structurally and their refs stay opaque, so\n * this package stays free of EmbedPDF imports.\n */\n\n/** The events an anchor sends the actions plugin itself; entering and leaving go through the hover pump. */\nexport type LinkAnchorEvent = 'mouseDown' | 'mouseUp' | 'focus' | 'blur';\n\n/** A link's fields these read: its `/A` tree, the annotation it is, and which hover trees it has. */\nexport interface LinkEventShape<Ref, Action = unknown, HoverEvents = unknown> {\n readonly activate?: Action;\n readonly ref?: Ref;\n readonly hoverEvents?: HoverEvents;\n}\n\n/** The link as the source of an actions-plugin event. */\nexport interface LinkActionSource<Ref, Page> {\n readonly kind: 'link';\n readonly annotation: Ref;\n readonly page: Page;\n}\n\n/** The actions plugin, as an anchor uses it. */\nexport interface LinkActionDispatcher<Ref, Page> {\n dispatch(trigger: {\n readonly scope: 'annotation';\n readonly event: LinkAnchorEvent;\n readonly ref: Ref;\n readonly page: Page;\n readonly source: LinkActionSource<Ref, Page>;\n }): unknown;\n}\n\n/** One link layer's hover pump (`createHoverPump` in the actions plugin's contract). */\nexport interface LinkHoverPump<Ref, Page, HoverEvents> {\n hover(\n target: {\n readonly ref: Ref;\n readonly page: Page;\n readonly source: LinkActionSource<Ref, Page>;\n readonly events: HoverEvents;\n } | null,\n ): void;\n}\n\n/**\n * The context a click or a key follows `link` with: its `/A` tree (the\n * actions plugin runs it instead of the target), the annotation it is, its\n * page, and the view it was followed in (a page destination moves that one).\n */\nexport function linkActivateContextOf<Ref, Action, Page, Stage>(\n link: LinkEventShape<Ref, Action>,\n page: Page,\n stage: Stage | null,\n): { activate?: Action; ref?: Ref; page: Page; stage?: Stage } {\n return { activate: link.activate, ref: link.ref, page, ...(stage ? { stage } : {}) };\n}\n\n/**\n * Send `link`'s own event for a press, a release or a focus change: only for\n * a link that is an annotation, and only with the actions plugin.\n */\nexport function sendLinkEvent<Ref, Page>(\n actions: NoInfer<LinkActionDispatcher<Ref, Page>> | null,\n link: LinkEventShape<Ref>,\n page: Page,\n event: LinkAnchorEvent,\n): void {\n if (!actions || !link.ref) return;\n const source: LinkActionSource<Ref, Page> = { kind: 'link', annotation: link.ref, page };\n void actions.dispatch({ scope: 'annotation', event, ref: link.ref, page, source });\n}\n\n/**\n * Tell the layer's hover pump the pointer entered `link`: only a link that is\n * an annotation with hover trees has enter and exit events. Leaving is\n * `pump.hover(null)`.\n */\nexport function hoverLink<Ref, Page, HoverEvents>(\n pump: NoInfer<LinkHoverPump<Ref, Page, HoverEvents>> | null,\n link: LinkEventShape<Ref, unknown, HoverEvents>,\n page: Page,\n): void {\n if (!pump || !link.ref || !link.hoverEvents) return;\n pump.hover({\n ref: link.ref,\n page,\n source: { kind: 'link', annotation: link.ref, page },\n events: link.hoverEvents,\n });\n}\n","/**\n * A search layer's paint: each line of each match on a page, as a piece to\n * draw in the page layer's pixels. An upright line is a box (a positioned\n * element with rounded corners); a turned one is its true quad (an SVG\n * polygon). The active match's lines take the active color. The plugin's hit\n * is mirrored structurally, so this package stays free of EmbedPDF imports.\n */\nimport { isUprightQuad, quadInPixels, svgPoints } from './page-pixels';\nimport type { PageQuad, PageToPixels, PixelRect } from './page-pixels';\n\n/** A match as the search plugin lists it: the fields the paint reads. */\nexport interface SearchHitShape {\n /** Where the match starts in the document's text: with the line's index, its key. */\n readonly start: number;\n /** One per line the match spans, in page points. */\n readonly segments: readonly { readonly quad: PageQuad }[];\n}\n\n/** One line of a match, ready to draw. */\nexport interface SearchHighlightPiece<Hit extends SearchHitShape> {\n readonly key: string;\n readonly hit: Hit;\n /** Whether it's a line of the active match. */\n readonly active: boolean;\n readonly fill: string;\n /** The line's box in the page layer's pixels when it's upright; null when it's turned. */\n readonly box: PixelRect | null;\n /** The four corners as SVG `points`, for a turned line; null when it's upright. */\n readonly points: string | null;\n}\n\n/** What {@link searchHighlightsOf} paints with. */\nexport interface SearchHighlightOptions<Hit extends SearchHitShape> {\n /** The search's active match (compared by identity), or null. */\n readonly active: Hit | null;\n /** The page layer's transform. */\n readonly page: PageToPixels;\n /** The fill of a match, and of the active one (`paint('search-highlight', …)`). */\n readonly color: string;\n readonly activeColor: string;\n}\n\n/** Every line of `hits` as a piece to draw, in hit order. */\nexport function searchHighlightsOf<Hit extends SearchHitShape>(\n hits: readonly Hit[],\n options: SearchHighlightOptions<Hit>,\n): SearchHighlightPiece<Hit>[] {\n const { page } = options;\n return hits.flatMap((hit) =>\n hit.segments.map(({ quad }, line): SearchHighlightPiece<Hit> => {\n const active = hit === options.active;\n const fill = active ? options.activeColor : options.color;\n const key = `${hit.start}:${line}`;\n if (!isUprightQuad(quad)) {\n return { key, hit, active, fill, box: null, points: svgPoints(quadInPixels(quad, page)) };\n }\n const topLeft = page.toPixels(quad.upperLeft);\n const bottomRight = page.toPixels(quad.lowerRight);\n const box = {\n left: topLeft.x,\n top: topLeft.y,\n width: bottomRight.x - topLeft.x,\n height: bottomRight.y - topLeft.y,\n };\n return { key, hit, active, fill, box, points: null };\n }),\n );\n}\n","/**\n * Telling a click from a drag on something drawn over a page (a search\n * match). The press still reaches the page, so a drag that starts there\n * selects text; only a press that stays put counts as a click.\n */\n\n/**\n * How far (CSS px) a press may travel and still count as a click. A mouse or\n * pen press that travels 4 px starts selecting text (the selection plugin's\n * default drag threshold); a finger wobbles more on its own, and the Stage\n * treats a touch that stays within 10 px as a tap.\n */\nconst CLICK_SLOP_PX: Readonly<Record<string, number>> = { mouse: 4, pen: 4, touch: 10 };\n\n/** Where a press started, from its `pointerdown`. */\ninterface Press {\n x: number;\n y: number;\n slop: number;\n}\n\n/** A click detector for one layer: feed it the `pointerdown`, ask it at the `click`. */\nexport interface ClickDetector {\n /** A press began (`pointerdown`). */\n press(event: { clientX: number; clientY: number; pointerType: string }): void;\n /** Whether this `click` is a click, not the end of a drag. Forgets the press. */\n isClick(event: { clientX: number; clientY: number }): boolean;\n}\n\nexport function createClickDetector(): ClickDetector {\n let started: Press | null = null;\n return {\n press(event) {\n started = {\n x: event.clientX,\n y: event.clientY,\n slop: CLICK_SLOP_PX[event.pointerType] ?? CLICK_SLOP_PX.mouse,\n };\n },\n isClick(event) {\n const start = started;\n started = null;\n return !start || Math.hypot(event.clientX - start.x, event.clientY - start.y) < start.slop;\n },\n };\n}\n","/**\n * The projectors page surfaces give anchored UI: how a box on a page lands on screen right now\n * (a {@link ViewProjector}).\n *\n * - A Stage projects through its camera: pure state, no DOM reads, so the overlay and the pages\n * update in the same render ({@link stageViewProjector}).\n * - A page shown on its own (a `PageView`) has no camera, so anchored UI is placed by measuring\n * the page element in client space, portalled to `<body>` and positioned `fixed`, so no\n * ancestor's overflow clips it ({@link clientPageProjector}).\n */\nimport type { AnchoredRect, PageViewEnv, ViewProjector } from './anchored-position';\nimport type { PageRef } from './page-ref';\n\n/** The page transform members a projection reads; `PageTransform` satisfies it. */\nexport interface ProjectedTransform {\n readonly viewScale: number;\n readonly rotation: 0 | 90 | 180 | 270;\n readonly zoom: number;\n}\n\n/**\n * The page surface a client projector measures: the page context's members. `transform` may be\n * the transform or a function that returns the current one (a signal).\n */\nexport interface ClientProjectedPage {\n readonly ref: PageRef;\n readonly transform: ProjectedTransform | (() => ProjectedTransform);\n toClientRect(rect: AnchoredRect): AnchoredRect;\n toClientPoint(point: { x: number; y: number }): { x: number; y: number };\n}\n\n/**\n * A client-space projector for one page. `mounted` says whether the page\n * element is in the document yet: until it is, every projection is `null`,\n * and the anchored UI waits for the first measurable pass. The view is the\n * window. Pair it with {@link observeClientGeometry}, which reports the\n * browser-driven moves (document scroll, window resize) no state announces.\n * Every member reads `page` when it runs, so a page context whose members\n * change keeps one projector.\n */\nexport function clientPageProjector(\n page: ClientProjectedPage,\n mounted: () => boolean,\n): ViewProjector {\n const isThisPage = (other: PageRef) => other.objectNumber === page.ref.objectNumber;\n const transformOf = (): ProjectedTransform =>\n typeof page.transform === 'function' ? page.transform() : page.transform;\n return {\n space: 'client',\n toScreen: (other, rect) => (isThisPage(other) && mounted() ? page.toClientRect(rect) : null),\n toScreenPoint: (other, at) => (isThisPage(other) && mounted() ? page.toClientPoint(at) : null),\n viewEnv: (other): PageViewEnv | null => {\n if (!isThisPage(other)) return null;\n const transform = transformOf();\n return { scale: transform.viewScale, rotation: transform.rotation, zoom: transform.zoom };\n },\n view: () =>\n typeof document === 'undefined'\n ? null\n : {\n x: 0,\n y: 0,\n width: document.documentElement.clientWidth,\n height: document.documentElement.clientHeight,\n },\n };\n}\n\n/** What a Stage projection reads from the Stage lens; `StageHostCapability` satisfies it. */\nexport interface StageProjection {\n pageRectToViewport(page: PageRef, rect: AnchoredRect): AnchoredRect | null;\n getPageFrame(page: PageRef): { readonly transform: ProjectedTransform } | null;\n getViewportSize(): { width: number; height: number };\n}\n\n/**\n * The Stage's projector: anchored UI positions through the camera, in the Stage's own box\n * (`'overlay'` space). `stage()` gives the lens when a projection runs, or null without one\n * (nothing projects then). It reads only the Stage's state, so the binding's revision (the\n * visible pages) is what tells anchored UI to place itself again, in the same render as the\n * pages.\n */\nexport function stageViewProjector(stage: () => StageProjection | null): ViewProjector {\n return {\n space: 'overlay',\n toScreen: (page, rect) => stage()?.pageRectToViewport(page, rect) ?? null,\n toScreenPoint: (page, at) => {\n const rect = stage()?.pageRectToViewport(page, { x: at.x, y: at.y, width: 0, height: 0 });\n return rect ? { x: rect.x, y: rect.y } : null;\n },\n viewEnv: (page) => {\n const transform = stage()?.getPageFrame(page)?.transform;\n return transform\n ? { scale: transform.viewScale, rotation: transform.rotation, zoom: transform.zoom }\n : null;\n },\n view: () => {\n const size = stage()?.getViewportSize();\n return size && size.width > 0 && size.height > 0 ? { x: 0, y: 0, ...size } : null;\n },\n };\n}\n","/**\n * The page context: what a page surface (a Stage page, a `PageView`) hands the layers drawn on\n * it. A layer depends only on this, never on the Stage, so the same layer works on both.\n *\n * Its client-space conversions measure the page's content box on screen when they run, and leave\n * the rotation and scale math to the page transform (`pageTransform` in `@embedpdf/core-geometry`),\n * so no adapter derives it again. The types are structural, like the rest of this package: an\n * adapter names its page context with the geometry's `PageTransform` and the render plugin's\n * `PageViewDemand`.\n */\nimport type { PageRef } from './page-ref';\n\ninterface ContextPoint {\n x: number;\n y: number;\n}\n\ninterface ContextRect {\n x: number;\n y: number;\n width: number;\n height: number;\n}\n\n/** The bands reserved around a page for its chrome, in screen pixels per side. */\nexport interface PageFrameShape {\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\n\n/** What the conversions need from the page transform; `PageTransform` satisfies it. */\nexport interface PageContextTransform {\n viewToPage(point: ContextPoint): ContextPoint;\n pageToView(point: ContextPoint): ContextPoint;\n pageToViewRect(rect: ContextRect): ContextRect;\n}\n\n/** Where the page's content box is on screen, in client pixels: `getBoundingClientRect()`. */\nexport interface ClientBox {\n readonly left: number;\n readonly top: number;\n}\n\n/** Between client (screen) pixels and points on one page. */\nexport interface PageClientSpace {\n /** A client (screen) point to a point on the page: the one platform-bound hit test. */\n toPagePoint(clientX: number, clientY: number): ContextPoint;\n /**\n * A point on the page to client pixels, the exact inverse of `toPagePoint` (rotation applied),\n * so UI in the view can anchor to a page point without a Stage camera.\n */\n toClientPoint(point: ContextPoint): ContextPoint;\n /** A box on the page to the client-pixel box around it: the box twin of `toClientPoint`. */\n toClientRect(rect: ContextRect): ContextRect;\n}\n\n/**\n * The page context. `Transform` is the page transform (`PageTransform`), `Demand` what the view\n * wants rendered (`PageViewDemand`).\n */\nexport interface PageContext<\n Transform extends PageContextTransform,\n Demand,\n> extends PageClientSpace {\n documentId: string;\n /**\n * The page's durable address: use it for keys, rendering and annotations (read\n * `ref.objectNumber` where a map key is needed). The same object for the surface's lifetime,\n * so layers may key their work on it.\n */\n ref: PageRef;\n /** The display index (page N), from 0: for ordering and page numbers people read. */\n pageIndex: number;\n /**\n * The bands reserved around the page, in screen pixels per side. The page chrome draws into\n * the outer box (content and frame): a label in the bottom band is\n * `bottom: 0; height: frame.bottom`.\n */\n frame: PageFrameShape;\n /**\n * The one bridge between PDF points, view pixels and device pixels for this page: `toPixels`\n * to place content, `renderScale` and `deviceWidth` to render, `contentWidth` for sizes\n * relative to the page. Never multiply by the scale or the device pixel ratio yourself.\n */\n transform: Transform;\n /**\n * What the page's view wants rendered, as a pull the host fills: a Stage reads the page's\n * visible part live at call time (a zero box while it is off screen, see\n * {@link stagePageDemand}); a surface without a camera leaves it out, which means the whole\n * page.\n */\n getViewDemand?: () => Demand;\n /**\n * The hosting view's identity: the stage lens id (`stage.getLensId()`) or a per-instance\n * `PageView` id. Per-view raster planning (tiles) keys its state by it, so two views showing\n * the same page never fight over one plan.\n */\n view: string;\n}\n\n/**\n * The client-space conversions of a page whose content box `box()` measures on screen when a\n * conversion runs: client pixels to box-local view pixels, then the transform undoes the turn\n * and the scale. `toPagePoint` and `toClientPoint` read the same live box, so they never drift.\n */\nexport function pageClientSpace(\n transform: () => PageContextTransform,\n box: () => ClientBox,\n): PageClientSpace {\n return {\n toPagePoint: (clientX, clientY) => {\n const origin = box();\n return transform().viewToPage({ x: clientX - origin.left, y: clientY - origin.top });\n },\n toClientPoint: (point) => {\n const origin = box();\n const viewPoint = transform().pageToView(point);\n return { x: origin.left + viewPoint.x, y: origin.top + viewPoint.y };\n },\n toClientRect: (rect) => {\n const origin = box();\n const viewRect = transform().pageToViewRect(rect);\n return {\n x: origin.left + viewRect.x,\n y: origin.top + viewRect.y,\n width: viewRect.width,\n height: viewRect.height,\n };\n },\n };\n}\n\n/**\n * Build a page context. `getRect` is the page's content box on screen (the turned content\n * element's bounding box), read when a conversion runs.\n */\nexport function makePageContext<Transform extends PageContextTransform, Demand = never>(\n documentId: string,\n view: string,\n ref: PageRef,\n pageIndex: number,\n frame: PageFrameShape,\n transform: Transform,\n getRect: () => ClientBox,\n getViewDemand?: () => Demand,\n): PageContext<Transform, Demand> {\n return {\n documentId,\n view,\n ref,\n pageIndex,\n frame,\n transform,\n ...(getViewDemand ? { getViewDemand } : {}),\n ...pageClientSpace(() => transform, getRect),\n };\n}\n\n/**\n * A page context that reads every member through `current()`: the same object for a surface's\n * lifetime, current in what it answers. For an adapter whose layers keep the object they were\n * given once (a Svelte component reads `page.transform` in a template and follows the zoom).\n */\nexport function livePageContext<Context extends PageContext<PageContextTransform, unknown>>(\n current: () => Context,\n): Context {\n const live: PageContext<PageContextTransform, unknown> = {\n get documentId() {\n return current().documentId;\n },\n get ref() {\n return current().ref;\n },\n get pageIndex() {\n return current().pageIndex;\n },\n get frame() {\n return current().frame;\n },\n get transform() {\n return current().transform;\n },\n get view() {\n return current().view;\n },\n get getViewDemand() {\n return current().getViewDemand;\n },\n toPagePoint: (clientX, clientY) => current().toPagePoint(clientX, clientY),\n toClientPoint: (point) => current().toClientPoint(point),\n toClientRect: (rect) => current().toClientRect(rect),\n };\n return live as Context;\n}\n","/**\n * Which parts of a page the layers on it paint themselves. The annotation\n * layer paints the annotations, the form layer the form fields; while one is\n * on the page it says so here, and the page's picture (the render layer)\n * leaves that part out, so nothing is drawn twice and nobody passes a flag.\n *\n * Kept per page surface, by the page ref it hands its layers (`page.ref`):\n * each surface makes its own, and keeps it while it shows the page, through\n * zooms. Changes reach subscribers a microtask later, so a layer may say what\n * it paints from any point in its framework's lifecycle (React's insertion\n * effects included, which must not start updates); `painted()` is current at\n * once.\n */\n\n/** A part of a page a layer can paint instead of the page's picture. */\nexport type PagePart = 'annotations' | 'formFields';\n\n/** Which parts the layers on one page paint. */\nexport interface PaintedParts {\n readonly annotations: boolean;\n readonly formFields: boolean;\n}\n\n/** One page's layers: which parts they paint. */\nexport interface PageLayers {\n /** Say this layer paints `part`, until the function it returns is called. */\n paint(part: PagePart): () => void;\n /** Which parts are painted now: the same object for the same answer. */\n painted(): PaintedParts;\n /** Hear when `painted()` changes. Returns the unsubscribe. */\n subscribe(listener: () => void): () => void;\n}\n\n/** Every answer `painted()` gives, one object each, so the same answer is the same object. */\nconst ANSWERS: readonly (readonly PaintedParts[])[] = [false, true].map((annotations) =>\n [false, true].map((formFields) => Object.freeze({ annotations, formFields })),\n);\n\nconst pages = new WeakMap<object, PageLayers>();\n\n/** The layers of one page surface, by the page ref it hands its layers. */\nexport function pageLayersOf(pageRef: object): PageLayers {\n let layers = pages.get(pageRef);\n if (!layers) {\n layers = createPageLayers();\n pages.set(pageRef, layers);\n }\n return layers;\n}\n\nfunction createPageLayers(): PageLayers {\n const painters: Record<PagePart, number> = { annotations: 0, formFields: 0 };\n const listeners = new Set<() => void>();\n let painted = ANSWERS[0]![0]!;\n let notifying = false;\n\n const update = () => {\n const next = ANSWERS[painters.annotations > 0 ? 1 : 0]![painters.formFields > 0 ? 1 : 0]!;\n if (next === painted) return;\n painted = next;\n if (notifying) return;\n notifying = true;\n queueMicrotask(() => {\n notifying = false;\n listeners.forEach((listener) => listener());\n });\n };\n\n return {\n paint(part) {\n painters[part] += 1;\n update();\n let released = false;\n return () => {\n if (released) return;\n released = true;\n painters[part] -= 1;\n update();\n };\n },\n painted: () => painted,\n subscribe(listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n };\n}\n\n/** The parts a render layer was told to draw (`true`) or leave out (`false`). */\nexport interface PicturePartProps {\n readonly annotations?: boolean;\n readonly formFields?: boolean;\n}\n\n/** What the user may read of each part (the render plugin's `getLayerRights()`). */\nexport interface PartRights {\n readonly annotations: boolean;\n readonly formFields: boolean;\n}\n\n/** The picture's layer options, as the render plugin takes them. */\nexport interface PictureLayerOptions {\n includeAnnotations: boolean;\n includeFormFields: boolean;\n}\n\n/**\n * What the page's picture draws, as the render plugin's layer options: a part\n * the props name is drawn or left out as they say; any other part is drawn\n * when no layer on the page paints it and the user may read it. Both are\n * always given, so a picture that leaves the annotations to a layer still\n * draws the form fields.\n */\nexport function pictureLayerOptionsOf(\n painted: PaintedParts,\n props: PicturePartProps,\n may: PartRights,\n): PictureLayerOptions {\n return {\n includeAnnotations: props.annotations ?? (!painted.annotations && may.annotations),\n includeFormFields: props.formFields ?? (!painted.formFields && may.formFields),\n };\n}\n\n/**\n * The parts a picture draws that a layer on the page paints too, because the\n * props asked for them: drawn twice.\n */\nexport function partsDrawnTwice(painted: PaintedParts, props: PicturePartProps): PagePart[] {\n const twice: PagePart[] = [];\n if (props.annotations === true && painted.annotations) twice.push('annotations');\n if (props.formFields === true && painted.formFields) twice.push('formFields');\n return twice;\n}\n","/**\n * A page surface's geometry, the same on a Stage page and in a `PageView`, in every framework.\n *\n * Every number comes from the page transform: the display footprint (`viewWidth`/`viewHeight`,\n * already swapped for a quarter turn and snapped to device pixels) and the unturned content box\n * (`contentWidth`/`contentHeight`). The outer box is the footprint plus the bands reserved for\n * page chrome; the drop shadow sits at the footprint, axis-aligned, so it stays put when the page\n * turns; the content box is centred on the footprint and turned about its centre, with no\n * translate, so an upright page carries no transform and snaps to pixels like its shadow.\n */\nimport type { PageFrameShape } from './page-context';\n\n/** A box in its parent's pixels. */\nexport interface SurfaceBox {\n left: number;\n top: number;\n width: number;\n height: number;\n}\n\n/** The transform members a surface is laid out from; `PageTransform` satisfies it. */\nexport interface SurfaceTransform {\n readonly rotation: 0 | 90 | 180 | 270;\n readonly viewWidth: number;\n readonly viewHeight: number;\n readonly contentWidth: number;\n readonly contentHeight: number;\n}\n\n/** The three boxes of a page surface. */\nexport interface PageSurfaceLayout {\n /** The footprint and the reserved bands, in the surface's parent; the page chrome fills it. */\n outer: SurfaceBox;\n /** The drop shadow at the footprint, in the outer box. */\n shadow: SurfaceBox;\n /** The page's content box in the outer box, before it turns; its layers draw in it. */\n content: SurfaceBox;\n /** The CSS transform that turns the content box about its centre, or null when it's upright. */\n turn: string | null;\n}\n\n/**\n * Lay out a page surface. `footprint` is where the page's footprint sits in the surface's\n * parent (a Stage page's device-snapped `screenX`/`screenY`); the outer box sits one band further\n * out, so the content keeps its place. Without it the outer box is at 0, 0 (a `PageView`, placed\n * by the page around it).\n */\nexport function pageSurfaceLayout(\n transform: SurfaceTransform,\n frame: PageFrameShape,\n footprint?: { x: number; y: number },\n): PageSurfaceLayout {\n const { viewWidth, viewHeight, contentWidth, contentHeight, rotation } = transform;\n return {\n outer: {\n left: footprint ? footprint.x - frame.left : 0,\n top: footprint ? footprint.y - frame.top : 0,\n width: viewWidth + frame.left + frame.right,\n height: viewHeight + frame.top + frame.bottom,\n },\n shadow: { left: frame.left, top: frame.top, width: viewWidth, height: viewHeight },\n content: {\n left: frame.left + (viewWidth - contentWidth) / 2,\n top: frame.top + (viewHeight - contentHeight) / 2,\n width: contentWidth,\n height: contentHeight,\n },\n turn: rotation ? `rotate(${rotation}deg)` : null,\n };\n}\n\n/** What {@link stagePageDemand} reads from the Stage; `StageCapability` satisfies it. */\nexport interface StageDemandSource {\n listVisiblePages(): readonly {\n readonly ref: { readonly objectNumber: number };\n readonly transform: { readonly deviceWidth: number };\n readonly visibleRect: { x: number; y: number; width: number; height: number };\n }[];\n}\n\n/** What a page's view wants rendered; the render plugin's `PageViewDemand` takes it. */\nexport interface StagePageDemand {\n desiredDeviceWidth: number;\n visibleRect: { x: number; y: number; width: number; height: number };\n}\n\n/**\n * What a Stage page's view wants rendered, read live from the Stage when the render layer asks:\n * the page's visible part at its device width, or a zero box (want nothing) while the page is\n * off screen, at `offScreenDeviceWidth`. Visibility is the Stage's data, so no adapter derives\n * camera math or keeps a copy of it.\n */\nexport function stagePageDemand(\n stage: StageDemandSource,\n pageObjectNumber: number,\n offScreenDeviceWidth: number,\n): StagePageDemand {\n const visible = stage\n .listVisiblePages()\n .find((visiblePage) => visiblePage.ref.objectNumber === pageObjectNumber);\n return visible\n ? { desiredDeviceWidth: visible.transform.deviceWidth, visibleRect: visible.visibleRect }\n : {\n desiredDeviceWidth: offScreenDeviceWidth,\n visibleRect: { x: 0, y: 0, width: 0, height: 0 },\n };\n}\n\n/** One CSS pixel per 1/96 inch, one PDF point per 1/72 inch. */\nconst CSS_PIXELS_PER_POINT = 96 / 72;\n\n/** The page a `PageView` shows, as its document's page list has it. */\nexport interface PageViewPage {\n readonly size: { readonly width: number; readonly height: number };\n readonly rotation: 0 | 90 | 180 | 270;\n readonly userUnit: number;\n}\n\n/** The input of `pageTransform()` (`@embedpdf/core-geometry`) for a `PageView`. */\nexport interface PageViewTransformInput {\n pageSize: { width: number; height: number };\n rotation: 0 | 90 | 180 | 270;\n scale: number;\n baseScale: number;\n dpr: number;\n}\n\n/**\n * What a `PageView` builds its page transform from: no camera, so the scale (view pixels per\n * point) is `width` over the page's width. The base scale is physical 100% on the web, a point\n * at 96/72 CSS pixels times the page's /UserUnit, so `transform.zoom` still means something: a\n * thumbnail-sized page reads as zoomed out. Without a page yet, a 1 × 1 placeholder.\n */\nexport function pageViewTransformInput(\n page: PageViewPage | null,\n width: number,\n dpr: number,\n): PageViewTransformInput {\n return {\n pageSize: page ? { width: page.size.width, height: page.size.height } : { width: 1, height: 1 },\n rotation: page?.rotation ?? 0,\n scale: page ? width / page.size.width : 1,\n baseScale: CSS_PIXELS_PER_POINT * (page?.userUnit ?? 1),\n dpr,\n };\n}\n","/**\n * A page's own pointer listener: what a page pointer source (`PagePointerSource`, a `PageView`'s\n * pointer surface) attaches to its element. It turns each pointer event into a point on the page\n * through the page context, so it needs nothing but the page, and hands the sample to the\n * interaction hub, whose active tool decides what the press means.\n *\n * A Stage takes its pointer input itself (`createStageSurface`); this is for one page on its own.\n * Like the rest of this package it speaks to the hub through structural types\n * ({@link PagePointerHub}), which `InteractionHostCapability` satisfies.\n */\nimport type { PageRef } from './page-ref';\n\ninterface PointerPoint {\n x: number;\n y: number;\n}\n\n/**\n * A multi-click counter. `pointerdown.detail` is 0 or 1 in several browsers, so clicks are\n * counted here from timing and distance: the standard double and triple click. Input is\n * normalized in the adapter; the hub and its handlers stay pure.\n */\nexport function createClickCounter(maxGapMs = 400, maxDistancePx = 6) {\n let last = 0;\n let lastX = 0;\n let lastY = 0;\n let count = 0;\n return (now: number, x: number, y: number): number => {\n count =\n now - last <= maxGapMs && Math.hypot(x - lastX, y - lastY) <= maxDistancePx ? count + 1 : 1;\n last = now;\n lastX = x;\n lastY = y;\n return count;\n };\n}\n\n/** What the listener reads from the page context when an event arrives. */\nexport interface PagePointerPage {\n readonly ref: PageRef;\n readonly transform: {\n readonly viewScale: number;\n readonly rotation: 0 | 90 | 180 | 270;\n readonly zoom: number;\n };\n toPagePoint(clientX: number, clientY: number): PointerPoint;\n}\n\n/** The sample the listener hands the hub: structurally the interaction plugin's `PointerSample`. */\nexport interface PagePointerSample {\n phase: 'down' | 'move' | 'up';\n viewport: PointerPoint;\n page: {\n ref: PageRef;\n point: PointerPoint;\n scale: number;\n rotation: 0 | 90 | 180 | 270;\n zoom: number;\n };\n project: (page: PageRef) => PointerPoint | null;\n modifiers: { shift: boolean; alt: boolean; ctrl: boolean; meta: boolean };\n clickCount: number;\n pointerType: 'mouse' | 'pen' | 'touch';\n}\n\n/** What the listener needs from the interaction hub; `InteractionHostCapability` satisfies it. */\nexport interface PagePointerHub {\n dispatchPointer(sample: PagePointerSample): void;\n}\n\n/**\n * Listen to the pointer on `element` for the page `page()` gives when an event arrives (a zoom\n * changes it, and a drag carries on through it), and dispatch every event to `hub`. A press\n * starts on the element; while it lasts the window listens, so a drag goes on outside the page.\n * Returns the function that stops listening.\n */\nexport function attachPagePointer(\n element: HTMLElement,\n hub: PagePointerHub,\n page: () => PagePointerPage,\n): () => void {\n const clicks = createClickCounter();\n const sample = (\n phase: PagePointerSample['phase'],\n event: PointerEvent,\n clickCount = 1,\n ): PagePointerSample => {\n const current = page();\n const rect = element.getBoundingClientRect();\n return {\n phase,\n viewport: { x: event.clientX - rect.left, y: event.clientY - rect.top },\n // The same per-page facts the Stage's own source finds for the page under the pointer,\n // read off the page transform, so every page surface drives handlers alike.\n page: {\n ref: current.ref,\n point: current.toPagePoint(event.clientX, event.clientY),\n scale: current.transform.viewScale,\n rotation: current.transform.rotation,\n zoom: current.transform.zoom,\n },\n // A page's source projects onto its own page only. `toPagePoint` is unclamped (the drag\n // listener is on the window), so a gesture that starts here keeps tracking past the edge.\n project: (target) =>\n target.kind === current.ref.kind && target.objectNumber === current.ref.objectNumber\n ? current.toPagePoint(event.clientX, event.clientY)\n : null,\n modifiers: {\n shift: event.shiftKey,\n alt: event.altKey,\n ctrl: event.ctrlKey,\n meta: event.metaKey,\n },\n clickCount,\n pointerType: (event.pointerType || 'mouse') as PagePointerSample['pointerType'],\n };\n };\n let dragging = false;\n\n const down = (event: PointerEvent) => {\n if (event.button !== 0) return;\n dragging = true;\n hub.dispatchPointer(sample('down', event, clicks(Date.now(), event.clientX, event.clientY)));\n };\n // Hovering, with no press: cursor feedback only, from the element.\n const hover = (event: PointerEvent) => {\n if (dragging) return;\n hub.dispatchPointer(sample('move', event));\n };\n const drag = (event: PointerEvent) => {\n if (!dragging) return;\n hub.dispatchPointer(sample('move', event));\n };\n const up = (event: PointerEvent) => {\n if (!dragging) return;\n dragging = false;\n hub.dispatchPointer(sample('up', event));\n };\n\n element.addEventListener('pointerdown', down);\n element.addEventListener('pointermove', hover);\n window.addEventListener('pointermove', drag);\n window.addEventListener('pointerup', up);\n return () => {\n element.removeEventListener('pointerdown', down);\n element.removeEventListener('pointermove', hover);\n window.removeEventListener('pointermove', drag);\n window.removeEventListener('pointerup', up);\n };\n}\n","/**\n * A Stage scrollbar's geometry and its press handling, shared by every framework's `Scrollbar`.\n *\n * The Stage gives its scroll position as a scrolling element has it (`scrollTop`,\n * `scrollHeight`, `clientHeight` and their horizontal twins, in screen pixels). From that and the\n * measured track this module draws the thumb, and turns presses into Stage calls the way a native\n * bar does: a thumb drag keeps the point it grabbed under the pointer, and a press on the track\n * pages toward the pointer, repeating while held and stopping when the thumb reaches it. A drag\n * is frozen at the press (grab point, travel, range) and applied as relative pans: absolute in\n * feel on a bounded Stage, and steady on an unbounded one, whose range moves under the pointer.\n *\n * The adapters keep their reactive glue: the track's size, auto-hide, hover and the markup.\n */\n\n/** The scroll position a Stage reports (`ScrollMetrics` from the stage plugin satisfies it). */\nexport interface ScrollbarMetrics {\n readonly scrollLeft: number;\n readonly scrollTop: number;\n readonly scrollWidth: number;\n readonly scrollHeight: number;\n readonly clientWidth: number;\n readonly clientHeight: number;\n}\n\n/** The bar along one axis, in track pixels. */\nexport interface ScrollbarLayout {\n /** The view's length along the axis. */\n client: number;\n /** How far the view is scrolled. */\n offset: number;\n /** The farthest it can scroll. */\n maxOffset: number;\n thumbLength: number;\n /** How far the thumb can move along the track. */\n travel: number;\n thumbPosition: number;\n}\n\n/**\n * The thumb from the metrics and the track's length: one formula for drawing, dragging and\n * paging, so they never disagree. The thumb never gets shorter than `minThumbSize`, and its\n * position stays proportional over the travel that leaves.\n */\nexport function scrollbarLayout(\n metrics: ScrollbarMetrics,\n vertical: boolean,\n trackPx: number,\n minThumbSize: number,\n): ScrollbarLayout {\n const client = vertical ? metrics.clientHeight : metrics.clientWidth;\n const total = vertical ? metrics.scrollHeight : metrics.scrollWidth;\n const offset = vertical ? metrics.scrollTop : metrics.scrollLeft;\n const maxOffset = Math.max(0, total - client);\n const thumbLength = Math.min(\n trackPx,\n Math.max(minThumbSize, total > 0 ? (client / total) * trackPx : 0),\n );\n const travel = Math.max(0, trackPx - thumbLength);\n const thumbPosition = maxOffset > 0 ? (offset / maxOffset) * travel : 0;\n return { client, offset, maxOffset, thumbLength, travel, thumbPosition };\n}\n\n/** The Stage calls a scrollbar makes; `StageCapability` satisfies it. */\nexport interface ScrollbarStage {\n scrollTo(position: { top?: number; left?: number }): void;\n scrollBy(delta: { top?: number; left?: number }): void;\n panBy(dx: number, dy: number): void;\n}\n\n/** The bar as it is at a press: what the press handlers read. */\nexport interface ScrollbarTrack {\n readonly stage: ScrollbarStage;\n readonly vertical: boolean;\n /** The track element: pointer positions are measured from its start. */\n readonly element: Element;\n /** The layout drawn now. */\n readonly layout: ScrollbarLayout;\n /** The layout from the Stage's metrics as they are now, for each paging step. */\n liveLayout(): ScrollbarLayout;\n}\n\n/**\n * The pointer event a press handler reads: a DOM `PointerEvent`, or a framework's own event\n * with the same members (React's), so `stopPropagation` and `currentTarget` mean what the\n * framework's handlers mean.\n */\nexport interface ScrollbarPointerEvent {\n readonly button: number;\n readonly pointerId: number;\n readonly clientX: number;\n readonly clientY: number;\n readonly target: EventTarget | null;\n readonly currentTarget: EventTarget | null;\n preventDefault(): void;\n stopPropagation(): void;\n}\n\n/** A scrollbar's presses: wire the thumb's and the track's pointer events to these. */\nexport interface ScrollbarPresses {\n /** A press on the thumb starts a drag. */\n pressThumb(event: ScrollbarPointerEvent, track: ScrollbarTrack): void;\n /**\n * A press on the track itself: `'page'` steps 90% of a view toward the pointer, repeats while\n * held and stops at the pointer; `'jump'` puts the thumb under the pointer and goes on as a\n * drag. A press that reached the track from the thumb is the thumb's.\n */\n pressTrack(event: ScrollbarPointerEvent, track: ScrollbarTrack, mode: 'page' | 'jump'): void;\n /** A pointer move on the track (pointer capture sends them there): drags, and paging's target. */\n move(event: ScrollbarPointerEvent, track: ScrollbarTrack): void;\n /** The press ended: no drag, no repeat. Also call it when the bar goes away. */\n release(): void;\n}\n\nconst clamp01 = (value: number) => Math.min(1, Math.max(0, value));\n\n/**\n * Pointer capture where the browser allows it: a pointer already released (a pen or touch race,\n * a synthetic event in a test) throws, and the press must still work, only without capture.\n */\nconst capturePointer = (element: Element, pointerId: number) => {\n try {\n element.setPointerCapture(pointerId);\n } catch {\n // Without capture, moves still arrive while the pointer stays on the bar.\n }\n};\n\n/** The pointer's position along the track, in pixels from its start. */\nconst positionOnTrack = (event: ScrollbarPointerEvent, track: ScrollbarTrack): number => {\n const rect = track.element.getBoundingClientRect();\n return track.vertical ? event.clientY - rect.top : event.clientX - rect.left;\n};\n\n/**\n * One scrollbar's press handling. `onDragging` hears when a drag starts and ends, for the bar's\n * `data-dragging` and to keep it shown.\n */\nexport function createScrollbarPresses(onDragging: (dragging: boolean) => void): ScrollbarPresses {\n let drag: { grab: number; applied: number; max: number; travel: number } | null = null;\n let paging: { timer: number } | null = null;\n let lastPointer = 0;\n\n const stopPaging = () => {\n if (!paging) return;\n clearTimeout(paging.timer);\n clearInterval(paging.timer);\n paging = null;\n };\n const beginDrag = (grab: number, applied: number, layout: ScrollbarLayout) => {\n drag = { grab, applied, max: layout.maxOffset, travel: layout.travel };\n onDragging(true);\n };\n\n return {\n pressThumb(event, track) {\n if (event.button !== 0) return;\n event.preventDefault();\n event.stopPropagation();\n capturePointer(event.target as Element, event.pointerId);\n const layout = track.layout;\n beginDrag(positionOnTrack(event, track) - layout.thumbPosition, layout.offset, layout);\n },\n\n pressTrack(event, track, mode) {\n if (event.button !== 0 || event.target !== event.currentTarget) return;\n event.preventDefault();\n capturePointer(event.currentTarget as Element, event.pointerId);\n const position = (lastPointer = positionOnTrack(event, track));\n const { layout, stage, vertical } = track;\n\n if (mode === 'jump') {\n // The thumb lands centred under the pointer, and the press goes on as a drag.\n const wanted =\n layout.travel > 0\n ? clamp01((position - layout.thumbLength / 2) / layout.travel) * layout.maxOffset\n : 0;\n stage.scrollTo(vertical ? { top: wanted } : { left: wanted });\n beginDrag(layout.thumbLength / 2, wanted, layout);\n return;\n }\n\n // Step toward the pointer, repeat while held, and stop once the thumb reaches it.\n const direction = position < layout.thumbPosition ? -1 : 1;\n const step = () => {\n const live = track.liveLayout();\n const reached =\n direction === 1\n ? lastPointer <= live.thumbPosition + live.thumbLength\n : lastPointer >= live.thumbPosition;\n if (reached) {\n stopPaging();\n return;\n }\n const distance = direction * live.client * 0.9;\n stage.scrollBy(vertical ? { top: distance } : { left: distance });\n };\n step();\n // The native cadence: a beat before the repeat starts, then a steady march.\n const timer = window.setTimeout(() => {\n if (paging) paging.timer = window.setInterval(step, 80);\n }, 350);\n paging = { timer };\n },\n\n move(event, track) {\n if (drag) {\n if (drag.travel <= 0) return;\n const wanted =\n clamp01((positionOnTrack(event, track) - drag.grab) / drag.travel) * drag.max;\n const delta = wanted - drag.applied;\n if (delta) {\n track.stage.panBy(track.vertical ? 0 : -delta, track.vertical ? -delta : 0);\n drag.applied = wanted;\n }\n } else if (paging) {\n lastPointer = positionOnTrack(event, track);\n }\n },\n\n release() {\n drag = null;\n stopPaging();\n onDragging(false);\n },\n };\n}\n","/**\n * The DOM half of a toolbar that folds to fit: measuring. A hidden layer\n * renders every unit in every variant (and each group's folded forms, and the\n * \"More\" button); each element there reports its length here, live, so a new\n * language, a font loading, browser zoom and your CSS all re-measure on their\n * own. A length runs along the toolbar: a width when it is horizontal, a\n * height when it is vertical. The lengths become the fit metrics core-ui's\n * `solve()` takes (the structural twin, {@link ToolbarFitMetrics}); the\n * adapter renders what the solve decides.\n */\nimport type { Orientation } from './orientation';\n\n/** The keys the measurement layer reports lengths under, the ones the metrics look up. */\nexport const toolbarMeasureKey = {\n /** A unit (`NormalizedUnit.key`) at one variant. */\n unit: (unitKey: string, variant: string): string => `u:${unitKey}@${variant}`,\n /** A group in its collapsed form. */\n group: (groupId: string): string => `g:${groupId}`,\n /** A shed group's disclosure trigger, measured with the whole group behind it. */\n groupTrigger: (groupId: string): string => `gt:${groupId}`,\n /** The \"More\" button. */\n overflowTrigger: 't:',\n} as const;\n\n/** The lengths a fit needs: the structural twin of core-ui's `FitMetrics`. */\nexport interface ToolbarFitMetrics {\n unit(key: string, variant: string): number | undefined;\n groupCollapsed(groupId: string): number | undefined;\n groupTrigger(groupId: string): number | undefined;\n readonly overflowTrigger: number;\n readonly gap: number;\n readonly separator: number;\n}\n\n/**\n * The measured lengths of one toolbar, kept apart per orientation: a toolbar\n * turned on its side never fits with the other axis's numbers.\n */\nexport interface ToolbarLengths {\n /**\n * A measured element's length along `orientation`, under its\n * {@link toolbarMeasureKey}. True when it changed by more than half a pixel:\n * render (and solve) again.\n */\n report(orientation: Orientation, key: string, length: number): boolean;\n /**\n * The lengths along `orientation` as fit metrics, for items `gap` px apart\n * and separators `separatorWidth` px thick (a separator's width is its\n * thickness, across a horizontal bar or down a vertical one). A separator is\n * one more flex child: its thickness plus one gap. Until the \"More\" button\n * is measured it counts as 32 px.\n */\n metrics(orientation: Orientation, gap: number, separatorWidth: number): ToolbarFitMetrics;\n}\n\nexport function createToolbarLengths(): ToolbarLengths {\n const byOrientation: Record<Orientation, Map<string, number>> = {\n horizontal: new Map(),\n vertical: new Map(),\n };\n return {\n report(orientation, key, length) {\n const lengths = byOrientation[orientation];\n const previous = lengths.get(key);\n if (previous !== undefined && Math.abs(previous - length) <= 0.5) return false;\n lengths.set(key, length);\n return true;\n },\n metrics(orientation, gap, separatorWidth) {\n const lengths = byOrientation[orientation];\n return {\n unit: (key, variant) => lengths.get(toolbarMeasureKey.unit(key, variant)),\n groupCollapsed: (groupId) => lengths.get(toolbarMeasureKey.group(groupId)),\n groupTrigger: (groupId) => lengths.get(toolbarMeasureKey.groupTrigger(groupId)),\n overflowTrigger: lengths.get(toolbarMeasureKey.overflowTrigger) ?? 32,\n gap,\n separator: separatorWidth + gap,\n };\n },\n };\n}\n\n// ── elements ─────────────────────────────────────────────────────────────────\n\n/** Report one side of an element's border box now and whenever it resizes. */\nfunction observeBorderBox(\n element: Element,\n side: 'width' | 'height',\n onLength: (length: number) => void,\n): () => void {\n const report = () => onLength(element.getBoundingClientRect()[side]);\n report();\n const observer = new ResizeObserver(report);\n observer.observe(element);\n return () => observer.disconnect();\n}\n\n/**\n * Report an element's width (its border box) now and whenever it resizes. Put\n * it on every element of a horizontal toolbar's measurement layer, and on a\n * custom slot's `<slot>` socket, which is measured live where it renders.\n * Returns the detach.\n */\nexport function observeWidth(element: Element, onWidth: (width: number) => void): () => void {\n return observeBorderBox(element, 'width', onWidth);\n}\n\n/** {@link observeWidth} for a vertical toolbar: an element's border-box height. */\nexport function observeHeight(element: Element, onHeight: (height: number) => void): () => void {\n return observeBorderBox(element, 'height', onHeight);\n}\n\n/** An element's length along a toolbar: {@link observeWidth} or {@link observeHeight}. */\nexport function observeLength(\n element: Element,\n orientation: Orientation,\n onLength: (length: number) => void,\n): () => void {\n return observeBorderBox(element, orientation === 'vertical' ? 'height' : 'width', onLength);\n}\n\n// ── the room a toolbar fills ─────────────────────────────────────────────────\n\n/** Report one side of an element's content box now, then on changes over half a pixel. */\nfunction observeContentBox(\n element: HTMLElement,\n side: 'width' | 'height',\n onLength: (length: number) => void,\n): () => void {\n let last = side === 'width' ? element.clientWidth : element.clientHeight;\n onLength(last);\n const observer = new ResizeObserver((entries) => {\n const length = entries[entries.length - 1]?.contentRect[side];\n if (length === undefined || Math.abs(last - length) <= 0.5) return;\n last = length;\n onLength(length);\n });\n observer.observe(element);\n return () => observer.disconnect();\n}\n\n/**\n * Report the width a toolbar has to fill (its content box) now and whenever\n * it changes by more than half a pixel. Returns the detach.\n */\nexport function observeContentWidth(\n element: HTMLElement,\n onWidth: (width: number) => void,\n): () => void {\n return observeContentBox(element, 'width', onWidth);\n}\n\n/**\n * The height a vertical toolbar has to fill, like {@link observeContentWidth}.\n * The toolbar fits the height it's given, so its container needs one.\n */\nexport function observeContentHeight(\n element: HTMLElement,\n onHeight: (height: number) => void,\n): () => void {\n return observeContentBox(element, 'height', onHeight);\n}\n\n/** The room along a toolbar: {@link observeContentWidth} or {@link observeContentHeight}. */\nexport function observeContentLength(\n element: HTMLElement,\n orientation: Orientation,\n onLength: (length: number) => void,\n): () => void {\n return observeContentBox(element, orientation === 'vertical' ? 'height' : 'width', onLength);\n}\n","/**\n * Which way a row of controls runs, and which way its text reads: what a\n * toolbar measures along, and what its arrow keys move along.\n */\n\n/** A row of controls side by side, or one under another (`aria-orientation`'s values). */\nexport type Orientation = 'horizontal' | 'vertical';\n\n/** The writing direction an element lays out in: its computed CSS `direction`. */\nexport function directionOf(element: Element): 'ltr' | 'rtl' {\n const view = element.ownerDocument.defaultView;\n return view?.getComputedStyle(element).direction === 'rtl' ? 'rtl' : 'ltr';\n}\n","/**\n * One tab stop for a row of controls, and arrow keys along it (the ARIA\n * roving tabindex, as toolbars and tab lists use it): Tab enters the row at\n * the control that had focus last, else its first, and the arrows, Home and\n * End move between its controls. The arrows follow the row's orientation, Left and Right swap in\n * right-to-left text, a control that can't be used now is passed over, and a\n * control that uses an arrow key itself (a text field, a select) keeps it.\n */\nimport { isUnavailable, usesKey } from './focusable';\nimport type { Orientation } from './orientation';\nimport { directionOf } from './orientation';\n\n/** Where a key moves focus in a row. */\nexport type RovingMove = 'next' | 'previous' | 'first' | 'last';\n\n/** The move `key` asks for in a row running `orientation`, or null for a key the row leaves alone. */\nexport function rovingMoveOf(\n key: string,\n orientation: Orientation,\n direction: 'ltr' | 'rtl',\n): RovingMove | null {\n if (key === 'Home') return 'first';\n if (key === 'End') return 'last';\n if (orientation === 'vertical') {\n if (key === 'ArrowDown') return 'next';\n if (key === 'ArrowUp') return 'previous';\n return null;\n }\n const forward = direction === 'rtl' ? 'ArrowLeft' : 'ArrowRight';\n const backward = direction === 'rtl' ? 'ArrowRight' : 'ArrowLeft';\n if (key === forward) return 'next';\n if (key === backward) return 'previous';\n return null;\n}\n\n/**\n * The index `move` lands on among `count` stops, from `current` (-1 for\n * none), passing over the stops `available` rejects. With `loop`, next and\n * previous wrap around the ends; without it they stop there. -1 when there is\n * nowhere to go.\n */\nexport function rovingTargetOf(\n move: RovingMove,\n current: number,\n count: number,\n available: (index: number) => boolean,\n loop: boolean,\n): number {\n if (move === 'first' || move === 'last') {\n const step = move === 'first' ? 1 : -1;\n for (let i = move === 'first' ? 0 : count - 1; i >= 0 && i < count; i += step) {\n if (available(i)) return i;\n }\n return -1;\n }\n const step = move === 'next' ? 1 : -1;\n for (let taken = 1; taken <= count; taken += 1) {\n let index = current + step * taken;\n if (index < 0 || index >= count) {\n if (!loop) return -1;\n index = ((index % count) + count) % count;\n }\n if (index === current) return -1;\n if (available(index)) return index;\n }\n return -1;\n}\n\nexport interface RovingFocusOptions {\n /** Which arrows move: Left and Right for `'horizontal'`, Up and Down for `'vertical'`. */\n readonly orientation: Orientation;\n /** The stops, in order; read again on every key and every {@link RovingFocus.refresh}. */\n readonly stops: () => readonly HTMLElement[];\n /** The writing direction. Default: the container's computed `direction`, read on each key. */\n readonly direction?: () => 'ltr' | 'rtl';\n /** Whether next and previous wrap around at the ends. Default true. */\n readonly loop?: boolean;\n}\n\n/** A row's roving focus, from {@link attachRovingFocus}. */\nexport interface RovingFocus {\n /**\n * Put the one tab stop back after the stops changed: on the stop that had\n * focus last while it's still there and usable, else on the first usable\n * one. Call it after every render that can add, remove or disable a stop.\n */\n refresh(): void;\n /** Stop handling keys and focus. The stops keep their `tabIndex`. */\n detach(): void;\n}\n\n/**\n * Make the stops of `container` one tab stop with arrow keys between them.\n * Keys count only while a stop itself has focus, so keys inside a menu or\n * another control the row holds are theirs; a key with Alt, Control, Meta or\n * Shift, or one a handler already took (`defaultPrevented`), is left alone.\n */\nexport function attachRovingFocus(\n container: HTMLElement,\n options: RovingFocusOptions,\n): RovingFocus {\n const direction = options.direction ?? (() => directionOf(container));\n const loop = options.loop ?? true;\n /**\n * The stop that had focus last. Until one has, the tab stop follows the\n * first usable stop, wherever the row's first render put it.\n */\n let used: HTMLElement | null = null;\n\n const giveTabStop = (stops: readonly HTMLElement[]) => {\n const keep = used !== null && stops.includes(used) && !isUnavailable(used);\n const tabStop = keep ? used : (stops.find((stop) => !isUnavailable(stop)) ?? null);\n for (const stop of stops) {\n const tabIndex = stop === tabStop ? 0 : -1;\n if (stop.tabIndex !== tabIndex) stop.tabIndex = tabIndex;\n }\n };\n\n const refresh = () => giveTabStop(options.stops());\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey)\n return;\n const stops = options.stops();\n const target = event.target as HTMLElement;\n const from = stops.indexOf(target);\n if (from < 0 || usesKey(target, event.key)) return;\n const move = rovingMoveOf(event.key, options.orientation, direction());\n if (!move) return;\n const to = rovingTargetOf(\n move,\n from,\n stops.length,\n (index) => !isUnavailable(stops[index]!),\n loop,\n );\n if (to < 0) return;\n event.preventDefault();\n const next = stops[to]!;\n used = next;\n giveTabStop(stops);\n next.focus();\n };\n\n // Focus that reaches a stop any other way (a click, Tab, a script) makes it the row's tab stop.\n const onFocusIn = (event: FocusEvent) => {\n const stops = options.stops();\n const target = event.target as HTMLElement;\n if (!stops.includes(target)) return;\n used = target;\n giveTabStop(stops);\n };\n\n container.addEventListener('keydown', onKeyDown);\n container.addEventListener('focusin', onFocusIn);\n refresh();\n return {\n refresh,\n detach() {\n container.removeEventListener('keydown', onKeyDown);\n container.removeEventListener('focusin', onFocusIn);\n },\n };\n}\n","/**\n * A toolbar's keyboard: one tab stop, and arrow keys along the bar\n * ({@link attachRovingFocus}). Each item of the live row (a command, your own\n * item, a folded group, the \"More\" button) sits in an element marked with\n * {@link TOOLBAR_ITEM_ATTRIBUTE}, and the first element in it that takes focus\n * is the item's stop: the rest of a custom item's controls are its own.\n */\nimport { focusablesIn } from './focusable';\nimport type { Orientation } from './orientation';\nimport type { RovingFocus } from './roving-focus';\nimport { attachRovingFocus } from './roving-focus';\n\n/** The attribute on the element around each item of a toolbar's live row. */\nexport const TOOLBAR_ITEM_ATTRIBUTE = 'data-epdf-toolbar-item';\n\n/**\n * A toolbar's stops: the first element of each item that takes focus, in\n * order. A menu an item holds open is not part of its stop.\n */\nexport function toolbarStopsOf(toolbar: ParentNode): HTMLElement[] {\n const stops: HTMLElement[] = [];\n for (const item of toolbar.querySelectorAll(`[${TOOLBAR_ITEM_ATTRIBUTE}]`)) {\n const stop = focusablesIn(item).find((element) => {\n const menu = element.closest('[role=\"menu\"]');\n return menu === null || !item.contains(menu);\n });\n if (stop) stops.push(stop);\n }\n return stops;\n}\n\n/** Arrow keys between a toolbar's items, along its orientation; `refresh()` after each render. */\nexport function attachToolbarFocus(toolbar: HTMLElement, orientation: Orientation): RovingFocus {\n return attachRovingFocus(toolbar, { orientation, stops: () => toolbarStopsOf(toolbar) });\n}\n","/**\n * The keyboard of an open menu (the ARIA menu button pattern). The menu\n * takes focus on its first item; Up, Down, Home and End move between items,\n * passing over the ones that can't be used now; typing jumps to the next item\n * that starts with what was typed; Enter and Space pick; Escape closes the\n * menu and gives focus back to its button, and Tab closes it and moves on\n * from that button. While the menu is open, its button says so with\n * `aria-expanded`.\n */\nimport { deepActiveElementOf, isUnavailable } from './focusable';\nimport { rovingMoveOf, rovingTargetOf } from './roving-focus';\n\n/** How long typed letters keep adding to one search, in milliseconds. */\nconst TYPEAHEAD_RESET_MS = 500;\n\n/** The menu's items when no `items` is given: its menu-item roles, in order. */\nconst MENU_ITEM_SELECTOR = '[role=\"menuitem\"], [role=\"menuitemradio\"], [role=\"menuitemcheckbox\"]';\n\n/**\n * The item typeahead lands on: the first one from `current` on, wrapping, that\n * is available and whose label starts with `query` (any case). A one-letter\n * query starts after `current`, so typing the same letter again moves on, and\n * a query of one letter repeated (\"ppp\") cycles like that letter. -1 when no\n * item matches.\n */\nexport function typeaheadMatchOf(\n labels: readonly string[],\n current: number,\n query: string,\n available: (index: number) => boolean,\n): number {\n const typed = query.toLocaleLowerCase();\n const repeated = typed.length > 1 && [...typed].every((letter) => letter === typed[0]);\n const search = repeated ? typed[0]! : typed;\n const offset = search.length === 1 ? 1 : 0;\n const count = labels.length;\n for (let taken = 0; taken < count; taken += 1) {\n const index = (((current + offset + taken) % count) + count) % count;\n if (available(index) && labels[index]!.toLocaleLowerCase().startsWith(search)) return index;\n }\n return -1;\n}\n\n/**\n * The words typeahead matches an item by: its `aria-label`, else its text,\n * without the marks a row may start with (a bullet, a check, an icon glyph).\n */\nexport function menuItemLabelOf(item: Element): string {\n const label = item.getAttribute('aria-label') ?? item.textContent ?? '';\n return label.trim().replace(/^[^\\p{L}\\p{N}]+/u, '');\n}\n\nexport interface MenuKeysOptions {\n /** The button that opened the menu: it says it's expanded, and gets focus back. */\n readonly trigger: HTMLElement | null;\n /** Close the menu; removing it is the caller's. */\n readonly close: () => void;\n /** The items, in order. Default: the menu's `menuitem`, `menuitemradio` and `menuitemcheckbox` elements. */\n readonly items?: () => readonly HTMLElement[];\n /** Which item takes focus when the menu opens. Default `'first'`. */\n readonly initialFocus?: 'first' | 'last' | 'none';\n}\n\n/**\n * Drive an open `menu` from the keyboard until the returned detach runs.\n * Detaching (the menu closed) gives focus back to the trigger when focus was\n * in the menu or went nowhere, and never when the user moved it elsewhere.\n */\nexport function attachMenuKeys(menu: HTMLElement, options: MenuKeysOptions): () => void {\n const { trigger, close } = options;\n const items =\n options.items ?? (() => [...menu.querySelectorAll<HTMLElement>(MENU_ITEM_SELECTOR)]);\n let typed = '';\n let typedAt = Number.NEGATIVE_INFINITY;\n\n // Items take focus from the arrows only, so Tab leaves the menu instead of walking through it.\n const listed = () => {\n const list = items();\n for (const item of list) if (item.tabIndex !== -1) item.tabIndex = -1;\n return list;\n };\n const available = (list: readonly HTMLElement[]) => (index: number) =>\n !isUnavailable(list[index]!);\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey) return;\n const list = listed();\n const from = list.indexOf(event.target as HTMLElement);\n\n if (event.key === 'Escape') {\n event.preventDefault();\n // The menu closes, not the dialog or panel it opened over.\n event.stopPropagation();\n close();\n trigger?.focus();\n return;\n }\n if (event.key === 'Tab') {\n // Focus goes back to the button first, so Tab moves on from there.\n close();\n trigger?.focus();\n return;\n }\n if ((event.key === 'Enter' || (event.key === ' ' && typed === '')) && from >= 0) {\n event.preventDefault();\n if (!isUnavailable(list[from]!)) list[from]!.click();\n return;\n }\n const move = event.shiftKey ? null : rovingMoveOf(event.key, 'vertical', 'ltr');\n if (move) {\n event.preventDefault();\n const to = rovingTargetOf(move, from, list.length, available(list), true);\n if (to >= 0) list[to]!.focus();\n return;\n }\n if (event.key.length === 1) {\n if (event.timeStamp - typedAt > TYPEAHEAD_RESET_MS) typed = '';\n typed += event.key;\n typedAt = event.timeStamp;\n const labels = list.map(menuItemLabelOf);\n const to = typeaheadMatchOf(labels, from, typed, available(list));\n if (to >= 0) {\n event.preventDefault();\n list[to]!.focus();\n }\n }\n };\n\n // A native button would click again on the Space key's release.\n const onKeyUp = (event: KeyboardEvent) => {\n if (event.key === ' ' && !event.defaultPrevented) event.preventDefault();\n };\n\n menu.addEventListener('keydown', onKeyDown);\n menu.addEventListener('keyup', onKeyUp);\n trigger?.setAttribute('aria-expanded', 'true');\n\n const initial = options.initialFocus ?? 'first';\n if (initial !== 'none') {\n const list = listed();\n const to = rovingTargetOf(initial, -1, list.length, available(list), false);\n if (to >= 0) list[to]!.focus();\n }\n\n return () => {\n menu.removeEventListener('keydown', onKeyDown);\n menu.removeEventListener('keyup', onKeyUp);\n trigger?.setAttribute('aria-expanded', 'false');\n const active = deepActiveElementOf(menu.ownerDocument);\n if (trigger && (active === null || menu.contains(active))) trigger.focus();\n };\n}\n","/**\n * Keep keyboard focus inside a dialog or sheet while it's open. It takes\n * focus when the trap starts, unless focus is inside already (the control you\n * name, else its first one that Tab stops at, else the container itself); Tab\n * and Shift+Tab go round its controls without leaving it; focus that lands\n * outside comes back; and when the trap is released, focus returns to where it\n * was before. Traps nest: only the one started last acts, until it's released.\n */\nimport { containsFocus, deepActiveElementOf, tabbablesIn } from './focusable';\n\n/**\n * Move focus into `container` unless it's there already: to `initialFocus`, else its first\n * control Tab stops at, else the container itself. Focus already inside stays where it is, so a\n * control that took focus as it appeared (a search field asking for the cursor) keeps it.\n */\nexport function moveFocusInto(container: HTMLElement, initialFocus?: HTMLElement | null): void {\n if (containsFocus(container)) return;\n if (initialFocus) {\n initialFocus.focus();\n return;\n }\n const first = tabbablesIn(container)[0];\n if (first) {\n first.focus();\n return;\n }\n if (!container.hasAttribute('tabindex')) container.tabIndex = -1;\n container.focus();\n}\n\nexport interface FocusTrapOptions {\n /**\n * What takes focus when the trap starts, unless focus is inside already. Default: the first\n * control Tab stops at.\n */\n readonly initialFocus?: HTMLElement | null;\n /** Whether focus goes back to where it was when the trap is released. Default true. */\n readonly returnFocus?: boolean;\n}\n\n/** The traps holding focus now, the innermost last. */\nconst activeTraps: HTMLElement[] = [];\n\n/** Trap focus in `container` until the returned release runs. */\nexport function attachFocusTrap(\n container: HTMLElement,\n options: FocusTrapOptions = {},\n): () => void {\n const doc = container.ownerDocument;\n const before = deepActiveElementOf(doc) as HTMLElement | null;\n const isInnermost = () => activeTraps[activeTraps.length - 1] === container;\n /** The control that had focus last inside the trap, to go back to. */\n let last: HTMLElement | null = null;\n\n const focusInside = (edge: 'first' | 'last') => {\n const tabbables = tabbablesIn(container);\n const target = edge === 'first' ? tabbables[0] : tabbables[tabbables.length - 1];\n if (target) {\n target.focus();\n return;\n }\n // Nothing to tab to: the container holds focus itself.\n if (!container.hasAttribute('tabindex')) container.tabIndex = -1;\n container.focus();\n };\n\n const onKeyDown = (event: KeyboardEvent) => {\n if (event.key !== 'Tab' || event.defaultPrevented || !isInnermost()) return;\n const tabbables = tabbablesIn(container);\n const active = deepActiveElementOf(doc);\n const first = tabbables[0];\n const lastTabbable = tabbables[tabbables.length - 1];\n const atStart = active === container || active === first || !first;\n const atEnd = active === lastTabbable || !lastTabbable;\n if (event.shiftKey ? atStart : atEnd) {\n event.preventDefault();\n focusInside(event.shiftKey ? 'last' : 'first');\n }\n };\n\n // Where focus lands is read from the composed path: at the document, focus inside a shadow\n // root targets its host.\n const onFocusIn = (event: FocusEvent) => {\n if (!isInnermost()) return;\n const path = event.composedPath();\n if (path.includes(container)) {\n last = path[0] as HTMLElement;\n return;\n }\n if (last?.isConnected && container.contains(last)) last.focus();\n else focusInside('first');\n };\n\n activeTraps.push(container);\n container.addEventListener('keydown', onKeyDown);\n doc.addEventListener('focusin', onFocusIn, true);\n moveFocusInto(container, options.initialFocus);\n // Focus that was inside already took no `focusin` here: it is what focus comes back to.\n if (last === null && containsFocus(container)) last = deepActiveElementOf(doc) as HTMLElement;\n\n return () => {\n const index = activeTraps.lastIndexOf(container);\n if (index >= 0) activeTraps.splice(index, 1);\n container.removeEventListener('keydown', onKeyDown);\n doc.removeEventListener('focusin', onFocusIn, true);\n if (options.returnFocus !== false && before?.isConnected) before.focus();\n };\n}\n","/**\n * Putting the cursor in a panel when it asks. The shell raises a surface's `focusRequest` on\n * every `open(id, { focus: true })` and clears it when the surface closes; the element that\n * follows it takes focus whenever the request changes to a new value above 0, and when it starts\n * following while the request is up (the panel opened, or its code loaded, because of it). The\n * adapters' surface-focus hooks bind a follower to their reactivity; the rule lives here.\n */\n\n/** How an element takes focus when its surface asks. */\nexport interface SurfaceFocusOptions {\n /** Select the text of an input or a text area as it takes focus, so typing replaces it. */\n readonly select?: boolean;\n}\n\n/** One element following one surface's focus requests. */\nexport interface SurfaceFocusFollower {\n /**\n * Take the surface's request as it is now. `shell` is the capability it was read from (null\n * without a document): another document's shell starts over, as a new mount does. Without an\n * element yet, a request waits for it.\n */\n follow(\n element: HTMLElement | null,\n shell: object | null,\n request: number,\n options?: SurfaceFocusOptions,\n ): void;\n}\n\n/** Whether an element has text that `select()` selects. */\nconst hasSelectableText = (element: HTMLElement): element is HTMLInputElement =>\n element.tagName === 'INPUT' || element.tagName === 'TEXTAREA';\n\nexport function createSurfaceFocusFollower(): SurfaceFocusFollower {\n let current: object | null = null;\n // The request the element last acted on: 0 at the start and for each new document.\n let taken = 0;\n return {\n follow(element, shell, request, options = {}) {\n if (shell !== current) {\n current = shell;\n taken = 0;\n }\n if (request === 0) {\n taken = 0;\n return;\n }\n // The count only climbs, or clears when the surface closes, so any other value above 0\n // is a new request: also one read after a close and a reopen that came in together.\n if (request === taken || !element) return;\n taken = request;\n element.focus();\n if (options.select && hasSelectableText(element)) element.select();\n },\n };\n}\n","/**\n * The browser's part of the standard commands (`createStandardCommands` in\n * `@embedpdf/plugin-commands/standard`): copying the selected text, handing\n * the document to the user as a download, and the print dialog. Every\n * framework adapter builds its `standardCommands` from this one object.\n */\nimport { copySelection } from './clipboard';\nimport { saveFile } from './file-picker';\n\n/** What the standard commands need from the browser: the structural twin of `StandardCommandsPlatform`. */\nexport interface StandardCommandsBrowser {\n copySelection: typeof copySelection;\n saveFile(bytes: Uint8Array, fileName: string): void;\n print(): void;\n}\n\nexport const standardCommandsBrowser: StandardCommandsBrowser = {\n copySelection,\n saveFile: (bytes, fileName) => saveFile(bytes, fileName, 'application/pdf'),\n print: () => window.print(),\n};\n"],"mappings":"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"}
|