@embedpdf/plugin-redaction 1.5.0 → 2.0.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/dist/index.cjs +1 -1
  2. package/dist/index.cjs.map +1 -1
  3. package/dist/index.js +573 -207
  4. package/dist/index.js.map +1 -1
  5. package/dist/lib/actions.d.ts +48 -13
  6. package/dist/lib/index.d.ts +1 -1
  7. package/dist/lib/redaction-plugin.d.ts +22 -8
  8. package/dist/lib/reducer.d.ts +4 -2
  9. package/dist/lib/selectors.d.ts +5 -3
  10. package/dist/lib/types.d.ts +68 -20
  11. package/dist/preact/index.cjs +1 -1
  12. package/dist/preact/index.cjs.map +1 -1
  13. package/dist/preact/index.js +86 -37
  14. package/dist/preact/index.js.map +1 -1
  15. package/dist/react/index.cjs +1 -1
  16. package/dist/react/index.cjs.map +1 -1
  17. package/dist/react/index.js +86 -37
  18. package/dist/react/index.js.map +1 -1
  19. package/dist/shared/components/marquee-redact.d.ts +4 -2
  20. package/dist/shared/components/pending-redactions.d.ts +4 -3
  21. package/dist/shared/components/redaction-layer.d.ts +11 -5
  22. package/dist/shared/components/selection-redact.d.ts +2 -1
  23. package/dist/shared/components/types.d.ts +6 -7
  24. package/dist/shared/hooks/use-redaction.d.ts +4 -4
  25. package/dist/shared-preact/components/marquee-redact.d.ts +4 -2
  26. package/dist/shared-preact/components/pending-redactions.d.ts +4 -3
  27. package/dist/shared-preact/components/redaction-layer.d.ts +11 -5
  28. package/dist/shared-preact/components/selection-redact.d.ts +2 -1
  29. package/dist/shared-preact/components/types.d.ts +6 -7
  30. package/dist/shared-preact/hooks/use-redaction.d.ts +4 -4
  31. package/dist/shared-react/components/marquee-redact.d.ts +4 -2
  32. package/dist/shared-react/components/pending-redactions.d.ts +4 -3
  33. package/dist/shared-react/components/redaction-layer.d.ts +11 -5
  34. package/dist/shared-react/components/selection-redact.d.ts +2 -1
  35. package/dist/shared-react/components/types.d.ts +6 -7
  36. package/dist/shared-react/hooks/use-redaction.d.ts +4 -4
  37. package/dist/svelte/components/highlight.svelte.d.ts +14 -0
  38. package/dist/svelte/components/index.d.ts +5 -0
  39. package/dist/svelte/components/marquee-redact.svelte.d.ts +16 -0
  40. package/dist/svelte/components/pending-redactions.svelte.d.ts +15 -0
  41. package/dist/svelte/components/redaction-layer.svelte.d.ts +20 -0
  42. package/dist/svelte/components/selection-redact.svelte.d.ts +8 -0
  43. package/dist/svelte/hooks/index.d.ts +1 -0
  44. package/dist/svelte/hooks/use-redaction.svelte.d.ts +21 -0
  45. package/dist/svelte/index.cjs +2 -0
  46. package/dist/svelte/index.cjs.map +1 -0
  47. package/dist/svelte/index.d.ts +4 -0
  48. package/dist/svelte/index.js +554 -0
  49. package/dist/svelte/index.js.map +1 -0
  50. package/dist/svelte/types.d.ts +10 -0
  51. package/dist/vue/components/highlight.vue.d.ts +2 -1
  52. package/dist/vue/components/marquee-redact.vue.d.ts +5 -2
  53. package/dist/vue/components/pending-redactions.vue.d.ts +18 -13
  54. package/dist/vue/components/redaction-layer.vue.d.ts +13 -4
  55. package/dist/vue/components/selection-redact.vue.d.ts +3 -1
  56. package/dist/vue/components/types.d.ts +9 -0
  57. package/dist/vue/hooks/use-redaction.d.ts +9 -102
  58. package/dist/vue/index.cjs +1 -1
  59. package/dist/vue/index.cjs.map +1 -1
  60. package/dist/vue/index.d.ts +1 -0
  61. package/dist/vue/index.js +219 -125
  62. package/dist/vue/index.js.map +1 -1
  63. package/package.json +18 -10
package/dist/index.js CHANGED
@@ -5,6 +5,9 @@ var RedactionMode = /* @__PURE__ */ ((RedactionMode2) => {
5
5
  RedactionMode2["RedactSelection"] = "redactSelection";
6
6
  return RedactionMode2;
7
7
  })(RedactionMode || {});
8
+ const INIT_REDACTION_STATE = "REDACTION/INIT_STATE";
9
+ const CLEANUP_REDACTION_STATE = "REDACTION/CLEANUP_STATE";
10
+ const SET_ACTIVE_DOCUMENT = "REDACTION/SET_ACTIVE_DOCUMENT";
8
11
  const START_REDACTION = "START_REDACTION";
9
12
  const END_REDACTION = "END_REDACTION";
10
13
  const SET_ACTIVE_TYPE = "SET_ACTIVE_TYPE";
@@ -13,25 +16,40 @@ const REMOVE_PENDING = "REMOVE_PENDING";
13
16
  const CLEAR_PENDING = "CLEAR_PENDING";
14
17
  const SELECT_PENDING = "SELECT_PENDING";
15
18
  const DESELECT_PENDING = "DESELECT_PENDING";
