@embedpdf/plugin-annotation 3.0.0-next.10 → 3.0.0-next.12

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 (43) hide show
  1. package/dist/authoring-BhtyfsMx.d.cts +37 -0
  2. package/dist/authoring-R4ezdnXd.d.ts +37 -0
  3. package/dist/authoring.cjs +16 -0
  4. package/dist/authoring.d.cts +2 -0
  5. package/dist/authoring.d.ts +2 -0
  6. package/dist/authoring.js +3 -0
  7. package/dist/{capability-BRf0HrOi.js → capability-Bh_1PL5v.js} +266 -280
  8. package/dist/capability-Bh_1PL5v.js.map +1 -0
  9. package/dist/{capability-Cl9x8oLS.cjs → capability-R7dgogJs.cjs} +332 -364
  10. package/dist/capability-R7dgogJs.cjs.map +1 -0
  11. package/dist/contract/host.cjs +3 -0
  12. package/dist/contract/host.d.cts +2 -0
  13. package/dist/contract/host.d.ts +2 -0
  14. package/dist/contract/host.js +2 -0
  15. package/dist/contract.cjs +10 -0
  16. package/dist/contract.cjs.map +1 -0
  17. package/dist/contract.d.cts +10 -0
  18. package/dist/contract.d.ts +10 -0
  19. package/dist/contract.js +8 -0
  20. package/dist/contract.js.map +1 -0
  21. package/dist/index.cjs +25 -24
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.cts +5 -47
  24. package/dist/index.d.ts +5 -47
  25. package/dist/index.js +15 -15
  26. package/dist/index.js.map +1 -1
  27. package/dist/internal.cjs +4 -2
  28. package/dist/internal.d.cts +2 -1
  29. package/dist/internal.d.ts +2 -1
  30. package/dist/internal.js +3 -1
  31. package/dist/seam-6pJeKOOJ.cjs +296 -0
  32. package/dist/seam-6pJeKOOJ.cjs.map +1 -0
  33. package/dist/seam-C4L_7rDt.js +225 -0
  34. package/dist/seam-C4L_7rDt.js.map +1 -0
  35. package/dist/{types-Due7UgzE.d.cts → types-BAPdbfPT.d.cts} +89 -18
  36. package/dist/{types-DITX_3CG.d.ts → types-BgetZ4BK.d.ts} +88 -17
  37. package/dist/types-BvB1vUKb.js +20 -0
  38. package/dist/types-BvB1vUKb.js.map +1 -0
  39. package/dist/types-Dlhdw2k_.cjs +31 -0
  40. package/dist/types-Dlhdw2k_.cjs.map +1 -0
  41. package/package.json +21 -7
  42. package/dist/capability-BRf0HrOi.js.map +0 -1
  43. package/dist/capability-Cl9x8oLS.cjs.map +0 -1
@@ -1,238 +1,13 @@
1
+ const require_seam = require("./seam-6pJeKOOJ.cjs");
2
+ const require_types = require("./types-Dlhdw2k_.cjs");
1
3
  let _embedpdf_core = require("@embedpdf/core");
2
- let _embedpdf_plugin_interaction = require("@embedpdf/plugin-interaction");
4
+ let _embedpdf_plugin_actions_contract = require("@embedpdf/plugin-actions/contract");
5
+ let _embedpdf_plugin_interaction_contract = require("@embedpdf/plugin-interaction/contract");
3
6
  let _embedpdf_engine_core_runtime = require("@embedpdf/engine-core/runtime");
4
- let _embedpdf_plugin_selection = require("@embedpdf/plugin-selection");
7
+ let _embedpdf_core_acrojs = require("@embedpdf/core-acrojs");
8
+ let _embedpdf_plugin_selection_contract = require("@embedpdf/plugin-selection/contract");
5
9
  let _embedpdf_core_annotation = require("@embedpdf/core-annotation");
6
10
  let _embedpdf_core_geometry = require("@embedpdf/core-geometry");
