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

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-Cl9x8oLS.cjs → capability-Csb9WMm-.cjs} +256 -348
  8. package/dist/capability-Csb9WMm-.cjs.map +1 -0
  9. package/dist/{capability-BRf0HrOi.js → capability-DBsr2D-L.js} +191 -259
  10. package/dist/capability-DBsr2D-L.js.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 +8 -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 +22 -23
  22. package/dist/index.cjs.map +1 -1
  23. package/dist/index.d.cts +4 -46
  24. package/dist/index.d.ts +4 -46
  25. package/dist/index.js +12 -13
  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-CwytT-TI.js +13 -0
  36. package/dist/types-CwytT-TI.js.map +1 -0
  37. package/dist/types-DimcLIic.cjs +17 -0
  38. package/dist/types-DimcLIic.cjs.map +1 -0
  39. package/dist/{types-Due7UgzE.d.cts → types-eLTpocXz.d.cts} +37 -9
  40. package/dist/{types-DITX_3CG.d.ts → types-otwv-Cdl.d.ts} +36 -8
  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,12 @@
1
- import { CONTINUOUS_RENDER_POLICY, createCapabilityToken, snapAppearanceScale } from "@embedpdf/core";
2
- import { InteractionToken } from "@embedpdf/plugin-interaction";
1
+ import { a as contentToPdfRect$1, c as insetPdfRectByRD, d as refKey, f as rotFromDTO, g as writableTarget, i as contentToPdfPoint, l as pdfToContentPoint$1, m as toPdfRotation, n as boxGeomFromDTO, o as cssToColor, p as styleFromDTO, r as colorToCss, s as fromPdfRotation, t as boxGeomFields, u as pdfToContentRect$1 } from "./seam-C4L_7rDt.js";
2
+ import { CONTINUOUS_RENDER_POLICY, snapAppearanceScale } from "@embedpdf/core";
3
+ import { ActionsToken, createHoverPump } from "@embedpdf/plugin-actions/contract";
4
+ import { InteractionToken } from "@embedpdf/plugin-interaction/contract";
3
5
  import { buildCommentThreads, encodeStableIdKey, resolveBinarySource, sniffBinaryMetadata } from "@embedpdf/engine-core/runtime";
4
- import { SelectionToken } from "@embedpdf/plugin-selection";
5
- import { FLAG_KEYS, annotContentsEditable, annotDeletable, annotTransformable, calloutLinePoints, canMove, chrome, clickCreateGeom, cloudyBorderExtent, contentToPdfPoint, contentToPdfRect, contentToPdfRect as contentToPdfRect$1, creationDraftAnchor, cursorAt, defaultsFor, expandGroups, fitStampBox, geomPdfBounds, geomRotation, geomVisualBounds, groupKeyOf, hitTest, initialTextStyle, isSubstrateOnly, linkChildrenOf, linkOf, normalizeDeg, pageItems, pdfToContentPoint, pdfToContentPoint as pdfToContentPoint$1, pdfToContentRect, pdfToContentRect as pdfToContentRect$1, propsFor, readProp, resolveClickPlacement, rotatedAabb, selectionAnchor, selectionQuad, sharedProps, styleFromProps, textBoxes, unionRect, update, uprightRotation, viewable } from "@embedpdf/core-annotation";
6
+ import { scriptColorToRgb } from "@embedpdf/core-acrojs";
7
+ import { SelectionToken } from "@embedpdf/plugin-selection/contract";
8
+ import { FLAG_KEYS, annotContentsEditable, annotDeletable, annotTransformable, calloutLinePoints, canMove, chrome, clickCreateGeom, cloudyBorderExtent, contentToPdfRect, creationDraftAnchor, cursorAt, defaultsFor, expandGroups, fitStampBox, geomPdfBounds, geomRotation, geomVisualBounds, groupKeyOf, hitTest, initialTextStyle, isSubstrateOnly, linkChildrenOf, linkOf, pageItems, pdfToContentPoint, pdfToContentRect, propsFor, readProp, resolveClickPlacement, rotatedAabb, selectionAnchor, selectionQuad, sharedProps, styleFromProps, textBoxes, unionRect, update, uprightRotation, viewable } from "@embedpdf/core-annotation";
6
9
  import { normalizeQuad, textQuadBounds } from "@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 = 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) => normalizeDeg(-rotCW);
