@signiphi/pdf-signer 0.2.0-beta.3 → 0.2.0-beta.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (158) hide show
  1. package/README.md +284 -0
  2. package/assets/viewer-annotations.html +1642 -0
  3. package/assets/viewer.html +1 -5
  4. package/dist/__tests__/helpers/mocks.d.ts +78 -0
  5. package/dist/__tests__/helpers/mocks.d.ts.map +1 -1
  6. package/dist/components/AcknowledgementModal.d.ts.map +1 -1
  7. package/dist/components/AnnotationChatPanel.d.ts +43 -0
  8. package/dist/components/AnnotationChatPanel.d.ts.map +1 -0
  9. package/dist/components/AnnotationLayer.d.ts +36 -0
  10. package/dist/components/AnnotationLayer.d.ts.map +1 -0
  11. package/dist/components/AnnotationToolProperties.d.ts +13 -0
  12. package/dist/components/AnnotationToolProperties.d.ts.map +1 -0
  13. package/dist/components/AnnotationToolbar.d.ts +24 -0
  14. package/dist/components/AnnotationToolbar.d.ts.map +1 -0
  15. package/dist/components/AttachmentUpload.d.ts +6 -3
  16. package/dist/components/AttachmentUpload.d.ts.map +1 -1
  17. package/dist/components/DateFieldCalendarPopup.d.ts +28 -0
  18. package/dist/components/DateFieldCalendarPopup.d.ts.map +1 -0
  19. package/dist/components/FormFieldsView.d.ts +6 -0
  20. package/dist/components/FormFieldsView.d.ts.map +1 -1
  21. package/dist/components/PdfDocument.d.ts +69 -0
  22. package/dist/components/PdfDocument.d.ts.map +1 -0
  23. package/dist/components/PdfJsProvider.d.ts +43 -0
  24. package/dist/components/PdfJsProvider.d.ts.map +1 -0
  25. package/dist/components/PdfViewerStyled.d.ts +2 -0
  26. package/dist/components/PdfViewerStyled.d.ts.map +1 -1
  27. package/dist/components/RequiredFieldNavigation.d.ts.map +1 -1
  28. package/dist/components/SignatureModal.d.ts +1 -1
  29. package/dist/components/SignatureModal.d.ts.map +1 -1
  30. package/dist/components/SignatureTypeInput.d.ts +27 -0
  31. package/dist/components/SignatureTypeInput.d.ts.map +1 -0
  32. package/dist/components/SubmissionForm.d.ts +75 -3
  33. package/dist/components/SubmissionForm.d.ts.map +1 -1
  34. package/dist/components/UnacknowledgedFieldsModal.d.ts +3 -1
  35. package/dist/components/UnacknowledgedFieldsModal.d.ts.map +1 -1
  36. package/dist/components/ViewToggleToolbar.d.ts.map +1 -1
  37. package/dist/components/form-fields/CheckboxRenderer.d.ts +2 -1
  38. package/dist/components/form-fields/CheckboxRenderer.d.ts.map +1 -1
  39. package/dist/components/form-fields/DateFieldRenderer.d.ts +2 -1
  40. package/dist/components/form-fields/DateFieldRenderer.d.ts.map +1 -1
  41. package/dist/components/form-fields/DropdownRenderer.d.ts +2 -1
  42. package/dist/components/form-fields/DropdownRenderer.d.ts.map +1 -1
  43. package/dist/components/form-fields/FormFieldRenderer.d.ts +1 -1
  44. package/dist/components/form-fields/FormFieldRenderer.d.ts.map +1 -1
  45. package/dist/components/form-fields/InitialsFieldRenderer.d.ts +2 -1
  46. package/dist/components/form-fields/InitialsFieldRenderer.d.ts.map +1 -1
  47. package/dist/components/form-fields/RadioGroupRenderer.d.ts +2 -1
  48. package/dist/components/form-fields/RadioGroupRenderer.d.ts.map +1 -1
  49. package/dist/components/form-fields/SignatureFieldRenderer.d.ts +2 -1
  50. package/dist/components/form-fields/SignatureFieldRenderer.d.ts.map +1 -1
  51. package/dist/components/form-fields/TextFieldRenderer.d.ts +2 -1
  52. package/dist/components/form-fields/TextFieldRenderer.d.ts.map +1 -1
  53. package/dist/components/index.d.ts +6 -0
  54. package/dist/components/index.d.ts.map +1 -1
  55. package/dist/components/index.js +8531 -1665
  56. package/dist/components/index.js.map +1 -1
  57. package/dist/components/index.mjs +8294 -1436
  58. package/dist/components/index.mjs.map +1 -1
  59. package/dist/core/PdfViewerCore.d.ts +2 -0
  60. package/dist/core/PdfViewerCore.d.ts.map +1 -1
  61. package/dist/core/index.js +791 -72
  62. package/dist/core/index.js.map +1 -1
  63. package/dist/core/index.mjs +792 -73
  64. package/dist/core/index.mjs.map +1 -1
  65. package/dist/hooks/index.d.ts +1 -0
  66. package/dist/hooks/index.d.ts.map +1 -1
  67. package/dist/hooks/index.js +3019 -377
  68. package/dist/hooks/index.js.map +1 -1
  69. package/dist/hooks/index.mjs +3020 -379
  70. package/dist/hooks/index.mjs.map +1 -1
  71. package/dist/hooks/useAcknowledgements.d.ts.map +1 -1
  72. package/dist/hooks/useAnnotationChat.d.ts +90 -0
  73. package/dist/hooks/useAnnotationChat.d.ts.map +1 -0
  74. package/dist/hooks/useAttachments.d.ts +1 -0
  75. package/dist/hooks/useAttachments.d.ts.map +1 -1
  76. package/dist/hooks/useFormFields.d.ts.map +1 -1
  77. package/dist/hooks/usePdfViewer.d.ts +7 -6
  78. package/dist/hooks/usePdfViewer.d.ts.map +1 -1
  79. package/dist/hooks/useRequiredFieldNavigation.d.ts.map +1 -1
  80. package/dist/hooks/useSignatures.d.ts.map +1 -1
  81. package/dist/index.css +675 -171
  82. package/dist/index.css.map +1 -1
  83. package/dist/index.d.ts +4 -4
  84. package/dist/index.d.ts.map +1 -1
  85. package/dist/index.js +8719 -1633
  86. package/dist/index.js.map +1 -1
  87. package/dist/index.mjs +8473 -1422
  88. package/dist/index.mjs.map +1 -1
  89. package/dist/lib/ui/popover.d.ts.map +1 -1
  90. package/dist/lib/ui/select.d.ts.map +1 -1
  91. package/dist/styles/index.css +595 -167
  92. package/dist/styles/index.d.ts +1 -0
  93. package/dist/types/annotations.d.ts +251 -0
  94. package/dist/types/annotations.d.ts.map +1 -0
  95. package/dist/types/index.d.ts +53 -6
  96. package/dist/types/index.d.ts.map +1 -1
  97. package/dist/types/index.js +21 -0
  98. package/dist/types/index.js.map +1 -1
  99. package/dist/types/index.mjs +20 -1
  100. package/dist/types/index.mjs.map +1 -1
  101. package/dist/utils/annotation-appearance.d.ts +85 -0
  102. package/dist/utils/annotation-appearance.d.ts.map +1 -0
  103. package/dist/utils/annotation-bridge.d.ts +214 -0
  104. package/dist/utils/annotation-bridge.d.ts.map +1 -0
  105. package/dist/utils/annotation-export.d.ts +30 -0
  106. package/dist/utils/annotation-export.d.ts.map +1 -0
  107. package/dist/utils/annotation-pdf-save.d.ts +255 -0
  108. package/dist/utils/annotation-pdf-save.d.ts.map +1 -0
  109. package/dist/utils/attachment-validators.d.ts +1 -1
  110. package/dist/utils/date-validation-iso.d.ts +11 -0
  111. package/dist/utils/date-validation-iso.d.ts.map +1 -0
  112. package/dist/utils/date-validation.d.ts +1 -4
  113. package/dist/utils/date-validation.d.ts.map +1 -1
  114. package/dist/utils/field-extraction.d.ts.map +1 -1
  115. package/dist/utils/field-visibility.d.ts +19 -15
  116. package/dist/utils/field-visibility.d.ts.map +1 -1
  117. package/dist/utils/font-loader.d.ts +50 -0
  118. package/dist/utils/font-loader.d.ts.map +1 -0
  119. package/dist/utils/form-flatten-helpers.d.ts +70 -0
  120. package/dist/utils/form-flatten-helpers.d.ts.map +1 -0
  121. package/dist/utils/index.d.ts +6 -0
  122. package/dist/utils/index.d.ts.map +1 -1
  123. package/dist/utils/index.js +2346 -246
  124. package/dist/utils/index.js.map +1 -1
  125. package/dist/utils/index.mjs +2316 -247
  126. package/dist/utils/index.mjs.map +1 -1
  127. package/dist/utils/pdf-field-type-helpers.d.ts +14 -5
  128. package/dist/utils/pdf-field-type-helpers.d.ts.map +1 -1
  129. package/dist/utils/pdf-helpers.d.ts +1 -1
  130. package/dist/utils/pdf-helpers.d.ts.map +1 -1
  131. package/dist/utils/pdf-manipulation.d.ts +2 -1
  132. package/dist/utils/pdf-manipulation.d.ts.map +1 -1
  133. package/dist/utils/pdf-metadata.d.ts +36 -1
  134. package/dist/utils/pdf-metadata.d.ts.map +1 -1
  135. package/dist/utils/pdf-signer-utils.d.ts +35 -0
  136. package/dist/utils/pdf-signer-utils.d.ts.map +1 -0
  137. package/dist/utils/pdf-text-wrapping.d.ts +42 -0
  138. package/dist/utils/pdf-text-wrapping.d.ts.map +1 -0
  139. package/dist/utils/pdf-validators.d.ts +47 -10
  140. package/dist/utils/pdf-validators.d.ts.map +1 -1
  141. package/dist/utils/pdf-viewer-filter.d.ts.map +1 -1
  142. package/dist/utils/pdfjs-config.d.ts +12 -0
  143. package/dist/utils/pdfjs-config.d.ts.map +1 -1
  144. package/dist/utils/pdfjs-version-check.d.ts.map +1 -1
  145. package/dist/utils/performance-monitor.d.ts +1 -1
  146. package/dist/utils/progressive-signing-step.d.ts +84 -0
  147. package/dist/utils/progressive-signing-step.d.ts.map +1 -0
  148. package/dist/utils/review-pdf-derivation.d.ts +75 -0
  149. package/dist/utils/review-pdf-derivation.d.ts.map +1 -0
  150. package/dist/utils/round-radio-appearance.d.ts +54 -0
  151. package/dist/utils/round-radio-appearance.d.ts.map +1 -0
  152. package/dist/utils/text-label-render.d.ts +36 -0
  153. package/dist/utils/text-label-render.d.ts.map +1 -0
  154. package/package.json +8 -4
  155. package/scripts/copy-utils.js +22 -3
  156. package/scripts/setup.js +1 -1
  157. package/src/styles/index.css +63 -5
  158. package/src/styles/index.d.ts +1 -0