16
- const addPending = (items) => ({
19
+ function initRedactionState(documentId, state) {
20
+ return { type: INIT_REDACTION_STATE, payload: { documentId, state } };
21
+ }
22
+ function cleanupRedactionState(documentId) {
23
+ return { type: CLEANUP_REDACTION_STATE, payload: documentId };
24
+ }
25
+ const addPending = (documentId, items) => ({
17
26
  type: ADD_PENDING,
18
- payload: items
27
+ payload: { documentId, items }
19
28
  });
20
- const removePending = (page, id) => ({
29
+ const removePending = (documentId, page, id) => ({
21
30
  type: REMOVE_PENDING,
22
- payload: { page, id }
31
+ payload: { documentId, page, id }
32
+ });
33
+ const clearPending = (documentId) => ({
34
+ type: CLEAR_PENDING,
35
+ payload: documentId
23
36
  });
24
- const clearPending = () => ({ type: CLEAR_PENDING });
25
- const startRedaction = (mode) => ({
37
+ const startRedaction = (documentId, mode) => ({
26
38
  type: START_REDACTION,
27
- payload: mode
39
+ payload: { documentId, mode }
28
40
  });
29
- const endRedaction = () => ({ type: END_REDACTION });
30
- const selectPending = (page, id) => ({
41
+ const endRedaction = (documentId) => ({
42
+ type: END_REDACTION,
43
+ payload: documentId
44
+ });
45
+ const selectPending = (documentId, page, id) => ({
31
46
  type: SELECT_PENDING,
32
- payload: { page, id }
47
+ payload: { documentId, page, id }
48
+ });
49
+ const deselectPending = (documentId) => ({
50
+ type: DESELECT_PENDING,
51
+ payload: documentId
33
52
  });
34
- const deselectPending = () => ({ type: DESELECT_PENDING });
35
53
  function createMarqueeHandler(opts) {
36
54
  const { pageSize, scale, minDragPx = 5, onPreview, onCommit } = opts;
37
55
  let start = null;
@@ -74,15 +92,201 @@ function createMarqueeHandler(opts) {
74
92
  }
75
93
  };
76
94
  }
95
+ const calculatePendingCount = (pending) => {
96
+ return Object.values(pending).reduce((total, items) => total + items.length, 0);
97
+ };
98
+ const initialDocumentState = {
99
+ isRedacting: false,
100
+ activeType: null,
101
+ pending: {},
102
+ pendingCount: 0,
103
+ selected: null
104
+ };
105
+ const initialState = {
106
+ documents: {},
107
+ activeDocumentId: null
108
+ };
109
+ const redactionReducer = (state = initialState, action) => {
110
+ switch (action.type) {
111
+ case INIT_REDACTION_STATE: {
112
+ const { documentId, state: docState } = action.payload;
113
+ return {
114
+ ...state,
115
+ documents: {
116
+ ...state.documents,
117
+ [documentId]: docState
118
+ },
119
+ // Set as active if no active document
120
+ activeDocumentId: state.activeDocumentId ?? documentId
121
+ };
122
+ }
123
+ case CLEANUP_REDACTION_STATE: {
124
+ const documentId = action.payload;
125
+ const { [documentId]: removed, ...remainingDocs } = state.documents;
126
+ return {
127
+ ...state,
128
+ documents: remainingDocs,
129
+ activeDocumentId: state.activeDocumentId === documentId ? null : state.activeDocumentId
130
+ };
131
+ }
132
+ case SET_ACTIVE_DOCUMENT: {
133
+ return {
134
+ ...state,
135
+ activeDocumentId: action.payload
136
+ };
137
+ }
138
+ case ADD_PENDING: {
139
+ const { documentId, items } = action.payload;
140
+ const docState = state.documents[documentId];
141
+ if (!docState) return state;
142
+ const next = { ...docState.pending };
143
+ for (const item of items) {
144
+ next[item.page] = (next[item.page] ?? []).concat(item);
145
+ }
146
+ return {
147
+ ...state,
148
+ documents: {
149
+ ...state.documents,
150
+ [documentId]: {
151
+ ...docState,
152
+ pending: next,
153
+ pendingCount: calculatePendingCount(next)
154
+ }
155
+ }
156
+ };
157
+ }
158
+ case REMOVE_PENDING: {
159
+ const { documentId, page, id } = action.payload;
160
+ const docState = state.documents[documentId];
161
+ if (!docState) return state;
162
+ const list = docState.pending[page] ?? [];
163
+ const filtered = list.filter((it) => it.id !== id);
164
+ const next = { ...docState.pending, [page]: filtered };
165
+ const stillSelected = docState.selected && !(docState.selected.page === page && docState.selected.id === id);
166
+ return {
167
+ ...state,
168
+ documents: {
169
+ ...state.documents,
170
+ [documentId]: {
171
+ ...docState,
172
+ pending: next,
173
+ pendingCount: calculatePendingCount(next),
174
+ selected: stillSelected ? docState.selected : null
175
+ }
176
+ }
177
+ };
178
+ }
179
+ case CLEAR_PENDING: {
180
+ const documentId = action.payload;
181
+ const docState = state.documents[documentId];
182
+ if (!docState) return state;
183
+ return {
184
+ ...state,
185
+ documents: {
186
+ ...state.documents,
187
+ [documentId]: {
188
+ ...docState,
189
+ pending: {},
190
+ pendingCount: 0,
191
+ selected: null
192
+ }
193
+ }
194
+ };
195
+ }
196
+ case SELECT_PENDING: {
197
+ const { documentId, page, id } = action.payload;
198
+ const docState = state.documents[documentId];
199
+ if (!docState) return state;
200
+ return {
201
+ ...state,
202
+ documents: {
203
+ ...state.documents,
204
+ [documentId]: {
205
+ ...docState,
206
+ selected: { page, id }
207
+ }
208
+ }
209
+ };
210
+ }
211
+ case DESELECT_PENDING: {
212
+ const documentId = action.payload;
213
+ const docState = state.documents[documentId];
214
+ if (!docState) return state;
215
+ return {
216
+ ...state,
217
+ documents: {
218
+ ...state.documents,
219
+ [documentId]: {
220
+ ...docState,
221
+ selected: null
222
+ }
223
+ }
224
+ };
225
+ }
226
+ case START_REDACTION: {
227
+ const { documentId, mode } = action.payload;
228
+ const docState = state.documents[documentId];
229
+ if (!docState) return state;
230
+ return {
231
+ ...state,
232
+ documents: {
233
+ ...state.documents,
234
+ [documentId]: {
235
+ ...docState,
236
+ isRedacting: true,
237
+ activeType: mode
238
+ }
239
+ }
240
+ };
241
+ }
242
+ case END_REDACTION: {
243
+ const documentId = action.payload;
244
+ const docState = state.documents[documentId];
245
+ if (!docState) return state;
246
+ return {
247
+ ...state,
248
+ documents: {
249
+ ...state.documents,
250
+ [documentId]: {
251
+ ...docState,
252
+ pending: {},
253
+ pendingCount: 0,
254
+ selected: null,
255
+ isRedacting: false,
256
+ activeType: null
257
+ }
258
+ }
259
+ };
260
+ }
261
+ case SET_ACTIVE_TYPE: {
262
+ const { documentId, mode } = action.payload;
263
+ const docState = state.documents[documentId];
264
+ if (!docState) return state;
265
+ return {
266
+ ...state,
267
+ documents: {
268
+ ...state.documents,
269
+ [documentId]: {
270
+ ...docState,
271
+ activeType: mode
272
+ }
273
+ }
274
+ };
275
+ }
276
+ default:
277
+ return state;
278
+ }
279
+ };
77
280
  const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
78
281
  constructor(id, registry, config) {
79
- var _a, _b, _c, _d, _e;
282
+ var _a, _b, _c;
80
283
  super(id, registry);
81
- this.redactionSelection$ = createBehaviorEmitter();
284
+ this.redactionSelection$ = /* @__PURE__ */ new Map();
82
285
  this.pending$ = createBehaviorEmitter();
83
286
  this.selected$ = createBehaviorEmitter();
84
287
  this.state$ = createBehaviorEmitter();
85
288
  this.events$ = createBehaviorEmitter();
289
+ this.documentUnsubscribers = /* @__PURE__ */ new Map();
86
290
  this.config = config;
87
291
  this.selectionCapability = (_a = this.registry.getPlugin("selection")) == null ? void 0 : _a.provides();
88
292
  this.interactionManagerCapability = (_b = this.registry.getPlugin("interaction-manager")) == null ? void 0 : _b.provides();
@@ -93,134 +297,277 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
93
297
  exclusive: true,
94
298
  cursor: "crosshair"
95
299
  });
96
- }
97
- if (this.interactionManagerCapability && this.selectionCapability) {
98
300
  this.interactionManagerCapability.registerMode({
99
301
  id: RedactionMode.RedactSelection,
100
302
  scope: "page",
101
303
  exclusive: false
102
304
  });
103
- this.selectionCapability.enableForMode(RedactionMode.RedactSelection);
104
305
  }
105
- this.unsubscribeModeChange = (_c = this.interactionManagerCapability) == null ? void 0 : _c.onModeChange((state) => {
106
- if (state.activeMode === RedactionMode.RedactSelection) {
107
- this.dispatch(startRedaction(RedactionMode.RedactSelection));
108
- } else if (state.activeMode === RedactionMode.MarqueeRedact) {
109
- this.dispatch(startRedaction(RedactionMode.MarqueeRedact));
306
+ (_c = this.interactionManagerCapability) == null ? void 0 : _c.onModeChange((modeState) => {
307
+ 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));
110
312
  } else {
111
- this.dispatch(endRedaction());
112
- }
113
- });
114
- this.unsubscribeSelectionChange = (_d = this.selectionCapability) == null ? void 0 : _d.onSelectionChange(() => {
115
- if (!this.selectionCapability) return;
116
- if (!this.state.isRedacting) return;
117
- const formattedSelection = this.selectionCapability.getFormattedSelection();
118
- this.redactionSelection$.emit(formattedSelection);
119
- });
120
- this.unsubscribeEndSelection = (_e = this.selectionCapability) == null ? void 0 : _e.onEndSelection(() => {
121
- if (!this.selectionCapability) return;
122
- if (!this.state.isRedacting) return;
123
- const formattedSelection = this.selectionCapability.getFormattedSelection();
124
- const items = formattedSelection.map((s) => ({
125
- id: uuidV4(),
126
- kind: "text",
127
- page: s.pageIndex,
128
- rect: s.rect,
129
- rects: s.segmentRects
130
- }));
131
- this.dispatch(addPending(items));
132
- this.redactionSelection$.emit([]);
133
- this.selectionCapability.clear();
134
- this.pending$.emit(this.state.pending);
135
- if (items.length) {
136
- this.selectPending(items[items.length - 1].page, items[items.length - 1].id);
313
+ const docState = this.getDocumentState(documentId);
314
+ if (docState == null ? void 0 : docState.isRedacting) {
315
+ this.dispatch(endRedaction(documentId));
316
+ }
137
317
  }
138
318
  });
139
319
  }
320
+ // ─────────────────────────────────────────────────────────
321
+ // Document Lifecycle Hooks (from BasePlugin)
322
+ // ─────────────────────────────────────────────────────────
323
+ onDocumentLoadingStarted(documentId) {
324
+ this.dispatch(
325
+ initRedactionState(documentId, {
326
+ ...initialDocumentState
327
+ })
328
+ );
329
+ this.redactionSelection$.set(documentId, createBehaviorEmitter());
330
+ const unsubscribers = [];
331
+ if (this.selectionCapability) {
332
+ const selectionScope = this.selectionCapability.forDocument(documentId);
333
+ const unsubSelection = selectionScope.onSelectionChange(() => {
334
+ const docState = this.getDocumentState(documentId);
335
+ if (!(docState == null ? void 0 : docState.isRedacting)) return;
336
+ const formattedSelection = selectionScope.getFormattedSelection();
337
+ const emitter = this.redactionSelection$.get(documentId);
338
+ emitter == null ? void 0 : emitter.emit(formattedSelection);
339
+ });
340
+ const unsubEndSelection = selectionScope.onEndSelection(() => {
341
+ const docState = this.getDocumentState(documentId);
342
+ if (!(docState == null ? void 0 : docState.isRedacting)) return;
343
+ const formattedSelection = selectionScope.getFormattedSelection();
344
+ const items = formattedSelection.map((s) => ({
345
+ id: uuidV4(),
346
+ kind: "text",
347
+ page: s.pageIndex,
348
+ rect: s.rect,
349
+ rects: s.segmentRects
350
+ }));
351
+ this.dispatch(addPending(documentId, items));
352
+ const emitter = this.redactionSelection$.get(documentId);
353
+ emitter == null ? void 0 : emitter.emit([]);
354
+ selectionScope.clear();
355
+ this.emitPendingChange(documentId);
356
+ if (items.length) {
357
+ this.selectPending(items[items.length - 1].page, items[items.length - 1].id, documentId);
358
+ }
359
+ });
360
+ unsubscribers.push(unsubSelection, unsubEndSelection);
361
+ }
362
+ this.documentUnsubscribers.set(documentId, unsubscribers);
363
+ this.logger.debug(
364
+ "RedactionPlugin",
365
+ "DocumentOpened",
366
+ `Initialized redaction state for document: ${documentId}`
367
+ );
368
+ }
369
+ onDocumentLoaded(documentId) {
370
+ var _a;
371
+ (_a = this.selectionCapability) == null ? void 0 : _a.enableForMode(RedactionMode.RedactSelection, documentId);
372
+ }
373
+ onDocumentClosed(documentId) {
374
+ this.dispatch(cleanupRedactionState(documentId));
375
+ const emitter = this.redactionSelection$.get(documentId);
376
+ emitter == null ? void 0 : emitter.clear();
377
+ this.redactionSelection$.delete(documentId);
378
+ const unsubscribers = this.documentUnsubscribers.get(documentId);
379
+ if (unsubscribers) {
380
+ unsubscribers.forEach((unsub) => unsub());
381
+ this.documentUnsubscribers.delete(documentId);
382
+ }
383
+ this.logger.debug(
384
+ "RedactionPlugin",
385
+ "DocumentClosed",
386
+ `Cleaned up redaction state for document: ${documentId}`
387
+ );
388
+ }
389
+ // ─────────────────────────────────────────────────────────
390
+ // Capability
391
+ // ─────────────────────────────────────────────────────────
140
392
  async initialize(_config) {
393
+ this.logger.info("RedactionPlugin", "Initialize", "Redaction plugin initialized");
141
394
  }
142
395
  buildCapability() {
143
396
  return {
397
+ // Active document operations
144
398
  queueCurrentSelectionAsPending: () => this.queueCurrentSelectionAsPending(),
145
399
  enableMarqueeRedact: () => this.enableMarqueeRedact(),
146
400
  toggleMarqueeRedact: () => this.toggleMarqueeRedact(),
147
- isMarqueeRedactActive: () => {
148
- var _a;
149
- return ((_a = this.interactionManagerCapability) == null ? void 0 : _a.getActiveMode()) === RedactionMode.MarqueeRedact;
150
- },
401
+ isMarqueeRedactActive: () => this.isMarqueeRedactActive(),
151
402
  enableRedactSelection: () => this.enableRedactSelection(),
152
403
  toggleRedactSelection: () => this.toggleRedactSelection(),
153
- isRedactSelectionActive: () => {
154
- var _a;
155
- return ((_a = this.interactionManagerCapability) == null ? void 0 : _a.getActiveMode()) === RedactionMode.RedactSelection;
156
- },
157
- addPending: (items) => {
158
- this.dispatch(addPending(items));
159
- this.pending$.emit(this.state.pending);
160
- this.events$.emit({ type: "add", items });
161
- },
162
- removePending: (page, id) => {
163
- this.dispatch(removePending(page, id));
164
- this.pending$.emit(this.state.pending);
165
- this.events$.emit({ type: "remove", page, id });
166
- },
167
- clearPending: () => {
168
- this.dispatch(clearPending());
169
- this.pending$.emit(this.state.pending);
170
- this.events$.emit({ type: "clear" });
171
- },
404
+ isRedactSelectionActive: () => this.isRedactSelectionActive(),
405
+ addPending: (items) => this.addPendingItems(items),
406
+ removePending: (page, id) => this.removePendingItem(page, id),
407
+ clearPending: () => this.clearPendingItems(),
172
408
  commitAllPending: () => this.commitAllPending(),
173
409
  commitPending: (page, id) => this.commitPendingOne(page, id),
174
- endRedaction: () => this.endRedaction(),
175
- startRedaction: () => this.startRedaction(),
410
+ endRedaction: () => this.endRedactionMode(),
411
+ startRedaction: () => this.startRedactionMode(),
176
412
  selectPending: (page, id) => this.selectPending(page, id),
413
+ getSelectedPending: () => this.getSelectedPending(),
177
414
  deselectPending: () => this.deselectPending(),
415
+ getState: () => this.getDocumentStateOrThrow(),
416
+ // Document-scoped operations
417
+ forDocument: (documentId) => this.createRedactionScope(documentId),
418
+ // Events
419
+ onPendingChange: this.pending$.on,
178
420
  onSelectedChange: this.selected$.on,
179
421
  onRedactionEvent: this.events$.on,
180
- onStateChange: this.state$.on,
181
- onPendingChange: this.pending$.on
422
+ onStateChange: this.state$.on
423
+ };
424
+ }
425
+ // ─────────────────────────────────────────────────────────
426
+ // Document Scoping
427
+ // ─────────────────────────────────────────────────────────
428
+ createRedactionScope(documentId) {
429
+ return {
430
+ queueCurrentSelectionAsPending: () => this.queueCurrentSelectionAsPending(documentId),
431
+ enableMarqueeRedact: () => this.enableMarqueeRedact(documentId),
432
+ toggleMarqueeRedact: () => this.toggleMarqueeRedact(documentId),
433
+ isMarqueeRedactActive: () => this.isMarqueeRedactActive(documentId),
434
+ enableRedactSelection: () => this.enableRedactSelection(documentId),
435
+ toggleRedactSelection: () => this.toggleRedactSelection(documentId),
436
+ isRedactSelectionActive: () => this.isRedactSelectionActive(documentId),
437
+ addPending: (items) => this.addPendingItems(items, documentId),
438
+ removePending: (page, id) => this.removePendingItem(page, id, documentId),
439
+ clearPending: () => this.clearPendingItems(documentId),
440
+ commitAllPending: () => this.commitAllPending(documentId),
441
+ commitPending: (page, id) => this.commitPendingOne(page, id, documentId),
442
+ endRedaction: () => this.endRedactionMode(documentId),
443
+ startRedaction: () => this.startRedactionMode(documentId),
444
+ selectPending: (page, id) => this.selectPending(page, id, documentId),
445
+ getSelectedPending: () => this.getSelectedPending(documentId),
446
+ deselectPending: () => this.deselectPending(documentId),
447
+ getState: () => this.getDocumentStateOrThrow(documentId),
448
+ onPendingChange: (listener) => this.pending$.on((event) => {
449
+ if (event.documentId === documentId) listener(event.pending);
450
+ }),
451
+ onSelectedChange: (listener) => this.selected$.on((event) => {
452
+ if (event.documentId === documentId) listener(event.selected);
453
+ }),
454
+ onRedactionEvent: (listener) => this.events$.on((event) => {
455
+ if (event.documentId === documentId) listener(event);
456
+ }),
457
+ onStateChange: (listener) => this.state$.on((event) => {
458
+ if (event.documentId === documentId) listener(event.state);
459
+ })
182
460
  };
183
461
  }
184
- onRedactionSelectionChange(callback) {
185
- return this.redactionSelection$.on(callback);
462
+ // ─────────────────────────────────────────────────────────
463
+ // State Helpers
464
+ // ─────────────────────────────────────────────────────────
465
+ getDocumentState(documentId) {
466
+ const id = documentId ?? this.getActiveDocumentId();
467
+ return this.state.documents[id] ?? null;
186
468
  }
187
- selectPending(page, id) {
469
+ getDocumentStateOrThrow(documentId) {
470
+ const state = this.getDocumentState(documentId);
471
+ if (!state) {
472
+ throw new Error(`Redaction state not found for document: ${documentId ?? "active"}`);
473
+ }
474
+ return state;
475
+ }
476
+ // ─────────────────────────────────────────────────────────
477
+ // Core Operations
478
+ // ─────────────────────────────────────────────────────────
479
+ addPendingItems(items, documentId) {
480
+ const id = documentId ?? this.getActiveDocumentId();
481
+ this.dispatch(addPending(id, items));
482
+ this.emitPendingChange(id);
483
+ this.events$.emit({ type: "add", documentId: id, items });
484
+ }
485
+ removePendingItem(page, itemId, documentId) {
486
+ const id = documentId ?? this.getActiveDocumentId();
487
+ this.dispatch(removePending(id, page, itemId));
488
+ this.emitPendingChange(id);
489
+ this.events$.emit({ type: "remove", documentId: id, page, id: itemId });
490
+ }
491
+ clearPendingItems(documentId) {
492
+ const id = documentId ?? this.getActiveDocumentId();
493
+ this.dispatch(clearPending(id));
494
+ this.emitPendingChange(id);
495
+ this.events$.emit({ type: "clear", documentId: id });
496
+ }
497
+ selectPending(page, itemId, documentId) {
188
498
  var _a;
189
- this.dispatch(selectPending(page, id));
190
- (_a = this.selectionCapability) == null ? void 0 : _a.clear();
191
- this.selected$.emit(this.state.selected);
499
+ const id = documentId ?? this.getActiveDocumentId();
500
+ this.dispatch(selectPending(id, page, itemId));
501
+ (_a = this.selectionCapability) == null ? void 0 : _a.forDocument(id).clear();
502
+ this.emitSelectedChange(id);
192
503
  }
193
- deselectPending() {
194
- this.dispatch(deselectPending());
195
- this.selected$.emit(this.state.selected);
504
+ getSelectedPending(documentId) {
505
+ var _a;
506
+ const id = documentId ?? this.getActiveDocumentId();
507
+ return ((_a = this.getDocumentState(id)) == null ? void 0 : _a.selected) ?? null;
196
508
  }
197
- enableRedactSelection() {
509
+ deselectPending(documentId) {
510
+ const id = documentId ?? this.getActiveDocumentId();
511
+ this.dispatch(deselectPending(id));
512
+ this.emitSelectedChange(id);
513
+ }
514
+ enableRedactSelection(documentId) {
198
515
  var _a;
199
- (_a = this.interactionManagerCapability) == null ? void 0 : _a.activate(RedactionMode.RedactSelection);
516
+ const id = documentId ?? this.getActiveDocumentId();
517
+ (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).activate(RedactionMode.RedactSelection);
200
518
  }
201
- toggleRedactSelection() {
202
- var _a, _b, _c;
203
- if (((_a = this.interactionManagerCapability) == null ? void 0 : _a.getActiveMode()) === RedactionMode.RedactSelection)
204
- (_b = this.interactionManagerCapability) == null ? void 0 : _b.activateDefaultMode();
205
- else (_c = this.interactionManagerCapability) == null ? void 0 : _c.activate(RedactionMode.RedactSelection);
519
+ toggleRedactSelection(documentId) {
520
+ var _a;
521
+ const id = documentId ?? this.getActiveDocumentId();
522
+ const scope = (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id);
523
+ if ((scope == null ? void 0 : scope.getActiveMode()) === RedactionMode.RedactSelection) {
524
+ scope.activateDefaultMode();
525
+ } else {
526
+ scope == null ? void 0 : scope.activate(RedactionMode.RedactSelection);
527
+ }
206
528
  }
207
- enableMarqueeRedact() {
529
+ isRedactSelectionActive(documentId) {
208
530
  var _a;
209
- (_a = this.interactionManagerCapability) == null ? void 0 : _a.activate(RedactionMode.MarqueeRedact);
531
+ const id = documentId ?? this.getActiveDocumentId();
532
+ return ((_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).getActiveMode()) === RedactionMode.RedactSelection;
210
533
  }
211
- toggleMarqueeRedact() {
212
- var _a, _b, _c;
213
- if (((_a = this.interactionManagerCapability) == null ? void 0 : _a.getActiveMode()) === RedactionMode.MarqueeRedact)
214
- (_b = this.interactionManagerCapability) == null ? void 0 : _b.activateDefaultMode();
215
- else (_c = this.interactionManagerCapability) == null ? void 0 : _c.activate(RedactionMode.MarqueeRedact);
534
+ enableMarqueeRedact(documentId) {
535
+ var _a;
536
+ const id = documentId ?? this.getActiveDocumentId();
537
+ (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).activate(RedactionMode.MarqueeRedact);
538
+ }
539
+ toggleMarqueeRedact(documentId) {
540
+ var _a;
541
+ const id = documentId ?? this.getActiveDocumentId();
542
+ const scope = (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id);
543
+ if ((scope == null ? void 0 : scope.getActiveMode()) === RedactionMode.MarqueeRedact) {
544
+ scope.activateDefaultMode();
545
+ } else {
546
+ scope == null ? void 0 : scope.activate(RedactionMode.MarqueeRedact);
547
+ }
548
+ }
549
+ isMarqueeRedactActive(documentId) {
550
+ var _a;
551
+ const id = documentId ?? this.getActiveDocumentId();
552
+ return ((_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).getActiveMode()) === RedactionMode.MarqueeRedact;
216
553
  }
217
- startRedaction() {
554
+ startRedactionMode(documentId) {
218
555
  var _a;
219
- (_a = this.interactionManagerCapability) == null ? void 0 : _a.activate(RedactionMode.RedactSelection);
556
+ const id = documentId ?? this.getActiveDocumentId();
557
+ (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).activate(RedactionMode.RedactSelection);
220
558
  }
221
- endRedaction() {
559
+ endRedactionMode(documentId) {
222
560
  var _a;
223
- (_a = this.interactionManagerCapability) == null ? void 0 : _a.activateDefaultMode();
561
+ const id = documentId ?? this.getActiveDocumentId();
562
+ (_a = this.interactionManagerCapability) == null ? void 0 : _a.forDocument(id).activateDefaultMode();
563
+ }
564
+ // ─────────────────────────────────────────────────────────
565
+ // Public Methods
566
+ // ─────────────────────────────────────────────────────────
567
+ onRedactionSelectionChange(documentId, callback) {
568
+ const emitter = this.redactionSelection$.get(documentId);
569
+ return (emitter == null ? void 0 : emitter.on(callback)) ?? (() => {
570
+ });
224
571
  }
225
572
  registerMarqueeOnPage(opts) {
226
573
  if (!this.interactionManagerCapability) {
@@ -232,13 +579,13 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
232
579
  return () => {
233
580
  };
234
581
  }
235
- const document = this.coreState.core.document;
236
- if (!document) {
582
+ const coreDoc = this.coreState.core.documents[opts.documentId];
583
+ if (!(coreDoc == null ? void 0 : coreDoc.document)) {
237
584
  this.logger.warn("RedactionPlugin", "DocumentNotFound", "Document not found");
238
585
  return () => {
239
586
  };
240
587
  }
241
- const page = document.pages[opts.pageIndex];
588
+ const page = coreDoc.document.pages[opts.pageIndex];
242
589
  if (!page) {
243
590
  this.logger.warn("RedactionPlugin", "PageNotFound", `Page ${opts.pageIndex} not found`);
244
591
  return () => {
@@ -256,27 +603,29 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
256
603
  page: opts.pageIndex,
257
604
  rect: r
258
605
  };
259
- this.dispatch(addPending([item]));
260
- this.pending$.emit(this.state.pending);
606
+ this.dispatch(addPending(opts.documentId, [item]));
607
+ this.emitPendingChange(opts.documentId);
261
608
  (_b = (_a = opts.callback).onCommit) == null ? void 0 : _b.call(_a, r);
262
- this.enableRedactSelection();
263
- this.selectPending(opts.pageIndex, item.id);
609
+ this.enableRedactSelection(opts.documentId);
610
+ this.selectPending(opts.pageIndex, item.id, opts.documentId);
264
611
  }
265
612
  });
266
613
  const off = this.interactionManagerCapability.registerAlways({
267
614
  handlers: {
268
615
  onPointerDown: (_, evt) => {
269
616
  if (evt.target === evt.currentTarget) {
270
- this.deselectPending();
617
+ this.deselectPending(opts.documentId);
271
618
  }
272
619
  }
273
620
  },
274
621
  scope: {
275
622
  type: "page",
623
+ documentId: opts.documentId,
276
624
  pageIndex: opts.pageIndex
277
625
  }
278
626
  });
279
627
  const off2 = this.interactionManagerCapability.registerHandlers({
628
+ documentId: opts.documentId,
280
629
  modeId: RedactionMode.MarqueeRedact,
281
630
  handlers,
282
631
  pageIndex: opts.pageIndex
@@ -286,69 +635,92 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
286
635
  off2();
287
636
  };
288
637
  }
289
- queueCurrentSelectionAsPending() {
290
- var _a;
638
+ queueCurrentSelectionAsPending(documentId) {
639
+ const id = documentId ?? this.getActiveDocumentId();
291
640
  if (!this.selectionCapability)
292
641
  return PdfTaskHelper.reject({
293
642
  code: PdfErrorCode.NotFound,
294
643
  message: "[RedactionPlugin] selection plugin required"
295
644
  });
296
- const doc = this.coreState.core.document;
297
- if (!doc)
645
+ const coreDoc = this.coreState.core.documents[id];
646
+ if (!(coreDoc == null ? void 0 : coreDoc.document))
298
647
  return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
299
- const formatted = this.selectionCapability.getFormattedSelection();
648
+ const selectionScope = this.selectionCapability.forDocument(id);
649
+ const formatted = selectionScope.getFormattedSelection();
300
650
  if (!formatted.length) return PdfTaskHelper.resolve(true);
301
- const id = uuidV4();
651
+ const uniqueId = uuidV4();
302
652
  const items = formatted.map((s) => ({
303
- id,
653
+ id: uniqueId,
304
654
  kind: "text",
305
655
  page: s.pageIndex,
306
656
  rect: s.rect,
307
657
  rects: s.segmentRects
308
658
  }));
309
- this.enableRedactSelection();
310
- this.dispatch(addPending(items));
311
- this.pending$.emit(this.state.pending);
659
+ this.enableRedactSelection(id);
660
+ this.dispatch(addPending(id, items));
661
+ this.emitPendingChange(id);
312
662
  const last = items[items.length - 1];
313
- this.selectPending(last.page, last.id);
314
- this.redactionSelection$.emit([]);
315
- (_a = this.selectionCapability) == null ? void 0 : _a.clear();
663
+ this.selectPending(last.page, last.id, id);
664
+ const emitter = this.redactionSelection$.get(id);
665
+ emitter == null ? void 0 : emitter.emit([]);
666
+ selectionScope.clear();
316
667
  return PdfTaskHelper.resolve(true);
317
668
  }
318
- commitPendingOne(page, id) {
319
- const doc = this.coreState.core.document;
320
- if (!doc)
669
+ commitPendingOne(page, id, documentId) {
670
+ const docId = documentId ?? this.getActiveDocumentId();
671
+ const coreDoc = this.coreState.core.documents[docId];
672
+ if (!(coreDoc == null ? void 0 : coreDoc.document))
321
673
  return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
322
- const item = (this.state.pending[page] ?? []).find((it) => it.id === id);
674
+ const docState = this.getDocumentState(docId);
675
+ if (!docState) {
676
+ return PdfTaskHelper.reject({
677
+ code: PdfErrorCode.NotFound,
678
+ message: "Document state not found"
679
+ });
680
+ }
681
+ const item = (docState.pending[page] ?? []).find((it) => it.id === id);
323
682
  if (!item) return PdfTaskHelper.resolve(true);
324
683
  const rects = item.kind === "text" ? item.rects : [item.rect];
325
- const pdfPage = doc.pages[page];
684
+ const pdfPage = coreDoc.document.pages[page];
326
685
  if (!pdfPage)
327
686
  return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Page not found" });
328
687
  const task = new Task();
329
- this.engine.redactTextInRects(doc, pdfPage, rects, {
688
+ this.engine.redactTextInRects(coreDoc.document, pdfPage, rects, {
330
689
  drawBlackBoxes: this.config.drawBlackBoxes
331
690
  }).wait(
332
691
  () => {
333
- this.dispatch(removePending(page, id));
334
- this.pending$.emit(this.state.pending);
335
- this.dispatchCoreAction(refreshPages([page]));
336
- this.events$.emit({ type: "commit", success: true });
692
+ this.dispatch(removePending(docId, page, id));
693
+ this.emitPendingChange(docId);
694
+ this.dispatchCoreAction(refreshPages(docId, [page]));
695
+ this.events$.emit({ type: "commit", documentId: docId, success: true });
337
696
  task.resolve(true);
338
697
  },
339
698
  (error) => {
340
- this.events$.emit({ type: "commit", success: false, error: error.reason });
699
+ this.events$.emit({
700
+ type: "commit",
701
+ documentId: docId,
702
+ success: false,
703
+ error: error.reason
704
+ });
341
705
  task.reject({ code: PdfErrorCode.Unknown, message: "Failed to commit redactions" });
342
706
  }
343
707
  );
344
708
  return task;
345
709
  }
346
- commitAllPending() {
347
- const doc = this.coreState.core.document;
348
- if (!doc)
710
+ commitAllPending(documentId) {
711
+ const docId = documentId ?? this.getActiveDocumentId();
712
+ const coreDoc = this.coreState.core.documents[docId];
713
+ if (!(coreDoc == null ? void 0 : coreDoc.document))
349
714
  return PdfTaskHelper.reject({ code: PdfErrorCode.NotFound, message: "Document not found" });
715
+ const docState = this.getDocumentState(docId);
716
+ if (!docState) {
717
+ return PdfTaskHelper.reject({
718
+ code: PdfErrorCode.NotFound,
719
+ message: "Document state not found"
720
+ });
721
+ }
350
722
  const perPage = /* @__PURE__ */ new Map();
351
- for (const [page, items] of Object.entries(this.state.pending)) {
723
+ for (const [page, items] of Object.entries(docState.pending)) {
352
724
  const p = Number(page);
353
725
  const list = perPage.get(p) ?? [];
354
726
  for (const it of items) {
@@ -360,11 +732,11 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
360
732
  const pagesToRefresh = Array.from(perPage.entries()).filter(([_, rects]) => rects.length > 0).map(([pageIndex]) => pageIndex);
361
733
  const tasks = [];
362
734
  for (const [pageIndex, rects] of perPage) {
363
- const page = doc.pages[pageIndex];
735
+ const page = coreDoc.document.pages[pageIndex];
364
736
  if (!page) continue;
365
737
  if (!rects.length) continue;
366
738
  tasks.push(
367
- this.engine.redactTextInRects(doc, page, rects, {
739
+ this.engine.redactTextInRects(coreDoc.document, page, rects, {
368
740
  drawBlackBoxes: this.config.drawBlackBoxes
369
741
  })
370
742
  );
@@ -372,33 +744,72 @@ const _RedactionPlugin = class _RedactionPlugin extends BasePlugin {
372
744
  const task = new Task();
373
745
  Task.all(tasks).wait(
374
746
  () => {
375
- this.dispatch(clearPending());
376
- this.dispatchCoreAction(refreshPages(pagesToRefresh));
377
- this.pending$.emit(this.state.pending);
378
- this.events$.emit({ type: "commit", success: true });
747
+ this.dispatch(clearPending(docId));
748
+ this.dispatchCoreAction(refreshPages(docId, pagesToRefresh));
749
+ this.emitPendingChange(docId);
750
+ this.events$.emit({ type: "commit", documentId: docId, success: true });
379
751
  task.resolve(true);
380
752
  },
381
753
  (error) => {
382
- this.events$.emit({ type: "commit", success: false, error: error.reason });
754
+ this.events$.emit({
755
+ type: "commit",
756
+ documentId: docId,
757
+ success: false,
758
+ error: error.reason
759
+ });
383
760
  task.reject({ code: PdfErrorCode.Unknown, message: "Failed to commit redactions" });
384
761
  }
385
762
  );
386
763
  return task;
387
764
  }
765
+ // ─────────────────────────────────────────────────────────
766
+ // Event Emission Helpers
767
+ // ─────────────────────────────────────────────────────────
768
+ emitPendingChange(documentId) {
769
+ const docState = this.getDocumentState(documentId);
770
+ if (docState) {
771
+ this.pending$.emit({ documentId, pending: docState.pending });
772
+ }
773
+ }
774
+ emitSelectedChange(documentId) {
775
+ const docState = this.getDocumentState(documentId);
776
+ if (docState) {
777
+ this.selected$.emit({ documentId, selected: docState.selected });
778
+ }
779
+ }
780
+ emitStateChange(documentId) {
781
+ const docState = this.getDocumentState(documentId);
782
+ if (docState) {
783
+ this.state$.emit({ documentId, state: docState });
784
+ }
785
+ }
786
+ // ─────────────────────────────────────────────────────────
787
+ // Store Update Handlers
788
+ // ─────────────────────────────────────────────────────────
388
789
  onStoreUpdated(_, newState) {
389
- this.pending$.emit(newState.pending);
390
- this.selected$.emit(newState.selected);
391
- this.state$.emit(newState);
790
+ for (const documentId in newState.documents) {
791
+ const docState = newState.documents[documentId];
792
+ if (docState) {
793
+ this.emitPendingChange(documentId);
794
+ this.emitSelectedChange(documentId);
795
+ this.emitStateChange(documentId);
796
+ }
797
+ }
392
798
  }
799
+ // ─────────────────────────────────────────────────────────
800
+ // Lifecycle
801
+ // ─────────────────────────────────────────────────────────
393
802
  async destroy() {
394
- var _a, _b, _c;
395
- this.redactionSelection$.clear();
396
803
  this.pending$.clear();
804
+ this.selected$.clear();
397
805
  this.state$.clear();
398
806
  this.events$.clear();
399
- (_a = this.unsubscribeSelectionChange) == null ? void 0 : _a.call(this);
400
- (_b = this.unsubscribeEndSelection) == null ? void 0 : _b.call(this);
401
- (_c = this.unsubscribeModeChange) == null ? void 0 : _c.call(this);
807
+ this.redactionSelection$.forEach((emitter) => emitter.clear());
808
+ this.redactionSelection$.clear();
809
+ this.documentUnsubscribers.forEach((unsubscribers) => {
810
+ unsubscribers.forEach((unsub) => unsub());
811
+ });
812
+ this.documentUnsubscribers.clear();
402
813
  await super.destroy();
403
814
  }
404
815
  };
@@ -417,63 +828,15 @@ const manifest = {
417
828
  drawBlackBoxes: true
418
829
  }
419
830
  };
420
- const calculatePendingCount = (pending) => {
421
- return Object.values(pending).reduce((total, items) => total + items.length, 0);
831
+ const getPendingRedactionsCount = (s) => s.pendingCount;
832
+ const hasPendingRedactions = (s) => s.pendingCount > 0;
833
+ const getDocumentPendingCount = (state, documentId) => {
834
+ var _a;
835
+ return ((_a = state.documents[documentId]) == null ? void 0 : _a.pendingCount) ?? 0;
422
836
  };
423
- const initialState = {
424
- isRedacting: false,
425
- activeType: null,
426
- pending: {},
427
- pendingCount: 0,
428
- selected: null
429
- };
430
- const redactionReducer = (state = initialState, action) => {
431
- switch (action.type) {
432
- case ADD_PENDING: {
433
- const next = { ...state.pending };
434
- for (const item of action.payload) {
435
- next[item.page] = (next[item.page] ?? []).concat(item);
436
- }
437
- return { ...state, pending: next, pendingCount: calculatePendingCount(next) };
438
- }
439
- case REMOVE_PENDING: {
440
- const { page, id } = action.payload;
441
- const list = state.pending[page] ?? [];
442
- const filtered = list.filter((it) => it.id !== id);
443
- const next = { ...state.pending, [page]: filtered };
444
- const stillSelected = state.selected && !(state.selected.page === page && state.selected.id === id);
445
- return {
446
- ...state,
447
- pending: next,
448
- pendingCount: calculatePendingCount(next),
449
- selected: stillSelected ? state.selected : null
450
- };
451
- }
452
- case CLEAR_PENDING:
453
- return { ...state, pending: {}, pendingCount: 0, selected: null };
454
- case SELECT_PENDING:
455
- return { ...state, selected: { page: action.payload.page, id: action.payload.id } };
456
- case DESELECT_PENDING:
457
- return { ...state, selected: null };
458
- case START_REDACTION:
459
- return { ...state, isRedacting: true, activeType: action.payload };
460
- case END_REDACTION:
461
- return {
462
- ...state,
463
- pending: {},
464
- pendingCount: 0,
465
- selected: null,
466
- isRedacting: false,
467
- activeType: null
468
- };
469
- case SET_ACTIVE_TYPE:
470
- return { ...state, activeType: action.payload };
471
- default:
472
- return state;
473
- }
837
+ const getTotalPendingCount = (state) => {
838
+ return Object.values(state.documents).reduce((sum, doc) => sum + doc.pendingCount, 0);
474
839
  };
475
- const getPendingRedactionsCount = (s) => Object.values(s.pending).reduce((sum, list) => sum + ((list == null ? void 0 : list.length) ?? 0), 0);
476
- const hasPendingRedactions = (s) => Object.values(s.pending).some((list) => ((list == null ? void 0 : list.length) ?? 0) > 0);
477
840
  const RedactionPluginPackage = {
478
841
  manifest,
479
842
  create: (registry, config) => new RedactionPlugin(REDACTION_PLUGIN_ID, registry, config),
@@ -485,8 +848,11 @@ export {
485
848
  RedactionMode,
486
849
  RedactionPlugin,
487
850
  RedactionPluginPackage,
851
+ getDocumentPendingCount,
488
852
  getPendingRedactionsCount,
853
+ getTotalPendingCount,
489
854
  hasPendingRedactions,
855
+ initialDocumentState,
490
856
  initialState,
491
857
  manifest
492
858
  };