@embedpdf/plugin-annotation 1.4.1 → 2.0.0-next.0

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 (106) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +1760 -1444
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/actions.d.ts +58 -19
  6. package/dist/lib/annotation-plugin.d.ts +14 -5
  7. package/dist/lib/index.d.ts +1 -1
  8. package/dist/lib/reducer.d.ts +2 -1
  9. package/dist/lib/selectors.d.ts +10 -8
  10. package/dist/lib/types.d.ts +53 -10
  11. package/dist/preact/index.cjs +1 -1
  12. package/dist/preact/index.cjs.map +1 -1
  13. package/dist/preact/index.js +96 -48
  14. package/dist/preact/index.js.map +1 -1
  15. package/dist/react/index.cjs +1 -1
  16. package/dist/react/index.cjs.map +1 -1
  17. package/dist/react/index.js +96 -48
  18. package/dist/react/index.js.map +1 -1
  19. package/dist/shared/components/annotation-container.d.ts +4 -3
  20. package/dist/shared/components/annotation-layer.d.ts +7 -7
  21. package/dist/shared/components/annotation-paint-layer.d.ts +2 -1
  22. package/dist/shared/components/annotations/stamp.d.ts +2 -1
  23. package/dist/shared/components/annotations.d.ts +3 -2
  24. package/dist/shared/components/render-annotation.d.ts +2 -1
  25. package/dist/shared/components/text-markup.d.ts +2 -1
  26. package/dist/shared/components/types.d.ts +7 -7
  27. package/dist/shared/hooks/use-annotation.d.ts +8 -4
  28. package/dist/shared/index.d.ts +1 -0
  29. package/dist/shared-preact/components/annotation-container.d.ts +4 -3
  30. package/dist/shared-preact/components/annotation-layer.d.ts +7 -7
  31. package/dist/shared-preact/components/annotation-paint-layer.d.ts +2 -1
  32. package/dist/shared-preact/components/annotations/stamp.d.ts +2 -1
  33. package/dist/shared-preact/components/annotations.d.ts +3 -2
  34. package/dist/shared-preact/components/render-annotation.d.ts +2 -1
  35. package/dist/shared-preact/components/text-markup.d.ts +2 -1
  36. package/dist/shared-preact/components/types.d.ts +7 -7
  37. package/dist/shared-preact/hooks/use-annotation.d.ts +8 -4
  38. package/dist/shared-preact/index.d.ts +1 -0
  39. package/dist/shared-react/components/annotation-container.d.ts +4 -3
  40. package/dist/shared-react/components/annotation-layer.d.ts +7 -7
  41. package/dist/shared-react/components/annotation-paint-layer.d.ts +2 -1
  42. package/dist/shared-react/components/annotations/stamp.d.ts +2 -1
  43. package/dist/shared-react/components/annotations.d.ts +3 -2
  44. package/dist/shared-react/components/render-annotation.d.ts +2 -1
  45. package/dist/shared-react/components/text-markup.d.ts +2 -1
  46. package/dist/shared-react/components/types.d.ts +7 -7
  47. package/dist/shared-react/hooks/use-annotation.d.ts +8 -4
  48. package/dist/shared-react/index.d.ts +1 -0
  49. package/dist/svelte/components/AnnotationContainer.svelte.d.ts +26 -0
  50. package/dist/svelte/components/AnnotationLayer.svelte.d.ts +27 -0
  51. package/dist/svelte/components/AnnotationPaintLayer.svelte.d.ts +8 -0
  52. package/dist/svelte/components/Annotations.svelte.d.ts +22 -0
  53. package/dist/svelte/components/PreviewRenderer.svelte.d.ts +8 -0
  54. package/dist/svelte/components/RenderAnnotation.svelte.d.ts +13 -0
  55. package/dist/svelte/components/TextMarkup.svelte.d.ts +8 -0
  56. package/dist/svelte/components/annotations/Circle.svelte.d.ts +26 -0
  57. package/dist/svelte/components/annotations/FreeText.svelte.d.ts +15 -0
  58. package/dist/svelte/components/annotations/Ink.svelte.d.ts +14 -0
  59. package/dist/svelte/components/annotations/Line.svelte.d.ts +18 -0
  60. package/dist/svelte/components/annotations/Polygon.svelte.d.ts +19 -0
  61. package/dist/svelte/components/annotations/Polyline.svelte.d.ts +17 -0
  62. package/dist/svelte/components/annotations/Square.svelte.d.ts +16 -0
  63. package/dist/svelte/components/annotations/Stamp.svelte.d.ts +13 -0
  64. package/dist/svelte/components/annotations/index.d.ts +8 -0
  65. package/dist/svelte/components/index.d.ts +9 -0
  66. package/dist/svelte/components/text-markup/Highlight.svelte.d.ts +13 -0
  67. package/dist/svelte/components/text-markup/Squiggly.svelte.d.ts +13 -0
  68. package/dist/svelte/components/text-markup/Strikeout.svelte.d.ts +13 -0
  69. package/dist/svelte/components/text-markup/Underline.svelte.d.ts +13 -0
  70. package/dist/svelte/components/text-markup/index.d.ts +4 -0
  71. package/dist/svelte/components/types.d.ts +32 -0
  72. package/dist/svelte/hooks/index.d.ts +1 -0
  73. package/dist/svelte/hooks/use-annotation.svelte.d.ts +21 -0
  74. package/dist/svelte/index.cjs +2 -0
  75. package/dist/svelte/index.cjs.map +1 -0
  76. package/dist/svelte/index.d.ts +4 -0
  77. package/dist/svelte/index.js +3083 -0
  78. package/dist/svelte/index.js.map +1 -0
  79. package/dist/svelte/types.d.ts +54 -0
  80. package/dist/vue/components/annotation-container.vue.d.ts +10 -5
  81. package/dist/vue/components/annotation-layer.vue.d.ts +13 -7
  82. package/dist/vue/components/annotation-paint-layer.vue.d.ts +3 -1
  83. package/dist/vue/components/annotations/circle.vue.d.ts +2 -1
  84. package/dist/vue/components/annotations/free-text.vue.d.ts +2 -1
  85. package/dist/vue/components/annotations/ink.vue.d.ts +2 -1
  86. package/dist/vue/components/annotations/line.vue.d.ts +2 -1
  87. package/dist/vue/components/annotations/polygon.vue.d.ts +2 -1
  88. package/dist/vue/components/annotations/polyline.vue.d.ts +2 -1
  89. package/dist/vue/components/annotations/square.vue.d.ts +2 -1
  90. package/dist/vue/components/annotations/stamp.vue.d.ts +3 -1
  91. package/dist/vue/components/annotations.vue.d.ts +7 -3
  92. package/dist/vue/components/preview-renderer.vue.d.ts +2 -1
  93. package/dist/vue/components/render-annotation.vue.d.ts +3 -1
  94. package/dist/vue/components/text-markup/highlight.vue.d.ts +2 -1
  95. package/dist/vue/components/text-markup/squiggly.vue.d.ts +2 -1
  96. package/dist/vue/components/text-markup/strikeout.vue.d.ts +2 -1
  97. package/dist/vue/components/text-markup/underline.vue.d.ts +2 -1
  98. package/dist/vue/components/text-markup.vue.d.ts +3 -1
  99. package/dist/vue/hooks/use-annotation.d.ts +21 -2682
  100. package/dist/vue/index.cjs +1 -1
  101. package/dist/vue/index.cjs.map +1 -1
  102. package/dist/vue/index.d.ts +1 -0
  103. package/dist/vue/index.js +354 -250
  104. package/dist/vue/index.js.map +1 -1
  105. package/dist/vue/types.d.ts +23 -0
  106. package/package.json +20 -12
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { clamp, BasePlugin, createBehaviorEmitter, SET_DOCUMENT } from "@embedpdf/core";
2
- import { PdfAnnotationSubtype, expandRect, rectFromPoints, uuidV4, PdfVerticalAlignment, PdfTextAlignment, PdfStandardFont, PdfAnnotationLineEnding, rotateAndTranslatePoint, PdfAnnotationBorderStyle, PdfAnnotationIcon, ignore, PdfTaskHelper, PdfErrorCode, Task, PdfBlendMode } from "@embedpdf/models";
1
+ import { clamp, BasePlugin, createBehaviorEmitter } from "@embedpdf/core";
2
+ import { PdfAnnotationSubtype, PdfBlendMode, PdfAnnotationBorderStyle, PdfAnnotationLineEnding, PdfVerticalAlignment, PdfTextAlignment, PdfStandardFont, expandRect, rectFromPoints, uuidV4, rotateAndTranslatePoint, PdfAnnotationIcon, ignore, PdfTaskHelper, PdfErrorCode, Task } from "@embedpdf/models";
3
3
  const ANNOTATION_PLUGIN_ID = "annotation";
4
4
  const manifest = {
5
5
  id: ANNOTATION_PLUGIN_ID,
@@ -16,51 +16,75 @@ const manifest = {
16
16
  selectAfterCreate: true
17
17
  }
18
18
  };
19
+ const INIT_ANNOTATION_STATE = "ANNOTATION/INIT_STATE";
20
+ const CLEANUP_ANNOTATION_STATE = "ANNOTATION/CLEANUP_STATE";
21
+ const SET_ACTIVE_DOCUMENT = "ANNOTATION/SET_ACTIVE_DOCUMENT";
19
22
  const SET_ANNOTATIONS = "ANNOTATION/SET_ANNOTATIONS";
20
23
  const SELECT_ANNOTATION = "ANNOTATION/SELECT_ANNOTATION";
21
24
  const DESELECT_ANNOTATION = "ANNOTATION/DESELECT_ANNOTATION";
22
- const ADD_COLOR_PRESET = "ANNOTATION/ADD_COLOR_PRESET";
25
+ const SET_ACTIVE_TOOL_ID = "ANNOTATION/SET_ACTIVE_TOOL_ID";
23
26
  const CREATE_ANNOTATION = "ANNOTATION/CREATE_ANNOTATION";
24
27
  const PATCH_ANNOTATION = "ANNOTATION/PATCH_ANNOTATION";
25
28
  const DELETE_ANNOTATION = "ANNOTATION/DELETE_ANNOTATION";
26
29
  const COMMIT_PENDING_CHANGES = "ANNOTATION/COMMIT";
27
30
  const PURGE_ANNOTATION = "ANNOTATION/PURGE_ANNOTATION";
28
- const SET_ACTIVE_TOOL_ID = "ANNOTATION/SET_ACTIVE_TOOL_ID";
31
+ const ADD_COLOR_PRESET = "ANNOTATION/ADD_COLOR_PRESET";
29
32
  const SET_TOOL_DEFAULTS = "ANNOTATION/SET_TOOL_DEFAULTS";
30
33
  const ADD_TOOL = "ANNOTATION/ADD_TOOL";
