@embedpdf/plugin-annotation 3.0.0-next.3 → 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 CHANGED
@@ -1,5 +1,5 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- const require_capability = require("./capability-hb2AU0f6.cjs");
2
+ const require_capability = require("./capability-CYbikyob.cjs");
3
3
  let _embedpdf_core = require("@embedpdf/core");
4
4
  let _embedpdf_plugin_interaction = require("@embedpdf/plugin-interaction");
5
5
  let _embedpdf_plugin_selection = require("@embedpdf/plugin-selection");
@@ -382,9 +382,9 @@ function wireMarkup(annotation, selection, interaction) {
382
382
  const previewSubtype = authoring?.kind === "markup" ? tool.subtype : authoring?.kind === "text-edit" && authoring.operation === "replace" ? "strikeout" : null;
383
383
  selection.setHighlightVisible(previewSubtype == null);
384
384
  if (previewSubtype && tool && selection.hasSelection()) {
385
- const rectsByPage = {};
386
- for (const page of selection.snapshot().pages) rectsByPage[page.pon] = page.rects;
387
- annotation.previewMarkup(previewSubtype, rectsByPage, tool.preset);
385
+ const quadsByPage = {};
386
+ for (const page of selection.snapshot().pages) quadsByPage[page.pon] = page.segments.map((s) => s.quad);
387
+ annotation.previewMarkup(previewSubtype, quadsByPage, tool.preset);
388
388
  } else annotation.clearMarkupPreview();
389
389
  };
390
390
  selection.onChange(sync);
@@ -395,19 +395,30 @@ function wireMarkup(annotation, selection, interaction) {
395
395
  if (!tool || !authoring) return;
396
396
  const snapshot = selection.snapshot();
397
397
  if (authoring.kind === "text-edit" && authoring.operation === "insert") {
398
- if (snapshot.end) annotation.createCaret(snapshot.end.pon, snapshot.end.rect);
398
+ if (snapshot.end) annotation.createCaret(snapshot.end.pon, {
399
+ glyphQuad: snapshot.end.glyphQuad,
400
+ advance: snapshot.end.advance
401
+ });
399
402
  selection.clear();
400
403
  return;
401
404
  }
402
405
  if (authoring.kind === "text-edit" && authoring.operation === "replace") {
403
406
  for (const page of snapshot.pages) {
404
- const endRect = page.rects[page.rects.length - 1];
405
- if (endRect) annotation.createReplaceText(page.pon, page.rects, endRect, tool.preset);
407
+ const last = page.segments[page.segments.length - 1];
408
+ if (!last) continue;
409
+ const anchor = snapshot.end && snapshot.end.pon === page.pon ? {
410
+ glyphQuad: snapshot.end.glyphQuad,
411
+ advance: snapshot.end.advance
412
+ } : {
413
+ glyphQuad: last.quad,
414
+ advance: last.advance
415
+ };
416
+ annotation.createReplaceText(page.pon, page.segments.map((s) => s.quad), anchor, tool.preset);
406
417
  }
407
418
  selection.clear();
408
419
  return;
409
420
  }
410
- for (const page of snapshot.pages) annotation.createMarkup(tool.subtype, page.pon, page.rects, tool.preset);
421
+ for (const page of snapshot.pages) annotation.createMarkup(tool.subtype, page.pon, page.segments.map((s) => s.quad), tool.preset);
411
422
  selection.clear();
412
423
  });