7
- //#region src/types.ts
8
- /**
9
- * The annotation capability token. Typed to the full {@link AnnotationHostCapability}
10
- * here (the package internals + the `/internal` entry use this view). The package
11
- * root re-exports the SAME token narrowed to {@link AnnotationCapability}.
12
- */
13
- const AnnotationToken = (0, _embedpdf_core.createCapabilityToken)("annotation", { hint: `add annotationPlugin() from '@embedpdf/plugin-annotation' to your plugins list` });
14
- //#endregion
15
- //#region src/repository/seam.ts
16
- function refKey(ref) {
17
- return ref.kind === "objectNumber" ? `obj:${ref.annotObjectNumber}` : ref.kind === "nm" ? `nm:${ref.nm}` : `idx:${ref.pageObjectNumber}:${ref.index}`;
18
- }
19
- const h2 = (n) => Math.max(0, Math.min(255, Math.round(n))).toString(16).padStart(2, "0");
20
- const colorToCss = (c) => `#${h2(c.r)}${h2(c.g)}${h2(c.b)}`;
21
- function cssToColor(css) {
22
- const s = css.trim();
23
- const m6 = /^#?([0-9a-f]{6})$/i.exec(s);
24
- if (m6) {
25
- const n = parseInt(m6[1], 16);
26
- return {
27
- r: n >> 16 & 255,
28
- g: n >> 8 & 255,
29
- b: n & 255
30
- };
31
- }
32
- const m3 = /^#?([0-9a-f]{3})$/i.exec(s);
33
- if (m3) {
34
- const [a, b, c] = m3[1];
35
- return {
36
- r: parseInt(a + a, 16),
37
- g: parseInt(b + b, 16),
38
- b: parseInt(c + c, 16)
39
- };
40
- }
41
- return {
42
- r: 0,
43
- g: 0,
44
- b: 0
45
- };
46
- }
47
- const toPdfRotation = (rotCW) => (0, _embedpdf_core_annotation.normalizeDeg)(-rotCW);
48
- const fromPdfRotation = (rotPdf) => (0, _embedpdf_core_annotation.normalizeDeg)(-rotPdf);
49
- /** Advisory `rot` for a vertex geom, from a DTO's (PDF-convention) `rotation`.
50
- * Absent → no `rot` key (kept off the geom so unrotated shapes stay clean). */
51
- const rotFromDTO = (rotation) => rotation ? { rot: fromPdfRotation(rotation) } : {};
52
- /**
53
- * Geometry/rotation fields for a BOX kind (square/circle/plain free-text). The
54
- * model's `rect` is the UNROTATED logical box and `rot` the applied tilt, so:
55
- * - rot == 0 → `/Rect` IS the box; the transform pair is stated as `null`
56
- * (TOTAL projection — the engine's tri-state writes preserve omissions, so
57
- * an omitted field would keep a stale rotation instead of flattening it).
58
- * - rot != 0 → `/Rect` is the rotated visual AABB (PDFium clips the baked /AP
59
- * to it), `unrotatedRect` the logical box, `rotation` the PDF angle.
60
- */
61
- function boxGeomFields(rect, rot, crop) {
62
- if (!rot) return {
63
- rect: (0, _embedpdf_core_annotation.contentToPdfRect)(rect, crop),
64
- rotation: null,
65
- unrotatedRect: null
66
- };
67
- return {
68
- rect: (0, _embedpdf_core_annotation.contentToPdfRect)((0, _embedpdf_core_annotation.rotatedAabb)(rect, rot), crop),
69
- unrotatedRect: (0, _embedpdf_core_annotation.contentToPdfRect)(rect, crop),
70
- rotation: toPdfRotation(rot)
71
- };
72
- }
73
- /** A box geom (square/circle/stamp) from its DTO: when rotated, the LOCAL box
74
- * is the stored `unrotatedRect` (the AABB `/Rect` is the rendered envelope)
75
- * and `rot` the converted tilt; unrotated, `/Rect` IS the box. */
76
- function boxGeomFromDTO(dto, rotation, unrotatedRect, crop, ellipse) {
77
- const rot = rotation ? fromPdfRotation(rotation) : 0;
78
- return {
79
- t: "rect",
80
- rect: (0, _embedpdf_core_annotation.pdfToContentRect)(rot && unrotatedRect ? unrotatedRect : dto.rect, crop),
81
- ellipse,
82
- ...rot ? { rot } : {}
83
- };
84
- }
85
- /** Inset a PdfRect by a `/RD` (PDF user space, y-up: all four are non-negative
86
- * insets from the matching `/Rect` edge). Used to recover the callout text box. */
87
- const insetPdfRectByRD = (r, rd) => rd ? {
88
- left: r.left + rd.left,
89
- bottom: r.bottom + rd.bottom,
90
- right: r.right - rd.right,
91
- top: r.top - rd.top
92
- } : r;
93
- /** Engine border fields (`/BS /S`, `/BS /D`, `/BE /I`) → the `Border` union. A
94
- * cloudy effect wins over the underlying border style (which stays solid). */
95
- function borderFromDTO(d) {
96
- if ((d.cloudyIntensity ?? 0) > 0) return {
97
- kind: "cloudy",
98
- intensity: d.cloudyIntensity
99
- };
100
- if (d.borderStyle === "dashed") return {
101
- kind: "dashed",
102
- dash: d.dashArray?.length ? d.dashArray : [3, 3]
103
- };
104
- return { kind: "solid" };
105
- }
106
- /** The writable projection of a `link` value: `goto`/`uri` pass through,
107
- * read-only arms (`javascript`, `named`, `goto-remote`, `launch`,
108
- * `unsupported`) yield `null` — they can be carried, never (re)written. */
109
- function writableTarget(t) {
110
- return t && (t.kind === "goto" || t.kind === "uri") ? t : null;
111
- }
112
- const TEXT_MARKUP = /* @__PURE__ */ new Set([
113
- "highlight",
114
- "underline",
115
- "squiggly",
116
- "strikeout"
117
- ]);
118
- const STROKE_KINDS = /* @__PURE__ */ new Set([
119
- "square",
120
- "circle",
121
- "line",
122
- "polygon",
123
- "polyline",
124
- "ink"
125
- ]);
126
- /** Engine DTO → content-space `Style` (CSS colours, `Border` union). Exported so
127
- * selection-aware UIs can read display values straight off a {@link AnnotationDTO}
128
- * without re-deriving the colour/border mapping. */
129
- function styleFromDTO(dto) {
130
- if (dto.subtype === "widget") return {
131
- color: dto.color ? colorToCss(dto.color) : "#1a1a1a",
132
- interiorColor: dto.interiorColor ? colorToCss(dto.interiorColor) : null,
133
- strokeWidth: dto.strokeWidth,
134
- opacity: 1,
135
- blendMode: dto.blendMode,
136
- border: dto.borderStyle === "dashed" ? {
137
- kind: "dashed",
138
- dash: [3, 3]
139
- } : { kind: "solid" }
140
- };
141
- if (STROKE_KINDS.has(dto.subtype)) {
142
- const d = dto;
143
- return {
144
- color: colorToCss(d.color),
145
- interiorColor: d.interiorColor ? colorToCss(d.interiorColor) : null,
146
- strokeWidth: d.strokeWidth,
147
- opacity: d.opacity,
148
- blendMode: dto.blendMode,
149
- border: borderFromDTO(d)
150
- };
151
- }
152
- if (TEXT_MARKUP.has(dto.subtype)) {
153
- const d = dto;
154
- return {
155
- color: colorToCss(d.color),
156
- interiorColor: null,
157
- strokeWidth: 0,
158
- opacity: d.opacity,
159
- blendMode: dto.blendMode,
160
- border: { kind: "solid" }
161
- };
162
- }
163
- if (dto.subtype === "caret") {
164
- const d = dto;
165
- return {
166
- color: colorToCss(d.color),
167
- interiorColor: null,
168
- strokeWidth: 1,
169
- opacity: d.opacity,
170
- blendMode: dto.blendMode,
171
- border: { kind: "solid" }
172
- };
173
- }
174
- if (dto.subtype === "free-text") {
175
- const d = dto;
176
- return {
177
- color: colorToCss(d.color),
178
- interiorColor: d.interiorColor ? colorToCss(d.interiorColor) : null,
179
- strokeWidth: d.strokeWidth,
180
- opacity: d.opacity,
181
- blendMode: dto.blendMode,
182
- border: borderFromDTO(d)
183
- };
184
- }
185
- if (dto.subtype === "redact") {
186
- const d = dto;
187
- return {
188
- color: colorToCss(d.color),
189
- interiorColor: d.interiorColor ? colorToCss(d.interiorColor) : null,
190
- strokeWidth: 1.5,
191
- opacity: d.opacity,
192
- blendMode: dto.blendMode,
193
- border: { kind: "solid" }
194
- };
195
- }
196
- if (dto.subtype === "text" || dto.subtype === "file-attachment") {
197
- const d = dto;
198
- return {
199
- color: colorToCss(d.color),
200
- interiorColor: null,
201
- strokeWidth: 1,
202
- opacity: d.opacity,
203
- blendMode: dto.blendMode,
204
- border: { kind: "solid" }
205
- };
206
- }
207
- return {
208
- color: "#444444",
209
- interiorColor: null,
210
- strokeWidth: 1,
211
- opacity: 1,
212
- blendMode: dto.blendMode,
213
- border: { kind: "solid" }
214
- };
215
- }
216
- /**
217
- * The flat props vocabulary (CSS colours, house keys) → the engine's widget
218
- * appearance for `doc.forms` authoring — the SAME mapping the widget patch
219
- * lowering uses, exported as a boundary utility so the form plugin can style
220
- * `placeField` from a tool's `currentDefaults` without growing a second CSS
221
- * parser. Absent keys stay absent (the engine writes nothing for them).
222
- */
223
- function widgetAppearanceFromProps(props) {
224
- return {
225
- ...props.color !== void 0 ? { color: cssToColor(props.color) } : {},
226
- ...props.interiorColor !== void 0 ? { interiorColor: props.interiorColor ? cssToColor(props.interiorColor) : null } : {},
227
- ...props.strokeWidth !== void 0 ? { strokeWidth: props.strokeWidth } : {},
228
- ...props.border !== void 0 ? { borderStyle: props.border.kind === "dashed" ? "dashed" : "solid" } : {},
229
- ...props.fontFamily !== void 0 ? { fontFamily: props.fontFamily } : {},
230
- ...props.fontSize !== void 0 ? { fontSize: props.fontSize } : {},
231
- ...props.fontColor !== void 0 ? { fontColor: cssToColor(props.fontColor) } : {},
232
- ...props.textAlign !== void 0 ? { textAlign: props.textAlign } : {}
233
- };
234
- }
235
- //#endregion
236
11
  //#region src/repository/props.ts
237
12
  /**
238
13
  * GENERIC per-key prop lowerings — the 1:1 mappings from the flat props
@@ -251,15 +26,15 @@ const borderSlice = (style) => ({
251
26
  * the old projections did (a fresh draft may not carry `text` yet). */
252
27
  const textOf = (a) => a.text ?? _embedpdf_core_annotation.initialTextStyle;