31
- const setAnnotations = (p) => ({
34
+ function initAnnotationState(documentId, state) {
35
+ return { type: INIT_ANNOTATION_STATE, payload: { documentId, state } };
36
+ }
37
+ function cleanupAnnotationState(documentId) {
38
+ return { type: CLEANUP_ANNOTATION_STATE, payload: documentId };
39
+ }
40
+ const setAnnotations = (documentId, annotations) => ({
32
41
  type: SET_ANNOTATIONS,
33
- payload: p
42
+ payload: { documentId, annotations }
34
43
  });
35
- const selectAnnotation = (pageIndex, id) => ({
44
+ const selectAnnotation = (documentId, pageIndex, id) => ({
36
45
  type: SELECT_ANNOTATION,
37
- payload: { pageIndex, id }
46
+ payload: { documentId, pageIndex, id }
38
47
  });
39
- const deselectAnnotation = () => ({ type: DESELECT_ANNOTATION });
40
- const addColorPreset = (c) => ({
41
- type: ADD_COLOR_PRESET,
42
- payload: c
48
+ const deselectAnnotation = (documentId) => ({
49
+ type: DESELECT_ANNOTATION,
50
+ payload: { documentId }
51
+ });
52
+ const setActiveToolId = (documentId, toolId) => ({
53
+ type: SET_ACTIVE_TOOL_ID,
54
+ payload: { documentId, toolId }
55
+ });
56
+ const createAnnotation = (documentId, pageIndex, annotation) => ({
57
+ type: CREATE_ANNOTATION,
58
+ payload: { documentId, pageIndex, annotation }
43
59
  });
44
- const createAnnotation = (pageIndex, annotation) => ({ type: CREATE_ANNOTATION, payload: { pageIndex, annotation } });
45
- const patchAnnotation = (pageIndex, id, patch) => ({ type: PATCH_ANNOTATION, payload: { pageIndex, id, patch } });
46
- const deleteAnnotation = (pageIndex, id) => ({
60
+ const patchAnnotation = (documentId, pageIndex, id, patch) => ({
61
+ type: PATCH_ANNOTATION,
62
+ payload: { documentId, pageIndex, id, patch }
63
+ });
64
+ const deleteAnnotation = (documentId, pageIndex, id) => ({
47
65
  type: DELETE_ANNOTATION,
48
- payload: { pageIndex, id }
66
+ payload: { documentId, pageIndex, id }
67
+ });
68
+ const commitPendingChanges = (documentId) => ({
69
+ type: COMMIT_PENDING_CHANGES,
70
+ payload: { documentId }
49
71
  });
50
- const commitPendingChanges = () => ({ type: COMMIT_PENDING_CHANGES });
51
- const purgeAnnotation = (uid) => ({
72
+ const purgeAnnotation = (documentId, uid) => ({
52
73
  type: PURGE_ANNOTATION,
53
- payload: { uid }
74
+ payload: { documentId, uid }
54
75
  });
55
- const setActiveToolId = (id) => ({
56
- type: SET_ACTIVE_TOOL_ID,
57
- payload: id
76
+ const addColorPreset = (c) => ({
77
+ type: ADD_COLOR_PRESET,
78
+ payload: c
58
79
  });
59
80
  const setToolDefaults = (toolId, patch) => ({
60
81
  type: SET_TOOL_DEFAULTS,
61
82
  payload: { toolId, patch }
62
83
  });
63
- const addTool = (tool) => ({ type: ADD_TOOL, payload: tool });
84
+ const addTool = (tool) => ({
85
+ type: ADD_TOOL,
86
+ payload: tool
87
+ });
64
88
  function isInk(a) {
65
89
  return a.object.type === PdfAnnotationSubtype.INK;
66
90
  }
@@ -113,6 +137,7 @@ const getAnnotations = (s) => {
113
137
  return out;
114
138
  };
115
139
  const getSelectedAnnotation = (s) => s.selectedUid ? s.byUid[s.selectedUid] : null;
140
+ const getAnnotationByUid = (s, uid) => s.byUid[uid] ?? null;
116
141
  const getSelectedAnnotationByPageIndex = (s, pageIndex) => {
117
142
  if (!s.selectedUid) return null;
118
143
  const pageUids = s.pages[pageIndex] ?? [];
@@ -166,394 +191,942 @@ const getSidebarAnnotationsWithReplies = (s) => {
166
191
  }
167
192
  return out;
168
193
  };
169
- function useState(initialValue) {
170
- let value = initialValue;
171
- const getValue = () => value;
172
- const setValue = (newValue) => {
173
- value = newValue;
174
- };
175
- return [getValue, setValue];
176
- }
177
- const inkHandlerFactory = {
178
- annotationType: PdfAnnotationSubtype.INK,
179
- create(context) {
180
- const { onCommit, onPreview, getTool, pageSize } = context;
181
- const [getStrokes, setStrokes] = useState([]);
182
- const [getIsDrawing, setIsDrawing] = useState(false);
183
- const timerRef = { current: null };
184
- const clampToPage = (pos) => ({
185
- x: clamp(pos.x, 0, pageSize.width),
186
- y: clamp(pos.y, 0, pageSize.height)
187
- });
188
- const getDefaults = () => {
189
- const tool = getTool();
190
- if (!tool) return null;
191
- return {
192
- ...tool.defaults,
193
- strokeWidth: tool.defaults.strokeWidth ?? 1,
194
- color: tool.defaults.color ?? "#000000",
195
- opacity: tool.defaults.opacity ?? 1,
196
- flags: tool.defaults.flags ?? ["print"]
197
- };
198
- };
199
- const getPreview = () => {
200
- const strokes = getStrokes();
201
- if (strokes.length === 0 || strokes[0].points.length === 0) return null;
202
- const defaults = getDefaults();
203
- if (!defaults) return null;
204
- const allPoints = strokes.flatMap((s) => s.points);
205
- const bounds = expandRect(rectFromPoints(allPoints), defaults.strokeWidth / 2);
206
- return {
207
- type: PdfAnnotationSubtype.INK,
208
- bounds,
209
- data: {
210
- ...defaults,
211
- rect: bounds,
212
- inkList: strokes
213
- }
214
- };
215
- };
216
- return {
217
- onPointerDown: (pos, evt) => {
218
- var _a;
219
- const clampedPos = clampToPage(pos);
220
- setIsDrawing(true);
221
- if (timerRef.current) clearTimeout(timerRef.current);
222
- const newStrokes = [...getStrokes(), { points: [clampedPos] }];
223
- setStrokes(newStrokes);
224
- onPreview(getPreview());
225
- (_a = evt.setPointerCapture) == null ? void 0 : _a.call(evt);
226
- },
227
- onPointerMove: (pos) => {
228
- if (!getIsDrawing()) return;
229
- const strokes = getStrokes();
230
- if (strokes.length === 0) return;
231
- const clampedPos = clampToPage(pos);
232
- strokes[strokes.length - 1].points.push(clampedPos);
233
- setStrokes(strokes);
234
- onPreview(getPreview());
235
- },
236
- onPointerUp: (_, evt) => {
237
- var _a;
238
- setIsDrawing(false);
239
- (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
240
- if (timerRef.current) clearTimeout(timerRef.current);
241
- timerRef.current = setTimeout(() => {
242
- const strokes = getStrokes();
243
- if (strokes.length > 0 && strokes[0].points.length > 1) {
244
- const defaults = getDefaults();
245
- if (!defaults) return;
246
- const allPoints = strokes.flatMap((s) => s.points);
247
- const rect = expandRect(rectFromPoints(allPoints), defaults.strokeWidth / 2);
248
- onCommit({
249
- ...defaults,
250
- inkList: strokes,
251
- rect,
252
- type: PdfAnnotationSubtype.INK,
253
- pageIndex: context.pageIndex,
254
- id: uuidV4(),
255
- created: /* @__PURE__ */ new Date()
256
- });
257
- }
258
- setStrokes([]);
259
- onPreview(null);
260
- }, 800);
261
- },
262
- onPointerCancel: (_, evt) => {
263
- var _a;
264
- setStrokes([]);
265
- setIsDrawing(false);
266
- onPreview(null);
267
- if (timerRef.current) clearTimeout(timerRef.current);
268
- (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
269
- }
270
- };
271
- }
272
- };
273
- function useClickDetector({
274
- threshold = 5,
275
- getTool,
276
- onClickDetected
277
- }) {
278
- const [getStartPos, setStartPos] = useState(null);
279
- const [getHasMoved, setHasMoved] = useState(false);
280
- return {
281
- onStart: (pos) => {
282
- setStartPos(pos);
283
- setHasMoved(false);
194
+ const defaultTools = [
195
+ // Text Markup Tools
196
+ {
197
+ id: "highlight",
198
+ name: "Highlight",
199
+ matchScore: (a) => a.type === PdfAnnotationSubtype.HIGHLIGHT ? 1 : 0,
200
+ interaction: {
201
+ exclusive: false,
202
+ textSelection: true,
203
+ isDraggable: false,
204
+ isResizable: false
284
205
  },
285
- onMove: (pos) => {
286
- const start = getStartPos();
287
- if (!start || getHasMoved()) return;
288
- const distance = Math.sqrt(Math.pow(pos.x - start.x, 2) + Math.pow(pos.y - start.y, 2));
289
- if (distance > threshold) {
290
- setHasMoved(true);
291
- }
206
+ defaults: {
207
+ type: PdfAnnotationSubtype.HIGHLIGHT,
208
+ color: "#FFCD45",
209
+ opacity: 1,
210
+ blendMode: PdfBlendMode.Multiply
211
+ }
212
+ },
213
+ {
214
+ id: "underline",
215
+ name: "Underline",
216
+ matchScore: (a) => a.type === PdfAnnotationSubtype.UNDERLINE ? 1 : 0,
217
+ interaction: {
218
+ exclusive: false,
219
+ textSelection: true,
220
+ isDraggable: false,
221
+ isResizable: false
292
222
  },
293
- onEnd: (pos) => {
294
- var _a;
295
- const start = getStartPos();
296
- if (start && !getHasMoved()) {
297
- const tool = getTool();
298
- if (tool && "clickBehavior" in tool && ((_a = tool.clickBehavior) == null ? void 0 : _a.enabled)) {
299
- onClickDetected(pos, tool);
300
- }
301
- }
302
- setStartPos(null);
303
- setHasMoved(false);
223
+ defaults: {
224
+ type: PdfAnnotationSubtype.UNDERLINE,
225
+ color: "#E44234",
226
+ opacity: 1
227
+ }
228
+ },
229
+ {
230
+ id: "strikeout",
231
+ name: "Strikeout",
232
+ matchScore: (a) => a.type === PdfAnnotationSubtype.STRIKEOUT ? 1 : 0,
233
+ interaction: {
234
+ exclusive: false,
235
+ textSelection: true
304
236
  },
305
- hasMoved: getHasMoved,
306
- reset: () => {
307
- setStartPos(null);
308
- setHasMoved(false);
237
+ defaults: {
238
+ type: PdfAnnotationSubtype.STRIKEOUT,
239
+ color: "#E44234",
240
+ opacity: 1
309
241
  }
310
- };
311
- }
312
- const freeTextHandlerFactory = {
313
- annotationType: PdfAnnotationSubtype.FREETEXT,
314
- create(context) {
315
- const { onCommit, onPreview, getTool, pageSize, pageIndex } = context;
316
- const [getStart, setStart] = useState(null);
317
- const clampToPage = (pos) => ({
318
- x: clamp(pos.x, 0, pageSize.width),
319
- y: clamp(pos.y, 0, pageSize.height)
320
- });
321
- const getDefaults = () => {
322
- const tool = getTool();
323
- if (!tool) return null;
324
- return {
325
- ...tool.defaults,
326
- fontColor: tool.defaults.fontColor ?? "#000000",
327
- opacity: tool.defaults.opacity ?? 1,
328
- fontSize: tool.defaults.fontSize ?? 12,
329
- fontFamily: tool.defaults.fontFamily ?? PdfStandardFont.Helvetica,
330
- backgroundColor: tool.defaults.backgroundColor ?? "transparent",
331
- textAlign: tool.defaults.textAlign ?? PdfTextAlignment.Left,
332
- verticalAlign: tool.defaults.verticalAlign ?? PdfVerticalAlignment.Top,
333
- contents: tool.defaults.contents ?? "Insert text here",
334
- flags: tool.defaults.flags ?? ["print"]
335
- };
336
- };
337
- const clickDetector = useClickDetector({
338
- threshold: 5,
339
- getTool,
340
- onClickDetected: (pos, tool) => {
341
- const defaults = getDefaults();
342
- if (!defaults) return;
343
- const clickConfig = tool.clickBehavior;
344
- if (!(clickConfig == null ? void 0 : clickConfig.enabled)) return;
345
- const { width, height } = clickConfig.defaultSize;
346
- const halfWidth = width / 2;
347
- const halfHeight = height / 2;
348
- const x = clamp(pos.x - halfWidth, 0, pageSize.width - width);
349
- const y = clamp(pos.y - halfHeight, 0, pageSize.height - height);
350
- const rect = {
351
- origin: { x, y },
352
- size: { width, height }
353
- };
354
- const contents = clickConfig.defaultContent ?? defaults.contents;
355
- const anno = {
356
- ...defaults,
357
- contents,
358
- type: PdfAnnotationSubtype.FREETEXT,
359
- rect,
360
- pageIndex,
361
- id: uuidV4(),
362
- created: /* @__PURE__ */ new Date()
363
- };
364
- onCommit(anno);
365
- }
366
- });
367
- const getPreview = (current) => {
368
- const start = getStart();
369
- if (!start) return null;
370
- const defaults = getDefaults();
371
- if (!defaults) return null;
372
- const minX = Math.min(start.x, current.x);
373
- const minY = Math.min(start.y, current.y);
374
- const width = Math.abs(start.x - current.x);
375
- const height = Math.abs(start.y - current.y);
376
- const rect = {
377
- origin: { x: minX, y: minY },
378
- size: { width, height }
379
- };
380
- return {
381
- type: PdfAnnotationSubtype.FREETEXT,
382
- bounds: rect,
383
- data: {
384
- ...defaults,
385
- rect
386
- }
387
- };
388
- };
389
- return {
390
- onPointerDown: (pos, evt) => {
391
- var _a;
392
- const clampedPos = clampToPage(pos);
393
- setStart(clampedPos);
394
- clickDetector.onStart(clampedPos);
395
- onPreview(getPreview(clampedPos));
396
- (_a = evt.setPointerCapture) == null ? void 0 : _a.call(evt);
397
- },
398
- onPointerMove: (pos) => {
399
- const clampedPos = clampToPage(pos);
400
- clickDetector.onMove(clampedPos);
401
- if (getStart() && clickDetector.hasMoved()) {
402
- onPreview(getPreview(clampedPos));
403
- }
404
- },
405
- onPointerUp: (pos, evt) => {
406
- var _a;
407
- const start = getStart();
408
- if (!start) return;
409
- const defaults = getDefaults();
410
- if (!defaults) return;
411
- const clampedPos = clampToPage(pos);
412
- if (!clickDetector.hasMoved()) {
413
- clickDetector.onEnd(clampedPos);
414
- } else {
415
- const minX = Math.min(start.x, clampedPos.x);
416
- const minY = Math.min(start.y, clampedPos.y);
417
- const width = Math.abs(start.x - clampedPos.x);
418
- const height = Math.abs(start.y - clampedPos.y);
419
- const rect = {
420
- origin: { x: minX, y: minY },
421
- size: { width, height }
422
- };
423
- const anno = {
424
- ...defaults,
425
- type: PdfAnnotationSubtype.FREETEXT,
426
- rect,
427
- pageIndex: context.pageIndex,
428
- id: uuidV4(),
429
- created: /* @__PURE__ */ new Date()
430
- };
431
- onCommit(anno);
432
- }
433
- setStart(null);
434
- onPreview(null);
435
- clickDetector.reset();
436
- (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
437
- },
438
- onPointerLeave: (_, evt) => {
439
- var _a;
440
- setStart(null);
441
- onPreview(null);
442
- clickDetector.reset();
443
- (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
444
- },
445
- onPointerCancel: (_, evt) => {
446
- var _a;
447
- setStart(null);
448
- onPreview(null);
449
- clickDetector.reset();
450
- (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
451
- }
452
- };
453
- }
454
- };
455
- function createArrowHandler(isClosed) {
456
- const calculateGeometry = (sw) => {
457
- const len = sw * 9;
458
- const a = Math.PI / 6;
459
- return {
460
- x: -len * Math.cos(a),
461
- y: len * Math.sin(a)
462
- };
463
- };
464
- return {
465
- getSvgPath: (sw) => {
466
- const { x, y } = calculateGeometry(sw);
467
- return isClosed ? `M 0 0 L ${x} ${y} L ${x} ${-y} Z` : `M ${x} ${y} L 0 0 L ${x} ${-y}`;
468
- },
469
- getLocalPoints: (sw) => {
470
- const { x, y } = calculateGeometry(sw);
471
- return [
472
- { x: 0, y: 0 },
473
- { x, y },
474
- { x, y: -y }
475
- ];
476
- },
477
- getRotation: (segmentAngle) => segmentAngle,
478
- filled: isClosed
479
- };
480
- }
481
- function createLineHandler(lengthFactor, rotationFn) {
482
- const getHalfLength = (sw) => sw * lengthFactor / 2;
483
- return {
484
- getSvgPath: (sw) => {
485
- const l = getHalfLength(sw);
486
- return `M ${-l} 0 L ${l} 0`;
487
- },
488
- getLocalPoints: (sw) => {
489
- const l = getHalfLength(sw);
490
- return [
491
- { x: -l, y: 0 },
492
- { x: l, y: 0 }
493
- ];
494
- },
495
- getRotation: rotationFn,
496
- filled: false
497
- };
498
- }
499
- const OpenArrowHandler = createArrowHandler(false);
500
- const ClosedArrowHandler = createArrowHandler(true);
501
- const LINE_ENDING_HANDLERS = {
502
- [PdfAnnotationLineEnding.OpenArrow]: OpenArrowHandler,
503
- [PdfAnnotationLineEnding.ClosedArrow]: ClosedArrowHandler,
504
- [PdfAnnotationLineEnding.ROpenArrow]: {
505
- ...OpenArrowHandler,
506
- getRotation: (segmentAngle) => segmentAngle + Math.PI
507
242
  },
508
- [PdfAnnotationLineEnding.RClosedArrow]: {
509
- ...ClosedArrowHandler,
510
- getRotation: (segmentAngle) => segmentAngle + Math.PI
243
+ {
244
+ id: "squiggly",
245
+ name: "Squiggly",
246
+ matchScore: (a) => a.type === PdfAnnotationSubtype.SQUIGGLY ? 1 : 0,
247
+ interaction: {
248
+ exclusive: false,
249
+ textSelection: true,
250
+ isDraggable: false,
251
+ isResizable: false
252
+ },
253
+ defaults: {
254
+ type: PdfAnnotationSubtype.SQUIGGLY,
255
+ color: "#E44234",
256
+ opacity: 1
257
+ }
511
258
  },
512
- [PdfAnnotationLineEnding.Circle]: {
513
- getSvgPath: (sw) => {
514
- const r = sw * 5 / 2;
515
- return `M ${r} 0 A ${r} ${r} 0 1 1 ${-r} 0 A ${r} ${r} 0 1 1 ${r} 0`;
259
+ // Drawing Tools
260
+ {
261
+ id: "ink",
262
+ name: "Pen",
263
+ matchScore: (a) => a.type === PdfAnnotationSubtype.INK && a.intent !== "InkHighlight" ? 5 : 0,
264
+ interaction: {
265
+ exclusive: false,
266
+ cursor: "crosshair",
267
+ isDraggable: true,
268
+ isResizable: true,
269
+ lockAspectRatio: false
516
270
  },
517
- getLocalPoints: (sw) => {
518
- const r = sw * 5 / 2;
519
- return [
520
- { x: -r, y: -r },
521
- { x: r, y: r }
522
- ];
271
+ defaults: {
272
+ type: PdfAnnotationSubtype.INK,
273
+ color: "#E44234",
274
+ opacity: 1,
275
+ strokeWidth: 6
276
+ }
277
+ },
278
+ {
279
+ id: "inkHighlighter",
280
+ name: "Ink Highlighter",
281
+ matchScore: (a) => a.type === PdfAnnotationSubtype.INK && a.intent === "InkHighlight" ? 10 : 0,
282
+ interaction: {
283
+ exclusive: false,
284
+ cursor: "crosshair",
285
+ isDraggable: true,
286
+ isResizable: true,
287
+ lockAspectRatio: false
523
288
  },
524
- getRotation: () => 0,
525
- filled: true
289
+ defaults: {
290
+ type: PdfAnnotationSubtype.INK,
291
+ intent: "InkHighlight",
292
+ color: "#FFCD45",
293
+ opacity: 1,
294
+ strokeWidth: 14,
295
+ blendMode: PdfBlendMode.Multiply
296
+ }
526
297
  },
527
- [PdfAnnotationLineEnding.Square]: {
528
- getSvgPath: (sw) => {
529
- const h = sw * 6 / 2;
530
- return `M ${-h} ${-h} L ${h} ${-h} L ${h} ${h} L ${-h} ${h} Z`;
298
+ // Shape Tools
299
+ {
300
+ id: "circle",
301
+ name: "Circle",
302
+ matchScore: (a) => a.type === PdfAnnotationSubtype.CIRCLE ? 1 : 0,
303
+ interaction: {
304
+ exclusive: false,
305
+ cursor: "crosshair",
306
+ isDraggable: true,
307
+ isResizable: true,
308
+ lockAspectRatio: false
531
309
  },
532
- getLocalPoints: (sw) => {
533
- const h = sw * 6 / 2;
534
- return [
535
- { x: -h, y: -h },
536
- // TL
537
- { x: h, y: -h },
538
- // TR
539
- { x: h, y: h },
540
- // BR
541
- { x: -h, y: h }
542
- // BL
543
- ];
310
+ defaults: {
311
+ type: PdfAnnotationSubtype.CIRCLE,
312
+ color: "transparent",
313
+ opacity: 1,
314
+ strokeWidth: 6,
315
+ strokeColor: "#E44234",
316
+ strokeStyle: PdfAnnotationBorderStyle.SOLID
544
317
  },
545
- getRotation: (segmentAngle) => segmentAngle,
546
- // keep your new orientation
547
- filled: true
318
+ clickBehavior: {
319
+ enabled: true,
320
+ defaultSize: { width: 100, height: 100 }
321
+ }
548
322
  },
549
- [PdfAnnotationLineEnding.Diamond]: {
550
- getSvgPath: (sw) => {
551
- const h = sw * 6 / 2;
552
- return `M 0 ${-h} L ${h} 0 L 0 ${h} L ${-h} 0 Z`;
553
- },
554
- getLocalPoints: (sw) => {
555
- const h = sw * 6 / 2;
556
- return [
323
+ {
324
+ id: "square",
325
+ name: "Square",
326
+ matchScore: (a) => a.type === PdfAnnotationSubtype.SQUARE ? 1 : 0,
327
+ interaction: {
328
+ exclusive: false,
329
+ cursor: "crosshair",
330
+ isDraggable: true,
331
+ isResizable: true,
332
+ lockAspectRatio: false
333
+ },
334
+ defaults: {
335
+ type: PdfAnnotationSubtype.SQUARE,
336
+ color: "transparent",
337
+ opacity: 1,
338
+ strokeWidth: 6,
339
+ strokeColor: "#E44234",
340
+ strokeStyle: PdfAnnotationBorderStyle.SOLID
341
+ },
342
+ clickBehavior: {
343
+ enabled: true,
344
+ defaultSize: { width: 100, height: 100 }
345
+ }
346
+ },
347
+ {
348
+ id: "line",
349
+ name: "Line",
350
+ matchScore: (a) => a.type === PdfAnnotationSubtype.LINE && a.intent !== "LineArrow" ? 5 : 0,
351
+ interaction: {
352
+ exclusive: false,
353
+ cursor: "crosshair",
354
+ isDraggable: true,
355
+ isResizable: false,
356
+ lockAspectRatio: false
357
+ },
358
+ defaults: {
359
+ type: PdfAnnotationSubtype.LINE,
360
+ color: "transparent",
361
+ opacity: 1,
362
+ strokeWidth: 6,
363
+ strokeColor: "#E44234"
364
+ },
365
+ clickBehavior: {
366
+ enabled: true,
367
+ defaultLength: 100,
368
+ defaultAngle: 0
369
+ }
370
+ },
371
+ {
372
+ id: "lineArrow",
373
+ name: "Arrow",
374
+ matchScore: (a) => a.type === PdfAnnotationSubtype.LINE && a.intent === "LineArrow" ? 10 : 0,
375
+ interaction: {
376
+ exclusive: false,
377
+ cursor: "crosshair",
378
+ isDraggable: true,
379
+ isResizable: false,
380
+ lockAspectRatio: false
381
+ },
382
+ defaults: {
383
+ type: PdfAnnotationSubtype.LINE,
384
+ intent: "LineArrow",
385
+ color: "transparent",
386
+ opacity: 1,
387
+ strokeWidth: 6,
388
+ strokeColor: "#E44234",
389
+ lineEndings: {
390
+ start: PdfAnnotationLineEnding.None,
391
+ end: PdfAnnotationLineEnding.OpenArrow
392
+ }
393
+ },
394
+ clickBehavior: {
395
+ enabled: true,
396
+ defaultLength: 100,
397
+ defaultAngle: 0
398
+ }
399
+ },
400
+ {
401
+ id: "polyline",
402
+ name: "Polyline",
403
+ matchScore: (a) => a.type === PdfAnnotationSubtype.POLYLINE ? 1 : 0,
404
+ interaction: {
405
+ exclusive: false,
406
+ cursor: "crosshair",
407
+ isDraggable: true,
408
+ isResizable: false,
409
+ lockAspectRatio: false
410
+ },
411
+ defaults: {
412
+ type: PdfAnnotationSubtype.POLYLINE,
413
+ color: "transparent",
414
+ opacity: 1,
415
+ strokeWidth: 6,
416
+ strokeColor: "#E44234"
417
+ }
418
+ },
419
+ {
420
+ id: "polygon",
421
+ name: "Polygon",
422
+ matchScore: (a) => a.type === PdfAnnotationSubtype.POLYGON ? 1 : 0,
423
+ interaction: {
424
+ exclusive: false,
425
+ cursor: "crosshair",
426
+ isDraggable: true,
427
+ isResizable: false,
428
+ lockAspectRatio: false
429
+ },
430
+ defaults: {
431
+ type: PdfAnnotationSubtype.POLYGON,
432
+ color: "transparent",
433
+ opacity: 1,
434
+ strokeWidth: 6,
435
+ strokeColor: "#E44234"
436
+ }
437
+ },
438
+ // Text & Stamp
439
+ {
440
+ id: "freeText",
441
+ name: "Free Text",
442
+ matchScore: (a) => a.type === PdfAnnotationSubtype.FREETEXT ? 1 : 0,
443
+ interaction: {
444
+ exclusive: false,
445
+ cursor: "crosshair",
446
+ isDraggable: true,
447
+ isResizable: true,
448
+ lockAspectRatio: false
449
+ },
450
+ defaults: {
451
+ type: PdfAnnotationSubtype.FREETEXT,
452
+ contents: "Insert text",
453
+ fontSize: 14,
454
+ fontColor: "#E44234",
455
+ fontFamily: PdfStandardFont.Helvetica,
456
+ textAlign: PdfTextAlignment.Left,
457
+ verticalAlign: PdfVerticalAlignment.Top,
458
+ backgroundColor: "transparent",
459
+ opacity: 1
460
+ },
461
+ clickBehavior: {
462
+ enabled: true,
463
+ defaultSize: { width: 100, height: 20 },
464
+ defaultContent: "Insert text"
465
+ }
466
+ },
467
+ {
468
+ id: "stamp",
469
+ name: "Image",
470
+ matchScore: (a) => a.type === PdfAnnotationSubtype.STAMP ? 1 : 0,
471
+ interaction: {
472
+ exclusive: false,
473
+ cursor: "copy",
474
+ isDraggable: true,
475
+ isResizable: true,
476
+ lockAspectRatio: true
477
+ },
478
+ defaults: {
479
+ type: PdfAnnotationSubtype.STAMP
480
+ // No imageSrc by default, which tells the UI to open a file picker
481
+ }
482
+ }
483
+ ];
484
+ const DEFAULT_COLORS = [
485
+ "#E44234",
486
+ "#FF8D00",
487
+ "#FFCD45",
488
+ "#5CC96E",
489
+ "#25D2D1",
490
+ "#597CE2",
491
+ "#C544CE",
492
+ "#7D2E25",
493
+ "#000000",
494
+ "#FFFFFF"
495
+ ];
496
+ const initialDocumentState = () => ({
497
+ pages: {},
498
+ byUid: {},
499
+ selectedUid: null,
500
+ activeToolId: null,
501
+ hasPendingChanges: false
502
+ });
503
+ const patchAnno = (docState, uid, patch) => {
504
+ const prev = docState.byUid[uid];
505
+ if (!prev) return docState;
506
+ return {
507
+ ...docState,
508
+ byUid: {
509
+ ...docState.byUid,
510
+ [uid]: {
511
+ ...prev,
512
+ commitState: prev.commitState === "synced" ? "dirty" : prev.commitState,
513
+ object: { ...prev.object, ...patch }
514
+ }
515
+ },
516
+ hasPendingChanges: true
517
+ };
518
+ };
519
+ const initialState = (cfg) => {
520
+ const toolMap = /* @__PURE__ */ new Map();
521
+ defaultTools.forEach((t) => toolMap.set(t.id, t));
522
+ (cfg.tools || []).forEach((t) => toolMap.set(t.id, t));
523
+ return {
524
+ documents: {},
525
+ activeDocumentId: null,
526
+ // `Array.from(toolMap.values())` now correctly returns `AnnotationTool[]`, which matches the state's type.
527
+ tools: Array.from(toolMap.values()),
528
+ colorPresets: cfg.colorPresets ?? DEFAULT_COLORS
529
+ };
530
+ };
531
+ const reducer = (state, action) => {
532
+ switch (action.type) {
533
+ case INIT_ANNOTATION_STATE: {
534
+ const { documentId, state: docState } = action.payload;
535
+ return {
536
+ ...state,
537
+ documents: {
538
+ ...state.documents,
539
+ [documentId]: docState
540
+ },
541
+ // Set as active if no active document
542
+ activeDocumentId: state.activeDocumentId ?? documentId
543
+ };
544
+ }
545
+ case CLEANUP_ANNOTATION_STATE: {
546
+ const documentId = action.payload;
547
+ const { [documentId]: removed, ...remainingDocs } = state.documents;
548
+ return {
549
+ ...state,
550
+ documents: remainingDocs,
551
+ activeDocumentId: state.activeDocumentId === documentId ? null : state.activeDocumentId
552
+ };
553
+ }
554
+ case SET_ACTIVE_DOCUMENT: {
555
+ return {
556
+ ...state,
557
+ activeDocumentId: action.payload
558
+ };
559
+ }
560
+ case SET_ANNOTATIONS: {
561
+ const { documentId, annotations } = action.payload;
562
+ const docState = state.documents[documentId];
563
+ if (!docState) return state;
564
+ const newPages = {};
565
+ const newByUid = {};
566
+ for (const [pgStr, list] of Object.entries(annotations)) {
567
+ const pageIndex = Number(pgStr);
568
+ const oldUidsOnPage = docState.pages[pageIndex] || [];
569
+ for (const uid of oldUidsOnPage) {
570
+ delete newByUid[uid];
571
+ }
572
+ const newUidsOnPage = list.map((a) => {
573
+ const uid = a.id;
574
+ newByUid[uid] = { commitState: "synced", object: a };
575
+ return uid;
576
+ });
577
+ newPages[pageIndex] = newUidsOnPage;
578
+ }
579
+ return {
580
+ ...state,
581
+ documents: {
582
+ ...state.documents,
583
+ [documentId]: {
584
+ ...docState,
585
+ pages: newPages,
586
+ byUid: newByUid
587
+ }
588
+ }
589
+ };
590
+ }
591
+ case SELECT_ANNOTATION: {
592
+ const { documentId, id } = action.payload;
593
+ const docState = state.documents[documentId];
594
+ if (!docState) return state;
595
+ return {
596
+ ...state,
597
+ documents: {
598
+ ...state.documents,
599
+ [documentId]: { ...docState, selectedUid: id }
600
+ }
601
+ };
602
+ }
603
+ case DESELECT_ANNOTATION: {
604
+ const { documentId } = action.payload;
605
+ const docState = state.documents[documentId];
606
+ if (!docState) return state;
607
+ return {
608
+ ...state,
609
+ documents: {
610
+ ...state.documents,
611
+ [documentId]: { ...docState, selectedUid: null }
612
+ }
613
+ };
614
+ }
615
+ case SET_ACTIVE_TOOL_ID: {
616
+ const { documentId, toolId } = action.payload;
617
+ const docState = state.documents[documentId];
618
+ if (!docState) return state;
619
+ return {
620
+ ...state,
621
+ documents: {
622
+ ...state.documents,
623
+ [documentId]: { ...docState, activeToolId: toolId }
624
+ }
625
+ };
626
+ }
627
+ case CREATE_ANNOTATION: {
628
+ const { documentId, pageIndex, annotation } = action.payload;
629
+ const docState = state.documents[documentId];
630
+ if (!docState) return state;
631
+ const uid = annotation.id;
632
+ return {
633
+ ...state,
634
+ documents: {
635
+ ...state.documents,
636
+ [documentId]: {
637
+ ...docState,
638
+ pages: {
639
+ ...docState.pages,
640
+ [pageIndex]: [...docState.pages[pageIndex] ?? [], uid]
641
+ },
642
+ byUid: {
643
+ ...docState.byUid,
644
+ [uid]: { commitState: "new", object: annotation }
645
+ },
646
+ hasPendingChanges: true
647
+ }
648
+ }
649
+ };
650
+ }
651
+ case DELETE_ANNOTATION: {
652
+ const { documentId, pageIndex, id: uid } = action.payload;
653
+ const docState = state.documents[documentId];
654
+ if (!docState || !docState.byUid[uid]) return state;
655
+ return {
656
+ ...state,
657
+ documents: {
658
+ ...state.documents,
659
+ [documentId]: {
660
+ ...docState,
661
+ pages: {
662
+ ...docState.pages,
663
+ [pageIndex]: (docState.pages[pageIndex] ?? []).filter((u) => u !== uid)
664
+ },
665
+ byUid: {
666
+ ...docState.byUid,
667
+ [uid]: { ...docState.byUid[uid], commitState: "deleted" }
668
+ },
669
+ hasPendingChanges: true
670
+ }
671
+ }
672
+ };
673
+ }
674
+ case PATCH_ANNOTATION: {
675
+ const { documentId, id, patch } = action.payload;
676
+ const docState = state.documents[documentId];
677
+ if (!docState) return state;
678
+ return {
679
+ ...state,
680
+ documents: {
681
+ ...state.documents,
682
+ [documentId]: patchAnno(docState, id, patch)
683
+ }
684
+ };
685
+ }
686
+ case COMMIT_PENDING_CHANGES: {
687
+ const { documentId } = action.payload;
688
+ const docState = state.documents[documentId];
689
+ if (!docState) return state;
690
+ const cleaned = {};
691
+ for (const [uid, ta] of Object.entries(docState.byUid)) {
692
+ cleaned[uid] = {
693
+ ...ta,
694
+ commitState: ta.commitState === "dirty" || ta.commitState === "new" ? "synced" : ta.commitState
695
+ };
696
+ }
697
+ return {
698
+ ...state,
699
+ documents: {
700
+ ...state.documents,
701
+ [documentId]: { ...docState, byUid: cleaned, hasPendingChanges: false }
702
+ }
703
+ };
704
+ }
705
+ case PURGE_ANNOTATION: {
706
+ const { documentId, uid } = action.payload;
707
+ const docState = state.documents[documentId];
708
+ if (!docState) return state;
709
+ const { [uid]: _gone, ...rest } = docState.byUid;
710
+ return {
711
+ ...state,
712
+ documents: {
713
+ ...state.documents,
714
+ [documentId]: { ...docState, byUid: rest }
715
+ }
716
+ };
717
+ }
718
+ // Global actions
719
+ case ADD_TOOL: {
720
+ const toolMap = new Map(state.tools.map((t) => [t.id, t]));
721
+ toolMap.set(action.payload.id, action.payload);
722
+ return { ...state, tools: Array.from(toolMap.values()) };
723
+ }
724
+ case SET_TOOL_DEFAULTS: {
725
+ const { toolId, patch } = action.payload;
726
+ return {
727
+ ...state,
728
+ tools: state.tools.map((tool) => {
729
+ if (tool.id === toolId) {
730
+ return { ...tool, defaults: { ...tool.defaults, ...patch } };
731
+ }
732
+ return tool;
733
+ })
734
+ };
735
+ }
736
+ case ADD_COLOR_PRESET:
737
+ return state.colorPresets.includes(action.payload) ? state : { ...state, colorPresets: [...state.colorPresets, action.payload] };
738
+ default:
739
+ return state;
740
+ }
741
+ };
742
+ function useState(initialValue) {
743
+ let value = initialValue;
744
+ const getValue = () => value;
745
+ const setValue = (newValue) => {
746
+ value = newValue;
747
+ };
748
+ return [getValue, setValue];
749
+ }
750
+ const inkHandlerFactory = {
751
+ annotationType: PdfAnnotationSubtype.INK,
752
+ create(context) {
753
+ const { onCommit, onPreview, getTool, pageSize } = context;
754
+ const [getStrokes, setStrokes] = useState([]);
755
+ const [getIsDrawing, setIsDrawing] = useState(false);
756
+ const timerRef = { current: null };
757
+ const clampToPage = (pos) => ({
758
+ x: clamp(pos.x, 0, pageSize.width),
759
+ y: clamp(pos.y, 0, pageSize.height)
760
+ });
761
+ const getDefaults = () => {
762
+ const tool = getTool();
763
+ if (!tool) return null;
764
+ return {
765
+ ...tool.defaults,
766
+ strokeWidth: tool.defaults.strokeWidth ?? 1,
767
+ color: tool.defaults.color ?? "#000000",
768
+ opacity: tool.defaults.opacity ?? 1,
769
+ flags: tool.defaults.flags ?? ["print"]
770
+ };
771
+ };
772
+ const getPreview = () => {
773
+ const strokes = getStrokes();
774
+ if (strokes.length === 0 || strokes[0].points.length === 0) return null;
775
+ const defaults = getDefaults();
776
+ if (!defaults) return null;
777
+ const allPoints = strokes.flatMap((s) => s.points);
778
+ const bounds = expandRect(rectFromPoints(allPoints), defaults.strokeWidth / 2);
779
+ return {
780
+ type: PdfAnnotationSubtype.INK,
781
+ bounds,
782
+ data: {
783
+ ...defaults,
784
+ rect: bounds,
785
+ inkList: strokes
786
+ }
787
+ };
788
+ };
789
+ return {
790
+ onPointerDown: (pos, evt) => {
791
+ var _a;
792
+ const clampedPos = clampToPage(pos);
793
+ setIsDrawing(true);
794
+ if (timerRef.current) clearTimeout(timerRef.current);
795
+ const newStrokes = [...getStrokes(), { points: [clampedPos] }];
796
+ setStrokes(newStrokes);
797
+ onPreview(getPreview());
798
+ (_a = evt.setPointerCapture) == null ? void 0 : _a.call(evt);
799
+ },
800
+ onPointerMove: (pos) => {
801
+ if (!getIsDrawing()) return;
802
+ const strokes = getStrokes();
803
+ if (strokes.length === 0) return;
804
+ const clampedPos = clampToPage(pos);
805
+ strokes[strokes.length - 1].points.push(clampedPos);
806
+ setStrokes(strokes);
807
+ onPreview(getPreview());
808
+ },
809
+ onPointerUp: (_, evt) => {
810
+ var _a;
811
+ setIsDrawing(false);
812
+ (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
813
+ if (timerRef.current) clearTimeout(timerRef.current);
814
+ timerRef.current = setTimeout(() => {
815
+ const strokes = getStrokes();
816
+ if (strokes.length > 0 && strokes[0].points.length > 1) {
817
+ const defaults = getDefaults();
818
+ if (!defaults) return;
819
+ const allPoints = strokes.flatMap((s) => s.points);
820
+ const rect = expandRect(rectFromPoints(allPoints), defaults.strokeWidth / 2);
821
+ onCommit({
822
+ ...defaults,
823
+ inkList: strokes,
824
+ rect,
825
+ type: PdfAnnotationSubtype.INK,
826
+ pageIndex: context.pageIndex,
827
+ id: uuidV4(),
828
+ created: /* @__PURE__ */ new Date()
829
+ });
830
+ }
831
+ setStrokes([]);
832
+ onPreview(null);
833
+ }, 800);
834
+ },
835
+ onPointerCancel: (_, evt) => {
836
+ var _a;
837
+ setStrokes([]);
838
+ setIsDrawing(false);
839
+ onPreview(null);
840
+ if (timerRef.current) clearTimeout(timerRef.current);
841
+ (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
842
+ }
843
+ };
844
+ }
845
+ };
846
+ function useClickDetector({
847
+ threshold = 5,
848
+ getTool,
849
+ onClickDetected
850
+ }) {
851
+ const [getStartPos, setStartPos] = useState(null);
852
+ const [getHasMoved, setHasMoved] = useState(false);
853
+ return {
854
+ onStart: (pos) => {
855
+ setStartPos(pos);
856
+ setHasMoved(false);
857
+ },
858
+ onMove: (pos) => {
859
+ const start = getStartPos();
860
+ if (!start || getHasMoved()) return;
861
+ const distance = Math.sqrt(Math.pow(pos.x - start.x, 2) + Math.pow(pos.y - start.y, 2));
862
+ if (distance > threshold) {
863
+ setHasMoved(true);
864
+ }
865
+ },
866
+ onEnd: (pos) => {
867
+ var _a;
868
+ const start = getStartPos();
869
+ if (start && !getHasMoved()) {
870
+ const tool = getTool();
871
+ if (tool && "clickBehavior" in tool && ((_a = tool.clickBehavior) == null ? void 0 : _a.enabled)) {
872
+ onClickDetected(pos, tool);
873
+ }
874
+ }
875
+ setStartPos(null);
876
+ setHasMoved(false);
877
+ },
878
+ hasMoved: getHasMoved,
879
+ reset: () => {
880
+ setStartPos(null);
881
+ setHasMoved(false);
882
+ }
883
+ };
884
+ }
885
+ const freeTextHandlerFactory = {
886
+ annotationType: PdfAnnotationSubtype.FREETEXT,
887
+ create(context) {
888
+ const { onCommit, onPreview, getTool, pageSize, pageIndex } = context;
889
+ const [getStart, setStart] = useState(null);
890
+ const clampToPage = (pos) => ({
891
+ x: clamp(pos.x, 0, pageSize.width),
892
+ y: clamp(pos.y, 0, pageSize.height)
893
+ });
894
+ const getDefaults = () => {
895
+ const tool = getTool();
896
+ if (!tool) return null;
897
+ return {
898
+ ...tool.defaults,
899
+ fontColor: tool.defaults.fontColor ?? "#000000",
900
+ opacity: tool.defaults.opacity ?? 1,
901
+ fontSize: tool.defaults.fontSize ?? 12,
902
+ fontFamily: tool.defaults.fontFamily ?? PdfStandardFont.Helvetica,
903
+ backgroundColor: tool.defaults.backgroundColor ?? "transparent",
904
+ textAlign: tool.defaults.textAlign ?? PdfTextAlignment.Left,
905
+ verticalAlign: tool.defaults.verticalAlign ?? PdfVerticalAlignment.Top,
906
+ contents: tool.defaults.contents ?? "Insert text here",
907
+ flags: tool.defaults.flags ?? ["print"]
908
+ };
909
+ };
910
+ const clickDetector = useClickDetector({
911
+ threshold: 5,
912
+ getTool,
913
+ onClickDetected: (pos, tool) => {
914
+ const defaults = getDefaults();
915
+ if (!defaults) return;
916
+ const clickConfig = tool.clickBehavior;
917
+ if (!(clickConfig == null ? void 0 : clickConfig.enabled)) return;
918
+ const { width, height } = clickConfig.defaultSize;
919
+ const halfWidth = width / 2;
920
+ const halfHeight = height / 2;
921
+ const x = clamp(pos.x - halfWidth, 0, pageSize.width - width);
922
+ const y = clamp(pos.y - halfHeight, 0, pageSize.height - height);
923
+ const rect = {
924
+ origin: { x, y },
925
+ size: { width, height }
926
+ };
927
+ const contents = clickConfig.defaultContent ?? defaults.contents;
928
+ const anno = {
929
+ ...defaults,
930
+ contents,
931
+ type: PdfAnnotationSubtype.FREETEXT,
932
+ rect,
933
+ pageIndex,
934
+ id: uuidV4(),
935
+ created: /* @__PURE__ */ new Date()
936
+ };
937
+ onCommit(anno);
938
+ }
939
+ });
940
+ const getPreview = (current) => {
941
+ const start = getStart();
942
+ if (!start) return null;
943
+ const defaults = getDefaults();
944
+ if (!defaults) return null;
945
+ const minX = Math.min(start.x, current.x);
946
+ const minY = Math.min(start.y, current.y);
947
+ const width = Math.abs(start.x - current.x);
948
+ const height = Math.abs(start.y - current.y);
949
+ const rect = {
950
+ origin: { x: minX, y: minY },
951
+ size: { width, height }
952
+ };
953
+ return {
954
+ type: PdfAnnotationSubtype.FREETEXT,
955
+ bounds: rect,
956
+ data: {
957
+ ...defaults,
958
+ rect
959
+ }
960
+ };
961
+ };
962
+ return {
963
+ onPointerDown: (pos, evt) => {
964
+ var _a;
965
+ const clampedPos = clampToPage(pos);
966
+ setStart(clampedPos);
967
+ clickDetector.onStart(clampedPos);
968
+ onPreview(getPreview(clampedPos));
969
+ (_a = evt.setPointerCapture) == null ? void 0 : _a.call(evt);
970
+ },
971
+ onPointerMove: (pos) => {
972
+ const clampedPos = clampToPage(pos);
973
+ clickDetector.onMove(clampedPos);
974
+ if (getStart() && clickDetector.hasMoved()) {
975
+ onPreview(getPreview(clampedPos));
976
+ }
977
+ },
978
+ onPointerUp: (pos, evt) => {
979
+ var _a;
980
+ const start = getStart();
981
+ if (!start) return;
982
+ const defaults = getDefaults();
983
+ if (!defaults) return;
984
+ const clampedPos = clampToPage(pos);
985
+ if (!clickDetector.hasMoved()) {
986
+ clickDetector.onEnd(clampedPos);
987
+ } else {
988
+ const minX = Math.min(start.x, clampedPos.x);
989
+ const minY = Math.min(start.y, clampedPos.y);
990
+ const width = Math.abs(start.x - clampedPos.x);
991
+ const height = Math.abs(start.y - clampedPos.y);
992
+ const rect = {
993
+ origin: { x: minX, y: minY },
994
+ size: { width, height }
995
+ };
996
+ const anno = {
997
+ ...defaults,
998
+ type: PdfAnnotationSubtype.FREETEXT,
999
+ rect,
1000
+ pageIndex: context.pageIndex,
1001
+ id: uuidV4(),
1002
+ created: /* @__PURE__ */ new Date()
1003
+ };
1004
+ onCommit(anno);
1005
+ }
1006
+ setStart(null);
1007
+ onPreview(null);
1008
+ clickDetector.reset();
1009
+ (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
1010
+ },
1011
+ onPointerLeave: (_, evt) => {
1012
+ var _a;
1013
+ setStart(null);
1014
+ onPreview(null);
1015
+ clickDetector.reset();
1016
+ (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
1017
+ },
1018
+ onPointerCancel: (_, evt) => {
1019
+ var _a;
1020
+ setStart(null);
1021
+ onPreview(null);
1022
+ clickDetector.reset();
1023
+ (_a = evt.releasePointerCapture) == null ? void 0 : _a.call(evt);
1024
+ }
1025
+ };
1026
+ }
1027
+ };
1028
+ function createArrowHandler(isClosed) {
1029
+ const calculateGeometry = (sw) => {
1030
+ const len = sw * 9;
1031
+ const a = Math.PI / 6;
1032
+ return {
1033
+ x: -len * Math.cos(a),
1034
+ y: len * Math.sin(a)
1035
+ };
1036
+ };
1037
+ return {
1038
+ getSvgPath: (sw) => {
1039
+ const { x, y } = calculateGeometry(sw);
1040
+ return isClosed ? `M 0 0 L ${x} ${y} L ${x} ${-y} Z` : `M ${x} ${y} L 0 0 L ${x} ${-y}`;
1041
+ },
1042
+ getLocalPoints: (sw) => {
1043
+ const { x, y } = calculateGeometry(sw);
1044
+ return [
1045
+ { x: 0, y: 0 },
1046
+ { x, y },
1047
+ { x, y: -y }
1048
+ ];
1049
+ },
1050
+ getRotation: (segmentAngle) => segmentAngle,
1051
+ filled: isClosed
1052
+ };
1053
+ }
1054
+ function createLineHandler(lengthFactor, rotationFn) {
1055
+ const getHalfLength = (sw) => sw * lengthFactor / 2;
1056
+ return {
1057
+ getSvgPath: (sw) => {
1058
+ const l = getHalfLength(sw);
1059
+ return `M ${-l} 0 L ${l} 0`;
1060
+ },
1061
+ getLocalPoints: (sw) => {
1062
+ const l = getHalfLength(sw);
1063
+ return [
1064
+ { x: -l, y: 0 },
1065
+ { x: l, y: 0 }
1066
+ ];
1067
+ },
1068
+ getRotation: rotationFn,
1069
+ filled: false
1070
+ };
1071
+ }
1072
+ const OpenArrowHandler = createArrowHandler(false);
1073
+ const ClosedArrowHandler = createArrowHandler(true);
1074
+ const LINE_ENDING_HANDLERS = {
1075
+ [PdfAnnotationLineEnding.OpenArrow]: OpenArrowHandler,
1076
+ [PdfAnnotationLineEnding.ClosedArrow]: ClosedArrowHandler,
1077
+ [PdfAnnotationLineEnding.ROpenArrow]: {
1078
+ ...OpenArrowHandler,
1079
+ getRotation: (segmentAngle) => segmentAngle + Math.PI
1080
+ },
1081
+ [PdfAnnotationLineEnding.RClosedArrow]: {
1082
+ ...ClosedArrowHandler,
1083
+ getRotation: (segmentAngle) => segmentAngle + Math.PI
1084
+ },
1085
+ [PdfAnnotationLineEnding.Circle]: {
1086
+ getSvgPath: (sw) => {
1087
+ const r = sw * 5 / 2;
1088
+ return `M ${r} 0 A ${r} ${r} 0 1 1 ${-r} 0 A ${r} ${r} 0 1 1 ${r} 0`;
1089
+ },
1090
+ getLocalPoints: (sw) => {
1091
+ const r = sw * 5 / 2;
1092
+ return [
1093
+ { x: -r, y: -r },
1094
+ { x: r, y: r }
1095
+ ];
1096
+ },
1097
+ getRotation: () => 0,
1098
+ filled: true
1099
+ },
1100
+ [PdfAnnotationLineEnding.Square]: {
1101
+ getSvgPath: (sw) => {
1102
+ const h = sw * 6 / 2;
1103
+ return `M ${-h} ${-h} L ${h} ${-h} L ${h} ${h} L ${-h} ${h} Z`;
1104
+ },
1105
+ getLocalPoints: (sw) => {
1106
+ const h = sw * 6 / 2;
1107
+ return [
1108
+ { x: -h, y: -h },
1109
+ // TL
1110
+ { x: h, y: -h },
1111
+ // TR
1112
+ { x: h, y: h },
1113
+ // BR
1114
+ { x: -h, y: h }
1115
+ // BL
1116
+ ];
1117
+ },
1118
+ getRotation: (segmentAngle) => segmentAngle,
1119
+ // keep your new orientation
1120
+ filled: true
1121
+ },
1122
+ [PdfAnnotationLineEnding.Diamond]: {
1123
+ getSvgPath: (sw) => {
1124
+ const h = sw * 6 / 2;
1125
+ return `M 0 ${-h} L ${h} 0 L 0 ${h} L ${-h} 0 Z`;
1126
+ },
1127
+ getLocalPoints: (sw) => {
1128
+ const h = sw * 6 / 2;
1129
+ return [
557
1130
  { x: 0, y: -h },
558
1131
  { x: h, y: 0 },
559
1132
  { x: 0, y: h },
@@ -1434,162 +2007,9 @@ const patchLine = (orig, ctx) => {
1434
2007
  if (ctx.changes.linePoints) {
1435
2008
  const { start, end } = ctx.changes.linePoints;
1436
2009
  const rect = lineRectWithEndings([start, end], orig.strokeWidth, orig.lineEndings);
1437
- return {
1438
- rect,
1439
- linePoints: { start, end }
1440
- };
1441
- }
1442
- return ctx.changes;
1443
- case "move":
1444
- if (ctx.changes.rect) {
1445
- const dx = ctx.changes.rect.origin.x - orig.rect.origin.x;
1446
- const dy = ctx.changes.rect.origin.y - orig.rect.origin.y;
1447
- return {
1448
- rect: ctx.changes.rect,
1449
- linePoints: {
1450
- start: { x: orig.linePoints.start.x + dx, y: orig.linePoints.start.y + dy },
1451
- end: { x: orig.linePoints.end.x + dx, y: orig.linePoints.end.y + dy }
1452
- }
1453
- };
1454
- }
1455
- return ctx.changes;
1456
- case "resize":
1457
- if (ctx.changes.rect) {
1458
- const oldRect = orig.rect;
1459
- const newRect = ctx.changes.rect;
1460
- let scaleX = newRect.size.width / oldRect.size.width;
1461
- let scaleY = newRect.size.height / oldRect.size.height;
1462
- const minSize = 10;
1463
- if (newRect.size.width < minSize || newRect.size.height < minSize) {
1464
- scaleX = Math.max(scaleX, minSize / oldRect.size.width);
1465
- scaleY = Math.max(scaleY, minSize / oldRect.size.height);
1466
- ctx.changes.rect = {
1467
- origin: newRect.origin,
1468
- size: {
1469
- width: oldRect.size.width * scaleX,
1470
- height: oldRect.size.height * scaleY
1471
- }
1472
- };
1473
- }
1474
- if ((_a = ctx.metadata) == null ? void 0 : _a.maintainAspectRatio) {
1475
- const minScale = Math.min(scaleX, scaleY);
1476
- scaleX = minScale;
1477
- scaleY = minScale;
1478
- ctx.changes.rect.size = {
1479
- width: oldRect.size.width * minScale,
1480
- height: oldRect.size.height * minScale
1481
- };
1482
- }
1483
- const newLinePoints = {
1484
- start: {
1485
- x: ctx.changes.rect.origin.x + (orig.linePoints.start.x - oldRect.origin.x) * scaleX,
1486
- y: ctx.changes.rect.origin.y + (orig.linePoints.start.y - oldRect.origin.y) * scaleY
1487
- },
1488
- end: {
1489
- x: ctx.changes.rect.origin.x + (orig.linePoints.end.x - oldRect.origin.x) * scaleX,
1490
- y: ctx.changes.rect.origin.y + (orig.linePoints.end.y - oldRect.origin.y) * scaleY
1491
- }
1492
- };
1493
- return {
1494
- rect: ctx.changes.rect,
1495
- linePoints: newLinePoints
1496
- };
1497
- }
1498
- return ctx.changes;
1499
- case "property-update":
1500
- if (ctx.changes.strokeWidth || ctx.changes.lineEndings) {
1501
- const merged = { ...orig, ...ctx.changes };
1502
- const rect = lineRectWithEndings(
1503
- [merged.linePoints.start, merged.linePoints.end],
1504
- merged.strokeWidth,
1505
- merged.lineEndings
1506
- );
1507
- return { ...ctx.changes, rect };
1508
- }
1509
- return ctx.changes;
1510
- default:
1511
- return ctx.changes;
1512
- }
1513
- };
1514
- const patchPolyline = (orig, ctx) => {
1515
- var _a;
1516
- switch (ctx.type) {
1517
- case "vertex-edit":
1518
- if (ctx.changes.vertices && ctx.changes.vertices.length) {
1519
- return {
1520
- rect: lineRectWithEndings(ctx.changes.vertices, orig.strokeWidth, orig.lineEndings),
1521
- vertices: ctx.changes.vertices
1522
- };
1523
- }
1524
- return ctx.changes;
1525
- case "move":
1526
- if (ctx.changes.rect) {
1527
- const dx = ctx.changes.rect.origin.x - orig.rect.origin.x;
1528
- const dy = ctx.changes.rect.origin.y - orig.rect.origin.y;
1529
- const moved = orig.vertices.map((p) => ({ x: p.x + dx, y: p.y + dy }));
1530
- return {
1531
- rect: ctx.changes.rect,
1532
- vertices: moved
1533
- };
1534
- }
1535
- return ctx.changes;
1536
- case "resize":
1537
- if (ctx.changes.rect) {
1538
- const oldRect = orig.rect;
1539
- const newRect = ctx.changes.rect;
1540
- let scaleX = newRect.size.width / oldRect.size.width;
1541
- let scaleY = newRect.size.height / oldRect.size.height;
1542
- const minSize = 10;
1543
- if (newRect.size.width < minSize || newRect.size.height < minSize) {
1544
- scaleX = Math.max(scaleX, minSize / oldRect.size.width);
1545
- scaleY = Math.max(scaleY, minSize / oldRect.size.height);
1546
- ctx.changes.rect = {
1547
- origin: newRect.origin,
1548
- size: {
1549
- width: oldRect.size.width * scaleX,
1550
- height: oldRect.size.height * scaleY
1551
- }
1552
- };
1553
- }
1554
- if ((_a = ctx.metadata) == null ? void 0 : _a.maintainAspectRatio) {
1555
- const minScale = Math.min(scaleX, scaleY);
1556
- scaleX = minScale;
1557
- scaleY = minScale;
1558
- ctx.changes.rect.size = {
1559
- width: oldRect.size.width * minScale,
1560
- height: oldRect.size.height * minScale
1561
- };
1562
- }
1563
- const scaledVertices = orig.vertices.map((vertex) => ({
1564
- x: ctx.changes.rect.origin.x + (vertex.x - oldRect.origin.x) * scaleX,
1565
- y: ctx.changes.rect.origin.y + (vertex.y - oldRect.origin.y) * scaleY
1566
- }));
1567
- return {
1568
- rect: ctx.changes.rect,
1569
- vertices: scaledVertices
1570
- };
1571
- }
1572
- return ctx.changes;
1573
- case "property-update":
1574
- if (ctx.changes.strokeWidth !== void 0 || ctx.changes.lineEndings !== void 0) {
1575
- const merged = { ...orig, ...ctx.changes };
1576
- const rect = lineRectWithEndings(merged.vertices, merged.strokeWidth, merged.lineEndings);
1577
- return { ...ctx.changes, rect };
1578
- }
1579
- return ctx.changes;
1580
- default:
1581
- return ctx.changes;
1582
- }
1583
- };
1584
- const patchPolygon = (orig, ctx) => {
1585
- var _a;
1586
- switch (ctx.type) {
1587
- case "vertex-edit":
1588
- if (ctx.changes.vertices && ctx.changes.vertices.length) {
1589
- const pad = orig.strokeWidth / 2;
1590
- return {
1591
- rect: expandRect(rectFromPoints(ctx.changes.vertices), pad),
1592
- vertices: ctx.changes.vertices
2010
+ return {
2011
+ rect,
2012
+ linePoints: { start, end }
1593
2013
  };
1594
2014
  }
1595
2015
  return ctx.changes;
@@ -1597,10 +2017,12 @@ const patchPolygon = (orig, ctx) => {
1597
2017
  if (ctx.changes.rect) {
1598
2018
  const dx = ctx.changes.rect.origin.x - orig.rect.origin.x;
1599
2019
  const dy = ctx.changes.rect.origin.y - orig.rect.origin.y;
1600
- const moved = orig.vertices.map((p) => ({ x: p.x + dx, y: p.y + dy }));
1601
2020
  return {
1602
2021
  rect: ctx.changes.rect,
1603
- vertices: moved
2022
+ linePoints: {
2023
+ start: { x: orig.linePoints.start.x + dx, y: orig.linePoints.start.y + dy },
2024
+ end: { x: orig.linePoints.end.x + dx, y: orig.linePoints.end.y + dy }
2025
+ }
1604
2026
  };
1605
2027
  }
1606
2028
  return ctx.changes;
@@ -1631,21 +2053,30 @@ const patchPolygon = (orig, ctx) => {
1631
2053
  height: oldRect.size.height * minScale
1632
2054
  };
1633
2055
  }
1634
- const scaledVertices = orig.vertices.map((vertex) => ({
1635
- x: ctx.changes.rect.origin.x + (vertex.x - oldRect.origin.x) * scaleX,
1636
- y: ctx.changes.rect.origin.y + (vertex.y - oldRect.origin.y) * scaleY
1637
- }));
2056
+ const newLinePoints = {
2057
+ start: {
2058
+ x: ctx.changes.rect.origin.x + (orig.linePoints.start.x - oldRect.origin.x) * scaleX,
2059
+ y: ctx.changes.rect.origin.y + (orig.linePoints.start.y - oldRect.origin.y) * scaleY
2060
+ },
2061
+ end: {
2062
+ x: ctx.changes.rect.origin.x + (orig.linePoints.end.x - oldRect.origin.x) * scaleX,
2063
+ y: ctx.changes.rect.origin.y + (orig.linePoints.end.y - oldRect.origin.y) * scaleY
2064
+ }
2065
+ };
1638
2066
  return {
1639
2067
  rect: ctx.changes.rect,
1640
- vertices: scaledVertices
2068
+ linePoints: newLinePoints
1641
2069
  };
1642
2070
  }
1643
2071
  return ctx.changes;
1644
2072
  case "property-update":
1645
- if (ctx.changes.strokeWidth !== void 0) {
2073
+ if (ctx.changes.strokeWidth || ctx.changes.lineEndings) {
1646
2074
  const merged = { ...orig, ...ctx.changes };
1647
- const pad = merged.strokeWidth / 2;
1648
- const rect = expandRect(rectFromPoints(merged.vertices), pad);
2075
+ const rect = lineRectWithEndings(
2076
+ [merged.linePoints.start, merged.linePoints.end],
2077
+ merged.strokeWidth,
2078
+ merged.lineEndings
2079
+ );
1649
2080
  return { ...ctx.changes, rect };
1650
2081
  }
1651
2082
  return ctx.changes;
@@ -1653,921 +2084,804 @@ const patchPolygon = (orig, ctx) => {
1653
2084
  return ctx.changes;
1654
2085
  }
1655
2086
  };
1656
- const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
1657
- constructor(id, registry, config) {
1658
- var _a, _b, _c;
1659
- super(id, registry);
1660
- this.ANNOTATION_HISTORY_TOPIC = "annotations";
1661
- this.state$ = createBehaviorEmitter();
1662
- this.pendingContexts = /* @__PURE__ */ new Map();
1663
- this.handlerFactories = /* @__PURE__ */ new Map();
1664
- this.activeTool$ = createBehaviorEmitter(null);
1665
- this.events$ = createBehaviorEmitter();
1666
- this.patchRegistry = new PatchRegistry();
1667
- this.isInitialLoadComplete = false;
1668
- this.importQueue = [];
1669
- this.config = config;
1670
- this.selection = ((_a = registry.getPlugin("selection")) == null ? void 0 : _a.provides()) ?? null;
1671
- this.history = ((_b = registry.getPlugin("history")) == null ? void 0 : _b.provides()) ?? null;
1672
- this.interactionManager = ((_c = registry.getPlugin("interaction-manager")) == null ? void 0 : _c.provides()) ?? null;
1673
- this.coreStore.onAction(SET_DOCUMENT, (_, state) => {
1674
- const doc = state.core.document;
1675
- this.isInitialLoadComplete = false;
1676
- if (doc) this.getAllAnnotations(doc);
1677
- });
1678
- this.registerHandlerFactories();
1679
- this.registerBuiltInPatches();
1680
- }
1681
- registerHandlerFactories() {
1682
- this.handlerFactories.set(PdfAnnotationSubtype.CIRCLE, circleHandlerFactory);
1683
- this.handlerFactories.set(PdfAnnotationSubtype.SQUARE, squareHandlerFactory);
1684
- this.handlerFactories.set(PdfAnnotationSubtype.STAMP, stampHandlerFactory);
1685
- this.handlerFactories.set(PdfAnnotationSubtype.POLYGON, polygonHandlerFactory);
1686
- this.handlerFactories.set(PdfAnnotationSubtype.POLYLINE, polylineHandlerFactory);
1687
- this.handlerFactories.set(PdfAnnotationSubtype.LINE, lineHandlerFactory);
1688
- this.handlerFactories.set(PdfAnnotationSubtype.INK, inkHandlerFactory);
1689
- this.handlerFactories.set(PdfAnnotationSubtype.FREETEXT, freeTextHandlerFactory);
1690
- }
1691
- registerBuiltInPatches() {
1692
- this.patchRegistry.register(PdfAnnotationSubtype.INK, patchInk);
1693
- this.patchRegistry.register(PdfAnnotationSubtype.LINE, patchLine);
1694
- this.patchRegistry.register(PdfAnnotationSubtype.POLYLINE, patchPolyline);
1695
- this.patchRegistry.register(PdfAnnotationSubtype.POLYGON, patchPolygon);
1696
- }
1697
- async initialize() {
1698
- var _a, _b, _c;
1699
- this.state.tools.forEach((tool) => this.registerInteractionForTool(tool));
1700
- (_a = this.history) == null ? void 0 : _a.onHistoryChange((topic) => {
1701
- if (topic === this.ANNOTATION_HISTORY_TOPIC && this.config.autoCommit !== false) {
1702
- this.commit();
1703
- }
1704
- });
1705
- (_b = this.interactionManager) == null ? void 0 : _b.onModeChange((s) => {
1706
- var _a2;
1707
- const newToolId = ((_a2 = this.state.tools.find((t) => (t.interaction.mode ?? t.id) === s.activeMode)) == null ? void 0 : _a2.id) ?? null;
1708
- if (newToolId !== this.state.activeToolId) {
1709
- this.dispatch(setActiveToolId(newToolId));
1710
- }
1711
- });
1712
- (_c = this.selection) == null ? void 0 : _c.onEndSelection(() => {
1713
- var _a2, _b2, _c2;
1714
- const activeTool = this.getActiveTool();
1715
- if (!activeTool || !activeTool.interaction.textSelection) return;
1716
- const formattedSelection = (_a2 = this.selection) == null ? void 0 : _a2.getFormattedSelection();
1717
- const selectionText = (_b2 = this.selection) == null ? void 0 : _b2.getSelectedText();
1718
- if (!formattedSelection || !selectionText) return;
1719
- for (const selection of formattedSelection) {
1720
- selectionText.wait((text) => {
1721
- const annotationId = uuidV4();
1722
- this.createAnnotation(selection.pageIndex, {
1723
- ...activeTool.defaults,
1724
- rect: selection.rect,
1725
- segmentRects: selection.segmentRects,
1726
- pageIndex: selection.pageIndex,
1727
- created: /* @__PURE__ */ new Date(),
1728
- id: annotationId,
1729
- custom: {
1730
- text: text.join("\n")
1731
- }
1732
- });
1733
- if (this.getToolBehavior(activeTool, "deactivateToolAfterCreate")) {
1734
- this.setActiveTool(null);
1735
- }
1736
- if (this.getToolBehavior(activeTool, "selectAfterCreate")) {
1737
- this.selectAnnotation(selection.pageIndex, annotationId);
1738
- }
1739
- }, ignore);
1740
- }
1741
- (_c2 = this.selection) == null ? void 0 : _c2.clear();
1742
- });
1743
- }
1744
- registerInteractionForTool(tool) {
1745
- var _a, _b;
1746
- (_a = this.interactionManager) == null ? void 0 : _a.registerMode({
1747
- id: tool.interaction.mode ?? tool.id,
1748
- scope: "page",
1749
- exclusive: tool.interaction.exclusive,
1750
- cursor: tool.interaction.cursor
1751
- });
1752
- if (tool.interaction.textSelection) {
1753
- (_b = this.selection) == null ? void 0 : _b.enableForMode(tool.interaction.mode ?? tool.id);
1754
- }
1755
- }
1756
- buildCapability() {
1757
- return {
1758
- getPageAnnotations: (options) => this.getPageAnnotations(options),
1759
- getSelectedAnnotation: () => getSelectedAnnotation(this.state),
1760
- selectAnnotation: (pageIndex, id) => this.selectAnnotation(pageIndex, id),
1761
- deselectAnnotation: () => this.dispatch(deselectAnnotation()),
1762
- getActiveTool: () => this.getActiveTool(),
1763
- setActiveTool: (toolId) => this.setActiveTool(toolId),
1764
- getTools: () => this.state.tools,
1765
- getTool: (toolId) => this.getTool(toolId),
1766
- addTool: (tool) => {
1767
- this.dispatch(addTool(tool));
1768
- this.registerInteractionForTool(tool);
1769
- },
1770
- transformAnnotation: (annotation, options) => this.transformAnnotation(annotation, options),
1771
- registerPatchFunction: (type, patchFn) => this.registerPatchFunction(type, patchFn),
1772
- findToolForAnnotation: (anno) => this.findToolForAnnotation(anno),
1773
- setToolDefaults: (toolId, patch) => this.dispatch(setToolDefaults(toolId, patch)),
1774
- getColorPresets: () => [...this.state.colorPresets],
1775
- addColorPreset: (color) => this.dispatch(addColorPreset(color)),
1776
- importAnnotations: (items) => this.importAnnotations(items),
1777
- createAnnotation: (pageIndex, anno, ctx) => this.createAnnotation(pageIndex, anno, ctx),
1778
- updateAnnotation: (pageIndex, id, patch) => this.updateAnnotation(pageIndex, id, patch),
1779
- deleteAnnotation: (pageIndex, id) => this.deleteAnnotation(pageIndex, id),
1780
- renderAnnotation: (options) => this.renderAnnotation(options),
1781
- onStateChange: this.state$.on,
1782
- onActiveToolChange: this.activeTool$.on,
1783
- onAnnotationEvent: this.events$.on,
1784
- commit: () => this.commit()
1785
- };
1786
- }
1787
- onStoreUpdated(prev, next) {
1788
- this.state$.emit(next);
1789
- if (prev.activeToolId !== next.activeToolId || prev.tools !== next.tools) {
1790
- this.activeTool$.emit(this.getActiveTool());
1791
- }
1792
- }
1793
- registerPatchFunction(type, patchFn) {
1794
- this.patchRegistry.register(type, patchFn);
1795
- }
1796
- transformAnnotation(annotation, options) {
1797
- const context = {
1798
- type: options.type,
1799
- changes: options.changes,
1800
- metadata: options.metadata
1801
- };
1802
- return this.patchRegistry.transform(annotation, context);
1803
- }
1804
- registerPageHandlers(pageIndex, scale, callbacks) {
1805
- var _a;
1806
- const page = (_a = this.coreState.core.document) == null ? void 0 : _a.pages[pageIndex];
1807
- if (!page) return () => {
1808
- };
1809
- if (!this.interactionManager) return () => {
1810
- };
1811
- const unregisterFns = [];
1812
- for (const tool of this.state.tools) {
1813
- if (!tool.defaults.type) continue;
1814
- const factory = this.handlerFactories.get(tool.defaults.type);
1815
- if (!factory) continue;
1816
- const context = {
1817
- pageIndex,
1818
- pageSize: page.size,
1819
- scale,
1820
- services: callbacks.services,
1821
- // Pass through services
1822
- onPreview: (state) => callbacks.onPreview(tool.id, state),
1823
- onCommit: (annotation, ctx) => {
1824
- this.createAnnotation(pageIndex, annotation, ctx);
1825
- if (this.getToolBehavior(tool, "deactivateToolAfterCreate")) {
1826
- this.setActiveTool(null);
1827
- }
1828
- if (this.getToolBehavior(tool, "selectAfterCreate")) {
1829
- this.selectAnnotation(pageIndex, annotation.id);
1830
- }
1831
- },
1832
- getTool: () => this.state.tools.find((t) => t.id === tool.id)
1833
- };
1834
- const unregister = this.interactionManager.registerHandlers({
1835
- modeId: tool.interaction.mode ?? tool.id,
1836
- handlers: factory.create(context),
1837
- pageIndex
1838
- });
1839
- unregisterFns.push(unregister);
1840
- }
1841
- return () => unregisterFns.forEach((fn) => fn());
1842
- }
1843
- getAllAnnotations(doc) {
1844
- const task = this.engine.getAllAnnotations(doc);
1845
- task.wait((annotations) => {
1846
- this.dispatch(setAnnotations(annotations));
1847
- this.isInitialLoadComplete = true;
1848
- if (this.importQueue.length > 0) {
1849
- this.processImportQueue();
2087
+ const patchPolyline = (orig, ctx) => {
2088
+ var _a;
2089
+ switch (ctx.type) {
2090
+ case "vertex-edit":
2091
+ if (ctx.changes.vertices && ctx.changes.vertices.length) {
2092
+ return {
2093
+ rect: lineRectWithEndings(ctx.changes.vertices, orig.strokeWidth, orig.lineEndings),
2094
+ vertices: ctx.changes.vertices
2095
+ };
1850
2096
  }
1851
- this.events$.emit({
1852
- type: "loaded",
1853
- total: Object.values(annotations).reduce(
1854
- (sum, pageAnnotations) => sum + pageAnnotations.length,
1855
- 0
1856
- )
1857
- });
1858
- }, ignore);
1859
- }
1860
- getPageAnnotations(options) {
1861
- const { pageIndex } = options;
1862
- const doc = this.coreState.core.document;
1863
- if (!doc) {
1864
- return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
1865
- }
1866
- const page = doc.pages.find((p) => p.index === pageIndex);
1867
- if (!page) {
1868
- return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Page not found" });
1869
- }
1870
- return this.engine.getPageAnnotations(doc, page);
1871
- }
1872
- renderAnnotation({ pageIndex, annotation, options }) {
1873
- const coreState = this.coreState.core;
1874
- if (!coreState.document) {
1875
- return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
1876
- }
1877
- const page = coreState.document.pages.find((page2) => page2.index === pageIndex);
1878
- if (!page) {
1879
- return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Page not found" });
1880
- }
1881
- return this.engine.renderPageAnnotation(coreState.document, page, annotation, options);
1882
- }
1883
- importAnnotations(items) {
1884
- if (!this.isInitialLoadComplete) {
1885
- this.importQueue.push(...items);
1886
- return;
1887
- }
1888
- this.processImportItems(items);
1889
- }
1890
- processImportQueue() {
1891
- if (this.importQueue.length === 0) return;
1892
- const items = [...this.importQueue];
1893
- this.importQueue = [];
1894
- this.processImportItems(items);
1895
- }
1896
- processImportItems(items) {
1897
- for (const item of items) {
1898
- const { annotation, ctx } = item;
1899
- const pageIndex = annotation.pageIndex;
1900
- const id = annotation.id;
1901
- this.dispatch(createAnnotation(pageIndex, annotation));
1902
- if (ctx) this.pendingContexts.set(id, ctx);
1903
- }
1904
- if (this.config.autoCommit !== false) this.commit();
1905
- }
1906
- createAnnotation(pageIndex, annotation, ctx) {
1907
- const id = annotation.id;
1908
- const newAnnotation = {
1909
- ...annotation,
1910
- author: annotation.author ?? this.config.annotationAuthor
1911
- };
1912
- const execute = () => {
1913
- this.dispatch(createAnnotation(pageIndex, newAnnotation));
1914
- if (ctx) this.pendingContexts.set(id, ctx);
1915
- this.events$.emit({
1916
- type: "create",
1917
- annotation: newAnnotation,
1918
- pageIndex,
1919
- ctx,
1920
- committed: false
1921
- });
1922
- };
1923
- if (!this.history) {
1924
- execute();
1925
- if (this.config.autoCommit) this.commit();
1926
- return;
1927
- }
1928
- const command = {
1929
- execute,
1930
- undo: () => {
1931
- this.pendingContexts.delete(id);
1932
- this.dispatch(deselectAnnotation());
1933
- this.dispatch(deleteAnnotation(pageIndex, id));
1934
- this.events$.emit({
1935
- type: "delete",
1936
- annotation: newAnnotation,
1937
- pageIndex,
1938
- committed: false
1939
- });
2097
+ return ctx.changes;
2098
+ case "move":
2099
+ if (ctx.changes.rect) {
2100
+ const dx = ctx.changes.rect.origin.x - orig.rect.origin.x;
2101
+ const dy = ctx.changes.rect.origin.y - orig.rect.origin.y;
2102
+ const moved = orig.vertices.map((p) => ({ x: p.x + dx, y: p.y + dy }));
2103
+ return {
2104
+ rect: ctx.changes.rect,
2105
+ vertices: moved
2106
+ };
1940
2107
  }
1941
- };
1942
- this.history.register(command, this.ANNOTATION_HISTORY_TOPIC);
1943
- }
1944
- buildPatch(original, patch) {
1945
- if ("rect" in patch) return patch;
1946
- return this.transformAnnotation(original, {
1947
- type: "property-update",
1948
- changes: patch
1949
- });
1950
- }
1951
- updateAnnotation(pageIndex, id, patch) {
1952
- const originalObject = this.state.byUid[id].object;
1953
- const finalPatch = this.buildPatch(originalObject, {
1954
- ...patch,
1955
- author: patch.author ?? this.config.annotationAuthor
1956
- });
1957
- const execute = () => {
1958
- this.dispatch(patchAnnotation(pageIndex, id, finalPatch));
1959
- this.events$.emit({
1960
- type: "update",
1961
- annotation: originalObject,
1962
- pageIndex,
1963
- patch: finalPatch,
1964
- committed: false
1965
- });
1966
- };
1967
- if (!this.history) {
1968
- execute();
1969
- if (this.config.autoCommit !== false) {
1970
- this.commit();
2108
+ return ctx.changes;
2109
+ case "resize":
2110
+ if (ctx.changes.rect) {
2111
+ const oldRect = orig.rect;
2112
+ const newRect = ctx.changes.rect;
2113
+ let scaleX = newRect.size.width / oldRect.size.width;
2114
+ let scaleY = newRect.size.height / oldRect.size.height;
2115
+ const minSize = 10;
2116
+ if (newRect.size.width < minSize || newRect.size.height < minSize) {
2117
+ scaleX = Math.max(scaleX, minSize / oldRect.size.width);
2118
+ scaleY = Math.max(scaleY, minSize / oldRect.size.height);
2119
+ ctx.changes.rect = {
2120
+ origin: newRect.origin,
2121
+ size: {
2122
+ width: oldRect.size.width * scaleX,
2123
+ height: oldRect.size.height * scaleY
2124
+ }
2125
+ };
2126
+ }
2127
+ if ((_a = ctx.metadata) == null ? void 0 : _a.maintainAspectRatio) {
2128
+ const minScale = Math.min(scaleX, scaleY);
2129
+ scaleX = minScale;
2130
+ scaleY = minScale;
2131
+ ctx.changes.rect.size = {
2132
+ width: oldRect.size.width * minScale,
2133
+ height: oldRect.size.height * minScale
2134
+ };
2135
+ }
2136
+ const scaledVertices = orig.vertices.map((vertex) => ({
2137
+ x: ctx.changes.rect.origin.x + (vertex.x - oldRect.origin.x) * scaleX,
2138
+ y: ctx.changes.rect.origin.y + (vertex.y - oldRect.origin.y) * scaleY
2139
+ }));
2140
+ return {
2141
+ rect: ctx.changes.rect,
2142
+ vertices: scaledVertices
2143
+ };
1971
2144
  }
1972
- return;
1973
- }
1974
- const originalPatch = Object.fromEntries(
1975
- Object.keys(patch).map((key) => [key, originalObject[key]])
1976
- );
1977
- const command = {
1978
- execute,
1979
- undo: () => {
1980
- this.dispatch(patchAnnotation(pageIndex, id, originalPatch));
1981
- this.events$.emit({
1982
- type: "update",
1983
- annotation: originalObject,
1984
- pageIndex,
1985
- patch: originalPatch,
1986
- committed: false
1987
- });
2145
+ return ctx.changes;
2146
+ case "property-update":
2147
+ if (ctx.changes.strokeWidth !== void 0 || ctx.changes.lineEndings !== void 0) {
2148
+ const merged = { ...orig, ...ctx.changes };
2149
+ const rect = lineRectWithEndings(merged.vertices, merged.strokeWidth, merged.lineEndings);
2150
+ return { ...ctx.changes, rect };
1988
2151
  }
1989
- };
1990
- this.history.register(command, this.ANNOTATION_HISTORY_TOPIC);
2152
+ return ctx.changes;
2153
+ default:
2154
+ return ctx.changes;
1991
2155
  }
1992
- deleteAnnotation(pageIndex, id) {
1993
- var _a;
1994
- const originalAnnotation = (_a = this.state.byUid[id]) == null ? void 0 : _a.object;
1995
- if (!originalAnnotation) return;
1996
- const execute = () => {
1997
- this.dispatch(deselectAnnotation());
1998
- this.dispatch(deleteAnnotation(pageIndex, id));
1999
- this.events$.emit({
2000
- type: "delete",
2001
- annotation: originalAnnotation,
2002
- pageIndex,
2003
- committed: false
2004
- });
2005
- };
2006
- if (!this.history) {
2007
- execute();
2008
- if (this.config.autoCommit !== false) this.commit();
2009
- return;
2010
- }
2011
- const command = {
2012
- execute,
2013
- undo: () => {
2014
- this.dispatch(createAnnotation(pageIndex, originalAnnotation));
2015
- this.events$.emit({
2016
- type: "create",
2017
- annotation: originalAnnotation,
2018
- pageIndex,
2019
- committed: false
2020
- });
2156
+ };
2157
+ const patchPolygon = (orig, ctx) => {
2158
+ var _a;
2159
+ switch (ctx.type) {
2160
+ case "vertex-edit":
2161
+ if (ctx.changes.vertices && ctx.changes.vertices.length) {
2162
+ const pad = orig.strokeWidth / 2;
2163
+ return {
2164
+ rect: expandRect(rectFromPoints(ctx.changes.vertices), pad),
2165
+ vertices: ctx.changes.vertices
2166
+ };
2021
2167
  }
2022
- };
2023
- this.history.register(command, this.ANNOTATION_HISTORY_TOPIC);
2024
- }
2025
- selectAnnotation(pageIndex, id) {
2026
- this.dispatch(selectAnnotation(pageIndex, id));
2027
- }
2028
- getActiveTool() {
2029
- if (!this.state.activeToolId) return null;
2030
- return this.state.tools.find((t) => t.id === this.state.activeToolId) ?? null;
2031
- }
2032
- setActiveTool(toolId) {
2033
- var _a, _b;
2034
- if (toolId === this.state.activeToolId) return;
2035
- const tool = this.state.tools.find((t) => t.id === toolId);
2036
- if (tool) {
2037
- (_a = this.interactionManager) == null ? void 0 : _a.activate(tool.interaction.mode ?? tool.id);
2038
- } else {
2039
- (_b = this.interactionManager) == null ? void 0 : _b.activateDefaultMode();
2040
- }
2041
- }
2042
- getTool(toolId) {
2043
- return this.state.tools.find((t) => t.id === toolId);
2044
- }
2045
- findToolForAnnotation(annotation) {
2046
- let bestTool = null;
2047
- let bestScore = 0;
2048
- for (const tool of this.state.tools) {
2049
- const score = tool.matchScore(annotation);
2050
- if (score > bestScore) {
2051
- bestScore = score;
2052
- bestTool = tool;
2168
+ return ctx.changes;
2169
+ case "move":
2170
+ if (ctx.changes.rect) {
2171
+ const dx = ctx.changes.rect.origin.x - orig.rect.origin.x;
2172
+ const dy = ctx.changes.rect.origin.y - orig.rect.origin.y;
2173
+ const moved = orig.vertices.map((p) => ({ x: p.x + dx, y: p.y + dy }));
2174
+ return {
2175
+ rect: ctx.changes.rect,
2176
+ vertices: moved
2177
+ };
2053
2178
  }
2054
- }
2055
- return bestTool;
2056
- }
2057
- commit() {
2058
- const task = new Task();
2059
- if (!this.state.hasPendingChanges) return PdfTaskHelper.resolve(true);
2060
- const doc = this.coreState.core.document;
2061
- if (!doc)
2062
- return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
2063
- const creations = [];
2064
- const updates = [];
2065
- const deletions = [];
2066
- for (const [uid, ta] of Object.entries(this.state.byUid)) {
2067
- if (ta.commitState === "synced") continue;
2068
- const page = doc.pages.find((p) => p.index === ta.object.pageIndex);
2069
- if (!page) continue;
2070
- switch (ta.commitState) {
2071
- case "new":
2072
- const ctx = this.pendingContexts.get(ta.object.id);
2073
- const task2 = this.engine.createPageAnnotation(doc, page, ta.object, ctx);
2074
- task2.wait(() => {
2075
- this.events$.emit({
2076
- type: "create",
2077
- annotation: ta.object,
2078
- pageIndex: ta.object.pageIndex,
2079
- ctx,
2080
- committed: true
2081
- });
2082
- this.pendingContexts.delete(ta.object.id);
2083
- }, ignore);
2084
- creations.push(task2);
2085
- break;
2086
- case "dirty":
2087
- const updateTask = this.engine.updatePageAnnotation(doc, page, ta.object);
2088
- updateTask.wait(() => {
2089
- this.events$.emit({
2090
- type: "update",
2091
- annotation: ta.object,
2092
- pageIndex: ta.object.pageIndex,
2093
- patch: ta.object,
2094
- committed: true
2095
- });
2096
- }, ignore);
2097
- updates.push(updateTask);
2098
- break;
2099
- case "deleted":
2100
- deletions.push({ ta, uid });
2101
- break;
2179
+ return ctx.changes;
2180
+ case "resize":
2181
+ if (ctx.changes.rect) {
2182
+ const oldRect = orig.rect;
2183
+ const newRect = ctx.changes.rect;
2184
+ let scaleX = newRect.size.width / oldRect.size.width;
2185
+ let scaleY = newRect.size.height / oldRect.size.height;
2186
+ const minSize = 10;
2187
+ if (newRect.size.width < minSize || newRect.size.height < minSize) {
2188
+ scaleX = Math.max(scaleX, minSize / oldRect.size.width);
2189
+ scaleY = Math.max(scaleY, minSize / oldRect.size.height);
2190
+ ctx.changes.rect = {
2191
+ origin: newRect.origin,
2192
+ size: {
2193
+ width: oldRect.size.width * scaleX,
2194
+ height: oldRect.size.height * scaleY
2195
+ }
2196
+ };
2197
+ }
2198
+ if ((_a = ctx.metadata) == null ? void 0 : _a.maintainAspectRatio) {
2199
+ const minScale = Math.min(scaleX, scaleY);
2200
+ scaleX = minScale;
2201
+ scaleY = minScale;
2202
+ ctx.changes.rect.size = {
2203
+ width: oldRect.size.width * minScale,
2204
+ height: oldRect.size.height * minScale
2205
+ };
2206
+ }
2207
+ const scaledVertices = orig.vertices.map((vertex) => ({
2208
+ x: ctx.changes.rect.origin.x + (vertex.x - oldRect.origin.x) * scaleX,
2209
+ y: ctx.changes.rect.origin.y + (vertex.y - oldRect.origin.y) * scaleY
2210
+ }));
2211
+ return {
2212
+ rect: ctx.changes.rect,
2213
+ vertices: scaledVertices
2214
+ };
2102
2215
  }
2103
- }
2104
- const deletionTasks = [];
2105
- for (const { ta, uid } of deletions) {
2106
- const page = doc.pages.find((p) => p.index === ta.object.pageIndex);
2107
- if (ta.commitState === "deleted" && ta.object.id) {
2108
- const task2 = new Task();
2109
- const removeTask = this.engine.removePageAnnotation(doc, page, ta.object);
2110
- removeTask.wait(() => {
2111
- this.dispatch(purgeAnnotation(uid));
2112
- this.events$.emit({
2113
- type: "delete",
2114
- annotation: ta.object,
2115
- pageIndex: ta.object.pageIndex,
2116
- committed: true
2117
- });
2118
- task2.resolve(true);
2119
- }, task2.fail);
2120
- deletionTasks.push(task2);
2121
- } else {
2122
- this.dispatch(purgeAnnotation(uid));
2216
+ return ctx.changes;
2217
+ case "property-update":
2218
+ if (ctx.changes.strokeWidth !== void 0) {
2219
+ const merged = { ...orig, ...ctx.changes };
2220
+ const pad = merged.strokeWidth / 2;
2221
+ const rect = expandRect(rectFromPoints(merged.vertices), pad);
2222
+ return { ...ctx.changes, rect };
2123
2223
  }
2124
- }
2125
- const allWriteTasks = [...creations, ...updates, ...deletionTasks];
2126
- Task.allSettled(allWriteTasks).wait(() => {
2127
- this.dispatch(commitPendingChanges());
2128
- task.resolve(true);
2129
- }, task.fail);
2130
- return task;
2131
- }
2132
- /**
2133
- * Gets the effective behavior setting for a tool, checking tool-specific config first,
2134
- * then falling back to plugin config.
2135
- */
2136
- getToolBehavior(tool, setting) {
2137
- var _a;
2138
- if (((_a = tool.behavior) == null ? void 0 : _a[setting]) !== void 0) {
2139
- return tool.behavior[setting];
2140
- }
2141
- return this.config[setting] !== false;
2224
+ return ctx.changes;
2225
+ default:
2226
+ return ctx.changes;
2142
2227
  }
2143
2228
  };
2144
- _AnnotationPlugin.id = "annotation";
2145
- let AnnotationPlugin = _AnnotationPlugin;
2146
- const defaultTools = [
2147
- // Text Markup Tools
2148
- {
2149
- id: "highlight",
2150
- name: "Highlight",
2151
- matchScore: (a) => a.type === PdfAnnotationSubtype.HIGHLIGHT ? 1 : 0,
2152
- interaction: {
2153
- exclusive: false,
2154
- textSelection: true,
2155
- isDraggable: false,
2156
- isResizable: false
2157
- },
2158
- defaults: {
2159
- type: PdfAnnotationSubtype.HIGHLIGHT,
2160
- color: "#FFCD45",
2161
- opacity: 1,
2162
- blendMode: PdfBlendMode.Multiply
2163
- }
2164
- },
2165
- {
2166
- id: "underline",
2167
- name: "Underline",
2168
- matchScore: (a) => a.type === PdfAnnotationSubtype.UNDERLINE ? 1 : 0,
2169
- interaction: {
2170
- exclusive: false,
2171
- textSelection: true,
2172
- isDraggable: false,
2173
- isResizable: false
2174
- },
2175
- defaults: {
2176
- type: PdfAnnotationSubtype.UNDERLINE,
2177
- color: "#E44234",
2178
- opacity: 1
2179
- }
2180
- },
2181
- {
2182
- id: "strikeout",
2183
- name: "Strikeout",
2184
- matchScore: (a) => a.type === PdfAnnotationSubtype.STRIKEOUT ? 1 : 0,
2185
- interaction: {
2186
- exclusive: false,
2187
- textSelection: true
2188
- },
2189
- defaults: {
2190
- type: PdfAnnotationSubtype.STRIKEOUT,
2191
- color: "#E44234",
2192
- opacity: 1
2193
- }
2194
- },
2195
- {
2196
- id: "squiggly",
2197
- name: "Squiggly",
2198
- matchScore: (a) => a.type === PdfAnnotationSubtype.SQUIGGLY ? 1 : 0,
2199
- interaction: {
2200
- exclusive: false,
2201
- textSelection: true,
2202
- isDraggable: false,
2203
- isResizable: false
2204
- },
2205
- defaults: {
2206
- type: PdfAnnotationSubtype.SQUIGGLY,
2207
- color: "#E44234",
2208
- opacity: 1
2229
+ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
2230
+ constructor(id, registry, config) {
2231
+ var _a, _b, _c;
2232
+ super(id, registry);
2233
+ this.ANNOTATION_HISTORY_TOPIC = "annotations";
2234
+ this.state$ = createBehaviorEmitter();
2235
+ this.pendingContexts = /* @__PURE__ */ new Map();
2236
+ this.isInitialLoadComplete = /* @__PURE__ */ new Map();
2237
+ this.importQueue = /* @__PURE__ */ new Map();
2238
+ this.handlerFactories = /* @__PURE__ */ new Map();
2239
+ this.activeTool$ = createBehaviorEmitter();
2240
+ this.events$ = createBehaviorEmitter();
2241
+ this.toolsChange$ = createBehaviorEmitter();
2242
+ this.patchRegistry = new PatchRegistry();
2243
+ this.config = config;
2244
+ this.selection = ((_a = registry.getPlugin("selection")) == null ? void 0 : _a.provides()) ?? null;
2245
+ this.history = ((_b = registry.getPlugin("history")) == null ? void 0 : _b.provides()) ?? null;
2246
+ this.interactionManager = ((_c = registry.getPlugin("interaction-manager")) == null ? void 0 : _c.provides()) ?? null;
2247
+ this.registerHandlerFactories();
2248
+ this.registerBuiltInPatches();
2249
+ }
2250
+ // ─────────────────────────────────────────────────────────
2251
+ // Document Lifecycle (from BasePlugin)
2252
+ // ─────────────────────────────────────────────────────────
2253
+ onDocumentLoadingStarted(documentId) {
2254
+ this.dispatch(initAnnotationState(documentId, initialDocumentState()));
2255
+ this.pendingContexts.set(documentId, /* @__PURE__ */ new Map());
2256
+ this.isInitialLoadComplete.set(documentId, false);
2257
+ this.importQueue.set(documentId, []);
2258
+ this.logger.debug(
2259
+ "AnnotationPlugin",
2260
+ "DocumentOpened",
2261
+ `Initialized annotation state for document: ${documentId}`
2262
+ );
2263
+ }
2264
+ onDocumentLoaded(documentId) {
2265
+ const docState = this.getCoreDocument(documentId);
2266
+ if (docState == null ? void 0 : docState.document) {
2267
+ this.getAllAnnotations(documentId, docState.document);
2209
2268
  }
2210
- },
2211
- // Drawing Tools
2212
- {
2213
- id: "ink",
2214
- name: "Pen",
2215
- matchScore: (a) => a.type === PdfAnnotationSubtype.INK && a.intent !== "InkHighlight" ? 5 : 0,
2216
- interaction: {
2217
- exclusive: false,
2218
- cursor: "crosshair",
2219
- isDraggable: true,
2220
- isResizable: true,
2221
- lockAspectRatio: false
2222
- },
2223
- defaults: {
2224
- type: PdfAnnotationSubtype.INK,
2225
- color: "#E44234",
2226
- opacity: 1,
2227
- strokeWidth: 6
2269
+ if (this.selection) {
2270
+ for (const tool of this.state.tools) {
2271
+ if (tool.interaction.textSelection) {
2272
+ this.selection.enableForMode(tool.interaction.mode ?? tool.id);
2273
+ }
2274
+ }
2228
2275
  }
2229
- },
2230
- {
2231
- id: "inkHighlighter",
2232
- name: "Ink Highlighter",
2233
- matchScore: (a) => a.type === PdfAnnotationSubtype.INK && a.intent === "InkHighlight" ? 10 : 0,
2234
- interaction: {
2235
- exclusive: false,
2236
- cursor: "crosshair",
2237
- isDraggable: true,
2238
- isResizable: true,
2239
- lockAspectRatio: false
2240
- },
2241
- defaults: {
2242
- type: PdfAnnotationSubtype.INK,
2243
- intent: "InkHighlight",
2244
- color: "#FFCD45",
2245
- opacity: 1,
2246
- strokeWidth: 14,
2247
- blendMode: PdfBlendMode.Multiply
2276
+ }
2277
+ onDocumentClosed(documentId) {
2278
+ this.dispatch(cleanupAnnotationState(documentId));
2279
+ this.pendingContexts.delete(documentId);
2280
+ this.isInitialLoadComplete.delete(documentId);
2281
+ this.importQueue.delete(documentId);
2282
+ this.logger.debug(
2283
+ "AnnotationPlugin",
2284
+ "DocumentClosed",
2285
+ `Cleaned up annotation state for document: ${documentId}`
2286
+ );
2287
+ }
2288
+ registerHandlerFactories() {
2289
+ this.handlerFactories.set(PdfAnnotationSubtype.CIRCLE, circleHandlerFactory);
2290
+ this.handlerFactories.set(PdfAnnotationSubtype.SQUARE, squareHandlerFactory);
2291
+ this.handlerFactories.set(PdfAnnotationSubtype.STAMP, stampHandlerFactory);
2292
+ this.handlerFactories.set(PdfAnnotationSubtype.POLYGON, polygonHandlerFactory);
2293
+ this.handlerFactories.set(PdfAnnotationSubtype.POLYLINE, polylineHandlerFactory);
2294
+ this.handlerFactories.set(PdfAnnotationSubtype.LINE, lineHandlerFactory);
2295
+ this.handlerFactories.set(PdfAnnotationSubtype.INK, inkHandlerFactory);
2296
+ this.handlerFactories.set(PdfAnnotationSubtype.FREETEXT, freeTextHandlerFactory);
2297
+ }
2298
+ registerBuiltInPatches() {
2299
+ this.patchRegistry.register(PdfAnnotationSubtype.INK, patchInk);
2300
+ this.patchRegistry.register(PdfAnnotationSubtype.LINE, patchLine);
2301
+ this.patchRegistry.register(PdfAnnotationSubtype.POLYLINE, patchPolyline);
2302
+ this.patchRegistry.register(PdfAnnotationSubtype.POLYGON, patchPolygon);
2303
+ }
2304
+ async initialize() {
2305
+ var _a, _b;
2306
+ this.state.tools.forEach((tool) => this.registerInteractionForTool(tool));
2307
+ if (this.history) {
2308
+ this.history.onHistoryChange((event) => {
2309
+ if (event.topic === this.ANNOTATION_HISTORY_TOPIC && this.config.autoCommit !== false) {
2310
+ this.commit(event.documentId);
2311
+ }
2312
+ });
2248
2313
  }
2249
- },
2250
- // Shape Tools
2251
- {
2252
- id: "circle",
2253
- name: "Circle",
2254
- matchScore: (a) => a.type === PdfAnnotationSubtype.CIRCLE ? 1 : 0,
2255
- interaction: {
2256
- exclusive: false,
2257
- cursor: "crosshair",
2258
- isDraggable: true,
2259
- isResizable: true,
2260
- lockAspectRatio: false
2261
- },
2262
- defaults: {
2263
- type: PdfAnnotationSubtype.CIRCLE,
2264
- color: "transparent",
2265
- opacity: 1,
2266
- strokeWidth: 6,
2267
- strokeColor: "#E44234",
2268
- strokeStyle: PdfAnnotationBorderStyle.SOLID
2269
- },
2270
- clickBehavior: {
2271
- enabled: true,
2272
- defaultSize: { width: 100, height: 100 }
2314
+ (_a = this.interactionManager) == null ? void 0 : _a.onModeChange((s) => {
2315
+ var _a2, _b2;
2316
+ const newToolId = ((_a2 = this.state.tools.find((t) => (t.interaction.mode ?? t.id) === s.activeMode)) == null ? void 0 : _a2.id) ?? null;
2317
+ const currentToolId = ((_b2 = this.state.documents[s.documentId]) == null ? void 0 : _b2.activeToolId) ?? null;
2318
+ if (newToolId !== currentToolId && s.documentId) {
2319
+ this.dispatch(setActiveToolId(s.documentId, newToolId));
2320
+ }
2321
+ });
2322
+ (_b = this.selection) == null ? void 0 : _b.onEndSelection(({ documentId }) => {
2323
+ var _a2, _b2, _c;
2324
+ const activeTool = this.getActiveTool(documentId);
2325
+ if (!activeTool || !activeTool.interaction.textSelection) return;
2326
+ const formattedSelection = (_a2 = this.selection) == null ? void 0 : _a2.getFormattedSelection();
2327
+ const selectionText = (_b2 = this.selection) == null ? void 0 : _b2.getSelectedText();
2328
+ if (!formattedSelection || !selectionText) return;
2329
+ for (const selection of formattedSelection) {
2330
+ selectionText.wait((text) => {
2331
+ const annotationId = uuidV4();
2332
+ this.createAnnotation(
2333
+ selection.pageIndex,
2334
+ {
2335
+ ...activeTool.defaults,
2336
+ rect: selection.rect,
2337
+ segmentRects: selection.segmentRects,
2338
+ pageIndex: selection.pageIndex,
2339
+ created: /* @__PURE__ */ new Date(),
2340
+ id: annotationId,
2341
+ custom: {
2342
+ text: text.join("\n")
2343
+ }
2344
+ },
2345
+ void 0,
2346
+ documentId
2347
+ );
2348
+ if (this.getToolBehavior(activeTool, "deactivateToolAfterCreate")) {
2349
+ this.setActiveTool(null, documentId);
2350
+ }
2351
+ if (this.getToolBehavior(activeTool, "selectAfterCreate")) {
2352
+ this.selectAnnotation(selection.pageIndex, annotationId, documentId);
2353
+ }
2354
+ }, ignore);
2355
+ }
2356
+ (_c = this.selection) == null ? void 0 : _c.clear();
2357
+ });
2358
+ }
2359
+ registerInteractionForTool(tool) {
2360
+ var _a;
2361
+ (_a = this.interactionManager) == null ? void 0 : _a.registerMode({
2362
+ id: tool.interaction.mode ?? tool.id,
2363
+ scope: "page",
2364
+ exclusive: tool.interaction.exclusive,
2365
+ cursor: tool.interaction.cursor
2366
+ });
2367
+ }
2368
+ buildCapability() {
2369
+ return {
2370
+ // Active document operations
2371
+ getActiveTool: () => this.getActiveTool(),
2372
+ setActiveTool: (toolId) => this.setActiveTool(toolId),
2373
+ getState: () => this.getDocumentState(),
2374
+ getPageAnnotations: (options) => this.getPageAnnotations(options),
2375
+ getSelectedAnnotation: () => this.getSelectedAnnotation(),
2376
+ getAnnotationById: (id) => this.getAnnotationById(id),
2377
+ selectAnnotation: (pageIndex, id) => this.selectAnnotation(pageIndex, id),
2378
+ deselectAnnotation: () => this.deselectAnnotation(),
2379
+ importAnnotations: (items) => this.importAnnotations(items),
2380
+ createAnnotation: (pageIndex, anno, ctx) => this.createAnnotation(pageIndex, anno, ctx),
2381
+ updateAnnotation: (pageIndex, id, patch) => this.updateAnnotation(pageIndex, id, patch),
2382
+ deleteAnnotation: (pageIndex, id) => this.deleteAnnotation(pageIndex, id),
2383
+ renderAnnotation: (options) => this.renderAnnotation(options),
2384
+ commit: () => this.commit(),
2385
+ // Document-scoped operations
2386
+ forDocument: (documentId) => this.createAnnotationScope(documentId),
2387
+ // Global operations
2388
+ getTools: () => this.state.tools,
2389
+ getTool: (toolId) => this.getTool(toolId),
2390
+ addTool: (tool) => {
2391
+ this.dispatch(addTool(tool));
2392
+ this.registerInteractionForTool(tool);
2393
+ },
2394
+ findToolForAnnotation: (anno) => this.findToolForAnnotation(anno),
2395
+ setToolDefaults: (toolId, patch) => this.dispatch(setToolDefaults(toolId, patch)),
2396
+ getColorPresets: () => [...this.state.colorPresets],
2397
+ addColorPreset: (color) => this.dispatch(addColorPreset(color)),
2398
+ transformAnnotation: (annotation, options) => this.transformAnnotation(annotation, options),
2399
+ registerPatchFunction: (type, patchFn) => this.registerPatchFunction(type, patchFn),
2400
+ // Events
2401
+ onStateChange: this.state$.on,
2402
+ onActiveToolChange: this.activeTool$.on,
2403
+ onAnnotationEvent: this.events$.on,
2404
+ onToolsChange: this.toolsChange$.on
2405
+ };
2406
+ }
2407
+ // ─────────────────────────────────────────────────────────
2408
+ // Document Scoping
2409
+ // ─────────────────────────────────────────────────────────
2410
+ createAnnotationScope(documentId) {
2411
+ return {
2412
+ getState: () => this.getDocumentState(documentId),
2413
+ getPageAnnotations: (options) => this.getPageAnnotations(options, documentId),
2414
+ getSelectedAnnotation: () => this.getSelectedAnnotation(documentId),
2415
+ getAnnotationById: (id) => this.getAnnotationById(id, documentId),
2416
+ selectAnnotation: (pageIndex, id) => this.selectAnnotation(pageIndex, id, documentId),
2417
+ deselectAnnotation: () => this.deselectAnnotation(documentId),
2418
+ getActiveTool: () => this.getActiveTool(documentId),
2419
+ setActiveTool: (toolId) => this.setActiveTool(toolId, documentId),
2420
+ findToolForAnnotation: (anno) => this.findToolForAnnotation(anno),
2421
+ importAnnotations: (items) => this.importAnnotations(items, documentId),
2422
+ createAnnotation: (pageIndex, anno, ctx) => this.createAnnotation(pageIndex, anno, ctx, documentId),
2423
+ updateAnnotation: (pageIndex, id, patch) => this.updateAnnotation(pageIndex, id, patch, documentId),
2424
+ deleteAnnotation: (pageIndex, id) => this.deleteAnnotation(pageIndex, id, documentId),
2425
+ renderAnnotation: (options) => this.renderAnnotation(options, documentId),
2426
+ commit: () => this.commit(documentId),
2427
+ onStateChange: (listener) => this.state$.on((event) => {
2428
+ if (event.documentId === documentId) listener(event.state);
2429
+ }),
2430
+ onAnnotationEvent: (listener) => this.events$.on((event) => {
2431
+ if (event.documentId === documentId) listener(event);
2432
+ }),
2433
+ onActiveToolChange: (listener) => this.activeTool$.on((event) => {
2434
+ if (event.documentId === documentId) listener(event.tool);
2435
+ })
2436
+ };
2437
+ }
2438
+ onStoreUpdated(prev, next) {
2439
+ for (const documentId in next.documents) {
2440
+ const prevDoc = prev.documents[documentId];
2441
+ const nextDoc = next.documents[documentId];
2442
+ if (prevDoc !== nextDoc) {
2443
+ this.state$.emit({
2444
+ documentId,
2445
+ state: nextDoc
2446
+ });
2447
+ if (prevDoc && prevDoc.activeToolId !== nextDoc.activeToolId) {
2448
+ this.activeTool$.emit({
2449
+ documentId,
2450
+ tool: this.getActiveTool(documentId)
2451
+ });
2452
+ }
2453
+ }
2273
2454
  }
2274
- },
2275
- {
2276
- id: "square",
2277
- name: "Square",
2278
- matchScore: (a) => a.type === PdfAnnotationSubtype.SQUARE ? 1 : 0,
2279
- interaction: {
2280
- exclusive: false,
2281
- cursor: "crosshair",
2282
- isDraggable: true,
2283
- isResizable: true,
2284
- lockAspectRatio: false
2285
- },
2286
- defaults: {
2287
- type: PdfAnnotationSubtype.SQUARE,
2288
- color: "transparent",
2289
- opacity: 1,
2290
- strokeWidth: 6,
2291
- strokeColor: "#E44234",
2292
- strokeStyle: PdfAnnotationBorderStyle.SOLID
2293
- },
2294
- clickBehavior: {
2295
- enabled: true,
2296
- defaultSize: { width: 100, height: 100 }
2455
+ if (prev.tools !== next.tools) {
2456
+ for (const documentId in next.documents) {
2457
+ this.activeTool$.emit({
2458
+ documentId,
2459
+ tool: this.getActiveTool(documentId)
2460
+ });
2461
+ }
2462
+ this.toolsChange$.emit({
2463
+ tools: next.tools
2464
+ });
2297
2465
  }
2298
- },
2299
- {
2300
- id: "line",
2301
- name: "Line",
2302
- matchScore: (a) => a.type === PdfAnnotationSubtype.LINE && a.intent !== "LineArrow" ? 5 : 0,
2303
- interaction: {
2304
- exclusive: false,
2305
- cursor: "crosshair",
2306
- isDraggable: true,
2307
- isResizable: false,
2308
- lockAspectRatio: false
2309
- },
2310
- defaults: {
2311
- type: PdfAnnotationSubtype.LINE,
2312
- color: "transparent",
2313
- opacity: 1,
2314
- strokeWidth: 6,
2315
- strokeColor: "#E44234"
2316
- },
2317
- clickBehavior: {
2318
- enabled: true,
2319
- defaultLength: 100,
2320
- defaultAngle: 0
2466
+ }
2467
+ registerPatchFunction(type, patchFn) {
2468
+ this.patchRegistry.register(type, patchFn);
2469
+ }
2470
+ transformAnnotation(annotation, options) {
2471
+ const context = {
2472
+ type: options.type,
2473
+ changes: options.changes,
2474
+ metadata: options.metadata
2475
+ };
2476
+ return this.patchRegistry.transform(annotation, context);
2477
+ }
2478
+ registerPageHandlers(documentId, pageIndex, scale, callbacks) {
2479
+ var _a;
2480
+ const docState = this.getCoreDocument(documentId);
2481
+ const page = (_a = docState == null ? void 0 : docState.document) == null ? void 0 : _a.pages[pageIndex];
2482
+ if (!page) return () => {
2483
+ };
2484
+ if (!this.interactionManager) return () => {
2485
+ };
2486
+ const unregisterFns = [];
2487
+ for (const tool of this.state.tools) {
2488
+ if (!tool.defaults.type) continue;
2489
+ const factory = this.handlerFactories.get(tool.defaults.type);
2490
+ if (!factory) continue;
2491
+ const context = {
2492
+ pageIndex,
2493
+ pageSize: page.size,
2494
+ scale,
2495
+ services: callbacks.services,
2496
+ // Pass through services
2497
+ onPreview: (state) => callbacks.onPreview(tool.id, state),
2498
+ onCommit: (annotation, ctx) => {
2499
+ this.createAnnotation(pageIndex, annotation, ctx, documentId);
2500
+ if (this.getToolBehavior(tool, "deactivateToolAfterCreate")) {
2501
+ this.setActiveTool(null, documentId);
2502
+ }
2503
+ if (this.getToolBehavior(tool, "selectAfterCreate")) {
2504
+ this.selectAnnotation(pageIndex, annotation.id, documentId);
2505
+ }
2506
+ },
2507
+ getTool: () => this.state.tools.find((t) => t.id === tool.id)
2508
+ };
2509
+ const unregister = this.interactionManager.registerHandlers({
2510
+ documentId,
2511
+ modeId: tool.interaction.mode ?? tool.id,
2512
+ handlers: factory.create(context),
2513
+ pageIndex
2514
+ });
2515
+ unregisterFns.push(unregister);
2321
2516
  }
2322
- },
2323
- {
2324
- id: "lineArrow",
2325
- name: "Arrow",
2326
- matchScore: (a) => a.type === PdfAnnotationSubtype.LINE && a.intent === "LineArrow" ? 10 : 0,
2327
- interaction: {
2328
- exclusive: false,
2329
- cursor: "crosshair",
2330
- isDraggable: true,
2331
- isResizable: false,
2332
- lockAspectRatio: false
2333
- },
2334
- defaults: {
2335
- type: PdfAnnotationSubtype.LINE,
2336
- intent: "LineArrow",
2337
- color: "transparent",
2338
- opacity: 1,
2339
- strokeWidth: 6,
2340
- strokeColor: "#E44234",
2341
- lineEndings: {
2342
- start: PdfAnnotationLineEnding.None,
2343
- end: PdfAnnotationLineEnding.OpenArrow
2517
+ return () => unregisterFns.forEach((fn) => fn());
2518
+ }
2519
+ // ─────────────────────────────────────────────────────────
2520
+ // Helper Methods
2521
+ // ─────────────────────────────────────────────────────────
2522
+ getDocumentState(documentId) {
2523
+ const id = documentId ?? this.getActiveDocumentId();
2524
+ const docState = this.state.documents[id];
2525
+ if (!docState) {
2526
+ throw new Error(`Annotation state not found for document: ${id}`);
2527
+ }
2528
+ return docState;
2529
+ }
2530
+ getAllAnnotations(documentId, doc) {
2531
+ const task = this.engine.getAllAnnotations(doc);
2532
+ task.wait((annotations) => {
2533
+ this.dispatch(setAnnotations(documentId, annotations));
2534
+ this.isInitialLoadComplete.set(documentId, true);
2535
+ const queue = this.importQueue.get(documentId);
2536
+ if (queue && queue.length > 0) {
2537
+ this.processImportQueue(documentId);
2344
2538
  }
2345
- },
2346
- clickBehavior: {
2347
- enabled: true,
2348
- defaultLength: 100,
2349
- defaultAngle: 0
2539
+ this.events$.emit({
2540
+ type: "loaded",
2541
+ documentId,
2542
+ total: Object.values(annotations).reduce(
2543
+ (sum, pageAnnotations) => sum + pageAnnotations.length,
2544
+ 0
2545
+ )
2546
+ });
2547
+ }, ignore);
2548
+ }
2549
+ getPageAnnotations(options, documentId) {
2550
+ const { pageIndex } = options;
2551
+ const id = documentId ?? this.getActiveDocumentId();
2552
+ const docState = this.getCoreDocument(id);
2553
+ const doc = docState == null ? void 0 : docState.document;
2554
+ if (!doc) {
2555
+ return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
2350
2556
  }
2351
- },
2352
- {
2353
- id: "polyline",
2354
- name: "Polyline",
2355
- matchScore: (a) => a.type === PdfAnnotationSubtype.POLYLINE ? 1 : 0,
2356
- interaction: {
2357
- exclusive: false,
2358
- cursor: "crosshair",
2359
- isDraggable: true,
2360
- isResizable: false,
2361
- lockAspectRatio: false
2362
- },
2363
- defaults: {
2364
- type: PdfAnnotationSubtype.POLYLINE,
2365
- color: "transparent",
2366
- opacity: 1,
2367
- strokeWidth: 6,
2368
- strokeColor: "#E44234"
2557
+ const page = doc.pages.find((p) => p.index === pageIndex);
2558
+ if (!page) {
2559
+ return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Page not found" });
2369
2560
  }
2370
- },
2371
- {
2372
- id: "polygon",
2373
- name: "Polygon",
2374
- matchScore: (a) => a.type === PdfAnnotationSubtype.POLYGON ? 1 : 0,
2375
- interaction: {
2376
- exclusive: false,
2377
- cursor: "crosshair",
2378
- isDraggable: true,
2379
- isResizable: false,
2380
- lockAspectRatio: false
2381
- },
2382
- defaults: {
2383
- type: PdfAnnotationSubtype.POLYGON,
2384
- color: "transparent",
2385
- opacity: 1,
2386
- strokeWidth: 6,
2387
- strokeColor: "#E44234"
2561
+ return this.engine.getPageAnnotations(doc, page);
2562
+ }
2563
+ getSelectedAnnotation(documentId) {
2564
+ return getSelectedAnnotation(this.getDocumentState(documentId));
2565
+ }
2566
+ getAnnotationById(id, documentId) {
2567
+ const docState = this.getDocumentState(documentId);
2568
+ return getAnnotationByUid(docState, id);
2569
+ }
2570
+ renderAnnotation({ pageIndex, annotation, options }, documentId) {
2571
+ const id = documentId ?? this.getActiveDocumentId();
2572
+ const docState = this.getCoreDocument(id);
2573
+ const doc = docState == null ? void 0 : docState.document;
2574
+ if (!doc) {
2575
+ return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
2388
2576
  }
2389
- },
2390
- // Text & Stamp
2391
- {
2392
- id: "freeText",
2393
- name: "Free Text",
2394
- matchScore: (a) => a.type === PdfAnnotationSubtype.FREETEXT ? 1 : 0,
2395
- interaction: {
2396
- exclusive: false,
2397
- cursor: "crosshair",
2398
- isDraggable: true,
2399
- isResizable: true,
2400
- lockAspectRatio: false
2401
- },
2402
- defaults: {
2403
- type: PdfAnnotationSubtype.FREETEXT,
2404
- contents: "Insert text",
2405
- fontSize: 14,
2406
- fontColor: "#E44234",
2407
- fontFamily: PdfStandardFont.Helvetica,
2408
- textAlign: PdfTextAlignment.Left,
2409
- verticalAlign: PdfVerticalAlignment.Top,
2410
- backgroundColor: "transparent",
2411
- opacity: 1
2412
- },
2413
- clickBehavior: {
2414
- enabled: true,
2415
- defaultSize: { width: 100, height: 20 },
2416
- defaultContent: "Insert text"
2577
+ const page = doc.pages.find((page2) => page2.index === pageIndex);
2578
+ if (!page) {
2579
+ return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Page not found" });
2417
2580
  }
2418
- },
2419
- {
2420
- id: "stamp",
2421
- name: "Image",
2422
- matchScore: (a) => a.type === PdfAnnotationSubtype.STAMP ? 1 : 0,
2423
- interaction: {
2424
- exclusive: false,
2425
- cursor: "copy",
2426
- isDraggable: true,
2427
- isResizable: true,
2428
- lockAspectRatio: true
2429
- },
2430
- defaults: {
2431
- type: PdfAnnotationSubtype.STAMP
2432
- // No imageSrc by default, which tells the UI to open a file picker
2581
+ return this.engine.renderPageAnnotation(doc, page, annotation, options);
2582
+ }
2583
+ importAnnotations(items, documentId) {
2584
+ const id = documentId ?? this.getActiveDocumentId();
2585
+ if (!this.isInitialLoadComplete.get(id)) {
2586
+ const queue = this.importQueue.get(id) || [];
2587
+ queue.push(...items);
2588
+ this.importQueue.set(id, queue);
2589
+ return;
2590
+ }
2591
+ this.processImportItems(id, items);
2592
+ }
2593
+ processImportQueue(documentId) {
2594
+ const queue = this.importQueue.get(documentId);
2595
+ if (!queue || queue.length === 0) return;
2596
+ const items = [...queue];
2597
+ this.importQueue.set(documentId, []);
2598
+ this.processImportItems(documentId, items);
2599
+ }
2600
+ processImportItems(documentId, items) {
2601
+ const contexts = this.pendingContexts.get(documentId);
2602
+ if (!contexts) return;
2603
+ for (const item of items) {
2604
+ const { annotation, ctx } = item;
2605
+ const pageIndex = annotation.pageIndex;
2606
+ const id = annotation.id;
2607
+ this.dispatch(createAnnotation(documentId, pageIndex, annotation));
2608
+ if (ctx) contexts.set(id, ctx);
2609
+ }
2610
+ if (this.config.autoCommit !== false) this.commit(documentId);
2611
+ }
2612
+ createAnnotation(pageIndex, annotation, ctx, documentId) {
2613
+ const id = annotation.id;
2614
+ const docId = documentId ?? this.getActiveDocumentId();
2615
+ const contexts = this.pendingContexts.get(docId);
2616
+ if (!contexts) return;
2617
+ const newAnnotation = {
2618
+ ...annotation,
2619
+ author: annotation.author ?? this.config.annotationAuthor
2620
+ };
2621
+ const execute = () => {
2622
+ this.dispatch(createAnnotation(docId, pageIndex, newAnnotation));
2623
+ if (ctx) contexts.set(id, ctx);
2624
+ this.events$.emit({
2625
+ type: "create",
2626
+ documentId: docId,
2627
+ annotation: newAnnotation,
2628
+ pageIndex,
2629
+ ctx,
2630
+ committed: false
2631
+ });
2632
+ };
2633
+ if (!this.history) {
2634
+ execute();
2635
+ if (this.config.autoCommit) this.commit(docId);
2636
+ return;
2433
2637
  }
2638
+ const command = {
2639
+ execute,
2640
+ undo: () => {
2641
+ contexts.delete(id);
2642
+ this.dispatch(deselectAnnotation(docId));
2643
+ this.dispatch(deleteAnnotation(docId, pageIndex, id));
2644
+ this.events$.emit({
2645
+ type: "delete",
2646
+ documentId: docId,
2647
+ annotation: newAnnotation,
2648
+ pageIndex,
2649
+ committed: false
2650
+ });
2651
+ }
2652
+ };
2653
+ const historyScope = this.history.forDocument(docId);
2654
+ historyScope.register(command, this.ANNOTATION_HISTORY_TOPIC);
2434
2655
  }
2435
- ];
2436
- const DEFAULT_COLORS = [
2437
- "#E44234",
2438
- "#FF8D00",
2439
- "#FFCD45",
2440
- "#5CC96E",
2441
- "#25D2D1",
2442
- "#597CE2",
2443
- "#C544CE",
2444
- "#7D2E25",
2445
- "#000000",
2446
- "#FFFFFF"
2447
- ];
2448
- const patchAnno = (state, uid, patch) => {
2449
- const prev = state.byUid[uid];
2450
- if (!prev) return state;
2451
- return {
2452
- ...state,
2453
- byUid: {
2454
- ...state.byUid,
2455
- [uid]: {
2456
- ...prev,
2457
- commitState: prev.commitState === "synced" ? "dirty" : prev.commitState,
2458
- object: { ...prev.object, ...patch }
2656
+ buildPatch(original, patch) {
2657
+ if ("rect" in patch) return patch;
2658
+ return this.transformAnnotation(original, {
2659
+ type: "property-update",
2660
+ changes: patch
2661
+ });
2662
+ }
2663
+ updateAnnotation(pageIndex, id, patch, documentId) {
2664
+ const docId = documentId ?? this.getActiveDocumentId();
2665
+ const docState = this.getDocumentState(docId);
2666
+ const originalObject = docState.byUid[id].object;
2667
+ const finalPatch = this.buildPatch(originalObject, {
2668
+ ...patch,
2669
+ author: patch.author ?? this.config.annotationAuthor
2670
+ });
2671
+ const execute = () => {
2672
+ this.dispatch(patchAnnotation(docId, pageIndex, id, finalPatch));
2673
+ this.events$.emit({
2674
+ type: "update",
2675
+ documentId: docId,
2676
+ annotation: originalObject,
2677
+ pageIndex,
2678
+ patch: finalPatch,
2679
+ committed: false
2680
+ });
2681
+ };
2682
+ if (!this.history) {
2683
+ execute();
2684
+ if (this.config.autoCommit !== false) {
2685
+ this.commit(docId);
2459
2686
  }
2460
- },
2461
- hasPendingChanges: true
2462
- };
2463
- };
2464
- const initialState = (cfg) => {
2465
- const toolMap = /* @__PURE__ */ new Map();
2466
- defaultTools.forEach((t) => toolMap.set(t.id, t));
2467
- (cfg.tools || []).forEach((t) => toolMap.set(t.id, t));
2468
- return {
2469
- pages: {},
2470
- byUid: {},
2471
- selectedUid: null,
2472
- activeToolId: null,
2473
- // `Array.from(toolMap.values())` now correctly returns `AnnotationTool[]`, which matches the state's type.
2474
- tools: Array.from(toolMap.values()),
2475
- colorPresets: cfg.colorPresets ?? DEFAULT_COLORS,
2476
- hasPendingChanges: false
2477
- };
2478
- };
2479
- const reducer = (state, action) => {
2480
- switch (action.type) {
2481
- case SET_ANNOTATIONS: {
2482
- const newPages = { ...state.pages };
2483
- const newByUid = { ...state.byUid };
2484
- for (const [pgStr, list] of Object.entries(action.payload)) {
2485
- const pageIndex = Number(pgStr);
2486
- const oldUidsOnPage = state.pages[pageIndex] || [];
2487
- for (const uid of oldUidsOnPage) {
2488
- delete newByUid[uid];
2489
- }
2490
- const newUidsOnPage = list.map((a) => {
2491
- const uid = a.id;
2492
- newByUid[uid] = { commitState: "synced", object: a };
2493
- return uid;
2687
+ return;
2688
+ }
2689
+ const originalPatch = Object.fromEntries(
2690
+ Object.keys(patch).map((key) => [key, originalObject[key]])
2691
+ );
2692
+ const command = {
2693
+ execute,
2694
+ undo: () => {
2695
+ this.dispatch(patchAnnotation(docId, pageIndex, id, originalPatch));
2696
+ this.events$.emit({
2697
+ type: "update",
2698
+ documentId: docId,
2699
+ annotation: originalObject,
2700
+ pageIndex,
2701
+ patch: originalPatch,
2702
+ committed: false
2494
2703
  });
2495
- newPages[pageIndex] = newUidsOnPage;
2496
2704
  }
2497
- return { ...state, pages: newPages, byUid: newByUid };
2498
- }
2499
- case SET_ACTIVE_TOOL_ID:
2500
- return { ...state, activeToolId: action.payload };
2501
- case ADD_TOOL: {
2502
- const toolMap = new Map(state.tools.map((t) => [t.id, t]));
2503
- toolMap.set(action.payload.id, action.payload);
2504
- return { ...state, tools: Array.from(toolMap.values()) };
2705
+ };
2706
+ const historyScope = this.history.forDocument(docId);
2707
+ historyScope.register(command, this.ANNOTATION_HISTORY_TOPIC);
2708
+ }
2709
+ deleteAnnotation(pageIndex, id, documentId) {
2710
+ var _a;
2711
+ const docId = documentId ?? this.getActiveDocumentId();
2712
+ const docState = this.getDocumentState(docId);
2713
+ const originalAnnotation = (_a = docState.byUid[id]) == null ? void 0 : _a.object;
2714
+ if (!originalAnnotation) return;
2715
+ const execute = () => {
2716
+ this.dispatch(deselectAnnotation(docId));
2717
+ this.dispatch(deleteAnnotation(docId, pageIndex, id));
2718
+ this.events$.emit({
2719
+ type: "delete",
2720
+ documentId: docId,
2721
+ annotation: originalAnnotation,
2722
+ pageIndex,
2723
+ committed: false
2724
+ });
2725
+ };
2726
+ if (!this.history) {
2727
+ execute();
2728
+ if (this.config.autoCommit !== false) this.commit(docId);
2729
+ return;
2505
2730
  }
2506
- case SET_TOOL_DEFAULTS: {
2507
- const { toolId, patch } = action.payload;
2508
- return {
2509
- ...state,
2510
- tools: state.tools.map((tool) => {
2511
- if (tool.id === toolId) {
2512
- return { ...tool, defaults: { ...tool.defaults, ...patch } };
2513
- }
2514
- return tool;
2515
- })
2516
- };
2731
+ const command = {
2732
+ execute,
2733
+ undo: () => {
2734
+ this.dispatch(createAnnotation(docId, pageIndex, originalAnnotation));
2735
+ this.events$.emit({
2736
+ type: "create",
2737
+ documentId: docId,
2738
+ annotation: originalAnnotation,
2739
+ pageIndex,
2740
+ committed: false
2741
+ });
2742
+ }
2743
+ };
2744
+ const historyScope = this.history.forDocument(docId);
2745
+ historyScope.register(command, this.ANNOTATION_HISTORY_TOPIC);
2746
+ }
2747
+ selectAnnotation(pageIndex, id, documentId) {
2748
+ const docId = documentId ?? this.getActiveDocumentId();
2749
+ this.dispatch(selectAnnotation(docId, pageIndex, id));
2750
+ }
2751
+ deselectAnnotation(documentId) {
2752
+ const docId = documentId ?? this.getActiveDocumentId();
2753
+ this.dispatch(deselectAnnotation(docId));
2754
+ }
2755
+ getActiveTool(documentId) {
2756
+ const docState = this.getDocumentState(documentId);
2757
+ if (!docState.activeToolId) return null;
2758
+ return this.state.tools.find((t) => t.id === docState.activeToolId) ?? null;
2759
+ }
2760
+ setActiveTool(toolId, documentId) {
2761
+ var _a, _b;
2762
+ const docId = documentId ?? this.getActiveDocumentId();
2763
+ const docState = this.getDocumentState(docId);
2764
+ if (toolId === docState.activeToolId) return;
2765
+ this.dispatch(setActiveToolId(docId, toolId));
2766
+ const tool = this.state.tools.find((t) => t.id === toolId);
2767
+ if (tool) {
2768
+ (_a = this.interactionManager) == null ? void 0 : _a.forDocument(docId).activate(tool.interaction.mode ?? tool.id);
2769
+ } else {
2770
+ (_b = this.interactionManager) == null ? void 0 : _b.forDocument(docId).activateDefaultMode();
2517
2771
  }
2518
- case SELECT_ANNOTATION:
2519
- return { ...state, selectedUid: action.payload.id };
2520
- case DESELECT_ANNOTATION:
2521
- return { ...state, selectedUid: null };
2522
- case ADD_COLOR_PRESET:
2523
- return state.colorPresets.includes(action.payload) ? state : { ...state, colorPresets: [...state.colorPresets, action.payload] };
2524
- case CREATE_ANNOTATION: {
2525
- const { pageIndex, annotation } = action.payload;
2526
- const uid = annotation.id;
2527
- return {
2528
- ...state,
2529
- pages: { ...state.pages, [pageIndex]: [...state.pages[pageIndex] ?? [], uid] },
2530
- byUid: { ...state.byUid, [uid]: { commitState: "new", object: annotation } },
2531
- hasPendingChanges: true
2532
- };
2772
+ }
2773
+ getTool(toolId) {
2774
+ return this.state.tools.find((t) => t.id === toolId);
2775
+ }
2776
+ findToolForAnnotation(annotation) {
2777
+ let bestTool = null;
2778
+ let bestScore = 0;
2779
+ for (const tool of this.state.tools) {
2780
+ const score = tool.matchScore(annotation);
2781
+ if (score > bestScore) {
2782
+ bestScore = score;
2783
+ bestTool = tool;
2784
+ }
2533
2785
  }
2534
- case DELETE_ANNOTATION: {
2535
- const { pageIndex, id: uid } = action.payload;
2536
- if (!state.byUid[uid]) return state;
2537
- return {
2538
- ...state,
2539
- pages: {
2540
- ...state.pages,
2541
- [pageIndex]: (state.pages[pageIndex] ?? []).filter((u) => u !== uid)
2542
- },
2543
- byUid: {
2544
- ...state.byUid,
2545
- [uid]: { ...state.byUid[uid], commitState: "deleted" }
2546
- },
2547
- hasPendingChanges: true
2548
- };
2786
+ return bestTool;
2787
+ }
2788
+ commit(documentId) {
2789
+ const task = new Task();
2790
+ const docId = documentId ?? this.getActiveDocumentId();
2791
+ const docState = this.getDocumentState(docId);
2792
+ if (!docState.hasPendingChanges) return PdfTaskHelper.resolve(true);
2793
+ const coreDocState = this.getCoreDocument(docId);
2794
+ const doc = coreDocState == null ? void 0 : coreDocState.document;
2795
+ if (!doc)
2796
+ return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
2797
+ const contexts = this.pendingContexts.get(docId);
2798
+ if (!contexts) return PdfTaskHelper.resolve(true);
2799
+ const creations = [];
2800
+ const updates = [];
2801
+ const deletions = [];
2802
+ for (const [uid, ta] of Object.entries(docState.byUid)) {
2803
+ if (ta.commitState === "synced") continue;
2804
+ const page = doc.pages.find((p) => p.index === ta.object.pageIndex);
2805
+ if (!page) continue;
2806
+ switch (ta.commitState) {
2807
+ case "new":
2808
+ const ctx = contexts.get(ta.object.id);
2809
+ const task2 = this.engine.createPageAnnotation(doc, page, ta.object, ctx);
2810
+ task2.wait(() => {
2811
+ this.events$.emit({
2812
+ type: "create",
2813
+ documentId: docId,
2814
+ annotation: ta.object,
2815
+ pageIndex: ta.object.pageIndex,
2816
+ ctx,
2817
+ committed: true
2818
+ });
2819
+ contexts.delete(ta.object.id);
2820
+ }, ignore);
2821
+ creations.push(task2);
2822
+ break;
2823
+ case "dirty":
2824
+ const updateTask = this.engine.updatePageAnnotation(doc, page, ta.object);
2825
+ updateTask.wait(() => {
2826
+ this.events$.emit({
2827
+ type: "update",
2828
+ documentId: docId,
2829
+ annotation: ta.object,
2830
+ pageIndex: ta.object.pageIndex,
2831
+ patch: ta.object,
2832
+ committed: true
2833
+ });
2834
+ }, ignore);
2835
+ updates.push(updateTask);
2836
+ break;
2837
+ case "deleted":
2838
+ deletions.push({ ta, uid });
2839
+ break;
2840
+ }
2549
2841
  }
2550
- case PATCH_ANNOTATION:
2551
- return patchAnno(state, action.payload.id, action.payload.patch);
2552
- case COMMIT_PENDING_CHANGES: {
2553
- const cleaned = {};
2554
- for (const [uid, ta] of Object.entries(state.byUid)) {
2555
- cleaned[uid] = {
2556
- ...ta,
2557
- commitState: ta.commitState === "dirty" || ta.commitState === "new" ? "synced" : ta.commitState
2558
- };
2842
+ const deletionTasks = [];
2843
+ for (const { ta, uid } of deletions) {
2844
+ const page = doc.pages.find((p) => p.index === ta.object.pageIndex);
2845
+ if (ta.commitState === "deleted" && ta.object.id) {
2846
+ const task2 = new Task();
2847
+ const removeTask = this.engine.removePageAnnotation(doc, page, ta.object);
2848
+ removeTask.wait(() => {
2849
+ this.dispatch(purgeAnnotation(docId, uid));
2850
+ this.events$.emit({
2851
+ type: "delete",
2852
+ documentId: docId,
2853
+ annotation: ta.object,
2854
+ pageIndex: ta.object.pageIndex,
2855
+ committed: true
2856
+ });
2857
+ task2.resolve(true);
2858
+ }, task2.fail);
2859
+ deletionTasks.push(task2);
2860
+ } else {
2861
+ this.dispatch(purgeAnnotation(docId, uid));
2559
2862
  }
2560
- return { ...state, byUid: cleaned, hasPendingChanges: false };
2561
2863
  }
2562
- case PURGE_ANNOTATION: {
2563
- const { uid } = action.payload;
2564
- const { [uid]: _gone, ...rest } = state.byUid;
2565
- return { ...state, byUid: rest };
2864
+ const allWriteTasks = [...creations, ...updates, ...deletionTasks];
2865
+ Task.allSettled(allWriteTasks).wait(() => {
2866
+ this.dispatch(commitPendingChanges(docId));
2867
+ task.resolve(true);
2868
+ }, task.fail);
2869
+ return task;
2870
+ }
2871
+ /**
2872
+ * Gets the effective behavior setting for a tool, checking tool-specific config first,
2873
+ * then falling back to plugin config.
2874
+ */
2875
+ getToolBehavior(tool, setting) {
2876
+ var _a;
2877
+ if (((_a = tool.behavior) == null ? void 0 : _a[setting]) !== void 0) {
2878
+ return tool.behavior[setting];
2566
2879
  }
2567
- default:
2568
- return state;
2880
+ return this.config[setting] !== false;
2569
2881
  }
2570
2882
  };
2883
+ _AnnotationPlugin.id = "annotation";
2884
+ let AnnotationPlugin = _AnnotationPlugin;
2571
2885
  function createToolPredicate(id) {
2572
2886
  return (tool) => {
2573
2887
  return (tool == null ? void 0 : tool.id) === id;
@@ -2597,6 +2911,7 @@ export {
2597
2911
  AnnotationPlugin,
2598
2912
  AnnotationPluginPackage,
2599
2913
  createToolPredicate,
2914
+ getAnnotationByUid,
2600
2915
  getAnnotations,
2601
2916
  getAnnotationsByPageIndex,
2602
2917
  getSelectedAnnotation,
@@ -2604,6 +2919,7 @@ export {
2604
2919
  getSidebarAnnotationsWithReplies,
2605
2920
  getSidebarAnnotationsWithRepliesGroupedByPage,
2606
2921
  getToolDefaultsById,
2922
+ initialDocumentState,
2607
2923
  initialState,
2608
2924
  isAnnotationSelected,
2609
2925
  isCircle,