@embedpdf/react 3.0.0-next.15 → 3.0.0-next.16
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/{AnnotationList-CkijRHet-HEFNUIZe.d.ts → AnnotationList-DcyhF2kU-CiBFOq1z.d.cts} +5 -4
- package/dist/{AnnotationList-CkijRHet-HEFNUIZe.d.cts → AnnotationList-DcyhF2kU-CiBFOq1z.d.ts} +5 -4
- package/dist/actions.cjs +2 -2
- package/dist/actions.js +2 -2
- package/dist/{annotation-C9dVwo9b.d.cts → annotation-3UvABfJm.d.cts} +5 -2
- package/dist/{annotation-Dex5EBda.cjs → annotation-BNHQpMKv.cjs} +11 -35
- package/dist/annotation-BNHQpMKv.cjs.map +1 -0
- package/dist/{annotation-C1ttM8Wj.js → annotation-CDI8CFb3.js} +12 -36
- package/dist/annotation-CDI8CFb3.js.map +1 -0
- package/dist/{annotation-B48UT2ln.d.ts → annotation-DhEfs0pH.d.ts} +5 -2
- package/dist/annotation.cjs +1 -1
- package/dist/annotation.d.cts +1 -1
- package/dist/annotation.d.ts +1 -1
- package/dist/annotation.js +1 -1
- package/dist/attachment.cjs +39 -0
- package/dist/attachment.cjs.map +1 -0
- package/dist/attachment.d.cts +29 -0
- package/dist/attachment.d.ts +29 -0
- package/dist/attachment.js +28 -0
- package/dist/attachment.js.map +1 -0
- package/dist/commands.cjs +2 -2
- package/dist/commands.js +2 -2
- package/dist/{form-55izw0jS.js → form-BSGvjA5D.js} +3 -3
- package/dist/{form-55izw0jS.js.map → form-BSGvjA5D.js.map} +1 -1
- package/dist/{form-BNqQfGGH.cjs → form-BgztZyOK.cjs} +3 -3
- package/dist/{form-BNqQfGGH.cjs.map → form-BgztZyOK.cjs.map} +1 -1
- package/dist/form.cjs +1 -1
- package/dist/form.d.cts +1 -1
- package/dist/form.d.ts +1 -1
- package/dist/form.js +1 -1
- package/dist/history.cjs +2 -2
- package/dist/history.js +2 -2
- package/dist/i18n.cjs +2 -2
- package/dist/i18n.js +2 -2
- package/dist/index.cjs +32 -5
- package/dist/index.d.cts +7 -3
- package/dist/index.d.ts +7 -3
- package/dist/index.js +11 -7
- package/dist/interaction.cjs +2 -2
- package/dist/interaction.js +2 -2
- package/dist/link.cjs +1 -1
- package/dist/link.js +1 -1
- package/dist/measurement.cjs +2 -2
- package/dist/measurement.js +2 -2
- package/dist/metadata.cjs +2 -2
- package/dist/metadata.js +2 -2
- package/dist/outline.cjs +39 -0
- package/dist/outline.cjs.map +1 -0
- package/dist/outline.d.cts +31 -0
- package/dist/outline.d.ts +31 -0
- package/dist/outline.js +28 -0
- package/dist/outline.js.map +1 -0
- package/dist/page-edit.cjs +9 -1
- package/dist/page-edit.cjs.map +1 -1
- package/dist/page-edit.d.cts +12 -1
- package/dist/page-edit.d.ts +12 -1
- package/dist/page-edit.js +10 -3
- package/dist/page-edit.js.map +1 -1
- package/dist/page-view.cjs +1 -1
- package/dist/page-view.js +1 -1
- package/dist/redaction.cjs +2 -2
- package/dist/redaction.d.cts +1 -1
- package/dist/redaction.d.ts +1 -1
- package/dist/redaction.js +2 -2
- package/dist/render.cjs +2 -2
- package/dist/render.js +2 -2
- package/dist/{runtime-fzpGv78v.cjs → runtime-BfnpyfY0.cjs} +3 -2
- package/dist/runtime-BfnpyfY0.cjs.map +1 -0
- package/dist/{runtime-B6xcPsEb.js → runtime-tD5iJU83.js} +4 -3
- package/dist/runtime-tD5iJU83.js.map +1 -0
- package/dist/runtime.cjs +1 -1
- package/dist/runtime.js +1 -1
- package/dist/search.cjs +2 -2
- package/dist/search.d.cts +1 -1
- package/dist/search.d.ts +1 -1
- package/dist/search.js +2 -2
- package/dist/selection.cjs +2 -2
- package/dist/selection.js +2 -2
- package/dist/shell.cjs +2 -2
- package/dist/shell.js +2 -2
- package/dist/signature.cjs +2 -2
- package/dist/signature.d.cts +1 -1
- package/dist/signature.d.ts +1 -1
- package/dist/signature.js +2 -2
- package/dist/{stage-BIG6aeVd.cjs → stage-B7BG2e8Q.cjs} +2 -2
- package/dist/{stage-BIG6aeVd.cjs.map → stage-B7BG2e8Q.cjs.map} +1 -1
- package/dist/{stage-DoVekb83.js → stage-BqfBjDu6.js} +2 -2
- package/dist/{stage-DoVekb83.js.map → stage-BqfBjDu6.js.map} +1 -1
- package/dist/stage.cjs +1 -1
- package/dist/stage.js +1 -1
- package/dist/stamp.cjs +2 -2
- package/dist/stamp.js +2 -2
- package/dist/{state-CbgxhwLA.js → state-CzI2KlZb.js} +2 -2
- package/dist/{state-CbgxhwLA.js.map → state-CzI2KlZb.js.map} +1 -1
- package/dist/{state-CsfZy_Eq.cjs → state-Dtf8HAue.cjs} +2 -2
- package/dist/{state-CsfZy_Eq.cjs.map → state-Dtf8HAue.cjs.map} +1 -1
- package/dist/toolbar.cjs +1 -1
- package/dist/toolbar.js +1 -1
- package/dist/view-manager.cjs +2 -2
- package/dist/view-manager.js +2 -2
- package/package.json +40 -30
- package/dist/annotation-C1ttM8Wj.js.map +0 -1
- package/dist/annotation-Dex5EBda.cjs.map +0 -1
- package/dist/runtime-B6xcPsEb.js.map +0 -1
- package/dist/runtime-fzpGv78v.cjs.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@embedpdf/react",
|
|
3
|
-
"version": "3.0.0-next.
|
|
3
|
+
"version": "3.0.0-next.16",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "React adapter for EmbedPDF v3: one generic reactive binding + structural components (Viewer, Stage, PageView) + headless layers & hooks. The whole framework surface.",
|
|
6
6
|
"type": "module",
|
|
@@ -25,6 +25,10 @@
|
|
|
25
25
|
"import": "./dist/annotation.js",
|
|
26
26
|
"require": "./dist/annotation.cjs"
|
|
27
27
|
},
|
|
28
|
+
"./attachment": {
|
|
29
|
+
"import": "./dist/attachment.js",
|
|
30
|
+
"require": "./dist/attachment.cjs"
|
|
31
|
+
},
|
|
28
32
|
"./commands": {
|
|
29
33
|
"import": "./dist/commands.js",
|
|
30
34
|
"require": "./dist/commands.cjs"
|
|
@@ -57,6 +61,10 @@
|
|
|
57
61
|
"import": "./dist/metadata.js",
|
|
58
62
|
"require": "./dist/metadata.cjs"
|
|
59
63
|
},
|
|
64
|
+
"./outline": {
|
|
65
|
+
"import": "./dist/outline.js",
|
|
66
|
+
"require": "./dist/outline.cjs"
|
|
67
|
+
},
|
|
60
68
|
"./page-edit": {
|
|
61
69
|
"import": "./dist/page-edit.js",
|
|
62
70
|
"require": "./dist/page-edit.cjs"
|
|
@@ -115,33 +123,35 @@
|
|
|
115
123
|
"dist"
|
|
116
124
|
],
|
|
117
125
|
"dependencies": {
|
|
118
|
-
"@embedpdf/core-
|
|
119
|
-
"@embedpdf/core": "3.0.0-next.
|
|
120
|
-
"@embedpdf/plugin-annotation": "3.0.0-next.
|
|
121
|
-
"@embedpdf/plugin-
|
|
122
|
-
"@embedpdf/plugin-
|
|
123
|
-
"@embedpdf/
|
|
124
|
-
"@embedpdf/plugin-
|
|
125
|
-
"@embedpdf/plugin-
|
|
126
|
-
"@embedpdf/plugin-metadata": "3.0.0-next.
|
|
127
|
-
"@embedpdf/
|
|
128
|
-
"@embedpdf/plugin-
|
|
129
|
-
"@embedpdf/plugin-
|
|
130
|
-
"@embedpdf/plugin-
|
|
131
|
-
"@embedpdf/plugin-
|
|
132
|
-
"@embedpdf/plugin-
|
|
133
|
-
"@embedpdf/plugin-
|
|
134
|
-
"@embedpdf/plugin-
|
|
135
|
-
"@embedpdf/plugin-
|
|
136
|
-
"@embedpdf/plugin-
|
|
137
|
-
"@embedpdf/
|
|
138
|
-
"@embedpdf/plugin-
|
|
139
|
-
"@embedpdf/
|
|
140
|
-
"@embedpdf/core-
|
|
141
|
-
"@embedpdf/
|
|
142
|
-
"@embedpdf/
|
|
143
|
-
"@embedpdf/
|
|
144
|
-
"@embedpdf/
|
|
126
|
+
"@embedpdf/core-geometry": "3.0.0-next.16",
|
|
127
|
+
"@embedpdf/core-annotation": "3.0.0-next.16",
|
|
128
|
+
"@embedpdf/plugin-annotation": "3.0.0-next.16",
|
|
129
|
+
"@embedpdf/plugin-attachment": "3.0.0-next.16",
|
|
130
|
+
"@embedpdf/plugin-commands": "3.0.0-next.16",
|
|
131
|
+
"@embedpdf/core": "3.0.0-next.16",
|
|
132
|
+
"@embedpdf/plugin-form": "3.0.0-next.16",
|
|
133
|
+
"@embedpdf/plugin-i18n": "3.0.0-next.16",
|
|
134
|
+
"@embedpdf/plugin-metadata": "3.0.0-next.16",
|
|
135
|
+
"@embedpdf/plugin-outline": "3.0.0-next.16",
|
|
136
|
+
"@embedpdf/plugin-history": "3.0.0-next.16",
|
|
137
|
+
"@embedpdf/plugin-link": "3.0.0-next.16",
|
|
138
|
+
"@embedpdf/plugin-page-edit": "3.0.0-next.16",
|
|
139
|
+
"@embedpdf/plugin-redaction": "3.0.0-next.16",
|
|
140
|
+
"@embedpdf/plugin-render": "3.0.0-next.16",
|
|
141
|
+
"@embedpdf/plugin-search": "3.0.0-next.16",
|
|
142
|
+
"@embedpdf/plugin-interaction": "3.0.0-next.16",
|
|
143
|
+
"@embedpdf/plugin-selection": "3.0.0-next.16",
|
|
144
|
+
"@embedpdf/plugin-shell": "3.0.0-next.16",
|
|
145
|
+
"@embedpdf/plugin-stage": "3.0.0-next.16",
|
|
146
|
+
"@embedpdf/plugin-signature": "3.0.0-next.16",
|
|
147
|
+
"@embedpdf/plugin-stamp": "3.0.0-next.16",
|
|
148
|
+
"@embedpdf/core-signature": "3.0.0-next.16",
|
|
149
|
+
"@embedpdf/core-stage": "3.0.0-next.16",
|
|
150
|
+
"@embedpdf/core-ui": "3.0.0-next.16",
|
|
151
|
+
"@embedpdf/web": "3.0.0-next.16",
|
|
152
|
+
"@embedpdf/plugin-view-manager": "3.0.0-next.16",
|
|
153
|
+
"@embedpdf/plugin-measurement": "3.0.0-next.16",
|
|
154
|
+
"@embedpdf/plugin-actions": "3.0.0-next.16"
|
|
145
155
|
},
|
|
146
156
|
"peerDependencies": {
|
|
147
157
|
"react": ">=18",
|
|
@@ -156,8 +166,8 @@
|
|
|
156
166
|
"typescript": "^5.0.0",
|
|
157
167
|
"vitest": "^2.1.9",
|
|
158
168
|
"vite": "^6.3.5",
|
|
159
|
-
"@embedpdf/
|
|
160
|
-
"@embedpdf/
|
|
169
|
+
"@embedpdf/tooling-build": "0.0.0",
|
|
170
|
+
"@embedpdf/engine": "3.0.0-next.16"
|
|
161
171
|
},
|
|
162
172
|
"license": "Apache-2.0",
|
|
163
173
|
"repository": {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"annotation-C1ttM8Wj.js","names":["AnnotationToken","AnnotationHostToken","AnnotationToken","React","AnnotationHostToken"],"sources":["../src/annotation-hooks.ts","../src/annotation-menu.tsx","../src/annotation.tsx"],"sourcesContent":["/**\n * The annotation hooks other feature entries use too (the form plugin reads\n * the selection), kept apart so they don't import the whole annotation\n * feature: the state and the settings, each from its declaration.\n */\nimport { AnnotationToken, annotationState } from '@embedpdf/plugin-annotation/contract';\n\nimport { settingsHook, stateHook } from './state';\n\n/**\n * The annotation state: `status` (`'loading'` until every annotation is in,\n * then `'ready'`), the `selected` annotations, the one `hovered` under the\n * pointer and the text box being typed in (`editing`). Re-renders only when a\n * field changes, or the value `select` picks. Empty without a document.\n */\nexport const useAnnotationState = stateHook(annotationState);\n\n/**\n * The annotation settings (`snap`, `chrome`, `afterCreate`, `tools`), or the\n * value `select` picks. They belong to the plugin, so they read without a\n * document; change them with `useAnnotation().updateSettings()`.\n */\nexport const useAnnotationSettings = settingsHook(AnnotationToken);\n","/**\n * Annotation menus: thin anchor plumbing over the one `<Anchored>` primitive.\n * They come from `/annotation`; this module stays apart so it doesn't pull\n * in the layer.\n *\n * These work identically under `<Stage>` (mount in the overlay slot; the\n * camera projects, no DOM reads) and `<PageView>` (measured and portalled):\n * the surface provides the projector, the menu doesn't care.\n *\n * No action bags: actions come from `useAnnotation()` and the state hooks,\n * which subscribe properly and compose across plugins. Render props carry\n * only the anchor's data (the draft menu's progress); the selection menu\n * takes plain children.\n */\nimport * as React from 'react';\nimport { AnnotationToken } from '@embedpdf/plugin-annotation/contract';\nimport { AnnotationToken as AnnotationHostToken } from '@embedpdf/plugin-annotation/contract/host';\nimport type { CreationDraftAnchor, RotationAnchor } from '@embedpdf/core-annotation';\nimport {\n paint,\n paintDefault,\n sameCreationDraftAnchor,\n sameRotationAnchor,\n sameSelectionAnchor,\n} from '@embedpdf/web';\nimport { Anchored, useProjectorBinding, type AnchoredPlacement } from './anchored';\nimport { useAnnotationSettings } from './annotation-hooks';\nimport { useOptionalSelector } from './runtime';\n\nexport interface AnnotationMenuProps {\n children: React.ReactNode;\n /** Gap in screen px between the selection box and the menu (default 15). */\n gap?: number;\n /** Where to place the menu relative to the selection box. Default 'top'. */\n placement?: AnchoredPlacement;\n}\n\n/**\n * Your own menu next to the selection (one anchor, also for a selection over\n * several pages), clear of the rotation handle. It hides while the selection\n * is dragged, resized or turned. What's in it is yours: build it from\n * `useAnnotation()` and `useAnnotationState()`.\n */\nexport function AnnotationMenu({ children, gap = 15, placement = 'top' }: AnnotationMenuProps) {\n // Reading the binding subscribes this component to projection changes\n // (its identity is the revision), so the anchor read below re-runs with\n // fresh view facts in the same commit as the surface: the rotation\n // handle's offset is screen-constant, so its page-space position depends\n // on the page's live view scale.\n const { projector } = useProjectorBinding();\n const anchor = useOptionalSelector(\n AnnotationHostToken,\n (annotation) => {\n const selectionAnchor = annotation.selection.getAnchor();\n if (!selectionAnchor) return null;\n const env = projector.viewEnv(selectionAnchor.page);\n return env ? annotation.getSelectionAnchorIn(env) : selectionAnchor;\n },\n null,\n sameSelectionAnchor,\n );\n if (!anchor) return null;\n return (\n <Anchored\n anchor={{\n page: anchor.page,\n bounds: anchor.bounds,\n ...(anchor.rotationHandle ? { avoid: [anchor.rotationHandle] } : {}),\n }}\n placement={placement}\n gap={gap}\n >\n {children}\n </Anchored>\n );\n}\n\nexport interface AnnotationDraftMenuProps {\n /** Render prop receiving the shape being drawn (`subtype`, `pointCount`,\n * `minPoints`, `canFinish`, …). The verbs are capability calls:\n * `useAnnotation().draft.finish()` and `.draft.cancel()`. */\n children: (anchor: CreationDraftAnchor) => React.ReactNode;\n /** Gap in screen px between the draft anchor and the menu (default 8). */\n gap?: number;\n /** Where to place the menu relative to the draft anchor. Default 'top'. */\n placement?: AnchoredPlacement;\n}\n\n/** Floats over a live multi-click creation draft (polygon, polyline, …). */\nexport function AnnotationDraftMenu({\n children,\n gap = 8,\n placement = 'top',\n}: AnnotationDraftMenuProps) {\n const anchor = useOptionalSelector(\n AnnotationToken,\n (annotation) => annotation.draft.get(),\n null,\n sameCreationDraftAnchor,\n );\n if (!anchor) return null;\n return (\n <Anchored anchor={anchor} placement={placement} gap={gap}>\n {children(anchor)}\n </Anchored>\n );\n}\n\nexport interface AnnotationRotationBadgeProps {\n /** Render prop receiving the rotation in progress (`angle`, degrees\n * clockwise, as the commit will apply it). Without it, the badge shows the\n * angle in the `chrome.readout` colors, when `chrome.readout.enabled`. */\n children?: (rotation: RotationAnchor) => React.ReactNode;\n /** Gap in screen px between the pointer and the badge (default 16). */\n gap?: number;\n /** Where to place the badge relative to the pointer. Default 'right'. */\n placement?: AnchoredPlacement;\n}\n\n/**\n * Follows the pointer while a selection is being turned, upright however the\n * page is shown: it floats over the page like the menus, not in it. Shows\n * nothing when no rotation is in progress.\n */\nexport function AnnotationRotationBadge({\n children,\n gap = 16,\n placement = 'right',\n}: AnnotationRotationBadgeProps) {\n const rotation = useOptionalSelector(\n AnnotationToken,\n (annotation) => annotation.selection.getRotationAnchor(),\n null,\n sameRotationAnchor,\n );\n const readout = useAnnotationSettings((settings) => settings.chrome.readout);\n if (!rotation || (!children && !readout.enabled)) return null;\n return (\n <Anchored\n anchor={{ page: rotation.page, bounds: { ...rotation.at, width: 0, height: 0 } }}\n placement={placement}\n gap={gap}\n >\n {children ? (\n children(rotation)\n ) : (\n <div\n style={{\n pointerEvents: 'none',\n whiteSpace: 'nowrap',\n borderRadius: 4,\n padding: '2px 6px',\n fontFamily: paintDefault('font-mono'),\n fontSize: 12,\n background: paint('readout-background', readout.background),\n color: paint('readout-color', readout.color),\n }}\n >\n {rotation.angle}°\n </div>\n )}\n </Anchored>\n );\n}\n","/**\n * The React view of @embedpdf/plugin-annotation.\n *\n * Pure paint: it reads the per-page render items and chrome and draws them.\n * Pointer events arrive through the interaction hub (the Stage's forwarding),\n * and the cursor is driven by the hub too (the edit handler claims\n * move/pointer/resize on hover). Each annotation resolves to one native node:\n * a vector scene, the engine's baked /AP <img>, or a registered behavior; a\n * renderer may wrap or replace it. Every chrome color is a setting painted\n * through `paint()`, so its `--epdf-annotation-*` CSS variable wins.\n *\n * What isn't React lives in `@embedpdf/web`, the same for every framework:\n * the scene as SVG elements, the chrome's paint and pixels, the text box\n * editor, which drawing each annotation gets and the behaviors renderers\n * register, and the object URLs of baked appearances and the stamp ghost.\n *\n * The hooks follow every plugin's four: `useAnnotation()` (the API),\n * `useAnnotationState()` (status, selected, hovered, editing),\n * `useAnnotationEvent()` and `useAnnotationSettings()`; the reads a component\n * follows on their own are `useAnnotationList()`, `useAnnotationDefaults()`,\n * `useAnnotationProperties()` and `useAnnotationAnchor()`.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-annotation';\nimport type { EventHook, PageRef } from '@embedpdf/core';\nimport {\n hasLiveDrawing,\n scene,\n MITER_LIMIT,\n type FieldValues,\n type Point,\n type RenderItem,\n} from '@embedpdf/core-annotation';\nimport {\n AnnotationToken,\n annotationKey,\n type Annotation,\n type AnnotationAnchor,\n type AnnotationCapability,\n type AnnotationFilter,\n type AnnotationProperties,\n type AnnotationRef,\n type ChromeSettings,\n type CommentsApi,\n type CommentThread,\n type FilePickerProvider,\n type HandleRole,\n type TextItem,\n type ToolDefaults,\n} from '@embedpdf/plugin-annotation';\n// The render layer is framework code, so it resolves the full host lens\n// (page items, chrome, appearances…). Same runtime token as the public one,\n// only the type differs. App code never imports this.\nimport {\n AnnotationToken as AnnotationHostToken,\n previewBucket,\n} from '@embedpdf/plugin-annotation/contract/host';\nimport { InteractionToken } from '@embedpdf/plugin-interaction/contract';\nimport { RenderToken } from '@embedpdf/plugin-render/contract/host';\nimport {\n annotationChromePaint,\n annotationDrawingOf,\n bakedAppearanceOf,\n chromeInPixels,\n createShownUrls,\n createTextBoxEditorFollower,\n editingTextKeyOf,\n enrichCommentThreads,\n frameInPixels,\n ghostOpacity,\n installFilePickerProvider,\n layerTextBoxesOf,\n loadAppearanceUrls,\n loadObjectUrl,\n lookFrameOf,\n pickRequestedFile,\n pictureOrPainting,\n rasterInFrame,\n rectInPixels,\n registerRendererBehaviors,\n sameAnnotationAnchor,\n sceneViewBox,\n shownAppearance,\n svgShapesOf,\n textBoxEditorScaleOf,\n textBoxStyleOf,\n textPlateInPixels,\n type AnnotationChromePaint,\n type AppearanceUrl,\n type FrameFraction,\n} from '@embedpdf/web';\nimport { useEffect, useRef, useState } from 'react';\nimport * as React from 'react';\n\nexport type {\n CreationDraftAnchor,\n RenderItem,\n LineEnding,\n LineEndings,\n Style,\n AnnotationFlags,\n FieldValues,\n TextAlign,\n TextStyle,\n} from '@embedpdf/core-annotation';\nimport { devWarn } from './dev';\nimport { usePaintsPagePart } from './page-layers';\nimport { useAnnotationSettings } from './annotation-hooks';\nimport { INERT } from './inert';\nimport {\n shallowArray,\n useCapability,\n useCapabilityEvent,\n useDocumentId,\n useKernelValue,\n useOptionalCapability,\n useOptionalSelector,\n usePage,\n useSelector,\n useViewerSettings,\n} from './runtime';\nimport type { PageContextValue, PageLayout } from './runtime';\nimport { SvgShapes } from './svg-shapes';\n\nexport { useAnnotationSettings, useAnnotationState } from './annotation-hooks';\nexport {\n AnnotationDraftMenu,\n AnnotationMenu,\n AnnotationRotationBadge,\n type AnnotationDraftMenuProps,\n type AnnotationMenuProps,\n type AnnotationRotationBadgeProps,\n} from './annotation-menu';\n\n// ── renderers ────────────────────────────────────────────────────────────────\n\n/** The box a renderer draws into: the size to draw at, how it's turned, and how it's scaled. */\nexport interface AnnotationFrame {\n /** Its width in pixels at the annotation's 100% size, before its turn: draw at this. */\n width: number;\n /** Its height in pixels at the annotation's 100% size, before its turn: draw at this. */\n height: number;\n /**\n * How it's turned on screen, degrees clockwise: the page's turn and the\n * annotation's own. 0 is upright; turn your content by `-rotation` to keep\n * it upright.\n */\n rotation: number;\n /**\n * How much the layer scales what you draw, with the page: the zoom, or for\n * an annotation that keeps its size on screen, the zoom up to 1.\n */\n scale: number;\n}\n\n/**\n * What a renderer's component gets: the annotation, the frame to draw into,\n * and the layer's own drawing of it to keep or wrap. The layer keeps handling\n * the pointer (select, move, resize) unless the renderer is `interactive`.\n */\nexport interface AnnotationRendererProps {\n /** The annotation, as `get()` returns it. */\n annotation: Annotation;\n /**\n * The box you draw into. The layer places, turns and scales it like the\n * annotation's own drawing, also during a drag, a resize or a turn. Fill it\n * (`width: 100%; height: 100%`) and draw at the annotation's 100% size: your\n * text and borders scale with the page.\n */\n frame: AnnotationFrame;\n /** The layer's own drawing of it, filling the frame. Render it to keep the original look and add to it. */\n native: React.ReactNode;\n /** The engine's picture of the annotation as an image (`url`), or `null`. `native` draws it in place. */\n appearance: { url: string } | null;\n /** True while the pointer is over it. */\n hovered: boolean;\n /** True while it's selected. */\n selected: boolean;\n /** True when what you draw takes the pointer (see `interactive` on the renderer). */\n interactive: boolean;\n}\n\n/**\n * What a sibling plugin's renderer gets for the annotations its behavior owns\n * (the form plugin's fill controls). It places its own controls: `item` is the\n * layer's projection of the annotation (its box, style, text and raster box,\n * in page coordinates), and `page.transform.toPixels()` turns those into the\n * layer's pixels.\n */\nexport interface BehaviorRendererProps {\n annotation: Annotation;\n item: RenderItem;\n page: PageContextValue;\n /** The layer's own drawing of it, where the layer draws it. */\n native: React.ReactNode;\n hovered: boolean;\n selected: boolean;\n interactive: boolean;\n}\n\n/** What an `interactive` function is asked: the annotation, and the active tool. */\nexport interface AnnotationInteractiveContext {\n annotation: Annotation;\n toolId: string;\n}\n\n/**\n * One rule: \"draw these annotations with this component\". `for` gets the\n * annotation with all its fields; the first renderer that matches wins.\n * Without `interactive` the component only draws: the layer keeps handling\n * the pointer. With `interactive` (or a function asked whenever it matters)\n * the component takes the pointer, and the annotation can't be selected or\n * moved while it does.\n *\n * Define the list once, outside your component or in `useMemo`: the layer\n * registers each entry, and a new list every render registers them again.\n *\n * `{ behavior, component }` draws what a sibling plugin's behavior owns (the\n * form plugin's fill controls); the plugin decides when it's engaged.\n */\nexport type AnnotationRenderer =\n | { behavior: string; component: React.ComponentType<BehaviorRendererProps> }\n | {\n /** A stable id for the registration (optional; generated). */\n id?: string;\n for: (annotation: Annotation) => boolean;\n component: React.ComponentType<AnnotationRendererProps>;\n interactive?: boolean | ((context: AnnotationInteractiveContext) => boolean);\n /**\n * Scale what you draw with the page (the default): you draw at the\n * annotation's 100% size. `false`: you draw at its size on screen, and\n * size things yourself from `frame.scale`.\n */\n scale?: boolean;\n };\n\n/** What a handle component you draw yourself gets. */\nexport interface HandleProps {\n /** The handle's center, in pixels on the page. */\n at: Point;\n /** The `size` from the settings, px. */\n size: number;\n /** The selection's rotation (degrees clockwise), so a square handle can turn with it. */\n rotation: number;\n /** A box's `'corner'` or `'side'`, or one `'point'` of a line or a polygon. */\n kind: HandleRole;\n /** True while it's being dragged. */\n active: boolean;\n}\n\n/** What a rotation handle component gets: a handle's props, and where its stalk starts on the box. */\nexport interface RotationHandleProps extends Omit<HandleProps, 'kind'> {\n /** Where the stalk starts on the box, in pixels on the page. */\n from: Point;\n}\n\n/** Your own handles, drawn in place of the layer's. The viewer still decides where they can be grabbed. */\nexport interface AnnotationLayerComponents {\n Handle?: React.ComponentType<HandleProps>;\n RotationHandle?: React.ComponentType<RotationHandleProps>;\n}\n\nexport interface AnnotationLayerProps {\n /** Your own look for some annotations ({@link AnnotationRenderer}). */\n renderers?: AnnotationRenderer[];\n /** Your own handles and rotation handle. */\n components?: AnnotationLayerComponents;\n}\n\n/** How much the layer scales the look around it: 1 outside a look, or in one drawn at its size on screen. */\nconst LookScaleContext = React.createContext(1);\n\n/**\n * The box an annotation draws into, placed, sized and turned like the\n * annotation (`item.frame`) inside the page layer, which the page itself\n * turns. The annotation's own drawing and any look of yours draw inside it.\n */\nfunction AnnotationFrame({\n item,\n page,\n interactive = false,\n inert = false,\n children,\n}: {\n item: RenderItem;\n page: PageContextValue;\n interactive?: boolean;\n inert?: boolean;\n children: React.ReactNode;\n}) {\n const box = frameInPixels(item.frame, page.transform);\n return (\n <div\n {...(inert ? INERT : {})}\n style={{\n position: 'absolute',\n left: box.left,\n top: box.top,\n width: box.width,\n height: box.height,\n transform: box.transform,\n transformOrigin: 'center',\n // On the frame: a turned frame groups what is inside it, so blending on\n // an inner element would stop blending with the page.\n mixBlendMode: item.blend,\n // An interactive renderer takes the pointer: the layer's own `none` ends here.\n pointerEvents: interactive ? 'auto' : 'none',\n }}\n >\n {children}\n </div>\n );\n}\n\n/** SVG attribute names as React props: `stroke-width` is `strokeWidth`. */\n/**\n * The scene, filling the item's frame: drawn upright in it, the frame turns\n * it. The core computed the box and the painted scene; `@embedpdf/web`\n * describes each scene node as one SVG element, so there is no per-kind\n * logic and no bounds math here.\n */\nfunction Shape({ item }: { item: RenderItem }) {\n // Nothing to draw until the annotation has area (the 0×0 draft at mouse-down).\n const viewBox = sceneViewBox(item.box);\n if (!viewBox) return null;\n return (\n <svg\n viewBox={viewBox}\n style={{\n position: 'absolute',\n left: 0,\n top: 0,\n width: '100%',\n height: '100%',\n overflow: 'visible',\n pointerEvents: 'none',\n // A ghost is see-through as a whole, so its fill and stroke don't stack.\n ...(item.source === 'ghost' ? { opacity: ghostOpacity(item.ghostOpacity ?? 0.5) } : {}),\n }}\n >\n <SvgShapes shapes={svgShapesOf(scene(item), { miterLimit: MITER_LIMIT })} />\n </svg>\n );\n}\n\n/** The engine's raster inside the item's frame, where `item.raster` puts it, at any frame size. */\nfunction BakedImage({ url, box }: { url: string; box: FrameFraction }) {\n return (\n <img\n src={url}\n alt=\"\"\n draggable={false}\n style={{\n position: 'absolute',\n left: box.left,\n top: box.top,\n width: box.width,\n height: box.height,\n // The AP box is sized in content units; a global `img { max-width: 100% }`\n // reset would otherwise clamp it to the containing block and distort the\n // aspect. This bites specifically when the box is wider than that block —\n // a landscape stamp whose unrotated box overhangs a view-rotated (portrait)\n // page — so honour the explicit size.\n maxWidth: 'none',\n maxHeight: 'none',\n pointerEvents: 'none',\n // The turn the engine took out of the raster, put back about its middle.\n transform: box.transform,\n transformOrigin: 'center',\n }}\n />\n );\n}\n\n/**\n * The armed stamp's ghost: a see-through render of the payload drawn in the\n * exact box a click would place it (the plugin computes it with the same fit\n * + clamp as placement). Every other tool's ghost rides `pageItems` like a\n * drawing in progress. The preview bytes live in the capability closure; this\n * layer owns only the object-URL lifetime, keyed on the armed stamp — a new\n * arm swaps the image, a disarm (or tool change) drops it.\n */\nfunction ToolGhostImage({ page }: { page: PageContextValue }) {\n const anno = useCapability(AnnotationHostToken);\n const ghost = useSelector(AnnotationHostToken, (annotation) =>\n annotation.getImageGhost(page.ref),\n );\n const armed = useSelector(AnnotationHostToken, (annotation) => annotation.getArmedStamp());\n const [url, setUrl] = useState<string | null>(null);\n // The ghost is a bitmap of vector artwork, right at one size: ask for the\n // bucket that covers the box's device width (points × device px per point),\n // so it stays sharp at every zoom and density. The plugin caches per bucket.\n const bucket = ghost ? previewBucket(ghost.box.width * page.transform.renderScale) : 0;\n\n useEffect(() => {\n if (!bucket) {\n setUrl(null);\n return;\n }\n // The previous bucket's image stays up until this one resolves: no\n // flicker while a zoom crosses a bucket boundary.\n return loadObjectUrl(() => anno.renderArmedStampPreview(bucket), setUrl);\n }, [anno, armed, bucket]);\n\n if (!ghost || !url) return null;\n const frame = rectInPixels(ghost.box, page.transform);\n return (\n <img\n src={url}\n alt=\"\"\n draggable={false}\n style={{\n position: 'absolute',\n left: frame.left,\n top: frame.top,\n width: frame.width,\n height: frame.height,\n // Same explicit-size rule as BakedImage: never let a global img reset\n // clamp the box and distort the aspect.\n maxWidth: 'none',\n maxHeight: 'none',\n pointerEvents: 'none',\n opacity: ghostOpacity(ghost.opacity),\n ...(ghost.rot ? { transform: `rotate(${ghost.rot}deg)`, transformOrigin: 'center' } : {}),\n }}\n />\n );\n}\n\n// ── the selection's chrome ───────────────────────────────────────────────────\n\n/** The chrome settings, painted: follows the settings and the viewer's accent live. */\nfunction useChromePaint(): { chrome: ChromeSettings; painted: AnnotationChromePaint } {\n const chrome = useAnnotationSettings((settings) => settings.chrome);\n const accent = useViewerSettings((settings) => settings.accent);\n const painted = React.useMemo(() => annotationChromePaint(chrome, accent), [chrome, accent]);\n return { chrome, painted };\n}\n\n/** A handle as the layer draws it: a square or a circle, `size` px across. */\nfunction DefaultHandle({\n at,\n size,\n rotation,\n shape,\n style,\n}: {\n at: Point;\n size: number;\n rotation: number;\n shape: 'square' | 'circle';\n style: React.CSSProperties;\n}) {\n if (shape === 'circle') {\n return <circle cx={at.x} cy={at.y} r={size / 2} strokeWidth={1.5} style={style} />;\n }\n return (\n <rect\n x={at.x - size / 2}\n y={at.y - size / 2}\n width={size}\n height={size}\n strokeWidth={1.5}\n style={style}\n // The square rides a rotated box's orientation (spin about itself).\n {...(rotation ? { transform: `rotate(${rotation} ${at.x} ${at.y})` } : {})}\n />\n );\n}\n\nfunction Chrome({\n page,\n components,\n}: {\n page: PageContextValue;\n components?: AnnotationLayerComponents;\n}) {\n // The page's view scale converts the screen-pixel chrome settings into\n // content units inside the plugin (rotation handle offset, grab zones):\n // screen-constant at every zoom. What this draws is in screen pixels already.\n const scale = page.transform.viewScale;\n const rotation = page.transform.rotation;\n const zoom = page.transform.zoom;\n const nodes = useSelector(\n AnnotationHostToken,\n (annotation) => annotation.listChromeNodes(page.ref, scale, rotation, zoom),\n shallowArray,\n );\n const { chrome, painted } = useChromePaint();\n const Handle = components?.Handle;\n const RotationHandle = components?.RotationHandle;\n // Your own handles draw as HTML over the page; the layer's as SVG.\n const custom: React.ReactNode[] = [];\n const svg = chromeInPixels(nodes, page.transform).map((node, i) => {\n switch (node.kind) {\n case 'handle':\n if (Handle) {\n custom.push(\n <Handle\n key={i}\n at={node.at}\n size={chrome.handles.size}\n rotation={node.rotation}\n kind={node.role}\n active={node.active}\n />,\n );\n return null;\n }\n return (\n <DefaultHandle\n key={i}\n at={node.at}\n size={chrome.handles.size}\n rotation={node.rotation}\n shape={chrome.handles.shape}\n style={painted.handle}\n />\n );\n case 'guide':\n return (\n <line\n key={i}\n x1={node.from.x}\n y1={node.from.y}\n x2={node.to.x}\n y2={node.to.y}\n shapeRendering=\"crispEdges\"\n style={painted.guide}\n />\n );\n case 'turned-outline':\n return <polygon key={i} points={node.points} fill=\"none\" style={painted.outline} />;\n case 'rotation-guides':\n return (\n <g key={i}>\n {node.lines.map((line, j) => (\n <line\n key={j}\n x1={line.from.x}\n y1={line.from.y}\n x2={line.to.x}\n y2={line.to.y}\n opacity={line.opacity}\n style={painted.rotationGuide}\n />\n ))}\n </g>\n );\n case 'rotation-handle':\n if (RotationHandle) {\n custom.push(\n <RotationHandle\n key={i}\n at={node.at}\n from={node.from}\n size={chrome.rotationHandle.size}\n rotation={0}\n active={false}\n />,\n );\n return null;\n }\n return (\n <g key={i}>\n {chrome.rotationHandle.stalk && (\n <line\n x1={node.from.x}\n y1={node.from.y}\n x2={node.at.x}\n y2={node.at.y}\n strokeWidth={1}\n style={{ stroke: painted.rotationHandle.stroke }}\n />\n )}\n <circle\n cx={node.at.x}\n cy={node.at.y}\n r={chrome.rotationHandle.size / 2}\n strokeWidth={1.5}\n style={painted.rotationHandle}\n />\n </g>\n );\n // The box dragged to select keeps its own look (a see-through accent\n // fill, always dashed); the selection outline follows the settings.\n case 'marquee':\n return (\n <rect\n key={i}\n x={node.box.left}\n y={node.box.top}\n width={node.box.width}\n height={node.box.height}\n strokeWidth={1}\n strokeDasharray=\"4 3\"\n style={painted.marquee}\n />\n );\n case 'outline':\n return (\n <rect\n key={i}\n x={node.box.left}\n y={node.box.top}\n width={node.box.width}\n height={node.box.height}\n fill=\"none\"\n style={painted.outline}\n />\n );\n }\n });\n return (\n <>\n <svg style={{ position: 'absolute', inset: 0, overflow: 'visible', pointerEvents: 'none' }}>\n {svg}\n </svg>\n {custom.length > 0 && (\n <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>{custom}</div>\n )}\n </>\n );\n}\n\n// ── text boxes ───────────────────────────────────────────────────────────────\n\n/**\n * Make an element the editor of a text box: `@embedpdf/web`'s text box\n * editor binds it (rich text, focus and `contentEditable` following\n * `item.editing`, presses kept inside), and its follower attaches again for\n * another element or document and hands it the item and the scale. Returns\n * the ref callback for the element.\n */\nfunction useTextBoxEditor(\n item: TextItem | null,\n scale: number,\n): (element: HTMLElement | null) => void {\n const anno = useCapability(AnnotationHostToken);\n const [element, setElement] = useState<HTMLElement | null>(null);\n const [follower] = useState(() => createTextBoxEditorFollower<AnnotationRef>());\n // On every item or scale change too: the editor redraws only when the text differs.\n useEffect(\n () => follower.follow(element, anno, item, scale),\n [follower, anno, element, item, scale],\n );\n useEffect(() => () => follower.detach(), [follower]);\n return setElement;\n}\n\n/** A free text annotation: the same styled element for reading and typing. */\nfunction FreeText({ item, page }: { item: TextItem; page: PageContextValue }) {\n // The element is the engine's text plate: the box inset by its padding.\n const plate = textPlateInPixels(item, page.transform);\n const ref = useTextBoxEditor(item, plate.scale);\n const { painted } = useChromePaint();\n return (\n <div\n ref={ref}\n suppressContentEditableWarning\n style={{\n position: 'absolute',\n left: plate.left,\n top: plate.top,\n width: plate.width,\n // Fixed to the plate: the box never grows with its text; it scrolls\n // while typing and clips otherwise, where the baked /AP clips.\n height: plate.height,\n ...textBoxStyleOf(item.css, plate.scale),\n boxSizing: 'border-box',\n // The box's fill and border are the vector scene's, under this layer.\n background: 'transparent',\n whiteSpace: 'pre-wrap',\n overflowWrap: 'break-word',\n overflowY: item.editing ? 'auto' : 'hidden',\n overflowX: 'hidden',\n outline: item.editing ? `1px solid ${painted.textOutline}` : 'none',\n cursor: item.editing ? 'text' : 'default',\n // A plain text box turns about its centre, as the baked /AP does.\n ...(item.rot ? { transform: `rotate(${item.rot}deg)`, transformOrigin: 'center' } : {}),\n // Not typing: clicks fall through to the shapes (select, move, resize).\n pointerEvents: item.editing ? 'auto' : 'none',\n }}\n />\n );\n}\n\n/** What `useRichTextEditor()` gives the element you draw a text box with. */\nexport interface RichTextEditor {\n /** Put it on your element: it becomes the editor. */\n ref: (element: HTMLElement | null) => void;\n /** The box's body style as CSS: font, size, color and alignment. While typing it takes the pointer too. */\n style: React.CSSProperties;\n /** True while someone types in the box. */\n editing: boolean;\n}\n\nconst NO_STYLE: React.CSSProperties = {};\n\n/**\n * Make your own element the editor of a text box you draw yourself (a\n * renderer for free text): it draws the runs, turns typing back into runs,\n * keeps the caret when the style changes, and handles pasting, input methods\n * and the format shortcuts. The formatting calls (`text.toggleFormat`,\n * `selection.update`) work on it as they do on the built-in one.\n */\nexport function useRichTextEditor(annotation: Annotation): RichTextEditor {\n const page = usePage();\n const zoom = page.transform.zoom;\n const rotation = page.transform.rotation;\n const key = annotationKey(annotation.ref);\n const item = useOptionalSelector(\n AnnotationHostToken,\n (anno) =>\n anno\n .listTextItems(page.ref, { zoom, rotation })\n .find((text) => text.ref !== null && annotationKey(text.ref) === key) ?? null,\n null,\n );\n // Pixels per point where the element is: inside a scaled look, at the\n // annotation's 100% size; the look's scale does the rest.\n const scale = textBoxEditorScaleOf(item, page.transform, React.useContext(LookScaleContext));\n const ref = useTextBoxEditor(item, scale);\n // While typing, the element takes the pointer (the caret, a drag over words).\n const style = React.useMemo<React.CSSProperties>(\n () =>\n item\n ? { ...textBoxStyleOf(item.css, scale), ...(item.editing ? { pointerEvents: 'auto' } : {}) }\n : NO_STYLE,\n [item, scale],\n );\n return { ref, style, editing: item?.editing ?? false };\n}\n\n// ── renderers that take the pointer ──────────────────────────────────────────\n\n/**\n * Register the behaviors this layer's `interactive` renderers need, through\n * `@embedpdf/web`'s `registerRendererBehaviors`: counted per capability and\n * entry, since the layer mounts once per page. Entry identity is the key,\n * hence the \"define entries outside render\" rule on {@link AnnotationRenderer}.\n * Once they are registered the layer draws again: its drawings ask them.\n */\nfunction useRendererBehaviors(\n anno: Parameters<typeof registerRendererBehaviors<Annotation>>[0],\n activeToolId: () => string,\n renderers?: AnnotationRenderer[],\n): void {\n const toolRef = useRef(activeToolId);\n toolRef.current = activeToolId;\n const [, setRegistrations] = useState(0);\n useEffect(() => {\n if (!renderers) return;\n const release = registerRendererBehaviors(anno, renderers, () => toolRef.current());\n setRegistrations((count) => count + 1);\n return release;\n }, [anno, renderers]);\n}\n\n/**\n * Draws a page's annotations, the selection's outline and handles, the tool's\n * preview and the text boxes being typed in. Put it above the rendered page:\n * while it's there, the render layer leaves the annotations out of the\n * page's picture.\n */\nexport function AnnotationLayer({ renderers, components }: AnnotationLayerProps = {}) {\n const page = usePage();\n const anno = useCapability(AnnotationHostToken);\n // The active tool decides which interactive renderers take the pointer, so\n // a tool change repaints the layer (`interactive` functions read it live).\n useOptionalSelector(InteractionToken, (interaction) => interaction.getActiveToolId(), '');\n const interaction = useOptionalCapability(InteractionToken);\n // The page's view env (relative zoom + total display rotation) projects\n // screen-anchored (`noZoom`/`noRotate`) annotations to their effective\n // footprint inside the plugin: no flag logic lives in the framework.\n // `transform.zoom` (not `viewScale`): 1 = the page's physical 100%.\n const viewZoom = page.transform.zoom;\n const viewRotation = page.transform.rotation;\n const items = useSelector(\n AnnotationHostToken,\n (annotation) => annotation.listPageItems(page.ref, { zoom: viewZoom, rotation: viewRotation }),\n shallowArray,\n );\n const texts = useSelector(\n AnnotationHostToken,\n (annotation) => annotation.listTextItems(page.ref, { zoom: viewZoom, rotation: viewRotation }),\n shallowArray,\n );\n // `null` until the page's first pictures arrive.\n const [urls, setUrls] = useState<Record<string, AppearanceUrl> | null>(null);\n // The pictures shown stay valid until the next ones are shown.\n const [shown] = useState(createShownUrls);\n useEffect(() => () => shown.release(), [shown]);\n useRendererBehaviors(anno, () => (interaction?.getActiveToolId() as string) ?? '', renderers);\n usePaintsPagePart(page.ref, 'annotations');\n // Entry identity keys the behavior registration, so an inline `renderers`\n // array registers again on every render. Detect it once: a fresh array\n // whose entries are the previous ones.\n const previousRenderers = useRef(renderers);\n if (\n renderers &&\n previousRenderers.current &&\n renderers !== previousRenderers.current &&\n shallowArray(renderers, previousRenderers.current)\n ) {\n devWarn(\n 'annotation-renderers-inline',\n '<AnnotationLayer renderers> was given a new array with the same entries — define it ' +\n 'outside render (module scope or useMemo), because entry identity keys the behavior registration.',\n );\n }\n previousRenderers.current = renderers;\n\n // Baked annotations render from engine rasters: fetch again when the\n // page's baked set or an /AP version changes (a freshly placed stamp, a\n // resize whose re-bake resolved), and at appearance-scale crossings. A move\n // or a turn leaves the epoch as it is (the blit repositions the same\n // pixels), and live gestures don't touch it either: no mid-drag fetches.\n const bakedKey = useSelector(AnnotationHostToken, (annotation) =>\n annotation.getAppearanceEpoch(page.ref),\n );\n // The appearance scale follows the document's render policy: zoom ticks\n // inside an appearance-lattice rung re-bake nothing; crossing 1→2 re-bakes\n // once. Without the render plugin, only this session's pictures load.\n const bakeScale = useOptionalSelector(\n RenderToken,\n (render) => render.getAppearanceScale(page.transform.renderScale),\n page.transform.renderScale,\n );\n\n useEffect(\n () =>\n loadAppearanceUrls(\n shown,\n (signal) => anno.renderAppearances(page.ref, bakeScale, signal),\n annotationKey,\n setUrls,\n ),\n [shown, anno, page.ref, bakeScale, bakedKey],\n );\n\n /** The text box being typed in, by key. */\n const editingKey = editingTextKeyOf(texts, annotationKey);\n\n return (\n <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>\n {items.map((item) => {\n // The default look, inside the item's frame: the engine's raster where\n // the core places it (following a move as it happens), or the scene.\n const state = item.annotation?.appearanceState ?? null;\n const shows = pictureOrPainting({\n pending: item.source !== 'baked',\n picture: urls && shownAppearance(urls, item.id, state),\n version: item.pictureVersion ?? 0,\n loaded: urls !== null,\n paint: () => (hasLiveDrawing(item) ? item : null),\n });\n const native: React.ReactNode =\n shows?.kind === 'painting' ? (\n // shapes, cloudy, markup: all painted via scene()\n <Shape item={item} />\n ) : shows?.kind === 'picture' && item.raster ? (\n <BakedImage url={shows.picture.url} box={rasterInFrame(item.raster, item.frame)} />\n ) : null;\n const framed = (\n content: React.ReactNode,\n options?: { interactive?: boolean; inert?: boolean },\n ) => (\n <AnnotationFrame key={item.id} item={item} page={page} {...options}>\n {content}\n </AnnotationFrame>\n );\n const annotation = item.annotation;\n if (!annotation) return framed(native);\n /** Your look in the frame: drawn at the annotation's 100% size and scaled with the page, unless it opts out. */\n const look = (\n entry: Extract<AnnotationRenderer, { for: unknown }>,\n interactive: boolean,\n ): React.ReactNode => {\n const pixels = frameInPixels(item.frame, page.transform);\n const scaled = entry.scale !== false;\n const Look = entry.component;\n const drawn = (\n <Look\n annotation={annotation}\n frame={lookFrameOf(pixels, scaled)}\n native={native}\n appearance={urls && bakedAppearanceOf(urls, item.id, state)}\n hovered={item.hovered ?? false}\n selected={item.selected}\n interactive={interactive}\n />\n );\n if (!scaled) return drawn;\n return (\n <div\n style={{\n position: 'absolute',\n left: 0,\n top: 0,\n width: pixels.design.width,\n height: pixels.design.height,\n transform: `scale(${pixels.scale})`,\n transformOrigin: '0 0',\n }}\n >\n <LookScaleContext.Provider value={pixels.scale}>{drawn}</LookScaleContext.Provider>\n </div>\n );\n };\n\n // Ownership beats looks: an engaged behavior's renderer is\n // authoritative (form fill controls own their DOM); `for` rules apply\n // only to what the layer owns, and draw without the pointer. While its\n // text box is typed in, a look's editor (`useRichTextEditor`) takes\n // the keys: an inert subtree can't.\n const typing = editingKey !== null && annotationKey(annotation.ref) === editingKey;\n const drawing = annotationDrawingOf(annotation, anno, renderers, typing);\n switch (drawing.kind) {\n case 'owned': {\n // Its controls place themselves in the layer; the native drawing keeps its frame.\n const Owner = drawing.entry.component;\n return (\n <Owner\n key={item.id}\n annotation={annotation}\n item={item}\n page={page}\n native={framed(native)}\n hovered={item.hovered ?? false}\n selected={item.selected}\n interactive\n />\n );\n }\n case 'look':\n return framed(look(drawing.entry, drawing.interactive), {\n interactive: drawing.interactive,\n inert: drawing.inert,\n });\n case 'native':\n // Inert when an engaged behavior has no renderer of yours: its\n // plugin owns the input (a link's anchor takes the click), and the\n // annotation keeps its own look.\n return framed(native, { inert: drawing.inert });\n }\n })}\n {/* A text box your renderer draws is edited there (`useRichTextEditor`). */}\n {layerTextBoxesOf(texts, items, renderers, annotationKey).map((text) => (\n <FreeText key={text.id} item={text} page={page} />\n ))}\n <ToolGhostImage page={page} />\n <Chrome page={page} components={components} />\n </div>\n );\n}\n\n/**\n * The default file-picker provider: the built-in file dialog (from\n * `@embedpdf/web`), honouring the tool's `accept` filter. This is the adapter\n * fulfilling the plugin's DOM-free port — the dialog lives in the framework\n * layer, never in the plugin.\n */\nexport const filePickerProvider: FilePickerProvider = pickRequestedFile;\n\n/**\n * Install the file-picker provider for the active document — the one port\n * behind every click-then-pick tool (a stamp `'prompt'` source, the file-\n * attachment tool). Call once at a document-scoped spot (not inside\n * `<AnnotationLayer>`, which is per page). Defaults to\n * {@link filePickerProvider}, so a bare `useFilePickerProvider()` makes all of\n * them work out of the box; pass a custom provider (asset library, cloud\n * drive — switch on `req.subtype` / `req.toolId`) or `null` to make\n * click-then-pick tools inert. Cleared on unmount.\n */\nexport function useFilePickerProvider(\n provider: FilePickerProvider | null = filePickerProvider,\n): void {\n const anno = useOptionalCapability(AnnotationToken);\n useEffect(() => {\n if (!anno) return;\n // One port per document: a second caller silently replaces the first.\n return installFilePickerProvider(anno, provider, () =>\n devWarn(\n 'file-picker-provider-twice',\n 'useFilePickerProvider() is called from two mounted components for the same document — ' +\n 'the later one wins. Call it once, at a document-scoped spot.',\n ),\n );\n }, [anno, provider]);\n}\n\nexport function useAnnotation(): AnnotationCapability {\n return useCapability(AnnotationToken);\n}\n\n/** Listen to one annotation event while the component is mounted: `useAnnotationEvent((annotation) => annotation.onCreated, handler)`. */\nexport function useAnnotationEvent<T>(\n select: (annotation: AnnotationCapability) => EventHook<T>,\n handler: (event: T) => void,\n): void {\n useCapabilityEvent(AnnotationToken, select, handler);\n}\n\nconst NO_ANNOTATIONS: readonly Annotation[] = [];\n\n/**\n * The annotations matching `filter` (some pages, one kind), in drawing order,\n * as the user sees them; the same array while they stay the same. Empty\n * without a document.\n */\nexport function useAnnotationList(filter?: AnnotationFilter): readonly Annotation[] {\n const key = filter\n ? `${(filter.pages ?? [])\n .map((page) => (typeof page === 'number' ? `#${page}` : page.objectNumber))\n .join(',')}|${filter.pages ? 'p' : ''}|${filter.subtype ?? ''}`\n : '';\n // Keyed by value, so an inline filter object never subscribes again.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const stable = React.useMemo(() => filter, [key]);\n return useOptionalSelector(\n AnnotationToken,\n (annotation) => annotation.list(stable),\n NO_ANNOTATIONS,\n shallowArray,\n );\n}\n\nconst NO_DEFAULTS: ToolDefaults = {};\n\n/**\n * A tool's defaults: the fields its next annotation starts with, over the\n * engine's. The component re-renders when they change, so a color picker\n * always shows the current color. Empty without a document.\n */\nexport function useAnnotationDefaults(toolId: string): ToolDefaults {\n return useOptionalSelector(\n AnnotationToken,\n (annotation) => annotation.tools.getDefaults(toolId),\n NO_DEFAULTS,\n );\n}\n\nconst NO_PROPERTIES: AnnotationProperties = { properties: [], values: {}, mixed: [] };\n\n/**\n * What a style panel shows: the selection's properties (`selection.getProperties()`),\n * or, given a tool id, the tool's (`tools.getProperties(id)`). The component\n * re-renders when they change. Empty without a document.\n */\nexport function useAnnotationProperties(toolId?: string): AnnotationProperties {\n return useOptionalSelector(\n AnnotationToken,\n (annotation) =>\n toolId === undefined\n ? annotation.selection.getProperties()\n : annotation.tools.getProperties(toolId),\n NO_PROPERTIES,\n );\n}\n\n/**\n * An anchor for `<Anchored>` that keeps a card or a badge attached to one\n * annotation: its page and the box around what it shows, following a move\n * as it happens. `null` for `null`, or an annotation that isn't here. The\n * component re-renders when the annotation moves, not while people scroll or\n * zoom: a note that keeps its size on screen hands `<Anchored>` its\n * `boundsIn`.\n */\nexport function useAnnotationAnchor(ref: AnnotationRef | null): AnnotationAnchor | null {\n const key = ref ? annotationKey(ref) : null;\n // Keyed by value, so an inline ref never subscribes again.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const stable = React.useMemo(() => ref, [key]);\n return useOptionalSelector(\n AnnotationHostToken,\n (annotation) => (stable ? annotation.getAnnotationAnchor(stable) : null),\n null,\n sameAnnotationAnchor,\n );\n}\n\n// ── Comments (the conversation plane) ────────────────────────────────────────\n\n/**\n * A {@link CommentThread} enriched with its page's live display position —\n * the framework-layer join (`enrichCommentThreads` in `@embedpdf/web`).\n * Identity stays `page` (like every annotation surface); these two fields\n * are presentation, tracking page moves and deletes.\n */\nexport interface CommentThreadView extends CommentThread {\n /** Current 0-based display index of the thread's page; `-1` when the page\n * is no longer in the document (one-frame teardown race on delete). */\n pageIndex: number;\n /** The page's `/PageLabels` label when the PDF declares one (\"iv\", \"A-2\"),\n * else the 1-based position as a string — print it verbatim. */\n pageLabel: string;\n}\n\n/** The comments API: the `comments` part of `useAnnotation()`. Pair it with\n * {@link useCommentThreads} for the threads to show. */\nexport function useComments(): CommentsApi {\n return useCapability(AnnotationToken).comments;\n}\n\nconst EMPTY_PAGES: readonly PageLayout[] = [];\nconst NO_THREADS: readonly CommentThread[] = [];\n\n/**\n * Every comment thread in the document, display-ordered (page position →\n * top of page → creation date) and enriched with `pageIndex`/`pageLabel`.\n * Subscribed to both stores: annotation writes (own, remote, hydration)\n * recompute the threads; page moves/deletes re-run the join. Reference-\n * stable between changes — safe to memo child renders on the array.\n */\nexport function useCommentThreads(): CommentThreadView[] {\n const docId = useDocumentId();\n const threads = useOptionalSelector(\n AnnotationToken,\n (annotation) => annotation.comments.listThreads(),\n NO_THREADS,\n );\n const pages = useKernelValue((kernel) =>\n docId ? kernel.documents.listPages(docId) : EMPTY_PAGES,\n );\n return React.useMemo(() => enrichCommentThreads(threads, pages), [threads, pages]);\n}\n\n/** The enriched thread containing any member ref (root, reply, grouped part,\n * state annotation), or null. */\nexport function useCommentThread(ref: AnnotationRef | null): CommentThreadView | null {\n const views = useCommentThreads();\n const api = useOptionalCapability(AnnotationToken);\n if (ref == null || !api) return null;\n const thread = api.comments.getThread(ref);\n if (!thread) return null;\n return (\n views.find((view) => annotationKey(view.root.ref) === annotationKey(thread.root.ref)) ?? null\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"annotation-Dex5EBda.cjs","names":["stateHook","annotationState","settingsHook","AnnotationToken","useProjectorBinding","useOptionalSelector","AnnotationHostToken","sameSelectionAnchor","Anchored","AnnotationToken","sameCreationDraftAnchor","sameRotationAnchor","React","INERT","SvgShapes","MITER_LIMIT","useCapability","AnnotationHostToken","useSelector","useViewerSettings","shallowArray","usePage","useOptionalSelector","InteractionToken","useOptionalCapability","createShownUrls","RenderToken","annotationKey","pickRequestedFile","AnnotationToken","devWarn","sameAnnotationAnchor","useDocumentId","useKernelValue"],"sources":["../src/annotation-hooks.ts","../src/annotation-menu.tsx","../src/annotation.tsx"],"sourcesContent":["/**\n * The annotation hooks other feature entries use too (the form plugin reads\n * the selection), kept apart so they don't import the whole annotation\n * feature: the state and the settings, each from its declaration.\n */\nimport { AnnotationToken, annotationState } from '@embedpdf/plugin-annotation/contract';\n\nimport { settingsHook, stateHook } from './state';\n\n/**\n * The annotation state: `status` (`'loading'` until every annotation is in,\n * then `'ready'`), the `selected` annotations, the one `hovered` under the\n * pointer and the text box being typed in (`editing`). Re-renders only when a\n * field changes, or the value `select` picks. Empty without a document.\n */\nexport const useAnnotationState = stateHook(annotationState);\n\n/**\n * The annotation settings (`snap`, `chrome`, `afterCreate`, `tools`), or the\n * value `select` picks. They belong to the plugin, so they read without a\n * document; change them with `useAnnotation().updateSettings()`.\n */\nexport const useAnnotationSettings = settingsHook(AnnotationToken);\n","/**\n * Annotation menus: thin anchor plumbing over the one `<Anchored>` primitive.\n * They come from `/annotation`; this module stays apart so it doesn't pull\n * in the layer.\n *\n * These work identically under `<Stage>` (mount in the overlay slot; the\n * camera projects, no DOM reads) and `<PageView>` (measured and portalled):\n * the surface provides the projector, the menu doesn't care.\n *\n * No action bags: actions come from `useAnnotation()` and the state hooks,\n * which subscribe properly and compose across plugins. Render props carry\n * only the anchor's data (the draft menu's progress); the selection menu\n * takes plain children.\n */\nimport * as React from 'react';\nimport { AnnotationToken } from '@embedpdf/plugin-annotation/contract';\nimport { AnnotationToken as AnnotationHostToken } from '@embedpdf/plugin-annotation/contract/host';\nimport type { CreationDraftAnchor, RotationAnchor } from '@embedpdf/core-annotation';\nimport {\n paint,\n paintDefault,\n sameCreationDraftAnchor,\n sameRotationAnchor,\n sameSelectionAnchor,\n} from '@embedpdf/web';\nimport { Anchored, useProjectorBinding, type AnchoredPlacement } from './anchored';\nimport { useAnnotationSettings } from './annotation-hooks';\nimport { useOptionalSelector } from './runtime';\n\nexport interface AnnotationMenuProps {\n children: React.ReactNode;\n /** Gap in screen px between the selection box and the menu (default 15). */\n gap?: number;\n /** Where to place the menu relative to the selection box. Default 'top'. */\n placement?: AnchoredPlacement;\n}\n\n/**\n * Your own menu next to the selection (one anchor, also for a selection over\n * several pages), clear of the rotation handle. It hides while the selection\n * is dragged, resized or turned. What's in it is yours: build it from\n * `useAnnotation()` and `useAnnotationState()`.\n */\nexport function AnnotationMenu({ children, gap = 15, placement = 'top' }: AnnotationMenuProps) {\n // Reading the binding subscribes this component to projection changes\n // (its identity is the revision), so the anchor read below re-runs with\n // fresh view facts in the same commit as the surface: the rotation\n // handle's offset is screen-constant, so its page-space position depends\n // on the page's live view scale.\n const { projector } = useProjectorBinding();\n const anchor = useOptionalSelector(\n AnnotationHostToken,\n (annotation) => {\n const selectionAnchor = annotation.selection.getAnchor();\n if (!selectionAnchor) return null;\n const env = projector.viewEnv(selectionAnchor.page);\n return env ? annotation.getSelectionAnchorIn(env) : selectionAnchor;\n },\n null,\n sameSelectionAnchor,\n );\n if (!anchor) return null;\n return (\n <Anchored\n anchor={{\n page: anchor.page,\n bounds: anchor.bounds,\n ...(anchor.rotationHandle ? { avoid: [anchor.rotationHandle] } : {}),\n }}\n placement={placement}\n gap={gap}\n >\n {children}\n </Anchored>\n );\n}\n\nexport interface AnnotationDraftMenuProps {\n /** Render prop receiving the shape being drawn (`subtype`, `pointCount`,\n * `minPoints`, `canFinish`, …). The verbs are capability calls:\n * `useAnnotation().draft.finish()` and `.draft.cancel()`. */\n children: (anchor: CreationDraftAnchor) => React.ReactNode;\n /** Gap in screen px between the draft anchor and the menu (default 8). */\n gap?: number;\n /** Where to place the menu relative to the draft anchor. Default 'top'. */\n placement?: AnchoredPlacement;\n}\n\n/** Floats over a live multi-click creation draft (polygon, polyline, …). */\nexport function AnnotationDraftMenu({\n children,\n gap = 8,\n placement = 'top',\n}: AnnotationDraftMenuProps) {\n const anchor = useOptionalSelector(\n AnnotationToken,\n (annotation) => annotation.draft.get(),\n null,\n sameCreationDraftAnchor,\n );\n if (!anchor) return null;\n return (\n <Anchored anchor={anchor} placement={placement} gap={gap}>\n {children(anchor)}\n </Anchored>\n );\n}\n\nexport interface AnnotationRotationBadgeProps {\n /** Render prop receiving the rotation in progress (`angle`, degrees\n * clockwise, as the commit will apply it). Without it, the badge shows the\n * angle in the `chrome.readout` colors, when `chrome.readout.enabled`. */\n children?: (rotation: RotationAnchor) => React.ReactNode;\n /** Gap in screen px between the pointer and the badge (default 16). */\n gap?: number;\n /** Where to place the badge relative to the pointer. Default 'right'. */\n placement?: AnchoredPlacement;\n}\n\n/**\n * Follows the pointer while a selection is being turned, upright however the\n * page is shown: it floats over the page like the menus, not in it. Shows\n * nothing when no rotation is in progress.\n */\nexport function AnnotationRotationBadge({\n children,\n gap = 16,\n placement = 'right',\n}: AnnotationRotationBadgeProps) {\n const rotation = useOptionalSelector(\n AnnotationToken,\n (annotation) => annotation.selection.getRotationAnchor(),\n null,\n sameRotationAnchor,\n );\n const readout = useAnnotationSettings((settings) => settings.chrome.readout);\n if (!rotation || (!children && !readout.enabled)) return null;\n return (\n <Anchored\n anchor={{ page: rotation.page, bounds: { ...rotation.at, width: 0, height: 0 } }}\n placement={placement}\n gap={gap}\n >\n {children ? (\n children(rotation)\n ) : (\n <div\n style={{\n pointerEvents: 'none',\n whiteSpace: 'nowrap',\n borderRadius: 4,\n padding: '2px 6px',\n fontFamily: paintDefault('font-mono'),\n fontSize: 12,\n background: paint('readout-background', readout.background),\n color: paint('readout-color', readout.color),\n }}\n >\n {rotation.angle}°\n </div>\n )}\n </Anchored>\n );\n}\n","/**\n * The React view of @embedpdf/plugin-annotation.\n *\n * Pure paint: it reads the per-page render items and chrome and draws them.\n * Pointer events arrive through the interaction hub (the Stage's forwarding),\n * and the cursor is driven by the hub too (the edit handler claims\n * move/pointer/resize on hover). Each annotation resolves to one native node:\n * a vector scene, the engine's baked /AP <img>, or a registered behavior; a\n * renderer may wrap or replace it. Every chrome color is a setting painted\n * through `paint()`, so its `--epdf-annotation-*` CSS variable wins.\n *\n * What isn't React lives in `@embedpdf/web`, the same for every framework:\n * the scene as SVG elements, the chrome's paint and pixels, the text box\n * editor, which drawing each annotation gets and the behaviors renderers\n * register, and the object URLs of baked appearances and the stamp ghost.\n *\n * The hooks follow every plugin's four: `useAnnotation()` (the API),\n * `useAnnotationState()` (status, selected, hovered, editing),\n * `useAnnotationEvent()` and `useAnnotationSettings()`; the reads a component\n * follows on their own are `useAnnotationList()`, `useAnnotationDefaults()`,\n * `useAnnotationProperties()` and `useAnnotationAnchor()`.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/plugin-annotation';\nimport type { EventHook, PageRef } from '@embedpdf/core';\nimport {\n hasLiveDrawing,\n scene,\n MITER_LIMIT,\n type FieldValues,\n type Point,\n type RenderItem,\n} from '@embedpdf/core-annotation';\nimport {\n AnnotationToken,\n annotationKey,\n type Annotation,\n type AnnotationAnchor,\n type AnnotationCapability,\n type AnnotationFilter,\n type AnnotationProperties,\n type AnnotationRef,\n type ChromeSettings,\n type CommentsApi,\n type CommentThread,\n type FilePickerProvider,\n type HandleRole,\n type TextItem,\n type ToolDefaults,\n} from '@embedpdf/plugin-annotation';\n// The render layer is framework code, so it resolves the full host lens\n// (page items, chrome, appearances…). Same runtime token as the public one,\n// only the type differs. App code never imports this.\nimport {\n AnnotationToken as AnnotationHostToken,\n previewBucket,\n} from '@embedpdf/plugin-annotation/contract/host';\nimport { InteractionToken } from '@embedpdf/plugin-interaction/contract';\nimport { RenderToken } from '@embedpdf/plugin-render/contract/host';\nimport {\n annotationChromePaint,\n annotationDrawingOf,\n bakedAppearanceOf,\n chromeInPixels,\n createShownUrls,\n createTextBoxEditorFollower,\n editingTextKeyOf,\n enrichCommentThreads,\n frameInPixels,\n ghostOpacity,\n installFilePickerProvider,\n layerTextBoxesOf,\n loadAppearanceUrls,\n loadObjectUrl,\n lookFrameOf,\n pickRequestedFile,\n pictureOrPainting,\n rasterInFrame,\n rectInPixels,\n registerRendererBehaviors,\n sameAnnotationAnchor,\n sceneViewBox,\n shownAppearance,\n svgShapesOf,\n textBoxEditorScaleOf,\n textBoxStyleOf,\n textPlateInPixels,\n type AnnotationChromePaint,\n type AppearanceUrl,\n type FrameFraction,\n} from '@embedpdf/web';\nimport { useEffect, useRef, useState } from 'react';\nimport * as React from 'react';\n\nexport type {\n CreationDraftAnchor,\n RenderItem,\n LineEnding,\n LineEndings,\n Style,\n AnnotationFlags,\n FieldValues,\n TextAlign,\n TextStyle,\n} from '@embedpdf/core-annotation';\nimport { devWarn } from './dev';\nimport { usePaintsPagePart } from './page-layers';\nimport { useAnnotationSettings } from './annotation-hooks';\nimport { INERT } from './inert';\nimport {\n shallowArray,\n useCapability,\n useCapabilityEvent,\n useDocumentId,\n useKernelValue,\n useOptionalCapability,\n useOptionalSelector,\n usePage,\n useSelector,\n useViewerSettings,\n} from './runtime';\nimport type { PageContextValue, PageLayout } from './runtime';\nimport { SvgShapes } from './svg-shapes';\n\nexport { useAnnotationSettings, useAnnotationState } from './annotation-hooks';\nexport {\n AnnotationDraftMenu,\n AnnotationMenu,\n AnnotationRotationBadge,\n type AnnotationDraftMenuProps,\n type AnnotationMenuProps,\n type AnnotationRotationBadgeProps,\n} from './annotation-menu';\n\n// ── renderers ────────────────────────────────────────────────────────────────\n\n/** The box a renderer draws into: the size to draw at, how it's turned, and how it's scaled. */\nexport interface AnnotationFrame {\n /** Its width in pixels at the annotation's 100% size, before its turn: draw at this. */\n width: number;\n /** Its height in pixels at the annotation's 100% size, before its turn: draw at this. */\n height: number;\n /**\n * How it's turned on screen, degrees clockwise: the page's turn and the\n * annotation's own. 0 is upright; turn your content by `-rotation` to keep\n * it upright.\n */\n rotation: number;\n /**\n * How much the layer scales what you draw, with the page: the zoom, or for\n * an annotation that keeps its size on screen, the zoom up to 1.\n */\n scale: number;\n}\n\n/**\n * What a renderer's component gets: the annotation, the frame to draw into,\n * and the layer's own drawing of it to keep or wrap. The layer keeps handling\n * the pointer (select, move, resize) unless the renderer is `interactive`.\n */\nexport interface AnnotationRendererProps {\n /** The annotation, as `get()` returns it. */\n annotation: Annotation;\n /**\n * The box you draw into. The layer places, turns and scales it like the\n * annotation's own drawing, also during a drag, a resize or a turn. Fill it\n * (`width: 100%; height: 100%`) and draw at the annotation's 100% size: your\n * text and borders scale with the page.\n */\n frame: AnnotationFrame;\n /** The layer's own drawing of it, filling the frame. Render it to keep the original look and add to it. */\n native: React.ReactNode;\n /** The engine's picture of the annotation as an image (`url`), or `null`. `native` draws it in place. */\n appearance: { url: string } | null;\n /** True while the pointer is over it. */\n hovered: boolean;\n /** True while it's selected. */\n selected: boolean;\n /** True when what you draw takes the pointer (see `interactive` on the renderer). */\n interactive: boolean;\n}\n\n/**\n * What a sibling plugin's renderer gets for the annotations its behavior owns\n * (the form plugin's fill controls). It places its own controls: `item` is the\n * layer's projection of the annotation (its box, style, text and raster box,\n * in page coordinates), and `page.transform.toPixels()` turns those into the\n * layer's pixels.\n */\nexport interface BehaviorRendererProps {\n annotation: Annotation;\n item: RenderItem;\n page: PageContextValue;\n /** The layer's own drawing of it, where the layer draws it. */\n native: React.ReactNode;\n hovered: boolean;\n selected: boolean;\n interactive: boolean;\n}\n\n/** What an `interactive` function is asked: the annotation, and the active tool. */\nexport interface AnnotationInteractiveContext {\n annotation: Annotation;\n toolId: string;\n}\n\n/**\n * One rule: \"draw these annotations with this component\". `for` gets the\n * annotation with all its fields; the first renderer that matches wins.\n * Without `interactive` the component only draws: the layer keeps handling\n * the pointer. With `interactive` (or a function asked whenever it matters)\n * the component takes the pointer, and the annotation can't be selected or\n * moved while it does.\n *\n * Define the list once, outside your component or in `useMemo`: the layer\n * registers each entry, and a new list every render registers them again.\n *\n * `{ behavior, component }` draws what a sibling plugin's behavior owns (the\n * form plugin's fill controls); the plugin decides when it's engaged.\n */\nexport type AnnotationRenderer =\n | { behavior: string; component: React.ComponentType<BehaviorRendererProps> }\n | {\n /** A stable id for the registration (optional; generated). */\n id?: string;\n for: (annotation: Annotation) => boolean;\n component: React.ComponentType<AnnotationRendererProps>;\n interactive?: boolean | ((context: AnnotationInteractiveContext) => boolean);\n /**\n * Scale what you draw with the page (the default): you draw at the\n * annotation's 100% size. `false`: you draw at its size on screen, and\n * size things yourself from `frame.scale`.\n */\n scale?: boolean;\n };\n\n/** What a handle component you draw yourself gets. */\nexport interface HandleProps {\n /** The handle's center, in pixels on the page. */\n at: Point;\n /** The `size` from the settings, px. */\n size: number;\n /** The selection's rotation (degrees clockwise), so a square handle can turn with it. */\n rotation: number;\n /** A box's `'corner'` or `'side'`, or one `'point'` of a line or a polygon. */\n kind: HandleRole;\n /** True while it's being dragged. */\n active: boolean;\n}\n\n/** What a rotation handle component gets: a handle's props, and where its stalk starts on the box. */\nexport interface RotationHandleProps extends Omit<HandleProps, 'kind'> {\n /** Where the stalk starts on the box, in pixels on the page. */\n from: Point;\n}\n\n/** Your own handles, drawn in place of the layer's. The viewer still decides where they can be grabbed. */\nexport interface AnnotationLayerComponents {\n Handle?: React.ComponentType<HandleProps>;\n RotationHandle?: React.ComponentType<RotationHandleProps>;\n}\n\nexport interface AnnotationLayerProps {\n /** Your own look for some annotations ({@link AnnotationRenderer}). */\n renderers?: AnnotationRenderer[];\n /** Your own handles and rotation handle. */\n components?: AnnotationLayerComponents;\n}\n\n/** How much the layer scales the look around it: 1 outside a look, or in one drawn at its size on screen. */\nconst LookScaleContext = React.createContext(1);\n\n/**\n * The box an annotation draws into, placed, sized and turned like the\n * annotation (`item.frame`) inside the page layer, which the page itself\n * turns. The annotation's own drawing and any look of yours draw inside it.\n */\nfunction AnnotationFrame({\n item,\n page,\n interactive = false,\n inert = false,\n children,\n}: {\n item: RenderItem;\n page: PageContextValue;\n interactive?: boolean;\n inert?: boolean;\n children: React.ReactNode;\n}) {\n const box = frameInPixels(item.frame, page.transform);\n return (\n <div\n {...(inert ? INERT : {})}\n style={{\n position: 'absolute',\n left: box.left,\n top: box.top,\n width: box.width,\n height: box.height,\n transform: box.transform,\n transformOrigin: 'center',\n // On the frame: a turned frame groups what is inside it, so blending on\n // an inner element would stop blending with the page.\n mixBlendMode: item.blend,\n // An interactive renderer takes the pointer: the layer's own `none` ends here.\n pointerEvents: interactive ? 'auto' : 'none',\n }}\n >\n {children}\n </div>\n );\n}\n\n/** SVG attribute names as React props: `stroke-width` is `strokeWidth`. */\n/**\n * The scene, filling the item's frame: drawn upright in it, the frame turns\n * it. The core computed the box and the painted scene; `@embedpdf/web`\n * describes each scene node as one SVG element, so there is no per-kind\n * logic and no bounds math here.\n */\nfunction Shape({ item }: { item: RenderItem }) {\n // Nothing to draw until the annotation has area (the 0×0 draft at mouse-down).\n const viewBox = sceneViewBox(item.box);\n if (!viewBox) return null;\n return (\n <svg\n viewBox={viewBox}\n style={{\n position: 'absolute',\n left: 0,\n top: 0,\n width: '100%',\n height: '100%',\n overflow: 'visible',\n pointerEvents: 'none',\n // A ghost is see-through as a whole, so its fill and stroke don't stack.\n ...(item.source === 'ghost' ? { opacity: ghostOpacity(item.ghostOpacity ?? 0.5) } : {}),\n }}\n >\n <SvgShapes shapes={svgShapesOf(scene(item), { miterLimit: MITER_LIMIT })} />\n </svg>\n );\n}\n\n/** The engine's raster inside the item's frame, where `item.raster` puts it, at any frame size. */\nfunction BakedImage({ url, box }: { url: string; box: FrameFraction }) {\n return (\n <img\n src={url}\n alt=\"\"\n draggable={false}\n style={{\n position: 'absolute',\n left: box.left,\n top: box.top,\n width: box.width,\n height: box.height,\n // The AP box is sized in content units; a global `img { max-width: 100% }`\n // reset would otherwise clamp it to the containing block and distort the\n // aspect. This bites specifically when the box is wider than that block —\n // a landscape stamp whose unrotated box overhangs a view-rotated (portrait)\n // page — so honour the explicit size.\n maxWidth: 'none',\n maxHeight: 'none',\n pointerEvents: 'none',\n // The turn the engine took out of the raster, put back about its middle.\n transform: box.transform,\n transformOrigin: 'center',\n }}\n />\n );\n}\n\n/**\n * The armed stamp's ghost: a see-through render of the payload drawn in the\n * exact box a click would place it (the plugin computes it with the same fit\n * + clamp as placement). Every other tool's ghost rides `pageItems` like a\n * drawing in progress. The preview bytes live in the capability closure; this\n * layer owns only the object-URL lifetime, keyed on the armed stamp — a new\n * arm swaps the image, a disarm (or tool change) drops it.\n */\nfunction ToolGhostImage({ page }: { page: PageContextValue }) {\n const anno = useCapability(AnnotationHostToken);\n const ghost = useSelector(AnnotationHostToken, (annotation) =>\n annotation.getImageGhost(page.ref),\n );\n const armed = useSelector(AnnotationHostToken, (annotation) => annotation.getArmedStamp());\n const [url, setUrl] = useState<string | null>(null);\n // The ghost is a bitmap of vector artwork, right at one size: ask for the\n // bucket that covers the box's device width (points × device px per point),\n // so it stays sharp at every zoom and density. The plugin caches per bucket.\n const bucket = ghost ? previewBucket(ghost.box.width * page.transform.renderScale) : 0;\n\n useEffect(() => {\n if (!bucket) {\n setUrl(null);\n return;\n }\n // The previous bucket's image stays up until this one resolves: no\n // flicker while a zoom crosses a bucket boundary.\n return loadObjectUrl(() => anno.renderArmedStampPreview(bucket), setUrl);\n }, [anno, armed, bucket]);\n\n if (!ghost || !url) return null;\n const frame = rectInPixels(ghost.box, page.transform);\n return (\n <img\n src={url}\n alt=\"\"\n draggable={false}\n style={{\n position: 'absolute',\n left: frame.left,\n top: frame.top,\n width: frame.width,\n height: frame.height,\n // Same explicit-size rule as BakedImage: never let a global img reset\n // clamp the box and distort the aspect.\n maxWidth: 'none',\n maxHeight: 'none',\n pointerEvents: 'none',\n opacity: ghostOpacity(ghost.opacity),\n ...(ghost.rot ? { transform: `rotate(${ghost.rot}deg)`, transformOrigin: 'center' } : {}),\n }}\n />\n );\n}\n\n// ── the selection's chrome ───────────────────────────────────────────────────\n\n/** The chrome settings, painted: follows the settings and the viewer's accent live. */\nfunction useChromePaint(): { chrome: ChromeSettings; painted: AnnotationChromePaint } {\n const chrome = useAnnotationSettings((settings) => settings.chrome);\n const accent = useViewerSettings((settings) => settings.accent);\n const painted = React.useMemo(() => annotationChromePaint(chrome, accent), [chrome, accent]);\n return { chrome, painted };\n}\n\n/** A handle as the layer draws it: a square or a circle, `size` px across. */\nfunction DefaultHandle({\n at,\n size,\n rotation,\n shape,\n style,\n}: {\n at: Point;\n size: number;\n rotation: number;\n shape: 'square' | 'circle';\n style: React.CSSProperties;\n}) {\n if (shape === 'circle') {\n return <circle cx={at.x} cy={at.y} r={size / 2} strokeWidth={1.5} style={style} />;\n }\n return (\n <rect\n x={at.x - size / 2}\n y={at.y - size / 2}\n width={size}\n height={size}\n strokeWidth={1.5}\n style={style}\n // The square rides a rotated box's orientation (spin about itself).\n {...(rotation ? { transform: `rotate(${rotation} ${at.x} ${at.y})` } : {})}\n />\n );\n}\n\nfunction Chrome({\n page,\n components,\n}: {\n page: PageContextValue;\n components?: AnnotationLayerComponents;\n}) {\n // The page's view scale converts the screen-pixel chrome settings into\n // content units inside the plugin (rotation handle offset, grab zones):\n // screen-constant at every zoom. What this draws is in screen pixels already.\n const scale = page.transform.viewScale;\n const rotation = page.transform.rotation;\n const zoom = page.transform.zoom;\n const nodes = useSelector(\n AnnotationHostToken,\n (annotation) => annotation.listChromeNodes(page.ref, scale, rotation, zoom),\n shallowArray,\n );\n const { chrome, painted } = useChromePaint();\n const Handle = components?.Handle;\n const RotationHandle = components?.RotationHandle;\n // Your own handles draw as HTML over the page; the layer's as SVG.\n const custom: React.ReactNode[] = [];\n const svg = chromeInPixels(nodes, page.transform).map((node, i) => {\n switch (node.kind) {\n case 'handle':\n if (Handle) {\n custom.push(\n <Handle\n key={i}\n at={node.at}\n size={chrome.handles.size}\n rotation={node.rotation}\n kind={node.role}\n active={node.active}\n />,\n );\n return null;\n }\n return (\n <DefaultHandle\n key={i}\n at={node.at}\n size={chrome.handles.size}\n rotation={node.rotation}\n shape={chrome.handles.shape}\n style={painted.handle}\n />\n );\n case 'guide':\n return (\n <line\n key={i}\n x1={node.from.x}\n y1={node.from.y}\n x2={node.to.x}\n y2={node.to.y}\n shapeRendering=\"crispEdges\"\n style={painted.guide}\n />\n );\n case 'turned-outline':\n return <polygon key={i} points={node.points} fill=\"none\" style={painted.outline} />;\n case 'rotation-guides':\n return (\n <g key={i}>\n {node.lines.map((line, j) => (\n <line\n key={j}\n x1={line.from.x}\n y1={line.from.y}\n x2={line.to.x}\n y2={line.to.y}\n opacity={line.opacity}\n style={painted.rotationGuide}\n />\n ))}\n </g>\n );\n case 'rotation-handle':\n if (RotationHandle) {\n custom.push(\n <RotationHandle\n key={i}\n at={node.at}\n from={node.from}\n size={chrome.rotationHandle.size}\n rotation={0}\n active={false}\n />,\n );\n return null;\n }\n return (\n <g key={i}>\n {chrome.rotationHandle.stalk && (\n <line\n x1={node.from.x}\n y1={node.from.y}\n x2={node.at.x}\n y2={node.at.y}\n strokeWidth={1}\n style={{ stroke: painted.rotationHandle.stroke }}\n />\n )}\n <circle\n cx={node.at.x}\n cy={node.at.y}\n r={chrome.rotationHandle.size / 2}\n strokeWidth={1.5}\n style={painted.rotationHandle}\n />\n </g>\n );\n // The box dragged to select keeps its own look (a see-through accent\n // fill, always dashed); the selection outline follows the settings.\n case 'marquee':\n return (\n <rect\n key={i}\n x={node.box.left}\n y={node.box.top}\n width={node.box.width}\n height={node.box.height}\n strokeWidth={1}\n strokeDasharray=\"4 3\"\n style={painted.marquee}\n />\n );\n case 'outline':\n return (\n <rect\n key={i}\n x={node.box.left}\n y={node.box.top}\n width={node.box.width}\n height={node.box.height}\n fill=\"none\"\n style={painted.outline}\n />\n );\n }\n });\n return (\n <>\n <svg style={{ position: 'absolute', inset: 0, overflow: 'visible', pointerEvents: 'none' }}>\n {svg}\n </svg>\n {custom.length > 0 && (\n <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>{custom}</div>\n )}\n </>\n );\n}\n\n// ── text boxes ───────────────────────────────────────────────────────────────\n\n/**\n * Make an element the editor of a text box: `@embedpdf/web`'s text box\n * editor binds it (rich text, focus and `contentEditable` following\n * `item.editing`, presses kept inside), and its follower attaches again for\n * another element or document and hands it the item and the scale. Returns\n * the ref callback for the element.\n */\nfunction useTextBoxEditor(\n item: TextItem | null,\n scale: number,\n): (element: HTMLElement | null) => void {\n const anno = useCapability(AnnotationHostToken);\n const [element, setElement] = useState<HTMLElement | null>(null);\n const [follower] = useState(() => createTextBoxEditorFollower<AnnotationRef>());\n // On every item or scale change too: the editor redraws only when the text differs.\n useEffect(\n () => follower.follow(element, anno, item, scale),\n [follower, anno, element, item, scale],\n );\n useEffect(() => () => follower.detach(), [follower]);\n return setElement;\n}\n\n/** A free text annotation: the same styled element for reading and typing. */\nfunction FreeText({ item, page }: { item: TextItem; page: PageContextValue }) {\n // The element is the engine's text plate: the box inset by its padding.\n const plate = textPlateInPixels(item, page.transform);\n const ref = useTextBoxEditor(item, plate.scale);\n const { painted } = useChromePaint();\n return (\n <div\n ref={ref}\n suppressContentEditableWarning\n style={{\n position: 'absolute',\n left: plate.left,\n top: plate.top,\n width: plate.width,\n // Fixed to the plate: the box never grows with its text; it scrolls\n // while typing and clips otherwise, where the baked /AP clips.\n height: plate.height,\n ...textBoxStyleOf(item.css, plate.scale),\n boxSizing: 'border-box',\n // The box's fill and border are the vector scene's, under this layer.\n background: 'transparent',\n whiteSpace: 'pre-wrap',\n overflowWrap: 'break-word',\n overflowY: item.editing ? 'auto' : 'hidden',\n overflowX: 'hidden',\n outline: item.editing ? `1px solid ${painted.textOutline}` : 'none',\n cursor: item.editing ? 'text' : 'default',\n // A plain text box turns about its centre, as the baked /AP does.\n ...(item.rot ? { transform: `rotate(${item.rot}deg)`, transformOrigin: 'center' } : {}),\n // Not typing: clicks fall through to the shapes (select, move, resize).\n pointerEvents: item.editing ? 'auto' : 'none',\n }}\n />\n );\n}\n\n/** What `useRichTextEditor()` gives the element you draw a text box with. */\nexport interface RichTextEditor {\n /** Put it on your element: it becomes the editor. */\n ref: (element: HTMLElement | null) => void;\n /** The box's body style as CSS: font, size, color and alignment. While typing it takes the pointer too. */\n style: React.CSSProperties;\n /** True while someone types in the box. */\n editing: boolean;\n}\n\nconst NO_STYLE: React.CSSProperties = {};\n\n/**\n * Make your own element the editor of a text box you draw yourself (a\n * renderer for free text): it draws the runs, turns typing back into runs,\n * keeps the caret when the style changes, and handles pasting, input methods\n * and the format shortcuts. The formatting calls (`text.toggleFormat`,\n * `selection.update`) work on it as they do on the built-in one.\n */\nexport function useRichTextEditor(annotation: Annotation): RichTextEditor {\n const page = usePage();\n const zoom = page.transform.zoom;\n const rotation = page.transform.rotation;\n const key = annotationKey(annotation.ref);\n const item = useOptionalSelector(\n AnnotationHostToken,\n (anno) =>\n anno\n .listTextItems(page.ref, { zoom, rotation })\n .find((text) => text.ref !== null && annotationKey(text.ref) === key) ?? null,\n null,\n );\n // Pixels per point where the element is: inside a scaled look, at the\n // annotation's 100% size; the look's scale does the rest.\n const scale = textBoxEditorScaleOf(item, page.transform, React.useContext(LookScaleContext));\n const ref = useTextBoxEditor(item, scale);\n // While typing, the element takes the pointer (the caret, a drag over words).\n const style = React.useMemo<React.CSSProperties>(\n () =>\n item\n ? { ...textBoxStyleOf(item.css, scale), ...(item.editing ? { pointerEvents: 'auto' } : {}) }\n : NO_STYLE,\n [item, scale],\n );\n return { ref, style, editing: item?.editing ?? false };\n}\n\n// ── renderers that take the pointer ──────────────────────────────────────────\n\n/**\n * Register the behaviors this layer's `interactive` renderers need, through\n * `@embedpdf/web`'s `registerRendererBehaviors`: counted per capability and\n * entry, since the layer mounts once per page. Entry identity is the key,\n * hence the \"define entries outside render\" rule on {@link AnnotationRenderer}.\n * Once they are registered the layer draws again: its drawings ask them.\n */\nfunction useRendererBehaviors(\n anno: Parameters<typeof registerRendererBehaviors<Annotation>>[0],\n activeToolId: () => string,\n renderers?: AnnotationRenderer[],\n): void {\n const toolRef = useRef(activeToolId);\n toolRef.current = activeToolId;\n const [, setRegistrations] = useState(0);\n useEffect(() => {\n if (!renderers) return;\n const release = registerRendererBehaviors(anno, renderers, () => toolRef.current());\n setRegistrations((count) => count + 1);\n return release;\n }, [anno, renderers]);\n}\n\n/**\n * Draws a page's annotations, the selection's outline and handles, the tool's\n * preview and the text boxes being typed in. Put it above the rendered page:\n * while it's there, the render layer leaves the annotations out of the\n * page's picture.\n */\nexport function AnnotationLayer({ renderers, components }: AnnotationLayerProps = {}) {\n const page = usePage();\n const anno = useCapability(AnnotationHostToken);\n // The active tool decides which interactive renderers take the pointer, so\n // a tool change repaints the layer (`interactive` functions read it live).\n useOptionalSelector(InteractionToken, (interaction) => interaction.getActiveToolId(), '');\n const interaction = useOptionalCapability(InteractionToken);\n // The page's view env (relative zoom + total display rotation) projects\n // screen-anchored (`noZoom`/`noRotate`) annotations to their effective\n // footprint inside the plugin: no flag logic lives in the framework.\n // `transform.zoom` (not `viewScale`): 1 = the page's physical 100%.\n const viewZoom = page.transform.zoom;\n const viewRotation = page.transform.rotation;\n const items = useSelector(\n AnnotationHostToken,\n (annotation) => annotation.listPageItems(page.ref, { zoom: viewZoom, rotation: viewRotation }),\n shallowArray,\n );\n const texts = useSelector(\n AnnotationHostToken,\n (annotation) => annotation.listTextItems(page.ref, { zoom: viewZoom, rotation: viewRotation }),\n shallowArray,\n );\n // `null` until the page's first pictures arrive.\n const [urls, setUrls] = useState<Record<string, AppearanceUrl> | null>(null);\n // The pictures shown stay valid until the next ones are shown.\n const [shown] = useState(createShownUrls);\n useEffect(() => () => shown.release(), [shown]);\n useRendererBehaviors(anno, () => (interaction?.getActiveToolId() as string) ?? '', renderers);\n usePaintsPagePart(page.ref, 'annotations');\n // Entry identity keys the behavior registration, so an inline `renderers`\n // array registers again on every render. Detect it once: a fresh array\n // whose entries are the previous ones.\n const previousRenderers = useRef(renderers);\n if (\n renderers &&\n previousRenderers.current &&\n renderers !== previousRenderers.current &&\n shallowArray(renderers, previousRenderers.current)\n ) {\n devWarn(\n 'annotation-renderers-inline',\n '<AnnotationLayer renderers> was given a new array with the same entries — define it ' +\n 'outside render (module scope or useMemo), because entry identity keys the behavior registration.',\n );\n }\n previousRenderers.current = renderers;\n\n // Baked annotations render from engine rasters: fetch again when the\n // page's baked set or an /AP version changes (a freshly placed stamp, a\n // resize whose re-bake resolved), and at appearance-scale crossings. A move\n // or a turn leaves the epoch as it is (the blit repositions the same\n // pixels), and live gestures don't touch it either: no mid-drag fetches.\n const bakedKey = useSelector(AnnotationHostToken, (annotation) =>\n annotation.getAppearanceEpoch(page.ref),\n );\n // The appearance scale follows the document's render policy: zoom ticks\n // inside an appearance-lattice rung re-bake nothing; crossing 1→2 re-bakes\n // once. Without the render plugin, only this session's pictures load.\n const bakeScale = useOptionalSelector(\n RenderToken,\n (render) => render.getAppearanceScale(page.transform.renderScale),\n page.transform.renderScale,\n );\n\n useEffect(\n () =>\n loadAppearanceUrls(\n shown,\n (signal) => anno.renderAppearances(page.ref, bakeScale, signal),\n annotationKey,\n setUrls,\n ),\n [shown, anno, page.ref, bakeScale, bakedKey],\n );\n\n /** The text box being typed in, by key. */\n const editingKey = editingTextKeyOf(texts, annotationKey);\n\n return (\n <div style={{ position: 'absolute', inset: 0, pointerEvents: 'none' }}>\n {items.map((item) => {\n // The default look, inside the item's frame: the engine's raster where\n // the core places it (following a move as it happens), or the scene.\n const state = item.annotation?.appearanceState ?? null;\n const shows = pictureOrPainting({\n pending: item.source !== 'baked',\n picture: urls && shownAppearance(urls, item.id, state),\n version: item.pictureVersion ?? 0,\n loaded: urls !== null,\n paint: () => (hasLiveDrawing(item) ? item : null),\n });\n const native: React.ReactNode =\n shows?.kind === 'painting' ? (\n // shapes, cloudy, markup: all painted via scene()\n <Shape item={item} />\n ) : shows?.kind === 'picture' && item.raster ? (\n <BakedImage url={shows.picture.url} box={rasterInFrame(item.raster, item.frame)} />\n ) : null;\n const framed = (\n content: React.ReactNode,\n options?: { interactive?: boolean; inert?: boolean },\n ) => (\n <AnnotationFrame key={item.id} item={item} page={page} {...options}>\n {content}\n </AnnotationFrame>\n );\n const annotation = item.annotation;\n if (!annotation) return framed(native);\n /** Your look in the frame: drawn at the annotation's 100% size and scaled with the page, unless it opts out. */\n const look = (\n entry: Extract<AnnotationRenderer, { for: unknown }>,\n interactive: boolean,\n ): React.ReactNode => {\n const pixels = frameInPixels(item.frame, page.transform);\n const scaled = entry.scale !== false;\n const Look = entry.component;\n const drawn = (\n <Look\n annotation={annotation}\n frame={lookFrameOf(pixels, scaled)}\n native={native}\n appearance={urls && bakedAppearanceOf(urls, item.id, state)}\n hovered={item.hovered ?? false}\n selected={item.selected}\n interactive={interactive}\n />\n );\n if (!scaled) return drawn;\n return (\n <div\n style={{\n position: 'absolute',\n left: 0,\n top: 0,\n width: pixels.design.width,\n height: pixels.design.height,\n transform: `scale(${pixels.scale})`,\n transformOrigin: '0 0',\n }}\n >\n <LookScaleContext.Provider value={pixels.scale}>{drawn}</LookScaleContext.Provider>\n </div>\n );\n };\n\n // Ownership beats looks: an engaged behavior's renderer is\n // authoritative (form fill controls own their DOM); `for` rules apply\n // only to what the layer owns, and draw without the pointer. While its\n // text box is typed in, a look's editor (`useRichTextEditor`) takes\n // the keys: an inert subtree can't.\n const typing = editingKey !== null && annotationKey(annotation.ref) === editingKey;\n const drawing = annotationDrawingOf(annotation, anno, renderers, typing);\n switch (drawing.kind) {\n case 'owned': {\n // Its controls place themselves in the layer; the native drawing keeps its frame.\n const Owner = drawing.entry.component;\n return (\n <Owner\n key={item.id}\n annotation={annotation}\n item={item}\n page={page}\n native={framed(native)}\n hovered={item.hovered ?? false}\n selected={item.selected}\n interactive\n />\n );\n }\n case 'look':\n return framed(look(drawing.entry, drawing.interactive), {\n interactive: drawing.interactive,\n inert: drawing.inert,\n });\n case 'native':\n // Inert when an engaged behavior has no renderer of yours: its\n // plugin owns the input (a link's anchor takes the click), and the\n // annotation keeps its own look.\n return framed(native, { inert: drawing.inert });\n }\n })}\n {/* A text box your renderer draws is edited there (`useRichTextEditor`). */}\n {layerTextBoxesOf(texts, items, renderers, annotationKey).map((text) => (\n <FreeText key={text.id} item={text} page={page} />\n ))}\n <ToolGhostImage page={page} />\n <Chrome page={page} components={components} />\n </div>\n );\n}\n\n/**\n * The default file-picker provider: the built-in file dialog (from\n * `@embedpdf/web`), honouring the tool's `accept` filter. This is the adapter\n * fulfilling the plugin's DOM-free port — the dialog lives in the framework\n * layer, never in the plugin.\n */\nexport const filePickerProvider: FilePickerProvider = pickRequestedFile;\n\n/**\n * Install the file-picker provider for the active document — the one port\n * behind every click-then-pick tool (a stamp `'prompt'` source, the file-\n * attachment tool). Call once at a document-scoped spot (not inside\n * `<AnnotationLayer>`, which is per page). Defaults to\n * {@link filePickerProvider}, so a bare `useFilePickerProvider()` makes all of\n * them work out of the box; pass a custom provider (asset library, cloud\n * drive — switch on `req.subtype` / `req.toolId`) or `null` to make\n * click-then-pick tools inert. Cleared on unmount.\n */\nexport function useFilePickerProvider(\n provider: FilePickerProvider | null = filePickerProvider,\n): void {\n const anno = useOptionalCapability(AnnotationToken);\n useEffect(() => {\n if (!anno) return;\n // One port per document: a second caller silently replaces the first.\n return installFilePickerProvider(anno, provider, () =>\n devWarn(\n 'file-picker-provider-twice',\n 'useFilePickerProvider() is called from two mounted components for the same document — ' +\n 'the later one wins. Call it once, at a document-scoped spot.',\n ),\n );\n }, [anno, provider]);\n}\n\nexport function useAnnotation(): AnnotationCapability {\n return useCapability(AnnotationToken);\n}\n\n/** Listen to one annotation event while the component is mounted: `useAnnotationEvent((annotation) => annotation.onCreated, handler)`. */\nexport function useAnnotationEvent<T>(\n select: (annotation: AnnotationCapability) => EventHook<T>,\n handler: (event: T) => void,\n): void {\n useCapabilityEvent(AnnotationToken, select, handler);\n}\n\nconst NO_ANNOTATIONS: readonly Annotation[] = [];\n\n/**\n * The annotations matching `filter` (some pages, one kind), in drawing order,\n * as the user sees them; the same array while they stay the same. Empty\n * without a document.\n */\nexport function useAnnotationList(filter?: AnnotationFilter): readonly Annotation[] {\n const key = filter\n ? `${(filter.pages ?? [])\n .map((page) => (typeof page === 'number' ? `#${page}` : page.objectNumber))\n .join(',')}|${filter.pages ? 'p' : ''}|${filter.subtype ?? ''}`\n : '';\n // Keyed by value, so an inline filter object never subscribes again.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const stable = React.useMemo(() => filter, [key]);\n return useOptionalSelector(\n AnnotationToken,\n (annotation) => annotation.list(stable),\n NO_ANNOTATIONS,\n shallowArray,\n );\n}\n\nconst NO_DEFAULTS: ToolDefaults = {};\n\n/**\n * A tool's defaults: the fields its next annotation starts with, over the\n * engine's. The component re-renders when they change, so a color picker\n * always shows the current color. Empty without a document.\n */\nexport function useAnnotationDefaults(toolId: string): ToolDefaults {\n return useOptionalSelector(\n AnnotationToken,\n (annotation) => annotation.tools.getDefaults(toolId),\n NO_DEFAULTS,\n );\n}\n\nconst NO_PROPERTIES: AnnotationProperties = { properties: [], values: {}, mixed: [] };\n\n/**\n * What a style panel shows: the selection's properties (`selection.getProperties()`),\n * or, given a tool id, the tool's (`tools.getProperties(id)`). The component\n * re-renders when they change. Empty without a document.\n */\nexport function useAnnotationProperties(toolId?: string): AnnotationProperties {\n return useOptionalSelector(\n AnnotationToken,\n (annotation) =>\n toolId === undefined\n ? annotation.selection.getProperties()\n : annotation.tools.getProperties(toolId),\n NO_PROPERTIES,\n );\n}\n\n/**\n * An anchor for `<Anchored>` that keeps a card or a badge attached to one\n * annotation: its page and the box around what it shows, following a move\n * as it happens. `null` for `null`, or an annotation that isn't here. The\n * component re-renders when the annotation moves, not while people scroll or\n * zoom: a note that keeps its size on screen hands `<Anchored>` its\n * `boundsIn`.\n */\nexport function useAnnotationAnchor(ref: AnnotationRef | null): AnnotationAnchor | null {\n const key = ref ? annotationKey(ref) : null;\n // Keyed by value, so an inline ref never subscribes again.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const stable = React.useMemo(() => ref, [key]);\n return useOptionalSelector(\n AnnotationHostToken,\n (annotation) => (stable ? annotation.getAnnotationAnchor(stable) : null),\n null,\n sameAnnotationAnchor,\n );\n}\n\n// ── Comments (the conversation plane) ────────────────────────────────────────\n\n/**\n * A {@link CommentThread} enriched with its page's live display position —\n * the framework-layer join (`enrichCommentThreads` in `@embedpdf/web`).\n * Identity stays `page` (like every annotation surface); these two fields\n * are presentation, tracking page moves and deletes.\n */\nexport interface CommentThreadView extends CommentThread {\n /** Current 0-based display index of the thread's page; `-1` when the page\n * is no longer in the document (one-frame teardown race on delete). */\n pageIndex: number;\n /** The page's `/PageLabels` label when the PDF declares one (\"iv\", \"A-2\"),\n * else the 1-based position as a string — print it verbatim. */\n pageLabel: string;\n}\n\n/** The comments API: the `comments` part of `useAnnotation()`. Pair it with\n * {@link useCommentThreads} for the threads to show. */\nexport function useComments(): CommentsApi {\n return useCapability(AnnotationToken).comments;\n}\n\nconst EMPTY_PAGES: readonly PageLayout[] = [];\nconst NO_THREADS: readonly CommentThread[] = [];\n\n/**\n * Every comment thread in the document, display-ordered (page position →\n * top of page → creation date) and enriched with `pageIndex`/`pageLabel`.\n * Subscribed to both stores: annotation writes (own, remote, hydration)\n * recompute the threads; page moves/deletes re-run the join. Reference-\n * stable between changes — safe to memo child renders on the array.\n */\nexport function useCommentThreads(): CommentThreadView[] {\n const docId = useDocumentId();\n const threads = useOptionalSelector(\n AnnotationToken,\n (annotation) => annotation.comments.listThreads(),\n NO_THREADS,\n );\n const pages = useKernelValue((kernel) =>\n docId ? kernel.documents.listPages(docId) : EMPTY_PAGES,\n );\n return React.useMemo(() => enrichCommentThreads(threads, pages), [threads, pages]);\n}\n\n/** The enriched thread containing any member ref (root, reply, grouped part,\n * state annotation), or null. */\nexport function useCommentThread(ref: AnnotationRef | null): CommentThreadView | null {\n const views = useCommentThreads();\n const api = useOptionalCapability(AnnotationToken);\n if (ref == null || !api) return null;\n const thread = api.comments.getThread(ref);\n if (!thread) return null;\n return (\n views.find((view) => annotationKey(view.root.ref) === annotationKey(thread.root.ref)) ?? null\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAeA,MAAa,qBAAqBA,cAAAA,UAAUC,qCAAAA,eAAe;;;;;;AAO3D,MAAa,wBAAwBC,cAAAA,aAAaC,qCAAAA,eAAe;;;;;;;;;;;;;;;;;;;;;;;ACqBjE,SAAgB,eAAe,EAAE,UAAU,MAAM,IAAI,YAAY,SAA8B;CAM7F,MAAM,EAAE,cAAcC,iBAAAA,oBAAoB;CAC1C,MAAM,SAASC,gBAAAA,oBACbC,0CAAAA,kBACC,eAAe;EACd,MAAM,kBAAkB,WAAW,UAAU,UAAU;EACvD,IAAI,CAAC,iBAAiB,OAAO;EAC7B,MAAM,MAAM,UAAU,QAAQ,gBAAgB,IAAI;EAClD,OAAO,MAAM,WAAW,qBAAqB,GAAG,IAAI;CACtD,GACA,MACAC,cAAAA,mBACF;CACA,IAAI,CAAC,QAAQ,OAAO;CACpB,OACE,iBAAA,GAAA,kBAAA,IAAA,CAACC,iBAAAA,UAAD;EACE,QAAQ;GACN,MAAM,OAAO;GACb,QAAQ,OAAO;GACf,GAAI,OAAO,iBAAiB,EAAE,OAAO,CAAC,OAAO,cAAc,EAAE,IAAI,CAAC;EACpE;EACW;EACN;EAEJ;CACO,CAAA;AAEd;;AAcA,SAAgB,oBAAoB,EAClC,UACA,MAAM,GACN,YAAY,SACe;CAC3B,MAAM,SAASH,gBAAAA,oBACbI,qCAAAA,kBACC,eAAe,WAAW,MAAM,IAAI,GACrC,MACAC,cAAAA,uBACF;CACA,IAAI,CAAC,QAAQ,OAAO;CACpB,OACE,iBAAA,GAAA,kBAAA,IAAA,CAACF,iBAAAA,UAAD;EAAkB;EAAmB;EAAgB;EAClD,UAAA,SAAS,MAAM;CACR,CAAA;AAEd;;;;;;AAkBA,SAAgB,wBAAwB,EACtC,UACA,MAAM,IACN,YAAY,WACmB;CAC/B,MAAM,WAAWH,gBAAAA,oBACfI,qCAAAA,kBACC,eAAe,WAAW,UAAU,kBAAkB,GACvD,MACAE,cAAAA,kBACF;CACA,MAAM,UAAU,uBAAuB,aAAa,SAAS,OAAO,OAAO;CAC3E,IAAI,CAAC,YAAa,CAAC,YAAY,CAAC,QAAQ,SAAU,OAAO;CACzD,OACE,iBAAA,GAAA,kBAAA,IAAA,CAACH,iBAAAA,UAAD;EACE,QAAQ;GAAE,MAAM,SAAS;GAAM,QAAQ;IAAE,GAAG,SAAS;IAAI,OAAO;IAAG,QAAQ;GAAE;EAAE;EACpE;EACN;EAEJ,UAAA,WACC,SAAS,QAAQ,IAEjB,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;GACE,OAAO;IACL,eAAe;IACf,YAAY;IACZ,cAAc;IACd,SAAS;IACT,aAAA,GAAA,cAAA,aAAA,CAAyB,WAAW;IACpC,UAAU;IACV,aAAA,GAAA,cAAA,MAAA,CAAkB,sBAAsB,QAAQ,UAAU;IAC1D,QAAA,GAAA,cAAA,MAAA,CAAa,iBAAiB,QAAQ,KAAK;GAC7C;GAVF,UAAA,CAYG,SAAS,OAAM,GACb;;CAEC,CAAA;AAEd;;;;AC4GA,MAAM,mBAAmBI,MAAM,cAAc,CAAC;;;;;;AAO9C,SAAS,gBAAgB,EACvB,MACA,MACA,cAAc,OACd,QAAQ,OACR,YAOC;CACD,MAAM,OAAA,GAAA,cAAA,cAAA,CAAoB,KAAK,OAAO,KAAK,SAAS;CACpD,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EACE,GAAK,QAAQC,cAAAA,QAAQ,CAAC;EACtB,OAAO;GACL,UAAU;GACV,MAAM,IAAI;GACV,KAAK,IAAI;GACT,OAAO,IAAI;GACX,QAAQ,IAAI;GACZ,WAAW,IAAI;GACf,iBAAiB;GAGjB,cAAc,KAAK;GAEnB,eAAe,cAAc,SAAS;EACxC;EAEC;CACE,CAAA;AAET;;;;;;;;AASA,SAAS,MAAM,EAAE,QAA8B;CAE7C,MAAM,WAAA,GAAA,cAAA,aAAA,CAAuB,KAAK,GAAG;CACrC,IAAI,CAAC,SAAS,OAAO;CACrB,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EACW;EACT,OAAO;GACL,UAAU;GACV,MAAM;GACN,KAAK;GACL,OAAO;GACP,QAAQ;GACR,UAAU;GACV,eAAe;GAEf,GAAI,KAAK,WAAW,UAAU,EAAE,UAAA,GAAA,cAAA,aAAA,CAAsB,KAAK,gBAAgB,EAAG,EAAE,IAAI,CAAC;EACvF;EAEA,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAACC,mBAAAA,WAAD,EAAW,SAAA,GAAA,cAAA,YAAA,EAAA,GAAA,0BAAA,MAAA,CAA0B,IAAI,GAAG,EAAE,YAAYC,0BAAAA,YAAY,CAAC,EAAI,CAAA;CACxE,CAAA;AAET;;AAGA,SAAS,WAAW,EAAE,KAAK,OAA4C;CACrE,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EACE,KAAK;EACL,KAAI;EACJ,WAAW;EACX,OAAO;GACL,UAAU;GACV,MAAM,IAAI;GACV,KAAK,IAAI;GACT,OAAO,IAAI;GACX,QAAQ,IAAI;GAMZ,UAAU;GACV,WAAW;GACX,eAAe;GAEf,WAAW,IAAI;GACf,iBAAiB;EACnB;CACD,CAAA;AAEL;;;;;;;;;AAUA,SAAS,eAAe,EAAE,QAAoC;CAC5D,MAAM,OAAOC,gBAAAA,cAAcC,0CAAAA,eAAmB;CAC9C,MAAM,QAAQC,gBAAAA,YAAYD,0CAAAA,kBAAsB,eAC9C,WAAW,cAAc,KAAK,GAAG,CACnC;CACA,MAAM,QAAQC,gBAAAA,YAAYD,0CAAAA,kBAAsB,eAAe,WAAW,cAAc,CAAC;CACzF,MAAM,CAAC,KAAK,WAAA,GAAA,MAAA,SAAA,CAAkC,IAAI;CAIlD,MAAM,SAAS,SAAA,GAAA,0CAAA,cAAA,CAAsB,MAAM,IAAI,QAAQ,KAAK,UAAU,WAAW,IAAI;CAErF,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,IAAI,CAAC,QAAQ;GACX,OAAO,IAAI;GACX;EACF;EAGA,QAAA,GAAA,cAAA,cAAA,OAA2B,KAAK,wBAAwB,MAAM,GAAG,MAAM;CACzE,GAAG;EAAC;EAAM;EAAO;CAAM,CAAC;CAExB,IAAI,CAAC,SAAS,CAAC,KAAK,OAAO;CAC3B,MAAM,SAAA,GAAA,cAAA,aAAA,CAAqB,MAAM,KAAK,KAAK,SAAS;CACpD,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EACE,KAAK;EACL,KAAI;EACJ,WAAW;EACX,OAAO;GACL,UAAU;GACV,MAAM,MAAM;GACZ,KAAK,MAAM;GACX,OAAO,MAAM;GACb,QAAQ,MAAM;GAGd,UAAU;GACV,WAAW;GACX,eAAe;GACf,UAAA,GAAA,cAAA,aAAA,CAAsB,MAAM,OAAO;GACnC,GAAI,MAAM,MAAM;IAAE,WAAW,UAAU,MAAM,IAAI;IAAO,iBAAiB;GAAS,IAAI,CAAC;EACzF;CACD,CAAA;AAEL;;AAKA,SAAS,iBAA6E;CACpF,MAAM,SAAS,uBAAuB,aAAa,SAAS,MAAM;CAClE,MAAM,SAASE,gBAAAA,mBAAmB,aAAa,SAAS,MAAM;CAE9D,OAAO;EAAE;EAAQ,SADDP,MAAM,eAAA,GAAA,cAAA,sBAAA,CAAoC,QAAQ,MAAM,GAAG,CAAC,QAAQ,MAAM,CACnE;CAAE;AAC3B;;AAGA,SAAS,cAAc,EACrB,IACA,MACA,UACA,OACA,SAOC;CACD,IAAI,UAAU,UACZ,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;EAAQ,IAAI,GAAG;EAAG,IAAI,GAAG;EAAG,GAAG,OAAO;EAAG,aAAa;EAAY;CAAQ,CAAA;CAEnF,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;EACE,GAAG,GAAG,IAAI,OAAO;EACjB,GAAG,GAAG,IAAI,OAAO;EACjB,OAAO;EACP,QAAQ;EACR,aAAa;EACN;EAEP,GAAK,WAAW,EAAE,WAAW,UAAU,SAAS,GAAG,GAAG,EAAE,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC;CACzE,CAAA;AAEL;AAEA,SAAS,OAAO,EACd,MACA,cAIC;CAID,MAAM,QAAQ,KAAK,UAAU;CAC7B,MAAM,WAAW,KAAK,UAAU;CAChC,MAAM,OAAO,KAAK,UAAU;CAC5B,MAAM,QAAQM,gBAAAA,YACZD,0CAAAA,kBACC,eAAe,WAAW,gBAAgB,KAAK,KAAK,OAAO,UAAU,IAAI,GAC1EG,gBAAAA,YACF;CACA,MAAM,EAAE,QAAQ,YAAY,eAAe;CAC3C,MAAM,SAAS,YAAY;CAC3B,MAAM,iBAAiB,YAAY;CAEnC,MAAM,SAA4B,CAAC;CAyHnC,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAA,kBAAA,UAAA,EAAA,UAAA,CACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EAAK,OAAO;GAAE,UAAU;GAAY,OAAO;GAAG,UAAU;GAAW,eAAe;EAAO;EA1HlE,WAAA,GAAA,cAAA,eAAA,CAAA,OAAO,KAAK,SAAS,CAAC,CAAC,KAAK,MAAM,MAAM;GACjE,QAAQ,KAAK,MAAb;IACE,KAAK;KACH,IAAI,QAAQ;MACV,OAAO,KACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;OAEE,IAAI,KAAK;OACT,MAAM,OAAO,QAAQ;OACrB,UAAU,KAAK;OACf,MAAM,KAAK;OACX,QAAQ,KAAK;MACd,GANM,CAMN,CACH;MACA,OAAO;KACT;KACA,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,eAAD;MAEE,IAAI,KAAK;MACT,MAAM,OAAO,QAAQ;MACrB,UAAU,KAAK;MACf,OAAO,OAAO,QAAQ;MACtB,OAAO,QAAQ;KAChB,GANM,CAMN;IAEL,KAAK,SACH,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;KAEE,IAAI,KAAK,KAAK;KACd,IAAI,KAAK,KAAK;KACd,IAAI,KAAK,GAAG;KACZ,IAAI,KAAK,GAAG;KACZ,gBAAe;KACf,OAAO,QAAQ;IAChB,GAPM,CAON;IAEL,KAAK,kBACH,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAC,WAAD;KAAiB,QAAQ,KAAK;KAAQ,MAAK;KAAO,OAAO,QAAQ;IAAU,GAA7D,CAA6D;IACpF,KAAK,mBACH,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,KAAD,EAAA,UACG,KAAK,MAAM,KAAK,MAAM,MACrB,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;KAEE,IAAI,KAAK,KAAK;KACd,IAAI,KAAK,KAAK;KACd,IAAI,KAAK,GAAG;KACZ,IAAI,KAAK,GAAG;KACZ,SAAS,KAAK;KACd,OAAO,QAAQ;IAChB,GAPM,CAON,CACF,EACA,GAZK,CAYL;IAEP,KAAK;KACH,IAAI,gBAAgB;MAClB,OAAO,KACL,iBAAA,GAAA,kBAAA,IAAA,CAAC,gBAAD;OAEE,IAAI,KAAK;OACT,MAAM,KAAK;OACX,MAAM,OAAO,eAAe;OAC5B,UAAU;OACV,QAAQ;MACT,GANM,CAMN,CACH;MACA,OAAO;KACT;KACA,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,KAAD,EAAA,UAAA,CACG,OAAO,eAAe,SACrB,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;MACE,IAAI,KAAK,KAAK;MACd,IAAI,KAAK,KAAK;MACd,IAAI,KAAK,GAAG;MACZ,IAAI,KAAK,GAAG;MACZ,aAAa;MACb,OAAO,EAAE,QAAQ,QAAQ,eAAe,OAAO;KAChD,CAAA,GAEH,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;MACE,IAAI,KAAK,GAAG;MACZ,IAAI,KAAK,GAAG;MACZ,GAAG,OAAO,eAAe,OAAO;MAChC,aAAa;MACb,OAAO,QAAQ;KAChB,CAAA,CACA,EAAA,GAlBK,CAkBL;IAIP,KAAK,WACH,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;KAEE,GAAG,KAAK,IAAI;KACZ,GAAG,KAAK,IAAI;KACZ,OAAO,KAAK,IAAI;KAChB,QAAQ,KAAK,IAAI;KACjB,aAAa;KACb,iBAAgB;KAChB,OAAO,QAAQ;IAChB,GARM,CAQN;IAEL,KAAK,WACH,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;KAEE,GAAG,KAAK,IAAI;KACZ,GAAG,KAAK,IAAI;KACZ,OAAO,KAAK,IAAI;KAChB,QAAQ,KAAK,IAAI;KACjB,MAAK;KACL,OAAO,QAAQ;IAChB,GAPM,CAON;GAEP;EACF,CAIS;CACA,CAAA,GACJ,OAAO,SAAS,KACf,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EAAK,OAAO;GAAE,UAAU;GAAY,OAAO;GAAG,eAAe;EAAO;EAAI,UAAA;CAAY,CAAA,CAEtF,EAAA,CAAA;AAEN;;;;;;;;AAWA,SAAS,iBACP,MACA,OACuC;CACvC,MAAM,OAAOJ,gBAAAA,cAAcC,0CAAAA,eAAmB;CAC9C,MAAM,CAAC,SAAS,eAAA,GAAA,MAAA,SAAA,CAA2C,IAAI;CAC/D,MAAM,CAAC,aAAA,GAAA,MAAA,SAAA,QAAA,GAAA,cAAA,4BAAA,CAAsE,CAAC;CAE9E,CAAA,GAAA,MAAA,UAAA,OACQ,SAAS,OAAO,SAAS,MAAM,MAAM,KAAK,GAChD;EAAC;EAAU;EAAM;EAAS;EAAM;CAAK,CACvC;CACA,CAAA,GAAA,MAAA,UAAA,aAAsB,SAAS,OAAO,GAAG,CAAC,QAAQ,CAAC;CACnD,OAAO;AACT;;AAGA,SAAS,SAAS,EAAE,MAAM,QAAoD;CAE5E,MAAM,SAAA,GAAA,cAAA,kBAAA,CAA0B,MAAM,KAAK,SAAS;CACpD,MAAM,MAAM,iBAAiB,MAAM,MAAM,KAAK;CAC9C,MAAM,EAAE,YAAY,eAAe;CACnC,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;EACO;EACL,gCAAA;EACA,OAAO;GACL,UAAU;GACV,MAAM,MAAM;GACZ,KAAK,MAAM;GACX,OAAO,MAAM;GAGb,QAAQ,MAAM;GACd,IAAA,GAAA,cAAA,eAAA,CAAkB,KAAK,KAAK,MAAM,KAAK;GACvC,WAAW;GAEX,YAAY;GACZ,YAAY;GACZ,cAAc;GACd,WAAW,KAAK,UAAU,SAAS;GACnC,WAAW;GACX,SAAS,KAAK,UAAU,aAAa,QAAQ,gBAAgB;GAC7D,QAAQ,KAAK,UAAU,SAAS;GAEhC,GAAI,KAAK,MAAM;IAAE,WAAW,UAAU,KAAK,IAAI;IAAO,iBAAiB;GAAS,IAAI,CAAC;GAErF,eAAe,KAAK,UAAU,SAAS;EACzC;CACD,CAAA;AAEL;AAYA,MAAM,WAAgC,CAAC;;;;;;;;AASvC,SAAgB,kBAAkB,YAAwC;CACxE,MAAM,OAAOI,gBAAAA,QAAQ;CACrB,MAAM,OAAO,KAAK,UAAU;CAC5B,MAAM,WAAW,KAAK,UAAU;CAChC,MAAM,OAAA,GAAA,4BAAA,cAAA,CAAoB,WAAW,GAAG;CACxC,MAAM,OAAOC,gBAAAA,oBACXL,0CAAAA,kBACC,SACC,KACG,cAAc,KAAK,KAAK;EAAE;EAAM;CAAS,CAAC,CAAC,CAC3C,MAAM,SAAS,KAAK,QAAQ,SAAA,GAAA,4BAAA,cAAA,CAAsB,KAAK,GAAG,MAAM,GAAG,KAAK,MAC7E,IACF;CAGA,MAAM,SAAA,GAAA,cAAA,qBAAA,CAA6B,MAAM,KAAK,WAAWL,MAAM,WAAW,gBAAgB,CAAC;CAU3F,OAAO;EAAE,KATG,iBAAiB,MAAM,KASxB;EAAG,OAPAA,MAAM,cAEhB,OACI;GAAE,IAAA,GAAA,cAAA,eAAA,CAAkB,KAAK,KAAK,KAAK;GAAG,GAAI,KAAK,UAAU,EAAE,eAAe,OAAO,IAAI,CAAC;EAAG,IACzF,UACN,CAAC,MAAM,KAAK,CAEI;EAAG,SAAS,MAAM,WAAW;CAAM;AACvD;;;;;;;;AAWA,SAAS,qBACP,MACA,cACA,WACM;CACN,MAAM,WAAA,GAAA,MAAA,OAAA,CAAiB,YAAY;CACnC,QAAQ,UAAU;CAClB,MAAM,GAAG,qBAAA,GAAA,MAAA,SAAA,CAA6B,CAAC;CACvC,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,IAAI,CAAC,WAAW;EAChB,MAAM,WAAA,GAAA,cAAA,0BAAA,CAAoC,MAAM,iBAAiB,QAAQ,QAAQ,CAAC;EAClF,kBAAkB,UAAU,QAAQ,CAAC;EACrC,OAAO;CACT,GAAG,CAAC,MAAM,SAAS,CAAC;AACtB;;;;;;;AAQA,SAAgB,gBAAgB,EAAE,WAAW,eAAqC,CAAC,GAAG;CACpF,MAAM,OAAOS,gBAAAA,QAAQ;CACrB,MAAM,OAAOL,gBAAAA,cAAcC,0CAAAA,eAAmB;CAG9C,gBAAA,oBAAoBM,sCAAAA,mBAAmB,gBAAgB,YAAY,gBAAgB,GAAG,EAAE;CACxF,MAAM,cAAcC,gBAAAA,sBAAsBD,sCAAAA,gBAAgB;CAK1D,MAAM,WAAW,KAAK,UAAU;CAChC,MAAM,eAAe,KAAK,UAAU;CACpC,MAAM,QAAQL,gBAAAA,YACZD,0CAAAA,kBACC,eAAe,WAAW,cAAc,KAAK,KAAK;EAAE,MAAM;EAAU,UAAU;CAAa,CAAC,GAC7FG,gBAAAA,YACF;CACA,MAAM,QAAQF,gBAAAA,YACZD,0CAAAA,kBACC,eAAe,WAAW,cAAc,KAAK,KAAK;EAAE,MAAM;EAAU,UAAU;CAAa,CAAC,GAC7FG,gBAAAA,YACF;CAEA,MAAM,CAAC,MAAM,YAAA,GAAA,MAAA,SAAA,CAA0D,IAAI;CAE3E,MAAM,CAAC,UAAA,GAAA,MAAA,SAAA,CAAkBK,cAAAA,eAAe;CACxC,CAAA,GAAA,MAAA,UAAA,aAAsB,MAAM,QAAQ,GAAG,CAAC,KAAK,CAAC;CAC9C,qBAAqB,YAAa,aAAa,gBAAgB,KAAgB,IAAI,SAAS;CAC5F,oBAAA,kBAAkB,KAAK,KAAK,aAAa;CAIzC,MAAM,qBAAA,GAAA,MAAA,OAAA,CAA2B,SAAS;CAC1C,IACE,aACA,kBAAkB,WAClB,cAAc,kBAAkB,WAChCL,gBAAAA,aAAa,WAAW,kBAAkB,OAAO,GAEjD,gBAAA,QACE,+BACA,sLAEF;CAEF,kBAAkB,UAAU;CAO5B,MAAM,WAAWF,gBAAAA,YAAYD,0CAAAA,kBAAsB,eACjD,WAAW,mBAAmB,KAAK,GAAG,CACxC;CAIA,MAAM,YAAYK,gBAAAA,oBAChBI,sCAAAA,cACC,WAAW,OAAO,mBAAmB,KAAK,UAAU,WAAW,GAChE,KAAK,UAAU,WACjB;CAEA,CAAA,GAAA,MAAA,UAAA,QAAA,GAAA,cAAA,mBAAA,CAGM,QACC,WAAW,KAAK,kBAAkB,KAAK,KAAK,WAAW,MAAM,GAC9DC,4BAAAA,eACA,OACF,GACF;EAAC;EAAO;EAAM,KAAK;EAAK;EAAW;CAAQ,CAC7C;;CAGA,MAAM,cAAA,GAAA,cAAA,iBAAA,CAA8B,OAAOA,4BAAAA,aAAa;CAExD,OACE,iBAAA,GAAA,kBAAA,KAAA,CAAC,OAAD;EAAK,OAAO;GAAE,UAAU;GAAY,OAAO;GAAG,eAAe;EAAO;EAApE,UAAA;GACG,MAAM,KAAK,SAAS;IAGnB,MAAM,QAAQ,KAAK,YAAY,mBAAmB;IAClD,MAAM,SAAA,GAAA,cAAA,kBAAA,CAA0B;KAC9B,SAAS,KAAK,WAAW;KACzB,SAAS,SAAA,GAAA,cAAA,gBAAA,CAAwB,MAAM,KAAK,IAAI,KAAK;KACrD,SAAS,KAAK,kBAAkB;KAChC,QAAQ,SAAS;KACjB,cAAA,GAAA,0BAAA,eAAA,CAA6B,IAAI,IAAI,OAAO;IAC9C,CAAC;IACD,MAAM,SACJ,OAAO,SAAS,aAEd,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD,EAAa,KAAO,CAAA,IAClB,OAAO,SAAS,aAAa,KAAK,SACpC,iBAAA,GAAA,kBAAA,IAAA,CAAC,YAAD;KAAY,KAAK,MAAM,QAAQ;KAAK,MAAA,GAAA,cAAA,cAAA,CAAmB,KAAK,QAAQ,KAAK,KAAK;IAAI,CAAA,IAChF;IACN,MAAM,UACJ,SACA,YAEA,iBAAA,GAAA,kBAAA,IAAA,CAAC,iBAAD;KAAqC;KAAY;KAAM,GAAI;KACxD,UAAA;IACc,GAFK,KAAK,EAEV;IAEnB,MAAM,aAAa,KAAK;IACxB,IAAI,CAAC,YAAY,OAAO,OAAO,MAAM;;IAErC,MAAM,QACJ,OACA,gBACoB;KACpB,MAAM,UAAA,GAAA,cAAA,cAAA,CAAuB,KAAK,OAAO,KAAK,SAAS;KACvD,MAAM,SAAS,MAAM,UAAU;KAC/B,MAAM,OAAO,MAAM;KACnB,MAAM,QACJ,iBAAA,GAAA,kBAAA,IAAA,CAAC,MAAD;MACc;MACZ,QAAA,GAAA,cAAA,YAAA,CAAmB,QAAQ,MAAM;MACzB;MACR,YAAY,SAAA,GAAA,cAAA,kBAAA,CAA0B,MAAM,KAAK,IAAI,KAAK;MAC1D,SAAS,KAAK,WAAW;MACzB,UAAU,KAAK;MACF;KACd,CAAA;KAEH,IAAI,CAAC,QAAQ,OAAO;KACpB,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;MACE,OAAO;OACL,UAAU;OACV,MAAM;OACN,KAAK;OACL,OAAO,OAAO,OAAO;OACrB,QAAQ,OAAO,OAAO;OACtB,WAAW,SAAS,OAAO,MAAM;OACjC,iBAAiB;MACnB;MAEA,UAAA,iBAAA,GAAA,kBAAA,IAAA,CAAC,iBAAiB,UAAlB;OAA2B,OAAO,OAAO;OAAQ,UAAA;MAAiC,CAAA;KAC/E,CAAA;IAET;IAOA,MAAM,SAAS,eAAe,SAAA,GAAA,4BAAA,cAAA,CAAsB,WAAW,GAAG,MAAM;IACxE,MAAM,WAAA,GAAA,cAAA,oBAAA,CAA8B,YAAY,MAAM,WAAW,MAAM;IACvE,QAAQ,QAAQ,MAAhB;KACE,KAAK,SAAS;MAEZ,MAAM,QAAQ,QAAQ,MAAM;MAC5B,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,OAAD;OAEc;OACN;OACA;OACN,QAAQ,OAAO,MAAM;OACrB,SAAS,KAAK,WAAW;OACzB,UAAU,KAAK;OACf,aAAA;MACD,GARM,KAAK,EAQX;KAEL;KACA,KAAK,QACH,OAAO,OAAO,KAAK,QAAQ,OAAO,QAAQ,WAAW,GAAG;MACtD,aAAa,QAAQ;MACrB,OAAO,QAAQ;KACjB,CAAC;KACH,KAAK,UAIH,OAAO,OAAO,QAAQ,EAAE,OAAO,QAAQ,MAAM,CAAC;IAClD;GACF,CAAC;IAEiB,GAAA,cAAA,iBAAA,CAAA,OAAO,OAAO,WAAWA,4BAAAA,aAAa,CAAC,CAAC,KAAK,SAC7D,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAD;IAAwB,MAAM;IAAY;GAAO,GAAlC,KAAK,EAA6B,CAClD;GACD,iBAAA,GAAA,kBAAA,IAAA,CAAC,gBAAD,EAAsB,KAAO,CAAA;GAC7B,iBAAA,GAAA,kBAAA,IAAA,CAAC,QAAD;IAAc;IAAkB;GAAa,CAAA;EAC1C;;AAET;;;;;;;AAQA,MAAa,qBAAyCC,cAAAA;;;;;;;;;;;AAYtD,SAAgB,sBACd,WAAsC,oBAChC;CACN,MAAM,OAAOJ,gBAAAA,sBAAsBK,4BAAAA,eAAe;CAClD,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,IAAI,CAAC,MAAM;EAEX,QAAA,GAAA,cAAA,0BAAA,CAAiC,MAAM,gBACrCC,gBAAAA,QACE,8BACA,oJAEF,CACF;CACF,GAAG,CAAC,MAAM,QAAQ,CAAC;AACrB;AAEA,SAAgB,gBAAsC;CACpD,OAAOd,gBAAAA,cAAca,4BAAAA,eAAe;AACtC;;AAGA,SAAgB,mBACd,QACA,SACM;CACN,gBAAA,mBAAmBA,4BAAAA,iBAAiB,QAAQ,OAAO;AACrD;AAEA,MAAM,iBAAwC,CAAC;;;;;;AAO/C,SAAgB,kBAAkB,QAAkD;CAClF,MAAM,MAAM,SACR,IAAI,OAAO,SAAS,CAAC,EAAA,CAClB,KAAK,SAAU,OAAO,SAAS,WAAW,IAAI,SAAS,KAAK,YAAa,CAAC,CAC1E,KAAK,GAAG,EAAE,GAAG,OAAO,QAAQ,MAAM,GAAG,GAAG,OAAO,WAAW,OAC7D;CAGJ,MAAM,SAASjB,MAAM,cAAc,QAAQ,CAAC,GAAG,CAAC;CAChD,OAAOU,gBAAAA,oBACLO,4BAAAA,kBACC,eAAe,WAAW,KAAK,MAAM,GACtC,gBACAT,gBAAAA,YACF;AACF;AAEA,MAAM,cAA4B,CAAC;;;;;;AAOnC,SAAgB,sBAAsB,QAA8B;CAClE,OAAOE,gBAAAA,oBACLO,4BAAAA,kBACC,eAAe,WAAW,MAAM,YAAY,MAAM,GACnD,WACF;AACF;AAEA,MAAM,gBAAsC;CAAE,YAAY,CAAC;CAAG,QAAQ,CAAC;CAAG,OAAO,CAAC;AAAE;;;;;;AAOpF,SAAgB,wBAAwB,QAAuC;CAC7E,OAAOP,gBAAAA,oBACLO,4BAAAA,kBACC,eACC,WAAW,KAAA,IACP,WAAW,UAAU,cAAc,IACnC,WAAW,MAAM,cAAc,MAAM,GAC3C,aACF;AACF;;;;;;;;;AAUA,SAAgB,oBAAoB,KAAoD;CACtF,MAAM,MAAM,OAAA,GAAA,4BAAA,cAAA,CAAoB,GAAG,IAAI;CAGvC,MAAM,SAASjB,MAAM,cAAc,KAAK,CAAC,GAAG,CAAC;CAC7C,OAAOU,gBAAAA,oBACLL,0CAAAA,kBACC,eAAgB,SAAS,WAAW,oBAAoB,MAAM,IAAI,MACnE,MACAc,cAAAA,oBACF;AACF;;;AAqBA,SAAgB,cAA2B;CACzC,OAAOf,gBAAAA,cAAca,4BAAAA,eAAe,CAAC,CAAC;AACxC;AAEA,MAAM,cAAqC,CAAC;AAC5C,MAAM,aAAuC,CAAC;;;;;;;;AAS9C,SAAgB,oBAAyC;CACvD,MAAM,QAAQG,gBAAAA,cAAc;CAC5B,MAAM,UAAUV,gBAAAA,oBACdO,4BAAAA,kBACC,eAAe,WAAW,SAAS,YAAY,GAChD,UACF;CACA,MAAM,QAAQI,gBAAAA,gBAAgB,WAC5B,QAAQ,OAAO,UAAU,UAAU,KAAK,IAAI,WAC9C;CACA,OAAOrB,MAAM,eAAA,GAAA,cAAA,qBAAA,CAAmC,SAAS,KAAK,GAAG,CAAC,SAAS,KAAK,CAAC;AACnF;;;AAIA,SAAgB,iBAAiB,KAAqD;CACpF,MAAM,QAAQ,kBAAkB;CAChC,MAAM,MAAMY,gBAAAA,sBAAsBK,4BAAAA,eAAe;CACjD,IAAI,OAAO,QAAQ,CAAC,KAAK,OAAO;CAChC,MAAM,SAAS,IAAI,SAAS,UAAU,GAAG;CACzC,IAAI,CAAC,QAAQ,OAAO;CACpB,OACE,MAAM,MAAM,UAAA,GAAA,4BAAA,cAAA,CAAuB,KAAK,KAAK,GAAG,OAAA,GAAA,4BAAA,cAAA,CAAoB,OAAO,KAAK,GAAG,CAAC,KAAK;AAE7F"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-B6xcPsEb.js","names":[],"sources":["../src/theme.ts","../src/dev.ts","../src/runtime.tsx"],"sourcesContent":["/**\n * `epdfTheme()`: a theme written with setting names, as a style for the\n * element around your viewer. The variables and their lookup are framework-free\n * (`@embedpdf/web`); this only types the result as React's style.\n */\nimport type * as React from 'react';\nimport { epdfThemeVariables, type EpdfTheme } from '@embedpdf/web';\n\nexport type { EpdfTheme } from '@embedpdf/web';\n\n/**\n * The CSS variables of a theme, for a `style` prop: `<div style={epdfTheme({ accent })}>`.\n * Everything inside the element follows them, and a CSS variable wins over any setting.\n */\nexport function epdfTheme(theme: EpdfTheme): React.CSSProperties {\n return epdfThemeVariables(theme) as React.CSSProperties;\n}\n","/**\n * Development-time guardrails. Every warning fires once per process (keyed),\n * says what is wrong and what to do instead, and compiles out of production\n * builds ({@link isDev}).\n */\nconst warned = new Set<string>();\n\n/**\n * Whether this is a development build, as the app's bundler says: every bundler replaces\n * `process.env.NODE_ENV`, with `\"production\"` in a production build, so there this is `false`\n * and the warnings behind it compile out. A page with no bundler has no `process` at all, and\n * counts as development: the warnings help most while you try the package out.\n */\nexport function isDev(): boolean {\n try {\n return process.env.NODE_ENV !== 'production';\n } catch {\n return true;\n }\n}\n\n/** Warn once per key. No-op in production. */\nexport function devWarn(key: string, message: string): void {\n if (!isDev() || warned.has(key)) return;\n warned.add(key);\n console.warn(`[embedpdf] ${message}`);\n}\n\n/** Test seam: forget every warning so a suite can assert one fires again. */\nexport function resetDevWarnings(): void {\n warned.clear();\n}\n","/**\n * @embedpdf/react — the generic binding.\n *\n * Binds the kernel's one change stream to React (useSyncExternalStore), resolves\n * capabilities (document-scoped ones against the active or `<DocumentScope>`-given\n * document, with a stand-in while there is none), and provides the page\n * coordinate context. Every plugin and layer rides on this — there is no\n * per-plugin framework code.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/core';\n// What a page with a viewer needs from the browser: handing bytes to the user as a download,\n// and a font the engine draws with, for text you draw yourself.\nexport { mountWebFont, saveFile } from '@embedpdf/web';\n// The page context every layer draws through; its conversions are shared with every framework.\nexport { makePageContext } from '@embedpdf/web';\n// A theme written with setting names, as the style for the element around a viewer.\nexport { epdfTheme } from './theme';\nexport type { EpdfTheme } from './theme';\nimport * as React from 'react';\nimport {\n createContext,\n forwardRef,\n useContext,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport {\n DocumentsToken,\n createKernel,\n documentState,\n documentsState,\n isLocalEngine,\n shallowEqual,\n standInFor,\n viewerSettingsOf,\n} from '@embedpdf/core';\nimport type {\n AnyPlugin,\n CapabilityToken,\n DocumentInfo,\n DocumentsCapability,\n DocumentsState,\n Engine,\n EngineFactory,\n EventHook,\n FailedDocumentInfo,\n Identity,\n InitialDocument,\n Kernel,\n LockedDocumentInfo,\n PageInfo,\n ViewerPageSettings,\n ViewerSettings,\n} from '@embedpdf/core';\n// Pure coordinate math from the geometry base — not from stage-core. The\n// PageContext seam stays stage-agnostic (it must also serve standalone PageView).\nimport type { PageTransform } from '@embedpdf/core-geometry';\nimport type { PageViewDemand } from '@embedpdf/plugin-render/contract';\nimport { browserClock } from '@embedpdf/web';\nimport type { PageContext } from '@embedpdf/web';\n\nimport { isDev } from './dev';\n\nconst KernelCtx = createContext<Kernel | null>(null);\n/** The document a subtree is bound to. null => use the active document. */\nconst DocumentScopeCtx = createContext<string | null>(null);\n\nexport function useKernel(): Kernel {\n const kernel = useContext(KernelCtx);\n if (!kernel) throw new Error('useKernel must be used within <Viewer>/<EmbedPDF>');\n return kernel;\n}\n\nexport interface KernelProviderProps {\n kernel: Kernel;\n children?: React.ReactNode;\n}\n/**\n * Give a subtree a kernel someone else created and owns, so every hook in it\n * works against that kernel. The full viewer's wrapper puts the children it\n * slots into the viewer inside one. The provider never starts or destroys the\n * kernel; `<Viewer>` is the component that owns one.\n */\nexport function KernelProvider({ kernel, children }: KernelProviderProps) {\n return <KernelCtx.Provider value={kernel}>{children}</KernelCtx.Provider>;\n}\n\nexport const shallowArray = <T,>(left: readonly T[], right: readonly T[]): boolean =>\n left === right || (left.length === right.length && left.every((item, i) => item === right[i]));\n\n/** Read a value derived from the kernel, cached by equality (no tearing loop). */\nexport function useKernelValue<R>(\n select: (kernel: Kernel) => R,\n isEqual: (left: R, right: R) => boolean = Object.is,\n): R {\n const kernel = useKernel();\n const last = useRef<{ v: R } | null>(null);\n const get = () => {\n const next = select(kernel);\n if (last.current && isEqual(last.current.v, next)) return last.current.v;\n last.current = { v: next };\n return next;\n };\n return useSyncExternalStore(kernel.subscribe, get, get);\n}\n\nexport function useActiveDocumentId(): string | null {\n return useKernelValue((kernel) => kernel.documents.getActiveId());\n}\n\n/** The document id for this subtree: the nearest <DocumentScope>, else the active doc. */\nexport function useDocumentId(): string | null {\n const scoped = useDocumentScope();\n const active = useActiveDocumentId();\n return scoped ?? active;\n}\n\n/**\n * The document the nearest <DocumentScope> binds this subtree to, or null when\n * it follows the active document. For resolvers that pass it to\n * `kernel.tryCapability`, which reads the active document at call time; UI\n * that needs the id itself uses {@link useDocumentId}.\n */\nexport function useDocumentScope(): string | null {\n return useContext(DocumentScopeCtx);\n}\n\nexport interface DocumentScopeProps {\n id: string;\n children: React.ReactNode;\n}\n/** Bind a subtree to a specific document (panes, comparison). */\nexport function DocumentScope({ id, children }: DocumentScopeProps) {\n return <DocumentScopeCtx.Provider value={id}>{children}</DocumentScopeCtx.Provider>;\n}\n\nexport interface DocumentGateProps {\n /** Shown while this subtree has no document: none is open, or it's still opening. */\n fallback?: React.ReactNode;\n /** Shown while the document waits for its password; `fallback` without it. */\n locked?: (document: LockedDocumentInfo) => React.ReactNode;\n /** Shown when the document couldn't be opened, with why; `fallback` without it. */\n error?: (document: FailedDocumentInfo) => React.ReactNode;\n children?: React.ReactNode;\n}\n/**\n * Render children only while this subtree has a ready document — the\n * structural way to say \"this UI is defined over a document\". An empty\n * workspace is a legitimate, designable state (the Viewer no longer blocks on\n * documents so chrome can render at t≈0): workspace-scoped UI (toolbars,\n * commands, i18n) lives outside the gate; document-scoped UI (Stage, panels,\n * page chrome) lives inside it. A document that waits for its password, or\n * failed, renders `locked` or `error` with it, or else `fallback`. Sibling of\n * <DocumentScope>, which picks which document; this one handles whether.\n */\nexport function DocumentGate({ fallback = null, locked, error, children }: DocumentGateProps) {\n const document = useDocument();\n if (document.status === 'ready') return <>{children}</>;\n if (document.status === 'locked' && locked) {\n return <>{locked(document as LockedDocumentInfo)}</>;\n }\n if (document.status === 'error' && error) return <>{error(document as FailedDocumentInfo)}</>;\n return <>{fallback}</>;\n}\n\n/**\n * Resolve a capability by token, binding document-scoped ones to this\n * subtree's document. Resolution is a reactive read (`tryCapability` through\n * the kernel's one change stream), not a memoized call — under the\n * request-time lifecycle a document can become resolvable while its id stays\n * the same, so any id-keyed cache goes stale; subscribing makes staleness\n * structurally impossible.\n *\n * Outside a document (none open, not ready yet, or none in scope), a document\n * plugin resolves to its stand-in (`standInFor` from `@embedpdf/core`):\n * rendering never fails, and a method called too early refuses with\n * `not-ready` (a method that returns a promise rejects). A token no plugin\n * provides still throws the kernel's reason, because that is a setup mistake.\n */\nexport function useCapability<T>(token: CapabilityToken<T>): T {\n const kernel = useKernel();\n const capability = useOptionalCapability(token);\n if (capability) return capability;\n if (kernel.scopeOf(token) === 'document') return standInFor(kernel, token);\n return kernel.capability(token);\n}\n\n/** Like `useCapability`, but null while the token can't resolve (no plugin,\n * no document, or a document that isn't ready yet). */\nexport function useOptionalCapability<T>(token: CapabilityToken<T>): T | null {\n const scoped = useDocumentScope();\n return useKernelValue((kernel) => kernel.tryCapability(token, scoped ?? undefined));\n}\n\n/**\n * Subscribe to a selector over a (document-resolved) capability. Strict:\n * while the token can't resolve, it throws the kernel's reason (`no\n * document`, `document is loading`), for code that knows a document exists,\n * such as anything inside a <DocumentGate>. `useOptionalSelector` is its\n * total twin.\n */\nexport function useSelector<C, R>(\n token: CapabilityToken<C>,\n select: (capability: C) => R,\n isEqual: (left: R, right: R) => boolean = Object.is,\n): R {\n const kernel = useKernel();\n const scoped = useDocumentScope();\n const capability = useOptionalCapability(token) ?? kernel.capability(token, scoped ?? undefined);\n const last = useRef<{ v: R } | null>(null);\n const get = () => {\n const next = select(capability);\n if (last.current && isEqual(last.current.v, next)) return last.current.v;\n last.current = { v: next };\n return next;\n };\n return useSyncExternalStore(kernel.subscribe, get, get);\n}\n\n/**\n * Null-safe `useSelector`: `fallback` whenever the token can't resolve — no\n * provider, or a document-scoped token with no document. For chrome that stays\n * mounted across the empty-workspace state (a zoom readout, a mode band).\n *\n * The `select` guard also swallows reads through a capability whose document\n * closed between the store notification and this render — that teardown race\n * resolves to `fallback` for one frame, then re-renders against the new state.\n */\nexport function useOptionalSelector<C, R>(\n token: CapabilityToken<C>,\n select: (capability: C) => R,\n fallback: R,\n isEqual: (left: R, right: R) => boolean = Object.is,\n): R {\n const kernel = useKernel();\n const capability = useOptionalCapability(token);\n const last = useRef<{ v: R } | null>(null);\n const get = () => {\n let next: R;\n if (capability === null) {\n next = fallback;\n } else {\n try {\n next = select(capability);\n } catch {\n next = fallback;\n }\n }\n if (last.current && isEqual(last.current.v, next)) return last.current.v;\n last.current = { v: next };\n return next;\n };\n return useSyncExternalStore(kernel.subscribe, get, get);\n}\n\n/**\n * Subscribe to a capability's {@link EventHook} for the mounted lifetime —\n * `useCapabilityEvent(ActionsToken, (actions) => actions.onExecuted, handler)`. Events\n * carry occurrences, never state (a late subscriber that needs the current\n * value uses `useSelector`). The handler rides a ref, so a fresh closure per\n * render never resubscribes. Null-safe: no plugin/document → no subscription.\n */\nexport function useCapabilityEvent<C, T>(\n token: CapabilityToken<C>,\n select: (capability: C) => EventHook<T>,\n handler: (event: T) => void,\n): void {\n const capability = useOptionalCapability(token);\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n const selectRef = useRef(select);\n selectRef.current = select;\n useEffect(() => {\n if (!capability) return;\n return selectRef.current(capability)((event) => handlerRef.current(event));\n }, [capability]);\n}\n\n/**\n * The documents API: open, close, unlock, download, and the tabs. Inside a\n * <DocumentScope>, the calls that leave out the document use that one, so\n * `download()` without an id downloads the document in scope. The object\n * never changes, so it's safe in effects and callbacks. What to show comes\n * from `useDocument()` and `useDocumentsState()`.\n */\nexport function useDocuments(): DocumentsCapability {\n return useCapability(DocumentsToken);\n}\n\n/** Read a state declaration's value through the kernel, or the value `select` picks from it. */\nfunction useDeclared<Capability, State extends object, Selected>(\n token: CapabilityToken<Capability>,\n read: (capability: Capability) => State,\n empty: State,\n select: ((state: State) => Selected) | undefined,\n): Selected {\n const scoped = useDocumentScope();\n return useKernelValue((kernel) => {\n const capability = kernel.tryCapability(token, scoped ?? undefined);\n const state = capability ? read(capability) : empty;\n return select ? select(state) : (state as unknown as Selected);\n }, shallowEqual);\n}\n\n/**\n * The document this component talks to: the one its <DocumentScope> names, or\n * else the active one. Its `id`, `name`, `status`, `pageCount`,\n * `hasUnsavedChanges`, and why it's locked or failed. With no document, an\n * empty one that reads as still opening. Takes a selector, and re-renders only\n * when what it returns changes.\n */\nexport function useDocument<Selected = DocumentInfo>(\n select?: (document: DocumentInfo) => Selected,\n): Selected {\n return useDeclared(DocumentsToken, documentState.read, documentState.empty, select);\n}\n\n/**\n * Every open document, in tab order (loading, locked and failed ones too),\n * and the active one's id. Takes a selector, and re-renders only when what it\n * returns changes.\n */\nexport function useDocumentsState<Selected = DocumentsState>(\n select?: (state: DocumentsState) => Selected,\n): Selected {\n return useDeclared(DocumentsToken, documentsState.read, documentsState.empty, select);\n}\n\n/** Subscribe to one documents event for the mounted lifetime: `useDocumentsEvent((documents) => documents.onOpened, handler)`. */\nexport function useDocumentsEvent<T>(\n select: (documents: DocumentsCapability) => EventHook<T>,\n handler: (event: T) => void,\n): void {\n useCapabilityEvent(DocumentsToken, select, handler);\n}\n\nconst NO_PAGES: readonly PageInfo[] = Object.freeze([]);\n\n/**\n * The pages of the document this component talks to, in order: each with its\n * `ref`, `index`, `label`, size and rotation. Needs no Stage, so a thumbnail\n * list or a page picker works anywhere. The same array until a page is added,\n * removed, moved or rotated; empty with no document.\n */\nexport function usePageList(): readonly PageInfo[] {\n const documentId = useDocumentId();\n return useKernelValue((kernel) =>\n documentId ? kernel.documents.listPages(documentId) : NO_PAGES,\n );\n}\n\n/**\n * The viewer's own settings (`<Viewer identity scope accent page>`), or the\n * value `select` picks from them: the accent every part without a color of its\n * own follows, and how pages look. What paints a page or an accent reads it\n * here, through `paint()` from `@embedpdf/web`, so a CSS variable still wins.\n */\nexport function useViewerSettings<Selected = ViewerSettings>(\n select?: (settings: ViewerSettings) => Selected,\n): Selected {\n return useKernelValue((kernel) => {\n const settings = kernel.getSettings();\n return select ? select(settings) : (settings as unknown as Selected);\n }, shallowEqual);\n}\n\n// `InitialDocument` is the kernel's type (re-exported via `export * from\n// '@embedpdf/core'` above) — one shared shape for every adapter.\n\nexport interface ViewerProps {\n /**\n * The engine, as an instance or a thunk. Engines construct synchronously and\n * boot lazily (`localEngine()` allocates nothing until first use), so\n * ownership follows the shape of what you pass:\n *\n * - An **instance** (`engine={engine}`) is borrowed: the Viewer uses it and\n * never destroys it, because you acquired it and therefore own it. The\n * common path — a module-scope `const engine = localEngine()` shared\n * across viewers and route changes. The Viewer warms up a local engine\n * on mount so the boot overlaps app initialization.\n * - A **thunk** (`engine={() => localEngine()}`) is viewer-owned: the\n * Viewer calls it on mount and `destroy()`s the result on unmount. Use it\n * for per-mount isolation (StrictMode/HMR-clean teardown, independent\n * multi-viewer engines).\n *\n * Init-only: captured on first render; later identity changes are ignored (dev warns).\n */\n engine: Engine | EngineFactory;\n /** Init-only: captured on first render; later identity changes are ignored (dev warns). */\n plugins: AnyPlugin[];\n /** Documents to open on startup (with optional tab names). Init-only. */\n initialDocuments?: InitialDocument[];\n fallback?: React.ReactNode;\n /** Rendered when kernel construction or `start()` fails. Without it a boot\n * failure renders nothing — but never a silent forever-fallback. */\n renderError?: (error: unknown) => React.ReactNode;\n /** Called once the kernel has started — before `children` mount and before\n * `initialDocuments` open. The imperative door for code outside React\n * (register commands, subscribe events). The `ref` carries the same kernel. */\n onReady?: (kernel: Kernel) => void;\n /**\n * Who the user is, for every document opened without an `identity` of its\n * own (which replaces this one). Documents opened after a change use the new\n * value; open ones keep theirs. The local engine only: the cloud engine reads\n * it from the document's token.\n */\n identity?: Identity;\n /**\n * What the user may do, as permissions, in every document opened without a\n * `scope` of its own (which replaces this one). Left out, they may do\n * everything. Changes reach the documents opened after them, as `identity`.\n */\n scope?: readonly string[];\n /** The color every part without a color of its own follows. CSS `--epdf-accent` wins over it. */\n accent?: string;\n /** How pages look: `background` before the picture arrives, and the `shadow` under each page. */\n page?: Partial<ViewerPageSettings>;\n children?: React.ReactNode;\n}\n\ntype BootState =\n | { phase: 'booting'; kernel: Kernel | null }\n | { phase: 'ready'; kernel: Kernel }\n | { phase: 'error'; error: unknown };\n\n/**\n * Owns the kernel as an effect-scoped resource: each effect setup creates and\n * starts exactly one kernel; each cleanup destroys exactly that one. That is\n * the contract StrictMode exercises (two kernels in dev, the first fully\n * destroyed) — the kernel itself is never restarted after destroy.\n *\n * The kernel is published to context the moment it exists — before `start()`\n * resolves — so the `fallback` can use workspace capabilities (i18n copy on a\n * loading screen). The one exception is the very first render, which happens\n * before the effect: it renders nothing. Children mount once `start()` resolves\n * — which never touches the engine, so the shell is alive while WASM compiles\n * or the transport connects. `initialDocuments` open in the background and\n * stream into the registry (`useDocuments()` is reactive); per-document\n * loading UI is the Stage's job, not a root gate.\n *\n * Engine ownership. When `engine` is a thunk, the Viewer owns it: each effect\n * setup constructs one engine (construction is synchronous and inert — boot\n * happens lazily inside the engine) and each cleanup destroys exactly that one\n * — after the kernel, so handles close first. When `engine` is an instance it\n * is borrowed and never destroyed here; the Viewer only warms up a local\n * engine so the WASM boot overlaps plugin initialization. StrictMode's\n * double-mount therefore constructs two independent thunk engines and tears\n * the first fully down, matching the kernel's own effect-scoped lifecycle.\n * That means dev-only double resource use (two worker spawns, two font\n * fetches, briefly overlapping) — deliberate, because it is exactly the\n * leak-detection contract StrictMode exists to exercise; production mounts\n * once and boots once.\n */\nexport const Viewer = forwardRef<Kernel | null, ViewerProps>(function Viewer(\n {\n engine,\n plugins,\n initialDocuments,\n fallback,\n renderError,\n onReady,\n identity,\n scope,\n accent,\n page,\n children,\n }: ViewerProps,\n ref,\n) {\n // Init-only inputs: the kernel's lifetime is the component's lifetime, so a\n // changed engine/plugins identity cannot mean \"rebuild the workspace\" —\n // that would silently drop every open document. Capture once, warn in dev.\n const initial = useRef({ engine, plugins, initialDocuments });\n // The viewer's settings follow the props. The first ones go to the kernel when it's\n // created, so the initial documents open with them; later ones as they change.\n const settings = viewerSettingsOf({ identity, scope, accent, page });\n const latestSettings = useRef(settings);\n latestSettings.current = settings;\n const warned = useRef(false);\n if (isDev() && !warned.current) {\n if (initial.current.engine !== engine || initial.current.plugins !== plugins) {\n warned.current = true;\n console.warn(\n '[embedpdf] <Viewer> engine/plugins are init-only. A changed identity is ignored — ' +\n 'pass stable references (module scope, useState, or useMemo). ' +\n 'An inline `plugins={[...]}` array recreates its identity every render.',\n );\n }\n }\n\n const [boot, setBoot] = useState<BootState>({ phase: 'booting', kernel: null });\n const onReadyRef = useRef(onReady);\n onReadyRef.current = onReady;\n // The ref is the started kernel (null before boot and after an error).\n useImperativeHandle<Kernel | null, Kernel | null>(\n ref,\n () => (boot.phase === 'ready' ? boot.kernel : null),\n [boot],\n );\n useEffect(() => {\n const captured = initial.current;\n // A thunk is viewer-owned: call it now, destroy on unmount. An instance is\n // borrowed: use as-is, never destroy. Construction is synchronous and inert\n // either way — the engine boots lazily on first use — so a local engine's\n // `warmup()` overlaps the WASM boot with plugin initialization.\n const ownsEngine = typeof captured.engine === 'function';\n const engine: Engine = ownsEngine\n ? (captured.engine as EngineFactory)()\n : (captured.engine as Engine);\n if (isLocalEngine(engine)) engine.warmup();\n let kernel: Kernel;\n try {\n kernel = createKernel({\n engine,\n plugins: captured.plugins,\n settings: latestSettings.current,\n clock: browserClock(),\n });\n } catch (error) {\n setBoot({ phase: 'error', error }); // plan/graph errors surface, not throw mid-render\n if (ownsEngine) void engine.destroy();\n return;\n }\n let alive = true;\n setBoot({ phase: 'booting', kernel }); // context carries the kernel from this frame on\n kernel.start().then(\n () => {\n if (!alive) return; // unmounted mid-boot — don't open anything\n onReadyRef.current?.(kernel);\n setBoot({ phase: 'ready', kernel });\n // Kernel-owned boot policy: all tabs appear immediately in array\n // order; the `active` entry (else the first) is selected; failures\n // surface as tab status. See DocumentsCapability.openAll.\n kernel.documents.openAll(captured.initialDocuments ?? []);\n },\n (error) => {\n if (alive) setBoot({ phase: 'error', error });\n },\n );\n return () => {\n alive = false;\n // Kernel first (closes every document handle), then the engine we own —\n // ownership follows acquisition. `engine.destroy()` joins an in-flight\n // boot (or no-ops if it never started), so an unmount mid-boot is safe.\n void kernel.destroy().then(() => {\n if (ownsEngine) void engine.destroy();\n });\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // After every render: props compare by value (an inline `page={{ shadow: 'none' }}` is a new\n // object each time), and settings that didn't change change nothing.\n const liveKernel = boot.phase === 'error' ? null : boot.kernel;\n useEffect(() => {\n liveKernel?.updateSettings(latestSettings.current);\n });\n\n if (boot.phase === 'error') {\n return <>{renderError ? renderError(boot.error) : null}</>;\n }\n if (!boot.kernel) return null; // pre-effect first render only\n return (\n <KernelCtx.Provider value={boot.kernel}>\n {boot.phase === 'ready' ? children : (fallback ?? null)}\n </KernelCtx.Provider>\n );\n});\nexport const EmbedPDF = Viewer;\n\n/**\n * PageContext — the seam. A layer depends only on this, never on the Stage. So the\n * same layer works inside a virtualized Stage and in a standalone <PageView>.\n * Its members are documented on `PageContext` in `@embedpdf/web`.\n */\nexport type PageContextValue = PageContext<PageTransform, PageViewDemand>;\n\nconst PageCtx = createContext<PageContextValue | null>(null);\nexport const PageProvider = PageCtx.Provider;\n\nexport function usePage(): PageContextValue {\n const context = useContext(PageCtx);\n if (!context) throw new Error('usePage must be used inside <PageView> or a <Stage> page');\n return context;\n}\n"],"mappings":";;;;;;;;;AAcA,SAAgB,UAAU,OAAuC;CAC/D,OAAO,mBAAmB,KAAK;AACjC;;;;;;;;ACXA,MAAM,yBAAS,IAAI,IAAY;;;;;;;AAQ/B,SAAgB,QAAiB;CAC/B,IAAI;EACF,OAAO,QAAQ,IAAI,aAAa;CAClC,QAAQ;EACN,OAAO;CACT;AACF;;AAGA,SAAgB,QAAQ,KAAa,SAAuB;CAC1D,IAAI,CAAC,MAAM,KAAK,OAAO,IAAI,GAAG,GAAG;CACjC,OAAO,IAAI,GAAG;CACd,QAAQ,KAAK,cAAc,SAAS;AACtC;;;AC0CA,MAAM,YAAY,cAA6B,IAAI;;AAEnD,MAAM,mBAAmB,cAA6B,IAAI;AAE1D,SAAgB,YAAoB;CAClC,MAAM,SAAS,WAAW,SAAS;CACnC,IAAI,CAAC,QAAQ,MAAM,IAAI,MAAM,mDAAmD;CAChF,OAAO;AACT;;;;;;;AAYA,SAAgB,eAAe,EAAE,QAAQ,YAAiC;CACxE,OAAO,oBAAC,UAAU,UAAX;EAAoB,OAAO;EAAS;CAA6B,CAAA;AAC1E;AAEA,MAAa,gBAAoB,MAAoB,UACnD,SAAS,SAAU,KAAK,WAAW,MAAM,UAAU,KAAK,OAAO,MAAM,MAAM,SAAS,MAAM,EAAE;;AAG9F,SAAgB,eACd,QACA,UAA0C,OAAO,IAC9C;CACH,MAAM,SAAS,UAAU;CACzB,MAAM,OAAO,OAAwB,IAAI;CACzC,MAAM,YAAY;EAChB,MAAM,OAAO,OAAO,MAAM;EAC1B,IAAI,KAAK,WAAW,QAAQ,KAAK,QAAQ,GAAG,IAAI,GAAG,OAAO,KAAK,QAAQ;EACvE,KAAK,UAAU,EAAE,GAAG,KAAK;EACzB,OAAO;CACT;CACA,OAAO,qBAAqB,OAAO,WAAW,KAAK,GAAG;AACxD;AAEA,SAAgB,sBAAqC;CACnD,OAAO,gBAAgB,WAAW,OAAO,UAAU,YAAY,CAAC;AAClE;;AAGA,SAAgB,gBAA+B;CAC7C,MAAM,SAAS,iBAAiB;CAChC,MAAM,SAAS,oBAAoB;CACnC,OAAO,UAAU;AACnB;;;;;;;AAQA,SAAgB,mBAAkC;CAChD,OAAO,WAAW,gBAAgB;AACpC;;AAOA,SAAgB,cAAc,EAAE,IAAI,YAAgC;CAClE,OAAO,oBAAC,iBAAiB,UAAlB;EAA2B,OAAO;EAAK;CAAoC,CAAA;AACpF;;;;;;;;;;;AAqBA,SAAgB,aAAa,EAAE,WAAW,MAAM,QAAQ,OAAO,YAA+B;CAC5F,MAAM,WAAW,YAAY;CAC7B,IAAI,SAAS,WAAW,SAAS,OAAO,oBAAA,UAAA,EAAG,SAAW,CAAA;CACtD,IAAI,SAAS,WAAW,YAAY,QAClC,OAAO,oBAAA,UAAA,EAAA,UAAG,OAAO,QAA8B,EAAI,CAAA;CAErD,IAAI,SAAS,WAAW,WAAW,OAAO,OAAO,oBAAA,UAAA,EAAA,UAAG,MAAM,QAA8B,EAAI,CAAA;CAC5F,OAAO,oBAAA,UAAA,EAAA,UAAG,SAAW,CAAA;AACvB;;;;;;;;;;;;;;;AAgBA,SAAgB,cAAiB,OAA8B;CAC7D,MAAM,SAAS,UAAU;CACzB,MAAM,aAAa,sBAAsB,KAAK;CAC9C,IAAI,YAAY,OAAO;CACvB,IAAI,OAAO,QAAQ,KAAK,MAAM,YAAY,OAAO,WAAW,QAAQ,KAAK;CACzE,OAAO,OAAO,WAAW,KAAK;AAChC;;;AAIA,SAAgB,sBAAyB,OAAqC;CAC5E,MAAM,SAAS,iBAAiB;CAChC,OAAO,gBAAgB,WAAW,OAAO,cAAc,OAAO,UAAU,KAAA,CAAS,CAAC;AACpF;;;;;;;;AASA,SAAgB,YACd,OACA,QACA,UAA0C,OAAO,IAC9C;CACH,MAAM,SAAS,UAAU;CACzB,MAAM,SAAS,iBAAiB;CAChC,MAAM,aAAa,sBAAsB,KAAK,KAAK,OAAO,WAAW,OAAO,UAAU,KAAA,CAAS;CAC/F,MAAM,OAAO,OAAwB,IAAI;CACzC,MAAM,YAAY;EAChB,MAAM,OAAO,OAAO,UAAU;EAC9B,IAAI,KAAK,WAAW,QAAQ,KAAK,QAAQ,GAAG,IAAI,GAAG,OAAO,KAAK,QAAQ;EACvE,KAAK,UAAU,EAAE,GAAG,KAAK;EACzB,OAAO;CACT;CACA,OAAO,qBAAqB,OAAO,WAAW,KAAK,GAAG;AACxD;;;;;;;;;;AAWA,SAAgB,oBACd,OACA,QACA,UACA,UAA0C,OAAO,IAC9C;CACH,MAAM,SAAS,UAAU;CACzB,MAAM,aAAa,sBAAsB,KAAK;CAC9C,MAAM,OAAO,OAAwB,IAAI;CACzC,MAAM,YAAY;EAChB,IAAI;EACJ,IAAI,eAAe,MACjB,OAAO;OAEP,IAAI;GACF,OAAO,OAAO,UAAU;EAC1B,QAAQ;GACN,OAAO;EACT;EAEF,IAAI,KAAK,WAAW,QAAQ,KAAK,QAAQ,GAAG,IAAI,GAAG,OAAO,KAAK,QAAQ;EACvE,KAAK,UAAU,EAAE,GAAG,KAAK;EACzB,OAAO;CACT;CACA,OAAO,qBAAqB,OAAO,WAAW,KAAK,GAAG;AACxD;;;;;;;;AASA,SAAgB,mBACd,OACA,QACA,SACM;CACN,MAAM,aAAa,sBAAsB,KAAK;CAC9C,MAAM,aAAa,OAAO,OAAO;CACjC,WAAW,UAAU;CACrB,MAAM,YAAY,OAAO,MAAM;CAC/B,UAAU,UAAU;CACpB,gBAAgB;EACd,IAAI,CAAC,YAAY;EACjB,OAAO,UAAU,QAAQ,UAAU,CAAC,EAAE,UAAU,WAAW,QAAQ,KAAK,CAAC;CAC3E,GAAG,CAAC,UAAU,CAAC;AACjB;;;;;;;;AASA,SAAgB,eAAoC;CAClD,OAAO,cAAc,cAAc;AACrC;;AAGA,SAAS,YACP,OACA,MACA,OACA,QACU;CACV,MAAM,SAAS,iBAAiB;CAChC,OAAO,gBAAgB,WAAW;EAChC,MAAM,aAAa,OAAO,cAAc,OAAO,UAAU,KAAA,CAAS;EAClE,MAAM,QAAQ,aAAa,KAAK,UAAU,IAAI;EAC9C,OAAO,SAAS,OAAO,KAAK,IAAK;CACnC,GAAG,YAAY;AACjB;;;;;;;;AASA,SAAgB,YACd,QACU;CACV,OAAO,YAAY,gBAAgB,cAAc,MAAM,cAAc,OAAO,MAAM;AACpF;;;;;;AAOA,SAAgB,kBACd,QACU;CACV,OAAO,YAAY,gBAAgB,eAAe,MAAM,eAAe,OAAO,MAAM;AACtF;;AAGA,SAAgB,kBACd,QACA,SACM;CACN,mBAAmB,gBAAgB,QAAQ,OAAO;AACpD;AAEA,MAAM,WAAgC,OAAO,OAAO,CAAC,CAAC;;;;;;;AAQtD,SAAgB,cAAmC;CACjD,MAAM,aAAa,cAAc;CACjC,OAAO,gBAAgB,WACrB,aAAa,OAAO,UAAU,UAAU,UAAU,IAAI,QACxD;AACF;;;;;;;AAQA,SAAgB,kBACd,QACU;CACV,OAAO,gBAAgB,WAAW;EAChC,MAAM,WAAW,OAAO,YAAY;EACpC,OAAO,SAAS,OAAO,QAAQ,IAAK;CACtC,GAAG,YAAY;AACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyFA,MAAa,SAAS,WAAuC,SAAS,OACpE,EACE,QACA,SACA,kBACA,UACA,aACA,SACA,UACA,OACA,QACA,MACA,YAEF,KACA;CAIA,MAAM,UAAU,OAAO;EAAE;EAAQ;EAAS;CAAiB,CAAC;CAG5D,MAAM,WAAW,iBAAiB;EAAE;EAAU;EAAO;EAAQ;CAAK,CAAC;CACnE,MAAM,iBAAiB,OAAO,QAAQ;CACtC,eAAe,UAAU;CACzB,MAAM,SAAS,OAAO,KAAK;CAC3B,IAAI,MAAM,KAAK,CAAC,OAAO,SACjB;MAAA,QAAQ,QAAQ,WAAW,UAAU,QAAQ,QAAQ,YAAY,SAAS;GAC5E,OAAO,UAAU;GACjB,QAAQ,KACN,uNAGF;EACF;;CAGF,MAAM,CAAC,MAAM,WAAW,SAAoB;EAAE,OAAO;EAAW,QAAQ;CAAK,CAAC;CAC9E,MAAM,aAAa,OAAO,OAAO;CACjC,WAAW,UAAU;CAErB,oBACE,WACO,KAAK,UAAU,UAAU,KAAK,SAAS,MAC9C,CAAC,IAAI,CACP;CACA,gBAAgB;EACd,MAAM,WAAW,QAAQ;EAKzB,MAAM,aAAa,OAAO,SAAS,WAAW;EAC9C,MAAM,SAAiB,aAClB,SAAS,OAAyB,IAClC,SAAS;EACd,IAAI,cAAc,MAAM,GAAG,OAAO,OAAO;EACzC,IAAI;EACJ,IAAI;GACF,SAAS,aAAa;IACpB;IACA,SAAS,SAAS;IAClB,UAAU,eAAe;IACzB,OAAO,aAAa;GACtB,CAAC;EACH,SAAS,OAAO;GACd,QAAQ;IAAE,OAAO;IAAS;GAAM,CAAC;GACjC,IAAI,YAAY,OAAY,QAAQ;GACpC;EACF;EACA,IAAI,QAAQ;EACZ,QAAQ;GAAE,OAAO;GAAW;EAAO,CAAC;EACpC,OAAO,MAAM,CAAC,CAAC,WACP;GACJ,IAAI,CAAC,OAAO;GACZ,WAAW,UAAU,MAAM;GAC3B,QAAQ;IAAE,OAAO;IAAS;GAAO,CAAC;GAIlC,OAAO,UAAU,QAAQ,SAAS,oBAAoB,CAAC,CAAC;EAC1D,IACC,UAAU;GACT,IAAI,OAAO,QAAQ;IAAE,OAAO;IAAS;GAAM,CAAC;EAC9C,CACF;EACA,aAAa;GACX,QAAQ;GAIR,OAAY,QAAQ,CAAC,CAAC,WAAW;IAC/B,IAAI,YAAY,OAAY,QAAQ;GACtC,CAAC;EACH;CAEF,GAAG,CAAC,CAAC;CAIL,MAAM,aAAa,KAAK,UAAU,UAAU,OAAO,KAAK;CACxD,gBAAgB;EACd,YAAY,eAAe,eAAe,OAAO;CACnD,CAAC;CAED,IAAI,KAAK,UAAU,SACjB,OAAO,oBAAA,UAAA,EAAA,UAAG,cAAc,YAAY,KAAK,KAAK,IAAI,KAAO,CAAA;CAE3D,IAAI,CAAC,KAAK,QAAQ,OAAO;CACzB,OACE,oBAAC,UAAU,UAAX;EAAoB,OAAO,KAAK;EAC7B,UAAA,KAAK,UAAU,UAAU,WAAY,YAAY;CAChC,CAAA;AAExB,CAAC;AACD,MAAa,WAAW;AASxB,MAAM,UAAU,cAAuC,IAAI;AAC3D,MAAa,eAAe,QAAQ;AAEpC,SAAgB,UAA4B;CAC1C,MAAM,UAAU,WAAW,OAAO;CAClC,IAAI,CAAC,SAAS,MAAM,IAAI,MAAM,0DAA0D;CACxF,OAAO;AACT"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-fzpGv78v.cjs","names":["DocumentsToken","shallowEqual","documentState","documentsState"],"sources":["../src/theme.ts","../src/dev.ts","../src/runtime.tsx"],"sourcesContent":["/**\n * `epdfTheme()`: a theme written with setting names, as a style for the\n * element around your viewer. The variables and their lookup are framework-free\n * (`@embedpdf/web`); this only types the result as React's style.\n */\nimport type * as React from 'react';\nimport { epdfThemeVariables, type EpdfTheme } from '@embedpdf/web';\n\nexport type { EpdfTheme } from '@embedpdf/web';\n\n/**\n * The CSS variables of a theme, for a `style` prop: `<div style={epdfTheme({ accent })}>`.\n * Everything inside the element follows them, and a CSS variable wins over any setting.\n */\nexport function epdfTheme(theme: EpdfTheme): React.CSSProperties {\n return epdfThemeVariables(theme) as React.CSSProperties;\n}\n","/**\n * Development-time guardrails. Every warning fires once per process (keyed),\n * says what is wrong and what to do instead, and compiles out of production\n * builds ({@link isDev}).\n */\nconst warned = new Set<string>();\n\n/**\n * Whether this is a development build, as the app's bundler says: every bundler replaces\n * `process.env.NODE_ENV`, with `\"production\"` in a production build, so there this is `false`\n * and the warnings behind it compile out. A page with no bundler has no `process` at all, and\n * counts as development: the warnings help most while you try the package out.\n */\nexport function isDev(): boolean {\n try {\n return process.env.NODE_ENV !== 'production';\n } catch {\n return true;\n }\n}\n\n/** Warn once per key. No-op in production. */\nexport function devWarn(key: string, message: string): void {\n if (!isDev() || warned.has(key)) return;\n warned.add(key);\n console.warn(`[embedpdf] ${message}`);\n}\n\n/** Test seam: forget every warning so a suite can assert one fires again. */\nexport function resetDevWarnings(): void {\n warned.clear();\n}\n","/**\n * @embedpdf/react — the generic binding.\n *\n * Binds the kernel's one change stream to React (useSyncExternalStore), resolves\n * capabilities (document-scoped ones against the active or `<DocumentScope>`-given\n * document, with a stand-in while there is none), and provides the page\n * coordinate context. Every plugin and layer rides on this — there is no\n * per-plugin framework code.\n */\n\n// One-line-per-feature: registration travels with the UI.\nexport * from '@embedpdf/core';\n// What a page with a viewer needs from the browser: handing bytes to the user as a download,\n// and a font the engine draws with, for text you draw yourself.\nexport { mountWebFont, saveFile } from '@embedpdf/web';\n// The page context every layer draws through; its conversions are shared with every framework.\nexport { makePageContext } from '@embedpdf/web';\n// A theme written with setting names, as the style for the element around a viewer.\nexport { epdfTheme } from './theme';\nexport type { EpdfTheme } from './theme';\nimport * as React from 'react';\nimport {\n createContext,\n forwardRef,\n useContext,\n useEffect,\n useImperativeHandle,\n useRef,\n useState,\n useSyncExternalStore,\n} from 'react';\nimport {\n DocumentsToken,\n createKernel,\n documentState,\n documentsState,\n isLocalEngine,\n shallowEqual,\n standInFor,\n viewerSettingsOf,\n} from '@embedpdf/core';\nimport type {\n AnyPlugin,\n CapabilityToken,\n DocumentInfo,\n DocumentsCapability,\n DocumentsState,\n Engine,\n EngineFactory,\n EventHook,\n FailedDocumentInfo,\n Identity,\n InitialDocument,\n Kernel,\n LockedDocumentInfo,\n PageInfo,\n ViewerPageSettings,\n ViewerSettings,\n} from '@embedpdf/core';\n// Pure coordinate math from the geometry base — not from stage-core. The\n// PageContext seam stays stage-agnostic (it must also serve standalone PageView).\nimport type { PageTransform } from '@embedpdf/core-geometry';\nimport type { PageViewDemand } from '@embedpdf/plugin-render/contract';\nimport { browserClock } from '@embedpdf/web';\nimport type { PageContext } from '@embedpdf/web';\n\nimport { isDev } from './dev';\n\nconst KernelCtx = createContext<Kernel | null>(null);\n/** The document a subtree is bound to. null => use the active document. */\nconst DocumentScopeCtx = createContext<string | null>(null);\n\nexport function useKernel(): Kernel {\n const kernel = useContext(KernelCtx);\n if (!kernel) throw new Error('useKernel must be used within <Viewer>/<EmbedPDF>');\n return kernel;\n}\n\nexport interface KernelProviderProps {\n kernel: Kernel;\n children?: React.ReactNode;\n}\n/**\n * Give a subtree a kernel someone else created and owns, so every hook in it\n * works against that kernel. The full viewer's wrapper puts the children it\n * slots into the viewer inside one. The provider never starts or destroys the\n * kernel; `<Viewer>` is the component that owns one.\n */\nexport function KernelProvider({ kernel, children }: KernelProviderProps) {\n return <KernelCtx.Provider value={kernel}>{children}</KernelCtx.Provider>;\n}\n\nexport const shallowArray = <T,>(left: readonly T[], right: readonly T[]): boolean =>\n left === right || (left.length === right.length && left.every((item, i) => item === right[i]));\n\n/** Read a value derived from the kernel, cached by equality (no tearing loop). */\nexport function useKernelValue<R>(\n select: (kernel: Kernel) => R,\n isEqual: (left: R, right: R) => boolean = Object.is,\n): R {\n const kernel = useKernel();\n const last = useRef<{ v: R } | null>(null);\n const get = () => {\n const next = select(kernel);\n if (last.current && isEqual(last.current.v, next)) return last.current.v;\n last.current = { v: next };\n return next;\n };\n return useSyncExternalStore(kernel.subscribe, get, get);\n}\n\nexport function useActiveDocumentId(): string | null {\n return useKernelValue((kernel) => kernel.documents.getActiveId());\n}\n\n/** The document id for this subtree: the nearest <DocumentScope>, else the active doc. */\nexport function useDocumentId(): string | null {\n const scoped = useDocumentScope();\n const active = useActiveDocumentId();\n return scoped ?? active;\n}\n\n/**\n * The document the nearest <DocumentScope> binds this subtree to, or null when\n * it follows the active document. For resolvers that pass it to\n * `kernel.tryCapability`, which reads the active document at call time; UI\n * that needs the id itself uses {@link useDocumentId}.\n */\nexport function useDocumentScope(): string | null {\n return useContext(DocumentScopeCtx);\n}\n\nexport interface DocumentScopeProps {\n id: string;\n children: React.ReactNode;\n}\n/** Bind a subtree to a specific document (panes, comparison). */\nexport function DocumentScope({ id, children }: DocumentScopeProps) {\n return <DocumentScopeCtx.Provider value={id}>{children}</DocumentScopeCtx.Provider>;\n}\n\nexport interface DocumentGateProps {\n /** Shown while this subtree has no document: none is open, or it's still opening. */\n fallback?: React.ReactNode;\n /** Shown while the document waits for its password; `fallback` without it. */\n locked?: (document: LockedDocumentInfo) => React.ReactNode;\n /** Shown when the document couldn't be opened, with why; `fallback` without it. */\n error?: (document: FailedDocumentInfo) => React.ReactNode;\n children?: React.ReactNode;\n}\n/**\n * Render children only while this subtree has a ready document — the\n * structural way to say \"this UI is defined over a document\". An empty\n * workspace is a legitimate, designable state (the Viewer no longer blocks on\n * documents so chrome can render at t≈0): workspace-scoped UI (toolbars,\n * commands, i18n) lives outside the gate; document-scoped UI (Stage, panels,\n * page chrome) lives inside it. A document that waits for its password, or\n * failed, renders `locked` or `error` with it, or else `fallback`. Sibling of\n * <DocumentScope>, which picks which document; this one handles whether.\n */\nexport function DocumentGate({ fallback = null, locked, error, children }: DocumentGateProps) {\n const document = useDocument();\n if (document.status === 'ready') return <>{children}</>;\n if (document.status === 'locked' && locked) {\n return <>{locked(document as LockedDocumentInfo)}</>;\n }\n if (document.status === 'error' && error) return <>{error(document as FailedDocumentInfo)}</>;\n return <>{fallback}</>;\n}\n\n/**\n * Resolve a capability by token, binding document-scoped ones to this\n * subtree's document. Resolution is a reactive read (`tryCapability` through\n * the kernel's one change stream), not a memoized call — under the\n * request-time lifecycle a document can become resolvable while its id stays\n * the same, so any id-keyed cache goes stale; subscribing makes staleness\n * structurally impossible.\n *\n * Outside a document (none open, not ready yet, or none in scope), a document\n * plugin resolves to its stand-in (`standInFor` from `@embedpdf/core`):\n * rendering never fails, and a method called too early refuses with\n * `not-ready` (a method that returns a promise rejects). A token no plugin\n * provides still throws the kernel's reason, because that is a setup mistake.\n */\nexport function useCapability<T>(token: CapabilityToken<T>): T {\n const kernel = useKernel();\n const capability = useOptionalCapability(token);\n if (capability) return capability;\n if (kernel.scopeOf(token) === 'document') return standInFor(kernel, token);\n return kernel.capability(token);\n}\n\n/** Like `useCapability`, but null while the token can't resolve (no plugin,\n * no document, or a document that isn't ready yet). */\nexport function useOptionalCapability<T>(token: CapabilityToken<T>): T | null {\n const scoped = useDocumentScope();\n return useKernelValue((kernel) => kernel.tryCapability(token, scoped ?? undefined));\n}\n\n/**\n * Subscribe to a selector over a (document-resolved) capability. Strict:\n * while the token can't resolve, it throws the kernel's reason (`no\n * document`, `document is loading`), for code that knows a document exists,\n * such as anything inside a <DocumentGate>. `useOptionalSelector` is its\n * total twin.\n */\nexport function useSelector<C, R>(\n token: CapabilityToken<C>,\n select: (capability: C) => R,\n isEqual: (left: R, right: R) => boolean = Object.is,\n): R {\n const kernel = useKernel();\n const scoped = useDocumentScope();\n const capability = useOptionalCapability(token) ?? kernel.capability(token, scoped ?? undefined);\n const last = useRef<{ v: R } | null>(null);\n const get = () => {\n const next = select(capability);\n if (last.current && isEqual(last.current.v, next)) return last.current.v;\n last.current = { v: next };\n return next;\n };\n return useSyncExternalStore(kernel.subscribe, get, get);\n}\n\n/**\n * Null-safe `useSelector`: `fallback` whenever the token can't resolve — no\n * provider, or a document-scoped token with no document. For chrome that stays\n * mounted across the empty-workspace state (a zoom readout, a mode band).\n *\n * The `select` guard also swallows reads through a capability whose document\n * closed between the store notification and this render — that teardown race\n * resolves to `fallback` for one frame, then re-renders against the new state.\n */\nexport function useOptionalSelector<C, R>(\n token: CapabilityToken<C>,\n select: (capability: C) => R,\n fallback: R,\n isEqual: (left: R, right: R) => boolean = Object.is,\n): R {\n const kernel = useKernel();\n const capability = useOptionalCapability(token);\n const last = useRef<{ v: R } | null>(null);\n const get = () => {\n let next: R;\n if (capability === null) {\n next = fallback;\n } else {\n try {\n next = select(capability);\n } catch {\n next = fallback;\n }\n }\n if (last.current && isEqual(last.current.v, next)) return last.current.v;\n last.current = { v: next };\n return next;\n };\n return useSyncExternalStore(kernel.subscribe, get, get);\n}\n\n/**\n * Subscribe to a capability's {@link EventHook} for the mounted lifetime —\n * `useCapabilityEvent(ActionsToken, (actions) => actions.onExecuted, handler)`. Events\n * carry occurrences, never state (a late subscriber that needs the current\n * value uses `useSelector`). The handler rides a ref, so a fresh closure per\n * render never resubscribes. Null-safe: no plugin/document → no subscription.\n */\nexport function useCapabilityEvent<C, T>(\n token: CapabilityToken<C>,\n select: (capability: C) => EventHook<T>,\n handler: (event: T) => void,\n): void {\n const capability = useOptionalCapability(token);\n const handlerRef = useRef(handler);\n handlerRef.current = handler;\n const selectRef = useRef(select);\n selectRef.current = select;\n useEffect(() => {\n if (!capability) return;\n return selectRef.current(capability)((event) => handlerRef.current(event));\n }, [capability]);\n}\n\n/**\n * The documents API: open, close, unlock, download, and the tabs. Inside a\n * <DocumentScope>, the calls that leave out the document use that one, so\n * `download()` without an id downloads the document in scope. The object\n * never changes, so it's safe in effects and callbacks. What to show comes\n * from `useDocument()` and `useDocumentsState()`.\n */\nexport function useDocuments(): DocumentsCapability {\n return useCapability(DocumentsToken);\n}\n\n/** Read a state declaration's value through the kernel, or the value `select` picks from it. */\nfunction useDeclared<Capability, State extends object, Selected>(\n token: CapabilityToken<Capability>,\n read: (capability: Capability) => State,\n empty: State,\n select: ((state: State) => Selected) | undefined,\n): Selected {\n const scoped = useDocumentScope();\n return useKernelValue((kernel) => {\n const capability = kernel.tryCapability(token, scoped ?? undefined);\n const state = capability ? read(capability) : empty;\n return select ? select(state) : (state as unknown as Selected);\n }, shallowEqual);\n}\n\n/**\n * The document this component talks to: the one its <DocumentScope> names, or\n * else the active one. Its `id`, `name`, `status`, `pageCount`,\n * `hasUnsavedChanges`, and why it's locked or failed. With no document, an\n * empty one that reads as still opening. Takes a selector, and re-renders only\n * when what it returns changes.\n */\nexport function useDocument<Selected = DocumentInfo>(\n select?: (document: DocumentInfo) => Selected,\n): Selected {\n return useDeclared(DocumentsToken, documentState.read, documentState.empty, select);\n}\n\n/**\n * Every open document, in tab order (loading, locked and failed ones too),\n * and the active one's id. Takes a selector, and re-renders only when what it\n * returns changes.\n */\nexport function useDocumentsState<Selected = DocumentsState>(\n select?: (state: DocumentsState) => Selected,\n): Selected {\n return useDeclared(DocumentsToken, documentsState.read, documentsState.empty, select);\n}\n\n/** Subscribe to one documents event for the mounted lifetime: `useDocumentsEvent((documents) => documents.onOpened, handler)`. */\nexport function useDocumentsEvent<T>(\n select: (documents: DocumentsCapability) => EventHook<T>,\n handler: (event: T) => void,\n): void {\n useCapabilityEvent(DocumentsToken, select, handler);\n}\n\nconst NO_PAGES: readonly PageInfo[] = Object.freeze([]);\n\n/**\n * The pages of the document this component talks to, in order: each with its\n * `ref`, `index`, `label`, size and rotation. Needs no Stage, so a thumbnail\n * list or a page picker works anywhere. The same array until a page is added,\n * removed, moved or rotated; empty with no document.\n */\nexport function usePageList(): readonly PageInfo[] {\n const documentId = useDocumentId();\n return useKernelValue((kernel) =>\n documentId ? kernel.documents.listPages(documentId) : NO_PAGES,\n );\n}\n\n/**\n * The viewer's own settings (`<Viewer identity scope accent page>`), or the\n * value `select` picks from them: the accent every part without a color of its\n * own follows, and how pages look. What paints a page or an accent reads it\n * here, through `paint()` from `@embedpdf/web`, so a CSS variable still wins.\n */\nexport function useViewerSettings<Selected = ViewerSettings>(\n select?: (settings: ViewerSettings) => Selected,\n): Selected {\n return useKernelValue((kernel) => {\n const settings = kernel.getSettings();\n return select ? select(settings) : (settings as unknown as Selected);\n }, shallowEqual);\n}\n\n// `InitialDocument` is the kernel's type (re-exported via `export * from\n// '@embedpdf/core'` above) — one shared shape for every adapter.\n\nexport interface ViewerProps {\n /**\n * The engine, as an instance or a thunk. Engines construct synchronously and\n * boot lazily (`localEngine()` allocates nothing until first use), so\n * ownership follows the shape of what you pass:\n *\n * - An **instance** (`engine={engine}`) is borrowed: the Viewer uses it and\n * never destroys it, because you acquired it and therefore own it. The\n * common path — a module-scope `const engine = localEngine()` shared\n * across viewers and route changes. The Viewer warms up a local engine\n * on mount so the boot overlaps app initialization.\n * - A **thunk** (`engine={() => localEngine()}`) is viewer-owned: the\n * Viewer calls it on mount and `destroy()`s the result on unmount. Use it\n * for per-mount isolation (StrictMode/HMR-clean teardown, independent\n * multi-viewer engines).\n *\n * Init-only: captured on first render; later identity changes are ignored (dev warns).\n */\n engine: Engine | EngineFactory;\n /** Init-only: captured on first render; later identity changes are ignored (dev warns). */\n plugins: AnyPlugin[];\n /** Documents to open on startup (with optional tab names). Init-only. */\n initialDocuments?: InitialDocument[];\n fallback?: React.ReactNode;\n /** Rendered when kernel construction or `start()` fails. Without it a boot\n * failure renders nothing — but never a silent forever-fallback. */\n renderError?: (error: unknown) => React.ReactNode;\n /** Called once the kernel has started — before `children` mount and before\n * `initialDocuments` open. The imperative door for code outside React\n * (register commands, subscribe events). The `ref` carries the same kernel. */\n onReady?: (kernel: Kernel) => void;\n /**\n * Who the user is, for every document opened without an `identity` of its\n * own (which replaces this one). Documents opened after a change use the new\n * value; open ones keep theirs. The local engine only: the cloud engine reads\n * it from the document's token.\n */\n identity?: Identity;\n /**\n * What the user may do, as permissions, in every document opened without a\n * `scope` of its own (which replaces this one). Left out, they may do\n * everything. Changes reach the documents opened after them, as `identity`.\n */\n scope?: readonly string[];\n /** The color every part without a color of its own follows. CSS `--epdf-accent` wins over it. */\n accent?: string;\n /** How pages look: `background` before the picture arrives, and the `shadow` under each page. */\n page?: Partial<ViewerPageSettings>;\n children?: React.ReactNode;\n}\n\ntype BootState =\n | { phase: 'booting'; kernel: Kernel | null }\n | { phase: 'ready'; kernel: Kernel }\n | { phase: 'error'; error: unknown };\n\n/**\n * Owns the kernel as an effect-scoped resource: each effect setup creates and\n * starts exactly one kernel; each cleanup destroys exactly that one. That is\n * the contract StrictMode exercises (two kernels in dev, the first fully\n * destroyed) — the kernel itself is never restarted after destroy.\n *\n * The kernel is published to context the moment it exists — before `start()`\n * resolves — so the `fallback` can use workspace capabilities (i18n copy on a\n * loading screen). The one exception is the very first render, which happens\n * before the effect: it renders nothing. Children mount once `start()` resolves\n * — which never touches the engine, so the shell is alive while WASM compiles\n * or the transport connects. `initialDocuments` open in the background and\n * stream into the registry (`useDocuments()` is reactive); per-document\n * loading UI is the Stage's job, not a root gate.\n *\n * Engine ownership. When `engine` is a thunk, the Viewer owns it: each effect\n * setup constructs one engine (construction is synchronous and inert — boot\n * happens lazily inside the engine) and each cleanup destroys exactly that one\n * — after the kernel, so handles close first. When `engine` is an instance it\n * is borrowed and never destroyed here; the Viewer only warms up a local\n * engine so the WASM boot overlaps plugin initialization. StrictMode's\n * double-mount therefore constructs two independent thunk engines and tears\n * the first fully down, matching the kernel's own effect-scoped lifecycle.\n * That means dev-only double resource use (two worker spawns, two font\n * fetches, briefly overlapping) — deliberate, because it is exactly the\n * leak-detection contract StrictMode exists to exercise; production mounts\n * once and boots once.\n */\nexport const Viewer = forwardRef<Kernel | null, ViewerProps>(function Viewer(\n {\n engine,\n plugins,\n initialDocuments,\n fallback,\n renderError,\n onReady,\n identity,\n scope,\n accent,\n page,\n children,\n }: ViewerProps,\n ref,\n) {\n // Init-only inputs: the kernel's lifetime is the component's lifetime, so a\n // changed engine/plugins identity cannot mean \"rebuild the workspace\" —\n // that would silently drop every open document. Capture once, warn in dev.\n const initial = useRef({ engine, plugins, initialDocuments });\n // The viewer's settings follow the props. The first ones go to the kernel when it's\n // created, so the initial documents open with them; later ones as they change.\n const settings = viewerSettingsOf({ identity, scope, accent, page });\n const latestSettings = useRef(settings);\n latestSettings.current = settings;\n const warned = useRef(false);\n if (isDev() && !warned.current) {\n if (initial.current.engine !== engine || initial.current.plugins !== plugins) {\n warned.current = true;\n console.warn(\n '[embedpdf] <Viewer> engine/plugins are init-only. A changed identity is ignored — ' +\n 'pass stable references (module scope, useState, or useMemo). ' +\n 'An inline `plugins={[...]}` array recreates its identity every render.',\n );\n }\n }\n\n const [boot, setBoot] = useState<BootState>({ phase: 'booting', kernel: null });\n const onReadyRef = useRef(onReady);\n onReadyRef.current = onReady;\n // The ref is the started kernel (null before boot and after an error).\n useImperativeHandle<Kernel | null, Kernel | null>(\n ref,\n () => (boot.phase === 'ready' ? boot.kernel : null),\n [boot],\n );\n useEffect(() => {\n const captured = initial.current;\n // A thunk is viewer-owned: call it now, destroy on unmount. An instance is\n // borrowed: use as-is, never destroy. Construction is synchronous and inert\n // either way — the engine boots lazily on first use — so a local engine's\n // `warmup()` overlaps the WASM boot with plugin initialization.\n const ownsEngine = typeof captured.engine === 'function';\n const engine: Engine = ownsEngine\n ? (captured.engine as EngineFactory)()\n : (captured.engine as Engine);\n if (isLocalEngine(engine)) engine.warmup();\n let kernel: Kernel;\n try {\n kernel = createKernel({\n engine,\n plugins: captured.plugins,\n settings: latestSettings.current,\n clock: browserClock(),\n });\n } catch (error) {\n setBoot({ phase: 'error', error }); // plan/graph errors surface, not throw mid-render\n if (ownsEngine) void engine.destroy();\n return;\n }\n let alive = true;\n setBoot({ phase: 'booting', kernel }); // context carries the kernel from this frame on\n kernel.start().then(\n () => {\n if (!alive) return; // unmounted mid-boot — don't open anything\n onReadyRef.current?.(kernel);\n setBoot({ phase: 'ready', kernel });\n // Kernel-owned boot policy: all tabs appear immediately in array\n // order; the `active` entry (else the first) is selected; failures\n // surface as tab status. See DocumentsCapability.openAll.\n kernel.documents.openAll(captured.initialDocuments ?? []);\n },\n (error) => {\n if (alive) setBoot({ phase: 'error', error });\n },\n );\n return () => {\n alive = false;\n // Kernel first (closes every document handle), then the engine we own —\n // ownership follows acquisition. `engine.destroy()` joins an in-flight\n // boot (or no-ops if it never started), so an unmount mid-boot is safe.\n void kernel.destroy().then(() => {\n if (ownsEngine) void engine.destroy();\n });\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // After every render: props compare by value (an inline `page={{ shadow: 'none' }}` is a new\n // object each time), and settings that didn't change change nothing.\n const liveKernel = boot.phase === 'error' ? null : boot.kernel;\n useEffect(() => {\n liveKernel?.updateSettings(latestSettings.current);\n });\n\n if (boot.phase === 'error') {\n return <>{renderError ? renderError(boot.error) : null}</>;\n }\n if (!boot.kernel) return null; // pre-effect first render only\n return (\n <KernelCtx.Provider value={boot.kernel}>\n {boot.phase === 'ready' ? children : (fallback ?? null)}\n </KernelCtx.Provider>\n );\n});\nexport const EmbedPDF = Viewer;\n\n/**\n * PageContext — the seam. A layer depends only on this, never on the Stage. So the\n * same layer works inside a virtualized Stage and in a standalone <PageView>.\n * Its members are documented on `PageContext` in `@embedpdf/web`.\n */\nexport type PageContextValue = PageContext<PageTransform, PageViewDemand>;\n\nconst PageCtx = createContext<PageContextValue | null>(null);\nexport const PageProvider = PageCtx.Provider;\n\nexport function usePage(): PageContextValue {\n const context = useContext(PageCtx);\n if (!context) throw new Error('usePage must be used inside <PageView> or a <Stage> page');\n return context;\n}\n"],"mappings":";;;;;;;;;;;AAcA,SAAgB,UAAU,OAAuC;CAC/D,QAAA,GAAA,cAAA,mBAAA,CAA0B,KAAK;AACjC;;;;;;;;ACXA,MAAM,yBAAS,IAAI,IAAY;;;;;;;AAQ/B,SAAgB,QAAiB;CAC/B,IAAI;EACF,OAAO,QAAQ,IAAI,aAAa;CAClC,QAAQ;EACN,OAAO;CACT;AACF;;AAGA,SAAgB,QAAQ,KAAa,SAAuB;CAC1D,IAAI,CAAC,MAAM,KAAK,OAAO,IAAI,GAAG,GAAG;CACjC,OAAO,IAAI,GAAG;CACd,QAAQ,KAAK,cAAc,SAAS;AACtC;;;AC0CA,MAAM,aAAA,GAAA,MAAA,cAAA,CAAyC,IAAI;;AAEnD,MAAM,oBAAA,GAAA,MAAA,cAAA,CAAgD,IAAI;AAE1D,SAAgB,YAAoB;CAClC,MAAM,UAAA,GAAA,MAAA,WAAA,CAAoB,SAAS;CACnC,IAAI,CAAC,QAAQ,MAAM,IAAI,MAAM,mDAAmD;CAChF,OAAO;AACT;;;;;;;AAYA,SAAgB,eAAe,EAAE,QAAQ,YAAiC;CACxE,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAU,UAAX;EAAoB,OAAO;EAAS;CAA6B,CAAA;AAC1E;AAEA,MAAa,gBAAoB,MAAoB,UACnD,SAAS,SAAU,KAAK,WAAW,MAAM,UAAU,KAAK,OAAO,MAAM,MAAM,SAAS,MAAM,EAAE;;AAG9F,SAAgB,eACd,QACA,UAA0C,OAAO,IAC9C;CACH,MAAM,SAAS,UAAU;CACzB,MAAM,QAAA,GAAA,MAAA,OAAA,CAA+B,IAAI;CACzC,MAAM,YAAY;EAChB,MAAM,OAAO,OAAO,MAAM;EAC1B,IAAI,KAAK,WAAW,QAAQ,KAAK,QAAQ,GAAG,IAAI,GAAG,OAAO,KAAK,QAAQ;EACvE,KAAK,UAAU,EAAE,GAAG,KAAK;EACzB,OAAO;CACT;CACA,QAAA,GAAA,MAAA,qBAAA,CAA4B,OAAO,WAAW,KAAK,GAAG;AACxD;AAEA,SAAgB,sBAAqC;CACnD,OAAO,gBAAgB,WAAW,OAAO,UAAU,YAAY,CAAC;AAClE;;AAGA,SAAgB,gBAA+B;CAC7C,MAAM,SAAS,iBAAiB;CAChC,MAAM,SAAS,oBAAoB;CACnC,OAAO,UAAU;AACnB;;;;;;;AAQA,SAAgB,mBAAkC;CAChD,QAAA,GAAA,MAAA,WAAA,CAAkB,gBAAgB;AACpC;;AAOA,SAAgB,cAAc,EAAE,IAAI,YAAgC;CAClE,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAC,iBAAiB,UAAlB;EAA2B,OAAO;EAAK;CAAoC,CAAA;AACpF;;;;;;;;;;;AAqBA,SAAgB,aAAa,EAAE,WAAW,MAAM,QAAQ,OAAO,YAA+B;CAC5F,MAAM,WAAW,YAAY;CAC7B,IAAI,SAAS,WAAW,SAAS,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAA,kBAAA,UAAA,EAAG,SAAW,CAAA;CACtD,IAAI,SAAS,WAAW,YAAY,QAClC,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAA,kBAAA,UAAA,EAAA,UAAG,OAAO,QAA8B,EAAI,CAAA;CAErD,IAAI,SAAS,WAAW,WAAW,OAAO,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAA,kBAAA,UAAA,EAAA,UAAG,MAAM,QAA8B,EAAI,CAAA;CAC5F,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAA,kBAAA,UAAA,EAAA,UAAG,SAAW,CAAA;AACvB;;;;;;;;;;;;;;;AAgBA,SAAgB,cAAiB,OAA8B;CAC7D,MAAM,SAAS,UAAU;CACzB,MAAM,aAAa,sBAAsB,KAAK;CAC9C,IAAI,YAAY,OAAO;CACvB,IAAI,OAAO,QAAQ,KAAK,MAAM,YAAY,QAAA,GAAA,eAAA,WAAA,CAAkB,QAAQ,KAAK;CACzE,OAAO,OAAO,WAAW,KAAK;AAChC;;;AAIA,SAAgB,sBAAyB,OAAqC;CAC5E,MAAM,SAAS,iBAAiB;CAChC,OAAO,gBAAgB,WAAW,OAAO,cAAc,OAAO,UAAU,KAAA,CAAS,CAAC;AACpF;;;;;;;;AASA,SAAgB,YACd,OACA,QACA,UAA0C,OAAO,IAC9C;CACH,MAAM,SAAS,UAAU;CACzB,MAAM,SAAS,iBAAiB;CAChC,MAAM,aAAa,sBAAsB,KAAK,KAAK,OAAO,WAAW,OAAO,UAAU,KAAA,CAAS;CAC/F,MAAM,QAAA,GAAA,MAAA,OAAA,CAA+B,IAAI;CACzC,MAAM,YAAY;EAChB,MAAM,OAAO,OAAO,UAAU;EAC9B,IAAI,KAAK,WAAW,QAAQ,KAAK,QAAQ,GAAG,IAAI,GAAG,OAAO,KAAK,QAAQ;EACvE,KAAK,UAAU,EAAE,GAAG,KAAK;EACzB,OAAO;CACT;CACA,QAAA,GAAA,MAAA,qBAAA,CAA4B,OAAO,WAAW,KAAK,GAAG;AACxD;;;;;;;;;;AAWA,SAAgB,oBACd,OACA,QACA,UACA,UAA0C,OAAO,IAC9C;CACH,MAAM,SAAS,UAAU;CACzB,MAAM,aAAa,sBAAsB,KAAK;CAC9C,MAAM,QAAA,GAAA,MAAA,OAAA,CAA+B,IAAI;CACzC,MAAM,YAAY;EAChB,IAAI;EACJ,IAAI,eAAe,MACjB,OAAO;OAEP,IAAI;GACF,OAAO,OAAO,UAAU;EAC1B,QAAQ;GACN,OAAO;EACT;EAEF,IAAI,KAAK,WAAW,QAAQ,KAAK,QAAQ,GAAG,IAAI,GAAG,OAAO,KAAK,QAAQ;EACvE,KAAK,UAAU,EAAE,GAAG,KAAK;EACzB,OAAO;CACT;CACA,QAAA,GAAA,MAAA,qBAAA,CAA4B,OAAO,WAAW,KAAK,GAAG;AACxD;;;;;;;;AASA,SAAgB,mBACd,OACA,QACA,SACM;CACN,MAAM,aAAa,sBAAsB,KAAK;CAC9C,MAAM,cAAA,GAAA,MAAA,OAAA,CAAoB,OAAO;CACjC,WAAW,UAAU;CACrB,MAAM,aAAA,GAAA,MAAA,OAAA,CAAmB,MAAM;CAC/B,UAAU,UAAU;CACpB,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,IAAI,CAAC,YAAY;EACjB,OAAO,UAAU,QAAQ,UAAU,CAAC,EAAE,UAAU,WAAW,QAAQ,KAAK,CAAC;CAC3E,GAAG,CAAC,UAAU,CAAC;AACjB;;;;;;;;AASA,SAAgB,eAAoC;CAClD,OAAO,cAAcA,eAAAA,cAAc;AACrC;;AAGA,SAAS,YACP,OACA,MACA,OACA,QACU;CACV,MAAM,SAAS,iBAAiB;CAChC,OAAO,gBAAgB,WAAW;EAChC,MAAM,aAAa,OAAO,cAAc,OAAO,UAAU,KAAA,CAAS;EAClE,MAAM,QAAQ,aAAa,KAAK,UAAU,IAAI;EAC9C,OAAO,SAAS,OAAO,KAAK,IAAK;CACnC,GAAGC,eAAAA,YAAY;AACjB;;;;;;;;AASA,SAAgB,YACd,QACU;CACV,OAAO,YAAYD,eAAAA,gBAAgBE,eAAAA,cAAc,MAAMA,eAAAA,cAAc,OAAO,MAAM;AACpF;;;;;;AAOA,SAAgB,kBACd,QACU;CACV,OAAO,YAAYF,eAAAA,gBAAgBG,eAAAA,eAAe,MAAMA,eAAAA,eAAe,OAAO,MAAM;AACtF;;AAGA,SAAgB,kBACd,QACA,SACM;CACN,mBAAmBH,eAAAA,gBAAgB,QAAQ,OAAO;AACpD;AAEA,MAAM,WAAgC,OAAO,OAAO,CAAC,CAAC;;;;;;;AAQtD,SAAgB,cAAmC;CACjD,MAAM,aAAa,cAAc;CACjC,OAAO,gBAAgB,WACrB,aAAa,OAAO,UAAU,UAAU,UAAU,IAAI,QACxD;AACF;;;;;;;AAQA,SAAgB,kBACd,QACU;CACV,OAAO,gBAAgB,WAAW;EAChC,MAAM,WAAW,OAAO,YAAY;EACpC,OAAO,SAAS,OAAO,QAAQ,IAAK;CACtC,GAAGC,eAAAA,YAAY;AACjB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAyFA,MAAa,UAAA,GAAA,MAAA,WAAA,CAAgD,SAAS,OACpE,EACE,QACA,SACA,kBACA,UACA,aACA,SACA,UACA,OACA,QACA,MACA,YAEF,KACA;CAIA,MAAM,WAAA,GAAA,MAAA,OAAA,CAAiB;EAAE;EAAQ;EAAS;CAAiB,CAAC;CAG5D,MAAM,YAAA,GAAA,eAAA,iBAAA,CAA4B;EAAE;EAAU;EAAO;EAAQ;CAAK,CAAC;CACnE,MAAM,kBAAA,GAAA,MAAA,OAAA,CAAwB,QAAQ;CACtC,eAAe,UAAU;CACzB,MAAM,UAAA,GAAA,MAAA,OAAA,CAAgB,KAAK;CAC3B,IAAI,MAAM,KAAK,CAAC,OAAO,SACjB;MAAA,QAAQ,QAAQ,WAAW,UAAU,QAAQ,QAAQ,YAAY,SAAS;GAC5E,OAAO,UAAU;GACjB,QAAQ,KACN,uNAGF;EACF;;CAGF,MAAM,CAAC,MAAM,YAAA,GAAA,MAAA,SAAA,CAA+B;EAAE,OAAO;EAAW,QAAQ;CAAK,CAAC;CAC9E,MAAM,cAAA,GAAA,MAAA,OAAA,CAAoB,OAAO;CACjC,WAAW,UAAU;CAErB,CAAA,GAAA,MAAA,oBAAA,CACE,WACO,KAAK,UAAU,UAAU,KAAK,SAAS,MAC9C,CAAC,IAAI,CACP;CACA,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,MAAM,WAAW,QAAQ;EAKzB,MAAM,aAAa,OAAO,SAAS,WAAW;EAC9C,MAAM,SAAiB,aAClB,SAAS,OAAyB,IAClC,SAAS;EACd,KAAA,GAAA,eAAA,cAAA,CAAkB,MAAM,GAAG,OAAO,OAAO;EACzC,IAAI;EACJ,IAAI;GACF,UAAA,GAAA,eAAA,aAAA,CAAsB;IACpB;IACA,SAAS,SAAS;IAClB,UAAU,eAAe;IACzB,QAAA,GAAA,cAAA,aAAA,CAAoB;GACtB,CAAC;EACH,SAAS,OAAO;GACd,QAAQ;IAAE,OAAO;IAAS;GAAM,CAAC;GACjC,IAAI,YAAY,OAAY,QAAQ;GACpC;EACF;EACA,IAAI,QAAQ;EACZ,QAAQ;GAAE,OAAO;GAAW;EAAO,CAAC;EACpC,OAAO,MAAM,CAAC,CAAC,WACP;GACJ,IAAI,CAAC,OAAO;GACZ,WAAW,UAAU,MAAM;GAC3B,QAAQ;IAAE,OAAO;IAAS;GAAO,CAAC;GAIlC,OAAO,UAAU,QAAQ,SAAS,oBAAoB,CAAC,CAAC;EAC1D,IACC,UAAU;GACT,IAAI,OAAO,QAAQ;IAAE,OAAO;IAAS;GAAM,CAAC;EAC9C,CACF;EACA,aAAa;GACX,QAAQ;GAIR,OAAY,QAAQ,CAAC,CAAC,WAAW;IAC/B,IAAI,YAAY,OAAY,QAAQ;GACtC,CAAC;EACH;CAEF,GAAG,CAAC,CAAC;CAIL,MAAM,aAAa,KAAK,UAAU,UAAU,OAAO,KAAK;CACxD,CAAA,GAAA,MAAA,UAAA,OAAgB;EACd,YAAY,eAAe,eAAe,OAAO;CACnD,CAAC;CAED,IAAI,KAAK,UAAU,SACjB,OAAO,iBAAA,GAAA,kBAAA,IAAA,CAAA,kBAAA,UAAA,EAAA,UAAG,cAAc,YAAY,KAAK,KAAK,IAAI,KAAO,CAAA;CAE3D,IAAI,CAAC,KAAK,QAAQ,OAAO;CACzB,OACE,iBAAA,GAAA,kBAAA,IAAA,CAAC,UAAU,UAAX;EAAoB,OAAO,KAAK;EAC7B,UAAA,KAAK,UAAU,UAAU,WAAY,YAAY;CAChC,CAAA;AAExB,CAAC;AACD,MAAa,WAAW;AASxB,MAAM,WAAA,GAAA,MAAA,cAAA,CAAiD,IAAI;AAC3D,MAAa,eAAe,QAAQ;AAEpC,SAAgB,UAA4B;CAC1C,MAAM,WAAA,GAAA,MAAA,WAAA,CAAqB,OAAO;CAClC,IAAI,CAAC,SAAS,MAAM,IAAI,MAAM,0DAA0D;CACxF,OAAO;AACT"}
|