@embedpdf/plugin-redaction 2.2.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 +752 -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,13 @@ 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(RedactionMode.RedactSelection, documentId);
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(
517
+ RedactionMode.RedactSelection,
518
+ { showRects: false },
519
+ documentId
520
+ );
375
521
  }
376
522
  onDocumentClosed(documentId) {
377
523
  this.dispatch(cleanupRedactionState(documentId));
@@ -399,9 +545,16 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
399
545
  return {
400
546
  // Active document operations
401
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
402
554
  enableMarqueeRedact: () => this.enableMarqueeRedact(),
403
555
  toggleMarqueeRedact: () => this.toggleMarqueeRedact(),
404
556
  isMarqueeRedactActive: () => this.isMarqueeRedactActive(),
557
+ // Legacy selection mode
405
558
  enableRedactSelection: () => this.enableRedactSelection(),
406
559
  toggleRedactSelection: () => this.toggleRedactSelection(),
407
560
  isRedactSelectionActive: () => this.isRedactSelectionActive(),
@@ -410,8 +563,6 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
410
563
  clearPending: () => this.clearPendingItems(),
411
564
  commitAllPending: () => this.commitAllPending(),
412
565
  commitPending: (page, id) => this.commitPendingOne(page, id),
413
- endRedaction: () => this.endRedactionMode(),
414
- startRedaction: () => this.startRedactionMode(),
415
566
  selectPending: (page, id) => this.selectPending(page, id),
416
567
  getSelectedPending: () => this.getSelectedPending(),
417
568
  deselectPending: () => this.deselectPending(),
@@ -431,9 +582,16 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
431
582
  createRedactionScope(documentId) {
432
583
  return {
433
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
434
591
  enableMarqueeRedact: () => this.enableMarqueeRedact(documentId),
435
592
  toggleMarqueeRedact: () => this.toggleMarqueeRedact(documentId),
436
593
  isMarqueeRedactActive: () => this.isMarqueeRedactActive(documentId),
594
+ // Legacy selection mode
437
595
  enableRedactSelection: () => this.enableRedactSelection(documentId),
438
596
  toggleRedactSelection: () => this.toggleRedactSelection(documentId),
439
597
  isRedactSelectionActive: () => this.isRedactSelectionActive(documentId),
@@ -442,8 +600,6 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
442
600
  clearPending: () => this.clearPendingItems(documentId),
443
601
  commitAllPending: () => this.commitAllPending(documentId),
444
602
  commitPending: (page, id) => this.commitPendingOne(page, id, documentId),
445
- endRedaction: () => this.endRedactionMode(documentId),
446
- startRedaction: () => this.startRedactionMode(documentId),
447
603
  selectPending: (page, id) => this.selectPending(page, id, documentId),
448
604
  getSelectedPending: () => this.getSelectedPending(documentId),
449
605
  deselectPending: () => this.deselectPending(documentId),
@@ -465,6 +621,26 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
465
621
  // ─────────────────────────────────────────────────────────
466
622
  // State Helpers
467
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
+ }
468
644
  getDocumentState(documentId) {
469
645
  const id = documentId ?? this.getActiveDocumentId();
470
646
  return this.state.documents[id] ?? null;
@@ -477,6 +653,75 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
477
653
  return state;
478
654
  }
479
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
+ // ─────────────────────────────────────────────────────────
480
725
  // Core Operations
481
726
  // ─────────────────────────────────────────────────────────
482
727
  addPendingItems(items, documentId) {
@@ -489,28 +734,57 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
489
734
  );
490
735
  return;
491
736
  }
492
- this.dispatch(addPending(id, items));
493
- 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
+ }
494
750
  this.events$.emit({ type: "add", documentId: id, items });
495
751
  }
496
752
  removePendingItem(page, itemId, documentId) {
753
+ var _a;
497
754
  const id = documentId ?? this.getActiveDocumentId();
498
- this.dispatch(removePending(id, page, itemId));
499
- 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
+ }
500
760
  this.events$.emit({ type: "remove", documentId: id, page, id: itemId });
501
761
  }
