@embedpdf/plugin-annotation 3.0.0-next.14 → 3.0.0-next.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +129 -0
  2. package/dist/authoring.cjs +12 -2
  3. package/dist/authoring.d.cts +2 -2
  4. package/dist/authoring.d.ts +2 -2
  5. package/dist/authoring.js +2 -3
  6. package/dist/contract/host.cjs +18 -2
  7. package/dist/contract/host.d.cts +3 -2
  8. package/dist/contract/host.d.ts +3 -2
  9. package/dist/contract/host.js +3 -2
  10. package/dist/contract-BK4LHe9v.d.cts +1127 -0
  11. package/dist/contract-BOo9iZKo.d.ts +1127 -0
  12. package/dist/contract-CPzpRG3h.cjs +692 -0
  13. package/dist/contract-CPzpRG3h.cjs.map +1 -0
  14. package/dist/contract-b2e_LZvu.js +651 -0
  15. package/dist/contract-b2e_LZvu.js.map +1 -0
  16. package/dist/contract.cjs +11 -10
  17. package/dist/contract.d.cts +2 -3
  18. package/dist/contract.d.ts +2 -3
  19. package/dist/contract.js +2 -4
  20. package/dist/host-contract-BbjhS_Fo.d.ts +310 -0
  21. package/dist/host-contract-CxrQPcPj.js +52 -0
  22. package/dist/host-contract-CxrQPcPj.js.map +1 -0
  23. package/dist/{contract-a4eaQWga.cjs → host-contract-DY9jFPPF.cjs} +36 -16
  24. package/dist/host-contract-DY9jFPPF.cjs.map +1 -0
  25. package/dist/host-contract-Nzxwh0XS.d.cts +310 -0
  26. package/dist/index.cjs +4985 -418
  27. package/dist/index.cjs.map +1 -1
  28. package/dist/index.d.cts +12 -17
  29. package/dist/index.d.ts +12 -17
  30. package/dist/index.js +4937 -390
  31. package/dist/index.js.map +1 -1
  32. package/package.json +11 -14
  33. package/dist/authoring-BhtyfsMx.d.cts +0 -37
  34. package/dist/authoring-R4ezdnXd.d.ts +0 -37
  35. package/dist/capability-Bt320iJP.cjs +0 -3249
  36. package/dist/capability-Bt320iJP.cjs.map +0 -1
  37. package/dist/capability-CoL4na7g.js +0 -3226
  38. package/dist/capability-CoL4na7g.js.map +0 -1
  39. package/dist/contract-C7rcuNp_.d.cts +0 -34
  40. package/dist/contract-CM2Ovdq_.d.ts +0 -34
  41. package/dist/contract-DtXE1hjY.js +0 -32
  42. package/dist/contract-DtXE1hjY.js.map +0 -1
  43. package/dist/contract-a4eaQWga.cjs.map +0 -1
  44. package/dist/internal.cjs +0 -8
  45. package/dist/internal.d.cts +0 -14
  46. package/dist/internal.d.ts +0 -14
  47. package/dist/internal.js +0 -5
  48. package/dist/seam-6pJeKOOJ.cjs +0 -296
  49. package/dist/seam-6pJeKOOJ.cjs.map +0 -1
  50. package/dist/seam-C4L_7rDt.js +0 -225
  51. package/dist/seam-C4L_7rDt.js.map +0 -1
  52. package/dist/tools-B3i1gjRs.cjs +0 -589
  53. package/dist/tools-B3i1gjRs.cjs.map +0 -1
  54. package/dist/tools-DsfqBn_a.js +0 -566
  55. package/dist/tools-DsfqBn_a.js.map +0 -1
  56. package/dist/types-BvB1vUKb.js +0 -20
  57. package/dist/types-BvB1vUKb.js.map +0 -1
  58. package/dist/types-ChRnKozg.d.cts +0 -1246
  59. package/dist/types-Dlhdw2k_.cjs +0 -31
  60. package/dist/types-Dlhdw2k_.cjs.map +0 -1
  61. package/dist/types-Dmz3eRSG.d.ts +0 -1246