413
424
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["fromDTO","refKey","AnnotationToken","initialModel","AnnotationToken","InteractionToken","SelectionToken","createAnnotationCapability","AnnotationHostToken"],"sources":["../src/effects.ts","../src/handler.ts","../src/markup.ts","../src/reducer.ts","../src/annotation.plugin.ts","../src/index.ts"],"sourcesContent":["/**\n * Makes annotations REACTIVE to remote collaborators — the same pattern the\n * metadata plugin uses for the Info dict.\n *\n * Own edits already update the model through the capability's promise path\n * (optimistic draw + re-sync from the engine DTO). This effect folds in the\n * edits that DIDN'T originate here: it subscribes to the document event stream\n * and applies `annotation.*` events whose `origin.kind === 'remote'` — another\n * session's create/update/move/delete, delivered over SSE on cloud. Filtering\n * to remote avoids double-applying (and racing the temp-id reconcile of) our\n * own echoes.\n */\nimport type { DocumentEvent, EffectContext } from '@embedpdf/core';\nimport { encodeStableIdKey } from '@embedpdf/engine-core/runtime';\nimport { propsFor, update, type Annot, type Msg } from '@embedpdf/core-annotation';\n\nimport { fromDTO, refKey } from './repository';\nimport { AnnotationToken } from './types';\nimport type { AnnotationAction, AnnotationState } from './types';\n\nexport function registerAnnotationEffects(\n ctx: EffectContext<AnnotationState, AnnotationAction>,\n): void {\n const doc = ctx.doc;\n if (!doc) return;\n\n const cropOf = (pon: number) =>\n ctx.document()?.pages.find((p) => p.pageObjectNumber === pon)?.boxes.crop ?? null;\n\n const apply = (msg: Msg): void => {\n const [next] = update(ctx.getState().model, msg);\n ctx.dispatch({ type: 'SET_MODEL', model: next });\n };\n\n const upsert = (dtos: ReadonlyArray<Parameters<typeof fromDTO>[0]>, bumpAp: boolean): void => {\n // `bump` re-fetches rasters, `keep` doesn't. The split is driven by the\n // ENGINE'S appearance echo riding the event (`appearance.changed`) — the\n // same verdict local edits use — so a remote MOVE costs peers zero\n // appearance re-renders while a remote restyle refreshes exactly once.\n const bump: Annot[] = [];\n const keep: Annot[] = [];\n for (const dto of dtos) {\n const crop = cropOf(dto.pageObjectNumber);\n if (!crop) continue;\n // Another session authored this — trust the engine's baked AP.\n const a = fromDTO(dto, crop, 'baked');\n // A remote ATTACHED-link child (grouped /Link under a linkable local\n // parent) folds onto the parent instead of entering the model — the\n // same rule the page-load fold applies (see foldAttachedLinks). A child\n // retarget never repaints the PARENT, so the fold never bumps.\n if (a.subtype === 'link' && a.data?.subtype === 'link' && a.data.replyType === 'group') {\n const parentId = a.data.inReplyTo ? refKey(a.data.inReplyTo) : null;\n const parent = parentId ? ctx.getState().model.byId[parentId] : null;\n if (\n parent &&\n parent.subtype !== 'link' &&\n propsFor(parent.subtype).some((s) => s.key === 'link') &&\n a.ref\n ) {\n const refs = parent.linkRefs ?? [];\n const known = refs.some((r) => refKey(r) === a.id);\n keep.push({\n ...parent,\n link: a.data.target,\n linkRefs: known ? refs : [...refs, a.ref],\n });\n continue;\n }\n }\n (bumpAp ? bump : keep).push(a);\n }\n if (bump.length) apply({ t: 'upsert', annots: bump, bumpAp: true });\n if (keep.length) apply({ t: 'upsert', annots: keep });\n };\n\n const unsubscribe = doc.events.subscribe((event: DocumentEvent) => {\n // Widget appearances are re-baked by FORM value writes — a plane this\n // plugin doesn't own, so no remote-only filter: direct fills emit\n // `form.valueChanged`, while scripted batches emit `form.effectsApplied`.\n // Neither path touches this model directly. The bump tells the render\n // layer to re-fetch exactly the repainted widgets' rasters.\n if (event.type === 'form.valueChanged' || event.type === 'form.effectsApplied') {\n apply({\n t: 'bumpAp',\n ids: event.changedWidgets\n .filter((w) => w.annotObjectNumber > 0)\n .map((w) => encodeStableIdKey({ kind: 'objectNumber', value: w.annotObjectNumber })),\n });\n return;\n }\n // A redaction apply deleted the consumed marks plus every intersecting\n // annotation on the applied pages. ORIGIN-AGNOSTIC: our own applies run\n // through the redaction plugin's doc-level verb, never this plane's\n // capability paths, so the model is stale either way — reload the\n // affected pages from the engine. (Works without plugin-redaction\n // installed: a remote collaborator's apply still reconciles this view.)\n if (event.type === 'redaction.applied') {\n const affected = new Set(\n event.results.filter((r) => r.status === 'applied').map((r) => r.pageObjectNumber),\n );\n if (affected.size) {\n const host = ctx.get(AnnotationToken);\n for (const pon of affected) void host.reloadPage(pon);\n }\n return;\n }\n // Only fold in OTHER sessions' edits; our own flow through the capability.\n if (!('origin' in event) || event.origin.kind !== 'remote') return;\n switch (event.type) {\n case 'annotation.created':\n // A create ships with a freshly baked /AP — fetch it.\n upsert([event.created], true);\n break;\n case 'annotation.updated':\n // The engine's verdict rides the event: preserved moves keep the\n // cached raster, regenerated appearances re-fetch exactly once.\n upsert([event.updated], event.appearance.changed);\n break;\n case 'annotation.moved':\n // A z-order move never touches /AP.\n upsert(event.moved, false);\n break;\n case 'annotation.deleted':\n if (event.deleted) {\n const key = encodeStableIdKey(event.deleted);\n const m = ctx.getState().model;\n if (m.byId[key]) {\n apply({ t: 'remove', ids: [key] });\n } else {\n // Not in the model → possibly a folded ATTACHED-link child a\n // remote session deleted: prune it from its parent's join keys\n // (clearing the parent's link value with the last child).\n for (const id of m.order) {\n const a = m.byId[id];\n const refs = a?.linkRefs;\n if (!a || !refs?.length || !refs.some((r) => refKey(r) === key)) continue;\n const remaining = refs.filter((r) => refKey(r) !== key);\n apply({\n t: 'upsert',\n annots: [\n { ...a, linkRefs: remaining, ...(remaining.length ? {} : { link: null }) },\n ],\n });\n break;\n }\n }\n }\n break;\n default:\n break;\n }\n });\n ctx.cleanup(unsubscribe);\n}\n","import type {\n InteractionCapability,\n InteractionHandler,\n PointerSample,\n} from '@embedpdf/plugin-interaction';\nimport type { PageRotation } from '@embedpdf/core-geometry';\nimport type { Subtype, Vec } from '@embedpdf/core-annotation';\nimport type { AnnotationHostCapability } from './types';\n\nconst MARQUEE_DRAG_THRESHOLD_PX = 4;\nconst isPolyTool = (subtype: Subtype): boolean => subtype === 'polygon' || subtype === 'polyline';\nconst isCalloutTool = (subtype: Subtype): boolean => subtype === 'free-text-callout';\n\n/**\n * Resolve a sample against a gesture's HOME page. Annotation gestures are\n * page-anchored: they track the page they started on, even when the cursor\n * wanders off it — `s.page` re-resolves per event (a page-2 point is a\n * DIFFERENT coordinate frame, the teleport bug), so prefer the source's\n * unclamped projection and fall back to the page hit only when it's the same\n * page. Null → this sample can't speak for the home page; ignore it.\n */\nconst pointOn = (s: PointerSample, pon: number): Vec | null =>\n s.project?.(pon) ?? (s.page?.pon === pon ? s.page.point : null);\n\n/**\n * Click-to-place for every payload-carrying tool (stamp / note / file\n * attachment). Each click places one annotation centred on the point (the\n * tool stays active for repeat placement — v2 rubber-stamp behaviour); the\n * capability's `placeAt` routes by the active tool's kind: armed stamp bytes,\n * a stamp/attachment prompt (pick the spot first, the payload second), or an\n * immediate note. No drag gesture — placement size comes from the content\n * (stamp aspect / the fixed icon box), not the pointer.\n *\n * Priority is deliberately BELOW the edit handler (100): a click over an\n * EXISTING annotation selects it; placement happens on empty page space.\n */\nexport function createPlaceHandler(anno: AnnotationHostCapability): InteractionHandler {\n return {\n id: 'annotation-place',\n // `annotation-stamp` is honoured as a legacy alias for embedder tool\n // configs written before the tags were unified.\n priority: 95,\n enabledFor: (t) => t.enables.has('annotation-place') || t.enables.has('annotation-stamp'),\n onDown: (s) => {\n if (!s.page) return false;\n // The click sample's display rotation drives the tool's `upright` policy —\n // the placement lands reading horizontally on a rotated page/view.\n return anno.placeAt(s.page.pon, s.page.point, s.page.rotation);\n },\n };\n}\n\n/**\n * The armed tool's FOOTPRINT ghost: every hover re-computes the would-be\n * placement under the cursor (stamp image fit / click-create default geometry);\n * off-page clears it. One handler for every tool — `ghostHoverAt` resolves the\n * tool's ghost policy and clears when it isn't `footprint`. Never captures:\n * the highest priority makes its onDown run FIRST on every press (hiding the\n * ghost while a gesture runs), then declines so the real handlers route.\n */\nexport function createGhostHandler(\n anno: AnnotationHostCapability,\n interaction: InteractionCapability,\n): InteractionHandler {\n const hover = (s: PointerSample): void => {\n if (s.page)\n anno.ghostHoverAt(interaction.activeToolId(), s.page.pon, s.page.point, s.page.rotation);\n else anno.clearGhost();\n };\n return {\n id: 'annotation-ghost',\n priority: 1000,\n enabledFor: () => true,\n onDown: () => {\n anno.clearGhost();\n return false;\n },\n onHover: hover,\n };\n}\n\n/**\n * Ambient editing: live under the `annotation-edit` tag, which BOTH the pointer\n * and pan tools enable — so you select/move/resize in any navigation mode (Adobe\n * behaviour). It captures only over an annotation/handle; over empty it\n * deselects and declines (so text-selection / pan still work). Hover drives the\n * cursor (move / pointer / resize) via a priority cursor claim.\n */\nexport function createEditHandler(\n anno: AnnotationHostCapability,\n interaction: InteractionCapability,\n): InteractionHandler {\n // The gesture's home page + last resolved point, armed on down. Every\n // move/up resolves against THIS page (the annotation slides along its edge\n // when the cursor overshoots — the core clamps), never against whatever\n // page the sample happens to hit.\n let origin: { pon: number; point: Vec } | null = null;\n return {\n id: 'annotation-edit',\n priority: 100,\n enabledFor: (t) => t.enables.has('annotation-edit'),\n onDown: (s) => {\n if (!s.page) return false;\n // While a free-text box is being edited it owns its own pointer events, so a\n // down that reaches the hub at all is a click OUTSIDE the editor — commit and\n // leave text edit. This makes exit hub-driven (deterministic) rather than\n // relying on a DOM blur, which races the focus-steal of the entering gesture.\n const wasEditing = anno.currentEditing() != null;\n if (wasEditing) anno.endTextEdit();\n if (\n anno.hitKind(s.page.pon, s.page.point, s.page.scale, s.page.rotation, s.page.zoom) ===\n 'empty'\n ) {\n // Plain empty click drops the selection. Shift-empty preserves it so the\n // lower-priority marquee handler can additive/toggle-select.\n if (!s.modifiers.shift) anno.deselect();\n // A click that dismissed an active edit is CONSUMED: its sole job was to\n // leave edit mode, so the draw tool doesn't also spawn a new annotation\n // (matches v2). Only when nothing was being edited do we decline, letting\n // pan / text-selection / draw act on the empty click.\n return wasEditing;\n }\n // Double-click over a free-text box → enter text edit (not a move).\n if ((s.clickCount ?? 1) >= 2) {\n anno.beginTextEditAt(s.page.pon, s.page.point, s.page.scale, s.page.rotation, s.page.zoom);\n return true;\n }\n anno.editPointer(\n 'down',\n s.page.pon,\n s.page.point,\n s.modifiers.shift,\n s.page.scale,\n s.page.rotation,\n s.page.zoom,\n );\n origin = { pon: s.page.pon, point: s.page.point };\n return true;\n },\n onMove: (s) => {\n if (!origin) return;\n const point = pointOn(s, origin.pon);\n if (!point) return;\n origin.point = point;\n // The sample's scale/rotation ride along (uniform across pages), so a\n // screen-anchored member page-clamps at its effective footprint mid-drag.\n anno.editPointer(\n 'move',\n origin.pon,\n point,\n s.modifiers.shift,\n s.page?.scale,\n s.page?.rotation,\n s.page?.zoom,\n );\n },\n onUp: (s) => {\n if (!origin) return;\n // ALWAYS close the gesture — a release over a page gap or outside the\n // window must still commit (a dangling draft leaves a ghost that snaps\n // back on the next interaction). `editUp` doesn't read the point.\n anno.editPointer('up', origin.pon, pointOn(s, origin.pon) ?? origin.point, false);\n origin = null;\n },\n onHover: (s) => {\n // priority 20 → beats text-select's 'text' (10) over an annotation; null clears.\n interaction.setCursor(\n 'annotation',\n s.page\n ? anno.cursorAt(s.page.pon, s.page.point, s.page.scale, s.page.rotation, s.page.zoom)\n : null,\n 20,\n );\n // Hover state for scene affordances (redact preview) — rides the same\n // per-move sample; the capability dispatches on change only.\n anno.hoverAt(\n s.page\n ? {\n pon: s.page.pon,\n point: s.page.point,\n scale: s.page.scale,\n rotation: s.page.rotation,\n zoom: s.page.zoom,\n }\n : null,\n );\n },\n };\n}\n\n/**\n * Empty-page drag selection. Lower priority than annotation edit and text\n * selection, so it only owns drags that begin on empty, non-text page space.\n */\nexport function createMarqueeHandler(anno: AnnotationHostCapability): InteractionHandler {\n let anchor: {\n pon: number;\n point: Vec;\n vx: number;\n vy: number;\n shift: boolean;\n } | null = null;\n let last: { pon: number; point: Vec } | null = null;\n let dragging = false;\n\n // The marquee page's view env at the down sample — the `up` intersects\n // screen-anchored annotations at their effective footprint with it.\n let view: { scale?: number; rotation?: PageRotation; zoom?: number } = {};\n return {\n id: 'annotation-marquee',\n priority: 50,\n enabledFor: (t) => t.enables.has('annotation-marquee'),\n onDown: (s) => {\n if (!s.page) return false;\n anchor = {\n pon: s.page.pon,\n point: s.page.point,\n vx: s.viewport.x,\n vy: s.viewport.y,\n shift: s.modifiers.shift,\n };\n last = { pon: s.page.pon, point: s.page.point };\n view = { scale: s.page.scale, rotation: s.page.rotation, zoom: s.page.zoom };\n dragging = false;\n return true;\n },\n onMove: (s) => {\n if (!anchor) return;\n // Anchored to the page the drag started on; the projected point keeps the\n // marquee growing along the page edge when the cursor overshoots (the\n // core clamps it to the page box).\n const point = pointOn(s, anchor.pon);\n if (!point) return;\n last = { pon: anchor.pon, point };\n if (!dragging) {\n if (\n Math.hypot(s.viewport.x - anchor.vx, s.viewport.y - anchor.vy) < MARQUEE_DRAG_THRESHOLD_PX\n ) {\n return;\n }\n dragging = true;\n anno.marqueePointer(\n 'down',\n anchor.pon,\n anchor.point,\n anchor.shift,\n view.scale,\n view.rotation,\n view.zoom,\n );\n }\n anno.marqueePointer(\n 'move',\n anchor.pon,\n point,\n anchor.shift,\n view.scale,\n view.rotation,\n view.zoom,\n );\n },\n onUp: () => {\n if (dragging && anchor && last) {\n anno.marqueePointer(\n 'up',\n anchor.pon,\n last.point,\n anchor.shift,\n view.scale,\n view.rotation,\n view.zoom,\n );\n }\n anchor = null;\n last = null;\n dragging = false;\n },\n };\n}\n\n/** Drawing: live under `annotation-draw` (the square / circle / line tools). */\nexport function createDrawHandler(\n anno: AnnotationHostCapability,\n interaction: InteractionCapability,\n): InteractionHandler {\n // The active tool id + its ROUTING subtype. The id is what `createPointer` takes\n // (it resolves the defaults preset — arrow vs line); the subtype is what the\n // poly/callout gesture checks read (arrow → `line`, a polygon preset → `polygon`).\n const toolId = () => interaction.activeToolId();\n const subtypeOf = (id: string): Subtype => anno.toolSubtype(id);\n let drawingPoly = false;\n // A callout is mid-creation between its tip/knee/box clicks; while it is, hover\n // (no button) must still drive the leader/box preview, like a poly's vertices.\n let drawingCallout = false;\n // The active drag's home page (down→up): moves/ups resolve against it, so a\n // shape keeps sizing along the page edge when the cursor overshoots.\n let origin: { pon: number; point: Vec } | null = null;\n let pendingInk: {\n tool: string;\n pon: number;\n timer: ReturnType<typeof setTimeout>;\n } | null = null;\n const flushPendingInk = () => {\n if (!pendingInk) return;\n clearTimeout(pendingInk.timer);\n pendingInk = null;\n anno.finishInkDraft();\n };\n interaction.onToolChange(() => {\n flushPendingInk();\n drawingPoly = false;\n drawingCallout = false;\n origin = null;\n });\n return {\n id: 'annotation-draw',\n // The gesture cascade — relative order IS the composition policy:\n // 100 annotation-edit existing annotations own their gestures\n // 95 annotation-place click-to-place armed tools\n // 60 text-select claims TEXT ONLY (self-gates via isOverText,\n // yields everywhere else)\n // 55 annotation-draw drag-create takes whatever text didn't claim\n // 50 annotation-marquee empty-space selection, the final fallback\n // Draw sits BELOW text-select so a COMPOSED tool (redact: text-select +\n // annotation-draw) lets text claim text — over a paragraph the drag makes\n // per-line quad marks, anywhere else it drag-creates. Priority only breaks\n // ties between simultaneously-ELIGIBLE handlers, and draw-only tools\n // (square/ink/arrow…) never co-enable text-select, so they are unaffected.\n priority: 55,\n enabledFor: (t) => t.enables.has('annotation-draw'),\n onDown: (s) => {\n if (!s.page) return false;\n const tool = toolId();\n const st = subtypeOf(tool);\n if (st === 'ink' && pendingInk) {\n if (pendingInk.tool === tool && pendingInk.pon === s.page.pon) {\n clearTimeout(pendingInk.timer);\n pendingInk = null;\n } else {\n flushPendingInk();\n }\n }\n // A down is a fresh intent — it may legitimately start on another page\n // (the core restarts the draft there), so it re-anchors the gesture.\n origin = { pon: s.page.pon, point: s.page.point };\n if (isPolyTool(st)) {\n const finish = (s.clickCount ?? 1) >= 2;\n anno.createPointer(tool, 'down', s.page.pon, s.page.point, finish);\n drawingPoly = !finish;\n return true;\n }\n drawingPoly = false;\n // Each callout click advances the core's tip → knee → box state machine; the\n // final box click/drag commits and clears the draft (so `drawingCallout`\n // resets on the next tool change or simply idles harmlessly).\n if (isCalloutTool(st)) drawingCallout = true;\n // The DOWN sample's display rotation rides along for the tool's `upright`\n // policy; the core captures it on the draft (later phases don't carry it).\n anno.createPointer(tool, 'down', s.page.pon, s.page.point, false, s.page.rotation);\n return true;\n },\n onMove: (s) => {\n const tool = toolId();\n const st = subtypeOf(tool);\n // Drag-moves (button down): rect/line/ink/free-text size their box, and a\n // callout (a non-poly tool) sizes its text box during the box step. Poly\n // tools take vertices by click, so they ignore drag-moves.\n if (!origin || (isPolyTool(st) && !drawingPoly)) return;\n const point = pointOn(s, origin.pon);\n if (!point) return;\n origin.point = point;\n anno.createPointer(tool, 'move', origin.pon, point);\n },\n onUp: (s) => {\n const tool = toolId();\n const st = subtypeOf(tool);\n if (origin && !isPolyTool(st)) {\n // ALWAYS commit the drag, even released off-page (point pins in core).\n anno.createPointer(tool, 'up', origin.pon, pointOn(s, origin.pon) ?? origin.point);\n if (st === 'ink') {\n const groupStrokesMs = anno.tool(tool)?.ink?.groupStrokesMs ?? 0;\n if (groupStrokesMs > 0) {\n const pon = origin.pon;\n const timer = setTimeout(() => {\n pendingInk = null;\n anno.finishInkDraft();\n }, groupStrokesMs);\n pendingInk = { tool, pon, timer };\n }\n }\n }\n origin = null;\n },\n onHover: (s) => {\n const tool = toolId();\n const st = subtypeOf(tool);\n // Hover preview for the multi-click tools: poly (while placing vertices) and\n // callout (while placing the tip/knee/box) follow the cursor between clicks.\n if (s.page && ((drawingPoly && isPolyTool(st)) || (drawingCallout && isCalloutTool(st)))) {\n anno.createPointer(tool, 'move', s.page.pon, s.page.point);\n }\n },\n };\n}\n","/**\n * Text-selection authoring: the bridge that turns a TEXT SELECTION into markup\n * or a text-edit annotation (Insert / Replace). These tools have no gesture of\n * their own — they enable the selection plugin's `text-select` gesture, and this\n * module consumes its typed signals (`onCommit` to create, `onChange` to preview).\n * Selection is the producer; annotation the consumer — selection never knows\n * annotation exists. Kept out of the plugin-definition file so that stays lean.\n */\nimport type { InteractionCapability } from '@embedpdf/plugin-interaction';\nimport type { SelectionCapability } from '@embedpdf/plugin-selection';\nimport type { AnnotationHostCapability } from './types';\n\n/**\n * Wire the selection → annotation BRIDGE. The markup / caret TOOLS and their\n * defaults are registered by the plugin `init` from the tool registry; this\n * function only consumes the selection plugin's typed signals — so call it from\n * `init` only when a selection plugin is present. Selection is the producer,\n * annotation the consumer; selection never knows annotation exists.\n */\nexport function wireMarkup(\n annotation: AnnotationHostCapability,\n selection: SelectionCapability,\n interaction: InteractionCapability,\n): void {\n // Keep the live preview + the selection's own visual in sync with (active tool,\n // selection). While a markup tool is active the blue highlight is suppressed and\n // the in-progress selection renders as a markup ghost instead.\n const sync = () => {\n const tool = annotation.tool(interaction.activeToolId());\n const authoring = tool?.selection;\n const previewSubtype =\n authoring?.kind === 'markup'\n ? tool!.subtype\n : authoring?.kind === 'text-edit' && authoring.operation === 'replace'\n ? 'strikeout'\n : null;\n selection.setHighlightVisible(previewSubtype == null);\n if (previewSubtype && tool && selection.hasSelection()) {\n const rectsByPage: Record<number, ReturnType<typeof selection.rectsForPage>> = {};\n for (const page of selection.snapshot().pages) rectsByPage[page.pon] = page.rects;\n annotation.previewMarkup(previewSubtype, rectsByPage, tool.preset);\n } else {\n annotation.clearMarkupPreview();\n }\n };\n selection.onChange(sync); // drag-extend → live preview\n interaction.onToolChange(sync); // entering/leaving a markup tool → restore blue / clear ghost\n\n // On gesture-end, if a markup tool is active, turn the selection into markup.\n selection.onCommit(() => {\n const tool = annotation.tool(interaction.activeToolId());\n const authoring = tool?.selection;\n if (!tool || !authoring) return; // pointer tool → leave the selection (copy)\n const snapshot = selection.snapshot();\n if (authoring.kind === 'text-edit' && authoring.operation === 'insert') {\n if (snapshot.end) annotation.createCaret(snapshot.end.pon, snapshot.end.rect);\n selection.clear();\n return;\n }\n if (authoring.kind === 'text-edit' && authoring.operation === 'replace') {\n // `/IRT` relationships are page-local, so a cross-page selection becomes\n // one self-contained Caret + StrikeOut pair per page.\n for (const page of snapshot.pages) {\n const endRect = page.rects[page.rects.length - 1];\n if (endRect) annotation.createReplaceText(page.pon, page.rects, endRect, tool.preset);\n }\n selection.clear();\n return;\n }\n for (const page of snapshot.pages) {\n annotation.createMarkup(tool.subtype, page.pon, page.rects, tool.preset);\n }\n selection.clear(); // fires onChange → preview clears; blue stays suppressed (markup tool still active)\n });\n}\n","import { initialModel } from '@embedpdf/core-annotation';\nimport type {\n AnnotationAction,\n AnnotationConfig,\n AnnotationState,\n ChromeSettings,\n ChromeSettingsPatch,\n} from './types';\n\n/**\n * Out-of-the-box selection chrome — a sensible document-annotation feel. Every\n * length is CSS px (screen-constant across zoom); every color falls back to\n * `accent`. Just defaults: override any field at registration\n * (`annotationPlugin({ chrome })`) or at runtime (`setChrome`).\n */\nexport const DEFAULT_CHROME: ChromeSettings = {\n accent: '#3858e9',\n // Solid, like the shape's own resting look — one style at rest AND rotated.\n outline: { style: 'solid', width: 1 },\n // 8px squares to look at, 24px to grab (touch-friendly without visual bulk).\n handles: { size: 8, hitSize: 24, fill: '#ffffff' },\n knob: { size: 10, hitSize: 24, offset: 32, stalk: true, fill: '#ffffff' },\n // Faint reference cross, prominent live indicator (v2 feel).\n guides: { enabled: true, style: 'solid', width: 1, axisOpacity: 0.35, indicatorOpacity: 0.8 },\n};\n\n/** Deep-partial merge of a chrome patch — one level per piece, like the\n * stage-settings convention (align pairs / pageFrame). */\nexport const mergeChrome = (base: ChromeSettings, patch: ChromeSettingsPatch): ChromeSettings => ({\n accent: patch.accent ?? base.accent,\n outline: { ...base.outline, ...patch.outline },\n handles: { ...base.handles, ...patch.handles },\n knob: { ...base.knob, ...patch.knob },\n guides: { ...base.guides, ...patch.guides },\n});\n\n/**\n * The slice holds the annotation-core Model + the selection-chrome settings.\n * The pure `update` runs in the capability (the shell, which also performs\n * effects); the reducer only stores new state — keeping the kernel store a\n * dumb, serializable container. The registration config seeds the model's\n * snap settings and the chrome.\n */\nexport const initialAnnotationState = (config: AnnotationConfig = {}): AnnotationState => ({\n model: { ...initialModel, snap: { ...initialModel.snap, ...config.snap } },\n chrome: mergeChrome(DEFAULT_CHROME, config.chrome ?? {}),\n toolGhost: null,\n stampArmEpoch: 0,\n});\n\nexport const annotationReducer = (\n state: AnnotationState,\n action: AnnotationAction,\n): AnnotationState => {\n switch (action.type) {\n case 'SET_MODEL':\n return { ...state, model: action.model };\n case 'SET_CHROME':\n return { ...state, chrome: mergeChrome(state.chrome, action.patch) };\n case 'SET_TOOL_GHOST':\n return { ...state, toolGhost: action.ghost };\n case 'STAMP_ARM_CHANGED':\n // A new (or dropped) payload invalidates any ghost drawn for the old one.\n return { ...state, stampArmEpoch: state.stampArmEpoch + 1, toolGhost: null };\n default:\n return state;\n }\n};\n","import { definePlugin } from '@embedpdf/core';\nimport { InteractionToken } from '@embedpdf/plugin-interaction';\nimport { SelectionToken } from '@embedpdf/plugin-selection';\nimport { createAnnotationCapability } from './capability';\nimport { registerAnnotationEffects } from './effects';\nimport {\n createDrawHandler,\n createEditHandler,\n createGhostHandler,\n createMarqueeHandler,\n createPlaceHandler,\n} from './handler';\nimport { wireMarkup } from './markup';\nimport { annotationReducer, initialAnnotationState } from './reducer';\nimport { AnnotationToken } from './types';\nimport type {\n AnnotationAction,\n AnnotationConfig,\n AnnotationHostCapability,\n AnnotationState,\n} from './types';\n\n/**\n * The annotation plugin. Document-scoped; requires the interaction hub and\n * OPTIONALLY uses the selection plugin. Shapes/ink work with no selection; text\n * markup lights up only when a selection plugin is present.\n */\nexport const annotationPlugin = (config: AnnotationConfig = {}) =>\n definePlugin<AnnotationState, AnnotationAction, AnnotationHostCapability>({\n id: 'annotation',\n token: AnnotationToken,\n scope: 'document',\n requires: [InteractionToken],\n optional: [SelectionToken],\n initialState: () => initialAnnotationState(config),\n reduce: annotationReducer,\n // The capability owns the resolved tool registry (built-ins + config `tools`),\n // so it needs the config too — not just the reducer state.\n capability: (ctx) => createAnnotationCapability(ctx, config),\n // Fold in remote collaborators' edits (own edits flow through the capability).\n effects: registerAnnotationEffects,\n init: (ctx) => {\n const interaction = ctx.get(InteractionToken);\n const annotation = ctx.get(AnnotationToken);\n const selection = ctx.tryGet(SelectionToken);\n\n // Register every resolved tool (shapes, lines, ink, free-text, markup, stamp,\n // plus anything the embedder added via config `tools`) and seed its defaults.\n // A tool is a named preset over a subtype — see `tools.ts`. Markup / caret\n // tools ride the selection plugin's `text-select` gesture, so they stay\n // inert (skipped) when no selection plugin is installed.\n for (const tool of annotation.tools()) {\n if (tool.enables.has('text-select') && !selection) continue;\n interaction.registerTool({ id: tool.id, cursor: tool.cursor, enables: tool.enables });\n if (tool.defaults) annotation.setDefaults(tool.preset, tool.defaults);\n }\n\n interaction.registerHandler(createPlaceHandler(annotation));\n interaction.registerHandler(createGhostHandler(annotation, interaction));\n interaction.registerHandler(createEditHandler(annotation, interaction));\n interaction.registerHandler(createMarqueeHandler(annotation));\n interaction.registerHandler(createDrawHandler(annotation, interaction));\n interaction.onToolChange(() => {\n annotation.cancel();\n annotation.clearGhost(); // a footprint belongs to the tool that computed it\n // Engagement follows the tool: annotations whose Behavior just engaged\n // (form widgets under a fill tool) drop out of the selection — no\n // stranded chrome on a fill control.\n annotation.pruneEngagedSelection();\n // Leaving the stamp family drops any armed payload — bytes are tool state,\n // not document state (any stamp tool keeps it; a non-stamp tool clears it).\n if (!interaction.activeTool().enables.has('annotation-stamp')) annotation.disarmStamp();\n });\n\n // Markup is opt-in: wire the selection→annotation BRIDGE only when a\n // selection plugin is present (the markup TOOLS were registered above).\n if (selection) wireMarkup(annotation, selection, interaction);\n },\n });\n","/**\n * @embedpdf/plugin-annotation — annotations on the v3 stack.\n *\n * The pure @embedpdf/core-annotation wired to the engine repository (optimistic\n * create/patch/delete) and the interaction hub (ambient editing + draw tools).\n * Behaviors (forms, links) plug in via registerBehavior. Zero framework code.\n */\nimport type { CapabilityToken } from '@embedpdf/core';\nimport { AnnotationToken as AnnotationHostToken } from './types';\nimport type { AnnotationCapability } from './types';\n\nexport { annotationPlugin } from './annotation.plugin';\nexport {\n fromDTO,\n toCreateDraft,\n toPatch,\n refKey,\n styleFromDTO,\n widgetAppearanceFromProps,\n} from './repository';\n// The shared placement layer + the one click↔drag threshold, re-exported so a\n// sibling COMMIT PLANE (the form plugin's place handler) resolves clicks with\n// the exact call the annotation core and the footprint ghost use.\nexport { MIN_DRAG, resolveClickPlacement, type ClickPlacement } from '@embedpdf/core-annotation';\nexport { DEFAULT_CHROME } from './reducer';\nexport { DEFAULT_TOOLS } from './tools';\nexport type {\n AnnotationToolDef,\n AnnotationToolInput,\n GhostPolicy,\n InkAuthoringOptions,\n PromptSourceSpec,\n ResolvedTool,\n SelectionAuthoring,\n StampSourceSpec,\n ToolAuthoringKind,\n ToolDefaultsFor,\n} from './tools';\nexport type {\n AnnotationCapability,\n AnnotationConfig,\n AnnotationState,\n AnnotationAction,\n Behavior,\n ChromeSettings,\n ChromeSettingsPatch,\n LinkNavItem,\n SelectionFlags,\n SelectionProps,\n StampToolInput,\n FilePickerProvider,\n FilePromptRequest,\n TextItem,\n ToolGhost,\n} from './types';\n// The property vocabulary + schema (defined in the portable core; re-exported so\n// app code building property UIs needs only this package).\nexport { propsFor } from '@embedpdf/core-annotation';\nexport type {\n AnnotationFlags,\n AnnotationProps,\n AnnotationPropsPatch,\n BlendMode,\n Border,\n ClickCreate,\n LineEnding,\n LineEndings,\n PropKey,\n PropSpec,\n SnapSettings,\n TextAlign,\n} from '@embedpdf/core-annotation';\n\n/**\n * App-facing annotation token: resolves the public {@link AnnotationCapability}.\n * It is the SAME runtime token the plugin provides, narrowed to the public lens —\n * the framework-only surface (render projection, pointer gestures, behavior\n * registration) is reachable only via `@embedpdf/plugin-annotation/internal`.\n */\nexport const AnnotationToken =\n AnnotationHostToken as unknown as CapabilityToken<AnnotationCapability>;\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["fromDTO","refKey","AnnotationToken","initialModel","AnnotationToken","InteractionToken","SelectionToken","createAnnotationCapability","AnnotationHostToken"],"sources":["../src/effects.ts","../src/handler.ts","../src/markup.ts","../src/reducer.ts","../src/annotation.plugin.ts","../src/index.ts"],"sourcesContent":["/**\n * Makes annotations REACTIVE to remote collaborators — the same pattern the\n * metadata plugin uses for the Info dict.\n *\n * Own edits already update the model through the capability's promise path\n * (optimistic draw + re-sync from the engine DTO). This effect folds in the\n * edits that DIDN'T originate here: it subscribes to the document event stream\n * and applies `annotation.*` events whose `origin.kind === 'remote'` — another\n * session's create/update/move/delete, delivered over SSE on cloud. Filtering\n * to remote avoids double-applying (and racing the temp-id reconcile of) our\n * own echoes.\n */\nimport type { DocumentEvent, EffectContext } from '@embedpdf/core';\nimport { encodeStableIdKey } from '@embedpdf/engine-core/runtime';\nimport { propsFor, update, type Annot, type Msg } from '@embedpdf/core-annotation';\n\nimport { fromDTO, refKey } from './repository';\nimport { AnnotationToken } from './types';\nimport type { AnnotationAction, AnnotationState } from './types';\n\nexport function registerAnnotationEffects(\n ctx: EffectContext<AnnotationState, AnnotationAction>,\n): void {\n const doc = ctx.doc;\n if (!doc) return;\n\n const cropOf = (pon: number) =>\n ctx.document()?.pages.find((p) => p.pageObjectNumber === pon)?.boxes.crop ?? null;\n\n const apply = (msg: Msg): void => {\n const [next] = update(ctx.getState().model, msg);\n ctx.dispatch({ type: 'SET_MODEL', model: next });\n };\n\n const upsert = (dtos: ReadonlyArray<Parameters<typeof fromDTO>[0]>, bumpAp: boolean): void => {\n // `bump` re-fetches rasters, `keep` doesn't. The split is driven by the\n // ENGINE'S appearance echo riding the event (`appearance.changed`) — the\n // same verdict local edits use — so a remote MOVE costs peers zero\n // appearance re-renders while a remote restyle refreshes exactly once.\n const bump: Annot[] = [];\n const keep: Annot[] = [];\n for (const dto of dtos) {\n const crop = cropOf(dto.pageObjectNumber);\n if (!crop) continue;\n // Another session authored this — trust the engine's baked AP.\n const a = fromDTO(dto, crop, 'baked');\n // A remote ATTACHED-link child (grouped /Link under a linkable local\n // parent) folds onto the parent instead of entering the model — the\n // same rule the page-load fold applies (see foldAttachedLinks). A child\n // retarget never repaints the PARENT, so the fold never bumps.\n if (a.subtype === 'link' && a.data?.subtype === 'link' && a.data.replyType === 'group') {\n const parentId = a.data.inReplyTo ? refKey(a.data.inReplyTo) : null;\n const parent = parentId ? ctx.getState().model.byId[parentId] : null;\n if (\n parent &&\n parent.subtype !== 'link' &&\n propsFor(parent.subtype).some((s) => s.key === 'link') &&\n a.ref\n ) {\n const refs = parent.linkRefs ?? [];\n const known = refs.some((r) => refKey(r) === a.id);\n keep.push({\n ...parent,\n link: a.data.target,\n linkRefs: known ? refs : [...refs, a.ref],\n });\n continue;\n }\n }\n (bumpAp ? bump : keep).push(a);\n }\n if (bump.length) apply({ t: 'upsert', annots: bump, bumpAp: true });\n if (keep.length) apply({ t: 'upsert', annots: keep });\n };\n\n const unsubscribe = doc.events.subscribe((event: DocumentEvent) => {\n // Widget appearances are re-baked by FORM value writes — a plane this\n // plugin doesn't own, so no remote-only filter: direct fills emit\n // `form.valueChanged`, while scripted batches emit `form.effectsApplied`.\n // Neither path touches this model directly. The bump tells the render\n // layer to re-fetch exactly the repainted widgets' rasters.\n if (event.type === 'form.valueChanged' || event.type === 'form.effectsApplied') {\n apply({\n t: 'bumpAp',\n ids: event.changedWidgets\n .filter((w) => w.annotObjectNumber > 0)\n .map((w) => encodeStableIdKey({ kind: 'objectNumber', value: w.annotObjectNumber })),\n });\n return;\n }\n // A redaction apply deleted the consumed marks plus every intersecting\n // annotation on the applied pages. ORIGIN-AGNOSTIC: our own applies run\n // through the redaction plugin's doc-level verb, never this plane's\n // capability paths, so the model is stale either way — reload the\n // affected pages from the engine. (Works without plugin-redaction\n // installed: a remote collaborator's apply still reconciles this view.)\n if (event.type === 'redaction.applied') {\n const affected = new Set(\n event.results.filter((r) => r.status === 'applied').map((r) => r.pageObjectNumber),\n );\n if (affected.size) {\n const host = ctx.get(AnnotationToken);\n for (const pon of affected) void host.reloadPage(pon);\n }\n return;\n }\n // Only fold in OTHER sessions' edits; our own flow through the capability.\n if (!('origin' in event) || event.origin.kind !== 'remote') return;\n switch (event.type) {\n case 'annotation.created':\n // A create ships with a freshly baked /AP — fetch it.\n upsert([event.created], true);\n break;\n case 'annotation.updated':\n // The engine's verdict rides the event: preserved moves keep the\n // cached raster, regenerated appearances re-fetch exactly once.\n upsert([event.updated], event.appearance.changed);\n break;\n case 'annotation.moved':\n // A z-order move never touches /AP.\n upsert(event.moved, false);\n break;\n case 'annotation.deleted':\n if (event.deleted) {\n const key = encodeStableIdKey(event.deleted);\n const m = ctx.getState().model;\n if (m.byId[key]) {\n apply({ t: 'remove', ids: [key] });\n } else {\n // Not in the model → possibly a folded ATTACHED-link child a\n // remote session deleted: prune it from its parent's join keys\n // (clearing the parent's link value with the last child).\n for (const id of m.order) {\n const a = m.byId[id];\n const refs = a?.linkRefs;\n if (!a || !refs?.length || !refs.some((r) => refKey(r) === key)) continue;\n const remaining = refs.filter((r) => refKey(r) !== key);\n apply({\n t: 'upsert',\n annots: [\n { ...a, linkRefs: remaining, ...(remaining.length ? {} : { link: null }) },\n ],\n });\n break;\n }\n }\n }\n break;\n default:\n break;\n }\n });\n ctx.cleanup(unsubscribe);\n}\n","import type {\n InteractionCapability,\n InteractionHandler,\n PointerSample,\n} from '@embedpdf/plugin-interaction';\nimport type { PageRotation } from '@embedpdf/core-geometry';\nimport type { Subtype, Vec } from '@embedpdf/core-annotation';\nimport type { AnnotationHostCapability } from './types';\n\nconst MARQUEE_DRAG_THRESHOLD_PX = 4;\nconst isPolyTool = (subtype: Subtype): boolean => subtype === 'polygon' || subtype === 'polyline';\nconst isCalloutTool = (subtype: Subtype): boolean => subtype === 'free-text-callout';\n\n/**\n * Resolve a sample against a gesture's HOME page. Annotation gestures are\n * page-anchored: they track the page they started on, even when the cursor\n * wanders off it — `s.page` re-resolves per event (a page-2 point is a\n * DIFFERENT coordinate frame, the teleport bug), so prefer the source's\n * unclamped projection and fall back to the page hit only when it's the same\n * page. Null → this sample can't speak for the home page; ignore it.\n */\nconst pointOn = (s: PointerSample, pon: number): Vec | null =>\n s.project?.(pon) ?? (s.page?.pon === pon ? s.page.point : null);\n\n/**\n * Click-to-place for every payload-carrying tool (stamp / note / file\n * attachment). Each click places one annotation centred on the point (the\n * tool stays active for repeat placement — v2 rubber-stamp behaviour); the\n * capability's `placeAt` routes by the active tool's kind: armed stamp bytes,\n * a stamp/attachment prompt (pick the spot first, the payload second), or an\n * immediate note. No drag gesture — placement size comes from the content\n * (stamp aspect / the fixed icon box), not the pointer.\n *\n * Priority is deliberately BELOW the edit handler (100): a click over an\n * EXISTING annotation selects it; placement happens on empty page space.\n */\nexport function createPlaceHandler(anno: AnnotationHostCapability): InteractionHandler {\n return {\n id: 'annotation-place',\n // `annotation-stamp` is honoured as a legacy alias for embedder tool\n // configs written before the tags were unified.\n priority: 95,\n enabledFor: (t) => t.enables.has('annotation-place') || t.enables.has('annotation-stamp'),\n onDown: (s) => {\n if (!s.page) return false;\n // The click sample's display rotation drives the tool's `upright` policy —\n // the placement lands reading horizontally on a rotated page/view.\n return anno.placeAt(s.page.pon, s.page.point, s.page.rotation);\n },\n };\n}\n\n/**\n * The armed tool's FOOTPRINT ghost: every hover re-computes the would-be\n * placement under the cursor (stamp image fit / click-create default geometry);\n * off-page clears it. One handler for every tool — `ghostHoverAt` resolves the\n * tool's ghost policy and clears when it isn't `footprint`. Never captures:\n * the highest priority makes its onDown run FIRST on every press (hiding the\n * ghost while a gesture runs), then declines so the real handlers route.\n */\nexport function createGhostHandler(\n anno: AnnotationHostCapability,\n interaction: InteractionCapability,\n): InteractionHandler {\n const hover = (s: PointerSample): void => {\n if (s.page)\n anno.ghostHoverAt(interaction.activeToolId(), s.page.pon, s.page.point, s.page.rotation);\n else anno.clearGhost();\n };\n return {\n id: 'annotation-ghost',\n priority: 1000,\n enabledFor: () => true,\n onDown: () => {\n anno.clearGhost();\n return false;\n },\n onHover: hover,\n };\n}\n\n/**\n * Ambient editing: live under the `annotation-edit` tag, which BOTH the pointer\n * and pan tools enable — so you select/move/resize in any navigation mode (Adobe\n * behaviour). It captures only over an annotation/handle; over empty it\n * deselects and declines (so text-selection / pan still work). Hover drives the\n * cursor (move / pointer / resize) via a priority cursor claim.\n */\nexport function createEditHandler(\n anno: AnnotationHostCapability,\n interaction: InteractionCapability,\n): InteractionHandler {\n // The gesture's home page + last resolved point, armed on down. Every\n // move/up resolves against THIS page (the annotation slides along its edge\n // when the cursor overshoots — the core clamps), never against whatever\n // page the sample happens to hit.\n let origin: { pon: number; point: Vec } | null = null;\n return {\n id: 'annotation-edit',\n priority: 100,\n enabledFor: (t) => t.enables.has('annotation-edit'),\n onDown: (s) => {\n if (!s.page) return false;\n // While a free-text box is being edited it owns its own pointer events, so a\n // down that reaches the hub at all is a click OUTSIDE the editor — commit and\n // leave text edit. This makes exit hub-driven (deterministic) rather than\n // relying on a DOM blur, which races the focus-steal of the entering gesture.\n const wasEditing = anno.currentEditing() != null;\n if (wasEditing) anno.endTextEdit();\n if (\n anno.hitKind(s.page.pon, s.page.point, s.page.scale, s.page.rotation, s.page.zoom) ===\n 'empty'\n ) {\n // Plain empty click drops the selection. Shift-empty preserves it so the\n // lower-priority marquee handler can additive/toggle-select.\n if (!s.modifiers.shift) anno.deselect();\n // A click that dismissed an active edit is CONSUMED: its sole job was to\n // leave edit mode, so the draw tool doesn't also spawn a new annotation\n // (matches v2). Only when nothing was being edited do we decline, letting\n // pan / text-selection / draw act on the empty click.\n return wasEditing;\n }\n // Double-click over a free-text box → enter text edit (not a move).\n if ((s.clickCount ?? 1) >= 2) {\n anno.beginTextEditAt(s.page.pon, s.page.point, s.page.scale, s.page.rotation, s.page.zoom);\n return true;\n }\n anno.editPointer(\n 'down',\n s.page.pon,\n s.page.point,\n s.modifiers.shift,\n s.page.scale,\n s.page.rotation,\n s.page.zoom,\n );\n origin = { pon: s.page.pon, point: s.page.point };\n return true;\n },\n onMove: (s) => {\n if (!origin) return;\n const point = pointOn(s, origin.pon);\n if (!point) return;\n origin.point = point;\n // The sample's scale/rotation ride along (uniform across pages), so a\n // screen-anchored member page-clamps at its effective footprint mid-drag.\n anno.editPointer(\n 'move',\n origin.pon,\n point,\n s.modifiers.shift,\n s.page?.scale,\n s.page?.rotation,\n s.page?.zoom,\n );\n },\n onUp: (s) => {\n if (!origin) return;\n // ALWAYS close the gesture — a release over a page gap or outside the\n // window must still commit (a dangling draft leaves a ghost that snaps\n // back on the next interaction). `editUp` doesn't read the point.\n anno.editPointer('up', origin.pon, pointOn(s, origin.pon) ?? origin.point, false);\n origin = null;\n },\n onHover: (s) => {\n // priority 20 → beats text-select's 'text' (10) over an annotation; null clears.\n interaction.setCursor(\n 'annotation',\n s.page\n ? anno.cursorAt(s.page.pon, s.page.point, s.page.scale, s.page.rotation, s.page.zoom)\n : null,\n 20,\n );\n // Hover state for scene affordances (redact preview) — rides the same\n // per-move sample; the capability dispatches on change only.\n anno.hoverAt(\n s.page\n ? {\n pon: s.page.pon,\n point: s.page.point,\n scale: s.page.scale,\n rotation: s.page.rotation,\n zoom: s.page.zoom,\n }\n : null,\n );\n },\n };\n}\n\n/**\n * Empty-page drag selection. Lower priority than annotation edit and text\n * selection, so it only owns drags that begin on empty, non-text page space.\n */\nexport function createMarqueeHandler(anno: AnnotationHostCapability): InteractionHandler {\n let anchor: {\n pon: number;\n point: Vec;\n vx: number;\n vy: number;\n shift: boolean;\n } | null = null;\n let last: { pon: number; point: Vec } | null = null;\n let dragging = false;\n\n // The marquee page's view env at the down sample — the `up` intersects\n // screen-anchored annotations at their effective footprint with it.\n let view: { scale?: number; rotation?: PageRotation; zoom?: number } = {};\n return {\n id: 'annotation-marquee',\n priority: 50,\n enabledFor: (t) => t.enables.has('annotation-marquee'),\n onDown: (s) => {\n if (!s.page) return false;\n anchor = {\n pon: s.page.pon,\n point: s.page.point,\n vx: s.viewport.x,\n vy: s.viewport.y,\n shift: s.modifiers.shift,\n };\n last = { pon: s.page.pon, point: s.page.point };\n view = { scale: s.page.scale, rotation: s.page.rotation, zoom: s.page.zoom };\n dragging = false;\n return true;\n },\n onMove: (s) => {\n if (!anchor) return;\n // Anchored to the page the drag started on; the projected point keeps the\n // marquee growing along the page edge when the cursor overshoots (the\n // core clamps it to the page box).\n const point = pointOn(s, anchor.pon);\n if (!point) return;\n last = { pon: anchor.pon, point };\n if (!dragging) {\n if (\n Math.hypot(s.viewport.x - anchor.vx, s.viewport.y - anchor.vy) < MARQUEE_DRAG_THRESHOLD_PX\n ) {\n return;\n }\n dragging = true;\n anno.marqueePointer(\n 'down',\n anchor.pon,\n anchor.point,\n anchor.shift,\n view.scale,\n view.rotation,\n view.zoom,\n );\n }\n anno.marqueePointer(\n 'move',\n anchor.pon,\n point,\n anchor.shift,\n view.scale,\n view.rotation,\n view.zoom,\n );\n },\n onUp: () => {\n if (dragging && anchor && last) {\n anno.marqueePointer(\n 'up',\n anchor.pon,\n last.point,\n anchor.shift,\n view.scale,\n view.rotation,\n view.zoom,\n );\n }\n anchor = null;\n last = null;\n dragging = false;\n },\n };\n}\n\n/** Drawing: live under `annotation-draw` (the square / circle / line tools). */\nexport function createDrawHandler(\n anno: AnnotationHostCapability,\n interaction: InteractionCapability,\n): InteractionHandler {\n // The active tool id + its ROUTING subtype. The id is what `createPointer` takes\n // (it resolves the defaults preset — arrow vs line); the subtype is what the\n // poly/callout gesture checks read (arrow → `line`, a polygon preset → `polygon`).\n const toolId = () => interaction.activeToolId();\n const subtypeOf = (id: string): Subtype => anno.toolSubtype(id);\n let drawingPoly = false;\n // A callout is mid-creation between its tip/knee/box clicks; while it is, hover\n // (no button) must still drive the leader/box preview, like a poly's vertices.\n let drawingCallout = false;\n // The active drag's home page (down→up): moves/ups resolve against it, so a\n // shape keeps sizing along the page edge when the cursor overshoots.\n let origin: { pon: number; point: Vec } | null = null;\n let pendingInk: {\n tool: string;\n pon: number;\n timer: ReturnType<typeof setTimeout>;\n } | null = null;\n const flushPendingInk = () => {\n if (!pendingInk) return;\n clearTimeout(pendingInk.timer);\n pendingInk = null;\n anno.finishInkDraft();\n };\n interaction.onToolChange(() => {\n flushPendingInk();\n drawingPoly = false;\n drawingCallout = false;\n origin = null;\n });\n return {\n id: 'annotation-draw',\n // The gesture cascade — relative order IS the composition policy:\n // 100 annotation-edit existing annotations own their gestures\n // 95 annotation-place click-to-place armed tools\n // 60 text-select claims TEXT ONLY (self-gates via isOverText,\n // yields everywhere else)\n // 55 annotation-draw drag-create takes whatever text didn't claim\n // 50 annotation-marquee empty-space selection, the final fallback\n // Draw sits BELOW text-select so a COMPOSED tool (redact: text-select +\n // annotation-draw) lets text claim text — over a paragraph the drag makes\n // per-line quad marks, anywhere else it drag-creates. Priority only breaks\n // ties between simultaneously-ELIGIBLE handlers, and draw-only tools\n // (square/ink/arrow…) never co-enable text-select, so they are unaffected.\n priority: 55,\n enabledFor: (t) => t.enables.has('annotation-draw'),\n onDown: (s) => {\n if (!s.page) return false;\n const tool = toolId();\n const st = subtypeOf(tool);\n if (st === 'ink' && pendingInk) {\n if (pendingInk.tool === tool && pendingInk.pon === s.page.pon) {\n clearTimeout(pendingInk.timer);\n pendingInk = null;\n } else {\n flushPendingInk();\n }\n }\n // A down is a fresh intent — it may legitimately start on another page\n // (the core restarts the draft there), so it re-anchors the gesture.\n origin = { pon: s.page.pon, point: s.page.point };\n if (isPolyTool(st)) {\n const finish = (s.clickCount ?? 1) >= 2;\n anno.createPointer(tool, 'down', s.page.pon, s.page.point, finish);\n drawingPoly = !finish;\n return true;\n }\n drawingPoly = false;\n // Each callout click advances the core's tip → knee → box state machine; the\n // final box click/drag commits and clears the draft (so `drawingCallout`\n // resets on the next tool change or simply idles harmlessly).\n if (isCalloutTool(st)) drawingCallout = true;\n // The DOWN sample's display rotation rides along for the tool's `upright`\n // policy; the core captures it on the draft (later phases don't carry it).\n anno.createPointer(tool, 'down', s.page.pon, s.page.point, false, s.page.rotation);\n return true;\n },\n onMove: (s) => {\n const tool = toolId();\n const st = subtypeOf(tool);\n // Drag-moves (button down): rect/line/ink/free-text size their box, and a\n // callout (a non-poly tool) sizes its text box during the box step. Poly\n // tools take vertices by click, so they ignore drag-moves.\n if (!origin || (isPolyTool(st) && !drawingPoly)) return;\n const point = pointOn(s, origin.pon);\n if (!point) return;\n origin.point = point;\n anno.createPointer(tool, 'move', origin.pon, point);\n },\n onUp: (s) => {\n const tool = toolId();\n const st = subtypeOf(tool);\n if (origin && !isPolyTool(st)) {\n // ALWAYS commit the drag, even released off-page (point pins in core).\n anno.createPointer(tool, 'up', origin.pon, pointOn(s, origin.pon) ?? origin.point);\n if (st === 'ink') {\n const groupStrokesMs = anno.tool(tool)?.ink?.groupStrokesMs ?? 0;\n if (groupStrokesMs > 0) {\n const pon = origin.pon;\n const timer = setTimeout(() => {\n pendingInk = null;\n anno.finishInkDraft();\n }, groupStrokesMs);\n pendingInk = { tool, pon, timer };\n }\n }\n }\n origin = null;\n },\n onHover: (s) => {\n const tool = toolId();\n const st = subtypeOf(tool);\n // Hover preview for the multi-click tools: poly (while placing vertices) and\n // callout (while placing the tip/knee/box) follow the cursor between clicks.\n if (s.page && ((drawingPoly && isPolyTool(st)) || (drawingCallout && isCalloutTool(st)))) {\n anno.createPointer(tool, 'move', s.page.pon, s.page.point);\n }\n },\n };\n}\n","/**\n * Text-selection authoring: the bridge that turns a TEXT SELECTION into markup\n * or a text-edit annotation (Insert / Replace). These tools have no gesture of\n * their own — they enable the selection plugin's `text-select` gesture, and this\n * module consumes its typed signals (`onCommit` to create, `onChange` to preview).\n * Selection is the producer; annotation the consumer — selection never knows\n * annotation exists. Kept out of the plugin-definition file so that stays lean.\n */\nimport type { InteractionCapability } from '@embedpdf/plugin-interaction';\nimport type { SelectionCapability } from '@embedpdf/plugin-selection';\nimport type { AnnotationHostCapability } from './types';\n\n/**\n * Wire the selection → annotation BRIDGE. The markup / caret TOOLS and their\n * defaults are registered by the plugin `init` from the tool registry; this\n * function only consumes the selection plugin's typed signals — so call it from\n * `init` only when a selection plugin is present. Selection is the producer,\n * annotation the consumer; selection never knows annotation exists.\n */\nexport function wireMarkup(\n annotation: AnnotationHostCapability,\n selection: SelectionCapability,\n interaction: InteractionCapability,\n): void {\n // Keep the live preview + the selection's own visual in sync with (active tool,\n // selection). While a markup tool is active the blue highlight is suppressed and\n // the in-progress selection renders as a markup ghost instead.\n const sync = () => {\n const tool = annotation.tool(interaction.activeToolId());\n const authoring = tool?.selection;\n const previewSubtype =\n authoring?.kind === 'markup'\n ? tool!.subtype\n : authoring?.kind === 'text-edit' && authoring.operation === 'replace'\n ? 'strikeout'\n : null;\n selection.setHighlightVisible(previewSubtype == null);\n if (previewSubtype && tool && selection.hasSelection()) {\n const quadsByPage: Record<number, ReturnType<typeof selection.segmentsForPage>[number]['quad'][]> =\n {};\n for (const page of selection.snapshot().pages) {\n quadsByPage[page.pon] = page.segments.map((s) => s.quad);\n }\n annotation.previewMarkup(previewSubtype, quadsByPage, tool.preset);\n } else {\n annotation.clearMarkupPreview();\n }\n };\n selection.onChange(sync); // drag-extend → live preview\n interaction.onToolChange(sync); // entering/leaving a markup tool → restore blue / clear ghost\n\n // On gesture-end, if a markup tool is active, turn the selection into markup.\n selection.onCommit(() => {\n const tool = annotation.tool(interaction.activeToolId());\n const authoring = tool?.selection;\n if (!tool || !authoring) return; // pointer tool → leave the selection (copy)\n const snapshot = selection.snapshot();\n if (authoring.kind === 'text-edit' && authoring.operation === 'insert') {\n if (snapshot.end) {\n annotation.createCaret(snapshot.end.pon, {\n glyphQuad: snapshot.end.glyphQuad,\n advance: snapshot.end.advance,\n });\n }\n selection.clear();\n return;\n }\n if (authoring.kind === 'text-edit' && authoring.operation === 'replace') {\n // `/IRT` relationships are page-local, so a cross-page selection becomes\n // one self-contained Caret + StrikeOut pair per page. The true glyph-cell\n // anchor exists only on the selection's end page; other pages anchor at\n // their last segment's trailing edge.\n for (const page of snapshot.pages) {\n const last = page.segments[page.segments.length - 1];\n if (!last) continue;\n const anchor =\n snapshot.end && snapshot.end.pon === page.pon\n ? { glyphQuad: snapshot.end.glyphQuad, advance: snapshot.end.advance }\n : { glyphQuad: last.quad, advance: last.advance };\n annotation.createReplaceText(\n page.pon,\n page.segments.map((s) => s.quad),\n anchor,\n tool.preset,\n );\n }\n selection.clear();\n return;\n }\n for (const page of snapshot.pages) {\n annotation.createMarkup(\n tool.subtype,\n page.pon,\n page.segments.map((s) => s.quad),\n tool.preset,\n );\n }\n selection.clear(); // fires onChange → preview clears; blue stays suppressed (markup tool still active)\n });\n}\n","import { initialModel } from '@embedpdf/core-annotation';\nimport type {\n AnnotationAction,\n AnnotationConfig,\n AnnotationState,\n ChromeSettings,\n ChromeSettingsPatch,\n} from './types';\n\n/**\n * Out-of-the-box selection chrome — a sensible document-annotation feel. Every\n * length is CSS px (screen-constant across zoom); every color falls back to\n * `accent`. Just defaults: override any field at registration\n * (`annotationPlugin({ chrome })`) or at runtime (`setChrome`).\n */\nexport const DEFAULT_CHROME: ChromeSettings = {\n accent: '#3858e9',\n // Solid, like the shape's own resting look — one style at rest AND rotated.\n outline: { style: 'solid', width: 1 },\n // 8px squares to look at, 24px to grab (touch-friendly without visual bulk).\n handles: { size: 8, hitSize: 24, fill: '#ffffff' },\n knob: { size: 10, hitSize: 24, offset: 32, stalk: true, fill: '#ffffff' },\n // Faint reference cross, prominent live indicator (v2 feel).\n guides: { enabled: true, style: 'solid', width: 1, axisOpacity: 0.35, indicatorOpacity: 0.8 },\n};\n\n/** Deep-partial merge of a chrome patch — one level per piece, like the\n * stage-settings convention (align pairs / pageFrame). */\nexport const mergeChrome = (base: ChromeSettings, patch: ChromeSettingsPatch): ChromeSettings => ({\n accent: patch.accent ?? base.accent,\n outline: { ...base.outline, ...patch.outline },\n handles: { ...base.handles, ...patch.handles },\n knob: { ...base.knob, ...patch.knob },\n guides: { ...base.guides, ...patch.guides },\n});\n\n/**\n * The slice holds the annotation-core Model + the selection-chrome settings.\n * The pure `update` runs in the capability (the shell, which also performs\n * effects); the reducer only stores new state — keeping the kernel store a\n * dumb, serializable container. The registration config seeds the model's\n * snap settings and the chrome.\n */\nexport const initialAnnotationState = (config: AnnotationConfig = {}): AnnotationState => ({\n model: { ...initialModel, snap: { ...initialModel.snap, ...config.snap } },\n chrome: mergeChrome(DEFAULT_CHROME, config.chrome ?? {}),\n toolGhost: null,\n stampArmEpoch: 0,\n});\n\nexport const annotationReducer = (\n state: AnnotationState,\n action: AnnotationAction,\n): AnnotationState => {\n switch (action.type) {\n case 'SET_MODEL':\n return { ...state, model: action.model };\n case 'SET_CHROME':\n return { ...state, chrome: mergeChrome(state.chrome, action.patch) };\n case 'SET_TOOL_GHOST':\n return { ...state, toolGhost: action.ghost };\n case 'STAMP_ARM_CHANGED':\n // A new (or dropped) payload invalidates any ghost drawn for the old one.\n return { ...state, stampArmEpoch: state.stampArmEpoch + 1, toolGhost: null };\n default:\n return state;\n }\n};\n","import { definePlugin } from '@embedpdf/core';\nimport { InteractionToken } from '@embedpdf/plugin-interaction';\nimport { SelectionToken } from '@embedpdf/plugin-selection';\nimport { createAnnotationCapability } from './capability';\nimport { registerAnnotationEffects } from './effects';\nimport {\n createDrawHandler,\n createEditHandler,\n createGhostHandler,\n createMarqueeHandler,\n createPlaceHandler,\n} from './handler';\nimport { wireMarkup } from './markup';\nimport { annotationReducer, initialAnnotationState } from './reducer';\nimport { AnnotationToken } from './types';\nimport type {\n AnnotationAction,\n AnnotationConfig,\n AnnotationHostCapability,\n AnnotationState,\n} from './types';\n\n/**\n * The annotation plugin. Document-scoped; requires the interaction hub and\n * OPTIONALLY uses the selection plugin. Shapes/ink work with no selection; text\n * markup lights up only when a selection plugin is present.\n */\nexport const annotationPlugin = (config: AnnotationConfig = {}) =>\n definePlugin<AnnotationState, AnnotationAction, AnnotationHostCapability>({\n id: 'annotation',\n token: AnnotationToken,\n scope: 'document',\n requires: [InteractionToken],\n optional: [SelectionToken],\n initialState: () => initialAnnotationState(config),\n reduce: annotationReducer,\n // The capability owns the resolved tool registry (built-ins + config `tools`),\n // so it needs the config too — not just the reducer state.\n capability: (ctx) => createAnnotationCapability(ctx, config),\n // Fold in remote collaborators' edits (own edits flow through the capability).\n effects: registerAnnotationEffects,\n init: (ctx) => {\n const interaction = ctx.get(InteractionToken);\n const annotation = ctx.get(AnnotationToken);\n const selection = ctx.tryGet(SelectionToken);\n\n // Register every resolved tool (shapes, lines, ink, free-text, markup, stamp,\n // plus anything the embedder added via config `tools`) and seed its defaults.\n // A tool is a named preset over a subtype — see `tools.ts`. Markup / caret\n // tools ride the selection plugin's `text-select` gesture, so they stay\n // inert (skipped) when no selection plugin is installed.\n for (const tool of annotation.tools()) {\n if (tool.enables.has('text-select') && !selection) continue;\n interaction.registerTool({ id: tool.id, cursor: tool.cursor, enables: tool.enables });\n if (tool.defaults) annotation.setDefaults(tool.preset, tool.defaults);\n }\n\n interaction.registerHandler(createPlaceHandler(annotation));\n interaction.registerHandler(createGhostHandler(annotation, interaction));\n interaction.registerHandler(createEditHandler(annotation, interaction));\n interaction.registerHandler(createMarqueeHandler(annotation));\n interaction.registerHandler(createDrawHandler(annotation, interaction));\n interaction.onToolChange(() => {\n annotation.cancel();\n annotation.clearGhost(); // a footprint belongs to the tool that computed it\n // Engagement follows the tool: annotations whose Behavior just engaged\n // (form widgets under a fill tool) drop out of the selection — no\n // stranded chrome on a fill control.\n annotation.pruneEngagedSelection();\n // Leaving the stamp family drops any armed payload — bytes are tool state,\n // not document state (any stamp tool keeps it; a non-stamp tool clears it).\n if (!interaction.activeTool().enables.has('annotation-stamp')) annotation.disarmStamp();\n });\n\n // Markup is opt-in: wire the selection→annotation BRIDGE only when a\n // selection plugin is present (the markup TOOLS were registered above).\n if (selection) wireMarkup(annotation, selection, interaction);\n },\n });\n","/**\n * @embedpdf/plugin-annotation — annotations on the v3 stack.\n *\n * The pure @embedpdf/core-annotation wired to the engine repository (optimistic\n * create/patch/delete) and the interaction hub (ambient editing + draw tools).\n * Behaviors (forms, links) plug in via registerBehavior. Zero framework code.\n */\nimport type { CapabilityToken } from '@embedpdf/core';\nimport { AnnotationToken as AnnotationHostToken } from './types';\nimport type { AnnotationCapability } from './types';\n\nexport { annotationPlugin } from './annotation.plugin';\nexport {\n fromDTO,\n toCreateDraft,\n toPatch,\n refKey,\n styleFromDTO,\n widgetAppearanceFromProps,\n} from './repository';\n// The shared placement layer + the one click↔drag threshold, re-exported so a\n// sibling COMMIT PLANE (the form plugin's place handler) resolves clicks with\n// the exact call the annotation core and the footprint ghost use.\nexport { MIN_DRAG, resolveClickPlacement, type ClickPlacement } from '@embedpdf/core-annotation';\nexport { DEFAULT_CHROME } from './reducer';\nexport { DEFAULT_TOOLS } from './tools';\nexport type {\n AnnotationToolDef,\n AnnotationToolInput,\n GhostPolicy,\n InkAuthoringOptions,\n PromptSourceSpec,\n ResolvedTool,\n SelectionAuthoring,\n StampSourceSpec,\n ToolAuthoringKind,\n ToolDefaultsFor,\n} from './tools';\nexport type {\n AnnotationCapability,\n AnnotationConfig,\n AnnotationState,\n AnnotationAction,\n Behavior,\n ChromeSettings,\n ChromeSettingsPatch,\n LinkNavItem,\n SelectionFlags,\n SelectionProps,\n StampToolInput,\n FilePickerProvider,\n FilePromptRequest,\n TextItem,\n ToolGhost,\n} from './types';\n// The property vocabulary + schema (defined in the portable core; re-exported so\n// app code building property UIs needs only this package).\nexport { propsFor } from '@embedpdf/core-annotation';\nexport type {\n AnnotationFlags,\n AnnotationProps,\n AnnotationPropsPatch,\n BlendMode,\n Border,\n ClickCreate,\n LineEnding,\n LineEndings,\n PropKey,\n PropSpec,\n SnapSettings,\n TextAlign,\n} from '@embedpdf/core-annotation';\n\n/**\n * App-facing annotation token: resolves the public {@link AnnotationCapability}.\n * It is the SAME runtime token the plugin provides, narrowed to the public lens —\n * the framework-only surface (render projection, pointer gestures, behavior\n * registration) is reachable only via `@embedpdf/plugin-annotation/internal`.\n */\nexport const AnnotationToken =\n AnnotationHostToken as unknown as CapabilityToken<AnnotationCapability>;\n"],"mappings":"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"}
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as PromptSourceSpec, D as ToolAuthoringKind, E as StampSourceSpec, O as ToolDefaultsFor, S as InkAuthoringOptions, T as SelectionAuthoring, _ as ToolGhost, a as AnnotationState, b as DEFAULT_TOOLS, c as ChromeSettings, d as FilePromptRequest, f as LinkNavItem, g as TextItem, h as StampToolInput, i as AnnotationHostCapability, l as ChromeSettingsPatch, m as SelectionProps, n as AnnotationCapability, p as SelectionFlags, r as AnnotationConfig, s as Behavior, t as AnnotationAction, u as FilePickerProvider, v as AnnotationToolDef, w as ResolvedTool, x as GhostPolicy, y as AnnotationToolInput } from "./types-BhWgqgbX.cjs";
1
+ import { C as PromptSourceSpec, D as ToolAuthoringKind, E as StampSourceSpec, O as ToolDefaultsFor, S as InkAuthoringOptions, T as SelectionAuthoring, _ as ToolGhost, a as AnnotationState, b as DEFAULT_TOOLS, c as ChromeSettings, d as FilePromptRequest, f as LinkNavItem, g as TextItem, h as StampToolInput, i as AnnotationHostCapability, l as ChromeSettingsPatch, m as SelectionProps, n as AnnotationCapability, p as SelectionFlags, r as AnnotationConfig, s as Behavior, t as AnnotationAction, u as FilePickerProvider, v as AnnotationToolDef, w as ResolvedTool, x as GhostPolicy, y as AnnotationToolInput } from "./types-CfewRmXH.cjs";
2
2
  import { CapabilityToken } from "@embedpdf/core";
3
3
  import { AnnotationDTO, AnnotationDraft, AnnotationPatch, AnnotationRef, PdfLinkTarget, PdfRect, WidgetAppearance } from "@embedpdf/engine-core/runtime";
4
4
  import { Annot, AnnotationFlags, AnnotationProps, AnnotationPropsPatch, AnnotationPropsPatch as AnnotationPropsPatch$1, BlendMode, Border, ClickCreate, ClickPlacement, Geom, LineEnding, LineEndings, MIN_DRAG, PropKey, PropKey as PropKey$1, PropSpec, Rect, SnapSettings, Style, TextAlign, propsFor, resolveClickPlacement } from "@embedpdf/core-annotation";
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { C as PromptSourceSpec, D as ToolAuthoringKind, E as StampSourceSpec, O as ToolDefaultsFor, S as InkAuthoringOptions, T as SelectionAuthoring, _ as ToolGhost, a as AnnotationState, b as DEFAULT_TOOLS, c as ChromeSettings, d as FilePromptRequest, f as LinkNavItem, g as TextItem, h as StampToolInput, i as AnnotationHostCapability, l as ChromeSettingsPatch, m as SelectionProps, n as AnnotationCapability, p as SelectionFlags, r as AnnotationConfig, s as Behavior, t as AnnotationAction, u as FilePickerProvider, v as AnnotationToolDef, w as ResolvedTool, x as GhostPolicy, y as AnnotationToolInput } from "./types-Da6JRmhw.js";
1
+ import { C as PromptSourceSpec, D as ToolAuthoringKind, E as StampSourceSpec, O as ToolDefaultsFor, S as InkAuthoringOptions, T as SelectionAuthoring, _ as ToolGhost, a as AnnotationState, b as DEFAULT_TOOLS, c as ChromeSettings, d as FilePromptRequest, f as LinkNavItem, g as TextItem, h as StampToolInput, i as AnnotationHostCapability, l as ChromeSettingsPatch, m as SelectionProps, n as AnnotationCapability, p as SelectionFlags, r as AnnotationConfig, s as Behavior, t as AnnotationAction, u as FilePickerProvider, v as AnnotationToolDef, w as ResolvedTool, x as GhostPolicy, y as AnnotationToolInput } from "./types-DZ29RSW9.js";
2
2
  import { CapabilityToken } from "@embedpdf/core";
3
3
  import { AnnotationDTO, AnnotationDraft, AnnotationPatch, AnnotationRef, PdfLinkTarget, PdfRect, WidgetAppearance } from "@embedpdf/engine-core/runtime";
4
4
  import { Annot, AnnotationFlags, AnnotationProps, AnnotationPropsPatch, AnnotationPropsPatch as AnnotationPropsPatch$1, BlendMode, Border, ClickCreate, ClickPlacement, Geom, LineEnding, LineEndings, MIN_DRAG, PropKey, PropKey as PropKey$1, PropSpec, Rect, SnapSettings, Style, TextAlign, propsFor, resolveClickPlacement } from "@embedpdf/core-annotation";
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { a as toCreateDraft, c as styleFromDTO, i as fromDTO, l as widgetAppearanceFromProps, n as DEFAULT_TOOLS, o as toPatch, s as refKey, t as createAnnotationCapability, u as AnnotationToken$1 } from "./capability-fghl0JmE.js";
1
+ import { a as toCreateDraft, c as styleFromDTO, i as fromDTO, l as widgetAppearanceFromProps, n as DEFAULT_TOOLS, o as toPatch, s as refKey, t as createAnnotationCapability, u as AnnotationToken$1 } from "./capability-DjUdnoYn.js";
2
2
  import { definePlugin } from "@embedpdf/core";
3
3
  import { InteractionToken } from "@embedpdf/plugin-interaction";
4
4
  import { SelectionToken } from "@embedpdf/plugin-selection";
@@ -381,9 +381,9 @@ function wireMarkup(annotation, selection, interaction) {
381
381
  const previewSubtype = authoring?.kind === "markup" ? tool.subtype : authoring?.kind === "text-edit" && authoring.operation === "replace" ? "strikeout" : null;
382
382
  selection.setHighlightVisible(previewSubtype == null);
383
383
  if (previewSubtype && tool && selection.hasSelection()) {
384
- const rectsByPage = {};
385
- for (const page of selection.snapshot().pages) rectsByPage[page.pon] = page.rects;
386
- annotation.previewMarkup(previewSubtype, rectsByPage, tool.preset);
384
+ const quadsByPage = {};
385
+ for (const page of selection.snapshot().pages) quadsByPage[page.pon] = page.segments.map((s) => s.quad);
386
+ annotation.previewMarkup(previewSubtype, quadsByPage, tool.preset);
387
387
  } else annotation.clearMarkupPreview();
388
388
  };
389
389
  selection.onChange(sync);
@@ -394,19 +394,30 @@ function wireMarkup(annotation, selection, interaction) {
394
394
  if (!tool || !authoring) return;
395
395
  const snapshot = selection.snapshot();
396
396
  if (authoring.kind === "text-edit" && authoring.operation === "insert") {
397
- if (snapshot.end) annotation.createCaret(snapshot.end.pon, snapshot.end.rect);
397
+ if (snapshot.end) annotation.createCaret(snapshot.end.pon, {
398
+ glyphQuad: snapshot.end.glyphQuad,
399
+ advance: snapshot.end.advance
400
+ });
398
401
  selection.clear();
399
402
  return;
400
403
  }
401
404
  if (authoring.kind === "text-edit" && authoring.operation === "replace") {
402
405
  for (const page of snapshot.pages) {
403
- const endRect = page.rects[page.rects.length - 1];
404
- if (endRect) annotation.createReplaceText(page.pon, page.rects, endRect, tool.preset);
406
+ const last = page.segments[page.segments.length - 1];
407
+ if (!last) continue;
408
+ const anchor = snapshot.end && snapshot.end.pon === page.pon ? {
409
+ glyphQuad: snapshot.end.glyphQuad,
410
+ advance: snapshot.end.advance
411
+ } : {
412
+ glyphQuad: last.quad,
413
+ advance: last.advance
414
+ };
415
+ annotation.createReplaceText(page.pon, page.segments.map((s) => s.quad), anchor, tool.preset);
405
416
  }
406
417
  selection.clear();
407
418
  return;
408
419
  }
409
- for (const page of snapshot.pages) annotation.createMarkup(tool.subtype, page.pon, page.rects, tool.preset);
420
+ for (const page of snapshot.pages) annotation.createMarkup(tool.subtype, page.pon, page.segments.map((s) => s.quad), tool.preset);
410
421
  selection.clear();
411
422
  });
412
423
  }
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["propsFor","AnnotationToken","AnnotationToken","AnnotationHostToken"],"sources":["../src/effects.ts","../src/handler.ts","../src/markup.ts","../src/reducer.ts","../src/annotation.plugin.ts","../src/index.ts"],"sourcesContent":["/**\n * Makes annotations REACTIVE to remote collaborators — the same pattern the\n * metadata plugin uses for the Info dict.\n *\n * Own edits already update the model through the capability's promise path\n * (optimistic draw + re-sync from the engine DTO). This effect folds in the\n * edits that DIDN'T originate here: it subscribes to the document event stream\n * and applies `annotation.*` events whose `origin.kind === 'remote'` — another\n * session's create/update/move/delete, delivered over SSE on cloud. Filtering\n * to remote avoids double-applying (and racing the temp-id reconcile of) our\n * own echoes.\n */\nimport type { DocumentEvent, EffectContext } from '@embedpdf/core';\nimport { encodeStableIdKey } from '@embedpdf/engine-core/runtime';\nimport { propsFor, update, type Annot, type Msg } from '@embedpdf/core-annotation';\n\nimport { fromDTO, refKey } from './repository';\nimport { AnnotationToken } from './types';\nimport type { AnnotationAction, AnnotationState } from './types';\n\nexport function registerAnnotationEffects(\n ctx: EffectContext<AnnotationState, AnnotationAction>,\n): void {\n const doc = ctx.doc;\n if (!doc) return;\n\n const cropOf = (pon: number) =>\n ctx.document()?.pages.find((p) => p.pageObjectNumber === pon)?.boxes.crop ?? null;\n\n const apply = (msg: Msg): void => {\n const [next] = update(ctx.getState().model, msg);\n ctx.dispatch({ type: 'SET_MODEL', model: next });\n };\n\n const upsert = (dtos: ReadonlyArray<Parameters<typeof fromDTO>[0]>, bumpAp: boolean): void => {\n // `bump` re-fetches rasters, `keep` doesn't. The split is driven by the\n // ENGINE'S appearance echo riding the event (`appearance.changed`) — the\n // same verdict local edits use — so a remote MOVE costs peers zero\n // appearance re-renders while a remote restyle refreshes exactly once.\n const bump: Annot[] = [];\n const keep: Annot[] = [];\n for (const dto of dtos) {\n const crop = cropOf(dto.pageObjectNumber);\n if (!crop) continue;\n // Another session authored this — trust the engine's baked AP.\n const a = fromDTO(dto, crop, 'baked');\n // A remote ATTACHED-link child (grouped /Link under a linkable local\n // parent) folds onto the parent instead of entering the model — the\n // same rule the page-load fold applies (see foldAttachedLinks). A child\n // retarget never repaints the PARENT, so the fold never bumps.\n if (a.subtype === 'link' && a.data?.subtype === 'link' && a.data.replyType === 'group') {\n const parentId = a.data.inReplyTo ? refKey(a.data.inReplyTo) : null;\n const parent = parentId ? ctx.getState().model.byId[parentId] : null;\n if (\n parent &&\n parent.subtype !== 'link' &&\n propsFor(parent.subtype).some((s) => s.key === 'link') &&\n a.ref\n ) {\n const refs = parent.linkRefs ?? [];\n const known = refs.some((r) => refKey(r) === a.id);\n keep.push({\n ...parent,\n link: a.data.target,\n linkRefs: known ? refs : [...refs, a.ref],\n });\n continue;\n }\n }\n (bumpAp ? bump : keep).push(a);\n }\n if (bump.length) apply({ t: 'upsert', annots: bump, bumpAp: true });\n if (keep.length) apply({ t: 'upsert', annots: keep });\n };\n\n const unsubscribe = doc.events.subscribe((event: DocumentEvent) => {\n // Widget appearances are re-baked by FORM value writes — a plane this\n // plugin doesn't own, so no remote-only filter: direct fills emit\n // `form.valueChanged`, while scripted batches emit `form.effectsApplied`.\n // Neither path touches this model directly. The bump tells the render\n // layer to re-fetch exactly the repainted widgets' rasters.\n if (event.type === 'form.valueChanged' || event.type === 'form.effectsApplied') {\n apply({\n t: 'bumpAp',\n ids: event.changedWidgets\n .filter((w) => w.annotObjectNumber > 0)\n .map((w) => encodeStableIdKey({ kind: 'objectNumber', value: w.annotObjectNumber })),\n });\n return;\n }\n // A redaction apply deleted the consumed marks plus every intersecting\n // annotation on the applied pages. ORIGIN-AGNOSTIC: our own applies run\n // through the redaction plugin's doc-level verb, never this plane's\n // capability paths, so the model is stale either way — reload the\n // affected pages from the engine. (Works without plugin-redaction\n // installed: a remote collaborator's apply still reconciles this view.)\n if (event.type === 'redaction.applied') {\n const affected = new Set(\n event.results.filter((r) => r.status === 'applied').map((r) => r.pageObjectNumber),\n );\n if (affected.size) {\n const host = ctx.get(AnnotationToken);\n for (const pon of affected) void host.reloadPage(pon);\n }\n return;\n }\n // Only fold in OTHER sessions' edits; our own flow through the capability.\n if (!('origin' in event) || event.origin.kind !== 'remote') return;\n switch (event.type) {\n case 'annotation.created':\n // A create ships with a freshly baked /AP — fetch it.\n upsert([event.created], true);\n break;\n case 'annotation.updated':\n // The engine's verdict rides the event: preserved moves keep the\n // cached raster, regenerated appearances re-fetch exactly once.\n upsert([event.updated], event.appearance.changed);\n break;\n case 'annotation.moved':\n // A z-order move never touches /AP.\n upsert(event.moved, false);\n break;\n case 'annotation.deleted':\n if (event.deleted) {\n const key = encodeStableIdKey(event.deleted);\n const m = ctx.getState().model;\n if (m.byId[key]) {\n apply({ t: 'remove', ids: [key] });\n } else {\n // Not in the model → possibly a folded ATTACHED-link child a\n // remote session deleted: prune it from its parent's join keys\n // (clearing the parent's link value with the last child).\n for (const id of m.order) {\n const a = m.byId[id];\n const refs = a?.linkRefs;\n if (!a || !refs?.length || !refs.some((r) => refKey(r) === key)) continue;\n const remaining = refs.filter((r) => refKey(r) !== key);\n apply({\n t: 'upsert',\n annots: [\n { ...a, linkRefs: remaining, ...(remaining.length ? {} : { link: null }) },\n ],\n });\n break;\n }\n }\n }\n break;\n default:\n break;\n }\n });\n ctx.cleanup(unsubscribe);\n}\n","import type {\n InteractionCapability,\n InteractionHandler,\n PointerSample,\n} from '@embedpdf/plugin-interaction';\nimport type { PageRotation } from '@embedpdf/core-geometry';\nimport type { Subtype, Vec } from '@embedpdf/core-annotation';\nimport type { AnnotationHostCapability } from './types';\n\nconst MARQUEE_DRAG_THRESHOLD_PX = 4;\nconst isPolyTool = (subtype: Subtype): boolean => subtype === 'polygon' || subtype === 'polyline';\nconst isCalloutTool = (subtype: Subtype): boolean => subtype === 'free-text-callout';\n\n/**\n * Resolve a sample against a gesture's HOME page. Annotation gestures are\n * page-anchored: they track the page they started on, even when the cursor\n * wanders off it — `s.page` re-resolves per event (a page-2 point is a\n * DIFFERENT coordinate frame, the teleport bug), so prefer the source's\n * unclamped projection and fall back to the page hit only when it's the same\n * page. Null → this sample can't speak for the home page; ignore it.\n */\nconst pointOn = (s: PointerSample, pon: number): Vec | null =>\n s.project?.(pon) ?? (s.page?.pon === pon ? s.page.point : null);\n\n/**\n * Click-to-place for every payload-carrying tool (stamp / note / file\n * attachment). Each click places one annotation centred on the point (the\n * tool stays active for repeat placement — v2 rubber-stamp behaviour); the\n * capability's `placeAt` routes by the active tool's kind: armed stamp bytes,\n * a stamp/attachment prompt (pick the spot first, the payload second), or an\n * immediate note. No drag gesture — placement size comes from the content\n * (stamp aspect / the fixed icon box), not the pointer.\n *\n * Priority is deliberately BELOW the edit handler (100): a click over an\n * EXISTING annotation selects it; placement happens on empty page space.\n */\nexport function createPlaceHandler(anno: AnnotationHostCapability): InteractionHandler {\n return {\n id: 'annotation-place',\n // `annotation-stamp` is honoured as a legacy alias for embedder tool\n // configs written before the tags were unified.\n priority: 95,\n enabledFor: (t) => t.enables.has('annotation-place') || t.enables.has('annotation-stamp'),\n onDown: (s) => {\n if (!s.page) return false;\n // The click sample's display rotation drives the tool's `upright` policy —\n // the placement lands reading horizontally on a rotated page/view.\n return anno.placeAt(s.page.pon, s.page.point, s.page.rotation);\n },\n };\n}\n\n/**\n * The armed tool's FOOTPRINT ghost: every hover re-computes the would-be\n * placement under the cursor (stamp image fit / click-create default geometry);\n * off-page clears it. One handler for every tool — `ghostHoverAt` resolves the\n * tool's ghost policy and clears when it isn't `footprint`. Never captures:\n * the highest priority makes its onDown run FIRST on every press (hiding the\n * ghost while a gesture runs), then declines so the real handlers route.\n */\nexport function createGhostHandler(\n anno: AnnotationHostCapability,\n interaction: InteractionCapability,\n): InteractionHandler {\n const hover = (s: PointerSample): void => {\n if (s.page)\n anno.ghostHoverAt(interaction.activeToolId(), s.page.pon, s.page.point, s.page.rotation);\n else anno.clearGhost();\n };\n return {\n id: 'annotation-ghost',\n priority: 1000,\n enabledFor: () => true,\n onDown: () => {\n anno.clearGhost();\n return false;\n },\n onHover: hover,\n };\n}\n\n/**\n * Ambient editing: live under the `annotation-edit` tag, which BOTH the pointer\n * and pan tools enable — so you select/move/resize in any navigation mode (Adobe\n * behaviour). It captures only over an annotation/handle; over empty it\n * deselects and declines (so text-selection / pan still work). Hover drives the\n * cursor (move / pointer / resize) via a priority cursor claim.\n */\nexport function createEditHandler(\n anno: AnnotationHostCapability,\n interaction: InteractionCapability,\n): InteractionHandler {\n // The gesture's home page + last resolved point, armed on down. Every\n // move/up resolves against THIS page (the annotation slides along its edge\n // when the cursor overshoots — the core clamps), never against whatever\n // page the sample happens to hit.\n let origin: { pon: number; point: Vec } | null = null;\n return {\n id: 'annotation-edit',\n priority: 100,\n enabledFor: (t) => t.enables.has('annotation-edit'),\n onDown: (s) => {\n if (!s.page) return false;\n // While a free-text box is being edited it owns its own pointer events, so a\n // down that reaches the hub at all is a click OUTSIDE the editor — commit and\n // leave text edit. This makes exit hub-driven (deterministic) rather than\n // relying on a DOM blur, which races the focus-steal of the entering gesture.\n const wasEditing = anno.currentEditing() != null;\n if (wasEditing) anno.endTextEdit();\n if (\n anno.hitKind(s.page.pon, s.page.point, s.page.scale, s.page.rotation, s.page.zoom) ===\n 'empty'\n ) {\n // Plain empty click drops the selection. Shift-empty preserves it so the\n // lower-priority marquee handler can additive/toggle-select.\n if (!s.modifiers.shift) anno.deselect();\n // A click that dismissed an active edit is CONSUMED: its sole job was to\n // leave edit mode, so the draw tool doesn't also spawn a new annotation\n // (matches v2). Only when nothing was being edited do we decline, letting\n // pan / text-selection / draw act on the empty click.\n return wasEditing;\n }\n // Double-click over a free-text box → enter text edit (not a move).\n if ((s.clickCount ?? 1) >= 2) {\n anno.beginTextEditAt(s.page.pon, s.page.point, s.page.scale, s.page.rotation, s.page.zoom);\n return true;\n }\n anno.editPointer(\n 'down',\n s.page.pon,\n s.page.point,\n s.modifiers.shift,\n s.page.scale,\n s.page.rotation,\n s.page.zoom,\n );\n origin = { pon: s.page.pon, point: s.page.point };\n return true;\n },\n onMove: (s) => {\n if (!origin) return;\n const point = pointOn(s, origin.pon);\n if (!point) return;\n origin.point = point;\n // The sample's scale/rotation ride along (uniform across pages), so a\n // screen-anchored member page-clamps at its effective footprint mid-drag.\n anno.editPointer(\n 'move',\n origin.pon,\n point,\n s.modifiers.shift,\n s.page?.scale,\n s.page?.rotation,\n s.page?.zoom,\n );\n },\n onUp: (s) => {\n if (!origin) return;\n // ALWAYS close the gesture — a release over a page gap or outside the\n // window must still commit (a dangling draft leaves a ghost that snaps\n // back on the next interaction). `editUp` doesn't read the point.\n anno.editPointer('up', origin.pon, pointOn(s, origin.pon) ?? origin.point, false);\n origin = null;\n },\n onHover: (s) => {\n // priority 20 → beats text-select's 'text' (10) over an annotation; null clears.\n interaction.setCursor(\n 'annotation',\n s.page\n ? anno.cursorAt(s.page.pon, s.page.point, s.page.scale, s.page.rotation, s.page.zoom)\n : null,\n 20,\n );\n // Hover state for scene affordances (redact preview) — rides the same\n // per-move sample; the capability dispatches on change only.\n anno.hoverAt(\n s.page\n ? {\n pon: s.page.pon,\n point: s.page.point,\n scale: s.page.scale,\n rotation: s.page.rotation,\n zoom: s.page.zoom,\n }\n : null,\n );\n },\n };\n}\n\n/**\n * Empty-page drag selection. Lower priority than annotation edit and text\n * selection, so it only owns drags that begin on empty, non-text page space.\n */\nexport function createMarqueeHandler(anno: AnnotationHostCapability): InteractionHandler {\n let anchor: {\n pon: number;\n point: Vec;\n vx: number;\n vy: number;\n shift: boolean;\n } | null = null;\n let last: { pon: number; point: Vec } | null = null;\n let dragging = false;\n\n // The marquee page's view env at the down sample — the `up` intersects\n // screen-anchored annotations at their effective footprint with it.\n let view: { scale?: number; rotation?: PageRotation; zoom?: number } = {};\n return {\n id: 'annotation-marquee',\n priority: 50,\n enabledFor: (t) => t.enables.has('annotation-marquee'),\n onDown: (s) => {\n if (!s.page) return false;\n anchor = {\n pon: s.page.pon,\n point: s.page.point,\n vx: s.viewport.x,\n vy: s.viewport.y,\n shift: s.modifiers.shift,\n };\n last = { pon: s.page.pon, point: s.page.point };\n view = { scale: s.page.scale, rotation: s.page.rotation, zoom: s.page.zoom };\n dragging = false;\n return true;\n },\n onMove: (s) => {\n if (!anchor) return;\n // Anchored to the page the drag started on; the projected point keeps the\n // marquee growing along the page edge when the cursor overshoots (the\n // core clamps it to the page box).\n const point = pointOn(s, anchor.pon);\n if (!point) return;\n last = { pon: anchor.pon, point };\n if (!dragging) {\n if (\n Math.hypot(s.viewport.x - anchor.vx, s.viewport.y - anchor.vy) < MARQUEE_DRAG_THRESHOLD_PX\n ) {\n return;\n }\n dragging = true;\n anno.marqueePointer(\n 'down',\n anchor.pon,\n anchor.point,\n anchor.shift,\n view.scale,\n view.rotation,\n view.zoom,\n );\n }\n anno.marqueePointer(\n 'move',\n anchor.pon,\n point,\n anchor.shift,\n view.scale,\n view.rotation,\n view.zoom,\n );\n },\n onUp: () => {\n if (dragging && anchor && last) {\n anno.marqueePointer(\n 'up',\n anchor.pon,\n last.point,\n anchor.shift,\n view.scale,\n view.rotation,\n view.zoom,\n );\n }\n anchor = null;\n last = null;\n dragging = false;\n },\n };\n}\n\n/** Drawing: live under `annotation-draw` (the square / circle / line tools). */\nexport function createDrawHandler(\n anno: AnnotationHostCapability,\n interaction: InteractionCapability,\n): InteractionHandler {\n // The active tool id + its ROUTING subtype. The id is what `createPointer` takes\n // (it resolves the defaults preset — arrow vs line); the subtype is what the\n // poly/callout gesture checks read (arrow → `line`, a polygon preset → `polygon`).\n const toolId = () => interaction.activeToolId();\n const subtypeOf = (id: string): Subtype => anno.toolSubtype(id);\n let drawingPoly = false;\n // A callout is mid-creation between its tip/knee/box clicks; while it is, hover\n // (no button) must still drive the leader/box preview, like a poly's vertices.\n let drawingCallout = false;\n // The active drag's home page (down→up): moves/ups resolve against it, so a\n // shape keeps sizing along the page edge when the cursor overshoots.\n let origin: { pon: number; point: Vec } | null = null;\n let pendingInk: {\n tool: string;\n pon: number;\n timer: ReturnType<typeof setTimeout>;\n } | null = null;\n const flushPendingInk = () => {\n if (!pendingInk) return;\n clearTimeout(pendingInk.timer);\n pendingInk = null;\n anno.finishInkDraft();\n };\n interaction.onToolChange(() => {\n flushPendingInk();\n drawingPoly = false;\n drawingCallout = false;\n origin = null;\n });\n return {\n id: 'annotation-draw',\n // The gesture cascade — relative order IS the composition policy:\n // 100 annotation-edit existing annotations own their gestures\n // 95 annotation-place click-to-place armed tools\n // 60 text-select claims TEXT ONLY (self-gates via isOverText,\n // yields everywhere else)\n // 55 annotation-draw drag-create takes whatever text didn't claim\n // 50 annotation-marquee empty-space selection, the final fallback\n // Draw sits BELOW text-select so a COMPOSED tool (redact: text-select +\n // annotation-draw) lets text claim text — over a paragraph the drag makes\n // per-line quad marks, anywhere else it drag-creates. Priority only breaks\n // ties between simultaneously-ELIGIBLE handlers, and draw-only tools\n // (square/ink/arrow…) never co-enable text-select, so they are unaffected.\n priority: 55,\n enabledFor: (t) => t.enables.has('annotation-draw'),\n onDown: (s) => {\n if (!s.page) return false;\n const tool = toolId();\n const st = subtypeOf(tool);\n if (st === 'ink' && pendingInk) {\n if (pendingInk.tool === tool && pendingInk.pon === s.page.pon) {\n clearTimeout(pendingInk.timer);\n pendingInk = null;\n } else {\n flushPendingInk();\n }\n }\n // A down is a fresh intent — it may legitimately start on another page\n // (the core restarts the draft there), so it re-anchors the gesture.\n origin = { pon: s.page.pon, point: s.page.point };\n if (isPolyTool(st)) {\n const finish = (s.clickCount ?? 1) >= 2;\n anno.createPointer(tool, 'down', s.page.pon, s.page.point, finish);\n drawingPoly = !finish;\n return true;\n }\n drawingPoly = false;\n // Each callout click advances the core's tip → knee → box state machine; the\n // final box click/drag commits and clears the draft (so `drawingCallout`\n // resets on the next tool change or simply idles harmlessly).\n if (isCalloutTool(st)) drawingCallout = true;\n // The DOWN sample's display rotation rides along for the tool's `upright`\n // policy; the core captures it on the draft (later phases don't carry it).\n anno.createPointer(tool, 'down', s.page.pon, s.page.point, false, s.page.rotation);\n return true;\n },\n onMove: (s) => {\n const tool = toolId();\n const st = subtypeOf(tool);\n // Drag-moves (button down): rect/line/ink/free-text size their box, and a\n // callout (a non-poly tool) sizes its text box during the box step. Poly\n // tools take vertices by click, so they ignore drag-moves.\n if (!origin || (isPolyTool(st) && !drawingPoly)) return;\n const point = pointOn(s, origin.pon);\n if (!point) return;\n origin.point = point;\n anno.createPointer(tool, 'move', origin.pon, point);\n },\n onUp: (s) => {\n const tool = toolId();\n const st = subtypeOf(tool);\n if (origin && !isPolyTool(st)) {\n // ALWAYS commit the drag, even released off-page (point pins in core).\n anno.createPointer(tool, 'up', origin.pon, pointOn(s, origin.pon) ?? origin.point);\n if (st === 'ink') {\n const groupStrokesMs = anno.tool(tool)?.ink?.groupStrokesMs ?? 0;\n if (groupStrokesMs > 0) {\n const pon = origin.pon;\n const timer = setTimeout(() => {\n pendingInk = null;\n anno.finishInkDraft();\n }, groupStrokesMs);\n pendingInk = { tool, pon, timer };\n }\n }\n }\n origin = null;\n },\n onHover: (s) => {\n const tool = toolId();\n const st = subtypeOf(tool);\n // Hover preview for the multi-click tools: poly (while placing vertices) and\n // callout (while placing the tip/knee/box) follow the cursor between clicks.\n if (s.page && ((drawingPoly && isPolyTool(st)) || (drawingCallout && isCalloutTool(st)))) {\n anno.createPointer(tool, 'move', s.page.pon, s.page.point);\n }\n },\n };\n}\n","/**\n * Text-selection authoring: the bridge that turns a TEXT SELECTION into markup\n * or a text-edit annotation (Insert / Replace). These tools have no gesture of\n * their own — they enable the selection plugin's `text-select` gesture, and this\n * module consumes its typed signals (`onCommit` to create, `onChange` to preview).\n * Selection is the producer; annotation the consumer — selection never knows\n * annotation exists. Kept out of the plugin-definition file so that stays lean.\n */\nimport type { InteractionCapability } from '@embedpdf/plugin-interaction';\nimport type { SelectionCapability } from '@embedpdf/plugin-selection';\nimport type { AnnotationHostCapability } from './types';\n\n/**\n * Wire the selection → annotation BRIDGE. The markup / caret TOOLS and their\n * defaults are registered by the plugin `init` from the tool registry; this\n * function only consumes the selection plugin's typed signals — so call it from\n * `init` only when a selection plugin is present. Selection is the producer,\n * annotation the consumer; selection never knows annotation exists.\n */\nexport function wireMarkup(\n annotation: AnnotationHostCapability,\n selection: SelectionCapability,\n interaction: InteractionCapability,\n): void {\n // Keep the live preview + the selection's own visual in sync with (active tool,\n // selection). While a markup tool is active the blue highlight is suppressed and\n // the in-progress selection renders as a markup ghost instead.\n const sync = () => {\n const tool = annotation.tool(interaction.activeToolId());\n const authoring = tool?.selection;\n const previewSubtype =\n authoring?.kind === 'markup'\n ? tool!.subtype\n : authoring?.kind === 'text-edit' && authoring.operation === 'replace'\n ? 'strikeout'\n : null;\n selection.setHighlightVisible(previewSubtype == null);\n if (previewSubtype && tool && selection.hasSelection()) {\n const rectsByPage: Record<number, ReturnType<typeof selection.rectsForPage>> = {};\n for (const page of selection.snapshot().pages) rectsByPage[page.pon] = page.rects;\n annotation.previewMarkup(previewSubtype, rectsByPage, tool.preset);\n } else {\n annotation.clearMarkupPreview();\n }\n };\n selection.onChange(sync); // drag-extend → live preview\n interaction.onToolChange(sync); // entering/leaving a markup tool → restore blue / clear ghost\n\n // On gesture-end, if a markup tool is active, turn the selection into markup.\n selection.onCommit(() => {\n const tool = annotation.tool(interaction.activeToolId());\n const authoring = tool?.selection;\n if (!tool || !authoring) return; // pointer tool → leave the selection (copy)\n const snapshot = selection.snapshot();\n if (authoring.kind === 'text-edit' && authoring.operation === 'insert') {\n if (snapshot.end) annotation.createCaret(snapshot.end.pon, snapshot.end.rect);\n selection.clear();\n return;\n }\n if (authoring.kind === 'text-edit' && authoring.operation === 'replace') {\n // `/IRT` relationships are page-local, so a cross-page selection becomes\n // one self-contained Caret + StrikeOut pair per page.\n for (const page of snapshot.pages) {\n const endRect = page.rects[page.rects.length - 1];\n if (endRect) annotation.createReplaceText(page.pon, page.rects, endRect, tool.preset);\n }\n selection.clear();\n return;\n }\n for (const page of snapshot.pages) {\n annotation.createMarkup(tool.subtype, page.pon, page.rects, tool.preset);\n }\n selection.clear(); // fires onChange → preview clears; blue stays suppressed (markup tool still active)\n });\n}\n","import { initialModel } from '@embedpdf/core-annotation';\nimport type {\n AnnotationAction,\n AnnotationConfig,\n AnnotationState,\n ChromeSettings,\n ChromeSettingsPatch,\n} from './types';\n\n/**\n * Out-of-the-box selection chrome — a sensible document-annotation feel. Every\n * length is CSS px (screen-constant across zoom); every color falls back to\n * `accent`. Just defaults: override any field at registration\n * (`annotationPlugin({ chrome })`) or at runtime (`setChrome`).\n */\nexport const DEFAULT_CHROME: ChromeSettings = {\n accent: '#3858e9',\n // Solid, like the shape's own resting look — one style at rest AND rotated.\n outline: { style: 'solid', width: 1 },\n // 8px squares to look at, 24px to grab (touch-friendly without visual bulk).\n handles: { size: 8, hitSize: 24, fill: '#ffffff' },\n knob: { size: 10, hitSize: 24, offset: 32, stalk: true, fill: '#ffffff' },\n // Faint reference cross, prominent live indicator (v2 feel).\n guides: { enabled: true, style: 'solid', width: 1, axisOpacity: 0.35, indicatorOpacity: 0.8 },\n};\n\n/** Deep-partial merge of a chrome patch — one level per piece, like the\n * stage-settings convention (align pairs / pageFrame). */\nexport const mergeChrome = (base: ChromeSettings, patch: ChromeSettingsPatch): ChromeSettings => ({\n accent: patch.accent ?? base.accent,\n outline: { ...base.outline, ...patch.outline },\n handles: { ...base.handles, ...patch.handles },\n knob: { ...base.knob, ...patch.knob },\n guides: { ...base.guides, ...patch.guides },\n});\n\n/**\n * The slice holds the annotation-core Model + the selection-chrome settings.\n * The pure `update` runs in the capability (the shell, which also performs\n * effects); the reducer only stores new state — keeping the kernel store a\n * dumb, serializable container. The registration config seeds the model's\n * snap settings and the chrome.\n */\nexport const initialAnnotationState = (config: AnnotationConfig = {}): AnnotationState => ({\n model: { ...initialModel, snap: { ...initialModel.snap, ...config.snap } },\n chrome: mergeChrome(DEFAULT_CHROME, config.chrome ?? {}),\n toolGhost: null,\n stampArmEpoch: 0,\n});\n\nexport const annotationReducer = (\n state: AnnotationState,\n action: AnnotationAction,\n): AnnotationState => {\n switch (action.type) {\n case 'SET_MODEL':\n return { ...state, model: action.model };\n case 'SET_CHROME':\n return { ...state, chrome: mergeChrome(state.chrome, action.patch) };\n case 'SET_TOOL_GHOST':\n return { ...state, toolGhost: action.ghost };\n case 'STAMP_ARM_CHANGED':\n // A new (or dropped) payload invalidates any ghost drawn for the old one.\n return { ...state, stampArmEpoch: state.stampArmEpoch + 1, toolGhost: null };\n default:\n return state;\n }\n};\n","import { definePlugin } from '@embedpdf/core';\nimport { InteractionToken } from '@embedpdf/plugin-interaction';\nimport { SelectionToken } from '@embedpdf/plugin-selection';\nimport { createAnnotationCapability } from './capability';\nimport { registerAnnotationEffects } from './effects';\nimport {\n createDrawHandler,\n createEditHandler,\n createGhostHandler,\n createMarqueeHandler,\n createPlaceHandler,\n} from './handler';\nimport { wireMarkup } from './markup';\nimport { annotationReducer, initialAnnotationState } from './reducer';\nimport { AnnotationToken } from './types';\nimport type {\n AnnotationAction,\n AnnotationConfig,\n AnnotationHostCapability,\n AnnotationState,\n} from './types';\n\n/**\n * The annotation plugin. Document-scoped; requires the interaction hub and\n * OPTIONALLY uses the selection plugin. Shapes/ink work with no selection; text\n * markup lights up only when a selection plugin is present.\n */\nexport const annotationPlugin = (config: AnnotationConfig = {}) =>\n definePlugin<AnnotationState, AnnotationAction, AnnotationHostCapability>({\n id: 'annotation',\n token: AnnotationToken,\n scope: 'document',\n requires: [InteractionToken],\n optional: [SelectionToken],\n initialState: () => initialAnnotationState(config),\n reduce: annotationReducer,\n // The capability owns the resolved tool registry (built-ins + config `tools`),\n // so it needs the config too — not just the reducer state.\n capability: (ctx) => createAnnotationCapability(ctx, config),\n // Fold in remote collaborators' edits (own edits flow through the capability).\n effects: registerAnnotationEffects,\n init: (ctx) => {\n const interaction = ctx.get(InteractionToken);\n const annotation = ctx.get(AnnotationToken);\n const selection = ctx.tryGet(SelectionToken);\n\n // Register every resolved tool (shapes, lines, ink, free-text, markup, stamp,\n // plus anything the embedder added via config `tools`) and seed its defaults.\n // A tool is a named preset over a subtype — see `tools.ts`. Markup / caret\n // tools ride the selection plugin's `text-select` gesture, so they stay\n // inert (skipped) when no selection plugin is installed.\n for (const tool of annotation.tools()) {\n if (tool.enables.has('text-select') && !selection) continue;\n interaction.registerTool({ id: tool.id, cursor: tool.cursor, enables: tool.enables });\n if (tool.defaults) annotation.setDefaults(tool.preset, tool.defaults);\n }\n\n interaction.registerHandler(createPlaceHandler(annotation));\n interaction.registerHandler(createGhostHandler(annotation, interaction));\n interaction.registerHandler(createEditHandler(annotation, interaction));\n interaction.registerHandler(createMarqueeHandler(annotation));\n interaction.registerHandler(createDrawHandler(annotation, interaction));\n interaction.onToolChange(() => {\n annotation.cancel();\n annotation.clearGhost(); // a footprint belongs to the tool that computed it\n // Engagement follows the tool: annotations whose Behavior just engaged\n // (form widgets under a fill tool) drop out of the selection — no\n // stranded chrome on a fill control.\n annotation.pruneEngagedSelection();\n // Leaving the stamp family drops any armed payload — bytes are tool state,\n // not document state (any stamp tool keeps it; a non-stamp tool clears it).\n if (!interaction.activeTool().enables.has('annotation-stamp')) annotation.disarmStamp();\n });\n\n // Markup is opt-in: wire the selection→annotation BRIDGE only when a\n // selection plugin is present (the markup TOOLS were registered above).\n if (selection) wireMarkup(annotation, selection, interaction);\n },\n });\n","/**\n * @embedpdf/plugin-annotation — annotations on the v3 stack.\n *\n * The pure @embedpdf/core-annotation wired to the engine repository (optimistic\n * create/patch/delete) and the interaction hub (ambient editing + draw tools).\n * Behaviors (forms, links) plug in via registerBehavior. Zero framework code.\n */\nimport type { CapabilityToken } from '@embedpdf/core';\nimport { AnnotationToken as AnnotationHostToken } from './types';\nimport type { AnnotationCapability } from './types';\n\nexport { annotationPlugin } from './annotation.plugin';\nexport {\n fromDTO,\n toCreateDraft,\n toPatch,\n refKey,\n styleFromDTO,\n widgetAppearanceFromProps,\n} from './repository';\n// The shared placement layer + the one click↔drag threshold, re-exported so a\n// sibling COMMIT PLANE (the form plugin's place handler) resolves clicks with\n// the exact call the annotation core and the footprint ghost use.\nexport { MIN_DRAG, resolveClickPlacement, type ClickPlacement } from '@embedpdf/core-annotation';\nexport { DEFAULT_CHROME } from './reducer';\nexport { DEFAULT_TOOLS } from './tools';\nexport type {\n AnnotationToolDef,\n AnnotationToolInput,\n GhostPolicy,\n InkAuthoringOptions,\n PromptSourceSpec,\n ResolvedTool,\n SelectionAuthoring,\n StampSourceSpec,\n ToolAuthoringKind,\n ToolDefaultsFor,\n} from './tools';\nexport type {\n AnnotationCapability,\n AnnotationConfig,\n AnnotationState,\n AnnotationAction,\n Behavior,\n ChromeSettings,\n ChromeSettingsPatch,\n LinkNavItem,\n SelectionFlags,\n SelectionProps,\n StampToolInput,\n FilePickerProvider,\n FilePromptRequest,\n TextItem,\n ToolGhost,\n} from './types';\n// The property vocabulary + schema (defined in the portable core; re-exported so\n// app code building property UIs needs only this package).\nexport { propsFor } from '@embedpdf/core-annotation';\nexport type {\n AnnotationFlags,\n AnnotationProps,\n AnnotationPropsPatch,\n BlendMode,\n Border,\n ClickCreate,\n LineEnding,\n LineEndings,\n PropKey,\n PropSpec,\n SnapSettings,\n TextAlign,\n} from '@embedpdf/core-annotation';\n\n/**\n * App-facing annotation token: resolves the public {@link AnnotationCapability}.\n * It is the SAME runtime token the plugin provides, narrowed to the public lens —\n * the framework-only surface (render projection, pointer gestures, behavior\n * registration) is reachable only via `@embedpdf/plugin-annotation/internal`.\n */\nexport const AnnotationToken =\n AnnotationHostToken as unknown as CapabilityToken<AnnotationCapability>;\n"],"mappings":";;;;;;;AAoBA,SAAgB,0BACd,KACM;CACN,MAAM,MAAM,IAAI;CAChB,IAAI,CAAC,KAAK;CAEV,MAAM,UAAU,QACd,IAAI,SAAS,CAAC,EAAE,MAAM,MAAM,MAAM,EAAE,qBAAqB,GAAG,CAAC,EAAE,MAAM,QAAQ;CAE/E,MAAM,SAAS,QAAmB;EAChC,MAAM,CAAC,QAAQ,OAAO,IAAI,SAAS,CAAC,CAAC,OAAO,GAAG;EAC/C,IAAI,SAAS;GAAE,MAAM;GAAa,OAAO;EAAK,CAAC;CACjD;CAEA,MAAM,UAAU,MAAoD,WAA0B;EAK5F,MAAM,OAAgB,CAAC;EACvB,MAAM,OAAgB,CAAC;EACvB,KAAK,MAAM,OAAO,MAAM;GACtB,MAAM,OAAO,OAAO,IAAI,gBAAgB;GACxC,IAAI,CAAC,MAAM;GAEX,MAAM,IAAI,QAAQ,KAAK,MAAM,OAAO;GAKpC,IAAI,EAAE,YAAY,UAAU,EAAE,MAAM,YAAY,UAAU,EAAE,KAAK,cAAc,SAAS;IACtF,MAAM,WAAW,EAAE,KAAK,YAAY,OAAO,EAAE,KAAK,SAAS,IAAI;IAC/D,MAAM,SAAS,WAAW,IAAI,SAAS,CAAC,CAAC,MAAM,KAAK,YAAY;IAChE,IACE,UACA,OAAO,YAAY,UACnBA,WAAS,OAAO,OAAO,CAAC,CAAC,MAAM,MAAM,EAAE,QAAQ,MAAM,KACrD,EAAE,KACF;KACA,MAAM,OAAO,OAAO,YAAY,CAAC;KACjC,MAAM,QAAQ,KAAK,MAAM,MAAM,OAAO,CAAC,MAAM,EAAE,EAAE;KACjD,KAAK,KAAK;MACR,GAAG;MACH,MAAM,EAAE,KAAK;MACb,UAAU,QAAQ,OAAO,CAAC,GAAG,MAAM,EAAE,GAAG;KAC1C,CAAC;KACD;IACF;GACF;GACA,CAAC,SAAS,OAAO,KAAA,CAAM,KAAK,CAAC;EAC/B;EACA,IAAI,KAAK,QAAQ,MAAM;GAAE,GAAG;GAAU,QAAQ;GAAM,QAAQ;EAAK,CAAC;EAClE,IAAI,KAAK,QAAQ,MAAM;GAAE,GAAG;GAAU,QAAQ;EAAK,CAAC;CACtD;CAEA,MAAM,cAAc,IAAI,OAAO,WAAW,UAAyB;EAMjE,IAAI,MAAM,SAAS,uBAAuB,MAAM,SAAS,uBAAuB;GAC9E,MAAM;IACJ,GAAG;IACH,KAAK,MAAM,eACR,QAAQ,MAAM,EAAE,oBAAoB,CAAC,CAAC,CACtC,KAAK,MAAM,kBAAkB;KAAE,MAAM;KAAgB,OAAO,EAAE;IAAkB,CAAC,CAAC;GACvF,CAAC;GACD;EACF;EAOA,IAAI,MAAM,SAAS,qBAAqB;GACtC,MAAM,WAAW,IAAI,IACnB,MAAM,QAAQ,QAAQ,MAAM,EAAE,WAAW,SAAS,CAAC,CAAC,KAAK,MAAM,EAAE,gBAAgB,CACnF;GACA,IAAI,SAAS,MAAM;IACjB,MAAM,OAAO,IAAI,IAAIC,iBAAe;IACpC,KAAK,MAAM,OAAO,UAAU,KAAU,WAAW,GAAG;GACtD;GACA;EACF;EAEA,IAAI,EAAE,YAAY,UAAU,MAAM,OAAO,SAAS,UAAU;EAC5D,QAAQ,MAAM,MAAd;GACE,KAAK;IAEH,OAAO,CAAC,MAAM,OAAO,GAAG,IAAI;IAC5B;GACF,KAAK;IAGH,OAAO,CAAC,MAAM,OAAO,GAAG,MAAM,WAAW,OAAO;IAChD;GACF,KAAK;IAEH,OAAO,MAAM,OAAO,KAAK;IACzB;GACF,KAAK;IACH,IAAI,MAAM,SAAS;KACjB,MAAM,MAAM,kBAAkB,MAAM,OAAO;KAC3C,MAAM,IAAI,IAAI,SAAS,CAAC,CAAC;KACzB,IAAI,EAAE,KAAK,MACT,MAAM;MAAE,GAAG;MAAU,KAAK,CAAC,GAAG;KAAE,CAAC;UAKjC,KAAK,MAAM,MAAM,EAAE,OAAO;MACxB,MAAM,IAAI,EAAE,KAAK;MACjB,MAAM,OAAO,GAAG;MAChB,IAAI,CAAC,KAAK,CAAC,MAAM,UAAU,CAAC,KAAK,MAAM,MAAM,OAAO,CAAC,MAAM,GAAG,GAAG;MACjE,MAAM,YAAY,KAAK,QAAQ,MAAM,OAAO,CAAC,MAAM,GAAG;MACtD,MAAM;OACJ,GAAG;OACH,QAAQ,CACN;QAAE,GAAG;QAAG,UAAU;QAAW,GAAI,UAAU,SAAS,CAAC,IAAI,EAAE,MAAM,KAAK;OAAG,CAC3E;MACF,CAAC;MACD;KACF;IAEJ;IACA;GACF,SACE;EACJ;CACF,CAAC;CACD,IAAI,QAAQ,WAAW;AACzB;;;AChJA,MAAM,4BAA4B;AAClC,MAAM,cAAc,YAA8B,YAAY,aAAa,YAAY;AACvF,MAAM,iBAAiB,YAA8B,YAAY;;;;;;;;;AAUjE,MAAM,WAAW,GAAkB,QACjC,EAAE,UAAU,GAAG,MAAM,EAAE,MAAM,QAAQ,MAAM,EAAE,KAAK,QAAQ;;;;;;;;;;;;;AAc5D,SAAgB,mBAAmB,MAAoD;CACrF,OAAO;EACL,IAAI;EAGJ,UAAU;EACV,aAAa,MAAM,EAAE,QAAQ,IAAI,kBAAkB,KAAK,EAAE,QAAQ,IAAI,kBAAkB;EACxF,SAAS,MAAM;GACb,IAAI,CAAC,EAAE,MAAM,OAAO;GAGpB,OAAO,KAAK,QAAQ,EAAE,KAAK,KAAK,EAAE,KAAK,OAAO,EAAE,KAAK,QAAQ;EAC/D;CACF;AACF;;;;;;;;;AAUA,SAAgB,mBACd,MACA,aACoB;CACpB,MAAM,SAAS,MAA2B;EACxC,IAAI,EAAE,MACJ,KAAK,aAAa,YAAY,aAAa,GAAG,EAAE,KAAK,KAAK,EAAE,KAAK,OAAO,EAAE,KAAK,QAAQ;OACpF,KAAK,WAAW;CACvB;CACA,OAAO;EACL,IAAI;EACJ,UAAU;EACV,kBAAkB;EAClB,cAAc;GACZ,KAAK,WAAW;GAChB,OAAO;EACT;EACA,SAAS;CACX;AACF;;;;;;;;AASA,SAAgB,kBACd,MACA,aACoB;CAKpB,IAAI,SAA6C;CACjD,OAAO;EACL,IAAI;EACJ,UAAU;EACV,aAAa,MAAM,EAAE,QAAQ,IAAI,iBAAiB;EAClD,SAAS,MAAM;GACb,IAAI,CAAC,EAAE,MAAM,OAAO;GAKpB,MAAM,aAAa,KAAK,eAAe,KAAK;GAC5C,IAAI,YAAY,KAAK,YAAY;GACjC,IACE,KAAK,QAAQ,EAAE,KAAK,KAAK,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,IAAI,MACjF,SACA;IAGA,IAAI,CAAC,EAAE,UAAU,OAAO,KAAK,SAAS;IAKtC,OAAO;GACT;GAEA,KAAK,EAAE,cAAc,MAAM,GAAG;IAC5B,KAAK,gBAAgB,EAAE,KAAK,KAAK,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,IAAI;IACzF,OAAO;GACT;GACA,KAAK,YACH,QACA,EAAE,KAAK,KACP,EAAE,KAAK,OACP,EAAE,UAAU,OACZ,EAAE,KAAK,OACP,EAAE,KAAK,UACP,EAAE,KAAK,IACT;GACA,SAAS;IAAE,KAAK,EAAE,KAAK;IAAK,OAAO,EAAE,KAAK;GAAM;GAChD,OAAO;EACT;EACA,SAAS,MAAM;GACb,IAAI,CAAC,QAAQ;GACb,MAAM,QAAQ,QAAQ,GAAG,OAAO,GAAG;GACnC,IAAI,CAAC,OAAO;GACZ,OAAO,QAAQ;GAGf,KAAK,YACH,QACA,OAAO,KACP,OACA,EAAE,UAAU,OACZ,EAAE,MAAM,OACR,EAAE,MAAM,UACR,EAAE,MAAM,IACV;EACF;EACA,OAAO,MAAM;GACX,IAAI,CAAC,QAAQ;GAIb,KAAK,YAAY,MAAM,OAAO,KAAK,QAAQ,GAAG,OAAO,GAAG,KAAK,OAAO,OAAO,KAAK;GAChF,SAAS;EACX;EACA,UAAU,MAAM;GAEd,YAAY,UACV,cACA,EAAE,OACE,KAAK,SAAS,EAAE,KAAK,KAAK,EAAE,KAAK,OAAO,EAAE,KAAK,OAAO,EAAE,KAAK,UAAU,EAAE,KAAK,IAAI,IAClF,MACJ,EACF;GAGA,KAAK,QACH,EAAE,OACE;IACE,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE,KAAK;IACd,OAAO,EAAE,KAAK;IACd,UAAU,EAAE,KAAK;IACjB,MAAM,EAAE,KAAK;GACf,IACA,IACN;EACF;CACF;AACF;;;;;AAMA,SAAgB,qBAAqB,MAAoD;CACvF,IAAI,SAMO;CACX,IAAI,OAA2C;CAC/C,IAAI,WAAW;CAIf,IAAI,OAAmE,CAAC;CACxE,OAAO;EACL,IAAI;EACJ,UAAU;EACV,aAAa,MAAM,EAAE,QAAQ,IAAI,oBAAoB;EACrD,SAAS,MAAM;GACb,IAAI,CAAC,EAAE,MAAM,OAAO;GACpB,SAAS;IACP,KAAK,EAAE,KAAK;IACZ,OAAO,EAAE,KAAK;IACd,IAAI,EAAE,SAAS;IACf,IAAI,EAAE,SAAS;IACf,OAAO,EAAE,UAAU;GACrB;GACA,OAAO;IAAE,KAAK,EAAE,KAAK;IAAK,OAAO,EAAE,KAAK;GAAM;GAC9C,OAAO;IAAE,OAAO,EAAE,KAAK;IAAO,UAAU,EAAE,KAAK;IAAU,MAAM,EAAE,KAAK;GAAK;GAC3E,WAAW;GACX,OAAO;EACT;EACA,SAAS,MAAM;GACb,IAAI,CAAC,QAAQ;GAIb,MAAM,QAAQ,QAAQ,GAAG,OAAO,GAAG;GACnC,IAAI,CAAC,OAAO;GACZ,OAAO;IAAE,KAAK,OAAO;IAAK;GAAM;GAChC,IAAI,CAAC,UAAU;IACb,IACE,KAAK,MAAM,EAAE,SAAS,IAAI,OAAO,IAAI,EAAE,SAAS,IAAI,OAAO,EAAE,IAAI,2BAEjE;IAEF,WAAW;IACX,KAAK,eACH,QACA,OAAO,KACP,OAAO,OACP,OAAO,OACP,KAAK,OACL,KAAK,UACL,KAAK,IACP;GACF;GACA,KAAK,eACH,QACA,OAAO,KACP,OACA,OAAO,OACP,KAAK,OACL,KAAK,UACL,KAAK,IACP;EACF;EACA,YAAY;GACV,IAAI,YAAY,UAAU,MACxB,KAAK,eACH,MACA,OAAO,KACP,KAAK,OACL,OAAO,OACP,KAAK,OACL,KAAK,UACL,KAAK,IACP;GAEF,SAAS;GACT,OAAO;GACP,WAAW;EACb;CACF;AACF;;AAGA,SAAgB,kBACd,MACA,aACoB;CAIpB,MAAM,eAAe,YAAY,aAAa;CAC9C,MAAM,aAAa,OAAwB,KAAK,YAAY,EAAE;CAC9D,IAAI,cAAc;CAGlB,IAAI,iBAAiB;CAGrB,IAAI,SAA6C;CACjD,IAAI,aAIO;CACX,MAAM,wBAAwB;EAC5B,IAAI,CAAC,YAAY;EACjB,aAAa,WAAW,KAAK;EAC7B,aAAa;EACb,KAAK,eAAe;CACtB;CACA,YAAY,mBAAmB;EAC7B,gBAAgB;EAChB,cAAc;EACd,iBAAiB;EACjB,SAAS;CACX,CAAC;CACD,OAAO;EACL,IAAI;EAaJ,UAAU;EACV,aAAa,MAAM,EAAE,QAAQ,IAAI,iBAAiB;EAClD,SAAS,MAAM;GACb,IAAI,CAAC,EAAE,MAAM,OAAO;GACpB,MAAM,OAAO,OAAO;GACpB,MAAM,KAAK,UAAU,IAAI;GACzB,IAAI,OAAO,SAAS,YAClB,IAAI,WAAW,SAAS,QAAQ,WAAW,QAAQ,EAAE,KAAK,KAAK;IAC7D,aAAa,WAAW,KAAK;IAC7B,aAAa;GACf,OACE,gBAAgB;GAKpB,SAAS;IAAE,KAAK,EAAE,KAAK;IAAK,OAAO,EAAE,KAAK;GAAM;GAChD,IAAI,WAAW,EAAE,GAAG;IAClB,MAAM,UAAU,EAAE,cAAc,MAAM;IACtC,KAAK,cAAc,MAAM,QAAQ,EAAE,KAAK,KAAK,EAAE,KAAK,OAAO,MAAM;IACjE,cAAc,CAAC;IACf,OAAO;GACT;GACA,cAAc;GAId,IAAI,cAAc,EAAE,GAAG,iBAAiB;GAGxC,KAAK,cAAc,MAAM,QAAQ,EAAE,KAAK,KAAK,EAAE,KAAK,OAAO,OAAO,EAAE,KAAK,QAAQ;GACjF,OAAO;EACT;EACA,SAAS,MAAM;GACb,MAAM,OAAO,OAAO;GACpB,MAAM,KAAK,UAAU,IAAI;GAIzB,IAAI,CAAC,UAAW,WAAW,EAAE,KAAK,CAAC,aAAc;GACjD,MAAM,QAAQ,QAAQ,GAAG,OAAO,GAAG;GACnC,IAAI,CAAC,OAAO;GACZ,OAAO,QAAQ;GACf,KAAK,cAAc,MAAM,QAAQ,OAAO,KAAK,KAAK;EACpD;EACA,OAAO,MAAM;GACX,MAAM,OAAO,OAAO;GACpB,MAAM,KAAK,UAAU,IAAI;GACzB,IAAI,UAAU,CAAC,WAAW,EAAE,GAAG;IAE7B,KAAK,cAAc,MAAM,MAAM,OAAO,KAAK,QAAQ,GAAG,OAAO,GAAG,KAAK,OAAO,KAAK;IACjF,IAAI,OAAO,OAAO;KAChB,MAAM,iBAAiB,KAAK,KAAK,IAAI,CAAC,EAAE,KAAK,kBAAkB;KAC/D,IAAI,iBAAiB,GAMnB,aAAa;MAAE;MAAM,KALT,OAAO;MAKO,OAJZ,iBAAiB;OAC7B,aAAa;OACb,KAAK,eAAe;MACtB,GAAG,cAC2B;KAAE;IAEpC;GACF;GACA,SAAS;EACX;EACA,UAAU,MAAM;GACd,MAAM,OAAO,OAAO;GACpB,MAAM,KAAK,UAAU,IAAI;GAGzB,IAAI,EAAE,SAAU,eAAe,WAAW,EAAE,KAAO,kBAAkB,cAAc,EAAE,IACnF,KAAK,cAAc,MAAM,QAAQ,EAAE,KAAK,KAAK,EAAE,KAAK,KAAK;EAE7D;CACF;AACF;;;;;;;;;;AChYA,SAAgB,WACd,YACA,WACA,aACM;CAIN,MAAM,aAAa;EACjB,MAAM,OAAO,WAAW,KAAK,YAAY,aAAa,CAAC;EACvD,MAAM,YAAY,MAAM;EACxB,MAAM,iBACJ,WAAW,SAAS,WAChB,KAAM,UACN,WAAW,SAAS,eAAe,UAAU,cAAc,YACzD,cACA;EACR,UAAU,oBAAoB,kBAAkB,IAAI;EACpD,IAAI,kBAAkB,QAAQ,UAAU,aAAa,GAAG;GACtD,MAAM,cAAyE,CAAC;GAChF,KAAK,MAAM,QAAQ,UAAU,SAAS,CAAC,CAAC,OAAO,YAAY,KAAK,OAAO,KAAK;GAC5E,WAAW,cAAc,gBAAgB,aAAa,KAAK,MAAM;EACnE,OACE,WAAW,mBAAmB;CAElC;CACA,UAAU,SAAS,IAAI;CACvB,YAAY,aAAa,IAAI;CAG7B,UAAU,eAAe;EACvB,MAAM,OAAO,WAAW,KAAK,YAAY,aAAa,CAAC;EACvD,MAAM,YAAY,MAAM;EACxB,IAAI,CAAC,QAAQ,CAAC,WAAW;EACzB,MAAM,WAAW,UAAU,SAAS;EACpC,IAAI,UAAU,SAAS,eAAe,UAAU,cAAc,UAAU;GACtE,IAAI,SAAS,KAAK,WAAW,YAAY,SAAS,IAAI,KAAK,SAAS,IAAI,IAAI;GAC5E,UAAU,MAAM;GAChB;EACF;EACA,IAAI,UAAU,SAAS,eAAe,UAAU,cAAc,WAAW;GAGvE,KAAK,MAAM,QAAQ,SAAS,OAAO;IACjC,MAAM,UAAU,KAAK,MAAM,KAAK,MAAM,SAAS;IAC/C,IAAI,SAAS,WAAW,kBAAkB,KAAK,KAAK,KAAK,OAAO,SAAS,KAAK,MAAM;GACtF;GACA,UAAU,MAAM;GAChB;EACF;EACA,KAAK,MAAM,QAAQ,SAAS,OAC1B,WAAW,aAAa,KAAK,SAAS,KAAK,KAAK,KAAK,OAAO,KAAK,MAAM;EAEzE,UAAU,MAAM;CAClB,CAAC;AACH;;;;;;;;;AC3DA,MAAa,iBAAiC;CAC5C,QAAQ;CAER,SAAS;EAAE,OAAO;EAAS,OAAO;CAAE;CAEpC,SAAS;EAAE,MAAM;EAAG,SAAS;EAAI,MAAM;CAAU;CACjD,MAAM;EAAE,MAAM;EAAI,SAAS;EAAI,QAAQ;EAAI,OAAO;EAAM,MAAM;CAAU;CAExE,QAAQ;EAAE,SAAS;EAAM,OAAO;EAAS,OAAO;EAAG,aAAa;EAAM,kBAAkB;CAAI;AAC9F;;;AAIA,MAAa,eAAe,MAAsB,WAAgD;CAChG,QAAQ,MAAM,UAAU,KAAK;CAC7B,SAAS;EAAE,GAAG,KAAK;EAAS,GAAG,MAAM;CAAQ;CAC7C,SAAS;EAAE,GAAG,KAAK;EAAS,GAAG,MAAM;CAAQ;CAC7C,MAAM;EAAE,GAAG,KAAK;EAAM,GAAG,MAAM;CAAK;CACpC,QAAQ;EAAE,GAAG,KAAK;EAAQ,GAAG,MAAM;CAAO;AAC5C;;;;;;;;AASA,MAAa,0BAA0B,SAA2B,CAAC,OAAwB;CACzF,OAAO;EAAE,GAAG;EAAc,MAAM;GAAE,GAAG,aAAa;GAAM,GAAG,OAAO;EAAK;CAAE;CACzE,QAAQ,YAAY,gBAAgB,OAAO,UAAU,CAAC,CAAC;CACvD,WAAW;CACX,eAAe;AACjB;AAEA,MAAa,qBACX,OACA,WACoB;CACpB,QAAQ,OAAO,MAAf;EACE,KAAK,aACH,OAAO;GAAE,GAAG;GAAO,OAAO,OAAO;EAAM;EACzC,KAAK,cACH,OAAO;GAAE,GAAG;GAAO,QAAQ,YAAY,MAAM,QAAQ,OAAO,KAAK;EAAE;EACrE,KAAK,kBACH,OAAO;GAAE,GAAG;GAAO,WAAW,OAAO;EAAM;EAC7C,KAAK,qBAEH,OAAO;GAAE,GAAG;GAAO,eAAe,MAAM,gBAAgB;GAAG,WAAW;EAAK;EAC7E,SACE,OAAO;CACX;AACF;;;;;;;;ACxCA,MAAa,oBAAoB,SAA2B,CAAC,MAC3D,aAA0E;CACxE,IAAI;CACJ,OAAOC;CACP,OAAO;CACP,UAAU,CAAC,gBAAgB;CAC3B,UAAU,CAAC,cAAc;CACzB,oBAAoB,uBAAuB,MAAM;CACjD,QAAQ;CAGR,aAAa,QAAQ,2BAA2B,KAAK,MAAM;CAE3D,SAAS;CACT,OAAO,QAAQ;EACb,MAAM,cAAc,IAAI,IAAI,gBAAgB;EAC5C,MAAM,aAAa,IAAI,IAAIA,iBAAe;EAC1C,MAAM,YAAY,IAAI,OAAO,cAAc;EAO3C,KAAK,MAAM,QAAQ,WAAW,MAAM,GAAG;GACrC,IAAI,KAAK,QAAQ,IAAI,aAAa,KAAK,CAAC,WAAW;GACnD,YAAY,aAAa;IAAE,IAAI,KAAK;IAAI,QAAQ,KAAK;IAAQ,SAAS,KAAK;GAAQ,CAAC;GACpF,IAAI,KAAK,UAAU,WAAW,YAAY,KAAK,QAAQ,KAAK,QAAQ;EACtE;EAEA,YAAY,gBAAgB,mBAAmB,UAAU,CAAC;EAC1D,YAAY,gBAAgB,mBAAmB,YAAY,WAAW,CAAC;EACvE,YAAY,gBAAgB,kBAAkB,YAAY,WAAW,CAAC;EACtE,YAAY,gBAAgB,qBAAqB,UAAU,CAAC;EAC5D,YAAY,gBAAgB,kBAAkB,YAAY,WAAW,CAAC;EACtE,YAAY,mBAAmB;GAC7B,WAAW,OAAO;GAClB,WAAW,WAAW;GAItB,WAAW,sBAAsB;GAGjC,IAAI,CAAC,YAAY,WAAW,CAAC,CAAC,QAAQ,IAAI,kBAAkB,GAAG,WAAW,YAAY;EACxF,CAAC;EAID,IAAI,WAAW,WAAW,YAAY,WAAW,WAAW;CAC9D;AACF,CAAC;;;;;;;;;ACCH,MAAa,kBACXC"}
1
+ {"version":3,"file":"index.js","names":["propsFor","AnnotationToken","AnnotationToken","AnnotationHostToken"],"sources":["../src/effects.ts","../src/handler.ts","../src/markup.ts","../src/reducer.ts","../src/annotation.plugin.ts","../src/index.ts"],"sourcesContent":["/**\n * Makes annotations REACTIVE to remote collaborators — the same pattern the\n * metadata plugin uses for the Info dict.\n *\n * Own edits already update the model through the capability's promise path\n * (optimistic draw + re-sync from the engine DTO). This effect folds in the\n * edits that DIDN'T originate here: it subscribes to the document event stream\n * and applies `annotation.*` events whose `origin.kind === 'remote'` — another\n * session's create/update/move/delete, delivered over SSE on cloud. Filtering\n * to remote avoids double-applying (and racing the temp-id reconcile of) our\n * own echoes.\n */\nimport type { DocumentEvent, EffectContext } from '@embedpdf/core';\nimport { encodeStableIdKey } from '@embedpdf/engine-core/runtime';\nimport { propsFor, update, type Annot, type Msg } from '@embedpdf/core-annotation';\n\nimport { fromDTO, refKey } from './repository';\nimport { AnnotationToken } from './types';\nimport type { AnnotationAction, AnnotationState } from './types';\n\nexport function registerAnnotationEffects(\n ctx: EffectContext<AnnotationState, AnnotationAction>,\n): void {\n const doc = ctx.doc;\n if (!doc) return;\n\n const cropOf = (pon: number) =>\n ctx.document()?.pages.find((p) => p.pageObjectNumber === pon)?.boxes.crop ?? null;\n\n const apply = (msg: Msg): void => {\n const [next] = update(ctx.getState().model, msg);\n ctx.dispatch({ type: 'SET_MODEL', model: next });\n };\n\n const upsert = (dtos: ReadonlyArray<Parameters<typeof fromDTO>[0]>, bumpAp: boolean): void => {\n // `bump` re-fetches rasters, `keep` doesn't. The split is driven by the\n // ENGINE'S appearance echo riding the event (`appearance.changed`) — the\n // same verdict local edits use — so a remote MOVE costs peers zero\n // appearance re-renders while a remote restyle refreshes exactly once.\n const bump: Annot[] = [];\n const keep: Annot[] = [];\n for (const dto of dtos) {\n const crop = cropOf(dto.pageObjectNumber);\n if (!crop) continue;\n // Another session authored this — trust the engine's baked AP.\n const a = fromDTO(dto, crop, 'baked');\n // A remote ATTACHED-link child (grouped /Link under a linkable local\n // parent) folds onto the parent instead of entering the model — the\n // same rule the page-load fold applies (see foldAttachedLinks). A child\n // retarget never repaints the PARENT, so the fold never bumps.\n if (a.subtype === 'link' && a.data?.subtype === 'link' && a.data.replyType === 'group') {\n const parentId = a.data.inReplyTo ? refKey(a.data.inReplyTo) : null;\n const parent = parentId ? ctx.getState().model.byId[parentId] : null;\n if (\n parent &&\n parent.subtype !== 'link' &&\n propsFor(parent.subtype).some((s) => s.key === 'link') &&\n a.ref\n ) {\n const refs = parent.linkRefs ?? [];\n const known = refs.some((r) => refKey(r) === a.id);\n keep.push({\n ...parent,\n link: a.data.target,\n linkRefs: known ? refs : [...refs, a.ref],\n });\n continue;\n }\n }\n (bumpAp ? bump : keep).push(a);\n }\n if (bump.length) apply({ t: 'upsert', annots: bump, bumpAp: true });\n if (keep.length) apply({ t: 'upsert', annots: keep });\n };\n\n const unsubscribe = doc.events.subscribe((event: DocumentEvent) => {\n // Widget appearances are re-baked by FORM value writes — a plane this\n // plugin doesn't own, so no remote-only filter: direct fills emit\n // `form.valueChanged`, while scripted batches emit `form.effectsApplied`.\n // Neither path touches this model directly. The bump tells the render\n // layer to re-fetch exactly the repainted widgets' rasters.\n if (event.type === 'form.valueChanged' || event.type === 'form.effectsApplied') {\n apply({\n t: 'bumpAp',\n ids: event.changedWidgets\n .filter((w) => w.annotObjectNumber > 0)\n .map((w) => encodeStableIdKey({ kind: 'objectNumber', value: w.annotObjectNumber })),\n });\n return;\n }\n // A redaction apply deleted the consumed marks plus every intersecting\n // annotation on the applied pages. ORIGIN-AGNOSTIC: our own applies run\n // through the redaction plugin's doc-level verb, never this plane's\n // capability paths, so the model is stale either way — reload the\n // affected pages from the engine. (Works without plugin-redaction\n // installed: a remote collaborator's apply still reconciles this view.)\n if (event.type === 'redaction.applied') {\n const affected = new Set(\n event.results.filter((r) => r.status === 'applied').map((r) => r.pageObjectNumber),\n );\n if (affected.size) {\n const host = ctx.get(AnnotationToken);\n for (const pon of affected) void host.reloadPage(pon);\n }\n return;\n }\n // Only fold in OTHER sessions' edits; our own flow through the capability.\n if (!('origin' in event) || event.origin.kind !== 'remote') return;\n switch (event.type) {\n case 'annotation.created':\n // A create ships with a freshly baked /AP — fetch it.\n upsert([event.created], true);\n break;\n case 'annotation.updated':\n // The engine's verdict rides the event: preserved moves keep the\n // cached raster, regenerated appearances re-fetch exactly once.\n upsert([event.updated], event.appearance.changed);\n break;\n case 'annotation.moved':\n // A z-order move never touches /AP.\n upsert(event.moved, false);\n break;\n case 'annotation.deleted':\n if (event.deleted) {\n const key = encodeStableIdKey(event.deleted);\n const m = ctx.getState().model;\n if (m.byId[key]) {\n apply({ t: 'remove', ids: [key] });\n } else {\n // Not in the model → possibly a folded ATTACHED-link child a\n // remote session deleted: prune it from its parent's join keys\n // (clearing the parent's link value with the last child).\n for (const id of m.order) {\n const a = m.byId[id];\n const refs = a?.linkRefs;\n if (!a || !refs?.length || !refs.some((r) => refKey(r) === key)) continue;\n const remaining = refs.filter((r) => refKey(r) !== key);\n apply({\n t: 'upsert',\n annots: [\n { ...a, linkRefs: remaining, ...(remaining.length ? {} : { link: null }) },\n ],\n });\n break;\n }\n }\n }\n break;\n default:\n break;\n }\n });\n ctx.cleanup(unsubscribe);\n}\n","import type {\n InteractionCapability,\n InteractionHandler,\n PointerSample,\n} from '@embedpdf/plugin-interaction';\nimport type { PageRotation } from '@embedpdf/core-geometry';\nimport type { Subtype, Vec } from '@embedpdf/core-annotation';\nimport type { AnnotationHostCapability } from './types';\n\nconst MARQUEE_DRAG_THRESHOLD_PX = 4;\nconst isPolyTool = (subtype: Subtype): boolean => subtype === 'polygon' || subtype === 'polyline';\nconst isCalloutTool = (subtype: Subtype): boolean => subtype === 'free-text-callout';\n\n/**\n * Resolve a sample against a gesture's HOME page. Annotation gestures are\n * page-anchored: they track the page they started on, even when the cursor\n * wanders off it — `s.page` re-resolves per event (a page-2 point is a\n * DIFFERENT coordinate frame, the teleport bug), so prefer the source's\n * unclamped projection and fall back to the page hit only when it's the same\n * page. Null → this sample can't speak for the home page; ignore it.\n */\nconst pointOn = (s: PointerSample, pon: number): Vec | null =>\n s.project?.(pon) ?? (s.page?.pon === pon ? s.page.point : null);\n\n/**\n * Click-to-place for every payload-carrying tool (stamp / note / file\n * attachment). Each click places one annotation centred on the point (the\n * tool stays active for repeat placement — v2 rubber-stamp behaviour); the\n * capability's `placeAt` routes by the active tool's kind: armed stamp bytes,\n * a stamp/attachment prompt (pick the spot first, the payload second), or an\n * immediate note. No drag gesture — placement size comes from the content\n * (stamp aspect / the fixed icon box), not the pointer.\n *\n * Priority is deliberately BELOW the edit handler (100): a click over an\n * EXISTING annotation selects it; placement happens on empty page space.\n */\nexport function createPlaceHandler(anno: AnnotationHostCapability): InteractionHandler {\n return {\n id: 'annotation-place',\n // `annotation-stamp` is honoured as a legacy alias for embedder tool\n // configs written before the tags were unified.\n priority: 95,\n enabledFor: (t) => t.enables.has('annotation-place') || t.enables.has('annotation-stamp'),\n onDown: (s) => {\n if (!s.page) return false;\n // The click sample's display rotation drives the tool's `upright` policy —\n // the placement lands reading horizontally on a rotated page/view.\n return anno.placeAt(s.page.pon, s.page.point, s.page.rotation);\n },\n };\n}\n\n/**\n * The armed tool's FOOTPRINT ghost: every hover re-computes the would-be\n * placement under the cursor (stamp image fit / click-create default geometry);\n * off-page clears it. One handler for every tool — `ghostHoverAt` resolves the\n * tool's ghost policy and clears when it isn't `footprint`. Never captures:\n * the highest priority makes its onDown run FIRST on every press (hiding the\n * ghost while a gesture runs), then declines so the real handlers route.\n */\nexport function createGhostHandler(\n anno: AnnotationHostCapability,\n interaction: InteractionCapability,\n): InteractionHandler {\n const hover = (s: PointerSample): void => {\n if (s.page)\n anno.ghostHoverAt(interaction.activeToolId(), s.page.pon, s.page.point, s.page.rotation);\n else anno.clearGhost();\n };\n return {\n id: 'annotation-ghost',\n priority: 1000,\n enabledFor: () => true,\n onDown: () => {\n anno.clearGhost();\n return false;\n },\n onHover: hover,\n };\n}\n\n/**\n * Ambient editing: live under the `annotation-edit` tag, which BOTH the pointer\n * and pan tools enable — so you select/move/resize in any navigation mode (Adobe\n * behaviour). It captures only over an annotation/handle; over empty it\n * deselects and declines (so text-selection / pan still work). Hover drives the\n * cursor (move / pointer / resize) via a priority cursor claim.\n */\nexport function createEditHandler(\n anno: AnnotationHostCapability,\n interaction: InteractionCapability,\n): InteractionHandler {\n // The gesture's home page + last resolved point, armed on down. Every\n // move/up resolves against THIS page (the annotation slides along its edge\n // when the cursor overshoots — the core clamps), never against whatever\n // page the sample happens to hit.\n let origin: { pon: number; point: Vec } | null = null;\n return {\n id: 'annotation-edit',\n priority: 100,\n enabledFor: (t) => t.enables.has('annotation-edit'),\n onDown: (s) => {\n if (!s.page) return false;\n // While a free-text box is being edited it owns its own pointer events, so a\n // down that reaches the hub at all is a click OUTSIDE the editor — commit and\n // leave text edit. This makes exit hub-driven (deterministic) rather than\n // relying on a DOM blur, which races the focus-steal of the entering gesture.\n const wasEditing = anno.currentEditing() != null;\n if (wasEditing) anno.endTextEdit();\n if (\n anno.hitKind(s.page.pon, s.page.point, s.page.scale, s.page.rotation, s.page.zoom) ===\n 'empty'\n ) {\n // Plain empty click drops the selection. Shift-empty preserves it so the\n // lower-priority marquee handler can additive/toggle-select.\n if (!s.modifiers.shift) anno.deselect();\n // A click that dismissed an active edit is CONSUMED: its sole job was to\n // leave edit mode, so the draw tool doesn't also spawn a new annotation\n // (matches v2). Only when nothing was being edited do we decline, letting\n // pan / text-selection / draw act on the empty click.\n return wasEditing;\n }\n // Double-click over a free-text box → enter text edit (not a move).\n if ((s.clickCount ?? 1) >= 2) {\n anno.beginTextEditAt(s.page.pon, s.page.point, s.page.scale, s.page.rotation, s.page.zoom);\n return true;\n }\n anno.editPointer(\n 'down',\n s.page.pon,\n s.page.point,\n s.modifiers.shift,\n s.page.scale,\n s.page.rotation,\n s.page.zoom,\n );\n origin = { pon: s.page.pon, point: s.page.point };\n return true;\n },\n onMove: (s) => {\n if (!origin) return;\n const point = pointOn(s, origin.pon);\n if (!point) return;\n origin.point = point;\n // The sample's scale/rotation ride along (uniform across pages), so a\n // screen-anchored member page-clamps at its effective footprint mid-drag.\n anno.editPointer(\n 'move',\n origin.pon,\n point,\n s.modifiers.shift,\n s.page?.scale,\n s.page?.rotation,\n s.page?.zoom,\n );\n },\n onUp: (s) => {\n if (!origin) return;\n // ALWAYS close the gesture — a release over a page gap or outside the\n // window must still commit (a dangling draft leaves a ghost that snaps\n // back on the next interaction). `editUp` doesn't read the point.\n anno.editPointer('up', origin.pon, pointOn(s, origin.pon) ?? origin.point, false);\n origin = null;\n },\n onHover: (s) => {\n // priority 20 → beats text-select's 'text' (10) over an annotation; null clears.\n interaction.setCursor(\n 'annotation',\n s.page\n ? anno.cursorAt(s.page.pon, s.page.point, s.page.scale, s.page.rotation, s.page.zoom)\n : null,\n 20,\n );\n // Hover state for scene affordances (redact preview) — rides the same\n // per-move sample; the capability dispatches on change only.\n anno.hoverAt(\n s.page\n ? {\n pon: s.page.pon,\n point: s.page.point,\n scale: s.page.scale,\n rotation: s.page.rotation,\n zoom: s.page.zoom,\n }\n : null,\n );\n },\n };\n}\n\n/**\n * Empty-page drag selection. Lower priority than annotation edit and text\n * selection, so it only owns drags that begin on empty, non-text page space.\n */\nexport function createMarqueeHandler(anno: AnnotationHostCapability): InteractionHandler {\n let anchor: {\n pon: number;\n point: Vec;\n vx: number;\n vy: number;\n shift: boolean;\n } | null = null;\n let last: { pon: number; point: Vec } | null = null;\n let dragging = false;\n\n // The marquee page's view env at the down sample — the `up` intersects\n // screen-anchored annotations at their effective footprint with it.\n let view: { scale?: number; rotation?: PageRotation; zoom?: number } = {};\n return {\n id: 'annotation-marquee',\n priority: 50,\n enabledFor: (t) => t.enables.has('annotation-marquee'),\n onDown: (s) => {\n if (!s.page) return false;\n anchor = {\n pon: s.page.pon,\n point: s.page.point,\n vx: s.viewport.x,\n vy: s.viewport.y,\n shift: s.modifiers.shift,\n };\n last = { pon: s.page.pon, point: s.page.point };\n view = { scale: s.page.scale, rotation: s.page.rotation, zoom: s.page.zoom };\n dragging = false;\n return true;\n },\n onMove: (s) => {\n if (!anchor) return;\n // Anchored to the page the drag started on; the projected point keeps the\n // marquee growing along the page edge when the cursor overshoots (the\n // core clamps it to the page box).\n const point = pointOn(s, anchor.pon);\n if (!point) return;\n last = { pon: anchor.pon, point };\n if (!dragging) {\n if (\n Math.hypot(s.viewport.x - anchor.vx, s.viewport.y - anchor.vy) < MARQUEE_DRAG_THRESHOLD_PX\n ) {\n return;\n }\n dragging = true;\n anno.marqueePointer(\n 'down',\n anchor.pon,\n anchor.point,\n anchor.shift,\n view.scale,\n view.rotation,\n view.zoom,\n );\n }\n anno.marqueePointer(\n 'move',\n anchor.pon,\n point,\n anchor.shift,\n view.scale,\n view.rotation,\n view.zoom,\n );\n },\n onUp: () => {\n if (dragging && anchor && last) {\n anno.marqueePointer(\n 'up',\n anchor.pon,\n last.point,\n anchor.shift,\n view.scale,\n view.rotation,\n view.zoom,\n );\n }\n anchor = null;\n last = null;\n dragging = false;\n },\n };\n}\n\n/** Drawing: live under `annotation-draw` (the square / circle / line tools). */\nexport function createDrawHandler(\n anno: AnnotationHostCapability,\n interaction: InteractionCapability,\n): InteractionHandler {\n // The active tool id + its ROUTING subtype. The id is what `createPointer` takes\n // (it resolves the defaults preset — arrow vs line); the subtype is what the\n // poly/callout gesture checks read (arrow → `line`, a polygon preset → `polygon`).\n const toolId = () => interaction.activeToolId();\n const subtypeOf = (id: string): Subtype => anno.toolSubtype(id);\n let drawingPoly = false;\n // A callout is mid-creation between its tip/knee/box clicks; while it is, hover\n // (no button) must still drive the leader/box preview, like a poly's vertices.\n let drawingCallout = false;\n // The active drag's home page (down→up): moves/ups resolve against it, so a\n // shape keeps sizing along the page edge when the cursor overshoots.\n let origin: { pon: number; point: Vec } | null = null;\n let pendingInk: {\n tool: string;\n pon: number;\n timer: ReturnType<typeof setTimeout>;\n } | null = null;\n const flushPendingInk = () => {\n if (!pendingInk) return;\n clearTimeout(pendingInk.timer);\n pendingInk = null;\n anno.finishInkDraft();\n };\n interaction.onToolChange(() => {\n flushPendingInk();\n drawingPoly = false;\n drawingCallout = false;\n origin = null;\n });\n return {\n id: 'annotation-draw',\n // The gesture cascade — relative order IS the composition policy:\n // 100 annotation-edit existing annotations own their gestures\n // 95 annotation-place click-to-place armed tools\n // 60 text-select claims TEXT ONLY (self-gates via isOverText,\n // yields everywhere else)\n // 55 annotation-draw drag-create takes whatever text didn't claim\n // 50 annotation-marquee empty-space selection, the final fallback\n // Draw sits BELOW text-select so a COMPOSED tool (redact: text-select +\n // annotation-draw) lets text claim text — over a paragraph the drag makes\n // per-line quad marks, anywhere else it drag-creates. Priority only breaks\n // ties between simultaneously-ELIGIBLE handlers, and draw-only tools\n // (square/ink/arrow…) never co-enable text-select, so they are unaffected.\n priority: 55,\n enabledFor: (t) => t.enables.has('annotation-draw'),\n onDown: (s) => {\n if (!s.page) return false;\n const tool = toolId();\n const st = subtypeOf(tool);\n if (st === 'ink' && pendingInk) {\n if (pendingInk.tool === tool && pendingInk.pon === s.page.pon) {\n clearTimeout(pendingInk.timer);\n pendingInk = null;\n } else {\n flushPendingInk();\n }\n }\n // A down is a fresh intent — it may legitimately start on another page\n // (the core restarts the draft there), so it re-anchors the gesture.\n origin = { pon: s.page.pon, point: s.page.point };\n if (isPolyTool(st)) {\n const finish = (s.clickCount ?? 1) >= 2;\n anno.createPointer(tool, 'down', s.page.pon, s.page.point, finish);\n drawingPoly = !finish;\n return true;\n }\n drawingPoly = false;\n // Each callout click advances the core's tip → knee → box state machine; the\n // final box click/drag commits and clears the draft (so `drawingCallout`\n // resets on the next tool change or simply idles harmlessly).\n if (isCalloutTool(st)) drawingCallout = true;\n // The DOWN sample's display rotation rides along for the tool's `upright`\n // policy; the core captures it on the draft (later phases don't carry it).\n anno.createPointer(tool, 'down', s.page.pon, s.page.point, false, s.page.rotation);\n return true;\n },\n onMove: (s) => {\n const tool = toolId();\n const st = subtypeOf(tool);\n // Drag-moves (button down): rect/line/ink/free-text size their box, and a\n // callout (a non-poly tool) sizes its text box during the box step. Poly\n // tools take vertices by click, so they ignore drag-moves.\n if (!origin || (isPolyTool(st) && !drawingPoly)) return;\n const point = pointOn(s, origin.pon);\n if (!point) return;\n origin.point = point;\n anno.createPointer(tool, 'move', origin.pon, point);\n },\n onUp: (s) => {\n const tool = toolId();\n const st = subtypeOf(tool);\n if (origin && !isPolyTool(st)) {\n // ALWAYS commit the drag, even released off-page (point pins in core).\n anno.createPointer(tool, 'up', origin.pon, pointOn(s, origin.pon) ?? origin.point);\n if (st === 'ink') {\n const groupStrokesMs = anno.tool(tool)?.ink?.groupStrokesMs ?? 0;\n if (groupStrokesMs > 0) {\n const pon = origin.pon;\n const timer = setTimeout(() => {\n pendingInk = null;\n anno.finishInkDraft();\n }, groupStrokesMs);\n pendingInk = { tool, pon, timer };\n }\n }\n }\n origin = null;\n },\n onHover: (s) => {\n const tool = toolId();\n const st = subtypeOf(tool);\n // Hover preview for the multi-click tools: poly (while placing vertices) and\n // callout (while placing the tip/knee/box) follow the cursor between clicks.\n if (s.page && ((drawingPoly && isPolyTool(st)) || (drawingCallout && isCalloutTool(st)))) {\n anno.createPointer(tool, 'move', s.page.pon, s.page.point);\n }\n },\n };\n}\n","/**\n * Text-selection authoring: the bridge that turns a TEXT SELECTION into markup\n * or a text-edit annotation (Insert / Replace). These tools have no gesture of\n * their own — they enable the selection plugin's `text-select` gesture, and this\n * module consumes its typed signals (`onCommit` to create, `onChange` to preview).\n * Selection is the producer; annotation the consumer — selection never knows\n * annotation exists. Kept out of the plugin-definition file so that stays lean.\n */\nimport type { InteractionCapability } from '@embedpdf/plugin-interaction';\nimport type { SelectionCapability } from '@embedpdf/plugin-selection';\nimport type { AnnotationHostCapability } from './types';\n\n/**\n * Wire the selection → annotation BRIDGE. The markup / caret TOOLS and their\n * defaults are registered by the plugin `init` from the tool registry; this\n * function only consumes the selection plugin's typed signals — so call it from\n * `init` only when a selection plugin is present. Selection is the producer,\n * annotation the consumer; selection never knows annotation exists.\n */\nexport function wireMarkup(\n annotation: AnnotationHostCapability,\n selection: SelectionCapability,\n interaction: InteractionCapability,\n): void {\n // Keep the live preview + the selection's own visual in sync with (active tool,\n // selection). While a markup tool is active the blue highlight is suppressed and\n // the in-progress selection renders as a markup ghost instead.\n const sync = () => {\n const tool = annotation.tool(interaction.activeToolId());\n const authoring = tool?.selection;\n const previewSubtype =\n authoring?.kind === 'markup'\n ? tool!.subtype\n : authoring?.kind === 'text-edit' && authoring.operation === 'replace'\n ? 'strikeout'\n : null;\n selection.setHighlightVisible(previewSubtype == null);\n if (previewSubtype && tool && selection.hasSelection()) {\n const quadsByPage: Record<number, ReturnType<typeof selection.segmentsForPage>[number]['quad'][]> =\n {};\n for (const page of selection.snapshot().pages) {\n quadsByPage[page.pon] = page.segments.map((s) => s.quad);\n }\n annotation.previewMarkup(previewSubtype, quadsByPage, tool.preset);\n } else {\n annotation.clearMarkupPreview();\n }\n };\n selection.onChange(sync); // drag-extend → live preview\n interaction.onToolChange(sync); // entering/leaving a markup tool → restore blue / clear ghost\n\n // On gesture-end, if a markup tool is active, turn the selection into markup.\n selection.onCommit(() => {\n const tool = annotation.tool(interaction.activeToolId());\n const authoring = tool?.selection;\n if (!tool || !authoring) return; // pointer tool → leave the selection (copy)\n const snapshot = selection.snapshot();\n if (authoring.kind === 'text-edit' && authoring.operation === 'insert') {\n if (snapshot.end) {\n annotation.createCaret(snapshot.end.pon, {\n glyphQuad: snapshot.end.glyphQuad,\n advance: snapshot.end.advance,\n });\n }\n selection.clear();\n return;\n }\n if (authoring.kind === 'text-edit' && authoring.operation === 'replace') {\n // `/IRT` relationships are page-local, so a cross-page selection becomes\n // one self-contained Caret + StrikeOut pair per page. The true glyph-cell\n // anchor exists only on the selection's end page; other pages anchor at\n // their last segment's trailing edge.\n for (const page of snapshot.pages) {\n const last = page.segments[page.segments.length - 1];\n if (!last) continue;\n const anchor =\n snapshot.end && snapshot.end.pon === page.pon\n ? { glyphQuad: snapshot.end.glyphQuad, advance: snapshot.end.advance }\n : { glyphQuad: last.quad, advance: last.advance };\n annotation.createReplaceText(\n page.pon,\n page.segments.map((s) => s.quad),\n anchor,\n tool.preset,\n );\n }\n selection.clear();\n return;\n }\n for (const page of snapshot.pages) {\n annotation.createMarkup(\n tool.subtype,\n page.pon,\n page.segments.map((s) => s.quad),\n tool.preset,\n );\n }\n selection.clear(); // fires onChange → preview clears; blue stays suppressed (markup tool still active)\n });\n}\n","import { initialModel } from '@embedpdf/core-annotation';\nimport type {\n AnnotationAction,\n AnnotationConfig,\n AnnotationState,\n ChromeSettings,\n ChromeSettingsPatch,\n} from './types';\n\n/**\n * Out-of-the-box selection chrome — a sensible document-annotation feel. Every\n * length is CSS px (screen-constant across zoom); every color falls back to\n * `accent`. Just defaults: override any field at registration\n * (`annotationPlugin({ chrome })`) or at runtime (`setChrome`).\n */\nexport const DEFAULT_CHROME: ChromeSettings = {\n accent: '#3858e9',\n // Solid, like the shape's own resting look — one style at rest AND rotated.\n outline: { style: 'solid', width: 1 },\n // 8px squares to look at, 24px to grab (touch-friendly without visual bulk).\n handles: { size: 8, hitSize: 24, fill: '#ffffff' },\n knob: { size: 10, hitSize: 24, offset: 32, stalk: true, fill: '#ffffff' },\n // Faint reference cross, prominent live indicator (v2 feel).\n guides: { enabled: true, style: 'solid', width: 1, axisOpacity: 0.35, indicatorOpacity: 0.8 },\n};\n\n/** Deep-partial merge of a chrome patch — one level per piece, like the\n * stage-settings convention (align pairs / pageFrame). */\nexport const mergeChrome = (base: ChromeSettings, patch: ChromeSettingsPatch): ChromeSettings => ({\n accent: patch.accent ?? base.accent,\n outline: { ...base.outline, ...patch.outline },\n handles: { ...base.handles, ...patch.handles },\n knob: { ...base.knob, ...patch.knob },\n guides: { ...base.guides, ...patch.guides },\n});\n\n/**\n * The slice holds the annotation-core Model + the selection-chrome settings.\n * The pure `update` runs in the capability (the shell, which also performs\n * effects); the reducer only stores new state — keeping the kernel store a\n * dumb, serializable container. The registration config seeds the model's\n * snap settings and the chrome.\n */\nexport const initialAnnotationState = (config: AnnotationConfig = {}): AnnotationState => ({\n model: { ...initialModel, snap: { ...initialModel.snap, ...config.snap } },\n chrome: mergeChrome(DEFAULT_CHROME, config.chrome ?? {}),\n toolGhost: null,\n stampArmEpoch: 0,\n});\n\nexport const annotationReducer = (\n state: AnnotationState,\n action: AnnotationAction,\n): AnnotationState => {\n switch (action.type) {\n case 'SET_MODEL':\n return { ...state, model: action.model };\n case 'SET_CHROME':\n return { ...state, chrome: mergeChrome(state.chrome, action.patch) };\n case 'SET_TOOL_GHOST':\n return { ...state, toolGhost: action.ghost };\n case 'STAMP_ARM_CHANGED':\n // A new (or dropped) payload invalidates any ghost drawn for the old one.\n return { ...state, stampArmEpoch: state.stampArmEpoch + 1, toolGhost: null };\n default:\n return state;\n }\n};\n","import { definePlugin } from '@embedpdf/core';\nimport { InteractionToken } from '@embedpdf/plugin-interaction';\nimport { SelectionToken } from '@embedpdf/plugin-selection';\nimport { createAnnotationCapability } from './capability';\nimport { registerAnnotationEffects } from './effects';\nimport {\n createDrawHandler,\n createEditHandler,\n createGhostHandler,\n createMarqueeHandler,\n createPlaceHandler,\n} from './handler';\nimport { wireMarkup } from './markup';\nimport { annotationReducer, initialAnnotationState } from './reducer';\nimport { AnnotationToken } from './types';\nimport type {\n AnnotationAction,\n AnnotationConfig,\n AnnotationHostCapability,\n AnnotationState,\n} from './types';\n\n/**\n * The annotation plugin. Document-scoped; requires the interaction hub and\n * OPTIONALLY uses the selection plugin. Shapes/ink work with no selection; text\n * markup lights up only when a selection plugin is present.\n */\nexport const annotationPlugin = (config: AnnotationConfig = {}) =>\n definePlugin<AnnotationState, AnnotationAction, AnnotationHostCapability>({\n id: 'annotation',\n token: AnnotationToken,\n scope: 'document',\n requires: [InteractionToken],\n optional: [SelectionToken],\n initialState: () => initialAnnotationState(config),\n reduce: annotationReducer,\n // The capability owns the resolved tool registry (built-ins + config `tools`),\n // so it needs the config too — not just the reducer state.\n capability: (ctx) => createAnnotationCapability(ctx, config),\n // Fold in remote collaborators' edits (own edits flow through the capability).\n effects: registerAnnotationEffects,\n init: (ctx) => {\n const interaction = ctx.get(InteractionToken);\n const annotation = ctx.get(AnnotationToken);\n const selection = ctx.tryGet(SelectionToken);\n\n // Register every resolved tool (shapes, lines, ink, free-text, markup, stamp,\n // plus anything the embedder added via config `tools`) and seed its defaults.\n // A tool is a named preset over a subtype — see `tools.ts`. Markup / caret\n // tools ride the selection plugin's `text-select` gesture, so they stay\n // inert (skipped) when no selection plugin is installed.\n for (const tool of annotation.tools()) {\n if (tool.enables.has('text-select') && !selection) continue;\n interaction.registerTool({ id: tool.id, cursor: tool.cursor, enables: tool.enables });\n if (tool.defaults) annotation.setDefaults(tool.preset, tool.defaults);\n }\n\n interaction.registerHandler(createPlaceHandler(annotation));\n interaction.registerHandler(createGhostHandler(annotation, interaction));\n interaction.registerHandler(createEditHandler(annotation, interaction));\n interaction.registerHandler(createMarqueeHandler(annotation));\n interaction.registerHandler(createDrawHandler(annotation, interaction));\n interaction.onToolChange(() => {\n annotation.cancel();\n annotation.clearGhost(); // a footprint belongs to the tool that computed it\n // Engagement follows the tool: annotations whose Behavior just engaged\n // (form widgets under a fill tool) drop out of the selection — no\n // stranded chrome on a fill control.\n annotation.pruneEngagedSelection();\n // Leaving the stamp family drops any armed payload — bytes are tool state,\n // not document state (any stamp tool keeps it; a non-stamp tool clears it).\n if (!interaction.activeTool().enables.has('annotation-stamp')) annotation.disarmStamp();\n });\n\n // Markup is opt-in: wire the selection→annotation BRIDGE only when a\n // selection plugin is present (the markup TOOLS were registered above).\n if (selection) wireMarkup(annotation, selection, interaction);\n },\n });\n","/**\n * @embedpdf/plugin-annotation — annotations on the v3 stack.\n *\n * The pure @embedpdf/core-annotation wired to the engine repository (optimistic\n * create/patch/delete) and the interaction hub (ambient editing + draw tools).\n * Behaviors (forms, links) plug in via registerBehavior. Zero framework code.\n */\nimport type { CapabilityToken } from '@embedpdf/core';\nimport { AnnotationToken as AnnotationHostToken } from './types';\nimport type { AnnotationCapability } from './types';\n\nexport { annotationPlugin } from './annotation.plugin';\nexport {\n fromDTO,\n toCreateDraft,\n toPatch,\n refKey,\n styleFromDTO,\n widgetAppearanceFromProps,\n} from './repository';\n// The shared placement layer + the one click↔drag threshold, re-exported so a\n// sibling COMMIT PLANE (the form plugin's place handler) resolves clicks with\n// the exact call the annotation core and the footprint ghost use.\nexport { MIN_DRAG, resolveClickPlacement, type ClickPlacement } from '@embedpdf/core-annotation';\nexport { DEFAULT_CHROME } from './reducer';\nexport { DEFAULT_TOOLS } from './tools';\nexport type {\n AnnotationToolDef,\n AnnotationToolInput,\n GhostPolicy,\n InkAuthoringOptions,\n PromptSourceSpec,\n ResolvedTool,\n SelectionAuthoring,\n StampSourceSpec,\n ToolAuthoringKind,\n ToolDefaultsFor,\n} from './tools';\nexport type {\n AnnotationCapability,\n AnnotationConfig,\n AnnotationState,\n AnnotationAction,\n Behavior,\n ChromeSettings,\n ChromeSettingsPatch,\n LinkNavItem,\n SelectionFlags,\n SelectionProps,\n StampToolInput,\n FilePickerProvider,\n FilePromptRequest,\n TextItem,\n ToolGhost,\n} from './types';\n// The property vocabulary + schema (defined in the portable core; re-exported so\n// app code building property UIs needs only this package).\nexport { propsFor } from '@embedpdf/core-annotation';\nexport type {\n AnnotationFlags,\n AnnotationProps,\n AnnotationPropsPatch,\n BlendMode,\n Border,\n ClickCreate,\n LineEnding,\n LineEndings,\n PropKey,\n PropSpec,\n SnapSettings,\n TextAlign,\n} from '@embedpdf/core-annotation';\n\n/**\n * App-facing annotation token: resolves the public {@link AnnotationCapability}.\n * It is the SAME runtime token the plugin provides, narrowed to the public lens —\n * the framework-only surface (render projection, pointer gestures, behavior\n * registration) is reachable only via `@embedpdf/plugin-annotation/internal`.\n */\nexport const AnnotationToken =\n AnnotationHostToken as unknown as CapabilityToken<AnnotationCapability>;\n"],"mappings":"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"}