@embedpdf/plugin-annotation 2.1.2 → 2.3.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.
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1232 -93
- package/dist/index.js.map +1 -1
- package/dist/lib/actions.d.ts +31 -2
- package/dist/lib/annotation-plugin.d.ts +177 -2
- package/dist/lib/handlers/types.d.ts +1 -1
- package/dist/lib/helpers.d.ts +6 -1
- package/dist/lib/selectors.d.ts +97 -8
- package/dist/lib/tools/default-tools.d.ts +39 -11
- package/dist/lib/tools/types.d.ts +7 -1
- package/dist/lib/types.d.ts +265 -1
- package/dist/preact/adapter.d.ts +3 -3
- package/dist/preact/index.cjs +1 -1
- package/dist/preact/index.cjs.map +1 -1
- package/dist/preact/index.js +867 -406
- package/dist/preact/index.js.map +1 -1
- package/dist/react/adapter.d.ts +1 -1
- package/dist/react/index.cjs +1 -1
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +867 -406
- package/dist/react/index.js.map +1 -1
- package/dist/shared/components/annotation-container.d.ts +8 -1
- package/dist/shared/components/annotation-layer.d.ts +4 -2
- package/dist/shared/components/annotations/ink.d.ts +3 -3
- package/dist/shared/components/annotations/link.d.ts +28 -0
- package/dist/shared/components/annotations.d.ts +2 -1
- package/dist/shared/components/group-selection-box.d.ts +32 -0
- package/dist/shared/components/index.d.ts +1 -0
- package/dist/shared/components/text-markup/highlight.d.ts +3 -2
- package/dist/shared/components/text-markup/squiggly.d.ts +3 -2
- package/dist/shared/components/text-markup/strikeout.d.ts +3 -2
- package/dist/shared/components/text-markup/underline.d.ts +3 -2
- package/dist/shared/components/types.d.ts +7 -0
- package/dist/shared-preact/components/annotation-container.d.ts +8 -1
- package/dist/shared-preact/components/annotation-layer.d.ts +4 -2
- package/dist/shared-preact/components/annotations/ink.d.ts +3 -3
- package/dist/shared-preact/components/annotations/link.d.ts +28 -0
- package/dist/shared-preact/components/annotations.d.ts +2 -1
- package/dist/shared-preact/components/group-selection-box.d.ts +32 -0
- package/dist/shared-preact/components/index.d.ts +1 -0
- package/dist/shared-preact/components/text-markup/highlight.d.ts +3 -2
- package/dist/shared-preact/components/text-markup/squiggly.d.ts +3 -2
- package/dist/shared-preact/components/text-markup/strikeout.d.ts +3 -2
- package/dist/shared-preact/components/text-markup/underline.d.ts +3 -2
- package/dist/shared-preact/components/types.d.ts +7 -0
- package/dist/shared-react/components/annotation-container.d.ts +8 -1
- package/dist/shared-react/components/annotation-layer.d.ts +4 -2
- package/dist/shared-react/components/annotations/ink.d.ts +3 -3
- package/dist/shared-react/components/annotations/link.d.ts +28 -0
- package/dist/shared-react/components/annotations.d.ts +2 -1
- package/dist/shared-react/components/group-selection-box.d.ts +32 -0
- package/dist/shared-react/components/index.d.ts +1 -0
- package/dist/shared-react/components/text-markup/highlight.d.ts +3 -2
- package/dist/shared-react/components/text-markup/squiggly.d.ts +3 -2
- package/dist/shared-react/components/text-markup/strikeout.d.ts +3 -2
- package/dist/shared-react/components/text-markup/underline.d.ts +3 -2
- package/dist/shared-react/components/types.d.ts +7 -0
- package/dist/svelte/components/AnnotationLayer.svelte.d.ts +5 -1
- package/dist/svelte/components/Annotations.svelte.d.ts +5 -1
- package/dist/svelte/components/GroupSelectionBox.svelte.d.ts +32 -0
- package/dist/svelte/components/annotations/Ink.svelte.d.ts +2 -1
- package/dist/svelte/components/annotations/Link.svelte.d.ts +24 -0
- package/dist/svelte/components/annotations/index.d.ts +1 -0
- package/dist/svelte/components/index.d.ts +1 -0
- package/dist/svelte/components/text-markup/Highlight.svelte.d.ts +2 -1
- package/dist/svelte/components/text-markup/Squiggly.svelte.d.ts +2 -1
- package/dist/svelte/components/text-markup/Strikeout.svelte.d.ts +2 -1
- package/dist/svelte/components/text-markup/Underline.svelte.d.ts +2 -1
- package/dist/svelte/components/types.d.ts +2 -0
- package/dist/svelte/index.cjs +1 -1
- package/dist/svelte/index.cjs.map +1 -1
- package/dist/svelte/index.js +912 -258
- package/dist/svelte/index.js.map +1 -1
- package/dist/svelte/types.d.ts +7 -0
- package/dist/vue/components/annotation-container.vue.d.ts +2 -0
- package/dist/vue/components/annotation-layer.vue.d.ts +28 -5
- package/dist/vue/components/annotations/index.d.ts +1 -0
- package/dist/vue/components/annotations/ink.vue.d.ts +2 -2
- package/dist/vue/components/annotations/line.vue.d.ts +1 -1
- package/dist/vue/components/annotations/link.vue.d.ts +29 -0
- package/dist/vue/components/annotations/polygon.vue.d.ts +1 -1
- package/dist/vue/components/annotations/polyline.vue.d.ts +1 -1
- package/dist/vue/components/annotations.vue.d.ts +65 -1
- package/dist/vue/components/group-selection-box.vue.d.ts +73 -0
- package/dist/vue/components/index.d.ts +1 -0
- package/dist/vue/components/text-markup/highlight.vue.d.ts +2 -2
- package/dist/vue/components/text-markup/squiggly.vue.d.ts +2 -2
- package/dist/vue/components/text-markup/strikeout.vue.d.ts +2 -2
- package/dist/vue/components/text-markup/underline.vue.d.ts +2 -2
- package/dist/vue/hooks/use-annotation.d.ts +2 -0
- package/dist/vue/index.cjs +1 -1
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.js +962 -409
- package/dist/vue/index.js.map +1 -1
- package/dist/vue/types.d.ts +7 -0
- package/package.json +10 -10
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
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";
|
|
2
|
+
import { PdfAnnotationSubtype, PdfAnnotationReplyType, PdfBlendMode, PdfAnnotationBorderStyle, PdfAnnotationLineEnding, PdfVerticalAlignment, PdfTextAlignment, PdfStandardFont, expandRect, rectFromPoints, uuidV4, rotateAndTranslatePoint, PdfAnnotationIcon, PdfPermissionFlag, ignore, PdfTaskHelper, PdfErrorCode, Task, TaskStage } from "@embedpdf/models";
|
|
3
3
|
const ANNOTATION_PLUGIN_ID = "annotation";
|
|
4
4
|
const manifest = {
|
|
5
5
|
id: ANNOTATION_PLUGIN_ID,
|
|
@@ -21,6 +21,9 @@ const SET_ACTIVE_DOCUMENT = "ANNOTATION/SET_ACTIVE_DOCUMENT";
|
|
|
21
21
|
const SET_ANNOTATIONS = "ANNOTATION/SET_ANNOTATIONS";
|
|
22
22
|
const SELECT_ANNOTATION = "ANNOTATION/SELECT_ANNOTATION";
|
|
23
23
|
const DESELECT_ANNOTATION = "ANNOTATION/DESELECT_ANNOTATION";
|
|
24
|
+
const ADD_TO_SELECTION = "ANNOTATION/ADD_TO_SELECTION";
|
|
25
|
+
const REMOVE_FROM_SELECTION = "ANNOTATION/REMOVE_FROM_SELECTION";
|
|
26
|
+
const SET_SELECTION = "ANNOTATION/SET_SELECTION";
|
|
24
27
|
const SET_ACTIVE_TOOL_ID = "ANNOTATION/SET_ACTIVE_TOOL_ID";
|
|
25
28
|
const CREATE_ANNOTATION = "ANNOTATION/CREATE_ANNOTATION";
|
|
26
29
|
const PATCH_ANNOTATION = "ANNOTATION/PATCH_ANNOTATION";
|
|
@@ -48,6 +51,18 @@ const deselectAnnotation = (documentId) => ({
|
|
|
48
51
|
type: DESELECT_ANNOTATION,
|
|
49
52
|
payload: { documentId }
|
|
50
53
|
});
|
|
54
|
+
const addToSelection = (documentId, pageIndex, id) => ({
|
|
55
|
+
type: ADD_TO_SELECTION,
|
|
56
|
+
payload: { documentId, pageIndex, id }
|
|
57
|
+
});
|
|
58
|
+
const removeFromSelection = (documentId, id) => ({
|
|
59
|
+
type: REMOVE_FROM_SELECTION,
|
|
60
|
+
payload: { documentId, id }
|
|
61
|
+
});
|
|
62
|
+
const setSelection = (documentId, ids) => ({
|
|
63
|
+
type: SET_SELECTION,
|
|
64
|
+
payload: { documentId, ids }
|
|
65
|
+
});
|
|
51
66
|
const setActiveToolId = (documentId, toolId) => ({
|
|
52
67
|
type: SET_ACTIVE_TOOL_ID,
|
|
53
68
|
payload: { documentId, toolId }
|
|
@@ -64,9 +79,9 @@ const deleteAnnotation = (documentId, pageIndex, id) => ({
|
|
|
64
79
|
type: DELETE_ANNOTATION,
|
|
65
80
|
payload: { documentId, pageIndex, id }
|
|
66
81
|
});
|
|
67
|
-
const commitPendingChanges = (documentId) => ({
|
|
82
|
+
const commitPendingChanges = (documentId, committedUids) => ({
|
|
68
83
|
type: COMMIT_PENDING_CHANGES,
|
|
69
|
-
payload: { documentId }
|
|
84
|
+
payload: { documentId, committedUids }
|
|
70
85
|
});
|
|
71
86
|
const purgeAnnotation = (documentId, uid) => ({
|
|
72
87
|
type: PURGE_ANNOTATION,
|
|
@@ -84,6 +99,9 @@ const addTool = (tool) => ({
|
|
|
84
99
|
type: ADD_TOOL,
|
|
85
100
|
payload: tool
|
|
86
101
|
});
|
|
102
|
+
function rectsIntersect(a, b) {
|
|
103
|
+
return !(a.origin.x + a.size.width < b.origin.x || b.origin.x + b.size.width < a.origin.x || a.origin.y + a.size.height < b.origin.y || b.origin.y + b.size.height < a.origin.y);
|
|
104
|
+
}
|
|
87
105
|
function isInk(a) {
|
|
88
106
|
return a.object.type === PdfAnnotationSubtype.INK;
|
|
89
107
|
}
|
|
@@ -126,6 +144,9 @@ function isStamp(a) {
|
|
|
126
144
|
function isText(a) {
|
|
127
145
|
return a.object.type === PdfAnnotationSubtype.TEXT;
|
|
128
146
|
}
|
|
147
|
+
function isLink(a) {
|
|
148
|
+
return a.object.type === PdfAnnotationSubtype.LINK;
|
|
149
|
+
}
|
|
129
150
|
function isSidebarAnnotation(a) {
|
|
130
151
|
return isTextMarkup(a) || isInk(a) || isSquare(a) || isCircle(a) || isPolygon(a) || isLine(a) || isPolyline(a) || isFreeText(a) || isStamp(a);
|
|
131
152
|
}
|
|
@@ -135,17 +156,25 @@ const getAnnotations = (s) => {
|
|
|
135
156
|
for (const p of Object.keys(s.pages).map(Number)) out[p] = getAnnotationsByPageIndex(s, p);
|
|
136
157
|
return out;
|
|
137
158
|
};
|
|
138
|
-
const getSelectedAnnotation = (s) => s.
|
|
159
|
+
const getSelectedAnnotation = (s) => s.selectedUids.length > 0 ? s.byUid[s.selectedUids[0]] ?? null : null;
|
|
160
|
+
const getSelectedAnnotations = (s) => s.selectedUids.map((uid) => s.byUid[uid]).filter((ta) => ta !== void 0);
|
|
161
|
+
const getSelectedAnnotationIds = (s) => s.selectedUids;
|
|
139
162
|
const getAnnotationByUid = (s, uid) => s.byUid[uid] ?? null;
|
|
140
163
|
const getSelectedAnnotationByPageIndex = (s, pageIndex) => {
|
|
141
|
-
if (!s.selectedUid) return null;
|
|
142
164
|
const pageUids = s.pages[pageIndex] ?? [];
|
|
143
|
-
|
|
144
|
-
|
|
165
|
+
for (const uid of s.selectedUids) {
|
|
166
|
+
if (pageUids.includes(uid)) {
|
|
167
|
+
return s.byUid[uid] ?? null;
|
|
168
|
+
}
|
|
145
169
|
}
|
|
146
170
|
return null;
|
|
147
171
|
};
|
|
148
|
-
const
|
|
172
|
+
const getSelectedAnnotationsByPageIndex = (s, pageIndex) => {
|
|
173
|
+
const pageUids = new Set(s.pages[pageIndex] ?? []);
|
|
174
|
+
return s.selectedUids.filter((uid) => pageUids.has(uid)).map((uid) => s.byUid[uid]).filter((ta) => ta !== void 0);
|
|
175
|
+
};
|
|
176
|
+
const isAnnotationSelected = (s, id) => s.selectedUids.includes(id);
|
|
177
|
+
const hasMultipleSelected = (s) => s.selectedUids.length > 1;
|
|
149
178
|
function getToolDefaultsById(state, toolId) {
|
|
150
179
|
const tool = state.tools.find((t) => t.id === toolId);
|
|
151
180
|
return tool == null ? void 0 : tool.defaults;
|
|
@@ -190,6 +219,101 @@ const getSidebarAnnotationsWithReplies = (s) => {
|
|
|
190
219
|
}
|
|
191
220
|
return out;
|
|
192
221
|
};
|
|
222
|
+
const getIRTChildIds = (s, parentId) => {
|
|
223
|
+
const children = [];
|
|
224
|
+
for (const uidList of Object.values(s.pages)) {
|
|
225
|
+
for (const uid of uidList) {
|
|
226
|
+
const ta = s.byUid[uid];
|
|
227
|
+
if (ta && "inReplyToId" in ta.object && ta.object.inReplyToId === parentId) {
|
|
228
|
+
children.push({ id: ta.object.id, pageIndex: ta.object.pageIndex });
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return children;
|
|
233
|
+
};
|
|
234
|
+
const hasIRTChildren = (s, parentId) => {
|
|
235
|
+
for (const uidList of Object.values(s.pages)) {
|
|
236
|
+
for (const uid of uidList) {
|
|
237
|
+
const ta = s.byUid[uid];
|
|
238
|
+
if (ta && "inReplyToId" in ta.object && ta.object.inReplyToId === parentId) {
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
return false;
|
|
244
|
+
};
|
|
245
|
+
const getIRTChildrenByType = (s, parentId, types) => {
|
|
246
|
+
const children = [];
|
|
247
|
+
for (const uidList of Object.values(s.pages)) {
|
|
248
|
+
for (const uid of uidList) {
|
|
249
|
+
const ta = s.byUid[uid];
|
|
250
|
+
if (ta && "inReplyToId" in ta.object && ta.object.inReplyToId === parentId && types.includes(ta.object.type)) {
|
|
251
|
+
children.push(ta);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
return children;
|
|
256
|
+
};
|
|
257
|
+
const getAttachedLinks = (s, parentId) => getIRTChildrenByType(s, parentId, [PdfAnnotationSubtype.LINK]);
|
|
258
|
+
const hasAttachedLinks = (s, parentId) => getAttachedLinks(s, parentId).length > 0;
|
|
259
|
+
const getGroupLeaderId = (s, annotationId) => {
|
|
260
|
+
const ta = s.byUid[annotationId];
|
|
261
|
+
if (!ta) return void 0;
|
|
262
|
+
if (ta.object.inReplyToId && ta.object.replyType === PdfAnnotationReplyType.Group) {
|
|
263
|
+
return ta.object.inReplyToId;
|
|
264
|
+
}
|
|
265
|
+
return annotationId;
|
|
266
|
+
};
|
|
267
|
+
const getGroupMembers = (s, annotationId) => {
|
|
268
|
+
const leaderId = getGroupLeaderId(s, annotationId);
|
|
269
|
+
if (!leaderId) return [];
|
|
270
|
+
const members = [];
|
|
271
|
+
const leader = s.byUid[leaderId];
|
|
272
|
+
if (leader && leader.object.type !== PdfAnnotationSubtype.LINK) {
|
|
273
|
+
members.push(leader);
|
|
274
|
+
}
|
|
275
|
+
for (const uidList of Object.values(s.pages)) {
|
|
276
|
+
for (const uid of uidList) {
|
|
277
|
+
const ta = s.byUid[uid];
|
|
278
|
+
if (ta && ta.object.inReplyToId === leaderId && ta.object.replyType === PdfAnnotationReplyType.Group && ta.object.type !== PdfAnnotationSubtype.LINK) {
|
|
279
|
+
members.push(ta);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
return members;
|
|
284
|
+
};
|
|
285
|
+
const isInGroup = (s, annotationId) => {
|
|
286
|
+
const ta = s.byUid[annotationId];
|
|
287
|
+
if (!ta) return false;
|
|
288
|
+
if (ta.object.type === PdfAnnotationSubtype.LINK) {
|
|
289
|
+
return false;
|
|
290
|
+
}
|
|
291
|
+
if (ta.object.inReplyToId && ta.object.replyType === PdfAnnotationReplyType.Group) {
|
|
292
|
+
return true;
|
|
293
|
+
}
|
|
294
|
+
for (const uidList of Object.values(s.pages)) {
|
|
295
|
+
for (const uid of uidList) {
|
|
296
|
+
const other = s.byUid[uid];
|
|
297
|
+
if (other && other.object.inReplyToId === annotationId && other.object.replyType === PdfAnnotationReplyType.Group && other.object.type !== PdfAnnotationSubtype.LINK) {
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return false;
|
|
303
|
+
};
|
|
304
|
+
const getSelectionGroupingAction = (s) => {
|
|
305
|
+
const selected = getSelectedAnnotations(s);
|
|
306
|
+
if (selected.length === 0) return "disabled";
|
|
307
|
+
const firstId = selected[0].object.id;
|
|
308
|
+
if (isInGroup(s, firstId)) {
|
|
309
|
+
const members = getGroupMembers(s, firstId);
|
|
310
|
+
const memberIds = new Set(members.map((m) => m.object.id));
|
|
311
|
+
if (selected.every((ta) => memberIds.has(ta.object.id))) {
|
|
312
|
+
return "ungroup";
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return selected.length >= 2 ? "group" : "disabled";
|
|
316
|
+
};
|
|
193
317
|
const defaultTools = [
|
|
194
318
|
// Text Markup Tools
|
|
195
319
|
{
|
|
@@ -200,11 +324,16 @@ const defaultTools = [
|
|
|
200
324
|
exclusive: false,
|
|
201
325
|
textSelection: true,
|
|
202
326
|
isDraggable: false,
|
|
203
|
-
isResizable: false
|
|
327
|
+
isResizable: false,
|
|
328
|
+
// Text markup annotations are anchored to text and should not move/resize in groups
|
|
329
|
+
isGroupDraggable: false,
|
|
330
|
+
isGroupResizable: false
|
|
204
331
|
},
|
|
205
332
|
defaults: {
|
|
206
333
|
type: PdfAnnotationSubtype.HIGHLIGHT,
|
|
334
|
+
strokeColor: "#FFCD45",
|
|
207
335
|
color: "#FFCD45",
|
|
336
|
+
// deprecated alias
|
|
208
337
|
opacity: 1,
|
|
209
338
|
blendMode: PdfBlendMode.Multiply
|
|
210
339
|
}
|
|
@@ -217,11 +346,15 @@ const defaultTools = [
|
|
|
217
346
|
exclusive: false,
|
|
218
347
|
textSelection: true,
|
|
219
348
|
isDraggable: false,
|
|
220
|
-
isResizable: false
|
|
349
|
+
isResizable: false,
|
|
350
|
+
isGroupDraggable: false,
|
|
351
|
+
isGroupResizable: false
|
|
221
352
|
},
|
|
222
353
|
defaults: {
|
|
223
354
|
type: PdfAnnotationSubtype.UNDERLINE,
|
|
355
|
+
strokeColor: "#E44234",
|
|
224
356
|
color: "#E44234",
|
|
357
|
+
// deprecated alias
|
|
225
358
|
opacity: 1
|
|
226
359
|
}
|
|
227
360
|
},
|
|
@@ -231,11 +364,17 @@ const defaultTools = [
|
|
|
231
364
|
matchScore: (a) => a.type === PdfAnnotationSubtype.STRIKEOUT ? 1 : 0,
|
|
232
365
|
interaction: {
|
|
233
366
|
exclusive: false,
|
|
234
|
-
textSelection: true
|
|
367
|
+
textSelection: true,
|
|
368
|
+
isDraggable: false,
|
|
369
|
+
isResizable: false,
|
|
370
|
+
isGroupDraggable: false,
|
|
371
|
+
isGroupResizable: false
|
|
235
372
|
},
|
|
236
373
|
defaults: {
|
|
237
374
|
type: PdfAnnotationSubtype.STRIKEOUT,
|
|
375
|
+
strokeColor: "#E44234",
|
|
238
376
|
color: "#E44234",
|
|
377
|
+
// deprecated alias
|
|
239
378
|
opacity: 1
|
|
240
379
|
}
|
|
241
380
|
},
|
|
@@ -247,11 +386,15 @@ const defaultTools = [
|
|
|
247
386
|
exclusive: false,
|
|
248
387
|
textSelection: true,
|
|
249
388
|
isDraggable: false,
|
|
250
|
-
isResizable: false
|
|
389
|
+
isResizable: false,
|
|
390
|
+
isGroupDraggable: false,
|
|
391
|
+
isGroupResizable: false
|
|
251
392
|
},
|
|
252
393
|
defaults: {
|
|
253
394
|
type: PdfAnnotationSubtype.SQUIGGLY,
|
|
395
|
+
strokeColor: "#E44234",
|
|
254
396
|
color: "#E44234",
|
|
397
|
+
// deprecated alias
|
|
255
398
|
opacity: 1
|
|
256
399
|
}
|
|
257
400
|
},
|
|
@@ -269,7 +412,9 @@ const defaultTools = [
|
|
|
269
412
|
},
|
|
270
413
|
defaults: {
|
|
271
414
|
type: PdfAnnotationSubtype.INK,
|
|
415
|
+
strokeColor: "#E44234",
|
|
272
416
|
color: "#E44234",
|
|
417
|
+
// deprecated alias
|
|
273
418
|
opacity: 1,
|
|
274
419
|
strokeWidth: 6
|
|
275
420
|
}
|
|
@@ -288,7 +433,9 @@ const defaultTools = [
|
|
|
288
433
|
defaults: {
|
|
289
434
|
type: PdfAnnotationSubtype.INK,
|
|
290
435
|
intent: "InkHighlight",
|
|
436
|
+
strokeColor: "#FFCD45",
|
|
291
437
|
color: "#FFCD45",
|
|
438
|
+
// deprecated alias
|
|
292
439
|
opacity: 1,
|
|
293
440
|
strokeWidth: 14,
|
|
294
441
|
blendMode: PdfBlendMode.Multiply
|
|
@@ -352,7 +499,10 @@ const defaultTools = [
|
|
|
352
499
|
cursor: "crosshair",
|
|
353
500
|
isDraggable: true,
|
|
354
501
|
isResizable: false,
|
|
355
|
-
|
|
502
|
+
// Uses vertex editing when selected individually
|
|
503
|
+
lockAspectRatio: false,
|
|
504
|
+
isGroupResizable: true
|
|
505
|
+
// Scales proportionally in a group
|
|
356
506
|
},
|
|
357
507
|
defaults: {
|
|
358
508
|
type: PdfAnnotationSubtype.LINE,
|
|
@@ -376,7 +526,10 @@ const defaultTools = [
|
|
|
376
526
|
cursor: "crosshair",
|
|
377
527
|
isDraggable: true,
|
|
378
528
|
isResizable: false,
|
|
379
|
-
|
|
529
|
+
// Uses vertex editing when selected individually
|
|
530
|
+
lockAspectRatio: false,
|
|
531
|
+
isGroupResizable: true
|
|
532
|
+
// Scales proportionally in a group
|
|
380
533
|
},
|
|
381
534
|
defaults: {
|
|
382
535
|
type: PdfAnnotationSubtype.LINE,
|
|
@@ -405,7 +558,10 @@ const defaultTools = [
|
|
|
405
558
|
cursor: "crosshair",
|
|
406
559
|
isDraggable: true,
|
|
407
560
|
isResizable: false,
|
|
408
|
-
|
|
561
|
+
// Uses vertex editing when selected individually
|
|
562
|
+
lockAspectRatio: false,
|
|
563
|
+
isGroupResizable: true
|
|
564
|
+
// Scales proportionally in a group
|
|
409
565
|
},
|
|
410
566
|
defaults: {
|
|
411
567
|
type: PdfAnnotationSubtype.POLYLINE,
|
|
@@ -424,7 +580,10 @@ const defaultTools = [
|
|
|
424
580
|
cursor: "crosshair",
|
|
425
581
|
isDraggable: true,
|
|
426
582
|
isResizable: false,
|
|
427
|
-
|
|
583
|
+
// Uses vertex editing when selected individually
|
|
584
|
+
lockAspectRatio: false,
|
|
585
|
+
isGroupResizable: true
|
|
586
|
+
// Scales proportionally in a group
|
|
428
587
|
},
|
|
429
588
|
defaults: {
|
|
430
589
|
type: PdfAnnotationSubtype.POLYGON,
|
|
@@ -454,7 +613,10 @@ const defaultTools = [
|
|
|
454
613
|
fontFamily: PdfStandardFont.Helvetica,
|
|
455
614
|
textAlign: PdfTextAlignment.Left,
|
|
456
615
|
verticalAlign: PdfVerticalAlignment.Top,
|
|
616
|
+
color: "transparent",
|
|
617
|
+
// fill color (matches shape convention)
|
|
457
618
|
backgroundColor: "transparent",
|
|
619
|
+
// deprecated alias
|
|
458
620
|
opacity: 1
|
|
459
621
|
},
|
|
460
622
|
clickBehavior: {
|
|
@@ -492,9 +654,11 @@ const DEFAULT_COLORS = [
|
|
|
492
654
|
"#000000",
|
|
493
655
|
"#FFFFFF"
|
|
494
656
|
];
|
|
657
|
+
const computeSelectedUid = (selectedUids) => selectedUids.length === 1 ? selectedUids[0] : null;
|
|
495
658
|
const initialDocumentState = () => ({
|
|
496
659
|
pages: {},
|
|
497
660
|
byUid: {},
|
|
661
|
+
selectedUids: [],
|
|
498
662
|
selectedUid: null,
|
|
499
663
|
activeToolId: null,
|
|
500
664
|
hasPendingChanges: false
|
|
@@ -595,7 +759,7 @@ const reducer = (state, action) => {
|
|
|
595
759
|
...state,
|
|
596
760
|
documents: {
|
|
597
761
|
...state.documents,
|
|
598
|
-
[documentId]: { ...docState, selectedUid: id }
|
|
762
|
+
[documentId]: { ...docState, selectedUids: [id], selectedUid: id }
|
|
599
763
|
}
|
|
600
764
|
};
|
|
601
765
|
}
|
|
@@ -607,7 +771,58 @@ const reducer = (state, action) => {
|
|
|
607
771
|
...state,
|
|
608
772
|
documents: {
|
|
609
773
|
...state.documents,
|
|
610
|
-
[documentId]: { ...docState, selectedUid: null }
|
|
774
|
+
[documentId]: { ...docState, selectedUids: [], selectedUid: null }
|
|
775
|
+
}
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
case ADD_TO_SELECTION: {
|
|
779
|
+
const { documentId, id } = action.payload;
|
|
780
|
+
const docState = state.documents[documentId];
|
|
781
|
+
if (!docState) return state;
|
|
782
|
+
if (docState.selectedUids.includes(id)) return state;
|
|
783
|
+
const newSelectedUids = [...docState.selectedUids, id];
|
|
784
|
+
return {
|
|
785
|
+
...state,
|
|
786
|
+
documents: {
|
|
787
|
+
...state.documents,
|
|
788
|
+
[documentId]: {
|
|
789
|
+
...docState,
|
|
790
|
+
selectedUids: newSelectedUids,
|
|
791
|
+
selectedUid: computeSelectedUid(newSelectedUids)
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
};
|
|
795
|
+
}
|
|
796
|
+
case REMOVE_FROM_SELECTION: {
|
|
797
|
+
const { documentId, id } = action.payload;
|
|
798
|
+
const docState = state.documents[documentId];
|
|
799
|
+
if (!docState) return state;
|
|
800
|
+
const newSelectedUids = docState.selectedUids.filter((uid) => uid !== id);
|
|
801
|
+
return {
|
|
802
|
+
...state,
|
|
803
|
+
documents: {
|
|
804
|
+
...state.documents,
|
|
805
|
+
[documentId]: {
|
|
806
|
+
...docState,
|
|
807
|
+
selectedUids: newSelectedUids,
|
|
808
|
+
selectedUid: computeSelectedUid(newSelectedUids)
|
|
809
|
+
}
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
case SET_SELECTION: {
|
|
814
|
+
const { documentId, ids } = action.payload;
|
|
815
|
+
const docState = state.documents[documentId];
|
|
816
|
+
if (!docState) return state;
|
|
817
|
+
return {
|
|
818
|
+
...state,
|
|
819
|
+
documents: {
|
|
820
|
+
...state.documents,
|
|
821
|
+
[documentId]: {
|
|
822
|
+
...docState,
|
|
823
|
+
selectedUids: ids,
|
|
824
|
+
selectedUid: computeSelectedUid(ids)
|
|
825
|
+
}
|
|
611
826
|
}
|
|
612
827
|
};
|
|
613
828
|
}
|
|
@@ -683,21 +898,30 @@ const reducer = (state, action) => {
|
|
|
683
898
|
};
|
|
684
899
|
}
|
|
685
900
|
case COMMIT_PENDING_CHANGES: {
|
|
686
|
-
const { documentId } = action.payload;
|
|
901
|
+
const { documentId, committedUids } = action.payload;
|
|
687
902
|
const docState = state.documents[documentId];
|
|
688
903
|
if (!docState) return state;
|
|
904
|
+
const committedSet = new Set(committedUids);
|
|
689
905
|
const cleaned = {};
|
|
906
|
+
let stillHasPending = false;
|
|
690
907
|
for (const [uid, ta] of Object.entries(docState.byUid)) {
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
908
|
+
if (committedSet.has(uid)) {
|
|
909
|
+
cleaned[uid] = {
|
|
910
|
+
...ta,
|
|
911
|
+
commitState: ta.commitState === "dirty" || ta.commitState === "new" ? "synced" : ta.commitState
|
|
912
|
+
};
|
|
913
|
+
} else {
|
|
914
|
+
cleaned[uid] = ta;
|
|
915
|
+
if (ta.commitState === "new" || ta.commitState === "dirty" || ta.commitState === "deleted") {
|
|
916
|
+
stillHasPending = true;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
695
919
|
}
|
|
696
920
|
return {
|
|
697
921
|
...state,
|
|
698
922
|
documents: {
|
|
699
923
|
...state.documents,
|
|
700
|
-
[documentId]: { ...docState, byUid: cleaned, hasPendingChanges:
|
|
924
|
+
[documentId]: { ...docState, byUid: cleaned, hasPendingChanges: stillHasPending }
|
|
701
925
|
}
|
|
702
926
|
};
|
|
703
927
|
}
|
|
@@ -763,7 +987,7 @@ const inkHandlerFactory = {
|
|
|
763
987
|
return {
|
|
764
988
|
...tool.defaults,
|
|
765
989
|
strokeWidth: tool.defaults.strokeWidth ?? 1,
|
|
766
|
-
|
|
990
|
+
strokeColor: tool.defaults.strokeColor ?? tool.defaults.color ?? "#000000",
|
|
767
991
|
opacity: tool.defaults.opacity ?? 1,
|
|
768
992
|
flags: tool.defaults.flags ?? ["print"]
|
|
769
993
|
};
|
|
@@ -899,7 +1123,7 @@ const freeTextHandlerFactory = {
|
|
|
899
1123
|
opacity: tool.defaults.opacity ?? 1,
|
|
900
1124
|
fontSize: tool.defaults.fontSize ?? 12,
|
|
901
1125
|
fontFamily: tool.defaults.fontFamily ?? PdfStandardFont.Helvetica,
|
|
902
|
-
|
|
1126
|
+
color: tool.defaults.color ?? tool.defaults.backgroundColor ?? "transparent",
|
|
903
1127
|
textAlign: tool.defaults.textAlign ?? PdfTextAlignment.Left,
|
|
904
1128
|
verticalAlign: tool.defaults.verticalAlign ?? PdfVerticalAlignment.Top,
|
|
905
1129
|
contents: tool.defaults.contents ?? "Insert text here",
|
|
@@ -1435,7 +1659,10 @@ const polylineHandlerFactory = {
|
|
|
1435
1659
|
};
|
|
1436
1660
|
};
|
|
1437
1661
|
return {
|
|
1438
|
-
onClick: (pos) => {
|
|
1662
|
+
onClick: (pos, evt) => {
|
|
1663
|
+
if (evt.metaKey || evt.ctrlKey) {
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1439
1666
|
const clampedPos = clampToPage(pos);
|
|
1440
1667
|
const vertices = getVertices();
|
|
1441
1668
|
const lastVertex = vertices[vertices.length - 1];
|
|
@@ -1537,7 +1764,10 @@ const polygonHandlerFactory = {
|
|
|
1537
1764
|
};
|
|
1538
1765
|
};
|
|
1539
1766
|
return {
|
|
1540
|
-
onClick: (pos) => {
|
|
1767
|
+
onClick: (pos, evt) => {
|
|
1768
|
+
if (evt.metaKey || evt.ctrlKey) {
|
|
1769
|
+
return;
|
|
1770
|
+
}
|
|
1541
1771
|
const clampedPos = clampToPage(pos);
|
|
1542
1772
|
if (isInsideStartHandle(clampedPos) && getVertices().length >= 3) {
|
|
1543
1773
|
commitPolygon();
|
|
@@ -2234,11 +2464,16 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2234
2464
|
this.pendingContexts = /* @__PURE__ */ new Map();
|
|
2235
2465
|
this.isInitialLoadComplete = /* @__PURE__ */ new Map();
|
|
2236
2466
|
this.importQueue = /* @__PURE__ */ new Map();
|
|
2467
|
+
this.commitInProgress = /* @__PURE__ */ new Map();
|
|
2237
2468
|
this.handlerFactories = /* @__PURE__ */ new Map();
|
|
2238
2469
|
this.activeTool$ = createBehaviorEmitter();
|
|
2239
2470
|
this.events$ = createBehaviorEmitter();
|
|
2240
2471
|
this.toolsChange$ = createBehaviorEmitter();
|
|
2241
2472
|
this.patchRegistry = new PatchRegistry();
|
|
2473
|
+
this.unifiedDragStates = /* @__PURE__ */ new Map();
|
|
2474
|
+
this.unifiedDrag$ = createBehaviorEmitter();
|
|
2475
|
+
this.unifiedResizeStates = /* @__PURE__ */ new Map();
|
|
2476
|
+
this.unifiedResize$ = createBehaviorEmitter();
|
|
2242
2477
|
this.config = config;
|
|
2243
2478
|
this.selection = ((_a = registry.getPlugin("selection")) == null ? void 0 : _a.provides()) ?? null;
|
|
2244
2479
|
this.history = ((_b = registry.getPlugin("history")) == null ? void 0 : _b.provides()) ?? null;
|
|
@@ -2268,7 +2503,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2268
2503
|
if (this.selection) {
|
|
2269
2504
|
for (const tool of this.state.tools) {
|
|
2270
2505
|
if (tool.interaction.textSelection) {
|
|
2271
|
-
this.selection.enableForMode(tool.interaction.mode ?? tool.id);
|
|
2506
|
+
this.selection.enableForMode(tool.interaction.mode ?? tool.id, { showRects: false });
|
|
2272
2507
|
}
|
|
2273
2508
|
}
|
|
2274
2509
|
}
|
|
@@ -2301,7 +2536,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2301
2536
|
this.patchRegistry.register(PdfAnnotationSubtype.POLYGON, patchPolygon);
|
|
2302
2537
|
}
|
|
2303
2538
|
async initialize() {
|
|
2304
|
-
var _a, _b;
|
|
2539
|
+
var _a, _b, _c;
|
|
2305
2540
|
this.state.tools.forEach((tool) => this.registerInteractionForTool(tool));
|
|
2306
2541
|
if (this.history) {
|
|
2307
2542
|
this.history.onHistoryChange((event) => {
|
|
@@ -2318,8 +2553,31 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2318
2553
|
this.dispatch(setActiveToolId(s.documentId, newToolId));
|
|
2319
2554
|
}
|
|
2320
2555
|
});
|
|
2321
|
-
(_b = this.selection) == null ? void 0 : _b.
|
|
2322
|
-
|
|
2556
|
+
(_b = this.selection) == null ? void 0 : _b.onMarqueeEnd(({ documentId, pageIndex, rect }) => {
|
|
2557
|
+
const docState = this.state.documents[documentId];
|
|
2558
|
+
if (!docState) return;
|
|
2559
|
+
const pageAnnotations = (docState.pages[pageIndex] ?? []).map((uid) => docState.byUid[uid]).filter((ta) => ta !== void 0).filter((ta) => !isLink(ta));
|
|
2560
|
+
const selectedIds = pageAnnotations.filter((ta) => rectsIntersect(rect, ta.object.rect)).map((ta) => ta.object.id);
|
|
2561
|
+
if (selectedIds.length > 0) {
|
|
2562
|
+
const expandedIds = /* @__PURE__ */ new Set();
|
|
2563
|
+
for (const id of selectedIds) {
|
|
2564
|
+
if (this.isInGroupMethod(id, documentId)) {
|
|
2565
|
+
const members = this.getGroupMembersMethod(id, documentId);
|
|
2566
|
+
for (const member of members) {
|
|
2567
|
+
expandedIds.add(member.object.id);
|
|
2568
|
+
}
|
|
2569
|
+
} else {
|
|
2570
|
+
expandedIds.add(id);
|
|
2571
|
+
}
|
|
2572
|
+
}
|
|
2573
|
+
this.setSelectionMethod([...expandedIds], documentId);
|
|
2574
|
+
}
|
|
2575
|
+
});
|
|
2576
|
+
(_c = this.selection) == null ? void 0 : _c.onEndSelection(({ documentId }) => {
|
|
2577
|
+
var _a2, _b2, _c2;
|
|
2578
|
+
if (!this.checkPermission(documentId, PdfPermissionFlag.ModifyAnnotations)) {
|
|
2579
|
+
return;
|
|
2580
|
+
}
|
|
2323
2581
|
const activeTool = this.getActiveTool(documentId);
|
|
2324
2582
|
if (!activeTool || !activeTool.interaction.textSelection) return;
|
|
2325
2583
|
const formattedSelection = (_a2 = this.selection) == null ? void 0 : _a2.getFormattedSelection();
|
|
@@ -2352,7 +2610,7 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2352
2610
|
}
|
|
2353
2611
|
}, ignore);
|
|
2354
2612
|
}
|
|
2355
|
-
(
|
|
2613
|
+
(_c2 = this.selection) == null ? void 0 : _c2.clear();
|
|
2356
2614
|
});
|
|
2357
2615
|
}
|
|
2358
2616
|
registerInteractionForTool(tool) {
|
|
@@ -2372,15 +2630,32 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2372
2630
|
getState: () => this.getDocumentState(),
|
|
2373
2631
|
getPageAnnotations: (options) => this.getPageAnnotations(options),
|
|
2374
2632
|
getSelectedAnnotation: () => this.getSelectedAnnotation(),
|
|
2633
|
+
getSelectedAnnotations: () => this.getSelectedAnnotationsMethod(),
|
|
2634
|
+
getSelectedAnnotationIds: () => this.getSelectedAnnotationIdsMethod(),
|
|
2375
2635
|
getAnnotationById: (id) => this.getAnnotationById(id),
|
|
2376
2636
|
selectAnnotation: (pageIndex, id) => this.selectAnnotation(pageIndex, id),
|
|
2637
|
+
toggleSelection: (pageIndex, id) => this.toggleSelectionMethod(pageIndex, id),
|
|
2638
|
+
addToSelection: (pageIndex, id) => this.addToSelectionMethod(pageIndex, id),
|
|
2639
|
+
removeFromSelection: (id) => this.removeFromSelectionMethod(id),
|
|
2640
|
+
setSelection: (ids) => this.setSelectionMethod(ids),
|
|
2377
2641
|
deselectAnnotation: () => this.deselectAnnotation(),
|
|
2378
2642
|
importAnnotations: (items) => this.importAnnotations(items),
|
|
2379
2643
|
createAnnotation: (pageIndex, anno, ctx) => this.createAnnotation(pageIndex, anno, ctx),
|
|
2380
2644
|
updateAnnotation: (pageIndex, id, patch) => this.updateAnnotation(pageIndex, id, patch),
|
|
2645
|
+
updateAnnotations: (patches) => this.updateAnnotationsMethod(patches),
|
|
2381
2646
|
deleteAnnotation: (pageIndex, id) => this.deleteAnnotation(pageIndex, id),
|
|
2647
|
+
deleteAnnotations: (annotations, documentId) => this.deleteAnnotationsMethod(annotations, documentId),
|
|
2382
2648
|
renderAnnotation: (options) => this.renderAnnotation(options),
|
|
2383
2649
|
commit: () => this.commit(),
|
|
2650
|
+
// Attached links (IRT link children)
|
|
2651
|
+
getAttachedLinks: (id, documentId) => this.getAttachedLinksMethod(id, documentId),
|
|
2652
|
+
hasAttachedLinks: (id, documentId) => this.hasAttachedLinksMethod(id, documentId),
|
|
2653
|
+
deleteAttachedLinks: (id, documentId) => this.deleteAttachedLinksMethod(id, documentId),
|
|
2654
|
+
// Annotation grouping (RT = Group)
|
|
2655
|
+
groupAnnotations: (documentId) => this.groupAnnotationsMethod(documentId),
|
|
2656
|
+
ungroupAnnotations: (id, documentId) => this.ungroupAnnotationsMethod(id, documentId),
|
|
2657
|
+
getGroupMembers: (id, documentId) => this.getGroupMembersMethod(id, documentId),
|
|
2658
|
+
isInGroup: (id, documentId) => this.isInGroupMethod(id, documentId),
|
|
2384
2659
|
// Document-scoped operations
|
|
2385
2660
|
forDocument: (documentId) => this.createAnnotationScope(documentId),
|
|
2386
2661
|
// Global operations
|
|
@@ -2411,8 +2686,14 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2411
2686
|
getState: () => this.getDocumentState(documentId),
|
|
2412
2687
|
getPageAnnotations: (options) => this.getPageAnnotations(options, documentId),
|
|
2413
2688
|
getSelectedAnnotation: () => this.getSelectedAnnotation(documentId),
|
|
2689
|
+
getSelectedAnnotations: () => this.getSelectedAnnotationsMethod(documentId),
|
|
2690
|
+
getSelectedAnnotationIds: () => this.getSelectedAnnotationIdsMethod(documentId),
|
|
2414
2691
|
getAnnotationById: (id) => this.getAnnotationById(id, documentId),
|
|
2415
2692
|
selectAnnotation: (pageIndex, id) => this.selectAnnotation(pageIndex, id, documentId),
|
|
2693
|
+
toggleSelection: (pageIndex, id) => this.toggleSelectionMethod(pageIndex, id, documentId),
|
|
2694
|
+
addToSelection: (pageIndex, id) => this.addToSelectionMethod(pageIndex, id, documentId),
|
|
2695
|
+
removeFromSelection: (id) => this.removeFromSelectionMethod(id, documentId),
|
|
2696
|
+
setSelection: (ids) => this.setSelectionMethod(ids, documentId),
|
|
2416
2697
|
deselectAnnotation: () => this.deselectAnnotation(documentId),
|
|
2417
2698
|
getActiveTool: () => this.getActiveTool(documentId),
|
|
2418
2699
|
setActiveTool: (toolId) => this.setActiveTool(toolId, documentId),
|
|
@@ -2420,9 +2701,19 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2420
2701
|
importAnnotations: (items) => this.importAnnotations(items, documentId),
|
|
2421
2702
|
createAnnotation: (pageIndex, anno, ctx) => this.createAnnotation(pageIndex, anno, ctx, documentId),
|
|
2422
2703
|
updateAnnotation: (pageIndex, id, patch) => this.updateAnnotation(pageIndex, id, patch, documentId),
|
|
2704
|
+
updateAnnotations: (patches) => this.updateAnnotationsMethod(patches, documentId),
|
|
2423
2705
|
deleteAnnotation: (pageIndex, id) => this.deleteAnnotation(pageIndex, id, documentId),
|
|
2706
|
+
deleteAnnotations: (annotations) => this.deleteAnnotationsMethod(annotations, documentId),
|
|
2424
2707
|
renderAnnotation: (options) => this.renderAnnotation(options, documentId),
|
|
2425
2708
|
commit: () => this.commit(documentId),
|
|
2709
|
+
getAttachedLinks: (id) => this.getAttachedLinksMethod(id, documentId),
|
|
2710
|
+
hasAttachedLinks: (id) => this.hasAttachedLinksMethod(id, documentId),
|
|
2711
|
+
deleteAttachedLinks: (id) => this.deleteAttachedLinksMethod(id, documentId),
|
|
2712
|
+
groupAnnotations: () => this.groupAnnotationsMethod(documentId),
|
|
2713
|
+
ungroupAnnotations: (id) => this.ungroupAnnotationsMethod(id, documentId),
|
|
2714
|
+
getGroupMembers: (id) => this.getGroupMembersMethod(id, documentId),
|
|
2715
|
+
isInGroup: (id) => this.isInGroupMethod(id, documentId),
|
|
2716
|
+
getGroupingAction: () => this.getGroupingActionMethod(documentId),
|
|
2426
2717
|
onStateChange: (listener) => this.state$.on((event) => {
|
|
2427
2718
|
if (event.documentId === documentId) listener(event.state);
|
|
2428
2719
|
}),
|
|
@@ -2609,8 +2900,16 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2609
2900
|
if (this.config.autoCommit !== false) this.commit(documentId);
|
|
2610
2901
|
}
|
|
2611
2902
|
createAnnotation(pageIndex, annotation, ctx, documentId) {
|
|
2612
|
-
const id = annotation.id;
|
|
2613
2903
|
const docId = documentId ?? this.getActiveDocumentId();
|
|
2904
|
+
if (!this.checkPermission(docId, PdfPermissionFlag.ModifyAnnotations)) {
|
|
2905
|
+
this.logger.debug(
|
|
2906
|
+
"AnnotationPlugin",
|
|
2907
|
+
"CreateAnnotation",
|
|
2908
|
+
`Cannot create annotation: document ${docId} lacks ModifyAnnotations permission`
|
|
2909
|
+
);
|
|
2910
|
+
return;
|
|
2911
|
+
}
|
|
2912
|
+
const id = annotation.id;
|
|
2614
2913
|
const contexts = this.pendingContexts.get(docId);
|
|
2615
2914
|
if (!contexts) return;
|
|
2616
2915
|
const newAnnotation = {
|
|
@@ -2661,6 +2960,14 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2661
2960
|
}
|
|
2662
2961
|
updateAnnotation(pageIndex, id, patch, documentId) {
|
|
2663
2962
|
const docId = documentId ?? this.getActiveDocumentId();
|
|
2963
|
+
if (!this.checkPermission(docId, PdfPermissionFlag.ModifyAnnotations)) {
|
|
2964
|
+
this.logger.debug(
|
|
2965
|
+
"AnnotationPlugin",
|
|
2966
|
+
"UpdateAnnotation",
|
|
2967
|
+
`Cannot update annotation: document ${docId} lacks ModifyAnnotations permission`
|
|
2968
|
+
);
|
|
2969
|
+
return;
|
|
2970
|
+
}
|
|
2664
2971
|
const docState = this.getDocumentState(docId);
|
|
2665
2972
|
const originalObject = docState.byUid[id].object;
|
|
2666
2973
|
const finalPatch = this.buildPatch(originalObject, {
|
|
@@ -2708,10 +3015,37 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2708
3015
|
deleteAnnotation(pageIndex, id, documentId) {
|
|
2709
3016
|
var _a;
|
|
2710
3017
|
const docId = documentId ?? this.getActiveDocumentId();
|
|
3018
|
+
if (!this.checkPermission(docId, PdfPermissionFlag.ModifyAnnotations)) {
|
|
3019
|
+
this.logger.debug(
|
|
3020
|
+
"AnnotationPlugin",
|
|
3021
|
+
"DeleteAnnotation",
|
|
3022
|
+
`Cannot delete annotation: document ${docId} lacks ModifyAnnotations permission`
|
|
3023
|
+
);
|
|
3024
|
+
return;
|
|
3025
|
+
}
|
|
2711
3026
|
const docState = this.getDocumentState(docId);
|
|
2712
3027
|
const originalAnnotation = (_a = docState.byUid[id]) == null ? void 0 : _a.object;
|
|
2713
3028
|
if (!originalAnnotation) return;
|
|
3029
|
+
const irtChildren = getIRTChildIds(docState, id);
|
|
3030
|
+
const childAnnotations = irtChildren.map((child) => {
|
|
3031
|
+
var _a2;
|
|
3032
|
+
return (_a2 = docState.byUid[child.id]) == null ? void 0 : _a2.object;
|
|
3033
|
+
}).filter((obj) => obj !== void 0);
|
|
2714
3034
|
const execute = () => {
|
|
3035
|
+
var _a2;
|
|
3036
|
+
for (const child of irtChildren) {
|
|
3037
|
+
const childObj = (_a2 = docState.byUid[child.id]) == null ? void 0 : _a2.object;
|
|
3038
|
+
if (childObj) {
|
|
3039
|
+
this.dispatch(deleteAnnotation(docId, child.pageIndex, child.id));
|
|
3040
|
+
this.events$.emit({
|
|
3041
|
+
type: "delete",
|
|
3042
|
+
documentId: docId,
|
|
3043
|
+
annotation: childObj,
|
|
3044
|
+
pageIndex: child.pageIndex,
|
|
3045
|
+
committed: false
|
|
3046
|
+
});
|
|
3047
|
+
}
|
|
3048
|
+
}
|
|
2715
3049
|
this.dispatch(deselectAnnotation(docId));
|
|
2716
3050
|
this.dispatch(deleteAnnotation(docId, pageIndex, id));
|
|
2717
3051
|
this.events$.emit({
|
|
@@ -2738,19 +3072,703 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2738
3072
|
pageIndex,
|
|
2739
3073
|
committed: false
|
|
2740
3074
|
});
|
|
3075
|
+
for (const childObj of childAnnotations) {
|
|
3076
|
+
this.dispatch(createAnnotation(docId, childObj.pageIndex, childObj));
|
|
3077
|
+
this.events$.emit({
|
|
3078
|
+
type: "create",
|
|
3079
|
+
documentId: docId,
|
|
3080
|
+
annotation: childObj,
|
|
3081
|
+
pageIndex: childObj.pageIndex,
|
|
3082
|
+
committed: false
|
|
3083
|
+
});
|
|
3084
|
+
}
|
|
2741
3085
|
}
|
|
2742
3086
|
};
|
|
2743
3087
|
const historyScope = this.history.forDocument(docId);
|
|
2744
3088
|
historyScope.register(command, this.ANNOTATION_HISTORY_TOPIC);
|
|
2745
3089
|
}
|
|
3090
|
+
deleteAnnotationsMethod(annotations, documentId) {
|
|
3091
|
+
for (const { pageIndex, id } of annotations) {
|
|
3092
|
+
this.deleteAnnotation(pageIndex, id, documentId);
|
|
3093
|
+
}
|
|
3094
|
+
}
|
|
2746
3095
|
selectAnnotation(pageIndex, id, documentId) {
|
|
2747
3096
|
const docId = documentId ?? this.getActiveDocumentId();
|
|
2748
|
-
this.
|
|
3097
|
+
if (this.isInGroupMethod(id, docId)) {
|
|
3098
|
+
const members = this.getGroupMembersMethod(id, docId);
|
|
3099
|
+
const memberIds = members.map((m) => m.object.id);
|
|
3100
|
+
this.dispatch(setSelection(docId, memberIds));
|
|
3101
|
+
} else {
|
|
3102
|
+
this.dispatch(selectAnnotation(docId, pageIndex, id));
|
|
3103
|
+
}
|
|
2749
3104
|
}
|
|
2750
3105
|
deselectAnnotation(documentId) {
|
|
2751
3106
|
const docId = documentId ?? this.getActiveDocumentId();
|
|
2752
3107
|
this.dispatch(deselectAnnotation(docId));
|
|
2753
3108
|
}
|
|
3109
|
+
// ─────────────────────────────────────────────────────────
|
|
3110
|
+
// Multi-Select Methods
|
|
3111
|
+
// ─────────────────────────────────────────────────────────
|
|
3112
|
+
getSelectedAnnotationsMethod(documentId) {
|
|
3113
|
+
return getSelectedAnnotations(this.getDocumentState(documentId));
|
|
3114
|
+
}
|
|
3115
|
+
getSelectedAnnotationIdsMethod(documentId) {
|
|
3116
|
+
return getSelectedAnnotationIds(this.getDocumentState(documentId));
|
|
3117
|
+
}
|
|
3118
|
+
toggleSelectionMethod(pageIndex, id, documentId) {
|
|
3119
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3120
|
+
const docState = this.getDocumentState(docId);
|
|
3121
|
+
if (docState.selectedUids.includes(id)) {
|
|
3122
|
+
this.dispatch(removeFromSelection(docId, id));
|
|
3123
|
+
} else {
|
|
3124
|
+
if (this.isInGroupMethod(id, docId)) {
|
|
3125
|
+
const members = this.getGroupMembersMethod(id, docId);
|
|
3126
|
+
for (const member of members) {
|
|
3127
|
+
if (!docState.selectedUids.includes(member.object.id)) {
|
|
3128
|
+
this.dispatch(addToSelection(docId, member.object.pageIndex, member.object.id));
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
} else {
|
|
3132
|
+
this.dispatch(addToSelection(docId, pageIndex, id));
|
|
3133
|
+
}
|
|
3134
|
+
}
|
|
3135
|
+
}
|
|
3136
|
+
addToSelectionMethod(pageIndex, id, documentId) {
|
|
3137
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3138
|
+
this.dispatch(addToSelection(docId, pageIndex, id));
|
|
3139
|
+
}
|
|
3140
|
+
removeFromSelectionMethod(id, documentId) {
|
|
3141
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3142
|
+
this.dispatch(removeFromSelection(docId, id));
|
|
3143
|
+
}
|
|
3144
|
+
setSelectionMethod(ids, documentId) {
|
|
3145
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3146
|
+
this.dispatch(setSelection(docId, ids));
|
|
3147
|
+
}
|
|
3148
|
+
// ─────────────────────────────────────────────────────────
|
|
3149
|
+
// Attached Links Methods
|
|
3150
|
+
// ─────────────────────────────────────────────────────────
|
|
3151
|
+
getAttachedLinksMethod(annotationId, documentId) {
|
|
3152
|
+
return getAttachedLinks(this.getDocumentState(documentId), annotationId);
|
|
3153
|
+
}
|
|
3154
|
+
hasAttachedLinksMethod(annotationId, documentId) {
|
|
3155
|
+
return this.getAttachedLinksMethod(annotationId, documentId).length > 0;
|
|
3156
|
+
}
|
|
3157
|
+
deleteAttachedLinksMethod(annotationId, documentId) {
|
|
3158
|
+
const links = this.getAttachedLinksMethod(annotationId, documentId);
|
|
3159
|
+
for (const link of links) {
|
|
3160
|
+
this.deleteAnnotation(link.object.pageIndex, link.object.id, documentId);
|
|
3161
|
+
}
|
|
3162
|
+
}
|
|
3163
|
+
// ─────────────────────────────────────────────────────────
|
|
3164
|
+
// Annotation Grouping Methods
|
|
3165
|
+
// ─────────────────────────────────────────────────────────
|
|
3166
|
+
/**
|
|
3167
|
+
* Group the currently selected annotations.
|
|
3168
|
+
* The first selected annotation becomes the group leader.
|
|
3169
|
+
* All other selected annotations get their IRT set to the leader's ID with RT = Group.
|
|
3170
|
+
*/
|
|
3171
|
+
groupAnnotationsMethod(documentId) {
|
|
3172
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3173
|
+
if (!this.checkPermission(docId, PdfPermissionFlag.ModifyAnnotations)) {
|
|
3174
|
+
this.logger.debug(
|
|
3175
|
+
"AnnotationPlugin",
|
|
3176
|
+
"GroupAnnotations",
|
|
3177
|
+
`Cannot group annotations: document ${docId} lacks ModifyAnnotations permission`
|
|
3178
|
+
);
|
|
3179
|
+
return;
|
|
3180
|
+
}
|
|
3181
|
+
const selected = this.getSelectedAnnotationsMethod(docId);
|
|
3182
|
+
if (selected.length < 2) {
|
|
3183
|
+
this.logger.debug(
|
|
3184
|
+
"AnnotationPlugin",
|
|
3185
|
+
"GroupAnnotations",
|
|
3186
|
+
"Need at least 2 annotations to group"
|
|
3187
|
+
);
|
|
3188
|
+
return;
|
|
3189
|
+
}
|
|
3190
|
+
const leader = selected[0];
|
|
3191
|
+
const members = selected.slice(1);
|
|
3192
|
+
const patches = members.map((ta) => ({
|
|
3193
|
+
pageIndex: ta.object.pageIndex,
|
|
3194
|
+
id: ta.object.id,
|
|
3195
|
+
patch: {
|
|
3196
|
+
inReplyToId: leader.object.id,
|
|
3197
|
+
replyType: PdfAnnotationReplyType.Group
|
|
3198
|
+
}
|
|
3199
|
+
}));
|
|
3200
|
+
this.updateAnnotationsMethod(patches, docId);
|
|
3201
|
+
}
|
|
3202
|
+
/**
|
|
3203
|
+
* Ungroup all annotations in the group containing the specified annotation.
|
|
3204
|
+
* Clears IRT and RT from all group members (the leader doesn't have them).
|
|
3205
|
+
*/
|
|
3206
|
+
ungroupAnnotationsMethod(annotationId, documentId) {
|
|
3207
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3208
|
+
if (!this.checkPermission(docId, PdfPermissionFlag.ModifyAnnotations)) {
|
|
3209
|
+
this.logger.debug(
|
|
3210
|
+
"AnnotationPlugin",
|
|
3211
|
+
"UngroupAnnotations",
|
|
3212
|
+
`Cannot ungroup annotations: document ${docId} lacks ModifyAnnotations permission`
|
|
3213
|
+
);
|
|
3214
|
+
return;
|
|
3215
|
+
}
|
|
3216
|
+
const members = this.getGroupMembersMethod(annotationId, docId);
|
|
3217
|
+
const patches = members.filter((ta) => ta.object.inReplyToId && ta.object.replyType === PdfAnnotationReplyType.Group).map((ta) => ({
|
|
3218
|
+
pageIndex: ta.object.pageIndex,
|
|
3219
|
+
id: ta.object.id,
|
|
3220
|
+
patch: {
|
|
3221
|
+
inReplyToId: void 0,
|
|
3222
|
+
replyType: void 0
|
|
3223
|
+
}
|
|
3224
|
+
}));
|
|
3225
|
+
if (patches.length > 0) {
|
|
3226
|
+
this.updateAnnotationsMethod(patches, docId);
|
|
3227
|
+
}
|
|
3228
|
+
}
|
|
3229
|
+
/**
|
|
3230
|
+
* Get all annotations in the same group as the specified annotation.
|
|
3231
|
+
*/
|
|
3232
|
+
getGroupMembersMethod(annotationId, documentId) {
|
|
3233
|
+
return getGroupMembers(this.getDocumentState(documentId), annotationId);
|
|
3234
|
+
}
|
|
3235
|
+
/**
|
|
3236
|
+
* Check if an annotation is part of a group.
|
|
3237
|
+
*/
|
|
3238
|
+
isInGroupMethod(annotationId, documentId) {
|
|
3239
|
+
return isInGroup(this.getDocumentState(documentId), annotationId);
|
|
3240
|
+
}
|
|
3241
|
+
/**
|
|
3242
|
+
* Get the available grouping action for the current selection.
|
|
3243
|
+
*/
|
|
3244
|
+
getGroupingActionMethod(documentId) {
|
|
3245
|
+
return getSelectionGroupingAction(this.getDocumentState(documentId));
|
|
3246
|
+
}
|
|
3247
|
+
// ─────────────────────────────────────────────────────────
|
|
3248
|
+
// Multi-Drag Coordination (Internal API for framework components)
|
|
3249
|
+
// ─────────────────────────────────────────────────────────
|
|
3250
|
+
/**
|
|
3251
|
+
* Compute combined constraints from all selected annotations.
|
|
3252
|
+
* This finds the "weakest link" in each direction - the annotation with the least
|
|
3253
|
+
* room to move determines the group's limit.
|
|
3254
|
+
*/
|
|
3255
|
+
computeCombinedConstraints(annotations) {
|
|
3256
|
+
let maxUp = Infinity;
|
|
3257
|
+
let maxDown = Infinity;
|
|
3258
|
+
let maxLeft = Infinity;
|
|
3259
|
+
let maxRight = Infinity;
|
|
3260
|
+
for (const anno of annotations) {
|
|
3261
|
+
const upLimit = anno.rect.origin.y;
|
|
3262
|
+
const downLimit = anno.pageSize.height - (anno.rect.origin.y + anno.rect.size.height);
|
|
3263
|
+
const leftLimit = anno.rect.origin.x;
|
|
3264
|
+
const rightLimit = anno.pageSize.width - (anno.rect.origin.x + anno.rect.size.width);
|
|
3265
|
+
maxUp = Math.min(maxUp, upLimit);
|
|
3266
|
+
maxDown = Math.min(maxDown, downLimit);
|
|
3267
|
+
maxLeft = Math.min(maxLeft, leftLimit);
|
|
3268
|
+
maxRight = Math.min(maxRight, rightLimit);
|
|
3269
|
+
}
|
|
3270
|
+
if (!isFinite(maxUp)) maxUp = 0;
|
|
3271
|
+
if (!isFinite(maxDown)) maxDown = 0;
|
|
3272
|
+
if (!isFinite(maxLeft)) maxLeft = 0;
|
|
3273
|
+
if (!isFinite(maxRight)) maxRight = 0;
|
|
3274
|
+
return { maxUp, maxDown, maxLeft, maxRight };
|
|
3275
|
+
}
|
|
3276
|
+
/**
|
|
3277
|
+
* Clamp a delta to the combined constraints.
|
|
3278
|
+
* Negative y = moving up, positive y = moving down
|
|
3279
|
+
* Negative x = moving left, positive x = moving right
|
|
3280
|
+
*/
|
|
3281
|
+
clampDelta(rawDelta, constraints) {
|
|
3282
|
+
return {
|
|
3283
|
+
x: Math.max(-constraints.maxLeft, Math.min(constraints.maxRight, rawDelta.x)),
|
|
3284
|
+
y: Math.max(-constraints.maxUp, Math.min(constraints.maxDown, rawDelta.y))
|
|
3285
|
+
};
|
|
3286
|
+
}
|
|
3287
|
+
// ─────────────────────────────────────────────────────────
|
|
3288
|
+
// Unified Drag API (Plugin owns all logic - framework just calls these)
|
|
3289
|
+
// ─────────────────────────────────────────────────────────
|
|
3290
|
+
/**
|
|
3291
|
+
* Start a unified drag operation.
|
|
3292
|
+
* The plugin automatically expands the selection to include attached links.
|
|
3293
|
+
* Framework components should call this instead of building their own logic.
|
|
3294
|
+
*
|
|
3295
|
+
* @param documentId - The document ID
|
|
3296
|
+
* @param options - Drag options (annotationIds and pageSize)
|
|
3297
|
+
*/
|
|
3298
|
+
startDrag(documentId, options) {
|
|
3299
|
+
const { annotationIds, pageSize } = options;
|
|
3300
|
+
const attachedLinkIds = [];
|
|
3301
|
+
for (const id of annotationIds) {
|
|
3302
|
+
const links = this.getAttachedLinksMethod(id, documentId);
|
|
3303
|
+
for (const link of links) {
|
|
3304
|
+
if (!attachedLinkIds.includes(link.object.id)) {
|
|
3305
|
+
attachedLinkIds.push(link.object.id);
|
|
3306
|
+
}
|
|
3307
|
+
}
|
|
3308
|
+
}
|
|
3309
|
+
const allParticipantIds = [...annotationIds, ...attachedLinkIds];
|
|
3310
|
+
const originalRects = /* @__PURE__ */ new Map();
|
|
3311
|
+
const constraints = [];
|
|
3312
|
+
for (const id of allParticipantIds) {
|
|
3313
|
+
const ta = this.getAnnotationById(id, documentId);
|
|
3314
|
+
if (ta) {
|
|
3315
|
+
originalRects.set(id, { ...ta.object.rect });
|
|
3316
|
+
constraints.push({
|
|
3317
|
+
id,
|
|
3318
|
+
rect: ta.object.rect,
|
|
3319
|
+
pageIndex: ta.object.pageIndex,
|
|
3320
|
+
pageSize
|
|
3321
|
+
});
|
|
3322
|
+
}
|
|
3323
|
+
}
|
|
3324
|
+
const combinedConstraints = this.computeCombinedConstraints(constraints);
|
|
3325
|
+
const state = {
|
|
3326
|
+
documentId,
|
|
3327
|
+
isDragging: true,
|
|
3328
|
+
primaryIds: annotationIds,
|
|
3329
|
+
attachedLinkIds,
|
|
3330
|
+
allParticipantIds,
|
|
3331
|
+
originalRects,
|
|
3332
|
+
delta: { x: 0, y: 0 },
|
|
3333
|
+
combinedConstraints
|
|
3334
|
+
};
|
|
3335
|
+
this.unifiedDragStates.set(documentId, state);
|
|
3336
|
+
this.unifiedDrag$.emit({ documentId, type: "start", state, previewPatches: {} });
|
|
3337
|
+
}
|
|
3338
|
+
/**
|
|
3339
|
+
* Compute preview patches for all drag participants.
|
|
3340
|
+
* Uses transformAnnotation to properly handle vertices, inkList, etc.
|
|
3341
|
+
*/
|
|
3342
|
+
computeDragPreviewPatches(state, documentId) {
|
|
3343
|
+
const previewPatches = {};
|
|
3344
|
+
for (const id of state.allParticipantIds) {
|
|
3345
|
+
const ta = this.getAnnotationById(id, documentId);
|
|
3346
|
+
if (!ta) continue;
|
|
3347
|
+
const originalRect = state.originalRects.get(id);
|
|
3348
|
+
if (!originalRect) continue;
|
|
3349
|
+
const newRect = {
|
|
3350
|
+
...originalRect,
|
|
3351
|
+
origin: {
|
|
3352
|
+
x: originalRect.origin.x + state.delta.x,
|
|
3353
|
+
y: originalRect.origin.y + state.delta.y
|
|
3354
|
+
}
|
|
3355
|
+
};
|
|
3356
|
+
previewPatches[id] = this.transformAnnotation(ta.object, {
|
|
3357
|
+
type: "move",
|
|
3358
|
+
changes: { rect: newRect }
|
|
3359
|
+
});
|
|
3360
|
+
}
|
|
3361
|
+
return previewPatches;
|
|
3362
|
+
}
|
|
3363
|
+
/**
|
|
3364
|
+
* Update the drag delta during a unified drag operation.
|
|
3365
|
+
* Returns the clamped delta synchronously for the caller's preview.
|
|
3366
|
+
*
|
|
3367
|
+
* @param documentId - The document ID
|
|
3368
|
+
* @param rawDelta - The unconstrained delta from the drag gesture
|
|
3369
|
+
* @returns The clamped delta
|
|
3370
|
+
*/
|
|
3371
|
+
updateDrag(documentId, rawDelta) {
|
|
3372
|
+
const state = this.unifiedDragStates.get(documentId);
|
|
3373
|
+
if (!(state == null ? void 0 : state.isDragging)) {
|
|
3374
|
+
return { x: 0, y: 0 };
|
|
3375
|
+
}
|
|
3376
|
+
const clampedDelta = this.clampDelta(rawDelta, state.combinedConstraints);
|
|
3377
|
+
const newState = {
|
|
3378
|
+
...state,
|
|
3379
|
+
delta: clampedDelta
|
|
3380
|
+
};
|
|
3381
|
+
this.unifiedDragStates.set(documentId, newState);
|
|
3382
|
+
const previewPatches = this.computeDragPreviewPatches(newState, documentId);
|
|
3383
|
+
this.unifiedDrag$.emit({ documentId, type: "update", state: newState, previewPatches });
|
|
3384
|
+
return clampedDelta;
|
|
3385
|
+
}
|
|
3386
|
+
/**
|
|
3387
|
+
* Commit the drag - plugin builds and applies ALL patches.
|
|
3388
|
+
* This is the key method that centralizes patch building in the plugin.
|
|
3389
|
+
*
|
|
3390
|
+
* @param documentId - The document ID
|
|
3391
|
+
*/
|
|
3392
|
+
commitDrag(documentId) {
|
|
3393
|
+
const state = this.unifiedDragStates.get(documentId);
|
|
3394
|
+
if (!state) return;
|
|
3395
|
+
const finalDelta = state.delta;
|
|
3396
|
+
if (finalDelta.x !== 0 || finalDelta.y !== 0) {
|
|
3397
|
+
const patches = [];
|
|
3398
|
+
for (const id of state.allParticipantIds) {
|
|
3399
|
+
const ta = this.getAnnotationById(id, documentId);
|
|
3400
|
+
if (!ta) continue;
|
|
3401
|
+
const originalRect = state.originalRects.get(id) ?? ta.object.rect;
|
|
3402
|
+
const newRect = {
|
|
3403
|
+
...originalRect,
|
|
3404
|
+
origin: {
|
|
3405
|
+
x: originalRect.origin.x + finalDelta.x,
|
|
3406
|
+
y: originalRect.origin.y + finalDelta.y
|
|
3407
|
+
}
|
|
3408
|
+
};
|
|
3409
|
+
const patch = this.transformAnnotation(ta.object, {
|
|
3410
|
+
type: "move",
|
|
3411
|
+
changes: { rect: newRect }
|
|
3412
|
+
});
|
|
3413
|
+
patches.push({ pageIndex: ta.object.pageIndex, id, patch });
|
|
3414
|
+
}
|
|
3415
|
+
if (patches.length > 0) {
|
|
3416
|
+
this.updateAnnotationsMethod(patches, documentId);
|
|
3417
|
+
}
|
|
3418
|
+
}
|
|
3419
|
+
const endPatches = this.computeDragPreviewPatches(state, documentId);
|
|
3420
|
+
this.unifiedDrag$.emit({
|
|
3421
|
+
documentId,
|
|
3422
|
+
type: "end",
|
|
3423
|
+
state: { ...state, isDragging: false },
|
|
3424
|
+
previewPatches: endPatches
|
|
3425
|
+
});
|
|
3426
|
+
this.unifiedDragStates.delete(documentId);
|
|
3427
|
+
}
|
|
3428
|
+
/**
|
|
3429
|
+
* Cancel the drag without committing.
|
|
3430
|
+
*
|
|
3431
|
+
* @param documentId - The document ID
|
|
3432
|
+
*/
|
|
3433
|
+
cancelDrag(documentId) {
|
|
3434
|
+
const state = this.unifiedDragStates.get(documentId);
|
|
3435
|
+
if (!state) return;
|
|
3436
|
+
this.unifiedDrag$.emit({
|
|
3437
|
+
documentId,
|
|
3438
|
+
type: "cancel",
|
|
3439
|
+
state: { ...state, isDragging: false, delta: { x: 0, y: 0 } },
|
|
3440
|
+
previewPatches: {}
|
|
3441
|
+
});
|
|
3442
|
+
this.unifiedDragStates.delete(documentId);
|
|
3443
|
+
}
|
|
3444
|
+
/**
|
|
3445
|
+
* Get the current unified drag state for a document.
|
|
3446
|
+
*/
|
|
3447
|
+
getDragState(documentId) {
|
|
3448
|
+
return this.unifiedDragStates.get(documentId) ?? null;
|
|
3449
|
+
}
|
|
3450
|
+
/**
|
|
3451
|
+
* Subscribe to unified drag state changes.
|
|
3452
|
+
* Framework components use this for preview updates.
|
|
3453
|
+
*/
|
|
3454
|
+
get onDragChange() {
|
|
3455
|
+
return this.unifiedDrag$.on;
|
|
3456
|
+
}
|
|
3457
|
+
// ─────────────────────────────────────────────────────────
|
|
3458
|
+
// Unified Resize API (Plugin owns all logic - framework just calls these)
|
|
3459
|
+
// ─────────────────────────────────────────────────────────
|
|
3460
|
+
/**
|
|
3461
|
+
* Compute the union bounding box of multiple rects.
|
|
3462
|
+
*/
|
|
3463
|
+
computeUnifiedGroupBoundingBox(rects) {
|
|
3464
|
+
if (rects.length === 0) {
|
|
3465
|
+
return { origin: { x: 0, y: 0 }, size: { width: 0, height: 0 } };
|
|
3466
|
+
}
|
|
3467
|
+
let minX = Infinity;
|
|
3468
|
+
let minY = Infinity;
|
|
3469
|
+
let maxX = -Infinity;
|
|
3470
|
+
let maxY = -Infinity;
|
|
3471
|
+
for (const rect of rects) {
|
|
3472
|
+
minX = Math.min(minX, rect.origin.x);
|
|
3473
|
+
minY = Math.min(minY, rect.origin.y);
|
|
3474
|
+
maxX = Math.max(maxX, rect.origin.x + rect.size.width);
|
|
3475
|
+
maxY = Math.max(maxY, rect.origin.y + rect.size.height);
|
|
3476
|
+
}
|
|
3477
|
+
return {
|
|
3478
|
+
origin: { x: minX, y: minY },
|
|
3479
|
+
size: { width: maxX - minX, height: maxY - minY }
|
|
3480
|
+
};
|
|
3481
|
+
}
|
|
3482
|
+
/**
|
|
3483
|
+
* Compute relative positions for annotations within a group bounding box.
|
|
3484
|
+
*/
|
|
3485
|
+
computeUnifiedRelativePositions(annotations, groupBox) {
|
|
3486
|
+
return annotations.map((anno) => ({
|
|
3487
|
+
id: anno.id,
|
|
3488
|
+
originalRect: anno.rect,
|
|
3489
|
+
pageIndex: anno.pageIndex,
|
|
3490
|
+
isAttachedLink: anno.isAttachedLink,
|
|
3491
|
+
parentId: anno.parentId,
|
|
3492
|
+
relativeX: groupBox.size.width > 0 ? (anno.rect.origin.x - groupBox.origin.x) / groupBox.size.width : 0,
|
|
3493
|
+
relativeY: groupBox.size.height > 0 ? (anno.rect.origin.y - groupBox.origin.y) / groupBox.size.height : 0,
|
|
3494
|
+
relativeWidth: groupBox.size.width > 0 ? anno.rect.size.width / groupBox.size.width : 1,
|
|
3495
|
+
relativeHeight: groupBox.size.height > 0 ? anno.rect.size.height / groupBox.size.height : 1
|
|
3496
|
+
}));
|
|
3497
|
+
}
|
|
3498
|
+
/**
|
|
3499
|
+
* Compute new rects for all annotations based on the new group bounding box.
|
|
3500
|
+
*/
|
|
3501
|
+
computeUnifiedResizedRects(participatingAnnotations, newGroupBox, minSize = 10) {
|
|
3502
|
+
const result = /* @__PURE__ */ new Map();
|
|
3503
|
+
for (const anno of participatingAnnotations) {
|
|
3504
|
+
const newWidth = Math.max(minSize, anno.relativeWidth * newGroupBox.size.width);
|
|
3505
|
+
const newHeight = Math.max(minSize, anno.relativeHeight * newGroupBox.size.height);
|
|
3506
|
+
result.set(anno.id, {
|
|
3507
|
+
origin: {
|
|
3508
|
+
x: newGroupBox.origin.x + anno.relativeX * newGroupBox.size.width,
|
|
3509
|
+
y: newGroupBox.origin.y + anno.relativeY * newGroupBox.size.height
|
|
3510
|
+
},
|
|
3511
|
+
size: {
|
|
3512
|
+
width: newWidth,
|
|
3513
|
+
height: newHeight
|
|
3514
|
+
}
|
|
3515
|
+
});
|
|
3516
|
+
}
|
|
3517
|
+
return result;
|
|
3518
|
+
}
|
|
3519
|
+
/**
|
|
3520
|
+
* Compute preview patches for all resize participants.
|
|
3521
|
+
* Uses transformAnnotation to properly handle vertices, inkList, etc.
|
|
3522
|
+
*/
|
|
3523
|
+
computeResizePreviewPatches(computedRects, documentId) {
|
|
3524
|
+
const previewPatches = {};
|
|
3525
|
+
for (const [id, newRect] of computedRects) {
|
|
3526
|
+
const ta = this.getAnnotationById(id, documentId);
|
|
3527
|
+
if (!ta) continue;
|
|
3528
|
+
previewPatches[id] = this.transformAnnotation(ta.object, {
|
|
3529
|
+
type: "resize",
|
|
3530
|
+
changes: { rect: newRect }
|
|
3531
|
+
});
|
|
3532
|
+
}
|
|
3533
|
+
return previewPatches;
|
|
3534
|
+
}
|
|
3535
|
+
/**
|
|
3536
|
+
* Start a unified resize operation.
|
|
3537
|
+
* The plugin automatically expands the selection to include attached links.
|
|
3538
|
+
*
|
|
3539
|
+
* @param documentId - The document ID
|
|
3540
|
+
* @param options - Resize options
|
|
3541
|
+
*/
|
|
3542
|
+
startResize(documentId, options) {
|
|
3543
|
+
const { annotationIds, pageSize, resizeHandle } = options;
|
|
3544
|
+
const attachedLinkIds = [];
|
|
3545
|
+
const annotationsWithLinks = [];
|
|
3546
|
+
for (const id of annotationIds) {
|
|
3547
|
+
const ta = this.getAnnotationById(id, documentId);
|
|
3548
|
+
if (ta) {
|
|
3549
|
+
annotationsWithLinks.push({
|
|
3550
|
+
id,
|
|
3551
|
+
rect: ta.object.rect,
|
|
3552
|
+
pageIndex: ta.object.pageIndex,
|
|
3553
|
+
isAttachedLink: false
|
|
3554
|
+
});
|
|
3555
|
+
const links = this.getAttachedLinksMethod(id, documentId);
|
|
3556
|
+
for (const link of links) {
|
|
3557
|
+
if (!attachedLinkIds.includes(link.object.id)) {
|
|
3558
|
+
attachedLinkIds.push(link.object.id);
|
|
3559
|
+
annotationsWithLinks.push({
|
|
3560
|
+
id: link.object.id,
|
|
3561
|
+
rect: link.object.rect,
|
|
3562
|
+
pageIndex: link.object.pageIndex,
|
|
3563
|
+
isAttachedLink: true,
|
|
3564
|
+
parentId: id
|
|
3565
|
+
});
|
|
3566
|
+
}
|
|
3567
|
+
}
|
|
3568
|
+
}
|
|
3569
|
+
}
|
|
3570
|
+
const allParticipantIds = [...annotationIds, ...attachedLinkIds];
|
|
3571
|
+
const rects = annotationsWithLinks.map((a) => a.rect);
|
|
3572
|
+
const groupBox = this.computeUnifiedGroupBoundingBox(rects);
|
|
3573
|
+
const participatingAnnotations = this.computeUnifiedRelativePositions(
|
|
3574
|
+
annotationsWithLinks,
|
|
3575
|
+
groupBox
|
|
3576
|
+
);
|
|
3577
|
+
const computedRects = this.computeUnifiedResizedRects(participatingAnnotations, groupBox);
|
|
3578
|
+
const state = {
|
|
3579
|
+
documentId,
|
|
3580
|
+
isResizing: true,
|
|
3581
|
+
primaryIds: annotationIds,
|
|
3582
|
+
attachedLinkIds,
|
|
3583
|
+
allParticipantIds,
|
|
3584
|
+
originalGroupBox: groupBox,
|
|
3585
|
+
currentGroupBox: groupBox,
|
|
3586
|
+
participatingAnnotations,
|
|
3587
|
+
resizeHandle,
|
|
3588
|
+
computedRects
|
|
3589
|
+
};
|
|
3590
|
+
this.unifiedResizeStates.set(documentId, state);
|
|
3591
|
+
const startPatches = this.computeResizePreviewPatches(computedRects, documentId);
|
|
3592
|
+
this.unifiedResize$.emit({
|
|
3593
|
+
documentId,
|
|
3594
|
+
type: "start",
|
|
3595
|
+
state,
|
|
3596
|
+
computedRects: Object.fromEntries(computedRects),
|
|
3597
|
+
previewPatches: startPatches
|
|
3598
|
+
});
|
|
3599
|
+
}
|
|
3600
|
+
/**
|
|
3601
|
+
* Update the resize with a new group bounding box.
|
|
3602
|
+
* Returns the computed rects synchronously for immediate preview use.
|
|
3603
|
+
*
|
|
3604
|
+
* @param documentId - The document ID
|
|
3605
|
+
* @param newGroupBox - The new group bounding box
|
|
3606
|
+
* @returns Record of annotation ID to new rect
|
|
3607
|
+
*/
|
|
3608
|
+
updateResize(documentId, newGroupBox) {
|
|
3609
|
+
const state = this.unifiedResizeStates.get(documentId);
|
|
3610
|
+
if (!(state == null ? void 0 : state.isResizing)) {
|
|
3611
|
+
return {};
|
|
3612
|
+
}
|
|
3613
|
+
const computedRects = this.computeUnifiedResizedRects(
|
|
3614
|
+
state.participatingAnnotations,
|
|
3615
|
+
newGroupBox
|
|
3616
|
+
);
|
|
3617
|
+
const newState = {
|
|
3618
|
+
...state,
|
|
3619
|
+
currentGroupBox: newGroupBox,
|
|
3620
|
+
computedRects
|
|
3621
|
+
};
|
|
3622
|
+
this.unifiedResizeStates.set(documentId, newState);
|
|
3623
|
+
const computedRectsObj = Object.fromEntries(computedRects);
|
|
3624
|
+
const previewPatches = this.computeResizePreviewPatches(computedRects, documentId);
|
|
3625
|
+
this.unifiedResize$.emit({
|
|
3626
|
+
documentId,
|
|
3627
|
+
type: "update",
|
|
3628
|
+
state: newState,
|
|
3629
|
+
computedRects: computedRectsObj,
|
|
3630
|
+
previewPatches
|
|
3631
|
+
});
|
|
3632
|
+
return computedRectsObj;
|
|
3633
|
+
}
|
|
3634
|
+
/**
|
|
3635
|
+
* Commit the resize - plugin builds and applies ALL patches.
|
|
3636
|
+
*
|
|
3637
|
+
* @param documentId - The document ID
|
|
3638
|
+
*/
|
|
3639
|
+
commitResize(documentId) {
|
|
3640
|
+
const state = this.unifiedResizeStates.get(documentId);
|
|
3641
|
+
if (!state) return;
|
|
3642
|
+
const computedRects = this.computeUnifiedResizedRects(
|
|
3643
|
+
state.participatingAnnotations,
|
|
3644
|
+
state.currentGroupBox
|
|
3645
|
+
);
|
|
3646
|
+
const patches = [];
|
|
3647
|
+
for (const [id, newRect] of computedRects) {
|
|
3648
|
+
const ta = this.getAnnotationById(id, documentId);
|
|
3649
|
+
if (!ta) continue;
|
|
3650
|
+
const patch = this.transformAnnotation(ta.object, {
|
|
3651
|
+
type: "resize",
|
|
3652
|
+
changes: { rect: newRect }
|
|
3653
|
+
});
|
|
3654
|
+
patches.push({ pageIndex: ta.object.pageIndex, id, patch });
|
|
3655
|
+
}
|
|
3656
|
+
if (patches.length > 0) {
|
|
3657
|
+
this.updateAnnotationsMethod(patches, documentId);
|
|
3658
|
+
}
|
|
3659
|
+
const endPatches = this.computeResizePreviewPatches(computedRects, documentId);
|
|
3660
|
+
this.unifiedResize$.emit({
|
|
3661
|
+
documentId,
|
|
3662
|
+
type: "end",
|
|
3663
|
+
state: { ...state, isResizing: false },
|
|
3664
|
+
computedRects: Object.fromEntries(computedRects),
|
|
3665
|
+
previewPatches: endPatches
|
|
3666
|
+
});
|
|
3667
|
+
this.unifiedResizeStates.delete(documentId);
|
|
3668
|
+
}
|
|
3669
|
+
/**
|
|
3670
|
+
* Cancel the resize without committing.
|
|
3671
|
+
*
|
|
3672
|
+
* @param documentId - The document ID
|
|
3673
|
+
*/
|
|
3674
|
+
cancelResize(documentId) {
|
|
3675
|
+
const state = this.unifiedResizeStates.get(documentId);
|
|
3676
|
+
if (!state) return;
|
|
3677
|
+
const originalRects = this.computeUnifiedResizedRects(
|
|
3678
|
+
state.participatingAnnotations,
|
|
3679
|
+
state.originalGroupBox
|
|
3680
|
+
);
|
|
3681
|
+
this.unifiedResize$.emit({
|
|
3682
|
+
documentId,
|
|
3683
|
+
type: "cancel",
|
|
3684
|
+
state: { ...state, isResizing: false, currentGroupBox: state.originalGroupBox },
|
|
3685
|
+
computedRects: Object.fromEntries(originalRects),
|
|
3686
|
+
previewPatches: {}
|
|
3687
|
+
});
|
|
3688
|
+
this.unifiedResizeStates.delete(documentId);
|
|
3689
|
+
}
|
|
3690
|
+
/**
|
|
3691
|
+
* Get the current unified resize state for a document.
|
|
3692
|
+
*/
|
|
3693
|
+
getResizeState(documentId) {
|
|
3694
|
+
return this.unifiedResizeStates.get(documentId) ?? null;
|
|
3695
|
+
}
|
|
3696
|
+
/**
|
|
3697
|
+
* Subscribe to unified resize state changes.
|
|
3698
|
+
* Framework components use this for preview updates.
|
|
3699
|
+
*/
|
|
3700
|
+
get onResizeChange() {
|
|
3701
|
+
return this.unifiedResize$.on;
|
|
3702
|
+
}
|
|
3703
|
+
updateAnnotationsMethod(patches, documentId) {
|
|
3704
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3705
|
+
if (!this.checkPermission(docId, PdfPermissionFlag.ModifyAnnotations)) {
|
|
3706
|
+
this.logger.debug(
|
|
3707
|
+
"AnnotationPlugin",
|
|
3708
|
+
"UpdateAnnotations",
|
|
3709
|
+
`Cannot update annotations: document ${docId} lacks ModifyAnnotations permission`
|
|
3710
|
+
);
|
|
3711
|
+
return;
|
|
3712
|
+
}
|
|
3713
|
+
const docState = this.getDocumentState(docId);
|
|
3714
|
+
const patchData = patches.map(({ pageIndex, id, patch }) => {
|
|
3715
|
+
var _a;
|
|
3716
|
+
const originalObject = (_a = docState.byUid[id]) == null ? void 0 : _a.object;
|
|
3717
|
+
if (!originalObject) return null;
|
|
3718
|
+
const finalPatch = this.buildPatch(originalObject, {
|
|
3719
|
+
...patch,
|
|
3720
|
+
author: patch.author ?? this.config.annotationAuthor
|
|
3721
|
+
});
|
|
3722
|
+
return { pageIndex, id, patch: finalPatch, originalObject };
|
|
3723
|
+
}).filter((p) => p !== null);
|
|
3724
|
+
if (patchData.length === 0) return;
|
|
3725
|
+
const execute = () => {
|
|
3726
|
+
for (const { pageIndex, id, patch, originalObject } of patchData) {
|
|
3727
|
+
this.dispatch(patchAnnotation(docId, pageIndex, id, patch));
|
|
3728
|
+
this.events$.emit({
|
|
3729
|
+
type: "update",
|
|
3730
|
+
documentId: docId,
|
|
3731
|
+
annotation: originalObject,
|
|
3732
|
+
pageIndex,
|
|
3733
|
+
patch,
|
|
3734
|
+
committed: false
|
|
3735
|
+
});
|
|
3736
|
+
}
|
|
3737
|
+
};
|
|
3738
|
+
if (!this.history) {
|
|
3739
|
+
execute();
|
|
3740
|
+
if (this.config.autoCommit !== false) {
|
|
3741
|
+
this.commit(docId);
|
|
3742
|
+
}
|
|
3743
|
+
return;
|
|
3744
|
+
}
|
|
3745
|
+
const undoData = patchData.map(({ pageIndex, id, patch, originalObject }) => ({
|
|
3746
|
+
pageIndex,
|
|
3747
|
+
id,
|
|
3748
|
+
originalPatch: Object.fromEntries(
|
|
3749
|
+
Object.keys(patch).map((key) => [key, originalObject[key]])
|
|
3750
|
+
),
|
|
3751
|
+
originalObject
|
|
3752
|
+
}));
|
|
3753
|
+
const command = {
|
|
3754
|
+
execute,
|
|
3755
|
+
undo: () => {
|
|
3756
|
+
for (const { pageIndex, id, originalPatch, originalObject } of undoData) {
|
|
3757
|
+
this.dispatch(patchAnnotation(docId, pageIndex, id, originalPatch));
|
|
3758
|
+
this.events$.emit({
|
|
3759
|
+
type: "update",
|
|
3760
|
+
documentId: docId,
|
|
3761
|
+
annotation: originalObject,
|
|
3762
|
+
pageIndex,
|
|
3763
|
+
patch: originalPatch,
|
|
3764
|
+
committed: false
|
|
3765
|
+
});
|
|
3766
|
+
}
|
|
3767
|
+
}
|
|
3768
|
+
};
|
|
3769
|
+
const historyScope = this.history.forDocument(docId);
|
|
3770
|
+
historyScope.register(command, this.ANNOTATION_HISTORY_TOPIC);
|
|
3771
|
+
}
|
|
2754
3772
|
getActiveTool(documentId) {
|
|
2755
3773
|
const docState = this.getDocumentState(documentId);
|
|
2756
3774
|
if (!docState.activeToolId) return null;
|
|
@@ -2759,6 +3777,14 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2759
3777
|
setActiveTool(toolId, documentId) {
|
|
2760
3778
|
var _a, _b;
|
|
2761
3779
|
const docId = documentId ?? this.getActiveDocumentId();
|
|
3780
|
+
if (toolId !== null && !this.checkPermission(docId, PdfPermissionFlag.ModifyAnnotations)) {
|
|
3781
|
+
this.logger.debug(
|
|
3782
|
+
"AnnotationPlugin",
|
|
3783
|
+
"SetActiveTool",
|
|
3784
|
+
`Cannot activate tool: document ${docId} lacks ModifyAnnotations permission`
|
|
3785
|
+
);
|
|
3786
|
+
return;
|
|
3787
|
+
}
|
|
2762
3788
|
const docState = this.getDocumentState(docId);
|
|
2763
3789
|
if (toolId === docState.activeToolId) return;
|
|
2764
3790
|
this.dispatch(setActiveToolId(docId, toolId));
|
|
@@ -2784,87 +3810,185 @@ const _AnnotationPlugin = class _AnnotationPlugin extends BasePlugin {
|
|
|
2784
3810
|
}
|
|
2785
3811
|
return bestTool;
|
|
2786
3812
|
}
|
|
2787
|
-
|
|
2788
|
-
|
|
2789
|
-
|
|
3813
|
+
/**
|
|
3814
|
+
* Collects all pending annotation changes for a document into a batch.
|
|
3815
|
+
* This separates the "what to commit" from "how to commit" for cleaner code.
|
|
3816
|
+
*/
|
|
3817
|
+
collectPendingChanges(docId, doc) {
|
|
2790
3818
|
const docState = this.getDocumentState(docId);
|
|
2791
|
-
if (!docState.hasPendingChanges) return PdfTaskHelper.resolve(true);
|
|
2792
|
-
const coreDocState = this.getCoreDocument(docId);
|
|
2793
|
-
const doc = coreDocState == null ? void 0 : coreDocState.document;
|
|
2794
|
-
if (!doc)
|
|
2795
|
-
return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
|
|
2796
3819
|
const contexts = this.pendingContexts.get(docId);
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
3820
|
+
const batch = {
|
|
3821
|
+
creations: [],
|
|
3822
|
+
updates: [],
|
|
3823
|
+
deletions: [],
|
|
3824
|
+
committedUids: [],
|
|
3825
|
+
isEmpty: true
|
|
3826
|
+
};
|
|
2801
3827
|
for (const [uid, ta] of Object.entries(docState.byUid)) {
|
|
2802
3828
|
if (ta.commitState === "synced") continue;
|
|
2803
3829
|
const page = doc.pages.find((p) => p.index === ta.object.pageIndex);
|
|
2804
3830
|
if (!page) continue;
|
|
3831
|
+
batch.committedUids.push(uid);
|
|
3832
|
+
batch.isEmpty = false;
|
|
2805
3833
|
switch (ta.commitState) {
|
|
2806
3834
|
case "new":
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
documentId: docId,
|
|
2813
|
-
annotation: ta.object,
|
|
2814
|
-
pageIndex: ta.object.pageIndex,
|
|
2815
|
-
ctx,
|
|
2816
|
-
committed: true
|
|
2817
|
-
});
|
|
2818
|
-
contexts.delete(ta.object.id);
|
|
2819
|
-
}, ignore);
|
|
2820
|
-
creations.push(task2);
|
|
3835
|
+
batch.creations.push({
|
|
3836
|
+
uid,
|
|
3837
|
+
ta,
|
|
3838
|
+
ctx: contexts == null ? void 0 : contexts.get(ta.object.id)
|
|
3839
|
+
});
|
|
2821
3840
|
break;
|
|
2822
3841
|
case "dirty":
|
|
2823
|
-
|
|
2824
|
-
updateTask.wait(() => {
|
|
2825
|
-
this.events$.emit({
|
|
2826
|
-
type: "update",
|
|
2827
|
-
documentId: docId,
|
|
2828
|
-
annotation: ta.object,
|
|
2829
|
-
pageIndex: ta.object.pageIndex,
|
|
2830
|
-
patch: ta.object,
|
|
2831
|
-
committed: true
|
|
2832
|
-
});
|
|
2833
|
-
}, ignore);
|
|
2834
|
-
updates.push(updateTask);
|
|
3842
|
+
batch.updates.push({ uid, ta });
|
|
2835
3843
|
break;
|
|
2836
3844
|
case "deleted":
|
|
2837
|
-
deletions.push({
|
|
3845
|
+
batch.deletions.push({ uid, ta });
|
|
2838
3846
|
break;
|
|
2839
3847
|
}
|
|
2840
3848
|
}
|
|
2841
|
-
|
|
2842
|
-
|
|
3849
|
+
return batch;
|
|
3850
|
+
}
|
|
3851
|
+
/**
|
|
3852
|
+
* Executes a batch of pending changes by creating engine tasks.
|
|
3853
|
+
* Returns a task that resolves when all operations complete.
|
|
3854
|
+
*/
|
|
3855
|
+
executeCommitBatch(docId, doc, batch) {
|
|
3856
|
+
const task = new Task();
|
|
3857
|
+
const contexts = this.pendingContexts.get(docId);
|
|
3858
|
+
const pendingOps = [];
|
|
3859
|
+
for (const { uid, ta, ctx } of batch.creations) {
|
|
3860
|
+
const page = doc.pages.find((p) => p.index === ta.object.pageIndex);
|
|
3861
|
+
if (!page) continue;
|
|
3862
|
+
const createTask = this.engine.createPageAnnotation(doc, page, ta.object, ctx);
|
|
3863
|
+
pendingOps.push({ type: "create", task: createTask, ta, uid, ctx });
|
|
3864
|
+
}
|
|
3865
|
+
for (const { uid, ta } of batch.updates) {
|
|
3866
|
+
const page = doc.pages.find((p) => p.index === ta.object.pageIndex);
|
|
3867
|
+
if (!page) continue;
|
|
3868
|
+
const updateTask = this.engine.updatePageAnnotation(doc, page, ta.object);
|
|
3869
|
+
pendingOps.push({ type: "update", task: updateTask, ta, uid });
|
|
3870
|
+
}
|
|
3871
|
+
for (const { uid, ta } of batch.deletions) {
|
|
2843
3872
|
const page = doc.pages.find((p) => p.index === ta.object.pageIndex);
|
|
2844
|
-
if (
|
|
2845
|
-
|
|
3873
|
+
if (!page) continue;
|
|
3874
|
+
if (ta.object.id) {
|
|
3875
|
+
const deleteTask = new Task();
|
|
2846
3876
|
const removeTask = this.engine.removePageAnnotation(doc, page, ta.object);
|
|
2847
|
-
removeTask.wait(() =>
|
|
2848
|
-
|
|
3877
|
+
removeTask.wait(() => deleteTask.resolve(true), deleteTask.fail);
|
|
3878
|
+
pendingOps.push({ type: "delete", task: deleteTask, ta, uid });
|
|
3879
|
+
} else {
|
|
3880
|
+
this.dispatch(purgeAnnotation(docId, uid));
|
|
3881
|
+
}
|
|
3882
|
+
}
|
|
3883
|
+
const allTasks = pendingOps.map((op) => op.task);
|
|
3884
|
+
Task.allSettled(allTasks).wait(
|
|
3885
|
+
() => {
|
|
3886
|
+
this.emitCommitEvents(docId, pendingOps, contexts);
|
|
3887
|
+
this.dispatch(commitPendingChanges(docId, batch.committedUids));
|
|
3888
|
+
task.resolve(true);
|
|
3889
|
+
},
|
|
3890
|
+
(error) => task.fail(error)
|
|
3891
|
+
);
|
|
3892
|
+
return task;
|
|
3893
|
+
}
|
|
3894
|
+
/**
|
|
3895
|
+
* Emits commit events for all completed operations.
|
|
3896
|
+
* Centralizes event emission for cleaner separation of concerns.
|
|
3897
|
+
*/
|
|
3898
|
+
emitCommitEvents(docId, operations, contexts) {
|
|
3899
|
+
for (const op of operations) {
|
|
3900
|
+
if (op.task.state.stage !== TaskStage.Resolved) continue;
|
|
3901
|
+
switch (op.type) {
|
|
3902
|
+
case "create":
|
|
3903
|
+
this.events$.emit({
|
|
3904
|
+
type: "create",
|
|
3905
|
+
documentId: docId,
|
|
3906
|
+
annotation: op.ta.object,
|
|
3907
|
+
pageIndex: op.ta.object.pageIndex,
|
|
3908
|
+
ctx: op.ctx,
|
|
3909
|
+
committed: true
|
|
3910
|
+
});
|
|
3911
|
+
contexts == null ? void 0 : contexts.delete(op.ta.object.id);
|
|
3912
|
+
break;
|
|
3913
|
+
case "update":
|
|
3914
|
+
this.events$.emit({
|
|
3915
|
+
type: "update",
|
|
3916
|
+
documentId: docId,
|
|
3917
|
+
annotation: op.ta.object,
|
|
3918
|
+
pageIndex: op.ta.object.pageIndex,
|
|
3919
|
+
patch: op.ta.object,
|
|
3920
|
+
committed: true
|
|
3921
|
+
});
|
|
3922
|
+
break;
|
|
3923
|
+
case "delete":
|
|
3924
|
+
this.dispatch(purgeAnnotation(docId, op.uid));
|
|
2849
3925
|
this.events$.emit({
|
|
2850
3926
|
type: "delete",
|
|
2851
3927
|
documentId: docId,
|
|
2852
|
-
annotation: ta.object,
|
|
2853
|
-
pageIndex: ta.object.pageIndex,
|
|
3928
|
+
annotation: op.ta.object,
|
|
3929
|
+
pageIndex: op.ta.object.pageIndex,
|
|
2854
3930
|
committed: true
|
|
2855
3931
|
});
|
|
2856
|
-
|
|
2857
|
-
}, task2.fail);
|
|
2858
|
-
deletionTasks.push(task2);
|
|
2859
|
-
} else {
|
|
2860
|
-
this.dispatch(purgeAnnotation(docId, uid));
|
|
3932
|
+
break;
|
|
2861
3933
|
}
|
|
2862
3934
|
}
|
|
2863
|
-
|
|
2864
|
-
|
|
2865
|
-
|
|
2866
|
-
|
|
2867
|
-
|
|
3935
|
+
}
|
|
3936
|
+
/**
|
|
3937
|
+
* Attempts to acquire the commit lock for a document.
|
|
3938
|
+
* Returns true if acquired, false if a commit is already in progress.
|
|
3939
|
+
*/
|
|
3940
|
+
acquireCommitLock(docId) {
|
|
3941
|
+
if (this.commitInProgress.get(docId)) {
|
|
3942
|
+
return false;
|
|
3943
|
+
}
|
|
3944
|
+
this.commitInProgress.set(docId, true);
|
|
3945
|
+
return true;
|
|
3946
|
+
}
|
|
3947
|
+
/**
|
|
3948
|
+
* Releases the commit lock for a document.
|
|
3949
|
+
*/
|
|
3950
|
+
releaseCommitLock(docId) {
|
|
3951
|
+
this.commitInProgress.set(docId, false);
|
|
3952
|
+
}
|
|
3953
|
+
commit(documentId) {
|
|
3954
|
+
const docId = documentId ?? this.getActiveDocumentId();
|
|
3955
|
+
const docState = this.getDocumentState(docId);
|
|
3956
|
+
if (!docState.hasPendingChanges) {
|
|
3957
|
+
return PdfTaskHelper.resolve(true);
|
|
3958
|
+
}
|
|
3959
|
+
if (!this.acquireCommitLock(docId)) {
|
|
3960
|
+
return PdfTaskHelper.resolve(true);
|
|
3961
|
+
}
|
|
3962
|
+
const coreDocState = this.getCoreDocument(docId);
|
|
3963
|
+
const doc = coreDocState == null ? void 0 : coreDocState.document;
|
|
3964
|
+
if (!doc) {
|
|
3965
|
+
this.releaseCommitLock(docId);
|
|
3966
|
+
return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
|
|
3967
|
+
}
|
|
3968
|
+
const batch = this.collectPendingChanges(docId, doc);
|
|
3969
|
+
if (batch.isEmpty) {
|
|
3970
|
+
this.releaseCommitLock(docId);
|
|
3971
|
+
return PdfTaskHelper.resolve(true);
|
|
3972
|
+
}
|
|
3973
|
+
const task = new Task();
|
|
3974
|
+
this.executeCommitBatch(docId, doc, batch).wait(
|
|
3975
|
+
() => {
|
|
3976
|
+
this.releaseCommitLock(docId);
|
|
3977
|
+
const updatedDocState = this.getDocumentState(docId);
|
|
3978
|
+
if (updatedDocState.hasPendingChanges) {
|
|
3979
|
+
this.commit(docId).wait(
|
|
3980
|
+
(result) => task.resolve(result),
|
|
3981
|
+
(error) => task.fail(error)
|
|
3982
|
+
);
|
|
3983
|
+
} else {
|
|
3984
|
+
task.resolve(true);
|
|
3985
|
+
}
|
|
3986
|
+
},
|
|
3987
|
+
(error) => {
|
|
3988
|
+
this.releaseCommitLock(docId);
|
|
3989
|
+
task.fail(error);
|
|
3990
|
+
}
|
|
3991
|
+
);
|
|
2868
3992
|
return task;
|
|
2869
3993
|
}
|
|
2870
3994
|
/**
|
|
@@ -2913,11 +4037,23 @@ export {
|
|
|
2913
4037
|
getAnnotationByUid,
|
|
2914
4038
|
getAnnotations,
|
|
2915
4039
|
getAnnotationsByPageIndex,
|
|
4040
|
+
getAttachedLinks,
|
|
4041
|
+
getGroupLeaderId,
|
|
4042
|
+
getGroupMembers,
|
|
4043
|
+
getIRTChildIds,
|
|
4044
|
+
getIRTChildrenByType,
|
|
2916
4045
|
getSelectedAnnotation,
|
|
2917
4046
|
getSelectedAnnotationByPageIndex,
|
|
4047
|
+
getSelectedAnnotationIds,
|
|
4048
|
+
getSelectedAnnotations,
|
|
4049
|
+
getSelectedAnnotationsByPageIndex,
|
|
4050
|
+
getSelectionGroupingAction,
|
|
2918
4051
|
getSidebarAnnotationsWithReplies,
|
|
2919
4052
|
getSidebarAnnotationsWithRepliesGroupedByPage,
|
|
2920
4053
|
getToolDefaultsById,
|
|
4054
|
+
hasAttachedLinks,
|
|
4055
|
+
hasIRTChildren,
|
|
4056
|
+
hasMultipleSelected,
|
|
2921
4057
|
initialDocumentState,
|
|
2922
4058
|
initialState,
|
|
2923
4059
|
isAnnotationSelected,
|
|
@@ -2927,11 +4063,13 @@ export {
|
|
|
2927
4063
|
isFreeTextTool,
|
|
2928
4064
|
isHighlight,
|
|
2929
4065
|
isHighlightTool,
|
|
4066
|
+
isInGroup,
|
|
2930
4067
|
isInk,
|
|
2931
4068
|
isInkHighlighterTool,
|
|
2932
4069
|
isInkTool,
|
|
2933
4070
|
isLine,
|
|
2934
4071
|
isLineTool,
|
|
4072
|
+
isLink,
|
|
2935
4073
|
isPolygon,
|
|
2936
4074
|
isPolygonTool,
|
|
2937
4075
|
isPolyline,
|
|
@@ -2950,6 +4088,7 @@ export {
|
|
|
2950
4088
|
isUnderline,
|
|
2951
4089
|
isUnderlineTool,
|
|
2952
4090
|
manifest,
|
|
2953
|
-
index as patching
|
|
4091
|
+
index as patching,
|
|
4092
|
+
rectsIntersect
|
|
2954
4093
|
};
|
|
2955
4094
|
//# sourceMappingURL=index.js.map
|