@@ -1,1246 +0,0 @@
1
- import { Annot, AnnotationFlags, AnnotationProps, AnnotationPropsPatch, ChromeNode, ClickCreate, CreationDraftAnchor, Geom, Id, InkStraightenOptions, Model, PropKey, PropSpec, Rect, RenderItem, RichTextRange, SnapSettings, Subtype, TextEndAnchor, TextQuad, Vec, ViewEnv } from "@embedpdf/core-annotation";
2
- import { AnnotationAppearanceImage, AnnotationDTO, AnnotationDraft, AnnotationPatch, AnnotationRef, AttachmentContent, AttachmentFileSource, BinarySource, CommentThread, InkIntent, LineDimensionCaption, LineLeader, PageMeasurementViewport, PdfActionTree, PdfLinkTarget, PdfMeasure, PdfPoint, PdfRect, RichTextParagraph, SerializedEngineError, ShapeDimensionCaption } from "@embedpdf/engine-core/runtime";
3
- import { DocumentEvent, PageObjectNumber } from "@embedpdf/core";
4
- import { AnnotCommitEntry, AnnotCommitResult } from "@embedpdf/plugin-actions/contract/host";
5
- import { PageRotation } from "@embedpdf/core-geometry";
6
- //#region src/rich-text.d.ts
7
- type TextFormat = 'bold' | 'italic' | 'underline';
8
- /** The editor's selection inside one free-text annotation: flat offsets over
9
- * the plain projection (paragraphs joined by `\r`). */
10
- interface TextSelection extends RichTextRange {
11
- id: string;
12
- }
13
- /** The face a run or a body names. */
14
- interface Face {
15
- family: string;
16
- weight?: number;
17
- italic?: boolean;
18
- }
19
- //#endregion
20
- //#region src/tools.d.ts
21
- /**
22
- * How a stamp-family tool resolves the image bytes it places — pure DATA, so a
23
- * tool table stays JSON-serializable (config can come from a file, a DB, or the
24
- * server). Evaluated at CLICK time, so the user picks the spot first, then the
25
- * source:
26
- * - `{ kind: 'bytes' }` — fixed bytes: a company rubber-stamp.
27
- * - `{ kind: 'prompt' }` — ask the environment for bytes. The plugin does NOT
28
- * know how (a file dialog is DOM); the file-picker port (`FilePickerProvider`),
29
- * installed by the framework adapter, fulfils it. Resolve `null` there to cancel.
30
- */
31
- type StampSourceSpec = {
32
- kind: 'bytes';
33
- source: BinarySource;
34
- } | PromptSourceSpec;
35
- /**
36
- * The ask-the-environment resolution, shared by every click-then-pick tool
37
- * (a stamp's `'prompt'` source, the file-attachment tool). `accept` is the
38
- * file-dialog filter the picker request carries — a UX hint only, the engine
39
- * sniffs/validates the bytes for real. Unset = any file.
40
- */
41
- interface PromptSourceSpec {
42
- kind: 'prompt';
43
- accept?: string;
44
- }
45
- /**
46
- * The tool `armStamp` activates — and the only tool an armed payload survives
47
- * onto (plus the legacy `annotation-stamp` tag, honoured for embedder configs
48
- * written before the tags were unified). Placement consults the armed payload
49
- * BEFORE the active tool's own `source` spec, so a payload must not outlive
50
- * its tool onto a sibling stamp preset: an `image` preset's click has to open
51
- * its own picker, not silently place whatever the stamp panel last armed.
52
- */
53
- declare const ARMED_STAMP_TOOL_ID = "stamp";
54
- /** Declarative result of committing a text selection while a tool is active. */
55
- type SelectionAuthoring = {
56
- kind: 'markup';
57
- } | {
58
- kind: 'text-edit';
59
- operation: 'insert' | 'replace';
60
- };
61
- /**
62
- * The armed tool's IN-PAGE preview:
63
- * - `footprint` — the EXACT box a click would place, in the page (content
64
- * space, page-clamped, WYSIWYG). Meaningful only when the box is
65
- * determinable: an armed stamp payload, or a `clickCreate` size.
66
- * - `false` — no preview.
67
- * WHICH tool is armed is the CURSOR's job, not a ghost's: give the tool an
68
- * image cursor built from your toolbar icon (the hub's `setToolCursor` + the
69
- * web package's `svgCursor`), and the hub's claim/gap arbitration keeps it
70
- * honest over annotations, form fields, and page gaps.
71
- */
72
- type GhostPolicy = false | {
73
- mode: 'footprint';
74
- };
75
- /** Time/geometry policy for freehand ink authoring. */
76
- interface InkAuthoringOptions {
77
- /** Group strokes made within this interval into one `/InkList`; 0 commits immediately. */
78
- groupStrokesMs?: number;
79
- /** Optional pure straight-line recognition applied to each completed stroke. */
80
- straighten?: InkStraightenOptions;
81
- }
82
- /**
83
- * The creation properties each known authoring kind actually consumes. This is
84
- * deliberately separate from the flat internal patch vocabulary: it gives tool
85
- * configuration precise compile-time and runtime validation while mixed-selection
86
- * edits can keep using `AnnotationPropsPatch`.
87
- */
88
- declare const TOOL_DEFAULT_KEYS: {
89
- readonly square: readonly ["color", "interiorColor", "opacity", "strokeWidth", "border"];
90
- readonly circle: readonly ["color", "interiorColor", "opacity", "strokeWidth", "border"];
91
- readonly line: readonly ["color", "interiorColor", "opacity", "strokeWidth", "border", "lineEndings"];
92
- readonly polygon: readonly ["color", "interiorColor", "opacity", "strokeWidth", "border"];
93
- readonly polyline: readonly ["color", "interiorColor", "opacity", "strokeWidth", "border", "lineEndings"];
94
- readonly ink: readonly ["color", "opacity", "strokeWidth", "blendMode"];
95
- readonly 'free-text': readonly ["fontFamily", "fontSize", "fontColor", "bold", "italic", "underline", "textAlign", "opacity", "interiorColor", "color", "strokeWidth", "border"];
96
- readonly 'free-text-callout': readonly ["fontFamily", "fontSize", "fontColor", "bold", "italic", "underline", "textAlign", "opacity", "interiorColor", "color", "strokeWidth", "border", "lineEndings"];
97
- readonly highlight: readonly ["color", "opacity", "blendMode"];
98
- readonly underline: readonly ["color", "opacity", "blendMode"];
99
- readonly strikeout: readonly ["color", "opacity", "blendMode"];
100
- readonly squiggly: readonly ["color", "opacity", "blendMode"];
101
- readonly caret: readonly ["color", "opacity"];
102
- readonly redact: readonly ["color", "interiorColor", "opacity", "fontFamily", "fontSize", "fontColor", "textAlign"];
103
- readonly stamp: readonly [];
104
- readonly link: readonly ["link"];
105
- readonly text: readonly ["icon", "color", "opacity"];
106
- readonly 'file-attachment': readonly ["icon", "color", "opacity"];
107
- readonly 'widget-text': readonly ["color", "interiorColor", "strokeWidth", "border", "fontFamily", "fontSize", "fontColor", "textAlign"];
108
- readonly 'widget-choice': readonly ["color", "interiorColor", "strokeWidth", "border", "fontFamily", "fontSize", "fontColor", "textAlign"];
109
- readonly 'widget-toggle': readonly ["color", "interiorColor", "strokeWidth", "border"];
110
- readonly 'widget-box': readonly ["color", "interiorColor", "strokeWidth", "border"];
111
- };
112
- type ToolAuthoringKind = keyof typeof TOOL_DEFAULT_KEYS;
113
- type ToolDefaultKey<K extends ToolAuthoringKind> = (typeof TOOL_DEFAULT_KEYS)[K][number];
114
- type ToolDefaultsFor<K extends ToolAuthoringKind> = [ToolDefaultKey<K>] extends [never] ? never : Pick<AnnotationPropsPatch, ToolDefaultKey<K>>;
115
- /**
116
- * A tool definition — the public config vocabulary. Every field except `id` is
117
- * optional: the common cases are "configure a built-in" (`{ id, defaults }`) and
118
- * "add a preset" (`{ id, extends, defaults }`).
119
- */
120
- interface AnnotationToolDef<K extends ToolAuthoringKind = ToolAuthoringKind> {
121
- /** Stable tool id — the value passed to `activateTool` and the `defaults` key. */
122
- id: string;
123
- /** Inherit `subtype` / `propsKind` / `cursor` / `enables` / `source` /
124
- * `selection` / `intent` / `ink` / `meta`
125
- * from an existing tool id (a built-in or another entry). Own fields win. */
126
- extends?: string;
127
- /** The ROUTING KIND this tool authors — the core's client kind (the geometry
128
- * it draws + the props key). Usually the PDF subtype, but not always:
129
- * `free-text-callout` routes the callout gesture onto a free-text
130
- * annotation, and `widget-text`/`widget-choice`/`widget-toggle` are client
131
- * views of the ONE PDF `widget` subtype (a form tool's commit goes through
132
- * `doc.forms`, never this plugin — see the form plugin's tool table).
133
- * Defaults to the inherited kind, or the id when neither is given. */
134
- subtype?: Subtype;
135
- /** The kind whose editable-property specs a style panel shows for this tool.
136
- * Defaults to `subtype` (a callout authors `free-text` props, for example). */
137
- propsKind?: string;
138
- /** ADVANCED: the `defaults` key this tool reads/writes. Defaults to the id, and
139
- * that is almost always right — override it only to alias a shared defaults bag
140
- * (the built-in insert-caret tool points its preset at the `caret` key). */
141
- preset?: string;
142
- /** Seed defaults for newly drawn annotations — the flat AnnotationProps patch,
143
- * merged over any inherited defaults (line endings merge per side). */
144
- defaults?: ToolDefaultsFor<K>;
145
- /**
146
- * `/F` annotation flags seeded on everything this tool creates, merged over
147
- * the drawn default (`print` set — Acrobat parity) and any inherited seed.
148
- * A note tool passes `{ noZoom: true, noRotate: true }` so its icons stay
149
- * screen-sized and upright (the spec's rule for Text annotations).
150
- */
151
- flags?: Partial<AnnotationFlags>;
152
- /** The pointer cursor while this tool is active. Defaults inherited / `crosshair`. */
153
- cursor?: string;
154
- /** Interaction capability tags this tool enables (which handlers wake up). */
155
- enables?: string[];
156
- /**
157
- * How the click-to-place payload resolves. Stamps take the full spec
158
- * ({@link StampSourceSpec}: fixed bytes or prompt); file attachments are
159
- * prompt-only ({@link PromptSourceSpec} — the file always comes from the
160
- * environment). Other kinds carry no payload.
161
- */
162
- source?: K extends 'stamp' ? StampSourceSpec : K extends 'file-attachment' ? PromptSourceSpec : never;
163
- /** What a committed text selection authors. Omit for pointer/click tools. */
164
- selection?: SelectionAuthoring;
165
- /** PDF `/IT` authored by an intent-bearing ink preset. */
166
- intent?: K extends 'ink' ? InkIntent : K extends 'line' ? 'LineDimension' : K extends 'polygon' ? 'PolygonDimension' : K extends 'polyline' ? 'PolyLineDimension' : never;
167
- /** Caption defaults for a measurement preset. Shape centers use absolute PDF coordinates. */
168
- measurement?: K extends 'line' ? {
169
- caption: LineDimensionCaption;
170
- leader?: LineLeader;
171
- } : K extends 'polygon' | 'polyline' ? {
172
- caption: ShapeDimensionCaption;
173
- } : never;
174
- /** Ink-only stroke grouping and straightening policy. */
175
- ink?: K extends 'ink' ? InkAuthoringOptions : never;
176
- /**
177
- * Place annotations UPRIGHT: counter-rotate what this tool creates against the
178
- * page's TOTAL display rotation (document /Rotate + any stage view rotation),
179
- * so it reads horizontally exactly as the author saw it — Adobe's behaviour
180
- * for stamps and text on rotated pages. WYSIWYG at authoring time: the
181
- * rotation is baked into the annotation, so a save keeps what the author saw
182
- * (other viewers apply only /Rotate — document that when view rotation is in
183
- * play). Box kinds only (stamp / free-text, where reading orientation is
184
- * meaningful); ignored by vertex kinds and callouts. Default: on for the
185
- * built-in `stamp` and `free-text` tools, off elsewhere.
186
- */
187
- upright?: boolean;
188
- /**
189
- * What a bare CLICK creates (v2's `clickBehavior`): a default-size shape
190
- * centred on the point / a default-length line from it / free-text's
191
- * type-here box — page-clamped, in PDF pt. `false` = drag-only. Defaults:
192
- * on for the built-in square/circle/line/free-text, off elsewhere.
193
- */
194
- clickCreate?: ClickCreate | false;
195
- /**
196
- * The armed-tool in-page preview ({@link GhostPolicy}). Defaults: inherited,
197
- * else off (the built-in stamp declares `footprint`).
198
- */
199
- ghost?: GhostPolicy;
200
- /** Opaque presentation hints (label/icon…) for a toolbar or cursor that builds
201
- * itself from the tool table. `capture: true` captures a line gesture without
202
- * creating an annotation (used by calibration). */
203
- meta?: Record<string, unknown>;
204
- }
205
- interface BuiltinToolKindMap {
206
- square: 'square';
207
- circle: 'circle';
208
- line: 'line';
209
- distance: 'line';
210
- perimeter: 'polyline';
211
- area: 'polygon';
212
- calibrate: 'line';
213
- polygon: 'polygon';
214
- polyline: 'polyline';
215
- ink: 'ink';
216
- 'ink-highlight': 'ink';
217
- 'free-text': 'free-text';
218
- 'free-text-callout': 'free-text-callout';
219
- highlight: 'highlight';
220
- underline: 'underline';
221
- strikeout: 'strikeout';
222
- squiggly: 'squiggly';
223
- 'insert-text': 'caret';
224
- 'replace-text': 'strikeout';
225
- redact: 'redact';
226
- stamp: 'stamp';
227
- note: 'text';
228
- attachment: 'file-attachment';
229
- link: 'link';
230
- }
231
- type DirectToolDef = { [K in ToolAuthoringKind]: Omit<AnnotationToolDef<K>, 'subtype'> & {
232
- subtype: K;
233
- }; }[ToolAuthoringKind];
234
- type BuiltinToolOverride = { [I in keyof BuiltinToolKindMap]: Omit<AnnotationToolDef<BuiltinToolKindMap[I]>, 'id' | 'extends' | 'subtype'> & {
235
- id: I;
236
- extends?: never;
237
- subtype?: never;
238
- }; }[keyof BuiltinToolKindMap];
239
- type ExtendedBuiltinToolDef = { [I in keyof BuiltinToolKindMap]: Omit<AnnotationToolDef<BuiltinToolKindMap[I]>, 'extends' | 'subtype'> & {
240
- extends: I;
241
- subtype?: never;
242
- }; }[keyof BuiltinToolKindMap];
243
- /** Public tool input: known overrides, known-base presets, or an explicit subtype. */
244
- type AnnotationToolInput = DirectToolDef | BuiltinToolOverride | ExtendedBuiltinToolDef;
245
- /** A fully-resolved tool — what the plugin, handlers, and registration loop read. */
246
- interface ResolvedTool {
247
- id: string;
248
- /** Routing token for the draw core + the created annotation's PDF subtype. */
249
- subtype: Subtype;
250
- /** The `defaults` key (always the tool id) — keeps same-subtype tools apart. */
251
- preset: string;
252
- /** The `propsFor` key for the style panel. */
253
- propsKind: string;
254
- cursor: string;
255
- enables: ReadonlySet<string>;
256
- defaults?: AnnotationPropsPatch;
257
- /** `/F` seed for created annotations (see {@link AnnotationToolDef.flags}). */
258
- flags?: Partial<AnnotationFlags>;
259
- source?: StampSourceSpec;
260
- selection?: SelectionAuthoring;
261
- intent?: InkIntent | 'LineDimension' | 'PolyLineDimension' | 'PolygonDimension';
262
- measurement?: {
263
- caption: LineDimensionCaption | ShapeDimensionCaption;
264
- leader?: LineLeader;
265
- };
266
- ink?: InkAuthoringOptions;
267
- /** Counter-rotate creations against the page's display rotation (see
268
- * {@link AnnotationToolDef.upright}). */
269
- upright: boolean;
270
- /** What a bare click creates, or `false` for drag-only. */
271
- clickCreate: ClickCreate | false;
272
- /** The armed-tool in-page preview ({@link GhostPolicy}). */
273
- ghost: GhostPolicy;
274
- meta?: Record<string, unknown>;
275
- }
276
- /**
277
- * The built-in tools — a data mirror of v2's registrations (shapes + lines + ink
278
- * + free text in the draw channel, text markup + caret behind text selection, and
279
- * the click-to-place stamp). Order is display-neutral; the toolbar owns layout.
280
- */
281
- declare const DEFAULT_TOOLS: AnnotationToolInput[];
282
- /**
283
- * Resolve {@link DEFAULT_TOOLS} + embedder overrides into the ready-to-use table.
284
- * A same-id override merges over the built-in; a new id adds a tool; `extends`
285
- * inherits from an already-defined tool. Keyed by id, so lookups are O(1).
286
- */
287
- declare function buildToolRegistry(overrides?: AnnotationToolInput[]): Map<string, ResolvedTool>;
288
- //#endregion
289
- //#region src/types.d.ts
290
- /**
291
- * Selection-chrome settings: the outline, resize/vertex handles, and the rotate
292
- * knob. ONE unit story — every length is CSS px, screen-constant across zoom
293
- * (the plugin converts to content units per event/page via the view scale).
294
- * Every color falls back to `accent`, so the common case is one line:
295
- * `annotationPlugin({ chrome: { accent: '#e91e63' } })`. Deep-partial merged
296
- * over {@link DEFAULT_CHROME}; live-adjustable via
297
- * {@link AnnotationCapability.setChrome}.
298
- */
299
- interface ChromeSettings {
300
- /** The one color every chrome piece derives from unless overridden. */
301
- accent: string;
302
- outline: {
303
- /** ONE style at rest AND while rotated — the box never flips style mid-gesture. */
304
- style: 'solid' | 'dashed';
305
- /** Stroke width, px. */
306
- width: number;
307
- /** Overrides `accent`. */
308
- color?: string;
309
- };
310
- /** Resize + vertex handles (independent of the knob — size them apart). */
311
- handles: {
312
- /** Visual square side, px. */
313
- size: number;
314
- /** Grab-zone square side, px — keep ≥ 24 for touch. */
315
- hitSize: number;
316
- fill: string;
317
- /** Overrides `accent`. */
318
- stroke?: string;
319
- };
320
- /** The rotate handle. Page-bound placement (flip/clamp) always applies. */
321
- knob: {
322
- /** Grab-dot diameter, px. */
323
- size: number;
324
- /** Grab-zone square side, px — keep ≥ 24 for touch. */
325
- hitSize: number;
326
- /** Stalk length, px — selection edge to dot centre. */
327
- offset: number;
328
- /** Draw the connector stalk. */
329
- stalk: boolean;
330
- fill: string;
331
- /** Overrides `accent` (dot outline + stalk). */
332
- stroke?: string;
333
- };
334
- /** The rotation guides shown while a rotate gesture runs: a fixed 0°/90°
335
- * reference cross + a live indicator line, drawn as full-bleed chords of the
336
- * page through the pivot. */
337
- guides: {
338
- /** Show the guides at all. Default true. */
339
- enabled: boolean;
340
- style: 'solid' | 'dashed';
341
- /** Stroke width, px. */
342
- width: number;
343
- /** The fixed reference cross. Color overrides `accent`. */
344
- axisColor?: string;
345
- axisOpacity: number;
346
- /** The line riding the live angle. Color overrides `accent`. */
347
- indicatorColor?: string;
348
- indicatorOpacity: number;
349
- };
350
- }
351
- /** Deep-partial patch for {@link ChromeSettings} — config + `setChrome` input. */
352
- interface ChromeSettingsPatch {
353
- accent?: string;
354
- outline?: Partial<ChromeSettings['outline']>;
355
- handles?: Partial<ChromeSettings['handles']>;
356
- knob?: Partial<ChromeSettings['knob']>;
357
- guides?: Partial<ChromeSettings['guides']>;
358
- }
359
- /**
360
- * Whole-document annotation hydration. The plugin always hydrates ALL
361
- * pages through `doc.annotations.listRawAll()` (one bulk request on
362
- * cloud, one worker job locally) — a comments sidebar needs every page,
363
- * and per-page lazy loading could never truthfully claim completeness.
364
- * `loading` covers the initial ingest AND a desync re-ingest; `error`
365
- * means live events still apply but the whole-document view is
366
- * incomplete until a rehydrate succeeds.
367
- */
368
- type AnnotationHydration = {
369
- status: 'loading';
370
- } | {
371
- status: 'complete';
372
- } | {
373
- status: 'error';
374
- error: unknown;
375
- } | {
376
- status: 'forbidden';
377
- };
378
- interface AnnotationState {
379
- model: Model;
380
- hydration: AnnotationHydration;
381
- chrome: ChromeSettings;
382
- /**
383
- * The armed tool's FOOTPRINT ghost: where (and what) the NEXT click would
384
- * place — the stamp's fitted image box, or a click-create tool's default
385
- * geometry — computed by the same rules the placement uses (WYSIWYG). In the
386
- * store (not the capability closure) because it is RENDERED — vector ghosts
387
- * ride `pageItems`, image ghosts (stamp) render via the framework's
388
- * `ToolGhost`. The armed bytes stay out of the store.
389
- */
390
- toolGhost: ToolGhost | null;
391
- /** Bumps on every arm/disarm — the render layer's cue to rebuild (or drop)
392
- * the ghost preview object URL. Never rendered itself. */
393
- stampArmEpoch: number;
394
- /**
395
- * The text editor's selection inside the annotation being edited (flat
396
- * offsets over its plain projection), or null. In the store because it is
397
- * READ by the property surface: while a range is held, the range keys
398
- * (`getSelectionProps`) report and take the RUNS, not the body.
399
- */
400
- textSelection: TextSelection | null;
401
- }
402
- /** The armed tool's would-be placement under the cursor (content space). */
403
- type ToolGhost = {
404
- pon: PageObjectNumber;
405
- /** The exact box the click's placement would use. */
406
- box: Rect;
407
- /** The tool's upright counter-rotation at this hover (deg, CW). */
408
- rot: number;
409
- } & ({
410
- kind: 'image';
411
- } | {
412
- kind: 'vector';
413
- toolId: string;
414
- geom: Geom;
415
- });
416
- /** Registration options for {@link annotationPlugin} — the initial values of the
417
- * live-adjustable {@link AnnotationCapability.setSnap} /
418
- * {@link AnnotationCapability.setChrome} settings. */
419
- interface AnnotationConfig {
420
- snap?: {
421
- /** Alignment guides while moving (snap to other annotations + the page).
422
- * Default true. */
423
- guides?: boolean;
424
- /** Guide snap tolerance, content units (PDF pt). Default 5. */
425
- guideThreshold?: number;
426
- /** Snap the rotate gesture onto `rotationAngles`. Default true. */
427
- rotation?: boolean;
428
- /** Default `[0, 90, 180, 270]`. */
429
- rotationAngles?: number[];
430
- /** Rotation snap tolerance, degrees. Default 4. */
431
- rotationThreshold?: number;
432
- };
433
- /** Selection-chrome styling + grab geometry (all lengths CSS px). */
434
- chrome?: ChromeSettingsPatch;
435
- /**
436
- * Add or configure authoring tools at load. Entries MERGE over the built-ins by
437
- * id (configure one — `{ id: 'ink', defaults: { strokeWidth: 6 } }`), ADD a new
438
- * tool (a fresh id), or make a preset with `extends`
439
- * (`{ id: 'arrow', extends: 'line', defaults: { lineEndings: { end: 'open-arrow' } } }`).
440
- * See {@link AnnotationToolDef}. The runtime equivalent is
441
- * {@link AnnotationCapability.registerTool}.
442
- */
443
- tools?: AnnotationToolInput[];
444
- }
445
- type AnnotationAction = {
446
- type: 'SET_MODEL';
447
- model: Model;
448
- } | {
449
- type: 'SET_HYDRATION';
450
- hydration: AnnotationHydration;
451
- } | {
452
- type: 'SET_CHROME';
453
- patch: ChromeSettingsPatch;
454
- } | {
455
- type: 'SET_TOOL_GHOST';
456
- ghost: ToolGhost | null;
457
- } | {
458
- type: 'SET_TEXT_SELECTION';
459
- selection: TextSelection | null;
460
- } | {
461
- type: 'STAMP_ARM_CHANGED';
462
- };
463
- /** A plugin (forms, links) marks some annotations as interactive: while engaged,
464
- * they render their own DOM and are NOT geometry-editable. Suspend → editable. */
465
- /**
466
- * One clickable link area on a page (content space): a standalone link
467
- * annotation, or one segment of a parent's ATTACHED link. See
468
- * {@link AnnotationHostCapability.linkItemsOn}.
469
- */
470
- interface LinkNavItem {
471
- /** Stable per-page key: the annot id, `#n`-suffixed for attached segments. */
472
- id: string;
473
- rect: Rect;
474
- target: PdfLinkTarget;
475
- /**
476
- * True for a link CHILD riding an editable annotation (an `/RT /Group`
477
- * subordinate) — a PROPERTY of its parent while authoring, a nav behavior
478
- * only while reading. The nav layer stands its anchors down for attached
479
- * items whenever the active tool enables `annotation-edit`, so the parent
480
- * stays selectable/movable; standalone document links navigate regardless.
481
- */
482
- attached: boolean;
483
- /** The full payload-carrying `/A` tree, when one exists — the action
484
- * engine's dispatch input. `target` remains its root projection. */
485
- activate?: PdfActionTree;
486
- /** The annotation ref, carried for ActionSource context. */
487
- ref?: AnnotationRef;
488
- /** Which `/AA` hover trees this link carries — the nav layer's pump flags
489
- * (tree-less hover must cost zero dispatches). Links are behavior-inert
490
- * to the annotation plane's hover feed while navigable, so THEIR
491
- * cursorEnter/cursorExit can only fire from the LinkLayer anchors. */
492
- hoverEvents?: {
493
- enter: boolean;
494
- exit: boolean;
495
- };
496
- }
497
- interface Behavior {
498
- id: string;
499
- matches(a: {
500
- subtype: Subtype;
501
- ref: AnnotationRef | null;
502
- }): boolean;
503
- engaged(): boolean;
504
- }
505
- /**
506
- * The current selection's editable properties, ready to render: the ordered
507
- * {@link PropSpec}s EVERY selected kind declares (a mixed selection shows the
508
- * shared subset, in the first kind's order), the first member's `values`, and
509
- * which keys differ across members (`mixed` — render an indeterminate control).
510
- * Empty `specs` = nothing selected / nothing editable.
511
- */
512
- interface SelectionProps {
513
- specs: PropSpec[];
514
- values: Partial<AnnotationProps>;
515
- mixed: PropKey[];
516
- }
517
- /**
518
- * The selection's `/F` flag state, ready for a menu/sidebar: one value per
519
- * flag — `true`/`false` when every selected annotation agrees, `null` when
520
- * they differ (render an indeterminate control). The read half of
521
- * {@link AnnotationCapability.updateSelectionFlags}.
522
- */
523
- type SelectionFlags = { [K in keyof AnnotationFlags]: boolean | null; };
524
- /**
525
- * A free-text annotation projected for the framework: the box (content space,
526
- * live gesture applied) + the plain text + an `editing` flag + a ready-to-spread
527
- * CSS style. The framework renders ONE editable element from this and nothing
528
- * more — all the mapping (fonts, colours, alignment) is done here, once. The
529
- * element paints the TEXT only: the box's fill and border are the vector
530
- * scene's (`pageItems`), the same for a plain box and a callout, so the live
531
- * view matches the baked appearance.
532
- */
533
- interface TextItem {
534
- id: Id;
535
- ref: AnnotationRef | null;
536
- box: Rect;
537
- contents: string;
538
- /** The rich paragraphs the editor renders and edits (run deltas over the
539
- * body, which `css` carries). `contents` is their plain projection. */
540
- richText: {
541
- paragraphs: RichTextParagraph[];
542
- };
543
- editing: boolean;
544
- /** Applied rotation (deg, CW). `box` is the UNROTATED text box; the framework
545
- * rotates the editable element about its centre by this. 0/undefined = none. */
546
- rot?: number;
547
- css: {
548
- fontFamily: string;
549
- /** Content units (the framework multiplies by the page scale). The line
550
- * height is NOT here: the editor binding states the engine's line model
551
- * per face on the element itself (`@embedpdf/web`, `lineModelFor`). */
552
- fontSize: number;
553
- color: string;
554
- /** The body's formatting (runs override it as inline spans). */
555
- fontWeight: number;
556
- fontStyle: 'normal' | 'italic';
557
- textDecoration: 'none' | 'underline';
558
- align: 'left' | 'center' | 'right';
559
- padding: number;
560
- };
561
- }
562
- /**
563
- * The PUBLIC annotation API — the documented, stable surface for application code
564
- * (toolbars, sidebars, app logic). Resolve it with the token re-exported from the
565
- * package root (`@embedpdf/plugin-annotation`).
566
- *
567
- * Framework-only plumbing (render projection, pointer gestures, behavior
568
- * registration) lives on {@link AnnotationHostCapability}, reachable through
569
- * `@embedpdf/plugin-annotation/contract/host` (and the framework's `/internal`
570
- * entry). Both are the SAME runtime
571
- * object — two typed lenses on one token — so app code simply can't see the host
572
- * methods.
573
- */
574
- /**
575
- * Per-thread action gates, composed from two axes: authority (the
576
- * engine's collab-resolver mirrors — `allowsAnnotationCreate` for
577
- * reply/status, `allowsAnnotationMutation` against each target's
578
- * stamped owner for edit/delete) and PDF state (the two lock flags
579
- * gate DIFFERENT aspects, ISO 32000 Table 167: `lockedContents` blocks
580
- * text edits, `locked` blocks deletion). A courtesy, not the guard — the
581
- * engine independently enforces every write.
582
- */
583
- interface CommentPermissions {
584
- /** A reply is an annotation CREATE. */
585
- canReply: boolean;
586
- /** Editing this comment's text (`contents`) — gated by `lockedContents`. */
587
- canEditText: boolean;
588
- /** Deleting this one annotation — gated by `locked`. */
589
- canDelete: boolean;
590
- /** A status change is a NEW hidden annotation — create authority only,
591
- * never edit rights on someone else's comment. */
592
- canSetStatus: boolean;
593
- /** `canDelete` over EVERY thread member (root, replies, grouped parts,
594
- * state annotations) — the whole-thread preflight. */
595
- canDeleteThread: boolean;
596
- }
597
- interface ThreadDeleteResult {
598
- deleted: AnnotationRef[];
599
- failed: Array<{
600
- ref: AnnotationRef;
601
- error: unknown;
602
- }>;
603
- }
604
- /**
605
- * The conversation plane's surface: a derived, memoized threads index over
606
- * the annotation substrate, plus the ISO-native verbs. Every verb compiles
607
- * down to plain annotation creates/patches/deletes — one optimistic
608
- * pipeline, no second write path — so remote SSE events, own edits, and
609
- * hydration all update `threads()` for free.
610
- *
611
- * Threads carry `pageObjectNumber` (identity is PON, like everything
612
- * else); DISPLAY order is a behavior of `threads()` (sorted against the
613
- * live layout at computation time), and display labels (`pageIndex`) are a
614
- * framework-hook enrichment — the layer that watches both stores.
615
- */
616
- interface CommentsApi {
617
- /** Every comment thread in the document, display-ordered, memoized. */
618
- threads(): CommentThread[];
619
- /** The thread containing ANY member ref (root, reply, grouped part, or
620
- * state annotation), or null. */
621
- thread(ref: AnnotationRef): CommentThread | null;
622
- /** Whole-document hydration status (the sidebar's honest loading state). */
623
- hydration(): AnnotationHydration;
624
- /** Re-run whole-document hydration (desync recovery / manual refresh). */
625
- rehydrate(): Promise<void>;
626
- /** Reply to a thread. Writes FLAT (`/IRT` → the thread root, ISO reply
627
- * type), whatever member was passed — presentation stays one level. */
628
- reply(ref: AnnotationRef, text: string): Promise<AnnotationRef>;
629
- /** Edit a comment's text. Appearance-inert for every kind except
630
- * free-text and redaction (whose `/AP` paints `/Contents`). */
631
- edit(ref: AnnotationRef, text: string): Promise<void>;
632
- /**
633
- * Set this session's review status on the thread (ISO 32000 §12.5.6.3):
634
- * creates a hidden state annotation chained to the caller's previous
635
- * status when one exists, else to the root. The target annotation is
636
- * never modified. Known states: accepted / rejected / cancelled /
637
- * completed / none (custom vocabularies ride the raw create API).
638
- */
639
- setStatus(ref: AnnotationRef, state: string): Promise<void>;
640
- /** Toggle this session's Marked-model checkmark on the thread. */
641
- setMarked(ref: AnnotationRef, marked: boolean): Promise<void>;
642
- /** Delete exactly ONE annotation (a reply, a status, or a bare root). */
643
- remove(ref: AnnotationRef): Promise<void>;
644
- /**
645
- * Delete a WHOLE thread, children first. Preflighted: when any member
646
- * fails the delete gate the call returns every blocked ref as `failed`
647
- * and deletes nothing; mid-flight races surface the same way.
648
- */
649
- removeThread(ref: AnnotationRef): Promise<ThreadDeleteResult>;
650
- /** Truthful per-thread action gates for the ref's thread. */
651
- permissionsFor(ref: AnnotationRef): CommentPermissions;
652
- }
653
- interface RecalibrationReport {
654
- pon: number;
655
- scale: PdfMeasure;
656
- /** Page-wide read failure: no complete annotation set was available to recalculate. */
657
- error?: SerializedEngineError;
658
- updated: AnnotationRef[];
659
- skipped: {
660
- ref: AnnotationRef;
661
- reason: 'no-authority' | 'locked' | 'foreign-measure' | 'unavailable';
662
- }[];
663
- failed: {
664
- ref: AnnotationRef;
665
- error: SerializedEngineError;
666
- }[];
667
- }
668
- interface CapturedAnnotationDraft {
669
- tool: string;
670
- pon: number;
671
- /** Original PDF user space, matching the engine API. */
672
- from: PdfPoint;
673
- to: PdfPoint;
674
- }
675
- interface AnnotationCapability {
676
- /**
677
- * Create an annotation on a page. `draft` is the engine-core draft for its
678
- * subtype (PDF-space, sRGB). Resolves to the new annotation's durable `ref`.
679
- * The same path the draw tools use, so programmatic and interactive creation
680
- * share one optimistic flow + one event stream.
681
- */
682
- create(pon: PageObjectNumber, draft: AnnotationDraft): Promise<AnnotationRef>;
683
- /**
684
- * Patch an existing annotation. `patch` is the engine-core patch for the
685
- * annotation's subtype — style, endings, contents, geometry are ALL just
686
- * fields here (there is no separate "set style"). Attribution is automatic
687
- * from the session identity.
688
- */
689
- update(ref: AnnotationRef, patch: AnnotationPatch): Promise<void>;
690
- /** Delete an annotation by ref. */
691
- delete(ref: AnnotationRef): Promise<void>;
692
- /**
693
- * Restyle the current selection with ONE flat property patch — the write half
694
- * of {@link getSelectionProps}. Each selected annotation takes the keys its
695
- * kind declares (`propsFor`) and ignores the rest, so a single patch restyles
696
- * a mixed selection. Optimistic: the model updates immediately; one engine
697
- * write per changed member re-syncs from the authoritative DTO.
698
- */
699
- updateSelection(patch: AnnotationPropsPatch): void;
700
- /**
701
- * Merge a `/F` flags patch into the current selection — the write half of
702
- * {@link getSelectionFlags}. Optimistic; one flags-only engine write per
703
- * changed member, which never re-bakes an appearance. Deliberately NOT
704
- * blocked by `locked`: this is how you unlock (Acrobat's Locked checkbox
705
- * stays live on a locked annotation). E.g. `{ locked: true }`,
706
- * `{ print: false }`, `{ hidden: true }`.
707
- */
708
- updateSelectionFlags(patch: Partial<AnnotationFlags>): void;
709
- /**
710
- * Flip a rich-text format on the selection — the editor's Cmd/Ctrl+B/I/U
711
- * and a toolbar toggle: the text range's runs while the editor holds one,
712
- * else the selected free-text bodies. Reads the current state the way
713
- * {@link getSelectionProps} reports it and writes its inverse through
714
- * {@link updateSelection}.
715
- */
716
- toggleTextFormat(format: TextFormat): void;
717
- /**
718
- * The selection's `/F` flag state: per-flag `true`/`false`, or `null` when
719
- * the selected annotations disagree (indeterminate). `null` overall when
720
- * nothing is selected. Stable reference between model changes.
721
- */
722
- getSelectionFlags(): SelectionFlags | null;
723
- /**
724
- * The selection's editable properties: ordered specs shared by every selected
725
- * kind + current values + which keys are mixed. THE way to build a property
726
- * sidebar/toolbar — render `specs` in order, write back via
727
- * {@link updateSelection}. Stable reference between model changes.
728
- */
729
- getSelectionProps(): SelectionProps;
730
- /** The ordered property specs a TOOL's target kind declares (callout → the
731
- * free-text kind). Drives the same sidebar when nothing is selected, paired
732
- * with {@link currentDefaults}/{@link setDefaults}. */
733
- propsForTool(toolId: string): PropSpec[];
734
- canRead(): boolean;
735
- canCreate(): boolean;
736
- canEdit(ref: AnnotationRef): boolean;
737
- canDelete(ref: AnnotationRef): boolean;
738
- /** The conversation plane's read/write surface — see {@link CommentsApi}. */
739
- comments: CommentsApi;
740
- /**
741
- * The attached-link lens: an annotation's link is a `/Link` CHILD grouped
742
- * under it in the document (ISO-native, Acrobat-interoperable) — a
743
- * PROPERTY while authoring, a nav BEHAVIOR while reading. `of` derives
744
- * from the committed children (the one source of truth); `set`/`clear`
745
- * resolve when the children are committed, after which `of` reflects the
746
- * change. `updateSelection({ link })` remains the props-pipeline alias.
747
- * On the link KIND itself, `of` reads the annotation's own `/A` target
748
- * (retarget standalone links via `updateSelection`).
749
- */
750
- links: {
751
- of(ref: AnnotationRef): PdfLinkTarget | null;
752
- set(ref: AnnotationRef, target: PdfLinkTarget): Promise<void>;
753
- clear(ref: AnnotationRef): Promise<void>;
754
- };
755
- /** The annotation for a ref, or null if unknown / not yet committed. */
756
- get(ref: AnnotationRef): AnnotationDTO | null;
757
- /** Every committed annotation on a page, in z-order. */
758
- list(pon: PageObjectNumber): AnnotationDTO[];
759
- /** The selected annotations as DTOs (skips not-yet-committed drafts). */
760
- getSelected(): AnnotationDTO[];
761
- selection(): Id[];
762
- /** The current selection as durable annotation refs (skips not-yet-committed drafts). */
763
- getSelection(): AnnotationRef[];
764
- /**
765
- * Select an annotation by ref programmatically — e.g. auto-selecting a
766
- * freshly placed form widget. Selecting a group member takes the whole
767
- * group, exactly like a click. Unknown/unselectable refs no-op. `add`
768
- * extends the current selection instead of replacing it.
769
- */
770
- select(ref: AnnotationRef, options?: {
771
- add?: boolean;
772
- }): void;
773
- /** Group the current selection into one unit (the bottom-most member is the
774
- * primary; the rest become `/RT /Group` subordinates). Page-local: no-op
775
- * unless 2+ committed annotations on a single page are selected. */
776
- group(): Promise<void>;
777
- /** Ungroup the group(s) the current selection touches — every subordinate
778
- * becomes top-level again. */
779
- ungroup(): Promise<void>;
780
- /** Whether {@link group} would do something for the current selection. */
781
- canGroup(): boolean;
782
- /** Whether {@link ungroup} would do something for the current selection. */
783
- canUngroup(): boolean;
784
- /** Patch a tool's defaults for newly drawn annotations — the SAME flat
785
- * vocabulary {@link updateSelection} writes. */
786
- setDefaults(subtype: Subtype, patch: AnnotationPropsPatch): void;
787
- /** The RESOLVED full props bag a tool will use for new annotations. */
788
- currentDefaults(subtype: Subtype): AnnotationProps;
789
- /** Live-adjust snapping — wire a UI toggle here (e.g.
790
- * `setSnap({ guides: false })`). Initial values come from the plugin's
791
- * registration config ({@link AnnotationConfig}). */
792
- setSnap(patch: Partial<SnapSettings>): void;
793
- /** The current snap settings. */
794
- snapSettings(): SnapSettings;
795
- /** Live-adjust the selection chrome — wire theming here (e.g.
796
- * `setChrome({ accent: '#e91e63' })`). Deep-partial merge; initial values
797
- * come from the plugin's registration config ({@link AnnotationConfig}). */
798
- setChrome(patch: ChromeSettingsPatch): void;
799
- /** The current (fully resolved) chrome settings. */
800
- chromeSettings(): ChromeSettings;
801
- /** Rotate the current selection a quarter-turn clockwise about its centre
802
- * (a single shape's own centre / the union-box centre for a group). */
803
- rotateSelection90(): void;
804
- /** Reset the current selection to its as-authored orientation (rotation → 0). */
805
- resetSelectionRotation(): void;
806
- /**
807
- * Arm the stamp tool: `source` (PNG, JPEG, or single-page PDF bytes —
808
- * format sniffed, never trusted) becomes the content of the next stamp(s)
809
- * placed by clicking a page. The placement rect is sized from the image's
810
- * intrinsic aspect ratio around `targetWidth` (PDF points, default 150)
811
- * and centred on the click. Also activates the `'stamp'` interaction tool;
812
- * the payload stays armed for repeat placement until the tool changes or
813
- * {@link disarmStamp} is called. Resolves once the payload is validated.
814
- */
815
- armStamp(input: StampToolInput): Promise<void>;
816
- /** Drop the armed stamp payload (a tool change away from 'stamp' does this too). */
817
- disarmStamp(): void;
818
- /**
819
- * Place a stamp WITHOUT the pointer: the same validation, fit, page clamp,
820
- * `/Name`, and `/Subj` a click after {@link armStamp} produces — the one
821
- * placement law, exposed for code. Resolves to the created annotation's
822
- * ref; the placement is selected like a click's.
823
- */
824
- placeStamp(input: StampToolInput, placement: StampPlacement): Promise<AnnotationRef>;
825
- /**
826
- * Install the ONE file-picker port every click-then-pick tool resolves
827
- * through (see {@link FilePickerProvider}) — the stamp `'prompt'` source and
828
- * the file-attachment tool today. The framework adapter installs a
829
- * file-dialog provider by default; pass your own (asset library, cloud
830
- * drive — switch on `req.subtype` / `req.toolId`), or `null` to make
831
- * click-then-pick tools inert. One slot; last write wins.
832
- */
833
- setFilePickerProvider(provider: FilePickerProvider | null): void;
834
- /**
835
- * Decode and return the file embedded in a FileAttachment annotation —
836
- * bytes plus the name/mime needed to hand it to the user. Listings carry
837
- * only metadata (`FileAttachmentAnnotationDTO.file`); this is the explicit
838
- * bytes-out call. Throws when the engine transport has not shipped
839
- * attachment download (feature-detected).
840
- */
841
- downloadAttachment(ref: AnnotationRef): Promise<AttachmentContent>;
842
- /**
843
- * Register (or replace, by id) an authoring tool at runtime — the imperative
844
- * mirror of the `tools` config. Same {@link AnnotationToolDef} vocabulary
845
- * (`extends`, per-tool `defaults`, …). Returns an unregister fn.
846
- */
847
- registerTool(def: AnnotationToolInput): () => void;
848
- /**
849
- * Turn the CURRENT text selection into markup annotations — one per
850
- * selected page, from the selection's oriented per-line quads — then clear
851
- * the selection. The one-call form of what the markup tools do on
852
- * commit, made for selection-menu buttons ("Highlight") and automation.
853
- * `preset` is a tool id whose defaults style the markup (e.g.
854
- * `'highlight'`); defaults to the subtype's own. Returns false (and does
855
- * nothing) when no selection plugin is installed or nothing is selected.
856
- */
857
- markupFromSelection(subtype: Subtype, preset?: string): boolean;
858
- /** Whether a drawing gesture is active, including placement between clicks. */
859
- hasCreationDraft(): boolean;
860
- /** Commit the in-progress multi-click creation draft (no-op when there is
861
- * none, or it can't finish yet — see `creationDraftAnchor().canFinish`). */
862
- finishCreationDraft(): void;
863
- /** Discard the in-progress multi-click creation draft. */
864
- cancelCreationDraft(): void;
865
- deleteSelection(): void;
866
- deselect(): void;
867
- cancel(): void;
868
- }
869
- /** Payload for {@link AnnotationCapability.armStamp}. */
870
- interface StampToolInput {
871
- /** PNG, JPEG, or single-page PDF bytes (`Blob | Uint8Array | BinaryPayload`). */
872
- source: BinarySource;
873
- /** Placed width in PDF points (height follows the intrinsic aspect). Default 150. */
874
- targetWidth?: number;
875
- /** The placed annotation's `/Name` — the stamp identifier (standard or custom). */
876
- name?: string;
877
- /** The placed annotation's `/Subj`. */
878
- subject?: string;
879
- /**
880
- * The hover ghost's image. Either fixed bytes (PNG/JPEG) or — for vector
881
- * sources, which are only ever right at ONE on-screen size — a
882
- * {@link StampPreviewProvider} the ghost asks for a render at the device
883
- * pixel width it is displayed at. Raster sources default to themselves;
884
- * omit and the tool simply shows no ghost.
885
- */
886
- preview?: BinarySource | StampPreviewProvider;
887
- /**
888
- * The source's intrinsic size in PDF points. Raster sources are measured
889
- * from their own header, but PDF bytes carry no sniffable dimensions —
890
- * callers that know the page size (a stamp library does, from import)
891
- * pass it here so placement honours the true aspect instead of falling
892
- * back to a square.
893
- */
894
- intrinsicSize?: {
895
- width: number;
896
- height: number;
897
- };
898
- }
899
- /** The armed stamp's paintable preview, for the render layer's ghost `<img>`. */
900
- interface ArmedStampPreview {
901
- bytes: Uint8Array;
902
- mimeType?: string;
903
- }
904
- /**
905
- * Resolution-aware ghost preview: "give me this stamp at `devicePixelWidth`
906
- * pixels wide". The annotation plugin buckets the request (see
907
- * {@link previewBucket}) and caches per bucket for the arm's lifetime, so a
908
- * zoom gesture never renders per frame and one render serves a zoom range.
909
- * A stamp library renders its page lazily through its asset engine; a raster
910
- * returns itself (it cannot get sharper than its pixels).
911
- */
912
- type StampPreviewProvider = (devicePixelWidth: number) => Promise<ArmedStampPreview | null>;
913
- /**
914
- * Ghost render buckets: powers of two from 128 px up to `cap`. The same policy
915
- * the page renderer uses — one bitmap per size class, never per zoom step.
916
- */
917
- declare function previewBucket(devicePixelWidth: number, cap?: number): number;
918
- /**
919
- * Where a programmatic stamp placement lands — the inputs a click supplies.
920
- * The box is fitted and clamped exactly as the click path does it.
921
- */
922
- interface StampPlacement {
923
- pageObjectNumber: PageObjectNumber;
924
- /** Anchor in page points (content space): the placement is centred here. */
925
- at: Vec;
926
- /** Placed width in PDF points; default the payload's intrinsic size. */
927
- targetWidth?: number;
928
- /** Content rotation, degrees clockwise. Default 0. */
929
- rotation?: number;
930
- }
931
- /**
932
- * What the {@link FilePickerProvider} is asked for: which tool clicked (id +
933
- * the kind it creates), the tool's `accept` filter, and the page-space point
934
- * the click landed on — enough to route per tool (asset library for stamps,
935
- * cloud drive for attachments) or position a picker near the click. Pure
936
- * data — the request crosses the plugin↔adapter boundary as a message.
937
- */
938
- interface FilePromptRequest {
939
- toolId: string;
940
- /** The kind the placement creates — the routing key for per-tool pickers. */
941
- subtype: Subtype;
942
- /** The tool's file-dialog filter hint (from the tool def). UX only —
943
- * the engine sniffs/validates the bytes for real. */
944
- accept?: string;
945
- pon: PageObjectNumber;
946
- /** The content-space point the placement is centred on. */
947
- point: Vec;
948
- }
949
- /**
950
- * The ONE environment port behind every click-then-pick tool — the stamp
951
- * `'prompt'` source and the file-attachment tool (the file is picked AFTER
952
- * the spot): given a click, produce the file to place, `null` to cancel. The
953
- * plugin declares this contract but never implements it — "get bytes from the
954
- * environment" is a DOM concern (a file dialog), so the framework adapter
955
- * installs the implementation via
956
- * {@link AnnotationCapability.setFilePickerProvider}. This keeps the plugin
957
- * DOM-free (Rust-portable) while the zero-config file dialog works out of the
958
- * box. The return shape is the engine's file vocabulary: a stamp consumes
959
- * only `data`; an attachment embeds the whole thing. A picked `File` carries
960
- * its own name and mime — a provider returning raw bytes for an attachment
961
- * must supply `name` itself.
962
- */
963
- type FilePickerProvider = (req: FilePromptRequest) => Promise<AttachmentFileSource | null>;
964
- /**
965
- * The HOST (framework) surface: everything the render layer, the interaction hub,
966
- * and sibling plugins need, on top of the public {@link AnnotationCapability}.
967
- * Host-only — sibling plugins import the token from
968
- * `@embedpdf/plugin-annotation/contract/host`; framework implementation code may
969
- * use `/internal`. Never use either from application code.
970
- */
971
- interface AnnotationHostCapability extends AnnotationCapability {
972
- /** Page viewport cache, in original PDF coordinates. Undefined clears readiness. */
973
- setPageViewports(pon: number, viewports: PageMeasurementViewport[] | undefined, fallback: PdfMeasure): void;
974
- remeasurePage(pon: number, scale: PdfMeasure): Promise<RecalibrationReport>;
975
- onDraftCaptured(cb: (draft: CapturedAnnotationDraft) => void): () => void;
976
- /** `view` (the page's scale + total display rotation, from its transform)
977
- * projects screen-anchored (`noZoom`/`noRotate`) bodies to their effective
978
- * footprint. Pass it from the page context; absent → stored geometry. */
979
- pageItems(pon: PageObjectNumber, view?: ViewEnv): RenderItem[];
980
- /** `scale` (view px per content unit, from the page's transform) converts the
981
- * px chrome settings into content units — pass it so the knob stalk and grab
982
- * zones are screen-constant. `rotation` (the page's total display rotation)
983
- * and `zoom` (the page's zoom RELATIVE to its 100% baseline —
984
- * `transform.zoom`, NOT `viewScale`) complete the view env for
985
- * screen-anchored bodies. Absent scale → settings are read as content
986
- * units. */
987
- chrome(pon: PageObjectNumber, scale?: number, rotation?: PageRotation, zoom?: number): ChromeNode[];
988
- /** The anchor for a selection-aware floating menu: the primary page + the
989
- * selection's union box on that page (content space), or null when nothing
990
- * selectable is selected. One anchor regardless of cross-page selection.
991
- * `scale`/`rotation`/`zoom` as in {@link chrome} (the anchor page's view). */
992
- selectionAnchor(scale?: number, rotation?: PageRotation, zoom?: number): {
993
- pon: PageObjectNumber;
994
- bounds: Rect;
995
- knob?: Vec;
996
- } | null;
997
- /** The anchor + action state for a live multi-click creation draft, or null. */
998
- creationDraftAnchor(): CreationDraftAnchor | null;
999
- /** Home page while a distance draft is placing its dimension line. */
1000
- distanceCreationPage(): number | null;
1001
- /** Cache key for a page's baked appearances: the COMMITTED id + AP box of every
1002
- * baked annotation (gesture previews excluded). Changes exactly once per
1003
- * committed create/geometry-edit, so the render layer refetches rasters then —
1004
- * and only then (a stamp resize re-fits its AP engine-side, for example). */
1005
- appearanceEpoch(pon: PageObjectNumber): string;
1006
- /**
1007
- * The scale a baked appearance should render at for a desired device
1008
- * scale — `snapAppearanceScale` over the document's `renderPolicy` (the
1009
- * kernel-materialized fact; identity under `continuous`). THE identity a
1010
- * render layer keys its bake effect on: inside an appearance-lattice rung,
1011
- * zoom ticks produce the same value → no re-bake; crossing a rung re-bakes
1012
- * once. This plugin owns its raster product end to end — the policy is a
1013
- * document fact, not another plugin's possession.
1014
- */
1015
- bakeScale(renderScale: number): number;
1016
- /** The engine's rendered /AP appearance images for a page — the `baked` visual. */
1017
- appearances(pon: PageObjectNumber, scale: number, signal?: AbortSignal): Promise<AnnotationAppearanceImage[]>;
1018
- /** Convert an engine appearance rect (PDF user space) to a content-space box, so
1019
- * the renderer places the baked bitmap by its OWN `/Rect` without touching the
1020
- * PDF↔content seam. Null if the page's crop box is unknown. */
1021
- toContentBox(pon: PageObjectNumber, rect: PdfRect): Rect | null;
1022
- /** No-op since whole-document hydration: the model is seeded by
1023
- * `listRawAll()` at document open. Kept as API for layers that call it
1024
- * on page mount. */
1025
- ensurePage(pon: PageObjectNumber): void;
1026
- /**
1027
- * Drop and RE-READ one page's annotations from the engine — the hook for
1028
- * cross-plane mutations (e.g. `doc.forms.createField`/`deleteField`
1029
- * changing the page's widget population underneath this plugin). Resolves
1030
- * when the fresh page is in the model, so a caller can select what it just
1031
- * created.
1032
- */
1033
- reloadPage(pon: PageObjectNumber): Promise<void>;
1034
- /** Live hydration status — `loading` covers initial ingest AND a desync
1035
- * re-ingest. */
1036
- hydration(): AnnotationHydration;
1037
- /** Kick hydration exactly once per document; the effects layer calls
1038
- * this at registration. Subsequent calls no-op. */
1039
- ensureHydrated(): void;
1040
- /** Re-run whole-document hydration (desync recovery). Safe to call while
1041
- * one is in flight — the fresh run is chained after it. */
1042
- rehydrate(): Promise<void>;
1043
- /**
1044
- * The effects layer hands every REMOTE `annotation.*` event here: during
1045
- * a hydration window it queues (replayed by audit cursor after ingest —
1046
- * the delete-resurrection guard); otherwise it applies immediately.
1047
- */
1048
- deliverRemoteAnnotationEvent(event: DocumentEvent): void;
1049
- /** The free-text boxes on a page, ready to render as editable elements.
1050
- * `view` as in {@link pageItems}. */
1051
- textItems(pon: PageObjectNumber, view?: ViewEnv): TextItem[];
1052
- /** The id of the annotation currently being text-edited, or null. Read live (not
1053
- * from a stale render) so the editor can tell a real exit from a focus-steal. */
1054
- currentEditing(): Id | null;
1055
- /** Enter text-edit on a free-text annotation (focus its editable element). */
1056
- beginTextEdit(ref: AnnotationRef): void;
1057
- /** Enter text-edit on whatever free-text box is under a content point — wired
1058
- * to a double-click / long-press by the interaction edit handler. Returns
1059
- * whether an editable free-text was actually found and opened; false lets
1060
- * the caller fall through to a normal press (so a long-press on a highlight
1061
- * selects it instead of being swallowed by a no-op edit attempt). */
1062
- beginTextEditAt(pon: PageObjectNumber, point: Vec, scale?: number, rotation?: PageRotation, zoom?: number): boolean;
1063
- /** Apply the editor's plain text — optimistic locally, debounced to the engine. */
1064
- setContents(ref: AnnotationRef, text: string): void;
1065
- /** Apply the editor's rich paragraphs (run deltas over the body) —
1066
- * optimistic locally, debounced to the engine. The commit is plain
1067
- * `contents` while nothing overrides the body on a plain annotation, else
1068
- * `richText`. */
1069
- setRichText(ref: AnnotationRef, doc: {
1070
- paragraphs: RichTextParagraph[];
1071
- }): void;
1072
- /** The editor's selection inside the annotation (flat offsets over the
1073
- * plain projection), or null when the editor holds none. While a RANGE is
1074
- * held, `updateSelection`'s font/size/colour/format keys restyle the
1075
- * range's runs instead of the body. */
1076
- setTextSelection(ref: AnnotationRef, range: {
1077
- start: number;
1078
- end: number;
1079
- } | null): void;
1080
- /** The CSS family list for a face family a run names ("Helvetica" → a web
1081
- * stack, a registered family → its key, mounted by `mountWebFont`). */
1082
- cssFontFamily(family: string): string;
1083
- /** Leave text-edit (flush any pending write). */
1084
- endTextEdit(): void;
1085
- /** What's under a content point — for the edit handler's capture decision.
1086
- * `scale` (the page's view px per content unit) keeps grab zones
1087
- * screen-constant; `rotation` + `zoom` (relative, `transform.zoom`)
1088
- * complete the view env for screen-anchored bodies. Pass all three from
1089
- * the pointer sample. */
1090
- hitKind(pon: PageObjectNumber, point: Vec, scale?: number, rotation?: PageRotation, zoom?: number,
1091
- /** Widen grab zones to finger-sized targets (pass `pointerType === 'touch'`). */
1092
- touch?: boolean): 'handle' | 'rotate' | 'group-handle' | 'annot' | 'empty';
1093
- /**
1094
- * TOUCH CONSENT (the interaction hub's `claimsTouch` predicate): should a
1095
- * finger landing here own a TOOL gesture instead of navigating? True over
1096
- * the selection's chrome (resize handles, rotate knob, group box) and over
1097
- * the body of an already-SELECTED, transformable annotation — never over an
1098
- * unselected one, so annotation-dense pages still scroll; tap-select first
1099
- * is the platform convention (Apple Markup). Uses finger-sized grab zones.
1100
- * Pure read.
1101
- */
1102
- claimsTouchAt(pon: PageObjectNumber, point: Vec, scale?: number, rotation?: PageRotation, zoom?: number): boolean;
1103
- /** The cursor to show at a content point (resize over a handle, move/pointer over a body, else null). */
1104
- cursorAt(pon: PageObjectNumber, point: Vec, scale?: number, rotation?: PageRotation, zoom?: number): string | null;
1105
- /**
1106
- * Report the pointer's position for hover state (`null` = off every page).
1107
- * Diffs the topmost hit against the model and dispatches only on CHANGE
1108
- * (enter/leave cadence) — drives scene-level hover affordances like the
1109
- * redaction mark's applied-look preview. Called from the edit handler's
1110
- * onHover, which already samples every move for cursor claims.
1111
- */
1112
- hoverAt(at: {
1113
- pon: PageObjectNumber;
1114
- point: Vec;
1115
- scale?: number;
1116
- rotation?: PageRotation;
1117
- zoom?: number;
1118
- } | null): void;
1119
- behaviorFor(a: {
1120
- subtype: Subtype;
1121
- ref: AnnotationRef | null;
1122
- }): Behavior | null;
1123
- /**
1124
- * The clickable link areas of a page (content space): standalone link
1125
- * annotations plus every ATTACHED-link segment (folded children — their
1126
- * rects derived by the SAME rule the reconciler writes, so the clickable
1127
- * area and the written `/Rect`s can't disagree). Hidden annotations are
1128
- * excluded. THE data feed for the navigation plane (plugin-link);
1129
- * memoized by model identity for selector use.
1130
- */
1131
- linkItemsOn(pon: PageObjectNumber): LinkNavItem[];
1132
- /** Drop selected annotations whose Behavior is currently ENGAGED — inert
1133
- * things cannot stay selected. Call after anything that flips engagement
1134
- * (the plugin wires it to tool changes). */
1135
- pruneEngagedSelection(): void;
1136
- editPointer(phase: 'down' | 'move' | 'up', pon: PageObjectNumber, point: Vec, shift: boolean, scale?: number, rotation?: PageRotation, zoom?: number,
1137
- /** Finger-sized grab zones for the gesture's own hit resolution — pass the
1138
- * sample's `pointerType === 'touch'` so the gesture grabs exactly what
1139
- * {@link claimsTouchAt} claimed. */
1140
- touch?: boolean): void;
1141
- marqueePointer(phase: 'down' | 'move' | 'up', pon: PageObjectNumber, point: Vec, shift: boolean, scale?: number, rotation?: PageRotation, zoom?: number): void;
1142
- /** Run the draw gesture for an authoring TOOL (by id). The plugin resolves it
1143
- * to a routing subtype + defaults preset; a bare subtype also works (headless).
1144
- * `displayRotation` (the DOWN sample's page rotation: /Rotate + view rotation)
1145
- * feeds the tool's `upright` policy — omit it and upright is a no-op. */
1146
- createPointer(tool: string, phase: 'down' | 'move' | 'up', pon: PageObjectNumber, point: Vec, finish?: boolean, displayRotation?: PageRotation): void;
1147
- /** Commit the strokes currently buffered by an ink tool's grouping window. */
1148
- finishInkDraft(): void;
1149
- /** Create one text-markup annotation on a page from the selected text's
1150
- * per-line oriented quads (content space) — the `text-selection` create
1151
- * gesture. Axis-aligned callers build quads with `textQuadFromRect`. */
1152
- createMarkup(subtype: Subtype, pon: PageObjectNumber, quads: TextQuad[], preset?: string): void;
1153
- /** Create a caret annotation at a text-selection end anchor. */
1154
- createCaret(pon: PageObjectNumber, anchor: TextEndAnchor): void;
1155
- /** Create one Adobe-compatible Caret + StrikeOut replace-text group. */
1156
- createReplaceText(pon: PageObjectNumber, quads: TextQuad[], anchor: TextEndAnchor, preset?: string): void;
1157
- /** Set the live markup preview from the selection's per-page quads (renders a
1158
- * ghost that looks like the markup it will become). */
1159
- previewMarkup(subtype: Subtype, quadsByPage: Record<number, TextQuad[]>, preset?: string): void;
1160
- clearMarkupPreview(): void;
1161
- /** Place the armed stamp centred on a content point. Returns false (no
1162
- * capture) when nothing is armed. `displayRotation` (the click sample's page
1163
- * rotation) feeds the active tool's `upright` policy. */
1164
- placeArmedStamp(pon: PageObjectNumber, point: Vec, displayRotation?: PageRotation): boolean;
1165
- /**
1166
- * The one click-to-place entry for every payload-carrying tool (stamp /
1167
- * note / file attachment) — the place handler forwards each down here.
1168
- * Armed payload first, then the active tool's kind routes; returns whether
1169
- * the click was consumed.
1170
- */
1171
- placeAt(pon: PageObjectNumber, point: Vec, displayRotation?: PageRotation): boolean;
1172
- /** Whether a stamp payload is armed — the hover handler's cheap pre-check. */
1173
- hasArmedStamp(): boolean;
1174
- /**
1175
- * Update the armed tool's FOOTPRINT ghost to a content point — the stamp's
1176
- * fitted image box (same fit + clamp as placement), or a click-create tool's
1177
- * default geometry (same anchor + clamp as the click commit). The ghost IS
1178
- * the placement, never an approximation. Tools without a determinable
1179
- * footprint (or with a `false` ghost policy) clear instead.
1180
- */
1181
- ghostHoverAt(toolId: string, pon: PageObjectNumber, point: Vec, displayRotation?: PageRotation): void;
1182
- /** Drop the hover ghost (pointer left the pages / a gesture started). */
1183
- clearGhost(): void;
1184
- /**
1185
- * Drive the transient placement preview during an EXTERNALLY-owned creation
1186
- * gesture (the form plugin's drag-to-place): the box the commit would use,
1187
- * page-clamped, styled from the TOOL's defaults, painted through the same
1188
- * ghost pipeline as every footprint. Sibling plugins call THIS — never the
1189
- * annotation store directly.
1190
- */
1191
- setPlacementPreview(toolId: string, pon: PageObjectNumber, box: Rect): void;
1192
- /** Drop the placement preview (gesture ended — commit, cancel, or error). */
1193
- clearPlacementPreview(): void;
1194
- /** The armed tool's footprint ghost on a page (content space), or null.
1195
- * Vector ghosts also ride {@link pageItems}; only `kind: 'image'` ghosts
1196
- * need the framework's blit. */
1197
- toolGhost(pon: PageObjectNumber): ToolGhost | null;
1198
- /**
1199
- * The armed stamp's paintable preview at (roughly) `devicePixelWidth`
1200
- * pixels wide — bucketed and cached per bucket for the arm's lifetime —
1201
- * or null when there is no ghost to show. Omit the width for the
1202
- * smallest bucket.
1203
- */
1204
- armedStampPreview(devicePixelWidth?: number): Promise<ArmedStampPreview | null>;
1205
- /** Bumps on arm/disarm — keys the render layer's preview object-URL lifetime. */
1206
- stampArmEpoch(): number;
1207
- /**
1208
- * Resolve the ACTIVE tool's {@link StampSourceSpec} for a click and place a
1209
- * stamp centred on `point`: fixed `bytes` land immediately; a `'prompt'` source
1210
- * asks the installed {@link FilePickerProvider} (placement is dropped if it
1211
- * cancels, or if the tool/document changed while it was open). Returns false
1212
- * (no capture) when the active tool has no source. The click-then-pick
1213
- * counterpart of {@link placeArmedStamp}. `displayRotation` as on
1214
- * {@link placeArmedStamp}.
1215
- */
1216
- requestStampAt(pon: PageObjectNumber, point: Vec, displayRotation?: PageRotation): boolean;
1217
- /** Every resolved tool (built-ins + config `tools`), for the registration loop. */
1218
- tools(): ResolvedTool[];
1219
- /** One resolved tool by id, or null when it is not registered. */
1220
- tool(id: string): ResolvedTool | null;
1221
- /** A tool's routing subtype (arrow → `line`) — how the draw handler picks the
1222
- * gesture and the created annotation's PDF kind. Falls back to the id. */
1223
- toolSubtype(id: string): Subtype;
1224
- registerBehavior(b: Behavior): () => void;
1225
- /**
1226
- * The actions plane's session-visibility write (Hide actions, script
1227
- * `annot.hidden`): resolve annotation OBJECT NUMBERS to loaded model ids
1228
- * (the `obj:` refKey seam) and merge session-hidden overrides. Returns how
1229
- * many resolved — unresolved numbers (unloaded pages, nm/index refs) are
1230
- * the caller's diagnostics. Session state only; never an engine write.
1231
- */
1232
- /** The script/Hide DOCUMENT-commit door (full ISO): engine updates +
1233
- * model reconciliation, per entry, stop-on-failure. Never enqueues,
1234
- * never touches the script host — the D2 sink contract. */
1235
- commitScriptEffects(entries: AnnotCommitEntry[]): Promise<AnnotCommitResult>;
1236
- }
1237
- /**
1238
- * The annotation capability token. Typed to the full {@link AnnotationHostCapability}
1239
- * here (the package internals, `/contract/host`, and `/internal` use this view).
1240
- * The package root re-exports the SAME token narrowed to
1241
- * {@link AnnotationCapability}.
1242
- */
1243
- declare const AnnotationToken: import("@embedpdf/core").CapabilityToken<AnnotationHostCapability>;
1244
- //#endregion
1245
- export { DEFAULT_TOOLS as A, Face as B, TextItem as C, ARMED_STAMP_TOOL_ID as D, previewBucket as E, SelectionAuthoring as F, TextSelection as H, StampSourceSpec as I, ToolAuthoringKind as L, InkAuthoringOptions as M, PromptSourceSpec as N, AnnotationToolDef as O, ResolvedTool as P, ToolDefaultsFor as R, StampToolInput as S, ToolGhost as T, TextFormat as V, RecalibrationReport as _, AnnotationHydration as a, StampPlacement as b, Behavior as c, ChromeSettingsPatch as d, CommentPermissions as f, LinkNavItem as g, FilePromptRequest as h, AnnotationHostCapability as i, GhostPolicy as j, AnnotationToolInput as k, CapturedAnnotationDraft as l, FilePickerProvider as m, AnnotationCapability as n, AnnotationState as o, CommentsApi as p, AnnotationConfig as r, AnnotationToken as s, AnnotationAction as t, ChromeSettings as u, SelectionFlags as v, ThreadDeleteResult as w, StampPreviewProvider as x, SelectionProps as y, buildToolRegistry as z };
1246
- //# sourceMappingURL=types-ChRnKozg.d.cts.map