@embedpdf/plugin-redaction 2.3.0 → 2.4.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 (66) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +748 -105
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/actions.d.ts +25 -1
  6. package/dist/lib/index.d.ts +1 -0
  7. package/dist/lib/redaction-plugin.d.ts +98 -2
  8. package/dist/lib/tools.d.ts +28 -0
  9. package/dist/lib/types.d.ts +38 -11
  10. package/dist/preact/adapter.d.ts +4 -4
  11. package/dist/preact/annotation.d.ts +1 -0
  12. package/dist/preact/index.cjs +1 -1
  13. package/dist/preact/index.cjs.map +1 -1
  14. package/dist/preact/index.js +187 -11
  15. package/dist/preact/index.js.map +1 -1
  16. package/dist/react/annotation.d.ts +1 -0
  17. package/dist/react/index.cjs +1 -1
  18. package/dist/react/index.cjs.map +1 -1
  19. package/dist/react/index.js +187 -11
  20. package/dist/react/index.js.map +1 -1
  21. package/dist/shared/components/annotations/index.d.ts +2 -0
  22. package/dist/shared/components/annotations/redact-area.d.ts +18 -0
  23. package/dist/shared/components/annotations/redact-highlight.d.ts +18 -0
  24. package/dist/shared/components/index.d.ts +3 -0
  25. package/dist/shared/components/redact-renderer-registration.d.ts +5 -0
  26. package/dist/shared/components/redact-renderers.d.ts +7 -0
  27. package/dist/shared/index.d.ts +1 -0
  28. package/dist/shared-preact/components/annotations/index.d.ts +2 -0
  29. package/dist/shared-preact/components/annotations/redact-area.d.ts +18 -0
  30. package/dist/shared-preact/components/annotations/redact-highlight.d.ts +18 -0
  31. package/dist/shared-preact/components/index.d.ts +3 -0
  32. package/dist/shared-preact/components/redact-renderer-registration.d.ts +5 -0
  33. package/dist/shared-preact/components/redact-renderers.d.ts +7 -0
  34. package/dist/shared-preact/index.d.ts +1 -0
  35. package/dist/shared-react/components/annotations/index.d.ts +2 -0
  36. package/dist/shared-react/components/annotations/redact-area.d.ts +18 -0
  37. package/dist/shared-react/components/annotations/redact-highlight.d.ts +18 -0
  38. package/dist/shared-react/components/index.d.ts +3 -0
  39. package/dist/shared-react/components/redact-renderer-registration.d.ts +5 -0
  40. package/dist/shared-react/components/redact-renderers.d.ts +7 -0
  41. package/dist/shared-react/index.d.ts +1 -0
  42. package/dist/svelte/components/RedactRendererRegistration.svelte.d.ts +7 -0
  43. package/dist/svelte/components/annotations/RedactArea.svelte.d.ts +5 -0
  44. package/dist/svelte/components/annotations/RedactHighlight.svelte.d.ts +5 -0
  45. package/dist/svelte/components/annotations/index.d.ts +2 -0
  46. package/dist/svelte/components/index.d.ts +3 -0
  47. package/dist/svelte/components/redact-renderers.d.ts +7 -0
  48. package/dist/svelte/index.cjs +1 -1
  49. package/dist/svelte/index.cjs.map +1 -1
  50. package/dist/svelte/index.d.ts +1 -0
  51. package/dist/svelte/index.js +270 -22
  52. package/dist/svelte/index.js.map +1 -1
  53. package/dist/vue/components/annotations/index.d.ts +2 -0
  54. package/dist/vue/components/annotations/redact-area.vue.d.ts +6 -0
  55. package/dist/vue/components/annotations/redact-highlight.vue.d.ts +6 -0
  56. package/dist/vue/components/index.d.ts +3 -0
  57. package/dist/vue/components/redact-renderer-registration.vue.d.ts +13 -0
  58. package/dist/vue/components/redact-renderers.d.ts +7 -0
  59. package/dist/vue/components/types.d.ts +1 -1
  60. package/dist/vue/hooks/use-redaction.d.ts +2 -2
  61. package/dist/vue/index.cjs +1 -1
  62. package/dist/vue/index.cjs.map +1 -1
  63. package/dist/vue/index.d.ts +2 -1
  64. package/dist/vue/index.js +264 -59
  65. package/dist/vue/index.js.map +1 -1
  66. package/package.json +13 -9