48
- const fromPdfRotation = (rotPdf) => 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: contentToPdfRect(rect, crop),
64
- rotation: null,
65
- unrotatedRect: null
66
- };
67
- return {
68
- rect: contentToPdfRect(rotatedAabb(rect, rot), crop),
69
- unrotatedRect: 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: 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
10
  //#region src/repository/props.ts
237
11
  /**
238
12
  * GENERIC per-key prop lowerings — the 1:1 mappings from the flat props
@@ -909,6 +683,46 @@ function toCreateDraft(a, crop) {
909
683
  };
910
684
  }
911
685
  //#endregion
686
+ //#region src/hover-feed.ts
687
+ /**
688
+ * The annotation plane's E/X trigger feed — a thin adapter from the ONE
689
+ * pointer-driven hover seam (the `hoverAt` diff) onto the shared hover pump.
690
+ *
691
+ * Anti-cascade is structural and lives in the CALLER's seam placement:
692
+ * reducer-side hover clears (session-hide, remove, reload) never pass
693
+ * through `hoverAt`, so an effect-induced hover loss can never masquerade as
694
+ * a cursor exit. This module only decides WHO is hoverable here:
695
+ *
696
+ * - Drafts (no engine ref) can't carry /AA — skipped.
697
+ * - Widgets and links belong to their own event planes (fill controls /
698
+ * LinkLayer anchors own their pixels). When they ARE hit-testable on this
699
+ * plane, an authoring tool owns them — firing hover actions while editing
700
+ * would be wrong — so they are skipped unconditionally.
701
+ * - Tree-less annotations are FREE: no target, no dispatch, and per-event
702
+ * flags keep a lone /E or /X from dispatching its inert twin.
703
+ */
704
+ function createAnnotationHoverFeed(actions, annotOf) {
705
+ const pump = createHoverPump(actions.dispatch);
706
+ const targetOf = (id) => {
707
+ if (!id) return null;
708
+ const annot = annotOf(id);
709
+ if (!annot?.ref) return null;
710
+ if (annot.subtype.startsWith("widget") || annot.subtype === "link") return null;
711
+ const enter = Boolean(annot.data?.actions?.cursorEnter?.root);
712
+ const exit = Boolean(annot.data?.actions?.cursorExit?.root);
713
+ if (!enter && !exit) return null;
714
+ return {
715
+ ref: annot.ref,
716
+ pon: annot.pon,
717
+ events: {
718
+ enter,
719
+ exit
720
+ }
721
+ };
722
+ };
723
+ return { hover: (id) => pump.hover(targetOf(id)) };
724
+ }
725
+ //#endregion
912
726
  //#region src/text-item.ts
913
727
  /**
914
728
  * The free-text presentation projection: the core's geometry-only `textBoxes`
@@ -1522,6 +1336,8 @@ function createAnnotationCapability(ctx, config = {}) {
1522
1336
  * slot. See {@link FilePickerProvider}. */
1523
1337
  let filePickerProvider = null;
1524
1338
  const model = () => ctx.getState().model;
1339
+ const actionsForHover = ctx.tryGet(ActionsToken);
1340
+ const hoverFeed = actionsForHover ? createAnnotationHoverFeed(actionsForHover, (id) => model().byId[id] ?? null) : null;
1525
1341
  const chromeSettings = () => ctx.getState().chrome;
1526
1342
  /**
1527
1343
  * Ids on a page whose Behavior is currently ENGAGED (form widgets under a
@@ -1674,11 +1490,21 @@ function createAnnotationCapability(ctx, config = {}) {
1674
1490
  if (!viewable(a.flags, false)) continue;
1675
1491
  const target = a.link ?? (a.data?.subtype === "link" ? a.data.target ?? null : null);
1676
1492
  if (target == null || a.geom.t !== "rect") continue;
1493
+ const activate = a.data?.actions?.activate;
1494
+ const ref = a.ref ?? a.data?.ref ?? void 0;
1495
+ const hoverEnter = Boolean(a.data?.actions?.cursorEnter?.root);
1496
+ const hoverExit = Boolean(a.data?.actions?.cursorExit?.root);
1677
1497
  v.push({
1678
1498
  id,
1679
1499
  rect: a.geom.rect,
1680
1500
  target,
1681
- attached: a.group !== void 0
1501
+ attached: a.group !== void 0,
1502
+ ...activate ? { activate } : {},
1503
+ ...ref ? { ref } : {},
1504
+ ...hoverEnter || hoverExit ? { hoverEvents: {
1505
+ enter: hoverEnter,
1506
+ exit: hoverExit
1507
+ } } : {}
1682
1508
  });
1683
1509
  }
1684
1510
  linkItemsCache.set(pon, {
@@ -2637,8 +2463,62 @@ function createAnnotationCapability(ctx, config = {}) {
2637
2463
  });
2638
2464
  });
2639
2465
  } else if (fx.fx === "syncLink") scheduleLinkSync(fx.id, { target: fx.target });
2640
- else doc.page(fx.ref.pageObjectNumber).annotations.delete(fx.ref).then(() => {}, () => {});
2466
+ else {
2467
+ refKey(fx.ref);
2468
+ doc.page(fx.ref.pageObjectNumber).annotations.delete(fx.ref).then(() => {}, () => {});
2469
+ }
2641
2470
  }
2471
+ const reloadPageNow = async (pon) => {
2472
+ const doc = ctx.doc;
2473
+ const crop = cropOf(pon);
2474
+ if (!doc || !crop) return;
2475
+ try {
2476
+ const snap = await doc.page(pon).annotations.list();
2477
+ const m = model();
2478
+ const stale = m.order.filter((id) => m.byId[id]?.pon === pon);
2479
+ if (stale.length) apply({
2480
+ t: "remove",
2481
+ ids: stale
2482
+ });
2483
+ apply({
2484
+ t: "loaded",
2485
+ annots: snap.annotations.map((d) => ingestDTO(d, crop))
2486
+ });
2487
+ } catch {}
2488
+ };
2489
+ /** Script patch → the engine's per-kind patch vocabulary. Colors cross the
2490
+ * Acrobat-array → engine {r,g,b}/255 boundary here; everything else maps
2491
+ * one-to-one (the VM's validity matrix already scoped keys per kind). */
2492
+ const engineScriptPatch = (subtype, patch) => {
2493
+ const out = { subtype };
2494
+ const toEngineColor = (color) => {
2495
+ const rgb = scriptColorToRgb(color);
2496
+ return rgb ? {
2497
+ r: Math.round(rgb.r * 255),
2498
+ g: Math.round(rgb.g * 255),
2499
+ b: Math.round(rgb.b * 255)
2500
+ } : null;
2501
+ };
2502
+ if (patch.strokeColor) {
2503
+ const color = toEngineColor(patch.strokeColor);
2504
+ if (!color) return /* @__PURE__ */ new Error("transparent stroke colours are not supported");
2505
+ out.color = color;
2506
+ }
2507
+ if (patch.fillColor) out.interiorColor = toEngineColor(patch.fillColor);
2508
+ if (patch.opacity !== void 0) out.opacity = patch.opacity;
2509
+ if (patch.width !== void 0) out.strokeWidth = patch.width;
2510
+ if (patch.borderStyle) out.borderStyle = patch.borderStyle === "D" ? "dashed" : "solid";
2511
+ if (patch.dash) out.dashArray = patch.dash;
2512
+ if (patch.rect) out.rect = {
2513
+ left: patch.rect[0],
2514
+ bottom: patch.rect[1],
2515
+ right: patch.rect[2],
2516
+ top: patch.rect[3]
2517
+ };
2518
+ if (patch.contents !== void 0) out.contents = patch.contents;
2519
+ if (patch.flags) out.flags = patch.flags;
2520
+ return out;
2521
+ };
2642
2522
  return {
2643
2523
  create: async (pon, draft) => {
2644
2524
  const doc = ctx.doc;
@@ -2779,10 +2659,13 @@ function createAnnotationCapability(ctx, config = {}) {
2779
2659
  const h = hitTest(m, at.pon, at.point, chromeGeomAt(at.scale), m.hitMargin, pageBoxOf(at.pon), inertIdsAt(at.pon), viewEnv(at.zoom, at.rotation));
2780
2660
  if (h.t === "annot") id = h.id;
2781
2661
  }
2782
- if (m.hovered !== id) apply({
2783
- t: "hover",
2784
- id
2785
- });
2662
+ if (m.hovered !== id) {
2663
+ hoverFeed?.hover(id);
2664
+ apply({
2665
+ t: "hover",
2666
+ id
2667
+ });
2668
+ }
2786
2669
  },
2787
2670
  behaviorFor: (a) => behaviors.find((b) => b.matches(a) && b.engaged()) ?? null,
2788
2671
  linkItemsOn: (pon) => memoLinkItems(pon),
@@ -3002,24 +2885,7 @@ function createAnnotationCapability(ctx, config = {}) {
3002
2885
  return subs.length > 0 && subs.every((a) => a.ref != null && allowsMutation("update", a.ref));
3003
2886
  },
3004
2887
  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
- },
2888
+ reloadPage: (pon) => reloadPageNow(pon),
3023
2889
  hydration: () => ctx.getState().hydration,
3024
2890
  ensureHydrated: () => {
3025
2891
  if (hydrationStarted) return;
@@ -3085,6 +2951,72 @@ function createAnnotationCapability(ctx, config = {}) {
3085
2951
  textTimers.clear();
3086
2952
  apply({ t: "endTextEdit" });
3087
2953
  },
2954
+ commitScriptEffects: async (entries) => {
2955
+ const doc = ctx.doc;
2956
+ if (!doc) return { results: entries.map((entry) => ({
2957
+ annotObjectNumber: entry.annotObjectNumber,
2958
+ status: "failed",
2959
+ error: "no document"
2960
+ })) };
2961
+ const results = [];
2962
+ const touchedPons = /* @__PURE__ */ new Set();
2963
+ let failed = false;
2964
+ for (const entry of entries) {
2965
+ if (failed) {
2966
+ results.push({
2967
+ annotObjectNumber: entry.annotObjectNumber,
2968
+ status: "skipped"
2969
+ });
2970
+ continue;
2971
+ }
2972
+ const loaded = model().byId[`obj:${entry.annotObjectNumber}`];
2973
+ const pon = loaded?.pon ?? entry.pageObjectNumber;
2974
+ let ref = loaded?.ref ?? null;
2975
+ let subtype = loaded?.subtype;
2976
+ if ((!ref || !subtype) && pon !== void 0) try {
2977
+ const { annotations } = await doc.page(pon).annotations.list();
2978
+ const dto = annotations.find((candidate) => candidate.ref.kind === "objectNumber" && candidate.ref.annotObjectNumber === entry.annotObjectNumber);
2979
+ if (dto) {
2980
+ ref = dto.ref;
2981
+ subtype = dto.subtype;
2982
+ }
2983
+ } catch {}
2984
+ if (pon === void 0 || !ref || !subtype) {
2985
+ results.push({
2986
+ annotObjectNumber: entry.annotObjectNumber,
2987
+ status: "failed",
2988
+ error: "annotation not resolved (page not loaded)"
2989
+ });
2990
+ continue;
2991
+ }
2992
+ const patch = engineScriptPatch(subtype, entry.patch);
2993
+ if (patch instanceof Error) {
2994
+ results.push({
2995
+ annotObjectNumber: entry.annotObjectNumber,
2996
+ status: "failed",
2997
+ error: patch.message
2998
+ });
2999
+ continue;
3000
+ }
3001
+ try {
3002
+ await doc.page(pon).annotations.update(ref, patch);
3003
+ touchedPons.add(pon);
3004
+ results.push({
3005
+ annotObjectNumber: entry.annotObjectNumber,
3006
+ status: "applied"
3007
+ });
3008
+ } catch (error) {
3009
+ failed = true;
3010
+ results.push({
3011
+ annotObjectNumber: entry.annotObjectNumber,
3012
+ status: "failed",
3013
+ error: error instanceof Error ? error.message : String(error)
3014
+ });
3015
+ }
3016
+ }
3017
+ for (const touched of touchedPons) await reloadPageNow(touched);
3018
+ return { results };
3019
+ },
3088
3020
  registerBehavior: (b) => {
3089
3021
  behaviors.push(b);
3090
3022
  return () => {
@@ -3095,6 +3027,6 @@ function createAnnotationCapability(ctx, config = {}) {
3095
3027
  };
3096
3028
  }
3097
3029
  //#endregion
3098
- export { fromDTO as a, refKey as c, AnnotationToken as d, isTouchDirect as i, styleFromDTO as l, DEFAULT_TOOLS as n, toCreateDraft as o, buildToolRegistry as r, toPatch as s, createAnnotationCapability as t, widgetAppearanceFromProps as u };
3030
+ export { fromDTO as a, isTouchDirect as i, DEFAULT_TOOLS as n, toCreateDraft as o, buildToolRegistry as r, toPatch as s, createAnnotationCapability as t };
3099
3031
 
3100
- //# sourceMappingURL=capability-BRf0HrOi.js.map
3032
+ //# sourceMappingURL=capability-DBsr2D-L.js.map