253
28
  const GENERIC_PROPS = {
254
- color: (a) => ({ color: cssToColor(a.style.color) }),
29
+ color: (a) => ({ color: require_seam.cssToColor(a.style.color) }),
255
30
  opacity: (a) => ({ opacity: a.style.opacity }),
256
31
  blendMode: (a) => ({ blendMode: a.style.blendMode }),
257
- interiorColor: (a) => ({ interiorColor: a.style.interiorColor ? cssToColor(a.style.interiorColor) : null }),
32
+ interiorColor: (a) => ({ interiorColor: a.style.interiorColor ? require_seam.cssToColor(a.style.interiorColor) : null }),
258
33
  strokeWidth: (a) => ({ strokeWidth: a.style.strokeWidth }),
259
34
  border: (a) => borderSlice(a.style),
260
35
  fontFamily: (a) => ({ fontFamily: textOf(a).fontFamily }),
261
36
  fontSize: (a) => ({ fontSize: textOf(a).fontSize }),
262
- fontColor: (a) => ({ fontColor: cssToColor(textOf(a).fontColor) }),
37
+ fontColor: (a) => ({ fontColor: require_seam.cssToColor(textOf(a).fontColor) }),
263
38
  textAlign: (a) => ({ textAlign: textOf(a).textAlign }),
264
39
  icon: (a) => a.icon !== void 0 ? { icon: a.icon } : {},
265
40
  link: () => ({})
@@ -270,7 +45,7 @@ const GENERIC_PROPS = {
270
45
  * `/Rect`(+`unrotatedRect`+`rotation`), total (nulls state the clears). */
271
46
  const boxEmit = (a, crop) => {
272
47
  const g = a.geom;
273
- return boxGeomFields(g.rect, (0, _embedpdf_core_annotation.geomRotation)(a.geom), crop);
48
+ return require_seam.boxGeomFields(g.rect, (0, _embedpdf_core_annotation.geomRotation)(a.geom), crop);
274
49
  };
275
50
  //#endregion
276
51
  //#region src/repository/kinds/shape.ts
@@ -298,7 +73,7 @@ function cloudyExtras(a) {
298
73
  }
299
74
  const ingest = (dto, crop, ellipse) => {
300
75
  const d = dto;
301
- return { geom: boxGeomFromDTO(d, d.rotation, d.unrotatedRect, crop, ellipse) };
76
+ return { geom: require_seam.boxGeomFromDTO(d, d.rotation, d.unrotatedRect, crop, ellipse) };
302
77
  };
303
78
  const projection = (ellipse) => ({
304
79
  ingest: (dto, crop) => ingest(dto, crop, ellipse),
@@ -322,7 +97,7 @@ const circle = projection(true);
322
97
  * omission would preserve a stale advisory angle. */
323
98
  const advisoryRotation = (g) => {
324
99
  const rot = (0, _embedpdf_core_annotation.geomRotation)(g);
325
- return { rotation: rot ? toPdfRotation(rot) : null };
100
+ return { rotation: rot ? require_seam.toPdfRotation(rot) : null };
326
101
  };
327
102
  /** `/BE` intensity for a closed poly (polygon): the curls are generated from
328
103
  * /Vertices + /BE alone — per ISO 32000 no /RD applies — and the /Rect
@@ -367,7 +142,7 @@ const line = {
367
142
  a: (0, _embedpdf_core_annotation.pdfToContentPoint)(d.linePoints.start, crop),
368
143
  b: (0, _embedpdf_core_annotation.pdfToContentPoint)(d.linePoints.end, crop),
369
144
  ends: d.lineEndings,
370
- ...rotFromDTO(d.rotation)
145
+ ...require_seam.rotFromDTO(d.rotation)
371
146
  } };
372
147
  },
373
148
  geometry: (a, crop) => {
@@ -392,7 +167,7 @@ const polyProjection = (closed) => ({
392
167
  points: d.vertices.map((p) => (0, _embedpdf_core_annotation.pdfToContentPoint)(p, crop)),
393
168
  closed,
394
169
  ..."lineEndings" in d ? { ends: d.lineEndings } : {},
395
- ...rotFromDTO(d.rotation)
170
+ ...require_seam.rotFromDTO(d.rotation)
396
171
  } };
397
172
  },
398
173
  geometry: (a, crop) => {
@@ -415,7 +190,7 @@ const ink = {
415
190
  geom: {
416
191
  t: "ink",
417
192
  strokes: d.inkList.map((stroke) => stroke.map((p) => (0, _embedpdf_core_annotation.pdfToContentPoint)(p, crop))),
418
- ...rotFromDTO(d.rotation)
193
+ ...require_seam.rotFromDTO(d.rotation)
419
194
  },
420
195
  ...d.intent ? { intent: d.intent } : {}
421
196
  };
@@ -440,7 +215,7 @@ function textFromDTO(dto) {
440
215
  return {
441
216
  fontFamily: dto.fontFamily,
442
217
  fontSize: dto.fontSize,
443
- fontColor: colorToCss(dto.fontColor ?? dto.color),
218
+ fontColor: require_seam.colorToCss(dto.fontColor ?? dto.color),
444
219
  textAlign: dto.textAlign
445
220
  };
446
221
  }
@@ -484,7 +259,7 @@ function calloutFields(a, crop) {
484
259
  calloutLine,
485
260
  lineEnding: g.callout.ending,
486
261
  ...rot ? {
487
- rotation: toPdfRotation(rot),
262
+ rotation: require_seam.toPdfRotation(rot),
488
263
  unrotatedRect: (0, _embedpdf_core_annotation.contentToPdfRect)(g.rect, crop)
489
264
  } : {
490
265
  rotation: null,
@@ -497,11 +272,11 @@ const freeText = {
497
272
  const d = dto;
498
273
  if (d.intent === "free-text-callout" && d.calloutLine && d.calloutLine.length >= 2) {
499
274
  const cl = d.calloutLine;
500
- const rot = d.rotation ? fromPdfRotation(d.rotation) : 0;
275
+ const rot = d.rotation ? require_seam.fromPdfRotation(d.rotation) : 0;
501
276
  return {
502
277
  geom: {
503
278
  t: "text",
504
- rect: rot && d.unrotatedRect ? (0, _embedpdf_core_annotation.pdfToContentRect)(d.unrotatedRect, crop) : (0, _embedpdf_core_annotation.pdfToContentRect)(insetPdfRectByRD(d.rect, d.rectDifferences), crop),
279
+ rect: rot && d.unrotatedRect ? (0, _embedpdf_core_annotation.pdfToContentRect)(d.unrotatedRect, crop) : (0, _embedpdf_core_annotation.pdfToContentRect)(require_seam.insetPdfRectByRD(d.rect, d.rectDifferences), crop),
505
280
  callout: {
506
281
  tip: (0, _embedpdf_core_annotation.pdfToContentPoint)(cl[0], crop),
507
282
  knee: cl.length === 3 ? (0, _embedpdf_core_annotation.pdfToContentPoint)(cl[1], crop) : void 0,
@@ -512,7 +287,7 @@ const freeText = {
512
287
  text: textFromDTO(d)
513
288
  };
514
289
  }
515
- const rot = d.rotation ? fromPdfRotation(d.rotation) : 0;
290
+ const rot = d.rotation ? require_seam.fromPdfRotation(d.rotation) : 0;
516
291
  return {
517
292
  geom: {
518
293
  t: "text",
@@ -615,7 +390,7 @@ const strikeout = markupProjection("strikeout");
615
390
  const caret = {
616
391
  ingest: (dto, crop) => {
617
392
  const d = dto;
618
- const rot = d.rotation ? fromPdfRotation(d.rotation) : 0;
393
+ const rot = d.rotation ? require_seam.fromPdfRotation(d.rotation) : 0;
619
394
  return {
620
395
  geom: {
621
396
  t: "caret",
@@ -625,7 +400,7 @@ const caret = {
625
400
  ...d.intent ? { intent: d.intent } : {}
626
401
  };
627
402
  },
628
- geometry: (a, crop) => a.geom.t === "caret" ? boxGeomFields(a.geom.rect, a.geom.rot ?? 0, crop) : null,
403
+ geometry: (a, crop) => a.geom.t === "caret" ? require_seam.boxGeomFields(a.geom.rect, a.geom.rot ?? 0, crop) : null,
629
404
  draftExtras: (a) => ({
630
405
  rectDifferences: {
631
406
  left: .5,
@@ -652,7 +427,7 @@ const redact = {
652
427
  text: {
653
428
  fontFamily: d.fontFamily,
654
429
  fontSize: d.fontSize,
655
- fontColor: colorToCss(d.fontColor),
430
+ fontColor: require_seam.colorToCss(d.fontColor),
656
431
  textAlign: d.textAlign
657
432
  },
658
433
  ...d.overlayText ? { label: {
@@ -693,7 +468,7 @@ const fileAttachment = iconProjection("file-attachment");
693
468
  const stamp = {
694
469
  ingest: (dto, crop) => {
695
470
  const d = dto;
696
- return { geom: boxGeomFromDTO(d, d.rotation, d.unrotatedRect, crop, false) };
471
+ return { geom: require_seam.boxGeomFromDTO(d, d.rotation, d.unrotatedRect, crop, false) };
697
472
  },
698
473
  geometry: (a, crop) => boxEmit(a, crop),
699
474
  createable: false
@@ -712,10 +487,10 @@ const link = {
712
487
  },
713
488
  geometry: rectGeometry,
714
489
  prop: { link: (a) => {
715
- const target = writableTarget(a.link);
490
+ const target = require_seam.writableTarget(a.link);
716
491
  return target ? { target } : a.link == null ? { target: null } : {};
717
492
  } },
718
- draftExtras: (a) => ({ target: writableTarget(a.link) })
493
+ draftExtras: (a) => ({ target: require_seam.writableTarget(a.link) })
719
494
  };
720
495
  /** One PDF `widget` subtype → per-family CLIENT kinds (radios have no font). */
721
496
  const WIDGET_KIND_BY_FAMILY = {
@@ -736,7 +511,7 @@ function widgetTextFromDTO(dto) {
736
511
  return {
737
512
  fontFamily: dto.fontFamily ?? "helvetica",
738
513
  fontSize: dto.fontSize ?? 0,
739
- fontColor: dto.fontColor ? colorToCss(dto.fontColor) : "#000000",
514
+ fontColor: dto.fontColor ? require_seam.colorToCss(dto.fontColor) : "#000000",
740
515
  textAlign: dto.textAlign
741
516
  };
742
517
  }
@@ -744,7 +519,7 @@ const widget = {
744
519
  ingest: (dto, crop) => {
745
520
  const d = dto;
746
521
  return {
747
- geom: boxGeomFromDTO(d, void 0, void 0, crop, false),
522
+ geom: require_seam.boxGeomFromDTO(d, void 0, void 0, crop, false),
748
523
  ...WIDGET_TEXT_KINDS.has(widgetKindOf(d.fieldFamily)) ? { text: widgetTextFromDTO(d) } : {}
749
524
  };
750
525
  },
@@ -825,16 +600,16 @@ function fromDTO(dto, crop, source = "baked") {
825
600
  const slice = projectionOf(dto.subtype).ingest(dto, crop);
826
601
  const strippedRect = !(dto.subtype === "free-text" && dto.intent === "free-text-callout") && "unrotatedRect" in dto && "rotation" in dto && dto.rotation ? dto.unrotatedRect : void 0;
827
602
  return {
828
- id: refKey(dto.ref),
603
+ id: require_seam.refKey(dto.ref),
829
604
  ref: dto.ref,
830
605
  pon: dto.pageObjectNumber,
831
606
  subtype: dto.subtype === "widget" ? widgetKindOf(dto.fieldFamily) : dto.subtype,
832
607
  flags: dto.flags,
833
608
  source,
834
609
  data: dto,
835
- ...dto.inReplyTo ? { irt: refKey(dto.inReplyTo) } : {},
836
- ...dto.replyType === "group" && dto.inReplyTo ? { group: refKey(dto.inReplyTo) } : {},
837
- style: styleFromDTO(dto),
610
+ ...dto.inReplyTo ? { irt: require_seam.refKey(dto.inReplyTo) } : {},
611
+ ...dto.replyType === "group" && dto.inReplyTo ? { group: require_seam.refKey(dto.inReplyTo) } : {},
612
+ style: require_seam.styleFromDTO(dto),
838
613
  ...slice,
839
614
  apBox: (0, _embedpdf_core_annotation.pdfToContentRect)(strippedRect ?? dto.rect, crop),
840
615
  ...strippedRect ? { apRot: (0, _embedpdf_core_annotation.geomRotation)(slice.geom) } : {}
@@ -909,6 +684,46 @@ function toCreateDraft(a, crop) {
909
684
  };
910
685
  }
911
686
  //#endregion
687
+ //#region src/hover-feed.ts
688
+ /**
689
+ * The annotation plane's E/X trigger feed — a thin adapter from the ONE
690
+ * pointer-driven hover seam (the `hoverAt` diff) onto the shared hover pump.
691
+ *
692
+ * Anti-cascade is structural and lives in the CALLER's seam placement:
693
+ * reducer-side hover clears (session-hide, remove, reload) never pass
694
+ * through `hoverAt`, so an effect-induced hover loss can never masquerade as
695
+ * a cursor exit. This module only decides WHO is hoverable here:
696
+ *
697
+ * - Drafts (no engine ref) can't carry /AA — skipped.
698
+ * - Widgets and links belong to their own event planes (fill controls /
699
+ * LinkLayer anchors own their pixels). When they ARE hit-testable on this
700
+ * plane, an authoring tool owns them — firing hover actions while editing
701
+ * would be wrong — so they are skipped unconditionally.
702
+ * - Tree-less annotations are FREE: no target, no dispatch, and per-event
703
+ * flags keep a lone /E or /X from dispatching its inert twin.
704
+ */
705
+ function createAnnotationHoverFeed(actions, annotOf) {
706
+ const pump = (0, _embedpdf_plugin_actions_contract.createHoverPump)(actions.dispatch);
707
+ const targetOf = (id) => {
708
+ if (!id) return null;
709
+ const annot = annotOf(id);
710
+ if (!annot?.ref) return null;
711
+ if (annot.subtype.startsWith("widget") || annot.subtype === "link") return null;
712
+ const enter = Boolean(annot.data?.actions?.cursorEnter?.root);
713
+ const exit = Boolean(annot.data?.actions?.cursorExit?.root);
714
+ if (!enter && !exit) return null;
715
+ return {
716
+ ref: annot.ref,
717
+ pon: annot.pon,
718
+ events: {
719
+ enter,
720
+ exit
721
+ }
722
+ };
723
+ };
724
+ return { hover: (id) => pump.hover(targetOf(id)) };
725
+ }
726
+ //#endregion
912
727
  //#region src/text-item.ts
913
728
  /**
914
729
  * The free-text presentation projection: the core's geometry-only `textBoxes`
@@ -960,6 +775,15 @@ function buildTextItems(m, pon, view) {
960
775
  //#endregion
961
776
  //#region src/tools.ts
962
777
  /**
778
+ * The tool `armStamp` activates — and the only tool an armed payload survives
779
+ * onto (plus the legacy `annotation-stamp` tag, honoured for embedder configs
780
+ * written before the tags were unified). Placement consults the armed payload
781
+ * BEFORE the active tool's own `source` spec, so a payload must not outlive
782
+ * its tool onto a sibling stamp preset: an `image` preset's click has to open
783
+ * its own picker, not silently place whatever the stamp panel last armed.
784
+ */
785
+ const ARMED_STAMP_TOOL_ID = "stamp";
786
+ /**
963
787
  * The creation properties each known authoring kind actually consumes. This is
964
788
  * deliberately separate from the flat internal patch vocabulary: it gives tool
965
789
  * configuration precise compile-time and runtime validation while mixed-selection
@@ -1293,7 +1117,7 @@ const DEFAULT_TOOLS = [
1293
1117
  }
1294
1118
  },
1295
1119
  {
1296
- id: "stamp",
1120
+ id: ARMED_STAMP_TOOL_ID,
1297
1121
  subtype: "stamp",
1298
1122
  cursor: "copy",
1299
1123
  enables: ["annotation-place", "annotation-edit"],
@@ -1475,7 +1299,7 @@ const isIconPlaceKind = (subtype) => subtype === "text" || subtype === "file-att
1475
1299
  function iconPlacementDraft(subtype, geom, defaults, flags, file) {
1476
1300
  const shared = {
1477
1301
  ...geom,
1478
- color: cssToColor(defaults.color),
1302
+ color: require_seam.cssToColor(defaults.color),
1479
1303
  opacity: defaults.opacity,
1480
1304
  flags: {
1481
1305
  print: true,
@@ -1522,6 +1346,8 @@ function createAnnotationCapability(ctx, config = {}) {
1522
1346
  * slot. See {@link FilePickerProvider}. */
1523
1347
  let filePickerProvider = null;
1524
1348
  const model = () => ctx.getState().model;
1349
+ const actionsForHover = ctx.tryGet(_embedpdf_plugin_actions_contract.ActionsToken);
1350
+ const hoverFeed = actionsForHover ? createAnnotationHoverFeed(actionsForHover, (id) => model().byId[id] ?? null) : null;
1525
1351
  const chromeSettings = () => ctx.getState().chrome;
1526
1352
  /**
1527
1353
  * Ids on a page whose Behavior is currently ENGAGED (form widgets under a
@@ -1674,11 +1500,21 @@ function createAnnotationCapability(ctx, config = {}) {
1674
1500
  if (!(0, _embedpdf_core_annotation.viewable)(a.flags, false)) continue;
1675
1501
  const target = a.link ?? (a.data?.subtype === "link" ? a.data.target ?? null : null);
1676
1502
  if (target == null || a.geom.t !== "rect") continue;
1503
+ const activate = a.data?.actions?.activate;
1504
+ const ref = a.ref ?? a.data?.ref ?? void 0;
1505
+ const hoverEnter = Boolean(a.data?.actions?.cursorEnter?.root);
1506
+ const hoverExit = Boolean(a.data?.actions?.cursorExit?.root);
1677
1507
  v.push({
1678
1508
  id,
1679
1509
  rect: a.geom.rect,
1680
1510
  target,
1681
- attached: a.group !== void 0
1511
+ attached: a.group !== void 0,
1512
+ ...activate ? { activate } : {},
1513
+ ...ref ? { ref } : {},
1514
+ ...hoverEnter || hoverExit ? { hoverEvents: {
1515
+ enter: hoverEnter,
1516
+ exit: hoverExit
1517
+ } } : {}
1682
1518
  });
1683
1519
  }
1684
1520
  linkItemsCache.set(pon, {
@@ -1739,6 +1575,14 @@ function createAnnotationCapability(ctx, config = {}) {
1739
1575
  * rendered, never synced, and dies with the tool.
1740
1576
  */
1741
1577
  let armedStamp = null;
1578
+ /** Fixed bytes as a provider: the same image at every size. */
1579
+ const fixedPreview = (bytes, mimeType) => {
1580
+ const preview = {
1581
+ bytes,
1582
+ ...mimeType ? { mimeType } : {}
1583
+ };
1584
+ return async () => preview;
1585
+ };
1742
1586
  /**
1743
1587
  * The desired stamp size (PDF points) from sniffed bytes: the image's
1744
1588
  * INTRINSIC pixel dimensions taken 1:1 as points (the v2 rule — keep the
@@ -1767,23 +1611,21 @@ function createAnnotationCapability(ctx, config = {}) {
1767
1611
  const meta = (0, _embedpdf_engine_core_runtime.sniffBinaryMetadata)(resolved.bytes);
1768
1612
  if (!meta) throw new Error("[annotation] stamp source must be PNG, JPEG, or single-page PDF bytes");
1769
1613
  let preview = null;
1770
- if (input.preview) {
1614
+ if (typeof input.preview === "function") preview = input.preview;
1615
+ else if (input.preview) {
1771
1616
  const p = await (0, _embedpdf_engine_core_runtime.resolveBinarySource)(input.preview);
1772
- preview = {
1773
- bytes: new Uint8Array(p.bytes),
1774
- mimeType: p.mimeType
1775
- };
1776
- } else if (meta.mimeType !== "application/pdf") preview = {
1777
- bytes: new Uint8Array(resolved.bytes),
1778
- mimeType: meta.mimeType
1779
- };
1617
+ preview = fixedPreview(new Uint8Array(p.bytes), p.mimeType);
1618
+ } else if (meta.mimeType !== "application/pdf") preview = fixedPreview(new Uint8Array(resolved.bytes), meta.mimeType);
1780
1619
  armedStamp = {
1781
1620
  source: input.source,
1782
1621
  ...desiredStampSize(meta, input.targetWidth, input.intrinsicSize),
1783
- preview
1622
+ preview,
1623
+ previewCache: /* @__PURE__ */ new Map(),
1624
+ ...input.name !== void 0 ? { name: input.name } : {},
1625
+ ...input.subject !== void 0 ? { subject: input.subject } : {}
1784
1626
  };
1785
1627
  ctx.dispatch({ type: "STAMP_ARM_CHANGED" });
1786
- ctx.tryGet(_embedpdf_plugin_interaction.InteractionToken)?.activateTool("stamp");
1628
+ ctx.tryGet(_embedpdf_plugin_interaction_contract.InteractionToken)?.activateTool(ARMED_STAMP_TOOL_ID);
1787
1629
  };
1788
1630
  const disarmStamp = () => {
1789
1631
  if (!armedStamp) return;
@@ -1910,42 +1752,66 @@ function createAnnotationCapability(ctx, config = {}) {
1910
1752
  * emits the repository's box rotation fields — the engine bakes the tilted
1911
1753
  * /AP exactly as an interactively rotated stamp round-trips, and the fit uses
1912
1754
  * the ROTATED footprint. Returns false when the page/document isn't ready. */
1913
- const createStampAt = (pon, point, source, desired, rotCW = 0) => {
1755
+ const createStampAt = (pon, point, source, desired, rotCW = 0, identity = {}) => {
1914
1756
  const doc = ctx.doc;
1915
1757
  const crop = cropOf(pon);
1916
- if (!doc || !crop) return false;
1758
+ if (!doc || !crop) return null;
1917
1759
  const box = (0, _embedpdf_core_annotation.fitStampBox)(point, desired, {
1918
1760
  width: crop.right - crop.left,
1919
1761
  height: crop.top - crop.bottom
1920
1762
  }, rotCW);
1921
- doc.page(pon).annotations.create({
1763
+ return doc.page(pon).annotations.create({
1922
1764
  subtype: "stamp",
1923
- ...boxGeomFields(box, rotCW, crop),
1765
+ ...require_seam.boxGeomFields(box, rotCW, crop),
1924
1766
  source,
1925
- fit: "contain"
1767
+ fit: "contain",
1768
+ ...identity.name !== void 0 ? { name: identity.name } : {},
1769
+ ...identity.subject !== void 0 ? { subject: identity.subject } : {}
1926
1770
  }).then((res) => {
1927
1771
  syncDTO(res.created, "baked");
1928
1772
  apply({
1929
1773
  t: "select",
1930
- ids: [refKey(res.created.ref)]
1774
+ ids: [require_seam.refKey(res.created.ref)]
1931
1775
  });
1932
- }, (err) => console.error("[annotation] stamp placement failed:", err));
1776
+ return res.created.ref;
1777
+ });
1778
+ };
1779
+ /** The click path's fire-and-forget wrapper: a rejected placement is logged,
1780
+ * never thrown into the gesture. */
1781
+ const createStampAtSync = (pon, point, source, desired, rotCW = 0, identity = {}) => {
1782
+ const placed = createStampAt(pon, point, source, desired, rotCW, identity);
1783
+ if (!placed) return false;
1784
+ placed.catch((err) => console.error("[annotation] stamp placement failed:", err));
1933
1785
  return true;
1934
1786
  };
1787
+ /** Programmatic placement — the same law as a click, awaited. */
1788
+ const placeStamp = async (input, placement) => {
1789
+ const meta = (0, _embedpdf_engine_core_runtime.sniffBinaryMetadata)((await (0, _embedpdf_engine_core_runtime.resolveBinarySource)(input.source)).bytes);
1790
+ if (!meta) throw new Error("[annotation] stamp source must be PNG, JPEG, or single-page PDF bytes");
1791
+ const placed = createStampAt(placement.pageObjectNumber, placement.at, input.source, desiredStampSize(meta, placement.targetWidth ?? input.targetWidth, input.intrinsicSize), placement.rotation ?? 0, {
1792
+ ...input.name !== void 0 ? { name: input.name } : {},
1793
+ ...input.subject !== void 0 ? { subject: input.subject } : {}
1794
+ });
1795
+ if (!placed) throw new Error(`[annotation] cannot place a stamp on page ${placement.pageObjectNumber}: document or page not ready`);
1796
+ return placed;
1797
+ };
1935
1798
  /** The active tool's upright counter-rotation for a click at `displayRotation`
1936
1799
  * (0 when the tool doesn't ask for upright, or the display isn't rotated). */
1937
1800
  const uprightRotFor = (displayRotation) => {
1938
1801
  if (!displayRotation) return 0;
1939
- const ix = ctx.tryGet(_embedpdf_plugin_interaction.InteractionToken);
1802
+ const ix = ctx.tryGet(_embedpdf_plugin_interaction_contract.InteractionToken);
1940
1803
  return (ix ? registry.get(ix.activeToolId()) : void 0)?.upright ? (0, _embedpdf_core_annotation.uprightRotation)(displayRotation) : 0;
1941
1804
  };
1942
1805
  const placeArmedStamp = (pon, point, displayRotation) => {
1943
1806
  const armed = armedStamp;
1944
1807
  if (!armed) return false;
1945
- return createStampAt(pon, point, armed.source, {
1808
+ return createStampAtSync(pon, point, armed.source, {
1946
1809
  width: armed.width,
1947
1810
  height: armed.height
1948
- }, uprightRotFor(displayRotation));
1811
+ }, uprightRotFor(displayRotation), {
1812
+ ...armed.name !== void 0 ? { name: armed.name } : {},
1813
+ ...armed.subject !== void 0 ? { subject: armed.subject } : {}
1814
+ });
1949
1815
  };
1950
1816
  /** Sniff a stamp source (rejecting non-image bytes) and place it at its
1951
1817
  * intrinsic size (fit to the page in {@link createStampAt}); `targetWidth`
@@ -1956,7 +1822,7 @@ function createAnnotationCapability(ctx, config = {}) {
1956
1822
  console.error("[annotation] stamp source must be PNG, JPEG, or single-page PDF bytes");
1957
1823
  return;
1958
1824
  }
1959
- createStampAt(pon, point, source, desiredStampSize(meta, targetWidth), rotCW);
1825
+ createStampAtSync(pon, point, source, desiredStampSize(meta, targetWidth), rotCW);
1960
1826
  };
1961
1827
  /**
1962
1828
  * Click-to-place: resolve the ACTIVE tool's source spec. Fixed `bytes` place
@@ -1965,7 +1831,7 @@ function createAnnotationCapability(ctx, config = {}) {
1965
1831
  * document changed while the picker was open (the intent expired).
1966
1832
  */
1967
1833
  const requestStampAt = (pon, point, displayRotation) => {
1968
- const ix = ctx.tryGet(_embedpdf_plugin_interaction.InteractionToken);
1834
+ const ix = ctx.tryGet(_embedpdf_plugin_interaction_contract.InteractionToken);
1969
1835
  const tool = ix ? registry.get(ix.activeToolId()) : void 0;
1970
1836
  const spec = tool?.source;
1971
1837
  if (!spec) return false;
@@ -1998,7 +1864,7 @@ function createAnnotationCapability(ctx, config = {}) {
1998
1864
  provider(req).then((picked) => {
1999
1865
  if (!picked) return;
2000
1866
  if (ctx.doc !== docAtClick) return;
2001
- if (ctx.tryGet(_embedpdf_plugin_interaction.InteractionToken)?.activeToolId() !== tool.id) return;
1867
+ if (ctx.tryGet(_embedpdf_plugin_interaction_contract.InteractionToken)?.activeToolId() !== tool.id) return;
2002
1868
  place(picked);
2003
1869
  }, (err) => console.error("[annotation] file-picker provider failed:", err));
2004
1870
  return true;
@@ -2015,12 +1881,12 @@ function createAnnotationCapability(ctx, config = {}) {
2015
1881
  width: crop.right - crop.left,
2016
1882
  height: crop.top - crop.bottom
2017
1883
  }, rotCW);
2018
- const draft = iconPlacementDraft(tool.subtype, boxGeomFields(box, rotCW, crop), (0, _embedpdf_core_annotation.defaultsFor)(model(), tool.preset), tool.flags, file);
1884
+ const draft = iconPlacementDraft(tool.subtype, require_seam.boxGeomFields(box, rotCW, crop), (0, _embedpdf_core_annotation.defaultsFor)(model(), tool.preset), tool.flags, file);
2019
1885
  doc.page(pon).annotations.create(draft).then((res) => {
2020
1886
  syncDTO(res.created, "baked");
2021
1887
  apply({
2022
1888
  t: "select",
2023
- ids: [refKey(res.created.ref)]
1889
+ ids: [require_seam.refKey(res.created.ref)]
2024
1890
  });
2025
1891
  }, (err) => console.error("[annotation] icon placement failed:", err));
2026
1892
  return true;
@@ -2038,7 +1904,7 @@ function createAnnotationCapability(ctx, config = {}) {
2038
1904
  */
2039
1905
  const placeAt = (pon, point, displayRotation) => {
2040
1906
  if (placeArmedStamp(pon, point, displayRotation)) return true;
2041
- const ix = ctx.tryGet(_embedpdf_plugin_interaction.InteractionToken);
1907
+ const ix = ctx.tryGet(_embedpdf_plugin_interaction_contract.InteractionToken);
2042
1908
  const tool = ix ? registry.get(ix.activeToolId()) : void 0;
2043
1909
  if (!tool) return false;
2044
1910
  if (tool.subtype === "stamp") return requestStampAt(pon, point, displayRotation);
@@ -2049,7 +1915,7 @@ function createAnnotationCapability(ctx, config = {}) {
2049
1915
  };
2050
1916
  /** The page a ref lives on: from the loaded model first (covers obj/nm refs
2051
1917
  * that don't carry a pon), else the ref itself (index refs do). */
2052
- const ponForRef = (ref) => model().byId[refKey(ref)]?.pon ?? (ref.kind === "index" ? ref.pageObjectNumber : null);
1918
+ const ponForRef = (ref) => model().byId[require_seam.refKey(ref)]?.pon ?? (ref.kind === "index" ? ref.pageObjectNumber : null);
2053
1919
  /**
2054
1920
  * Re-sync one annotation into the model from the authoritative engine DTO,
2055
1921
  * with the render `source` the caller decides: `'vector'` when WE authored or
@@ -2228,10 +2094,10 @@ function createAnnotationCapability(ctx, config = {}) {
2228
2094
  });
2229
2095
  const byMember = /* @__PURE__ */ new Map();
2230
2096
  for (const t of threads) {
2231
- byMember.set(refKey(t.root.ref), t);
2232
- for (const r of t.replies) byMember.set(refKey(r.ref), t);
2233
- for (const g of t.groupedParts) byMember.set(refKey(g.ref), t);
2234
- for (const s of t.review.statusRefs) byMember.set(refKey(s), t);
2097
+ byMember.set(require_seam.refKey(t.root.ref), t);
2098
+ for (const r of t.replies) byMember.set(require_seam.refKey(r.ref), t);
2099
+ for (const g of t.groupedParts) byMember.set(require_seam.refKey(g.ref), t);
2100
+ for (const s of t.review.statusRefs) byMember.set(require_seam.refKey(s), t);
2235
2101
  }
2236
2102
  return {
2237
2103
  threads,
@@ -2253,7 +2119,7 @@ function createAnnotationCapability(ctx, config = {}) {
2253
2119
  return threadsMemo;
2254
2120
  };
2255
2121
  const threadOf = (ref) => {
2256
- const t = threadsIndex().byMember.get(refKey(ref));
2122
+ const t = threadsIndex().byMember.get(require_seam.refKey(ref));
2257
2123
  if (!t) throw new Error("[annotation] no comment thread contains this ref");
2258
2124
  return t;
2259
2125
  };
@@ -2273,7 +2139,7 @@ function createAnnotationCapability(ctx, config = {}) {
2273
2139
  await doc.page(ref.pageObjectNumber).annotations.delete(ref);
2274
2140
  apply({
2275
2141
  t: "remove",
2276
- ids: [refKey(ref)]
2142
+ ids: [require_seam.refKey(ref)]
2277
2143
  });
2278
2144
  };
2279
2145
  const REPLY_FLAGS = {
@@ -2289,7 +2155,7 @@ function createAnnotationCapability(ctx, config = {}) {
2289
2155
  /** Coarse authority mirror (doc.annotate.modify) — B3 replaces this with
2290
2156
  * the collab-scope mirrors; the FLAG split is already the real rule. */
2291
2157
  const mutationTarget = (ref) => {
2292
- const d = model().byId[refKey(ref)]?.data;
2158
+ const d = model().byId[require_seam.refKey(ref)]?.data;
2293
2159
  return {
2294
2160
  ...d?.userId !== void 0 ? { userId: d.userId } : {},
2295
2161
  ...d?.groupId !== void 0 ? { groupId: d.groupId } : {}
@@ -2322,7 +2188,7 @@ function createAnnotationCapability(ctx, config = {}) {
2322
2188
  };
2323
2189
  const allowsMutation = (action, ref) => ctx.doc?.security.allowsAnnotationMutation(action, mutationTarget(ref)) ?? false;
2324
2190
  const deletableOne = (ref) => {
2325
- const a = model().byId[refKey(ref)];
2191
+ const a = model().byId[require_seam.refKey(ref)];
2326
2192
  return !!a && (0, _embedpdf_core_annotation.annotDeletable)(a);
2327
2193
  };
2328
2194
  const threadMemberRefs = (t) => [
@@ -2333,7 +2199,7 @@ function createAnnotationCapability(ctx, config = {}) {
2333
2199
  ];
2334
2200
  const comments = {
2335
2201
  threads: () => threadsIndex().threads,
2336
- thread: (ref) => threadsIndex().byMember.get(refKey(ref)) ?? null,
2202
+ thread: (ref) => threadsIndex().byMember.get(require_seam.refKey(ref)) ?? null,
2337
2203
  hydration: () => ctx.getState().hydration,
2338
2204
  rehydrate,
2339
2205
  reply: async (ref, text) => {
@@ -2348,7 +2214,7 @@ function createAnnotationCapability(ctx, config = {}) {
2348
2214
  })).ref;
2349
2215
  },
2350
2216
  edit: async (ref, text) => {
2351
- const subtype = model().byId[refKey(ref)]?.data?.subtype;
2217
+ const subtype = model().byId[require_seam.refKey(ref)]?.data?.subtype;
2352
2218
  if (!subtype) throw new Error("[annotation] cannot edit an uncommitted annotation");
2353
2219
  await updateOne(ref, {
2354
2220
  subtype,
@@ -2411,13 +2277,13 @@ function createAnnotationCapability(ctx, config = {}) {
2411
2277
  };
2412
2278
  },
2413
2279
  permissionsFor: (ref) => {
2414
- model().byId[refKey(ref)]?.flags;
2415
- const t = threadsIndex().byMember.get(refKey(ref)) ?? null;
2280
+ model().byId[require_seam.refKey(ref)]?.flags;
2281
+ const t = threadsIndex().byMember.get(require_seam.refKey(ref)) ?? null;
2416
2282
  return {
2417
2283
  canReply: allowsCreate(),
2418
2284
  canSetStatus: allowsCreate(),
2419
2285
  canEditText: (() => {
2420
- const a = model().byId[refKey(ref)];
2286
+ const a = model().byId[require_seam.refKey(ref)];
2421
2287
  return !!a && (0, _embedpdf_core_annotation.annotContentsEditable)(a);
2422
2288
  })(),
2423
2289
  canDelete: deletableOne(ref),
@@ -2463,7 +2329,7 @@ function createAnnotationCapability(ctx, config = {}) {
2463
2329
  const crop = cropOf(a.pon);
2464
2330
  if (!crop) return;
2465
2331
  const page = doc.page(a.pon);
2466
- const target = writableTarget(desired);
2332
+ const target = require_seam.writableTarget(desired);
2467
2333
  const rects = desired == null ? [] : linkChildRects(a).map((r) => (0, _embedpdf_core_annotation.contentToPdfRect)(r, crop));
2468
2334
  const current = (0, _embedpdf_core_annotation.linkChildrenOf)(model(), id);
2469
2335
  try {
@@ -2561,7 +2427,7 @@ function createAnnotationCapability(ctx, config = {}) {
2561
2427
  apply({
2562
2428
  t: "created",
2563
2429
  tempId: fx.primary,
2564
- id: refKey(primaryResult.created.ref),
2430
+ id: require_seam.refKey(primaryResult.created.ref),
2565
2431
  ref: primaryResult.created.ref
2566
2432
  });
2567
2433
  syncDTO(primaryResult.created, "vector");
@@ -2580,7 +2446,7 @@ function createAnnotationCapability(ctx, config = {}) {
2580
2446
  apply({
2581
2447
  t: "created",
2582
2448
  tempId,
2583
- id: refKey(result.created.ref),
2449
+ id: require_seam.refKey(result.created.ref),
2584
2450
  ref: result.created.ref
2585
2451
  });
2586
2452
  syncDTO(result.created, "vector");
@@ -2589,7 +2455,7 @@ function createAnnotationCapability(ctx, config = {}) {
2589
2455
  const removeIds = ids.filter((id) => !committed.some((c) => c.tempId === id));
2590
2456
  for (const part of [...committed].reverse()) try {
2591
2457
  await doc.page(primary.pon).annotations.delete(part.ref);
2592
- removeIds.push(refKey(part.ref));
2458
+ removeIds.push(require_seam.refKey(part.ref));
2593
2459
  } catch {}
2594
2460
  if (removeIds.length) apply({
2595
2461
  t: "remove",
@@ -2607,7 +2473,7 @@ function createAnnotationCapability(ctx, config = {}) {
2607
2473
  apply({
2608
2474
  t: "created",
2609
2475
  tempId: fx.id,
2610
- id: refKey(res.created.ref),
2476
+ id: require_seam.refKey(res.created.ref),
2611
2477
  ref: res.created.ref
2612
2478
  });
2613
2479
  syncDTO(res.created, "vector");
@@ -2637,8 +2503,62 @@ function createAnnotationCapability(ctx, config = {}) {
2637
2503
  });
2638
2504
  });
2639
2505
  } else if (fx.fx === "syncLink") scheduleLinkSync(fx.id, { target: fx.target });
2640
- else doc.page(fx.ref.pageObjectNumber).annotations.delete(fx.ref).then(() => {}, () => {});
2506
+ else {
2507
+ require_seam.refKey(fx.ref);
2508
+ doc.page(fx.ref.pageObjectNumber).annotations.delete(fx.ref).then(() => {}, () => {});
2509
+ }
2641
2510
  }
2511
+ const reloadPageNow = async (pon) => {
2512
+ const doc = ctx.doc;
2513
+ const crop = cropOf(pon);
2514
+ if (!doc || !crop) return;
2515
+ try {
2516
+ const snap = await doc.page(pon).annotations.list();
2517
+ const m = model();
2518
+ const stale = m.order.filter((id) => m.byId[id]?.pon === pon);
2519
+ if (stale.length) apply({
2520
+ t: "remove",
2521
+ ids: stale
2522
+ });
2523
+ apply({
2524
+ t: "loaded",
2525
+ annots: snap.annotations.map((d) => ingestDTO(d, crop))
2526
+ });
2527
+ } catch {}
2528
+ };
2529
+ /** Script patch → the engine's per-kind patch vocabulary. Colors cross the
2530
+ * Acrobat-array → engine {r,g,b}/255 boundary here; everything else maps
2531
+ * one-to-one (the VM's validity matrix already scoped keys per kind). */
2532
+ const engineScriptPatch = (subtype, patch) => {
2533
+ const out = { subtype };
2534
+ const toEngineColor = (color) => {
2535
+ const rgb = (0, _embedpdf_core_acrojs.scriptColorToRgb)(color);
2536
+ return rgb ? {
2537
+ r: Math.round(rgb.r * 255),
2538
+ g: Math.round(rgb.g * 255),
2539
+ b: Math.round(rgb.b * 255)
2540
+ } : null;
2541
+ };
2542
+ if (patch.strokeColor) {
2543
+ const color = toEngineColor(patch.strokeColor);
2544
+ if (!color) return /* @__PURE__ */ new Error("transparent stroke colours are not supported");
2545
+ out.color = color;
2546
+ }
2547
+ if (patch.fillColor) out.interiorColor = toEngineColor(patch.fillColor);
2548
+ if (patch.opacity !== void 0) out.opacity = patch.opacity;
2549
+ if (patch.width !== void 0) out.strokeWidth = patch.width;
2550
+ if (patch.borderStyle) out.borderStyle = patch.borderStyle === "D" ? "dashed" : "solid";
2551
+ if (patch.dash) out.dashArray = patch.dash;
2552
+ if (patch.rect) out.rect = {
2553
+ left: patch.rect[0],
2554
+ bottom: patch.rect[1],
2555
+ right: patch.rect[2],
2556
+ top: patch.rect[3]
2557
+ };
2558
+ if (patch.contents !== void 0) out.contents = patch.contents;
2559
+ if (patch.flags) out.flags = patch.flags;
2560
+ return out;
2561
+ };
2642
2562
  return {
2643
2563
  create: async (pon, draft) => {
2644
2564
  const doc = ctx.doc;
@@ -2652,6 +2572,7 @@ function createAnnotationCapability(ctx, config = {}) {
2652
2572
  return res.created.ref;
2653
2573
  },
2654
2574
  armStamp,
2575
+ placeStamp,
2655
2576
  disarmStamp,
2656
2577
  placeArmedStamp,
2657
2578
  requestStampAt,
@@ -2665,7 +2586,20 @@ function createAnnotationCapability(ctx, config = {}) {
2665
2586
  const g = ctx.getState().toolGhost;
2666
2587
  return g && g.pon === pon ? g : null;
2667
2588
  },
2668
- armedStampPreview: () => armedStamp?.preview ?? null,
2589
+ armedStampPreview: (devicePixelWidth) => {
2590
+ const armed = armedStamp;
2591
+ if (!armed?.preview) return Promise.resolve(null);
2592
+ const bucket = require_types.previewBucket(devicePixelWidth ?? 0);
2593
+ let pending = armed.previewCache.get(bucket);
2594
+ if (!pending) {
2595
+ pending = armed.preview(bucket).catch((error) => {
2596
+ console.error("[annotation] stamp ghost preview failed:", error);
2597
+ return null;
2598
+ });
2599
+ armed.previewCache.set(bucket, pending);
2600
+ }
2601
+ return pending;
2602
+ },
2669
2603
  stampArmEpoch: () => ctx.getState().stampArmEpoch,
2670
2604
  setFilePickerProvider: (provider) => {
2671
2605
  filePickerProvider = provider;
@@ -2686,7 +2620,7 @@ function createAnnotationCapability(ctx, config = {}) {
2686
2620
  const resolved = buildToolRegistry([...configTools, def]).get(def.id);
2687
2621
  if (!resolved) throw new Error(`[annotation] could not resolve tool '${def.id}'`);
2688
2622
  registry.set(resolved.id, resolved);
2689
- const un = ctx.tryGet(_embedpdf_plugin_interaction.InteractionToken)?.registerTool({
2623
+ const un = ctx.tryGet(_embedpdf_plugin_interaction_contract.InteractionToken)?.registerTool({
2690
2624
  id: resolved.id,
2691
2625
  cursor: resolved.cursor,
2692
2626
  enables: resolved.enables,
@@ -2720,34 +2654,34 @@ function createAnnotationCapability(ctx, config = {}) {
2720
2654
  await doc.page(pon).annotations.delete(ref);
2721
2655
  apply({
2722
2656
  t: "remove",
2723
- ids: [refKey(ref)]
2657
+ ids: [require_seam.refKey(ref)]
2724
2658
  });
2725
2659
  },
2726
2660
  canRead: () => canRead(),
2727
2661
  canCreate: () => allowsCreate(),
2728
2662
  canEdit: (ref) => {
2729
- const a = model().byId[refKey(ref)];
2663
+ const a = model().byId[require_seam.refKey(ref)];
2730
2664
  return !!a && (0, _embedpdf_core_annotation.annotTransformable)(a);
2731
2665
  },
2732
2666
  canDelete: (ref) => {
2733
- const a = model().byId[refKey(ref)];
2667
+ const a = model().byId[require_seam.refKey(ref)];
2734
2668
  return !!a && (0, _embedpdf_core_annotation.annotDeletable)(a);
2735
2669
  },
2736
2670
  comments,
2737
2671
  links: {
2738
2672
  of: (ref) => {
2739
- const a = model().byId[refKey(ref)];
2673
+ const a = model().byId[require_seam.refKey(ref)];
2740
2674
  if (!a) return null;
2741
2675
  return a.subtype === "link" ? a.link ?? null : (0, _embedpdf_core_annotation.linkOf)(model(), a.id);
2742
2676
  },
2743
- set: (ref, target) => scheduleLinkSync(refKey(ref), { target }),
2744
- clear: (ref) => scheduleLinkSync(refKey(ref), { target: null })
2677
+ set: (ref, target) => scheduleLinkSync(require_seam.refKey(ref), { target }),
2678
+ clear: (ref) => scheduleLinkSync(require_seam.refKey(ref), { target: null })
2745
2679
  },
2746
2680
  getSelection: () => {
2747
2681
  const m = model();
2748
2682
  return m.selected.map((id) => m.byId[id]?.ref).filter((r) => r != null);
2749
2683
  },
2750
- get: (ref) => model().byId[refKey(ref)]?.data ?? null,
2684
+ get: (ref) => model().byId[require_seam.refKey(ref)]?.data ?? null,
2751
2685
  list: (pon) => {
2752
2686
  const m = model();
2753
2687
  return m.order.map((id) => m.byId[id]).filter((a) => a?.pon === pon).map((a) => a?.data).filter((d) => d != null);
@@ -2779,10 +2713,13 @@ function createAnnotationCapability(ctx, config = {}) {
2779
2713
  const h = (0, _embedpdf_core_annotation.hitTest)(m, at.pon, at.point, chromeGeomAt(at.scale), m.hitMargin, pageBoxOf(at.pon), inertIdsAt(at.pon), viewEnv(at.zoom, at.rotation));
2780
2714
  if (h.t === "annot") id = h.id;
2781
2715
  }
2782
- if (m.hovered !== id) apply({
2783
- t: "hover",
2784
- id
2785
- });
2716
+ if (m.hovered !== id) {
2717
+ hoverFeed?.hover(id);
2718
+ apply({
2719
+ t: "hover",
2720
+ id
2721
+ });
2722
+ }
2786
2723
  },
2787
2724
  behaviorFor: (a) => behaviors.find((b) => b.matches(a) && b.engaged()) ?? null,
2788
2725
  linkItemsOn: (pon) => memoLinkItems(pon),
@@ -2866,7 +2803,7 @@ function createAnnotationCapability(ctx, config = {}) {
2866
2803
  cancelCreationDraft: () => apply({ t: "cancel" }),
2867
2804
  markupFromSelection: (subtype, preset) => {
2868
2805
  if (!allowsCreate()) return false;
2869
- const selection = ctx.tryGet(_embedpdf_plugin_selection.SelectionToken);
2806
+ const selection = ctx.tryGet(_embedpdf_plugin_selection_contract.SelectionToken);
2870
2807
  if (!selection || !selection.hasSelection()) return false;
2871
2808
  const snapshot = selection.snapshot();
2872
2809
  let created = false;
@@ -2942,7 +2879,7 @@ function createAnnotationCapability(ctx, config = {}) {
2942
2879
  select: (ref, options) => {
2943
2880
  apply({
2944
2881
  t: "select",
2945
- ids: [refKey(ref)],
2882
+ ids: [require_seam.refKey(ref)],
2946
2883
  add: options?.add
2947
2884
  });
2948
2885
  },
@@ -3002,24 +2939,7 @@ function createAnnotationCapability(ctx, config = {}) {
3002
2939
  return subs.length > 0 && subs.every((a) => a.ref != null && allowsMutation("update", a.ref));
3003
2940
  },
3004
2941
  ensurePage: () => {},
3005
- reloadPage: async (pon) => {
3006
- const doc = ctx.doc;
3007
- const crop = cropOf(pon);
3008
- if (!doc || !crop) return;
3009
- try {
3010
- const snap = await doc.page(pon).annotations.list();
3011
- const m = model();
3012
- const stale = m.order.filter((id) => m.byId[id]?.pon === pon);
3013
- if (stale.length) apply({
3014
- t: "remove",
3015
- ids: stale
3016
- });
3017
- apply({
3018
- t: "loaded",
3019
- annots: snap.annotations.map((d) => ingestDTO(d, crop))
3020
- });
3021
- } catch {}
3022
- },
2942
+ reloadPage: (pon) => reloadPageNow(pon),
3023
2943
  hydration: () => ctx.getState().hydration,
3024
2944
  ensureHydrated: () => {
3025
2945
  if (hydrationStarted) return;
@@ -3038,7 +2958,7 @@ function createAnnotationCapability(ctx, config = {}) {
3038
2958
  currentEditing: () => model().editing,
3039
2959
  beginTextEdit: (ref) => apply({
3040
2960
  t: "beginTextEdit",
3041
- id: refKey(ref)
2961
+ id: require_seam.refKey(ref)
3042
2962
  }),
3043
2963
  beginTextEditAt: (pon, point, scale, rotation, zoom) => {
3044
2964
  const m = model();
@@ -3056,10 +2976,10 @@ function createAnnotationCapability(ctx, config = {}) {
3056
2976
  setContents: (ref, text) => {
3057
2977
  apply({
3058
2978
  t: "setText",
3059
- id: refKey(ref),
2979
+ id: require_seam.refKey(ref),
3060
2980
  text
3061
2981
  });
3062
- const key = refKey(ref);
2982
+ const key = require_seam.refKey(ref);
3063
2983
  clearTimeout(textTimers.get(key));
3064
2984
  textTimers.set(key, setTimeout(() => {
3065
2985
  textTimers.delete(key);
@@ -3085,6 +3005,72 @@ function createAnnotationCapability(ctx, config = {}) {
3085
3005
  textTimers.clear();
3086
3006
  apply({ t: "endTextEdit" });
3087
3007
  },
3008
+ commitScriptEffects: async (entries) => {
3009
+ const doc = ctx.doc;
3010
+ if (!doc) return { results: entries.map((entry) => ({
3011
+ annotObjectNumber: entry.annotObjectNumber,
3012
+ status: "failed",
3013
+ error: "no document"
3014
+ })) };
3015
+ const results = [];
3016
+ const touchedPons = /* @__PURE__ */ new Set();
3017
+ let failed = false;
3018
+ for (const entry of entries) {
3019
+ if (failed) {
3020
+ results.push({
3021
+ annotObjectNumber: entry.annotObjectNumber,
3022
+ status: "skipped"
3023
+ });
3024
+ continue;
3025
+ }
3026
+ const loaded = model().byId[`obj:${entry.annotObjectNumber}`];
3027
+ const pon = loaded?.pon ?? entry.pageObjectNumber;
3028
+ let ref = loaded?.ref ?? null;
3029
+ let subtype = loaded?.subtype;
3030
+ if ((!ref || !subtype) && pon !== void 0) try {
3031
+ const { annotations } = await doc.page(pon).annotations.list();
3032
+ const dto = annotations.find((candidate) => candidate.ref.kind === "objectNumber" && candidate.ref.annotObjectNumber === entry.annotObjectNumber);
3033
+ if (dto) {
3034
+ ref = dto.ref;
3035
+ subtype = dto.subtype;
3036
+ }
3037
+ } catch {}
3038
+ if (pon === void 0 || !ref || !subtype) {
3039
+ results.push({
3040
+ annotObjectNumber: entry.annotObjectNumber,
3041
+ status: "failed",
3042
+ error: "annotation not resolved (page not loaded)"
3043
+ });
3044
+ continue;
3045
+ }
3046
+ const patch = engineScriptPatch(subtype, entry.patch);
3047
+ if (patch instanceof Error) {
3048
+ results.push({
3049
+ annotObjectNumber: entry.annotObjectNumber,
3050
+ status: "failed",
3051
+ error: patch.message
3052
+ });
3053
+ continue;
3054
+ }
3055
+ try {
3056
+ await doc.page(pon).annotations.update(ref, patch);
3057
+ touchedPons.add(pon);
3058
+ results.push({
3059
+ annotObjectNumber: entry.annotObjectNumber,
3060
+ status: "applied"
3061
+ });
3062
+ } catch (error) {
3063
+ failed = true;
3064
+ results.push({
3065
+ annotObjectNumber: entry.annotObjectNumber,
3066
+ status: "failed",
3067
+ error: error instanceof Error ? error.message : String(error)
3068
+ });
3069
+ }
3070
+ }
3071
+ for (const touched of touchedPons) await reloadPageNow(touched);
3072
+ return { results };
3073
+ },
3088
3074
  registerBehavior: (b) => {
3089
3075
  behaviors.push(b);
3090
3076
  return () => {
@@ -3095,10 +3081,10 @@ function createAnnotationCapability(ctx, config = {}) {
3095
3081
  };
3096
3082
  }
3097
3083
  //#endregion
3098
- Object.defineProperty(exports, "AnnotationToken", {
3084
+ Object.defineProperty(exports, "ARMED_STAMP_TOOL_ID", {
3099
3085
  enumerable: true,
3100
3086
  get: function() {
3101
- return AnnotationToken;
3087
+ return ARMED_STAMP_TOOL_ID;
3102
3088
  }
3103
3089
  });
3104
3090
  Object.defineProperty(exports, "DEFAULT_TOOLS", {
@@ -3131,18 +3117,6 @@ Object.defineProperty(exports, "isTouchDirect", {
3131
3117
  return isTouchDirect;
3132
3118
  }
3133
3119
  });
3134
- Object.defineProperty(exports, "refKey", {
3135
- enumerable: true,
3136
- get: function() {
3137
- return refKey;
3138
- }
3139
- });
3140
- Object.defineProperty(exports, "styleFromDTO", {
3141
- enumerable: true,
3142
- get: function() {
3143
- return styleFromDTO;
3144
- }
3145
- });
3146
3120
  Object.defineProperty(exports, "toCreateDraft", {
3147
3121
  enumerable: true,
3148
3122
  get: function() {
@@ -3155,11 +3129,5 @@ Object.defineProperty(exports, "toPatch", {
3155
3129
  return toPatch;
3156
3130
  }
3157
3131
  });
3158
- Object.defineProperty(exports, "widgetAppearanceFromProps", {
3159
- enumerable: true,
3160
- get: function() {
3161
- return widgetAppearanceFromProps;
3162
- }
3163
- });
3164
3132
 
3165
- //# sourceMappingURL=capability-Cl9x8oLS.cjs.map
3133
+ //# sourceMappingURL=capability-R7dgogJs.cjs.map