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

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