package/dist/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import { clamp, BasePlugin, createBehaviorEmitter, refreshPages } from "@embedpdf/core";
2
- import { PdfPermissionFlag, uuidV4, PdfTaskHelper, PdfErrorCode, Task } from "@embedpdf/models";
2
+ import { PdfAnnotationSubtype, PdfPermissionFlag, PdfTaskHelper, PdfErrorCode, Task, uuidV4 } from "@embedpdf/models";
3
3
  var RedactionMode = /* @__PURE__ */ ((RedactionMode2) => {
4
+ RedactionMode2["Redact"] = "redact";
4
5
  RedactionMode2["MarqueeRedact"] = "marqueeRedact";
5
6
  RedactionMode2["RedactSelection"] = "redactSelection";
6
7
  return RedactionMode2;
@@ -13,6 +14,7 @@ const END_REDACTION = "END_REDACTION";
13
14
  const SET_ACTIVE_TYPE = "SET_ACTIVE_TYPE";
14
15
  const ADD_PENDING = "ADD_PENDING";
15
16
  const REMOVE_PENDING = "REMOVE_PENDING";
17
+ const UPDATE_PENDING = "UPDATE_PENDING";
16
18
  const CLEAR_PENDING = "CLEAR_PENDING";
17
19
  const SELECT_PENDING = "SELECT_PENDING";
18
20
  const DESELECT_PENDING = "DESELECT_PENDING";
@@ -34,6 +36,10 @@ const clearPending = (documentId) => ({
34
36
  type: CLEAR_PENDING,
35
37
  payload: documentId
36
38
  });
39
+ const updatePending = (documentId, page, id, patch) => ({
40
+ type: UPDATE_PENDING,
41
+ payload: { documentId, page, id, patch }
42
+ });
37
43
  const startRedaction = (documentId, mode) => ({
38
44
  type: START_REDACTION,
39
45
  payload: { documentId, mode }
@@ -141,7 +147,9 @@ const redactionReducer = (state = initialState, action) => {
141
147
  if (!docState) return state;
142
148
  const next = { ...docState.pending };
143
149
  for (const item of items) {
144
- next[item.page] = (next[item.page] ?? []).concat(item);
150
+ const existing = next[item.page] ?? [];
151
+ if (existing.some((it) => it.id === item.id)) continue;
152
+ next[item.page] = existing.concat(item);
145
153
  }
146
154
  return {
147
155
  ...state,
@@ -176,6 +184,23 @@ const redactionReducer = (state = initialState, action) => {
176
184
  }
177
185
  };
178
186
  }
187
+ case UPDATE_PENDING: {
188
+ const { documentId, page, id, patch } = action.payload;
189
+ const docState = state.documents[documentId];
190
+ if (!docState) return state;
191
+ const list = docState.pending[page] ?? [];
192
+ const updated = list.map((item) => item.id === id ? { ...item, ...patch } : item);
193
+ return {
194
+ ...state,
195
+ documents: {
196
+ ...state.documents,
197
+ [documentId]: {
198
+ ...docState,
199
+ pending: { ...docState.pending, [page]: updated }
200
+ }
201
+ }
202
+ };
203
+ }
179
204
  case CLEAR_PENDING: {
180
205
  const documentId = action.payload;
181
206
  const docState = state.documents[documentId];
@@ -249,9 +274,6 @@ const redactionReducer = (state = initialState, action) => {
249
274
  ...state.documents,
250
275
  [documentId]: {
251
276
  ...docState,
252
- pending: {},
253
- pendingCount: 0,
254
- selected: null,
255
277
  isRedacting: false,
256
278
  activeType: null
257
279
  }
@@ -277,9 +299,42 @@ const redactionReducer = (state = initialState, action) => {
277
299
  return state;
278
300
  }
279
301
  };
302
+ const redactTool = {
303
+ id: "redact",
304
+ name: "Redact",
305
+ matchScore: (a) => a.type === PdfAnnotationSubtype.REDACT ? 10 : 0,
306
+ interaction: {
307
+ mode: RedactionMode.Redact,
308
+ exclusive: false,
309
+ cursor: "crosshair",
310
+ textSelection: true,
311
+ // Dynamic based on whether it's a text or area redaction
312
+ isDraggable: (anno) => {
313
+ var _a;
314
+ if (anno.type !== PdfAnnotationSubtype.REDACT) return true;
315
+ return !((_a = anno.segmentRects) == null ? void 0 : _a.length);
316
+ },
317
+ isResizable: (anno) => {
318
+ var _a;
319
+ if (anno.type !== PdfAnnotationSubtype.REDACT) return true;
320
+ return !((_a = anno.segmentRects) == null ? void 0 : _a.length);
321
+ },
322
+ lockAspectRatio: false,
323
+ isGroupDraggable: false,
324
+ isGroupResizable: false
325
+ },
326
+ defaults: {
327
+ type: PdfAnnotationSubtype.REDACT,
328
+ color: "#E44234",
329
+ overlayColor: "#000000",
330
+ strokeColor: "#E44234",
331
+ opacity: 1
332
+ }
333
+ };
334
+ const redactTools = [redactTool];
280
335
  const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
281
336
  constructor(id, registry, config) {
282
- var _a, _b, _c;
337
+ var _a, _b, _c, _d;
283
338
  super(id, registry);
284
339
  this.redactionSelection$ = /* @__PURE__ */ new Map();
285
340
  this.pending$ = createBehaviorEmitter();
@@ -290,25 +345,71 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
290
345
  this.config = config;
291
346
  this.selectionCapability = (_a = this.registry.getPlugin("selection")) == null ? void 0 : _a.provides();
292
347
  this.interactionManagerCapability = (_b = this.registry.getPlugin("interaction-manager")) == null ? void 0 : _b.provides();
293
- if (this.interactionManagerCapability) {
294
- this.interactionManagerCapability.registerMode({
295
- id: RedactionMode.MarqueeRedact,
296
- scope: "page",
297
- exclusive: true,
298
- cursor: "crosshair"
299
- });
300
- this.interactionManagerCapability.registerMode({
301
- id: RedactionMode.RedactSelection,
302
- scope: "page",
303
- exclusive: false
304
- });
348
+ this.annotationCapability = (_c = this.registry.getPlugin("annotation")) == null ? void 0 : _c.provides();
349
+ this.historyCapability = (_d = this.registry.getPlugin("history")) == null ? void 0 : _d.provides();
350
+ if (this.config.useAnnotationMode) {
351
+ if (this.annotationCapability) {
352
+ this.useAnnotationMode = true;
353
+ } else {
354
+ this.logger.warn(
355
+ "RedactionPlugin",
356
+ "ConfigError",
357
+ "useAnnotationMode is enabled but annotation plugin is not available. Falling back to legacy mode."
358
+ );
359
+ this.useAnnotationMode = false;
360
+ }
361
+ } else {
362
+ this.useAnnotationMode = false;
305
363
  }
306
- (_c = this.interactionManagerCapability) == null ? void 0 : _c.onModeChange((modeState) => {
364
+ if (this.useAnnotationMode) {
365
+ for (const tool of redactTools) {
366
+ this.annotationCapability.addTool(tool);
367
+ }
368
+ }
369
+ this.setupRedactionModes();
370
+ if (!this.useAnnotationMode && this.annotationCapability) {
371
+ this.logger.info(
372
+ "RedactionPlugin",
373
+ "LegacyMode",
374
+ "Using legacy redaction mode. Set useAnnotationMode: true in config to use annotation-based redactions."
375
+ );
376
+ }
377
+ this.setupModeChangeListener();
378
+ }
379
+ /**
380
+ * Setup redaction modes - registers all interaction modes for redaction.
381
+ * Works for both annotation mode and legacy mode.
382
+ */
383
+ setupRedactionModes() {
384
+ if (!this.interactionManagerCapability) return;
385
+ this.interactionManagerCapability.registerMode({
386
+ id: RedactionMode.Redact,
387
+ scope: "page",
388
+ exclusive: false,
389
+ cursor: "crosshair"
390
+ });
391
+ this.interactionManagerCapability.registerMode({
392
+ id: RedactionMode.MarqueeRedact,
393
+ scope: "page",
394
+ exclusive: false,
395
+ cursor: "crosshair"
396
+ });
397
+ this.interactionManagerCapability.registerMode({
398
+ id: RedactionMode.RedactSelection,
399
+ scope: "page",
400
+ exclusive: false
401
+ });
402
+ }
403
+ /**
404
+ * Setup mode change listener - handles all redaction modes
405
+ */
406
+ setupModeChangeListener() {
407
+ var _a;
408
+ (_a = this.interactionManagerCapability) == null ? void 0 : _a.onModeChange((modeState) => {
307
409
  const documentId = modeState.documentId;
308
- if (modeState.activeMode === RedactionMode.RedactSelection) {
309
- this.dispatch(startRedaction(documentId, RedactionMode.RedactSelection));
310
- } else if (modeState.activeMode === RedactionMode.MarqueeRedact) {
311
- this.dispatch(startRedaction(documentId, RedactionMode.MarqueeRedact));
410
+ const isRedactionMode = modeState.activeMode === RedactionMode.Redact || modeState.activeMode === RedactionMode.MarqueeRedact || modeState.activeMode === RedactionMode.RedactSelection;
411
+ if (isRedactionMode) {
412
+ this.dispatch(startRedaction(documentId, modeState.activeMode));
312
413
  } else {
313
414
  const docState = this.getDocumentState(documentId);
314
415
  if (docState == null ? void 0 : docState.isRedacting) {
@@ -340,28 +441,68 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
340
441
  const unsubEndSelection = selectionScope.onEndSelection(() => {
341
442
  const docState = this.getDocumentState(documentId);
342
443
  if (!(docState == null ? void 0 : docState.isRedacting)) return;
343
- if (!this.checkPermission(documentId, PdfPermissionFlag.ModifyContents)) {
344
- return;
345
- }
444
+ if (!this.checkPermission(documentId, PdfPermissionFlag.ModifyContents)) return;
346
445
  const formattedSelection = selectionScope.getFormattedSelection();
347
- const items = formattedSelection.map((s) => ({
348
- id: uuidV4(),
349
- kind: "text",
350
- page: s.pageIndex,
351
- rect: s.rect,
352
- rects: s.segmentRects
353
- }));
354
- this.dispatch(addPending(documentId, items));
446
+ if (!formattedSelection.length) return;
447
+ const textTask = selectionScope.getSelectedText();
355
448
  const emitter = this.redactionSelection$.get(documentId);
356
449
  emitter == null ? void 0 : emitter.emit([]);
357
450
  selectionScope.clear();
358
- this.emitPendingChange(documentId);
359
- if (items.length) {
360
- this.selectPending(items[items.length - 1].page, items[items.length - 1].id, documentId);
361
- }
451
+ textTask.wait(
452
+ (textArr) => {
453
+ const text = textArr.join(" ");
454
+ this.createRedactionsFromSelection(documentId, formattedSelection, text);
455
+ },
456
+ () => {
457
+ this.createRedactionsFromSelection(documentId, formattedSelection);
458
+ }
459
+ );
362
460
  });
363
461
  unsubscribers.push(unsubSelection, unsubEndSelection);
364
462
  }
463
+ if (this.useAnnotationMode && this.annotationCapability) {
464
+ const annoScope = this.annotationCapability.forDocument(documentId);
465
+ const unsubEvents = annoScope.onAnnotationEvent((event) => {
466
+ var _a;
467
+ if (event.type === "loaded") {
468
+ this.syncFromAnnotationLoad(documentId);
469
+ return;
470
+ }
471
+ if (((_a = event.annotation) == null ? void 0 : _a.type) !== PdfAnnotationSubtype.REDACT) return;
472
+ const redactAnno = event.annotation;
473
+ if (event.type === "create") {
474
+ this.syncFromAnnotationCreate(documentId, redactAnno);
475
+ this.events$.emit({
476
+ type: "add",
477
+ documentId,
478
+ items: [this.annotationToRedactionItem(redactAnno)]
479
+ });
480
+ } else if (event.type === "update") {
481
+ this.logger.debug("RedactionPlugin", "AnnotationUpdated", {
482
+ documentId,
483
+ redactAnno,
484
+ patch: event.patch
485
+ });
486
+ this.syncFromAnnotationUpdate(
487
+ documentId,
488
+ redactAnno,
489
+ event.patch
490
+ );
491
+ } else if (event.type === "delete") {
492
+ this.syncFromAnnotationDelete(documentId, redactAnno);
493
+ this.events$.emit({
494
+ type: "remove",
495
+ documentId,
496
+ page: redactAnno.pageIndex,
497
+ id: redactAnno.id
498
+ });
499
+ }
500
+ });
501
+ const unsubState = annoScope.onStateChange(() => {
502
+ this.syncSelectionFromAnnotation(documentId);
503
+ });
504
+ unsubscribers.push(unsubEvents, unsubState);
505
+ }
365
506
  this.documentUnsubscribers.set(documentId, unsubscribers);
366
507
  this.logger.debug(
367
508
  "RedactionPlugin",
@@ -370,8 +511,9 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
370
511
  );
371
512
  }
372
513
  onDocumentLoaded(documentId) {
373
- var _a;
374
- (_a = this.selectionCapability) == null ? void 0 : _a.enableForMode(
514
+ var _a, _b;
515
+ (_a = this.selectionCapability) == null ? void 0 : _a.enableForMode(RedactionMode.Redact, { showRects: false }, documentId);
516
+ (_b = this.selectionCapability) == null ? void 0 : _b.enableForMode(
375
517
  RedactionMode.RedactSelection,
376
518
  { showRects: false },
377
519
  documentId
@@ -403,9 +545,16 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
403
545
  return {
404
546
  // Active document operations
405
547
  queueCurrentSelectionAsPending: () => this.queueCurrentSelectionAsPending(),
548
+ // Unified redact mode
549
+ enableRedact: () => this.enableRedact(),
550
+ toggleRedact: () => this.toggleRedact(),
551
+ isRedactActive: () => this.isRedactActive(),
552
+ endRedact: () => this.endRedact(),
553
+ // Legacy marquee mode
406
554
  enableMarqueeRedact: () => this.enableMarqueeRedact(),
407
555
  toggleMarqueeRedact: () => this.toggleMarqueeRedact(),
408
556
  isMarqueeRedactActive: () => this.isMarqueeRedactActive(),
557
+ // Legacy selection mode
409
558
  enableRedactSelection: () => this.enableRedactSelection(),
410
559
  toggleRedactSelection: () => this.toggleRedactSelection(),
411
560
  isRedactSelectionActive: () => this.isRedactSelectionActive(),
@@ -414,8 +563,6 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
414
563
  clearPending: () => this.clearPendingItems(),
415
564
  commitAllPending: () => this.commitAllPending(),
416
565
  commitPending: (page, id) => this.commitPendingOne(page, id),
417
- endRedaction: () => this.endRedactionMode(),
418
- startRedaction: () => this.startRedactionMode(),
419
566
  selectPending: (page, id) => this.selectPending(page, id),
420
567
  getSelectedPending: () => this.getSelectedPending(),
421
568
  deselectPending: () => this.deselectPending(),
@@ -435,9 +582,16 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
435
582
  createRedactionScope(documentId) {
436
583
  return {
437
584
  queueCurrentSelectionAsPending: () => this.queueCurrentSelectionAsPending(documentId),
585
+ // Unified redact mode
586
+ enableRedact: () => this.enableRedact(documentId),
587
+ toggleRedact: () => this.toggleRedact(documentId),
588
+ isRedactActive: () => this.isRedactActive(documentId),
589
+ endRedact: () => this.endRedact(documentId),
590
+ // Legacy marquee mode
438
591
  enableMarqueeRedact: () => this.enableMarqueeRedact(documentId),
439
592
  toggleMarqueeRedact: () => this.toggleMarqueeRedact(documentId),
440
593
  isMarqueeRedactActive: () => this.isMarqueeRedactActive(documentId),
594
+ // Legacy selection mode
441
595
  enableRedactSelection: () => this.enableRedactSelection(documentId),
442
596
  toggleRedactSelection: () => this.toggleRedactSelection(documentId),
443
597
  isRedactSelectionActive: () => this.isRedactSelectionActive(documentId),
@@ -446,8 +600,6 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
446
600
  clearPending: () => this.clearPendingItems(documentId),
447
601
  commitAllPending: () => this.commitAllPending(documentId),
448
602
  commitPending: (page, id) => this.commitPendingOne(page, id, documentId),
449
- endRedaction: () => this.endRedactionMode(documentId),
450
- startRedaction: () => this.startRedactionMode(documentId),
451
603
  selectPending: (page, id) => this.selectPending(page, id, documentId),
452
604
  getSelectedPending: () => this.getSelectedPending(documentId),
453
605
  deselectPending: () => this.deselectPending(documentId),
@@ -469,6 +621,26 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
469
621
  // ─────────────────────────────────────────────────────────
470
622
  // State Helpers
471
623
  // ─────────────────────────────────────────────────────────
624
+ /**
625
+ * Get pending redactions derived from annotation plugin (annotation mode only)
626
+ */
627
+ getPendingFromAnnotations(documentId) {
628
+ if (!this.annotationCapability) return {};
629
+ try {
630
+ const annoState = this.annotationCapability.forDocument(documentId).getState();
631
+ const result = {};
632
+ for (const ta of Object.values(annoState.byUid)) {
633
+ if (ta.object.type === PdfAnnotationSubtype.REDACT) {
634
+ const item = this.annotationToRedactionItem(ta.object);
635
+ const page = ta.object.pageIndex;
636
+ (result[page] ?? (result[page] = [])).push(item);
637
+ }
638
+ }
639
+ return result;
640
+ } catch {
641
+ return {};
642
+ }
643
+ }
472
644
  getDocumentState(documentId) {
473
645
  const id = documentId ?? this.getActiveDocumentId();
474
646
  return this.state.documents[id] ?? null;
@@ -481,6 +653,75 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
481
653
  return state;
482
654
  }
483
655
  // ─────────────────────────────────────────────────────────
656
+ // Annotation Mode State Sync
657
+ // ─────────────────────────────────────────────────────────
658
+ /**
659
+ * Sync internal state when REDACT annotation is created.
660
+ * Called from annotation event listener in annotation mode.
661
+ */
662
+ syncFromAnnotationCreate(documentId, annotation) {
663
+ const item = this.annotationToRedactionItem(annotation);
664
+ this.dispatch(addPending(documentId, [item]));
665
+ }
666
+ /**
667
+ * Sync internal state when REDACT annotation is updated (moved/resized/color changed).
668
+ * Called from annotation event listener in annotation mode.
669
+ */
670
+ syncFromAnnotationUpdate(documentId, annotation, patch) {
671
+ if (!("rect" in patch) && !("segmentRects" in patch) && !("strokeColor" in patch) && !("color" in patch))
672
+ return;
673
+ const updatePatch = {};
674
+ if (patch.rect) updatePatch.rect = patch.rect;
675
+ if (patch.segmentRects) updatePatch.rects = patch.segmentRects;
676
+ if (patch.strokeColor) updatePatch.markColor = patch.strokeColor;
677
+ if (patch.color) updatePatch.redactionColor = patch.color;
678
+ this.logger.debug("RedactionPlugin", "AnnotationUpdated", {
679
+ documentId,
680
+ annotation,
681
+ patch: updatePatch
682
+ });
683
+ this.dispatch(updatePending(documentId, annotation.pageIndex, annotation.id, updatePatch));
684
+ }
685
+ /**
686
+ * Sync internal state when REDACT annotation is deleted.
687
+ * Called from annotation event listener in annotation mode.
688
+ */
689
+ syncFromAnnotationDelete(documentId, annotation) {
690
+ this.dispatch(removePending(documentId, annotation.pageIndex, annotation.id));
691
+ }
692
+ /**
693
+ * Sync internal state from existing REDACT annotations after initial load.
694
+ * Called when annotation plugin emits 'loaded' event.
695
+ */
696
+ syncFromAnnotationLoad(documentId) {
697
+ const pending = this.getPendingFromAnnotations(documentId);
698
+ this.dispatch(clearPending(documentId));
699
+ for (const [, items] of Object.entries(pending)) {
700
+ if (items.length > 0) {
701
+ this.dispatch(addPending(documentId, items));
702
+ }
703
+ }
704
+ }
705
+ /**
706
+ * Sync selection state from annotation plugin's selected REDACT annotation.
707
+ * Called when annotation plugin state changes.
708
+ */
709
+ syncSelectionFromAnnotation(documentId) {
710
+ var _a;
711
+ const annoState = (_a = this.annotationCapability) == null ? void 0 : _a.forDocument(documentId).getState();
712
+ if (!annoState) return;
713
+ const selectedRedact = annoState.selectedUids.map((uid) => annoState.byUid[uid]).find((ta) => (ta == null ? void 0 : ta.object.type) === PdfAnnotationSubtype.REDACT);
714
+ if (selectedRedact) {
715
+ const obj = selectedRedact.object;
716
+ this.dispatch(selectPending(documentId, obj.pageIndex, obj.id));
717
+ } else {
718
+ const docState = this.getDocumentState(documentId);
719
+ if (docState == null ? void 0 : docState.selected) {
720
+ this.dispatch(deselectPending(documentId));
721
+ }
722
+ }
723
+ }
724
+ // ─────────────────────────────────────────────────────────
484
725
  // Core Operations
485
726
  // ─────────────────────────────────────────────────────────
486
727
  addPendingItems(items, documentId) {
@@ -493,28 +734,57 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
493
734
  );
494
735
  return;
495
736
  }
496
- this.dispatch(addPending(id, items));
497
- this.emitPendingChange(id);
737
+ if (this.useAnnotationMode) {
738
+ const annoScope = this.annotationCapability.forDocument(id);
739
+ for (const item of items) {
740
+ const annotation = this.redactionItemToAnnotation(item);
741
+ annoScope.createAnnotation(item.page, annotation);
742
+ }
743
+ if (items.length > 0) {
744
+ const lastItem = items[items.length - 1];
745
+ annoScope.selectAnnotation(lastItem.page, lastItem.id);
746
+ }
747
+ } else {
748
+ this.dispatch(addPending(id, items));
749
+ }
498
750
  this.events$.emit({ type: "add", documentId: id, items });
499
751
  }
500
752
  removePendingItem(page, itemId, documentId) {
753
+ var _a;
501
754
  const id = documentId ?? this.getActiveDocumentId();
502
- this.dispatch(removePending(id, page, itemId));
503
- this.emitPendingChange(id);
755
+ if (this.useAnnotationMode) {
756
+ (_a = this.annotationCapability) == null ? void 0 : _a.forDocument(id).deleteAnnotation(page, itemId);
757
+ } else {
758
+ this.dispatch(removePending(id, page, itemId));
759
+ }
504
760
  this.events$.emit({ type: "remove", documentId: id, page, id: itemId });
505
761
  }
506
762
  clearPendingItems(documentId) {
763
+ var _a;
507
764
  const id = documentId ?? this.getActiveDocumentId();
508
- this.dispatch(clearPending(id));
509
- this.emitPendingChange(id);
765
+ if (this.useAnnotationMode) {
766
+ const pending = this.getPendingFromAnnotations(id);
767
+ const annoScope = (_a = this.annotationCapability) == null ? void 0 : _a.forDocument(id);
768
+ for (const [pageStr, items] of Object.entries(pending)) {
769
+ const page = Number(pageStr);
770
+ for (const item of items) {
771
+ annoScope == null ? void 0 : annoScope.deleteAnnotation(page, item.id);
772
+ }
773
+ }
774
+ } else {
775
+ this.dispatch(clearPending(id));
776
+ }
510
777
  this.events$.emit({ type: "clear", documentId: id });
511
778
  }
512
779
  selectPending(page, itemId, documentId) {
513
- var _a;
780
+ var _a, _b;
514
781
  const id = documentId ?? this.getActiveDocumentId();
515
- this.dispatch(selectPending(id, page, itemId));
516
- (_a = this.selectionCapability) == null ? void 0 : _a.forDocument(id).clear();
517
- this.emitSelectedChange(id);
782
+ if (this.useAnnotationMode) {
783
+ (_a = this.annotationCapability) == null ? void 0 : _a.forDocument(id).selectAnnotation(page, itemId);
784
+ } else {
785
+ this.dispatch(selectPending(id, page, itemId));
786
+ }
787
+ (_b = this.selectionCapability) == null ? void 0 : _b.forDocument(id).clear();
518
788
  }
519
789
  getSelectedPending(documentId) {
520
790
  var _a;
@@ -522,10 +792,17 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
522
792
  return ((_a = this.getDocumentState(id)) == null ? void 0 : _a.selected) ?? null;
523
793
  }
524
794
  deselectPending(documentId) {
795
+ var _a;
525
796
  const id = documentId ?? this.getActiveDocumentId();
526
- this.dispatch(deselectPending(id));
527
- this.emitSelectedChange(id);
797
+ if (this.useAnnotationMode) {
798
+ (_a = this.annotationCapability) == null ? void 0 : _a.forDocument(id).deselectAnnotation();
799
+ } else {
800
+ this.dispatch(deselectPending(id));
801
+ }
528
802
  }
803
+ // ─────────────────────────────────────────────────────────
804
+ // Legacy Selection Mode (text-based redactions)
805
+ // ─────────────────────────────────────────────────────────
529
806
  enableRedactSelection(documentId) {
530
807
  var _a;
531
808
  const id = documentId ?? this.getActiveDocumentId();
@@ -543,17 +820,25 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
543
820
  var _a;
544
821
  const id = documentId ?? this.getActiveDocumentId();
545
822
  const scope = (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id);
546
- if ((scope == null ? void 0 : scope.getActiveMode()) === RedactionMode.RedactSelection) {
547
- scope.activateDefaultMode();
823
+ const activeMode = scope == null ? void 0 : scope.getActiveMode();
824
+ if (activeMode === RedactionMode.RedactSelection) {
825
+ scope == null ? void 0 : scope.activateDefaultMode();
548
826
  } else {
827
+ if (!this.checkPermission(id, PdfPermissionFlag.ModifyContents)) {
828
+ return;
829
+ }
549
830
  scope == null ? void 0 : scope.activate(RedactionMode.RedactSelection);
550
831
  }
551
832
  }
552
833
  isRedactSelectionActive(documentId) {
553
834
  var _a;
554
835
  const id = documentId ?? this.getActiveDocumentId();
555
- return ((_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).getActiveMode()) === RedactionMode.RedactSelection;
836
+ const activeMode = (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).getActiveMode();
837
+ return activeMode === RedactionMode.Redact || activeMode === RedactionMode.RedactSelection;
556
838
  }
839
+ // ─────────────────────────────────────────────────────────
840
+ // Legacy Marquee Mode (area-based redactions)
841
+ // ─────────────────────────────────────────────────────────
557
842
  enableMarqueeRedact(documentId) {
558
843
  var _a;
559
844
  const id = documentId ?? this.getActiveDocumentId();
@@ -571,31 +856,59 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
571
856
  var _a;
572
857
  const id = documentId ?? this.getActiveDocumentId();
573
858
  const scope = (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id);
574
- if ((scope == null ? void 0 : scope.getActiveMode()) === RedactionMode.MarqueeRedact) {
575
- scope.activateDefaultMode();
859
+ const activeMode = scope == null ? void 0 : scope.getActiveMode();
860
+ if (activeMode === RedactionMode.MarqueeRedact) {
861
+ scope == null ? void 0 : scope.activateDefaultMode();
576
862
  } else {
863
+ if (!this.checkPermission(id, PdfPermissionFlag.ModifyContents)) {
864
+ return;
865
+ }
577
866
  scope == null ? void 0 : scope.activate(RedactionMode.MarqueeRedact);
578
867
  }
579
868
  }
580
869
  isMarqueeRedactActive(documentId) {
581
870
  var _a;
582
871
  const id = documentId ?? this.getActiveDocumentId();
583
- return ((_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).getActiveMode()) === RedactionMode.MarqueeRedact;
872
+ const activeMode = (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).getActiveMode();
873
+ return activeMode === RedactionMode.Redact || activeMode === RedactionMode.MarqueeRedact;
584
874
  }
585
- startRedactionMode(documentId) {
875
+ // ─────────────────────────────────────────────────────────
876
+ // Unified Redact Mode (recommended)
877
+ // ─────────────────────────────────────────────────────────
878
+ enableRedact(documentId) {
586
879
  var _a;
587
880
  const id = documentId ?? this.getActiveDocumentId();
588
881
  if (!this.checkPermission(id, PdfPermissionFlag.ModifyContents)) {
589
882
  this.logger.debug(
590
883
  "RedactionPlugin",
591
- "StartRedactionMode",
592
- `Cannot start redaction mode: document ${id} lacks ModifyContents permission`
884
+ "EnableRedact",
885
+ `Cannot enable redact mode: document ${id} lacks ModifyContents permission`
593
886
  );
594
887
  return;
595
888
  }
596
- (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).activate(RedactionMode.RedactSelection);
889
+ (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).activate(RedactionMode.Redact);
890
+ }
891
+ toggleRedact(documentId) {
892
+ var _a;
893
+ const id = documentId ?? this.getActiveDocumentId();
894
+ const scope = (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id);
895
+ const activeMode = scope == null ? void 0 : scope.getActiveMode();
896
+ if (activeMode === RedactionMode.Redact) {
897
+ scope == null ? void 0 : scope.activateDefaultMode();
898
+ } else {
899
+ if (!this.checkPermission(id, PdfPermissionFlag.ModifyContents)) {
900
+ return;
901
+ }
902
+ scope == null ? void 0 : scope.activate(RedactionMode.Redact);
903
+ }
597
904
  }
598
- endRedactionMode(documentId) {
905
+ isRedactActive(documentId) {
906
+ var _a;
907
+ const id = documentId ?? this.getActiveDocumentId();
908
+ const activeMode = (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).getActiveMode();
909
+ return activeMode === RedactionMode.Redact;
910
+ }
911
+ endRedact(documentId) {
599
912
  var _a;
600
913
  const id = documentId ?? this.getActiveDocumentId();
601
914
  (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).activateDefaultMode();
@@ -608,6 +921,18 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
608
921
  return (emitter == null ? void 0 : emitter.on(callback)) ?? (() => {
609
922
  });
610
923
  }
924
+ /**
925
+ * Get the stroke color for redaction previews.
926
+ * In annotation mode: returns tool's defaults.strokeColor
927
+ * In legacy mode: returns hardcoded red
928
+ */
929
+ getPreviewStrokeColor() {
930
+ if (this.useAnnotationMode && this.annotationCapability) {
931
+ const tool = this.annotationCapability.getTool("redact");
932
+ return (tool == null ? void 0 : tool.defaults.strokeColor) ?? "#FF0000";
933
+ }
934
+ return "#FF0000";
935
+ }
611
936
  registerMarqueeOnPage(opts) {
612
937
  if (!this.interactionManagerCapability) {
613
938
  this.logger.warn(
@@ -636,17 +961,23 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
636
961
  onPreview: opts.callback.onPreview,
637
962
  onCommit: (r) => {
638
963
  var _a, _b;
639
- const item = {
640
- id: uuidV4(),
641
- kind: "area",
642
- page: opts.pageIndex,
643
- rect: r
644
- };
645
- this.dispatch(addPending(opts.documentId, [item]));
646
- this.emitPendingChange(opts.documentId);
647
964
  (_b = (_a = opts.callback).onCommit) == null ? void 0 : _b.call(_a, r);
648
- this.enableRedactSelection(opts.documentId);
649
- this.selectPending(opts.pageIndex, item.id, opts.documentId);
965
+ if (this.useAnnotationMode) {
966
+ this.createRedactAnnotationFromArea(opts.documentId, opts.pageIndex, r);
967
+ } else {
968
+ const redactionColor = this.config.drawBlackBoxes ? "#000000" : "transparent";
969
+ const item = {
970
+ id: uuidV4(),
971
+ kind: "area",
972
+ page: opts.pageIndex,
973
+ rect: r,
974
+ source: "legacy",
975
+ markColor: "#FF0000",
976
+ redactionColor
977
+ };
978
+ this.dispatch(addPending(opts.documentId, [item]));
979
+ this.selectPending(opts.pageIndex, item.id, opts.documentId);
980
+ }
650
981
  }
651
982
  });
652
983
  const off = this.interactionManagerCapability.registerAlways({
@@ -664,6 +995,12 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
664
995
  }
665
996
  });
666
997
  const off2 = this.interactionManagerCapability.registerHandlers({
998
+ documentId: opts.documentId,
999
+ modeId: RedactionMode.Redact,
1000
+ handlers,
1001
+ pageIndex: opts.pageIndex
1002
+ });
1003
+ const off3 = this.interactionManagerCapability.registerHandlers({
667
1004
  documentId: opts.documentId,
668
1005
  modeId: RedactionMode.MarqueeRedact,
669
1006
  handlers,
@@ -672,38 +1009,44 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
672
1009
  return () => {
673
1010
  off();
674
1011
  off2();
1012
+ off3();
675
1013
  };
676
1014
  }
677
1015
  queueCurrentSelectionAsPending(documentId) {
678
1016
  const id = documentId ?? this.getActiveDocumentId();
679
- if (!this.selectionCapability)
1017
+ if (!this.selectionCapability) {
680
1018
  return PdfTaskHelper.reject({
681
1019
  code: PdfErrorCode.NotFound,
682
1020
  message: "[RedactionPlugin] selection plugin required"
683
1021
  });
1022
+ }
684
1023
  const coreDoc = this.coreState.core.documents[id];
685
- if (!(coreDoc == null ? void 0 : coreDoc.document))
1024
+ if (!(coreDoc == null ? void 0 : coreDoc.document)) {
686
1025
  return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
1026
+ }
687
1027
  const selectionScope = this.selectionCapability.forDocument(id);
688
1028
  const formatted = selectionScope.getFormattedSelection();
689
1029
  if (!formatted.length) return PdfTaskHelper.resolve(true);
690
- const uniqueId = uuidV4();
691
- const items = formatted.map((s) => ({
692
- id: uniqueId,
693
- kind: "text",
694
- page: s.pageIndex,
695
- rect: s.rect,
696
- rects: s.segmentRects
697
- }));
698
- this.enableRedactSelection(id);
699
- this.dispatch(addPending(id, items));
700
- this.emitPendingChange(id);
701
- const last = items[items.length - 1];
702
- this.selectPending(last.page, last.id, id);
1030
+ const textTask = selectionScope.getSelectedText();
703
1031
  const emitter = this.redactionSelection$.get(id);
704
1032
  emitter == null ? void 0 : emitter.emit([]);
705
1033
  selectionScope.clear();
706
- return PdfTaskHelper.resolve(true);
1034
+ if (!this.useAnnotationMode) {
1035
+ this.enableRedactSelection(id);
1036
+ }
1037
+ const task = new Task();
1038
+ textTask.wait(
1039
+ (textArr) => {
1040
+ const text = textArr.join(" ");
1041
+ this.createRedactionsFromSelection(id, formatted, text);
1042
+ task.resolve(true);
1043
+ },
1044
+ () => {
1045
+ this.createRedactionsFromSelection(id, formatted);
1046
+ task.resolve(true);
1047
+ }
1048
+ );
1049
+ return task;
707
1050
  }
708
1051
  commitPendingOne(page, id, documentId) {
709
1052
  const docId = documentId ?? this.getActiveDocumentId();
@@ -721,6 +1064,17 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
721
1064
  const coreDoc = this.coreState.core.documents[docId];
722
1065
  if (!(coreDoc == null ? void 0 : coreDoc.document))
723
1066
  return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
1067
+ const pdfPage = coreDoc.document.pages[page];
1068
+ if (!pdfPage)
1069
+ return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Page not found" });
1070
+ if (this.useAnnotationMode) {
1071
+ this.logger.debug(
1072
+ "RedactionPlugin",
1073
+ "CommitPendingOne",
1074
+ `Applying redaction in annotation mode: page ${page}, id ${id}`
1075
+ );
1076
+ return this.applyRedactionAnnotationMode(docId, coreDoc.document, pdfPage, id);
1077
+ }
724
1078
  const docState = this.getDocumentState(docId);
725
1079
  if (!docState) {
726
1080
  return PdfTaskHelper.reject({
@@ -729,18 +1083,27 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
729
1083
  });
730
1084
  }
731
1085
  const item = (docState.pending[page] ?? []).find((it) => it.id === id);
732
- if (!item) return PdfTaskHelper.resolve(true);
1086
+ if (!item) {
1087
+ this.logger.debug(
1088
+ "RedactionPlugin",
1089
+ "CommitPendingOne",
1090
+ `No pending item found for page ${page}, id ${id}`
1091
+ );
1092
+ return PdfTaskHelper.resolve(true);
1093
+ }
1094
+ return this.commitPendingOneLegacy(docId, coreDoc.document, pdfPage, page, item);
1095
+ }
1096
+ /**
1097
+ * Legacy commit single redaction using redactTextInRects
1098
+ */
1099
+ commitPendingOneLegacy(docId, doc, pdfPage, page, item) {
733
1100
  const rects = item.kind === "text" ? item.rects : [item.rect];
734
- const pdfPage = coreDoc.document.pages[page];
735
- if (!pdfPage)
736
- return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Page not found" });
737
1101
  const task = new Task();
738
- this.engine.redactTextInRects(coreDoc.document, pdfPage, rects, {
1102
+ this.engine.redactTextInRects(doc, pdfPage, rects, {
739
1103
  drawBlackBoxes: this.config.drawBlackBoxes
740
1104
  }).wait(
741
1105
  () => {
742
- this.dispatch(removePending(docId, page, id));
743
- this.emitPendingChange(docId);
1106
+ this.dispatch(removePending(docId, page, item.id));
744
1107
  this.dispatchCoreAction(refreshPages(docId, [page]));
745
1108
  this.events$.emit({ type: "commit", documentId: docId, success: true });
746
1109
  task.resolve(true);
@@ -757,6 +1120,73 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
757
1120
  );
758
1121
  return task;
759
1122
  }
1123
+ /**
1124
+ * Annotation mode: Apply single redaction using engine.applyRedaction
1125
+ */
1126
+ applyRedactionAnnotationMode(docId, doc, pdfPage, annotationId) {
1127
+ var _a;
1128
+ const task = new Task();
1129
+ const anno = (_a = this.annotationCapability) == null ? void 0 : _a.forDocument(docId).getAnnotationById(annotationId);
1130
+ this.logger.debug(
1131
+ "RedactionPlugin",
1132
+ "ApplyRedactionAnnotationMode",
1133
+ `Looking for annotation ${annotationId}, found: ${!!anno}, type: ${anno == null ? void 0 : anno.object.type}`
1134
+ );
1135
+ if (!anno || anno.object.type !== PdfAnnotationSubtype.REDACT) {
1136
+ this.logger.warn(
1137
+ "RedactionPlugin",
1138
+ "ApplyRedactionAnnotationMode",
1139
+ `Redaction annotation not found or wrong type: ${annotationId}`
1140
+ );
1141
+ return PdfTaskHelper.reject({
1142
+ code: PdfErrorCode.NotFound,
1143
+ message: "Redaction annotation not found"
1144
+ });
1145
+ }
1146
+ this.logger.debug(
1147
+ "RedactionPlugin",
1148
+ "ApplyRedactionAnnotationMode",
1149
+ `Calling engine.applyRedaction for annotation ${annotationId} on page ${pdfPage.index}`
1150
+ );
1151
+ this.engine.applyRedaction(doc, pdfPage, anno.object).wait(
1152
+ () => {
1153
+ var _a2, _b;
1154
+ this.logger.debug(
1155
+ "RedactionPlugin",
1156
+ "ApplyRedactionAnnotationMode",
1157
+ `Successfully applied redaction ${annotationId} on page ${pdfPage.index}`
1158
+ );
1159
+ (_a2 = this.annotationCapability) == null ? void 0 : _a2.forDocument(docId).purgeAnnotation(pdfPage.index, annotationId);
1160
+ this.dispatch(removePending(docId, pdfPage.index, annotationId));
1161
+ (_b = this.historyCapability) == null ? void 0 : _b.forDocument(docId).purgeByMetadata(
1162
+ (meta) => {
1163
+ var _a3;
1164
+ return ((_a3 = meta == null ? void 0 : meta.annotationIds) == null ? void 0 : _a3.includes(annotationId)) ?? false;
1165
+ },
1166
+ "annotations"
1167
+ );
1168
+ this.dispatchCoreAction(refreshPages(docId, [pdfPage.index]));
1169
+ this.events$.emit({ type: "commit", documentId: docId, success: true });
1170
+ task.resolve(true);
1171
+ },
1172
+ (error) => {
1173
+ var _a2;
1174
+ this.logger.error(
1175
+ "RedactionPlugin",
1176
+ "ApplyRedactionAnnotationMode",
1177
+ `Failed to apply redaction ${annotationId}: ${((_a2 = error.reason) == null ? void 0 : _a2.message) ?? "Unknown error"}`
1178
+ );
1179
+ this.events$.emit({
1180
+ type: "commit",
1181
+ documentId: docId,
1182
+ success: false,
1183
+ error: error.reason
1184
+ });
1185
+ task.reject({ code: PdfErrorCode.Unknown, message: "Failed to apply redaction" });
1186
+ }
1187
+ );
1188
+ return task;
1189
+ }
760
1190
  commitAllPending(documentId) {
761
1191
  const docId = documentId ?? this.getActiveDocumentId();
762
1192
  if (!this.checkPermission(docId, PdfPermissionFlag.ModifyContents)) {
@@ -780,6 +1210,16 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
780
1210
  message: "Document state not found"
781
1211
  });
782
1212
  }
1213
+ if (this.useAnnotationMode) {
1214
+ return this.applyAllRedactionsAnnotationMode(docId, coreDoc.document);
1215
+ } else {
1216
+ return this.commitAllPendingLegacy(docId, coreDoc.document, docState);
1217
+ }
1218
+ }
1219
+ /**
1220
+ * Legacy commit all redactions using redactTextInRects
1221
+ */
1222
+ commitAllPendingLegacy(docId, doc, docState) {
783
1223
  const perPage = /* @__PURE__ */ new Map();
784
1224
  for (const [page, items] of Object.entries(docState.pending)) {
785
1225
  const p = Number(page);
@@ -793,11 +1233,11 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
793
1233
  const pagesToRefresh = Array.from(perPage.entries()).filter(([_, rects]) => rects.length > 0).map(([pageIndex]) => pageIndex);
794
1234
  const tasks = [];
795
1235
  for (const [pageIndex, rects] of perPage) {
796
- const page = coreDoc.document.pages[pageIndex];
1236
+ const page = doc.pages[pageIndex];
797
1237
  if (!page) continue;
798
1238
  if (!rects.length) continue;
799
1239
  tasks.push(
800
- this.engine.redactTextInRects(coreDoc.document, page, rects, {
1240
+ this.engine.redactTextInRects(doc, page, rects, {
801
1241
  drawBlackBoxes: this.config.drawBlackBoxes
802
1242
  })
803
1243
  );
@@ -807,7 +1247,6 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
807
1247
  () => {
808
1248
  this.dispatch(clearPending(docId));
809
1249
  this.dispatchCoreAction(refreshPages(docId, pagesToRefresh));
810
- this.emitPendingChange(docId);
811
1250
  this.events$.emit({ type: "commit", documentId: docId, success: true });
812
1251
  task.resolve(true);
813
1252
  },
@@ -823,6 +1262,208 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
823
1262
  );
824
1263
  return task;
825
1264
  }
1265
+ /**
1266
+ * Annotation mode: Apply all redactions using engine.applyAllRedactions per page
1267
+ */
1268
+ applyAllRedactionsAnnotationMode(docId, doc) {
1269
+ const annoState = this.annotationCapability.forDocument(docId).getState();
1270
+ const redactAnnotationsByPage = /* @__PURE__ */ new Map();
1271
+ for (const ta of Object.values(annoState.byUid)) {
1272
+ if (ta.object.type === PdfAnnotationSubtype.REDACT) {
1273
+ const pageIds = redactAnnotationsByPage.get(ta.object.pageIndex) ?? [];
1274
+ pageIds.push(ta.object.id);
1275
+ redactAnnotationsByPage.set(ta.object.pageIndex, pageIds);
1276
+ }
1277
+ }
1278
+ const pagesToProcess = Array.from(redactAnnotationsByPage.keys());
1279
+ if (pagesToProcess.length === 0) {
1280
+ return PdfTaskHelper.resolve(true);
1281
+ }
1282
+ const tasks = [];
1283
+ for (const pageIndex of pagesToProcess) {
1284
+ const page = doc.pages[pageIndex];
1285
+ if (!page) continue;
1286
+ tasks.push(this.engine.applyAllRedactions(doc, page));
1287
+ }
1288
+ const task = new Task();
1289
+ Task.all(tasks).wait(
1290
+ () => {
1291
+ var _a, _b;
1292
+ const annoScope = (_a = this.annotationCapability) == null ? void 0 : _a.forDocument(docId);
1293
+ const allPurgedIds = [];
1294
+ for (const [pageIndex, ids] of redactAnnotationsByPage) {
1295
+ for (const id of ids) {
1296
+ annoScope == null ? void 0 : annoScope.purgeAnnotation(pageIndex, id);
1297
+ this.dispatch(removePending(docId, pageIndex, id));
1298
+ allPurgedIds.push(id);
1299
+ }
1300
+ }
1301
+ if (allPurgedIds.length > 0) {
1302
+ (_b = this.historyCapability) == null ? void 0 : _b.forDocument(docId).purgeByMetadata(
1303
+ (meta) => {
1304
+ var _a2;
1305
+ return ((_a2 = meta == null ? void 0 : meta.annotationIds) == null ? void 0 : _a2.some((id) => allPurgedIds.includes(id))) ?? false;
1306
+ },
1307
+ "annotations"
1308
+ );
1309
+ }
1310
+ this.dispatchCoreAction(refreshPages(docId, pagesToProcess));
1311
+ this.events$.emit({ type: "commit", documentId: docId, success: true });
1312
+ task.resolve(true);
1313
+ },
1314
+ (error) => {
1315
+ this.events$.emit({
1316
+ type: "commit",
1317
+ documentId: docId,
1318
+ success: false,
1319
+ error: error.reason
1320
+ });
1321
+ task.reject({ code: PdfErrorCode.Unknown, message: "Failed to apply redactions" });
1322
+ }
1323
+ );
1324
+ return task;
1325
+ }
1326
+ // ─────────────────────────────────────────────────────────
1327
+ // Annotation Mode Helpers
1328
+ // ─────────────────────────────────────────────────────────
1329
+ /**
1330
+ * Create REDACT annotations from text selection (annotation mode only)
1331
+ * @returns Array of annotation IDs that were created
1332
+ */
1333
+ createRedactAnnotationsFromSelection(documentId, formattedSelection, text) {
1334
+ if (!this.annotationCapability) return [];
1335
+ const annoScope = this.annotationCapability.forDocument(documentId);
1336
+ const tool = this.annotationCapability.getTool("redact");
1337
+ const defaults = tool == null ? void 0 : tool.defaults;
1338
+ const annotationIds = [];
1339
+ for (const selection of formattedSelection) {
1340
+ const annotationId = uuidV4();
1341
+ annotationIds.push(annotationId);
1342
+ const annotation = {
1343
+ ...defaults,
1344
+ id: annotationId,
1345
+ type: PdfAnnotationSubtype.REDACT,
1346
+ pageIndex: selection.pageIndex,
1347
+ rect: selection.rect,
1348
+ segmentRects: selection.segmentRects,
1349
+ ...text ? { custom: { text } } : {},
1350
+ created: /* @__PURE__ */ new Date()
1351
+ };
1352
+ annoScope.createAnnotation(selection.pageIndex, annotation);
1353
+ if (selection === formattedSelection[formattedSelection.length - 1]) {
1354
+ annoScope.selectAnnotation(selection.pageIndex, annotationId);
1355
+ }
1356
+ }
1357
+ if (text) {
1358
+ for (let i = 0; i < annotationIds.length; i++) {
1359
+ const pageIndex = formattedSelection[i].pageIndex;
1360
+ this.dispatch(updatePending(documentId, pageIndex, annotationIds[i], { text }));
1361
+ }
1362
+ }
1363
+ return annotationIds;
1364
+ }
1365
+ /**
1366
+ * Create legacy RedactionItems from text selection (legacy mode only)
1367
+ */
1368
+ createLegacyRedactionsFromSelection(documentId, formattedSelection, text) {
1369
+ const redactionColor = this.config.drawBlackBoxes ? "#000000" : "transparent";
1370
+ const items = formattedSelection.map((s) => ({
1371
+ id: uuidV4(),
1372
+ kind: "text",
1373
+ page: s.pageIndex,
1374
+ rect: s.rect,
1375
+ rects: s.segmentRects,
1376
+ source: "legacy",
1377
+ markColor: "#FF0000",
1378
+ redactionColor,
1379
+ text
1380
+ }));
1381
+ this.dispatch(addPending(documentId, items));
1382
+ if (items.length) {
1383
+ this.selectPending(items[items.length - 1].page, items[items.length - 1].id, documentId);
1384
+ }
1385
+ }
1386
+ /**
1387
+ * Unified method to create redactions from text selection.
1388
+ * Delegates to annotation mode or legacy mode helper based on configuration.
1389
+ */
1390
+ createRedactionsFromSelection(documentId, formattedSelection, text) {
1391
+ if (this.useAnnotationMode) {
1392
+ this.createRedactAnnotationsFromSelection(documentId, formattedSelection, text);
1393
+ } else {
1394
+ this.createLegacyRedactionsFromSelection(documentId, formattedSelection, text);
1395
+ }
1396
+ }
1397
+ /**
1398
+ * Create a REDACT annotation from an area/marquee selection (annotation mode only)
1399
+ */
1400
+ createRedactAnnotationFromArea(documentId, pageIndex, rect) {
1401
+ if (!this.annotationCapability) return;
1402
+ const annoScope = this.annotationCapability.forDocument(documentId);
1403
+ const tool = this.annotationCapability.getTool("redact");
1404
+ const defaults = tool == null ? void 0 : tool.defaults;
1405
+ const annotationId = uuidV4();
1406
+ const annotation = {
1407
+ ...defaults,
1408
+ id: annotationId,
1409
+ type: PdfAnnotationSubtype.REDACT,
1410
+ pageIndex,
1411
+ rect,
1412
+ segmentRects: [],
1413
+ // No segment rects for area redaction
1414
+ created: /* @__PURE__ */ new Date()
1415
+ };
1416
+ annoScope.createAnnotation(pageIndex, annotation);
1417
+ annoScope.selectAnnotation(pageIndex, annotationId);
1418
+ }
1419
+ /**
1420
+ * Convert a RedactionItem to a PdfRedactAnnoObject
1421
+ */
1422
+ redactionItemToAnnotation(item) {
1423
+ var _a;
1424
+ const tool = (_a = this.annotationCapability) == null ? void 0 : _a.getTool("redact");
1425
+ const defaults = (tool == null ? void 0 : tool.defaults) ?? {};
1426
+ return {
1427
+ ...defaults,
1428
+ id: item.id,
1429
+ type: PdfAnnotationSubtype.REDACT,
1430
+ pageIndex: item.page,
1431
+ rect: item.rect,
1432
+ segmentRects: item.kind === "text" ? item.rects : [],
1433
+ created: /* @__PURE__ */ new Date()
1434
+ };
1435
+ }
1436
+ /**
1437
+ * Convert a PdfRedactAnnoObject to a RedactionItem
1438
+ */
1439
+ annotationToRedactionItem(anno) {
1440
+ var _a;
1441
+ const markColor = anno.strokeColor ?? "#FF0000";
1442
+ const redactionColor = anno.color ?? "transparent";
1443
+ if (anno.segmentRects && anno.segmentRects.length > 0) {
1444
+ return {
1445
+ id: anno.id,
1446
+ kind: "text",
1447
+ page: anno.pageIndex,
1448
+ rect: anno.rect,
1449
+ rects: anno.segmentRects,
1450
+ source: "annotation",
1451
+ markColor,
1452
+ redactionColor,
1453
+ ...((_a = anno.custom) == null ? void 0 : _a.text) ? { text: anno.custom.text } : {}
1454
+ };
1455
+ } else {
1456
+ return {
1457
+ id: anno.id,
1458
+ kind: "area",
1459
+ page: anno.pageIndex,
1460
+ rect: anno.rect,
1461
+ source: "annotation",
1462
+ markColor,
1463
+ redactionColor
1464
+ };
1465
+ }
1466
+ }
826
1467
  // ─────────────────────────────────────────────────────────
827
1468
  // Event Emission Helpers
828
1469
  // ─────────────────────────────────────────────────────────
@@ -883,7 +1524,7 @@ const manifest = {
883
1524
  version: "1.0.0",
884
1525
  provides: ["redaction"],
885
1526
  requires: [],
886
- optional: ["interaction-manager", "selection"],
1527
+ optional: ["interaction-manager", "selection", "annotation"],
887
1528
  defaultConfig: {
888
1529
  drawBlackBoxes: true
889
1530
  }
@@ -914,6 +1555,8 @@ export {
914
1555
  hasPendingRedactions,
915
1556
  initialDocumentState,
916
1557
  initialState,
917
- manifest
1558
+ manifest,
1559
+ redactTool,
1560
+ redactTools
918
1561
  };
919
1562
  //# sourceMappingURL=index.js.map