502
762
  clearPendingItems(documentId) {
763
+ var _a;
503
764
  const id = documentId ?? this.getActiveDocumentId();
504
- this.dispatch(clearPending(id));
505
- 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
+ }
506
777
  this.events$.emit({ type: "clear", documentId: id });
507
778
  }
508
779
  selectPending(page, itemId, documentId) {
509
- var _a;
780
+ var _a, _b;
510
781
  const id = documentId ?? this.getActiveDocumentId();
511
- this.dispatch(selectPending(id, page, itemId));
512
- (_a = this.selectionCapability) == null ? void 0 : _a.forDocument(id).clear();
513
- 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();
514
788
  }
515
789
  getSelectedPending(documentId) {
516
790
  var _a;
@@ -518,10 +792,17 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
518
792
  return ((_a = this.getDocumentState(id)) == null ? void 0 : _a.selected) ?? null;
519
793
  }
520
794
  deselectPending(documentId) {
795
+ var _a;
521
796
  const id = documentId ?? this.getActiveDocumentId();
522
- this.dispatch(deselectPending(id));
523
- 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
+ }
524
802
  }
803
+ // ─────────────────────────────────────────────────────────
804
+ // Legacy Selection Mode (text-based redactions)
805
+ // ─────────────────────────────────────────────────────────
525
806
  enableRedactSelection(documentId) {
526
807
  var _a;
527
808
  const id = documentId ?? this.getActiveDocumentId();
@@ -539,17 +820,25 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
539
820
  var _a;
540
821
  const id = documentId ?? this.getActiveDocumentId();
541
822
  const scope = (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id);
542
- if ((scope == null ? void 0 : scope.getActiveMode()) === RedactionMode.RedactSelection) {
543
- scope.activateDefaultMode();
823
+ const activeMode = scope == null ? void 0 : scope.getActiveMode();
824
+ if (activeMode === RedactionMode.RedactSelection) {
825
+ scope == null ? void 0 : scope.activateDefaultMode();
544
826
  } else {
827
+ if (!this.checkPermission(id, PdfPermissionFlag.ModifyContents)) {
828
+ return;
829
+ }
545
830
  scope == null ? void 0 : scope.activate(RedactionMode.RedactSelection);
546
831
  }
547
832
  }
548
833
  isRedactSelectionActive(documentId) {
549
834
  var _a;
550
835
  const id = documentId ?? this.getActiveDocumentId();
551
- 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;
552
838
  }
839
+ // ─────────────────────────────────────────────────────────
840
+ // Legacy Marquee Mode (area-based redactions)
841
+ // ─────────────────────────────────────────────────────────
553
842
  enableMarqueeRedact(documentId) {
554
843
  var _a;
555
844
  const id = documentId ?? this.getActiveDocumentId();
@@ -567,31 +856,59 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
567
856
  var _a;
568
857
  const id = documentId ?? this.getActiveDocumentId();
569
858
  const scope = (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id);
570
- if ((scope == null ? void 0 : scope.getActiveMode()) === RedactionMode.MarqueeRedact) {
571
- scope.activateDefaultMode();
859
+ const activeMode = scope == null ? void 0 : scope.getActiveMode();
860
+ if (activeMode === RedactionMode.MarqueeRedact) {
861
+ scope == null ? void 0 : scope.activateDefaultMode();
572
862
  } else {
863
+ if (!this.checkPermission(id, PdfPermissionFlag.ModifyContents)) {
864
+ return;
865
+ }
573
866
  scope == null ? void 0 : scope.activate(RedactionMode.MarqueeRedact);
574
867
  }
575
868
  }
576
869
  isMarqueeRedactActive(documentId) {
577
870
  var _a;
578
871
  const id = documentId ?? this.getActiveDocumentId();
579
- 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;
580
874
  }
581
- startRedactionMode(documentId) {
875
+ // ─────────────────────────────────────────────────────────
876
+ // Unified Redact Mode (recommended)
877
+ // ─────────────────────────────────────────────────────────
878
+ enableRedact(documentId) {
582
879
  var _a;
583
880
  const id = documentId ?? this.getActiveDocumentId();
584
881
  if (!this.checkPermission(id, PdfPermissionFlag.ModifyContents)) {
585
882
  this.logger.debug(
586
883
  "RedactionPlugin",
587
- "StartRedactionMode",
588
- `Cannot start redaction mode: document ${id} lacks ModifyContents permission`
884
+ "EnableRedact",
885
+ `Cannot enable redact mode: document ${id} lacks ModifyContents permission`
589
886
  );
590
887
  return;
591
888
  }
592
- (_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
+ }
593
904
  }
594
- 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) {
595
912
  var _a;
596
913
  const id = documentId ?? this.getActiveDocumentId();
597
914
  (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).activateDefaultMode();
@@ -604,6 +921,18 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
604
921
  return (emitter == null ? void 0 : emitter.on(callback)) ?? (() => {
605
922
  });
606
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
+ }
607
936
  registerMarqueeOnPage(opts) {
608
937
  if (!this.interactionManagerCapability) {
609
938
  this.logger.warn(
@@ -632,17 +961,23 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
632
961
  onPreview: opts.callback.onPreview,
633
962
  onCommit: (r) => {
634
963
  var _a, _b;
635
- const item = {
636
- id: uuidV4(),
637
- kind: "area",
638
- page: opts.pageIndex,
639
- rect: r
640
- };
641
- this.dispatch(addPending(opts.documentId, [item]));
642
- this.emitPendingChange(opts.documentId);
643
964
  (_b = (_a = opts.callback).onCommit) == null ? void 0 : _b.call(_a, r);
644
- this.enableRedactSelection(opts.documentId);
645
- 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
+ }
646
981
  }
647
982
  });
648
983
  const off = this.interactionManagerCapability.registerAlways({
@@ -660,6 +995,12 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
660
995
  }
661
996
  });
662
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({
663
1004
  documentId: opts.documentId,
664
1005
  modeId: RedactionMode.MarqueeRedact,
665
1006
  handlers,
@@ -668,38 +1009,44 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
668
1009
  return () => {
669
1010
  off();
670
1011
  off2();
1012
+ off3();
671
1013
  };
672
1014
  }
673
1015
  queueCurrentSelectionAsPending(documentId) {
674
1016
  const id = documentId ?? this.getActiveDocumentId();
675
- if (!this.selectionCapability)
1017
+ if (!this.selectionCapability) {
676
1018
  return PdfTaskHelper.reject({
677
1019
  code: PdfErrorCode.NotFound,
678
1020
  message: "[RedactionPlugin] selection plugin required"
679
1021
  });
1022
+ }
680
1023
  const coreDoc = this.coreState.core.documents[id];
681
- if (!(coreDoc == null ? void 0 : coreDoc.document))
1024
+ if (!(coreDoc == null ? void 0 : coreDoc.document)) {
682
1025
  return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
1026
+ }
683
1027
  const selectionScope = this.selectionCapability.forDocument(id);
684
1028
  const formatted = selectionScope.getFormattedSelection();
685
1029
  if (!formatted.length) return PdfTaskHelper.resolve(true);
686
- const uniqueId = uuidV4();
687
- const items = formatted.map((s) => ({
688
- id: uniqueId,
689
- kind: "text",
690
- page: s.pageIndex,
691
- rect: s.rect,
692
- rects: s.segmentRects
693
- }));
694
- this.enableRedactSelection(id);
695
- this.dispatch(addPending(id, items));
696
- this.emitPendingChange(id);
697
- const last = items[items.length - 1];
698
- this.selectPending(last.page, last.id, id);
1030
+ const textTask = selectionScope.getSelectedText();
699
1031
  const emitter = this.redactionSelection$.get(id);
700
1032
  emitter == null ? void 0 : emitter.emit([]);
701
1033
  selectionScope.clear();
702
- 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;
703
1050
  }
704
1051
  commitPendingOne(page, id, documentId) {
705
1052
  const docId = documentId ?? this.getActiveDocumentId();
@@ -717,6 +1064,17 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
717
1064
  const coreDoc = this.coreState.core.documents[docId];
718
1065
  if (!(coreDoc == null ? void 0 : coreDoc.document))
719
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
+ }
720
1078
  const docState = this.getDocumentState(docId);
721
1079
  if (!docState) {
722
1080
  return PdfTaskHelper.reject({
@@ -725,18 +1083,27 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
725
1083
  });
726
1084
  }
727
1085
  const item = (docState.pending[page] ?? []).find((it) => it.id === id);
728
- 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) {
729
1100
  const rects = item.kind === "text" ? item.rects : [item.rect];
730
- const pdfPage = coreDoc.document.pages[page];
731
- if (!pdfPage)
732
- return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Page not found" });
733
1101
  const task = new Task();
734
- this.engine.redactTextInRects(coreDoc.document, pdfPage, rects, {
1102
+ this.engine.redactTextInRects(doc, pdfPage, rects, {
735
1103
  drawBlackBoxes: this.config.drawBlackBoxes
736
1104
  }).wait(
737
1105
  () => {
738
- this.dispatch(removePending(docId, page, id));
739
- this.emitPendingChange(docId);
1106
+ this.dispatch(removePending(docId, page, item.id));
740
1107
  this.dispatchCoreAction(refreshPages(docId, [page]));
741
1108
  this.events$.emit({ type: "commit", documentId: docId, success: true });
742
1109
  task.resolve(true);
@@ -753,6 +1120,73 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
753
1120
  );
754
1121
  return task;
755
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
+ }
756
1190
  commitAllPending(documentId) {
757
1191
  const docId = documentId ?? this.getActiveDocumentId();
758
1192
  if (!this.checkPermission(docId, PdfPermissionFlag.ModifyContents)) {
@@ -776,6 +1210,16 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
776
1210
  message: "Document state not found"
777
1211
  });
778
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) {
779
1223
  const perPage = /* @__PURE__ */ new Map();
780
1224
  for (const [page, items] of Object.entries(docState.pending)) {
781
1225
  const p = Number(page);
@@ -789,11 +1233,11 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
789
1233
  const pagesToRefresh = Array.from(perPage.entries()).filter(([_, rects]) => rects.length > 0).map(([pageIndex]) => pageIndex);
790
1234
  const tasks = [];
791
1235
  for (const [pageIndex, rects] of perPage) {
792
- const page = coreDoc.document.pages[pageIndex];
1236
+ const page = doc.pages[pageIndex];
793
1237
  if (!page) continue;
794
1238
  if (!rects.length) continue;
795
1239
  tasks.push(
796
- this.engine.redactTextInRects(coreDoc.document, page, rects, {
1240
+ this.engine.redactTextInRects(doc, page, rects, {
797
1241
  drawBlackBoxes: this.config.drawBlackBoxes
798
1242
  })
799
1243
  );
@@ -803,7 +1247,6 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
803
1247
  () => {
804
1248
  this.dispatch(clearPending(docId));
805
1249
  this.dispatchCoreAction(refreshPages(docId, pagesToRefresh));
806
- this.emitPendingChange(docId);
807
1250
  this.events$.emit({ type: "commit", documentId: docId, success: true });
808
1251
  task.resolve(true);
809
1252
  },
@@ -819,6 +1262,208 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
819
1262
  );
820
1263
  return task;
821
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
+ }
822
1467
  // ─────────────────────────────────────────────────────────
823
1468
  // Event Emission Helpers
824
1469
  // ─────────────────────────────────────────────────────────
@@ -879,7 +1524,7 @@ const manifest = {
879
1524
  version: "1.0.0",
880
1525
  provides: ["redaction"],
881
1526
  requires: [],
882
- optional: ["interaction-manager", "selection"],
1527
+ optional: ["interaction-manager", "selection", "annotation"],
883
1528
  defaultConfig: {
884
1529
  drawBlackBoxes: true
885
1530
  }
@@ -910,6 +1555,8 @@ export {
910
1555
  hasPendingRedactions,
911
1556
  initialDocumentState,
912
1557
  initialState,
913
- manifest
1558
+ manifest,
1559
+ redactTool,
1560
+ redactTools
914
1561
  };
915
1562
  //# sourceMappingURL=index.js.map