@embedpdf/plugin-redaction 3.0.0-next.2 → 3.0.0-next.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +2 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -114,8 +114,8 @@ function createRedactionCapability(ctx) {
|
|
|
114
114
|
if (!selection || !selection.hasSelection()) return false;
|
|
115
115
|
const snapshot = selection.snapshot();
|
|
116
116
|
for (const page of snapshot.pages) {
|
|
117
|
-
if (page.
|
|
118
|
-
anno.createMarkup("redact", page.pon, page.
|
|
117
|
+
if (page.segments.length === 0) continue;
|
|
118
|
+
anno.createMarkup("redact", page.pon, page.segments.map((segment) => segment.quad), "redact");
|
|
119
119
|
}
|
|
120
120
|
selection.clear();
|
|
121
121
|
return true;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["AnnotationToken","InteractionToken","SelectionToken","AnnotationToken","InteractionToken","SelectionToken"],"sources":["../src/capability.ts","../src/reducer.ts","../src/types.ts","../src/redaction.plugin.ts"],"sourcesContent":["import type { DocCapability, PluginContext } from '@embedpdf/core';\nimport { AnnotationToken } from '@embedpdf/plugin-annotation/internal';\nimport { InteractionToken } from '@embedpdf/plugin-interaction';\nimport { SelectionToken } from '@embedpdf/plugin-selection';\nimport type {\n AnnotationDTO,\n AnnotationRef,\n PdfRect,\n RedactionApplyResult,\n} from '@embedpdf/engine-core';\nimport type {\n RedactionAction,\n RedactionCapability,\n RedactionLabelPatch,\n RedactionPendingItem,\n RedactionState,\n} from './types';\n\n/** Applying destroys content — its own granted power, narrower than annotate. */\nconst REDACT_CAPABILITY: DocCapability = 'doc.redact';\n\n/** The annotation plugin's model-id convention for a durable ref. */\nconst idOf = (ref: AnnotationRef): string =>\n ref.kind === 'objectNumber'\n ? `obj:${ref.annotObjectNumber}`\n : ref.kind === 'nm'\n ? `nm:${ref.nm}`\n : `idx:${ref.pageObjectNumber}:${ref.index}`;\n\ntype RedactDTO = Extract<AnnotationDTO, { subtype: 'redact' }>;\n\n/** The PDF-space regions a redact mark targets: its quads' boxes, else `/Rect`. */\nfunction regionsOf(dto: RedactDTO): PdfRect[] {\n if (dto.quadPoints.length === 0) return [dto.rect];\n return dto.quadPoints.map((q) => {\n const xs = [q.p1.x, q.p2.x, q.p3.x, q.p4.x];\n const ys = [q.p1.y, q.p2.y, q.p3.y, q.p4.y];\n return {\n left: Math.min(...xs),\n right: Math.max(...xs),\n bottom: Math.min(...ys),\n top: Math.max(...ys),\n };\n });\n}\n\n/** Positive-area intersection — the engine's collateral rule. */\nconst intersects = (a: PdfRect, b: PdfRect): boolean =>\n Math.min(a.right, b.right) > Math.max(a.left, b.left) &&\n Math.min(a.top, b.top) > Math.max(a.bottom, b.bottom);\n\nexport function createRedactionCapability(\n ctx: PluginContext<RedactionState, RedactionAction>,\n): RedactionCapability {\n const anno = ctx.get(AnnotationToken);\n const appliedCallbacks = new Set<(result: RedactionApplyResult) => void>();\n\n const requireDoc = () => {\n const doc = ctx.doc;\n if (!doc) throw new Error('[redaction] no document bound');\n return doc;\n };\n const requireService = () => {\n const doc = requireDoc();\n if (!doc.redaction) throw new Error('[redaction] engine has no redaction service');\n return doc.redaction;\n };\n\n const pons = (): number[] => (ctx.document()?.pages ?? []).map((p) => p.pageObjectNumber);\n\n const pageIndexOf = (pon: number): number =>\n ctx.document()?.pages.find((p) => p.pageObjectNumber === pon)?.index ?? -1;\n\n const pendingOn = (pon: number): RedactDTO[] =>\n anno.list(pon).filter((a): a is RedactDTO => a.subtype === 'redact');\n\n const collectPending = (): RedactionPendingItem[] => {\n const items: RedactionPendingItem[] = [];\n for (const pon of pons()) {\n for (const dto of pendingOn(pon)) {\n items.push({\n id: idOf(dto.ref),\n ref: dto.ref,\n pageObjectNumber: pon,\n pageIndex: pageIndexOf(pon),\n kind: dto.quadPoints.length > 0 ? 'text' : 'area',\n overlayText: dto.overlayText,\n });\n }\n }\n return items;\n };\n\n const fireApplied = (result: RedactionApplyResult) => {\n for (const cb of appliedCallbacks) {\n try {\n cb(result);\n } catch (err) {\n console.error('[redaction] onApplied callback failed', err);\n }\n }\n };\n\n const runApply = async (\n scope: Parameters<ReturnType<typeof requireService>['apply']>[0],\n ): Promise<RedactionApplyResult> => {\n const service = requireService();\n ctx.dispatch({ type: 'APPLY_STARTED' });\n try {\n const result = await service.apply(scope);\n ctx.dispatch({ type: 'APPLY_FINISHED', result });\n fireApplied(result);\n return result;\n } catch (err) {\n ctx.dispatch({ type: 'APPLY_FINISHED', result: null });\n throw err;\n }\n };\n\n // A REMOTE collaborator's apply arrives only as a document event (own\n // applies flow through runApply above). Annotation state + raster\n // invalidation are handled by their own planes; here we only surface it.\n const doc = ctx.doc;\n if (doc) {\n ctx.cleanup(\n doc.events.subscribe((event) => {\n if (event.type !== 'redaction.applied') return;\n if (event.origin.kind !== 'remote') return;\n ctx.dispatch({ type: 'APPLY_FINISHED', result: event });\n fireApplied(event);\n }),\n );\n }\n\n return {\n canApply: () =>\n (ctx.doc?.redaction !== undefined && ctx.doc?.security.allows(REDACT_CAPABILITY)) ?? false,\n isApplying: () => ctx.getState().applying,\n lastResult: () => ctx.getState().lastResult,\n\n enableRedact: () => ctx.tryGet(InteractionToken)?.activateTool('redact'),\n toggleRedact: () => {\n const interaction = ctx.tryGet(InteractionToken);\n if (!interaction) return;\n interaction.activateTool(interaction.activeToolId() === 'redact' ? 'pointer' : 'redact');\n },\n isRedactActive: () => ctx.tryGet(InteractionToken)?.activeToolId() === 'redact',\n\n queueCurrentSelection: async () => {\n const selection = ctx.tryGet(SelectionToken);\n if (!selection || !selection.hasSelection()) return false;\n const snapshot = selection.snapshot();\n for (const page of snapshot.pages) {\n if (page.rects.length === 0) continue;\n anno.createMarkup('redact', page.pon, page.rects, 'redact');\n }\n selection.clear();\n return true;\n },\n\n preparePending: async () => {\n await Promise.all(pons().map((pon) => anno.ensurePage(pon)));\n },\n getPending: collectPending,\n pendingCount: () => collectPending().length,\n\n estimateCollateral: (ids) => {\n const wanted = ids ? new Set(ids) : null;\n let count = 0;\n for (const pon of pons()) {\n const all = anno.list(pon);\n const marks = all.filter(\n (a): a is RedactDTO => a.subtype === 'redact' && (!wanted || wanted.has(idOf(a.ref))),\n );\n if (marks.length === 0) continue;\n const regions = marks.flatMap(regionsOf);\n for (const other of all) {\n if (other.subtype === 'redact') continue;\n if (regions.some((r) => intersects(r, other.rect))) count++;\n }\n }\n return count;\n },\n\n setLabel: async (ref, patch: RedactionLabelPatch) => {\n const current = anno.get(ref);\n if (!current || current.subtype !== 'redact') {\n throw new Error('[redaction] setLabel target is not a redact annotation');\n }\n // Always carry the current /DA styling: the engine rewrites /DA whenever\n // a label field rides a patch, so a text-only edit must not let the\n // styling fall back to defaults.\n await anno.update(ref, {\n subtype: 'redact',\n ...(patch.overlayText !== undefined ? { overlayText: patch.overlayText } : {}),\n ...(patch.repeat !== undefined ? { repeat: patch.repeat } : {}),\n fontFamily: current.fontFamily,\n fontSize: current.fontSize,\n fontColor: current.fontColor,\n textAlign: current.textAlign,\n });\n },\n\n apply: async (ids) => {\n const wanted = new Set(ids);\n const refs = collectPending()\n .filter((p) => wanted.has(p.id))\n .map((p) => p.ref);\n if (refs.length === 0) throw new Error('[redaction] no matching pending redactions');\n return runApply({ kind: 'annotations', refs });\n },\n\n applyAll: async () => {\n // Pages scope: authoritative for the WHOLE document, including marks on\n // pages this client never loaded. Pages without marks report 'unchanged'.\n const pageObjectNumbers = pons();\n if (pageObjectNumbers.length === 0) throw new Error('[redaction] document has no pages');\n return runApply({ kind: 'pages', pageObjectNumbers });\n },\n\n onApplied: (cb) => {\n appliedCallbacks.add(cb);\n return () => appliedCallbacks.delete(cb);\n },\n };\n}\n","import type { RedactionAction, RedactionState } from './types';\n\nexport const initialRedactionState = (): RedactionState => ({\n applying: false,\n lastResult: null,\n});\n\nexport const redactionReducer = (\n state: RedactionState,\n action: RedactionAction,\n): RedactionState => {\n switch (action.type) {\n case 'APPLY_STARTED':\n return { ...state, applying: true };\n case 'APPLY_FINISHED':\n return { applying: false, lastResult: action.result ?? state.lastResult };\n default:\n return state;\n }\n};\n","import { createCapabilityToken } from '@embedpdf/core';\nimport type { AnnotationRef, RedactionApplyResult } from '@embedpdf/engine-core';\n\n/**\n * One pending redaction mark — a live projection of a `redact` annotation on\n * the annotation plane (the plugin owns NO mark state of its own; delete the\n * annotation and the pending item is gone).\n */\nexport interface RedactionPendingItem {\n /** Stable list key derived from the ref. */\n id: string;\n ref: AnnotationRef;\n pageObjectNumber: number;\n /** Display index (0-based) from the page registry — for \"page N\" labels. */\n pageIndex: number;\n /** `area` = rect-only mark (marquee); `text` = per-line quads (selection). */\n kind: 'area' | 'text';\n /** `/OverlayText` label, when set. */\n overlayText: string | null;\n}\n\nexport interface RedactionState {\n /** A destructive apply is in flight. */\n applying: boolean;\n /** The last apply result seen (own or remote), for status UI. */\n lastResult: RedactionApplyResult | null;\n}\n\nexport type RedactionAction =\n | { type: 'APPLY_STARTED' }\n | { type: 'APPLY_FINISHED'; result: RedactionApplyResult | null };\n\n/** A label edit: `overlayText: null` clears it; `repeat` tiles it. */\nexport interface RedactionLabelPatch {\n overlayText?: string | null;\n repeat?: boolean;\n}\n\nexport interface RedactionCapability {\n /** Engine support present AND this session holds `doc.redact`. The engine\n * enforces the same capability independently — this is the UI mirror. */\n canApply(): boolean;\n isApplying(): boolean;\n lastResult(): RedactionApplyResult | null;\n\n /** The composed marking tool (text-selection over text + area drag elsewhere). */\n enableRedact(): void;\n toggleRedact(): void;\n isRedactActive(): boolean;\n\n /** Mark the current text selection (per page) and clear it. False when\n * there is no selection plugin or no selection. */\n queueCurrentSelection(): Promise<boolean>;\n\n /** Load every page's annotations so the pending view is complete (the\n * annotation plane loads lazily per page). Call when opening a panel. */\n preparePending(): Promise<void>;\n /** Pending marks over the LOADED annotation state (see preparePending). */\n getPending(): RedactionPendingItem[];\n pendingCount(): number;\n /**\n * Client-side estimate of how many OTHER annotations the given pending\n * marks (default: all) would destroy — for confirm dialogs BEFORE the\n * apply. The authoritative count comes back on the result.\n */\n estimateCollateral(ids?: string[]): number;\n\n /** Edit a mark's label. Reads the mark's current `/DA` styling and writes\n * the full label set, so a text-only edit never resets the styling. */\n setLabel(ref: AnnotationRef, patch: RedactionLabelPatch): Promise<void>;\n\n /** Apply specific pending marks (refs scope). Irreversible. */\n apply(ids: string[]): Promise<RedactionApplyResult>;\n /** Apply every redaction in the document (pages scope — including marks on\n * pages this client never loaded). Irreversible. */\n applyAll(): Promise<RedactionApplyResult>;\n\n /** Fires after ANY confirmed apply — own or a remote collaborator's. */\n onApplied(cb: (result: RedactionApplyResult) => void): () => void;\n}\n\nexport const RedactionToken = createCapabilityToken<RedactionCapability>('redaction');\n","import { definePlugin } from '@embedpdf/core';\nimport { AnnotationToken } from '@embedpdf/plugin-annotation/internal';\nimport { InteractionToken } from '@embedpdf/plugin-interaction';\nimport { SelectionToken } from '@embedpdf/plugin-selection';\nimport { createRedactionCapability } from './capability';\nimport { initialRedactionState, redactionReducer } from './reducer';\nimport { RedactionToken } from './types';\nimport type { RedactionAction, RedactionCapability, RedactionState } from './types';\n\n/**\n * Document-scoped redaction plugin — the DESTRUCTIVE half of the two-stage\n * model. Marking is annotation-plane work (the composed `redact` tool ships\n * with plugin-annotation); this plugin wraps `doc.redaction.apply`, projects\n * the pending queue out of annotation state, and estimates collateral for\n * confirm dialogs. It owns no mark state of its own.\n *\n * Trust boundary: on a layered document, applying rewrites the LAYER's bytes;\n * the immutable base keeps the original. See the package README.\n */\nexport const redactionPlugin = () =>\n definePlugin<RedactionState, RedactionAction, RedactionCapability>({\n id: 'redaction',\n token: RedactionToken,\n scope: 'document',\n requires: [AnnotationToken],\n optional: [InteractionToken, SelectionToken],\n initialState: initialRedactionState,\n reduce: redactionReducer,\n capability: createRedactionCapability,\n });\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["AnnotationToken","InteractionToken","SelectionToken","AnnotationToken","InteractionToken","SelectionToken"],"sources":["../src/capability.ts","../src/reducer.ts","../src/types.ts","../src/redaction.plugin.ts"],"sourcesContent":["import type { DocCapability, PluginContext } from '@embedpdf/core';\nimport { AnnotationToken } from '@embedpdf/plugin-annotation/internal';\nimport { InteractionToken } from '@embedpdf/plugin-interaction';\nimport { SelectionToken } from '@embedpdf/plugin-selection';\nimport type {\n AnnotationDTO,\n AnnotationRef,\n PdfRect,\n RedactionApplyResult,\n} from '@embedpdf/engine-core';\nimport type {\n RedactionAction,\n RedactionCapability,\n RedactionLabelPatch,\n RedactionPendingItem,\n RedactionState,\n} from './types';\n\n/** Applying destroys content — its own granted power, narrower than annotate. */\nconst REDACT_CAPABILITY: DocCapability = 'doc.redact';\n\n/** The annotation plugin's model-id convention for a durable ref. */\nconst idOf = (ref: AnnotationRef): string =>\n ref.kind === 'objectNumber'\n ? `obj:${ref.annotObjectNumber}`\n : ref.kind === 'nm'\n ? `nm:${ref.nm}`\n : `idx:${ref.pageObjectNumber}:${ref.index}`;\n\ntype RedactDTO = Extract<AnnotationDTO, { subtype: 'redact' }>;\n\n/** The PDF-space regions a redact mark targets: its quads' boxes, else `/Rect`. */\nfunction regionsOf(dto: RedactDTO): PdfRect[] {\n if (dto.quadPoints.length === 0) return [dto.rect];\n return dto.quadPoints.map((q) => {\n const xs = [q.p1.x, q.p2.x, q.p3.x, q.p4.x];\n const ys = [q.p1.y, q.p2.y, q.p3.y, q.p4.y];\n return {\n left: Math.min(...xs),\n right: Math.max(...xs),\n bottom: Math.min(...ys),\n top: Math.max(...ys),\n };\n });\n}\n\n/** Positive-area intersection — the engine's collateral rule. */\nconst intersects = (a: PdfRect, b: PdfRect): boolean =>\n Math.min(a.right, b.right) > Math.max(a.left, b.left) &&\n Math.min(a.top, b.top) > Math.max(a.bottom, b.bottom);\n\nexport function createRedactionCapability(\n ctx: PluginContext<RedactionState, RedactionAction>,\n): RedactionCapability {\n const anno = ctx.get(AnnotationToken);\n const appliedCallbacks = new Set<(result: RedactionApplyResult) => void>();\n\n const requireDoc = () => {\n const doc = ctx.doc;\n if (!doc) throw new Error('[redaction] no document bound');\n return doc;\n };\n const requireService = () => {\n const doc = requireDoc();\n if (!doc.redaction) throw new Error('[redaction] engine has no redaction service');\n return doc.redaction;\n };\n\n const pons = (): number[] => (ctx.document()?.pages ?? []).map((p) => p.pageObjectNumber);\n\n const pageIndexOf = (pon: number): number =>\n ctx.document()?.pages.find((p) => p.pageObjectNumber === pon)?.index ?? -1;\n\n const pendingOn = (pon: number): RedactDTO[] =>\n anno.list(pon).filter((a): a is RedactDTO => a.subtype === 'redact');\n\n const collectPending = (): RedactionPendingItem[] => {\n const items: RedactionPendingItem[] = [];\n for (const pon of pons()) {\n for (const dto of pendingOn(pon)) {\n items.push({\n id: idOf(dto.ref),\n ref: dto.ref,\n pageObjectNumber: pon,\n pageIndex: pageIndexOf(pon),\n kind: dto.quadPoints.length > 0 ? 'text' : 'area',\n overlayText: dto.overlayText,\n });\n }\n }\n return items;\n };\n\n const fireApplied = (result: RedactionApplyResult) => {\n for (const cb of appliedCallbacks) {\n try {\n cb(result);\n } catch (err) {\n console.error('[redaction] onApplied callback failed', err);\n }\n }\n };\n\n const runApply = async (\n scope: Parameters<ReturnType<typeof requireService>['apply']>[0],\n ): Promise<RedactionApplyResult> => {\n const service = requireService();\n ctx.dispatch({ type: 'APPLY_STARTED' });\n try {\n const result = await service.apply(scope);\n ctx.dispatch({ type: 'APPLY_FINISHED', result });\n fireApplied(result);\n return result;\n } catch (err) {\n ctx.dispatch({ type: 'APPLY_FINISHED', result: null });\n throw err;\n }\n };\n\n // A REMOTE collaborator's apply arrives only as a document event (own\n // applies flow through runApply above). Annotation state + raster\n // invalidation are handled by their own planes; here we only surface it.\n const doc = ctx.doc;\n if (doc) {\n ctx.cleanup(\n doc.events.subscribe((event) => {\n if (event.type !== 'redaction.applied') return;\n if (event.origin.kind !== 'remote') return;\n ctx.dispatch({ type: 'APPLY_FINISHED', result: event });\n fireApplied(event);\n }),\n );\n }\n\n return {\n canApply: () =>\n (ctx.doc?.redaction !== undefined && ctx.doc?.security.allows(REDACT_CAPABILITY)) ?? false,\n isApplying: () => ctx.getState().applying,\n lastResult: () => ctx.getState().lastResult,\n\n enableRedact: () => ctx.tryGet(InteractionToken)?.activateTool('redact'),\n toggleRedact: () => {\n const interaction = ctx.tryGet(InteractionToken);\n if (!interaction) return;\n interaction.activateTool(interaction.activeToolId() === 'redact' ? 'pointer' : 'redact');\n },\n isRedactActive: () => ctx.tryGet(InteractionToken)?.activeToolId() === 'redact',\n\n queueCurrentSelection: async () => {\n const selection = ctx.tryGet(SelectionToken);\n if (!selection || !selection.hasSelection()) return false;\n const snapshot = selection.snapshot();\n for (const page of snapshot.pages) {\n if (page.segments.length === 0) continue;\n // Preserve the selected text frame all the way into `/QuadPoints`.\n // Native apply now uses these exact cells for text removal and the\n // final overlay, so marked == previewed == applied for oriented text.\n anno.createMarkup(\n 'redact',\n page.pon,\n page.segments.map((segment) => segment.quad),\n 'redact',\n );\n }\n selection.clear();\n return true;\n },\n\n preparePending: async () => {\n await Promise.all(pons().map((pon) => anno.ensurePage(pon)));\n },\n getPending: collectPending,\n pendingCount: () => collectPending().length,\n\n estimateCollateral: (ids) => {\n const wanted = ids ? new Set(ids) : null;\n let count = 0;\n for (const pon of pons()) {\n const all = anno.list(pon);\n const marks = all.filter(\n (a): a is RedactDTO => a.subtype === 'redact' && (!wanted || wanted.has(idOf(a.ref))),\n );\n if (marks.length === 0) continue;\n const regions = marks.flatMap(regionsOf);\n for (const other of all) {\n if (other.subtype === 'redact') continue;\n if (regions.some((r) => intersects(r, other.rect))) count++;\n }\n }\n return count;\n },\n\n setLabel: async (ref, patch: RedactionLabelPatch) => {\n const current = anno.get(ref);\n if (!current || current.subtype !== 'redact') {\n throw new Error('[redaction] setLabel target is not a redact annotation');\n }\n // Always carry the current /DA styling: the engine rewrites /DA whenever\n // a label field rides a patch, so a text-only edit must not let the\n // styling fall back to defaults.\n await anno.update(ref, {\n subtype: 'redact',\n ...(patch.overlayText !== undefined ? { overlayText: patch.overlayText } : {}),\n ...(patch.repeat !== undefined ? { repeat: patch.repeat } : {}),\n fontFamily: current.fontFamily,\n fontSize: current.fontSize,\n fontColor: current.fontColor,\n textAlign: current.textAlign,\n });\n },\n\n apply: async (ids) => {\n const wanted = new Set(ids);\n const refs = collectPending()\n .filter((p) => wanted.has(p.id))\n .map((p) => p.ref);\n if (refs.length === 0) throw new Error('[redaction] no matching pending redactions');\n return runApply({ kind: 'annotations', refs });\n },\n\n applyAll: async () => {\n // Pages scope: authoritative for the WHOLE document, including marks on\n // pages this client never loaded. Pages without marks report 'unchanged'.\n const pageObjectNumbers = pons();\n if (pageObjectNumbers.length === 0) throw new Error('[redaction] document has no pages');\n return runApply({ kind: 'pages', pageObjectNumbers });\n },\n\n onApplied: (cb) => {\n appliedCallbacks.add(cb);\n return () => appliedCallbacks.delete(cb);\n },\n };\n}\n","import type { RedactionAction, RedactionState } from './types';\n\nexport const initialRedactionState = (): RedactionState => ({\n applying: false,\n lastResult: null,\n});\n\nexport const redactionReducer = (\n state: RedactionState,\n action: RedactionAction,\n): RedactionState => {\n switch (action.type) {\n case 'APPLY_STARTED':\n return { ...state, applying: true };\n case 'APPLY_FINISHED':\n return { applying: false, lastResult: action.result ?? state.lastResult };\n default:\n return state;\n }\n};\n","import { createCapabilityToken } from '@embedpdf/core';\nimport type { AnnotationRef, RedactionApplyResult } from '@embedpdf/engine-core';\n\n/**\n * One pending redaction mark — a live projection of a `redact` annotation on\n * the annotation plane (the plugin owns NO mark state of its own; delete the\n * annotation and the pending item is gone).\n */\nexport interface RedactionPendingItem {\n /** Stable list key derived from the ref. */\n id: string;\n ref: AnnotationRef;\n pageObjectNumber: number;\n /** Display index (0-based) from the page registry — for \"page N\" labels. */\n pageIndex: number;\n /** `area` = rect-only mark (marquee); `text` = per-line quads (selection). */\n kind: 'area' | 'text';\n /** `/OverlayText` label, when set. */\n overlayText: string | null;\n}\n\nexport interface RedactionState {\n /** A destructive apply is in flight. */\n applying: boolean;\n /** The last apply result seen (own or remote), for status UI. */\n lastResult: RedactionApplyResult | null;\n}\n\nexport type RedactionAction =\n | { type: 'APPLY_STARTED' }\n | { type: 'APPLY_FINISHED'; result: RedactionApplyResult | null };\n\n/** A label edit: `overlayText: null` clears it; `repeat` tiles it. */\nexport interface RedactionLabelPatch {\n overlayText?: string | null;\n repeat?: boolean;\n}\n\nexport interface RedactionCapability {\n /** Engine support present AND this session holds `doc.redact`. The engine\n * enforces the same capability independently — this is the UI mirror. */\n canApply(): boolean;\n isApplying(): boolean;\n lastResult(): RedactionApplyResult | null;\n\n /** The composed marking tool (text-selection over text + area drag elsewhere). */\n enableRedact(): void;\n toggleRedact(): void;\n isRedactActive(): boolean;\n\n /** Mark the current text selection (per page) and clear it. False when\n * there is no selection plugin or no selection. */\n queueCurrentSelection(): Promise<boolean>;\n\n /** Load every page's annotations so the pending view is complete (the\n * annotation plane loads lazily per page). Call when opening a panel. */\n preparePending(): Promise<void>;\n /** Pending marks over the LOADED annotation state (see preparePending). */\n getPending(): RedactionPendingItem[];\n pendingCount(): number;\n /**\n * Client-side estimate of how many OTHER annotations the given pending\n * marks (default: all) would destroy — for confirm dialogs BEFORE the\n * apply. The authoritative count comes back on the result.\n */\n estimateCollateral(ids?: string[]): number;\n\n /** Edit a mark's label. Reads the mark's current `/DA` styling and writes\n * the full label set, so a text-only edit never resets the styling. */\n setLabel(ref: AnnotationRef, patch: RedactionLabelPatch): Promise<void>;\n\n /** Apply specific pending marks (refs scope). Irreversible. */\n apply(ids: string[]): Promise<RedactionApplyResult>;\n /** Apply every redaction in the document (pages scope — including marks on\n * pages this client never loaded). Irreversible. */\n applyAll(): Promise<RedactionApplyResult>;\n\n /** Fires after ANY confirmed apply — own or a remote collaborator's. */\n onApplied(cb: (result: RedactionApplyResult) => void): () => void;\n}\n\nexport const RedactionToken = createCapabilityToken<RedactionCapability>('redaction');\n","import { definePlugin } from '@embedpdf/core';\nimport { AnnotationToken } from '@embedpdf/plugin-annotation/internal';\nimport { InteractionToken } from '@embedpdf/plugin-interaction';\nimport { SelectionToken } from '@embedpdf/plugin-selection';\nimport { createRedactionCapability } from './capability';\nimport { initialRedactionState, redactionReducer } from './reducer';\nimport { RedactionToken } from './types';\nimport type { RedactionAction, RedactionCapability, RedactionState } from './types';\n\n/**\n * Document-scoped redaction plugin — the DESTRUCTIVE half of the two-stage\n * model. Marking is annotation-plane work (the composed `redact` tool ships\n * with plugin-annotation); this plugin wraps `doc.redaction.apply`, projects\n * the pending queue out of annotation state, and estimates collateral for\n * confirm dialogs. It owns no mark state of its own.\n *\n * Trust boundary: on a layered document, applying rewrites the LAYER's bytes;\n * the immutable base keeps the original. See the package README.\n */\nexport const redactionPlugin = () =>\n definePlugin<RedactionState, RedactionAction, RedactionCapability>({\n id: 'redaction',\n token: RedactionToken,\n scope: 'document',\n requires: [AnnotationToken],\n optional: [InteractionToken, SelectionToken],\n initialState: initialRedactionState,\n reduce: redactionReducer,\n capability: createRedactionCapability,\n });\n"],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -113,8 +113,8 @@ function createRedactionCapability(ctx) {
|
|
|
113
113
|
if (!selection || !selection.hasSelection()) return false;
|
|
114
114
|
const snapshot = selection.snapshot();
|
|
115
115
|
for (const page of snapshot.pages) {
|
|
116
|
-
if (page.
|
|
117
|
-
anno.createMarkup("redact", page.pon, page.
|
|
116
|
+
if (page.segments.length === 0) continue;
|
|
117
|
+
anno.createMarkup("redact", page.pon, page.segments.map((segment) => segment.quad), "redact");
|
|
118
118
|
}
|
|
119
119
|
selection.clear();
|
|
120
120
|
return true;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/capability.ts","../src/reducer.ts","../src/types.ts","../src/redaction.plugin.ts"],"sourcesContent":["import type { DocCapability, PluginContext } from '@embedpdf/core';\nimport { AnnotationToken } from '@embedpdf/plugin-annotation/internal';\nimport { InteractionToken } from '@embedpdf/plugin-interaction';\nimport { SelectionToken } from '@embedpdf/plugin-selection';\nimport type {\n AnnotationDTO,\n AnnotationRef,\n PdfRect,\n RedactionApplyResult,\n} from '@embedpdf/engine-core';\nimport type {\n RedactionAction,\n RedactionCapability,\n RedactionLabelPatch,\n RedactionPendingItem,\n RedactionState,\n} from './types';\n\n/** Applying destroys content — its own granted power, narrower than annotate. */\nconst REDACT_CAPABILITY: DocCapability = 'doc.redact';\n\n/** The annotation plugin's model-id convention for a durable ref. */\nconst idOf = (ref: AnnotationRef): string =>\n ref.kind === 'objectNumber'\n ? `obj:${ref.annotObjectNumber}`\n : ref.kind === 'nm'\n ? `nm:${ref.nm}`\n : `idx:${ref.pageObjectNumber}:${ref.index}`;\n\ntype RedactDTO = Extract<AnnotationDTO, { subtype: 'redact' }>;\n\n/** The PDF-space regions a redact mark targets: its quads' boxes, else `/Rect`. */\nfunction regionsOf(dto: RedactDTO): PdfRect[] {\n if (dto.quadPoints.length === 0) return [dto.rect];\n return dto.quadPoints.map((q) => {\n const xs = [q.p1.x, q.p2.x, q.p3.x, q.p4.x];\n const ys = [q.p1.y, q.p2.y, q.p3.y, q.p4.y];\n return {\n left: Math.min(...xs),\n right: Math.max(...xs),\n bottom: Math.min(...ys),\n top: Math.max(...ys),\n };\n });\n}\n\n/** Positive-area intersection — the engine's collateral rule. */\nconst intersects = (a: PdfRect, b: PdfRect): boolean =>\n Math.min(a.right, b.right) > Math.max(a.left, b.left) &&\n Math.min(a.top, b.top) > Math.max(a.bottom, b.bottom);\n\nexport function createRedactionCapability(\n ctx: PluginContext<RedactionState, RedactionAction>,\n): RedactionCapability {\n const anno = ctx.get(AnnotationToken);\n const appliedCallbacks = new Set<(result: RedactionApplyResult) => void>();\n\n const requireDoc = () => {\n const doc = ctx.doc;\n if (!doc) throw new Error('[redaction] no document bound');\n return doc;\n };\n const requireService = () => {\n const doc = requireDoc();\n if (!doc.redaction) throw new Error('[redaction] engine has no redaction service');\n return doc.redaction;\n };\n\n const pons = (): number[] => (ctx.document()?.pages ?? []).map((p) => p.pageObjectNumber);\n\n const pageIndexOf = (pon: number): number =>\n ctx.document()?.pages.find((p) => p.pageObjectNumber === pon)?.index ?? -1;\n\n const pendingOn = (pon: number): RedactDTO[] =>\n anno.list(pon).filter((a): a is RedactDTO => a.subtype === 'redact');\n\n const collectPending = (): RedactionPendingItem[] => {\n const items: RedactionPendingItem[] = [];\n for (const pon of pons()) {\n for (const dto of pendingOn(pon)) {\n items.push({\n id: idOf(dto.ref),\n ref: dto.ref,\n pageObjectNumber: pon,\n pageIndex: pageIndexOf(pon),\n kind: dto.quadPoints.length > 0 ? 'text' : 'area',\n overlayText: dto.overlayText,\n });\n }\n }\n return items;\n };\n\n const fireApplied = (result: RedactionApplyResult) => {\n for (const cb of appliedCallbacks) {\n try {\n cb(result);\n } catch (err) {\n console.error('[redaction] onApplied callback failed', err);\n }\n }\n };\n\n const runApply = async (\n scope: Parameters<ReturnType<typeof requireService>['apply']>[0],\n ): Promise<RedactionApplyResult> => {\n const service = requireService();\n ctx.dispatch({ type: 'APPLY_STARTED' });\n try {\n const result = await service.apply(scope);\n ctx.dispatch({ type: 'APPLY_FINISHED', result });\n fireApplied(result);\n return result;\n } catch (err) {\n ctx.dispatch({ type: 'APPLY_FINISHED', result: null });\n throw err;\n }\n };\n\n // A REMOTE collaborator's apply arrives only as a document event (own\n // applies flow through runApply above). Annotation state + raster\n // invalidation are handled by their own planes; here we only surface it.\n const doc = ctx.doc;\n if (doc) {\n ctx.cleanup(\n doc.events.subscribe((event) => {\n if (event.type !== 'redaction.applied') return;\n if (event.origin.kind !== 'remote') return;\n ctx.dispatch({ type: 'APPLY_FINISHED', result: event });\n fireApplied(event);\n }),\n );\n }\n\n return {\n canApply: () =>\n (ctx.doc?.redaction !== undefined && ctx.doc?.security.allows(REDACT_CAPABILITY)) ?? false,\n isApplying: () => ctx.getState().applying,\n lastResult: () => ctx.getState().lastResult,\n\n enableRedact: () => ctx.tryGet(InteractionToken)?.activateTool('redact'),\n toggleRedact: () => {\n const interaction = ctx.tryGet(InteractionToken);\n if (!interaction) return;\n interaction.activateTool(interaction.activeToolId() === 'redact' ? 'pointer' : 'redact');\n },\n isRedactActive: () => ctx.tryGet(InteractionToken)?.activeToolId() === 'redact',\n\n queueCurrentSelection: async () => {\n const selection = ctx.tryGet(SelectionToken);\n if (!selection || !selection.hasSelection()) return false;\n const snapshot = selection.snapshot();\n for (const page of snapshot.pages) {\n if (page.rects.length === 0) continue;\n anno.createMarkup('redact', page.pon, page.rects, 'redact');\n }\n selection.clear();\n return true;\n },\n\n preparePending: async () => {\n await Promise.all(pons().map((pon) => anno.ensurePage(pon)));\n },\n getPending: collectPending,\n pendingCount: () => collectPending().length,\n\n estimateCollateral: (ids) => {\n const wanted = ids ? new Set(ids) : null;\n let count = 0;\n for (const pon of pons()) {\n const all = anno.list(pon);\n const marks = all.filter(\n (a): a is RedactDTO => a.subtype === 'redact' && (!wanted || wanted.has(idOf(a.ref))),\n );\n if (marks.length === 0) continue;\n const regions = marks.flatMap(regionsOf);\n for (const other of all) {\n if (other.subtype === 'redact') continue;\n if (regions.some((r) => intersects(r, other.rect))) count++;\n }\n }\n return count;\n },\n\n setLabel: async (ref, patch: RedactionLabelPatch) => {\n const current = anno.get(ref);\n if (!current || current.subtype !== 'redact') {\n throw new Error('[redaction] setLabel target is not a redact annotation');\n }\n // Always carry the current /DA styling: the engine rewrites /DA whenever\n // a label field rides a patch, so a text-only edit must not let the\n // styling fall back to defaults.\n await anno.update(ref, {\n subtype: 'redact',\n ...(patch.overlayText !== undefined ? { overlayText: patch.overlayText } : {}),\n ...(patch.repeat !== undefined ? { repeat: patch.repeat } : {}),\n fontFamily: current.fontFamily,\n fontSize: current.fontSize,\n fontColor: current.fontColor,\n textAlign: current.textAlign,\n });\n },\n\n apply: async (ids) => {\n const wanted = new Set(ids);\n const refs = collectPending()\n .filter((p) => wanted.has(p.id))\n .map((p) => p.ref);\n if (refs.length === 0) throw new Error('[redaction] no matching pending redactions');\n return runApply({ kind: 'annotations', refs });\n },\n\n applyAll: async () => {\n // Pages scope: authoritative for the WHOLE document, including marks on\n // pages this client never loaded. Pages without marks report 'unchanged'.\n const pageObjectNumbers = pons();\n if (pageObjectNumbers.length === 0) throw new Error('[redaction] document has no pages');\n return runApply({ kind: 'pages', pageObjectNumbers });\n },\n\n onApplied: (cb) => {\n appliedCallbacks.add(cb);\n return () => appliedCallbacks.delete(cb);\n },\n };\n}\n","import type { RedactionAction, RedactionState } from './types';\n\nexport const initialRedactionState = (): RedactionState => ({\n applying: false,\n lastResult: null,\n});\n\nexport const redactionReducer = (\n state: RedactionState,\n action: RedactionAction,\n): RedactionState => {\n switch (action.type) {\n case 'APPLY_STARTED':\n return { ...state, applying: true };\n case 'APPLY_FINISHED':\n return { applying: false, lastResult: action.result ?? state.lastResult };\n default:\n return state;\n }\n};\n","import { createCapabilityToken } from '@embedpdf/core';\nimport type { AnnotationRef, RedactionApplyResult } from '@embedpdf/engine-core';\n\n/**\n * One pending redaction mark — a live projection of a `redact` annotation on\n * the annotation plane (the plugin owns NO mark state of its own; delete the\n * annotation and the pending item is gone).\n */\nexport interface RedactionPendingItem {\n /** Stable list key derived from the ref. */\n id: string;\n ref: AnnotationRef;\n pageObjectNumber: number;\n /** Display index (0-based) from the page registry — for \"page N\" labels. */\n pageIndex: number;\n /** `area` = rect-only mark (marquee); `text` = per-line quads (selection). */\n kind: 'area' | 'text';\n /** `/OverlayText` label, when set. */\n overlayText: string | null;\n}\n\nexport interface RedactionState {\n /** A destructive apply is in flight. */\n applying: boolean;\n /** The last apply result seen (own or remote), for status UI. */\n lastResult: RedactionApplyResult | null;\n}\n\nexport type RedactionAction =\n | { type: 'APPLY_STARTED' }\n | { type: 'APPLY_FINISHED'; result: RedactionApplyResult | null };\n\n/** A label edit: `overlayText: null` clears it; `repeat` tiles it. */\nexport interface RedactionLabelPatch {\n overlayText?: string | null;\n repeat?: boolean;\n}\n\nexport interface RedactionCapability {\n /** Engine support present AND this session holds `doc.redact`. The engine\n * enforces the same capability independently — this is the UI mirror. */\n canApply(): boolean;\n isApplying(): boolean;\n lastResult(): RedactionApplyResult | null;\n\n /** The composed marking tool (text-selection over text + area drag elsewhere). */\n enableRedact(): void;\n toggleRedact(): void;\n isRedactActive(): boolean;\n\n /** Mark the current text selection (per page) and clear it. False when\n * there is no selection plugin or no selection. */\n queueCurrentSelection(): Promise<boolean>;\n\n /** Load every page's annotations so the pending view is complete (the\n * annotation plane loads lazily per page). Call when opening a panel. */\n preparePending(): Promise<void>;\n /** Pending marks over the LOADED annotation state (see preparePending). */\n getPending(): RedactionPendingItem[];\n pendingCount(): number;\n /**\n * Client-side estimate of how many OTHER annotations the given pending\n * marks (default: all) would destroy — for confirm dialogs BEFORE the\n * apply. The authoritative count comes back on the result.\n */\n estimateCollateral(ids?: string[]): number;\n\n /** Edit a mark's label. Reads the mark's current `/DA` styling and writes\n * the full label set, so a text-only edit never resets the styling. */\n setLabel(ref: AnnotationRef, patch: RedactionLabelPatch): Promise<void>;\n\n /** Apply specific pending marks (refs scope). Irreversible. */\n apply(ids: string[]): Promise<RedactionApplyResult>;\n /** Apply every redaction in the document (pages scope — including marks on\n * pages this client never loaded). Irreversible. */\n applyAll(): Promise<RedactionApplyResult>;\n\n /** Fires after ANY confirmed apply — own or a remote collaborator's. */\n onApplied(cb: (result: RedactionApplyResult) => void): () => void;\n}\n\nexport const RedactionToken = createCapabilityToken<RedactionCapability>('redaction');\n","import { definePlugin } from '@embedpdf/core';\nimport { AnnotationToken } from '@embedpdf/plugin-annotation/internal';\nimport { InteractionToken } from '@embedpdf/plugin-interaction';\nimport { SelectionToken } from '@embedpdf/plugin-selection';\nimport { createRedactionCapability } from './capability';\nimport { initialRedactionState, redactionReducer } from './reducer';\nimport { RedactionToken } from './types';\nimport type { RedactionAction, RedactionCapability, RedactionState } from './types';\n\n/**\n * Document-scoped redaction plugin — the DESTRUCTIVE half of the two-stage\n * model. Marking is annotation-plane work (the composed `redact` tool ships\n * with plugin-annotation); this plugin wraps `doc.redaction.apply`, projects\n * the pending queue out of annotation state, and estimates collateral for\n * confirm dialogs. It owns no mark state of its own.\n *\n * Trust boundary: on a layered document, applying rewrites the LAYER's bytes;\n * the immutable base keeps the original. See the package README.\n */\nexport const redactionPlugin = () =>\n definePlugin<RedactionState, RedactionAction, RedactionCapability>({\n id: 'redaction',\n token: RedactionToken,\n scope: 'document',\n requires: [AnnotationToken],\n optional: [InteractionToken, SelectionToken],\n initialState: initialRedactionState,\n reduce: redactionReducer,\n capability: createRedactionCapability,\n });\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/capability.ts","../src/reducer.ts","../src/types.ts","../src/redaction.plugin.ts"],"sourcesContent":["import type { DocCapability, PluginContext } from '@embedpdf/core';\nimport { AnnotationToken } from '@embedpdf/plugin-annotation/internal';\nimport { InteractionToken } from '@embedpdf/plugin-interaction';\nimport { SelectionToken } from '@embedpdf/plugin-selection';\nimport type {\n AnnotationDTO,\n AnnotationRef,\n PdfRect,\n RedactionApplyResult,\n} from '@embedpdf/engine-core';\nimport type {\n RedactionAction,\n RedactionCapability,\n RedactionLabelPatch,\n RedactionPendingItem,\n RedactionState,\n} from './types';\n\n/** Applying destroys content — its own granted power, narrower than annotate. */\nconst REDACT_CAPABILITY: DocCapability = 'doc.redact';\n\n/** The annotation plugin's model-id convention for a durable ref. */\nconst idOf = (ref: AnnotationRef): string =>\n ref.kind === 'objectNumber'\n ? `obj:${ref.annotObjectNumber}`\n : ref.kind === 'nm'\n ? `nm:${ref.nm}`\n : `idx:${ref.pageObjectNumber}:${ref.index}`;\n\ntype RedactDTO = Extract<AnnotationDTO, { subtype: 'redact' }>;\n\n/** The PDF-space regions a redact mark targets: its quads' boxes, else `/Rect`. */\nfunction regionsOf(dto: RedactDTO): PdfRect[] {\n if (dto.quadPoints.length === 0) return [dto.rect];\n return dto.quadPoints.map((q) => {\n const xs = [q.p1.x, q.p2.x, q.p3.x, q.p4.x];\n const ys = [q.p1.y, q.p2.y, q.p3.y, q.p4.y];\n return {\n left: Math.min(...xs),\n right: Math.max(...xs),\n bottom: Math.min(...ys),\n top: Math.max(...ys),\n };\n });\n}\n\n/** Positive-area intersection — the engine's collateral rule. */\nconst intersects = (a: PdfRect, b: PdfRect): boolean =>\n Math.min(a.right, b.right) > Math.max(a.left, b.left) &&\n Math.min(a.top, b.top) > Math.max(a.bottom, b.bottom);\n\nexport function createRedactionCapability(\n ctx: PluginContext<RedactionState, RedactionAction>,\n): RedactionCapability {\n const anno = ctx.get(AnnotationToken);\n const appliedCallbacks = new Set<(result: RedactionApplyResult) => void>();\n\n const requireDoc = () => {\n const doc = ctx.doc;\n if (!doc) throw new Error('[redaction] no document bound');\n return doc;\n };\n const requireService = () => {\n const doc = requireDoc();\n if (!doc.redaction) throw new Error('[redaction] engine has no redaction service');\n return doc.redaction;\n };\n\n const pons = (): number[] => (ctx.document()?.pages ?? []).map((p) => p.pageObjectNumber);\n\n const pageIndexOf = (pon: number): number =>\n ctx.document()?.pages.find((p) => p.pageObjectNumber === pon)?.index ?? -1;\n\n const pendingOn = (pon: number): RedactDTO[] =>\n anno.list(pon).filter((a): a is RedactDTO => a.subtype === 'redact');\n\n const collectPending = (): RedactionPendingItem[] => {\n const items: RedactionPendingItem[] = [];\n for (const pon of pons()) {\n for (const dto of pendingOn(pon)) {\n items.push({\n id: idOf(dto.ref),\n ref: dto.ref,\n pageObjectNumber: pon,\n pageIndex: pageIndexOf(pon),\n kind: dto.quadPoints.length > 0 ? 'text' : 'area',\n overlayText: dto.overlayText,\n });\n }\n }\n return items;\n };\n\n const fireApplied = (result: RedactionApplyResult) => {\n for (const cb of appliedCallbacks) {\n try {\n cb(result);\n } catch (err) {\n console.error('[redaction] onApplied callback failed', err);\n }\n }\n };\n\n const runApply = async (\n scope: Parameters<ReturnType<typeof requireService>['apply']>[0],\n ): Promise<RedactionApplyResult> => {\n const service = requireService();\n ctx.dispatch({ type: 'APPLY_STARTED' });\n try {\n const result = await service.apply(scope);\n ctx.dispatch({ type: 'APPLY_FINISHED', result });\n fireApplied(result);\n return result;\n } catch (err) {\n ctx.dispatch({ type: 'APPLY_FINISHED', result: null });\n throw err;\n }\n };\n\n // A REMOTE collaborator's apply arrives only as a document event (own\n // applies flow through runApply above). Annotation state + raster\n // invalidation are handled by their own planes; here we only surface it.\n const doc = ctx.doc;\n if (doc) {\n ctx.cleanup(\n doc.events.subscribe((event) => {\n if (event.type !== 'redaction.applied') return;\n if (event.origin.kind !== 'remote') return;\n ctx.dispatch({ type: 'APPLY_FINISHED', result: event });\n fireApplied(event);\n }),\n );\n }\n\n return {\n canApply: () =>\n (ctx.doc?.redaction !== undefined && ctx.doc?.security.allows(REDACT_CAPABILITY)) ?? false,\n isApplying: () => ctx.getState().applying,\n lastResult: () => ctx.getState().lastResult,\n\n enableRedact: () => ctx.tryGet(InteractionToken)?.activateTool('redact'),\n toggleRedact: () => {\n const interaction = ctx.tryGet(InteractionToken);\n if (!interaction) return;\n interaction.activateTool(interaction.activeToolId() === 'redact' ? 'pointer' : 'redact');\n },\n isRedactActive: () => ctx.tryGet(InteractionToken)?.activeToolId() === 'redact',\n\n queueCurrentSelection: async () => {\n const selection = ctx.tryGet(SelectionToken);\n if (!selection || !selection.hasSelection()) return false;\n const snapshot = selection.snapshot();\n for (const page of snapshot.pages) {\n if (page.segments.length === 0) continue;\n // Preserve the selected text frame all the way into `/QuadPoints`.\n // Native apply now uses these exact cells for text removal and the\n // final overlay, so marked == previewed == applied for oriented text.\n anno.createMarkup(\n 'redact',\n page.pon,\n page.segments.map((segment) => segment.quad),\n 'redact',\n );\n }\n selection.clear();\n return true;\n },\n\n preparePending: async () => {\n await Promise.all(pons().map((pon) => anno.ensurePage(pon)));\n },\n getPending: collectPending,\n pendingCount: () => collectPending().length,\n\n estimateCollateral: (ids) => {\n const wanted = ids ? new Set(ids) : null;\n let count = 0;\n for (const pon of pons()) {\n const all = anno.list(pon);\n const marks = all.filter(\n (a): a is RedactDTO => a.subtype === 'redact' && (!wanted || wanted.has(idOf(a.ref))),\n );\n if (marks.length === 0) continue;\n const regions = marks.flatMap(regionsOf);\n for (const other of all) {\n if (other.subtype === 'redact') continue;\n if (regions.some((r) => intersects(r, other.rect))) count++;\n }\n }\n return count;\n },\n\n setLabel: async (ref, patch: RedactionLabelPatch) => {\n const current = anno.get(ref);\n if (!current || current.subtype !== 'redact') {\n throw new Error('[redaction] setLabel target is not a redact annotation');\n }\n // Always carry the current /DA styling: the engine rewrites /DA whenever\n // a label field rides a patch, so a text-only edit must not let the\n // styling fall back to defaults.\n await anno.update(ref, {\n subtype: 'redact',\n ...(patch.overlayText !== undefined ? { overlayText: patch.overlayText } : {}),\n ...(patch.repeat !== undefined ? { repeat: patch.repeat } : {}),\n fontFamily: current.fontFamily,\n fontSize: current.fontSize,\n fontColor: current.fontColor,\n textAlign: current.textAlign,\n });\n },\n\n apply: async (ids) => {\n const wanted = new Set(ids);\n const refs = collectPending()\n .filter((p) => wanted.has(p.id))\n .map((p) => p.ref);\n if (refs.length === 0) throw new Error('[redaction] no matching pending redactions');\n return runApply({ kind: 'annotations', refs });\n },\n\n applyAll: async () => {\n // Pages scope: authoritative for the WHOLE document, including marks on\n // pages this client never loaded. Pages without marks report 'unchanged'.\n const pageObjectNumbers = pons();\n if (pageObjectNumbers.length === 0) throw new Error('[redaction] document has no pages');\n return runApply({ kind: 'pages', pageObjectNumbers });\n },\n\n onApplied: (cb) => {\n appliedCallbacks.add(cb);\n return () => appliedCallbacks.delete(cb);\n },\n };\n}\n","import type { RedactionAction, RedactionState } from './types';\n\nexport const initialRedactionState = (): RedactionState => ({\n applying: false,\n lastResult: null,\n});\n\nexport const redactionReducer = (\n state: RedactionState,\n action: RedactionAction,\n): RedactionState => {\n switch (action.type) {\n case 'APPLY_STARTED':\n return { ...state, applying: true };\n case 'APPLY_FINISHED':\n return { applying: false, lastResult: action.result ?? state.lastResult };\n default:\n return state;\n }\n};\n","import { createCapabilityToken } from '@embedpdf/core';\nimport type { AnnotationRef, RedactionApplyResult } from '@embedpdf/engine-core';\n\n/**\n * One pending redaction mark — a live projection of a `redact` annotation on\n * the annotation plane (the plugin owns NO mark state of its own; delete the\n * annotation and the pending item is gone).\n */\nexport interface RedactionPendingItem {\n /** Stable list key derived from the ref. */\n id: string;\n ref: AnnotationRef;\n pageObjectNumber: number;\n /** Display index (0-based) from the page registry — for \"page N\" labels. */\n pageIndex: number;\n /** `area` = rect-only mark (marquee); `text` = per-line quads (selection). */\n kind: 'area' | 'text';\n /** `/OverlayText` label, when set. */\n overlayText: string | null;\n}\n\nexport interface RedactionState {\n /** A destructive apply is in flight. */\n applying: boolean;\n /** The last apply result seen (own or remote), for status UI. */\n lastResult: RedactionApplyResult | null;\n}\n\nexport type RedactionAction =\n | { type: 'APPLY_STARTED' }\n | { type: 'APPLY_FINISHED'; result: RedactionApplyResult | null };\n\n/** A label edit: `overlayText: null` clears it; `repeat` tiles it. */\nexport interface RedactionLabelPatch {\n overlayText?: string | null;\n repeat?: boolean;\n}\n\nexport interface RedactionCapability {\n /** Engine support present AND this session holds `doc.redact`. The engine\n * enforces the same capability independently — this is the UI mirror. */\n canApply(): boolean;\n isApplying(): boolean;\n lastResult(): RedactionApplyResult | null;\n\n /** The composed marking tool (text-selection over text + area drag elsewhere). */\n enableRedact(): void;\n toggleRedact(): void;\n isRedactActive(): boolean;\n\n /** Mark the current text selection (per page) and clear it. False when\n * there is no selection plugin or no selection. */\n queueCurrentSelection(): Promise<boolean>;\n\n /** Load every page's annotations so the pending view is complete (the\n * annotation plane loads lazily per page). Call when opening a panel. */\n preparePending(): Promise<void>;\n /** Pending marks over the LOADED annotation state (see preparePending). */\n getPending(): RedactionPendingItem[];\n pendingCount(): number;\n /**\n * Client-side estimate of how many OTHER annotations the given pending\n * marks (default: all) would destroy — for confirm dialogs BEFORE the\n * apply. The authoritative count comes back on the result.\n */\n estimateCollateral(ids?: string[]): number;\n\n /** Edit a mark's label. Reads the mark's current `/DA` styling and writes\n * the full label set, so a text-only edit never resets the styling. */\n setLabel(ref: AnnotationRef, patch: RedactionLabelPatch): Promise<void>;\n\n /** Apply specific pending marks (refs scope). Irreversible. */\n apply(ids: string[]): Promise<RedactionApplyResult>;\n /** Apply every redaction in the document (pages scope — including marks on\n * pages this client never loaded). Irreversible. */\n applyAll(): Promise<RedactionApplyResult>;\n\n /** Fires after ANY confirmed apply — own or a remote collaborator's. */\n onApplied(cb: (result: RedactionApplyResult) => void): () => void;\n}\n\nexport const RedactionToken = createCapabilityToken<RedactionCapability>('redaction');\n","import { definePlugin } from '@embedpdf/core';\nimport { AnnotationToken } from '@embedpdf/plugin-annotation/internal';\nimport { InteractionToken } from '@embedpdf/plugin-interaction';\nimport { SelectionToken } from '@embedpdf/plugin-selection';\nimport { createRedactionCapability } from './capability';\nimport { initialRedactionState, redactionReducer } from './reducer';\nimport { RedactionToken } from './types';\nimport type { RedactionAction, RedactionCapability, RedactionState } from './types';\n\n/**\n * Document-scoped redaction plugin — the DESTRUCTIVE half of the two-stage\n * model. Marking is annotation-plane work (the composed `redact` tool ships\n * with plugin-annotation); this plugin wraps `doc.redaction.apply`, projects\n * the pending queue out of annotation state, and estimates collateral for\n * confirm dialogs. It owns no mark state of its own.\n *\n * Trust boundary: on a layered document, applying rewrites the LAYER's bytes;\n * the immutable base keeps the original. See the package README.\n */\nexport const redactionPlugin = () =>\n definePlugin<RedactionState, RedactionAction, RedactionCapability>({\n id: 'redaction',\n token: RedactionToken,\n scope: 'document',\n requires: [AnnotationToken],\n optional: [InteractionToken, SelectionToken],\n initialState: initialRedactionState,\n reduce: redactionReducer,\n capability: createRedactionCapability,\n });\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@embedpdf/plugin-redaction",
|
|
3
|
-
"version": "3.0.0-next.
|
|
3
|
+
"version": "3.0.0-next.4",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Document-scoped redaction: the destructive apply stage over redact annotation marks — pending queue view, collateral estimation, and the doc.redaction.apply wrapper. Marking itself rides the annotation plane.",
|
|
6
6
|
"type": "module",
|
|
@@ -18,11 +18,11 @@
|
|
|
18
18
|
"dist"
|
|
19
19
|
],
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"@embedpdf/core": "3.0.0-next.
|
|
22
|
-
"@embedpdf/plugin-
|
|
23
|
-
"@embedpdf/plugin-
|
|
24
|
-
"@embedpdf/
|
|
25
|
-
"@embedpdf/
|
|
21
|
+
"@embedpdf/core": "3.0.0-next.4",
|
|
22
|
+
"@embedpdf/plugin-interaction": "3.0.0-next.4",
|
|
23
|
+
"@embedpdf/plugin-annotation": "3.0.0-next.4",
|
|
24
|
+
"@embedpdf/engine-core": "3.0.0-next.4",
|
|
25
|
+
"@embedpdf/plugin-selection": "3.0.0-next.4"
|
|
26
26
|
},
|
|
27
27
|
"devDependencies": {
|
|
28
28
|
"typescript": "^5.0.0",
|