package/README.md CHANGED
@@ -7,6 +7,7 @@ Flexible React components for PDF viewing, form filling, and signature capture.
7
7
  - 📄 **PDF Viewing** - Render PDFs with PDF.js viewer
8
8
  - ✍️ **Signature Capture** - Draw or upload signatures
9
9
  - 📝 **Form Filling** - Fill PDF form fields
10
+ - 💬 **Annotation Chat** - Threaded highlights, ink, and free-text with sender/receiver review round-trips
10
11
  - 🎨 **Flexible Styling** - Use headless components or pre-styled components
11
12
  - 📱 **Responsive** - Works on desktop and mobile devices
12
13
  - 🔧 **TypeScript** - Fully typed API
@@ -1138,6 +1139,289 @@ For implementation details, troubleshooting, and advanced topics, see:
1138
1139
 
1139
1140
  ---
1140
1141
 
1142
+ ## Annotation Chat & Review Round-Trips
1143
+
1144
+ `SubmissionForm` can host an interactive annotation panel on top of the PDF
1145
+ viewer. Receivers can highlight, free-draw, drop free-text notes, and reply
1146
+ to threads; senders can review submissions, reply, add their own
1147
+ annotations, and volley the document back. The flow is opt-in and the
1148
+ state is the consumer's responsibility — the form emits change events,
1149
+ the host app persists.
1150
+
1151
+ ### Two roles, one component
1152
+
1153
+ The same `SubmissionForm` renders for both sides of a review round-trip.
1154
+ The role is gated by the **`role`** prop (route-driven, NOT identity-driven):
1155
+
1156
+ | `role` | Sign tab? | Can annotate? | Submit-for-Review? |
1157
+ |--------------|-----------|---------------|--------------------|
1158
+ | `'signer'` | yes | yes | yes (volley back) |
1159
+ | `'reviewer'` | no | yes | yes (volley back) |
1160
+
1161
+ Set `role` per-route in your host app — e.g. the receiver's signing
1162
+ URL renders with `role="signer"`, the sender's review URL renders with
1163
+ `role="reviewer"`. Don't drive it from `currentAuthor.name`; that would
1164
+ couple the role to a display string.
1165
+
1166
+ > **Note:** the deprecated `reviewMode` boolean is still accepted —
1167
+ > `reviewMode={true}` maps to `role="reviewer"`. Prefer `role` in new
1168
+ > code; `reviewMode` will be removed in a future major.
1169
+
1170
+ ### The `pdfUrl` / `pdfUrlForSigning` contract
1171
+
1172
+ Annotations are baked into the PDF on submit (`Submit for Review`). The
1173
+ receiver's *signing* operation must run against a clean copy — otherwise
1174
+ the signed PDF carries the review marks burned into page content.
1175
+
1176
+ - `pdfUrl` — what the user sees in the viewer.
1177
+ - `pdfUrlForSigning` — the original/prepared PDF the final fill runs against.
1178
+
1179
+ When `enableAnnotationChat` or `reviewMode` is true, **always pass both**.
1180
+
1181
+ **Strict contract (signing after review):** when the document actually
1182
+ carries review annotations, signing it requires a clean `pdfUrlForSigning`
1183
+ that is **distinct** from `pdfUrl`. If `pdfUrlForSigning` is
1184
+ missing/empty/whitespace-only, or points at the same file as `pdfUrl` —
1185
+ including trivial variations like a cache-buster query (`file.pdf?v=2`), a
1186
+ trailing slash, or host casing — `handleSubmit` **throws a
1187
+ `ReviewAnnotationSigningError` at submit time** — before any fill runs — so
1188
+ the signed copy can never silently embed the review marks. Correctly
1189
+ configured signing (a distinct, un-baked `pdfUrlForSigning`) bakes the
1190
+ signer's form-field signatures only; review annotations are never written
1191
+ into the signed deliverable. (Separately, a softer render-time warning still
1192
+ logs — and throws in development — whenever annotation features are enabled
1193
+ without `pdfUrlForSigning`, to catch the misconfiguration early.)
1194
+
1195
+ URL comparison is best-effort: it cannot detect two *different* URLs (e.g. a
1196
+ CDN alias on another host) that serve the same baked file. The durable
1197
+ guarantee is content-based — before filling, the actual signing bytes are
1198
+ scanned for baked review markup and signing is refused if any is found
1199
+ (`assertSigningBytesFreeOfReviewMarkup`), independent of how the URLs were
1200
+ wired.
1201
+
1202
+ The guard is also exported standalone for custom submit flows:
1203
+
1204
+ ```ts
1205
+ import { assertSigningSourceIsClean } from '@signiphi/pdf-signer/utils';
1206
+
1207
+ assertSigningSourceIsClean({
1208
+ hasReviewAnnotations, // true if the doc went through review
1209
+ pdfUrl,
1210
+ pdfUrlForSigning,
1211
+ }); // throws ReviewAnnotationSigningError on misconfiguration
1212
+ ```
1213
+
1214
+ ### Round-trip example
1215
+
1216
+ ```tsx
1217
+ import {
1218
+ SubmissionForm,
1219
+ type AnnotationThread,
1220
+ type AnnotationChatConfig,
1221
+ } from '@signiphi/pdf-signer';
1222
+
1223
+ function ReceiverSigningPage({ document, currentUser, savedAnnotations }) {
1224
+ const [annotations, setAnnotations] = useState<AnnotationThread[]>(
1225
+ savedAnnotations ?? []
1226
+ );
1227
+
1228
+ const annotationChatConfig: AnnotationChatConfig = {
1229
+ currentAuthor: {
1230
+ id: currentUser.id,
1231
+ name: currentUser.name,
1232
+ email: currentUser.email,
1233
+ },
1234
+ initialAnnotations: annotations,
1235
+ onAnnotationsChange: setAnnotations,
1236
+ enabledTools: ['highlight', 'freehand_highlight', 'ink', 'freetext'],
1237
+ enableReplies: true,
1238
+ enableResolve: true,
1239
+ };
1240
+
1241
+ return (
1242
+ <SubmissionForm
1243
+ pdfUrl={document.bakedReviewPdfUrl ?? document.preparedPdfUrl}
1244
+ pdfUrlForSigning={document.preparedPdfUrl} // clean source
1245
+ enableAnnotationChat
1246
+ annotationChatConfig={annotationChatConfig}
1247
+ role="signer" // receiver = signer
1248
+ onRequestReview={async (data) => {
1249
+ await api.submitForReview(document.id, data);
1250
+ }}
1251
+ onSubmit={async (data) => {
1252
+ await api.submitSigned(document.id, data);
1253
+ }}
1254
+ // ...other props
1255
+ />
1256
+ );
1257
+ }
1258
+
1259
+ function SenderReviewPage({ document, currentUser, latestAnnotations }) {
1260
+ const [annotations, setAnnotations] = useState<AnnotationThread[]>(
1261
+ latestAnnotations
1262
+ );
1263
+
1264
+ return (
1265
+ <SubmissionForm
1266
+ pdfUrl={document.bakedReviewPdfUrl}
1267
+ pdfUrlForSigning={document.preparedPdfUrl}
1268
+ enableAnnotationChat
1269
+ role="reviewer" // sender = reviewer
1270
+ annotationChatConfig={{
1271
+ currentAuthor: { id: currentUser.id, name: currentUser.name, email: currentUser.email },
1272
+ initialAnnotations: annotations,
1273
+ onAnnotationsChange: setAnnotations,
1274
+ enabledTools: ['highlight', 'ink', 'freetext'],
1275
+ enableReplies: true,
1276
+ }}
1277
+ requestReviewButtonLabel="Send back to receiver"
1278
+ onRequestReview={async (data) => {
1279
+ await api.respondToReview(document.id, data);
1280
+ }}
1281
+ onSubmit={async () => {}} // unused in reviewMode
1282
+ />
1283
+ );
1284
+ }
1285
+ ```
1286
+
1287
+ ### `initialAnnotations` shape
1288
+
1289
+ `AnnotationChatConfig.initialAnnotations` accepts `AnnotationThread[]`.
1290
+ Round-trip the same shape via `onAnnotationsChange`:
1291
+
1292
+ ```ts
1293
+ type AnnotationThread = {
1294
+ annotation: Annotation; // primary annotation (highlight, ink, etc.)
1295
+ replies: AnnotationReply[]; // chat replies, threaded via PDF /IRT entries
1296
+ };
1297
+ ```
1298
+
1299
+ Each `Annotation` carries a `pdfJsSerializedData` blob — populated by the
1300
+ bridge when the editor is created/modified. **Persist that blob with the
1301
+ annotation.** It contains the PDF user-space rect/inkList/quadPoints used
1302
+ when baking. If you store annotations through a non-PDF.js path (e.g.
1303
+ imported JSON without serialized data) `embedAnnotationsInPdf` will skip
1304
+ those threads on save — see "Surfacing dropped annotations" below.
1305
+
1306
+ ### Surfacing dropped annotations
1307
+
1308
+ When the host calls `embedAnnotationsInPdf` directly (e.g. to render a
1309
+ final reviewable PDF), threads missing PDF user-space data are returned
1310
+ in the `skipped` array:
1311
+
1312
+ ```ts
1313
+ import { embedAnnotationsInPdf } from '@signiphi/pdf-signer';
1314
+
1315
+ const { pdfBytes, skipped } = await embedAnnotationsInPdf(rawPdfBytes, threads, {
1316
+ // Either inspect skipped after the fact:
1317
+ onSkipped: (s) => toast.warn(`Dropped ${s.length} annotation(s)`),
1318
+ // ...or hard-fail:
1319
+ // throwOnSkip: true,
1320
+ });
1321
+ ```
1322
+
1323
+ Pre-flight check: every annotation that should be persisted must carry
1324
+ `annotation.pdfJsSerializedData` by the time the user clicks submit.
1325
+
1326
+ ### Stamp (image) tool
1327
+
1328
+ The `'stamp'` tool opens a native file picker. Uploads are validated
1329
+ client-side before they reach PDF.js: only `image/*` MIME types are
1330
+ accepted, and files larger than **10 MB** are rejected. Oversized or
1331
+ non-image selections are dropped silently with a console warning — the
1332
+ consumer doesn't need to gate the picker.
1333
+
1334
+ ### Composable building blocks (greenfield consumers)
1335
+
1336
+ `<SubmissionForm>` is a back-compat preset bundling viewer + signing +
1337
+ annotations. New consumers who want their own UI can compose smaller
1338
+ primitives instead:
1339
+
1340
+ | Component / hook | Purpose |
1341
+ |------------------------|----------------------------------------------------------------------|
1342
+ | `<PdfJsProvider>` | Supplies PDF.js config (worker / viewer paths) to the subtree. |
1343
+ | `<PdfDocument>` | Renders the viewer and exposes its ref + load state via context. |
1344
+ | `<AnnotationLayer>` | Opt-in annotation chat; render-prop API on top of `useAnnotationChat`.|
1345
+ | `usePdfDocument()` | Read the surrounding `<PdfDocument>` context (viewer ref, URLs). |
1346
+ | `usePdfJsConfig()` | Read the active PDF.js config from a provider or the global default. |
1347
+
1348
+ ```tsx
1349
+ import {
1350
+ PdfJsProvider,
1351
+ PdfDocument,
1352
+ AnnotationLayer,
1353
+ AnnotationChatPanel,
1354
+ } from '@signiphi/pdf-signer';
1355
+
1356
+ function MyReviewer({ pdfUrl, originalUrl, currentAuthor, savedAnnotations }) {
1357
+ return (
1358
+ <PdfJsProvider config={{ viewerBasePath: '/static/pdfjs' }}>
1359
+ <PdfDocument pdfUrl={pdfUrl} pdfUrlForSigning={originalUrl} enableAnnotationEditor>
1360
+ <AnnotationLayer
1361
+ role="reviewer"
1362
+ config={{
1363
+ currentAuthor,
1364
+ initialAnnotations: savedAnnotations,
1365
+ onAnnotationsChange: (anns) => persist(anns),
1366
+ }}
1367
+ >
1368
+ {(api) => (
1369
+ <AnnotationChatPanel
1370
+ annotations={api.annotations}
1371
+ activeAnnotationId={api.activeAnnotationId}
1372
+ onAddReply={api.addReply}
1373
+ onResolveThread={api.resolveThread}
1374
+ onUnresolveThread={api.unresolveThread}
1375
+ onSelectAnnotation={api.setActiveAnnotation}
1376
+ onDeleteAnnotation={api.removeAnnotation}
1377
+ currentAuthor={currentAuthor}
1378
+ enabledTools={['highlight', 'ink', 'freetext']}
1379
+ enableReplies
1380
+ enableResolve
1381
+ />
1382
+ )}
1383
+ </AnnotationLayer>
1384
+ </PdfDocument>
1385
+ </PdfJsProvider>
1386
+ );
1387
+ }
1388
+ ```
1389
+
1390
+ For the all-in-one experience (signing + annotations + multi-signer +
1391
+ attachments), continue to use `<SubmissionForm>` — it's the supported
1392
+ preset for the full review/signing flow.
1393
+
1394
+ ---
1395
+
1396
+ ### Headless usage (advanced)
1397
+
1398
+ `useAnnotationChat`, `AnnotationBridge`, and `embedAnnotationsInPdf` are
1399
+ exported as building blocks, but the hook layer ships with one tight
1400
+ coupling that consumers must respect:
1401
+
1402
+ **Iframe contract.** `useAnnotationChat` drives an `AnnotationBridge`
1403
+ that reaches into a PDF.js viewer iframe to deserialize/inject editors.
1404
+ The discovery routine looks for an `<iframe title="PDF Viewer">` in the
1405
+ DOM and a `viewerRef` whose `current.getPDFViewerApplication()` returns
1406
+ the same `PDFViewerApplication` instance running inside that iframe. If
1407
+ you frame your own viewer (custom shell, embedded `<embed>`, etc.) the
1408
+ hook will not find the iframe and the bridge will silently noop.
1409
+
1410
+ If your custom UI cannot satisfy that contract, the supported path is to
1411
+ keep `<SubmissionForm>` (or `<PdfViewerStyled>`) at the framing layer and
1412
+ build your own panel UI on top — they expose `viewerRef` and the
1413
+ underlying `pdfViewerApplication` for that purpose. Truly headless
1414
+ support (custom iframe sources, explicit `iframeRef` prop) is on the
1415
+ roadmap; until then treat `useAnnotationChat` as an implementation detail
1416
+ of the framed components.
1417
+
1418
+ The other building blocks are framework-agnostic:
1419
+ - `embedAnnotationsInPdf(bytes, threads, options?)` — pure function.
1420
+ - `AnnotationBridge` — accepts an `iframeRef` directly; safe to drive
1421
+ from any UI as long as the iframe runs the bundled PDF.js viewer.
1422
+
1423
+ ---
1424
+
1141
1425
  ## Multi-Signer Support
1142
1426
 
1143
1427
  The package supports multi-signer workflows where multiple people sign the same document in sequence. Each signer only sees and fills their assigned fields.