@signiphi/pdf-signer 0.2.0-beta.2 → 0.2.0-beta.21

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 (197) hide show
  1. package/assets/viewer.html +1 -5
  2. package/dist/components/index.js +2746 -901
  3. package/dist/components/index.js.map +1 -1
  4. package/dist/components/index.mjs +2513 -669
  5. package/dist/components/index.mjs.map +1 -1
  6. package/dist/core/index.js +420 -20
  7. package/dist/core/index.js.map +1 -1
  8. package/dist/core/index.mjs +420 -20
  9. package/dist/core/index.mjs.map +1 -1
  10. package/dist/hooks/index.js +506 -211
  11. package/dist/hooks/index.js.map +1 -1
  12. package/dist/hooks/index.mjs +507 -212
  13. package/dist/hooks/index.mjs.map +1 -1
  14. package/dist/index.css +214 -191
  15. package/dist/index.css.map +1 -1
  16. package/dist/index.js +3019 -893
  17. package/dist/index.js.map +1 -1
  18. package/dist/index.mjs +2762 -653
  19. package/dist/index.mjs.map +1 -1
  20. package/dist/styles/index.css +202 -172
  21. package/dist/types/index.js.map +1 -1
  22. package/dist/types/index.mjs.map +1 -1
  23. package/dist/utils/index.js +792 -147
  24. package/dist/utils/index.js.map +1 -1
  25. package/dist/utils/index.mjs +777 -148
  26. package/dist/utils/index.mjs.map +1 -1
  27. package/package.json +2 -2
  28. package/scripts/copy-utils.js +14 -3
  29. package/src/styles/index.css +33 -3
  30. package/dist/__tests__/helpers/fixtures.d.ts +0 -43
  31. package/dist/__tests__/helpers/fixtures.d.ts.map +0 -1
  32. package/dist/__tests__/helpers/mocks.d.ts +0 -333
  33. package/dist/__tests__/helpers/mocks.d.ts.map +0 -1
  34. package/dist/__tests__/setup.d.ts +0 -6
  35. package/dist/__tests__/setup.d.ts.map +0 -1
  36. package/dist/components/AcknowledgementModal.d.ts +0 -21
  37. package/dist/components/AcknowledgementModal.d.ts.map +0 -1
  38. package/dist/components/AcknowledgementsSidebar.d.ts +0 -22
  39. package/dist/components/AcknowledgementsSidebar.d.ts.map +0 -1
  40. package/dist/components/AttachmentUpload.d.ts +0 -17
  41. package/dist/components/AttachmentUpload.d.ts.map +0 -1
  42. package/dist/components/EditableFieldsPanel.d.ts +0 -30
  43. package/dist/components/EditableFieldsPanel.d.ts.map +0 -1
  44. package/dist/components/ErrorBoundary.d.ts +0 -67
  45. package/dist/components/ErrorBoundary.d.ts.map +0 -1
  46. package/dist/components/FormFieldsView.d.ts +0 -46
  47. package/dist/components/FormFieldsView.d.ts.map +0 -1
  48. package/dist/components/InitialsModal.d.ts +0 -16
  49. package/dist/components/InitialsModal.d.ts.map +0 -1
  50. package/dist/components/PdfViewerStyled.d.ts +0 -16
  51. package/dist/components/PdfViewerStyled.d.ts.map +0 -1
  52. package/dist/components/PoweredBySigniphi.d.ts +0 -11
  53. package/dist/components/PoweredBySigniphi.d.ts.map +0 -1
  54. package/dist/components/RequiredFieldNavigation.d.ts +0 -18
  55. package/dist/components/RequiredFieldNavigation.d.ts.map +0 -1
  56. package/dist/components/SignatureCanvas.d.ts +0 -12
  57. package/dist/components/SignatureCanvas.d.ts.map +0 -1
  58. package/dist/components/SignatureInitialsBox.d.ts +0 -25
  59. package/dist/components/SignatureInitialsBox.d.ts.map +0 -1
  60. package/dist/components/SignatureModal.d.ts +0 -21
  61. package/dist/components/SignatureModal.d.ts.map +0 -1
  62. package/dist/components/SigningInstructions.d.ts +0 -12
  63. package/dist/components/SigningInstructions.d.ts.map +0 -1
  64. package/dist/components/SubmissionForm.d.ts +0 -52
  65. package/dist/components/SubmissionForm.d.ts.map +0 -1
  66. package/dist/components/UnacknowledgedFieldsModal.d.ts +0 -23
  67. package/dist/components/UnacknowledgedFieldsModal.d.ts.map +0 -1
  68. package/dist/components/ViewToggleToolbar.d.ts +0 -38
  69. package/dist/components/ViewToggleToolbar.d.ts.map +0 -1
  70. package/dist/components/form-fields/CheckboxRenderer.d.ts +0 -10
  71. package/dist/components/form-fields/CheckboxRenderer.d.ts.map +0 -1
  72. package/dist/components/form-fields/DateFieldRenderer.d.ts +0 -14
  73. package/dist/components/form-fields/DateFieldRenderer.d.ts.map +0 -1
  74. package/dist/components/form-fields/DropdownRenderer.d.ts +0 -14
  75. package/dist/components/form-fields/DropdownRenderer.d.ts.map +0 -1
  76. package/dist/components/form-fields/FormFieldRenderer.d.ts +0 -22
  77. package/dist/components/form-fields/FormFieldRenderer.d.ts.map +0 -1
  78. package/dist/components/form-fields/InitialsFieldRenderer.d.ts +0 -16
  79. package/dist/components/form-fields/InitialsFieldRenderer.d.ts.map +0 -1
  80. package/dist/components/form-fields/RadioGroupRenderer.d.ts +0 -10
  81. package/dist/components/form-fields/RadioGroupRenderer.d.ts.map +0 -1
  82. package/dist/components/form-fields/SignatureFieldRenderer.d.ts +0 -16
  83. package/dist/components/form-fields/SignatureFieldRenderer.d.ts.map +0 -1
  84. package/dist/components/form-fields/TextFieldRenderer.d.ts +0 -14
  85. package/dist/components/form-fields/TextFieldRenderer.d.ts.map +0 -1
  86. package/dist/components/form-fields/TextLabelRenderer.d.ts +0 -14
  87. package/dist/components/form-fields/TextLabelRenderer.d.ts.map +0 -1
  88. package/dist/components/form-fields/index.d.ts +0 -14
  89. package/dist/components/form-fields/index.d.ts.map +0 -1
  90. package/dist/components/index.d.ts +0 -17
  91. package/dist/components/index.d.ts.map +0 -1
  92. package/dist/core/PdfViewerCore.d.ts +0 -19
  93. package/dist/core/PdfViewerCore.d.ts.map +0 -1
  94. package/dist/core/SignatureCaptureCore.d.ts +0 -37
  95. package/dist/core/SignatureCaptureCore.d.ts.map +0 -1
  96. package/dist/core/index.d.ts +0 -3
  97. package/dist/core/index.d.ts.map +0 -1
  98. package/dist/hooks/index.d.ts +0 -9
  99. package/dist/hooks/index.d.ts.map +0 -1
  100. package/dist/hooks/useAcknowledgements.d.ts +0 -50
  101. package/dist/hooks/useAcknowledgements.d.ts.map +0 -1
  102. package/dist/hooks/useAttachments.d.ts +0 -25
  103. package/dist/hooks/useAttachments.d.ts.map +0 -1
  104. package/dist/hooks/useFieldFiltering.d.ts +0 -29
  105. package/dist/hooks/useFieldFiltering.d.ts.map +0 -1
  106. package/dist/hooks/useFormFields.d.ts +0 -23
  107. package/dist/hooks/useFormFields.d.ts.map +0 -1
  108. package/dist/hooks/useMultiSignerContext.d.ts +0 -25
  109. package/dist/hooks/useMultiSignerContext.d.ts.map +0 -1
  110. package/dist/hooks/usePdfViewer.d.ts +0 -52
  111. package/dist/hooks/usePdfViewer.d.ts.map +0 -1
  112. package/dist/hooks/useRequiredFieldNavigation.d.ts +0 -16
  113. package/dist/hooks/useRequiredFieldNavigation.d.ts.map +0 -1
  114. package/dist/hooks/useSignatureCapture.d.ts +0 -17
  115. package/dist/hooks/useSignatureCapture.d.ts.map +0 -1
  116. package/dist/hooks/useSignatures.d.ts +0 -29
  117. package/dist/hooks/useSignatures.d.ts.map +0 -1
  118. package/dist/index.d.ts +0 -17
  119. package/dist/index.d.ts.map +0 -1
  120. package/dist/integrations/index.d.ts +0 -6
  121. package/dist/integrations/index.d.ts.map +0 -1
  122. package/dist/integrations/next-config.d.ts +0 -46
  123. package/dist/integrations/next-config.d.ts.map +0 -1
  124. package/dist/integrations/vite-plugin.d.ts +0 -48
  125. package/dist/integrations/vite-plugin.d.ts.map +0 -1
  126. package/dist/lib/index.d.ts +0 -3
  127. package/dist/lib/index.d.ts.map +0 -1
  128. package/dist/lib/ui/accordion.d.ts +0 -8
  129. package/dist/lib/ui/accordion.d.ts.map +0 -1
  130. package/dist/lib/ui/alert.d.ts +0 -9
  131. package/dist/lib/ui/alert.d.ts.map +0 -1
  132. package/dist/lib/ui/button.d.ts +0 -12
  133. package/dist/lib/ui/button.d.ts.map +0 -1
  134. package/dist/lib/ui/calendar.d.ts +0 -10
  135. package/dist/lib/ui/calendar.d.ts.map +0 -1
  136. package/dist/lib/ui/card.d.ts +0 -9
  137. package/dist/lib/ui/card.d.ts.map +0 -1
  138. package/dist/lib/ui/checkbox.d.ts +0 -5
  139. package/dist/lib/ui/checkbox.d.ts.map +0 -1
  140. package/dist/lib/ui/dialog.d.ts +0 -20
  141. package/dist/lib/ui/dialog.d.ts.map +0 -1
  142. package/dist/lib/ui/index.d.ts +0 -13
  143. package/dist/lib/ui/index.d.ts.map +0 -1
  144. package/dist/lib/ui/input.d.ts +0 -6
  145. package/dist/lib/ui/input.d.ts.map +0 -1
  146. package/dist/lib/ui/label.d.ts +0 -6
  147. package/dist/lib/ui/label.d.ts.map +0 -1
  148. package/dist/lib/ui/popover.d.ts +0 -7
  149. package/dist/lib/ui/popover.d.ts.map +0 -1
  150. package/dist/lib/ui/radio-group.d.ts +0 -6
  151. package/dist/lib/ui/radio-group.d.ts.map +0 -1
  152. package/dist/lib/ui/select.d.ts +0 -14
  153. package/dist/lib/ui/select.d.ts.map +0 -1
  154. package/dist/lib/utils.d.ts +0 -7
  155. package/dist/lib/utils.d.ts.map +0 -1
  156. package/dist/types/index.d.ts +0 -278
  157. package/dist/types/index.d.ts.map +0 -1
  158. package/dist/utils/attachment-validators.d.ts +0 -118
  159. package/dist/utils/attachment-validators.d.ts.map +0 -1
  160. package/dist/utils/audit-trail.d.ts +0 -27
  161. package/dist/utils/audit-trail.d.ts.map +0 -1
  162. package/dist/utils/date-validation.d.ts +0 -30
  163. package/dist/utils/date-validation.d.ts.map +0 -1
  164. package/dist/utils/errors.d.ts +0 -106
  165. package/dist/utils/errors.d.ts.map +0 -1
  166. package/dist/utils/field-extraction.d.ts +0 -36
  167. package/dist/utils/field-extraction.d.ts.map +0 -1
  168. package/dist/utils/field-visibility.d.ts +0 -104
  169. package/dist/utils/field-visibility.d.ts.map +0 -1
  170. package/dist/utils/index.d.ts +0 -18
  171. package/dist/utils/index.d.ts.map +0 -1
  172. package/dist/utils/logger.d.ts +0 -16
  173. package/dist/utils/logger.d.ts.map +0 -1
  174. package/dist/utils/pdf-field-type-helpers.d.ts +0 -78
  175. package/dist/utils/pdf-field-type-helpers.d.ts.map +0 -1
  176. package/dist/utils/pdf-helpers.d.ts +0 -38
  177. package/dist/utils/pdf-helpers.d.ts.map +0 -1
  178. package/dist/utils/pdf-lib-loader.d.ts +0 -45
  179. package/dist/utils/pdf-lib-loader.d.ts.map +0 -1
  180. package/dist/utils/pdf-manipulation.d.ts +0 -93
  181. package/dist/utils/pdf-manipulation.d.ts.map +0 -1
  182. package/dist/utils/pdf-metadata.d.ts +0 -41
  183. package/dist/utils/pdf-metadata.d.ts.map +0 -1
  184. package/dist/utils/pdf-validators.d.ts +0 -149
  185. package/dist/utils/pdf-validators.d.ts.map +0 -1
  186. package/dist/utils/pdf-viewer-filter.d.ts +0 -35
  187. package/dist/utils/pdf-viewer-filter.d.ts.map +0 -1
  188. package/dist/utils/pdf-widget-helpers.d.ts +0 -98
  189. package/dist/utils/pdf-widget-helpers.d.ts.map +0 -1
  190. package/dist/utils/pdfjs-config.d.ts +0 -56
  191. package/dist/utils/pdfjs-config.d.ts.map +0 -1
  192. package/dist/utils/pdfjs-version-check.d.ts +0 -28
  193. package/dist/utils/pdfjs-version-check.d.ts.map +0 -1
  194. package/dist/utils/performance-monitor.d.ts +0 -172
  195. package/dist/utils/performance-monitor.d.ts.map +0 -1
  196. package/dist/utils/tracking.d.ts +0 -89
  197. package/dist/utils/tracking.d.ts.map +0 -1
@@ -77,6 +77,7 @@ function validatePdfBytes(pdfBytes) {
77
77
  }
78
78
  function isAutoGeneratedLabel(label) {
79
79
  if (!label || !label.trim()) return true;
80
+ const trimmed = label.trim();
80
81
  const autoLabels = [
81
82
  "Signature",
82
83
  "Initials",
@@ -87,7 +88,65 @@ function isAutoGeneratedLabel(label) {
87
88
  "Option",
88
89
  "Radio"
89
90
  ];
90
- return autoLabels.includes(label.trim());
91
+ if (autoLabels.includes(trimmed)) return true;
92
+ if (/^\d{10,}$/.test(trimmed)) return true;
93
+ const typeTimestampPattern = new RegExp(
94
+ `^(${autoLabels.join("|")})[\\s_-]?\\d{10,}$`,
95
+ "i"
96
+ );
97
+ if (typeTimestampPattern.test(trimmed)) return true;
98
+ const typeNumberPattern = new RegExp(
99
+ `^(${autoLabels.join("|")})[\\s_-]?\\d{1,3}$`,
100
+ "i"
101
+ );
102
+ if (typeNumberPattern.test(trimmed)) return true;
103
+ const typeWordsJoined = autoLabels.join("|");
104
+ const corruptedPattern = new RegExp(
105
+ `^(${typeWordsJoined})[\\s_-]?\\d{10,}[\\s_-]?(${typeWordsJoined})+`,
106
+ "i"
107
+ );
108
+ if (corruptedPattern.test(trimmed)) return true;
109
+ const repeatedTypePattern = new RegExp(
110
+ `^((${typeWordsJoined})[\\s_-]+)+(${typeWordsJoined})$`,
111
+ "i"
112
+ );
113
+ if (repeatedTypePattern.test(trimmed)) return true;
114
+ return false;
115
+ }
116
+ function hasDrawableLabel(field) {
117
+ if (!field.label || !field.label.trim()) return false;
118
+ if (field.isLabelAutoGenerated) return false;
119
+ if (isAutoGeneratedLabel(field.label)) return false;
120
+ return true;
121
+ }
122
+ function getFieldDisplayName(field) {
123
+ if (field.label && !isAutoGeneratedLabel(field.label)) {
124
+ return field.label;
125
+ }
126
+ const typeNames = {
127
+ "text": "Text field",
128
+ "signature": "Signature",
129
+ "initials": "Initials",
130
+ "date": "Date field",
131
+ "checkbox": "Checkbox",
132
+ "dropdown": "Dropdown",
133
+ "radio": "Radio selection",
134
+ "radiogroup": "Radio selection",
135
+ "text_label": "Text label"
136
+ };
137
+ if (field.type) {
138
+ const typeName = typeNames[field.type.toLowerCase()];
139
+ if (typeName) {
140
+ return typeName;
141
+ }
142
+ }
143
+ if (field.name) {
144
+ let cleaned = field.name.replace(/_?(signature|initials|date)$/i, "").replace(/[_-]\d{10,}$/, "").replace(/[_-]/g, " ").trim();
145
+ if (cleaned && !isAutoGeneratedLabel(cleaned)) {
146
+ return cleaned.replace(/\b\w/g, (l) => l.toUpperCase());
147
+ }
148
+ }
149
+ return "This field";
91
150
  }
92
151
  function validateFieldValues(values) {
93
152
  const errors = [];
@@ -234,18 +293,15 @@ function isFieldVisibleToSigner(field, multiSignerContext) {
234
293
  if (!multiSignerContext.isMultiSigner) {
235
294
  return true;
236
295
  }
237
- const { currentSignerEmail, isPrimarySigner, isFinalSigner } = multiSignerContext;
296
+ const { currentSignerEmail } = multiSignerContext;
238
297
  if (!field.assignedSignerEmail) {
239
- return isFinalSigner;
298
+ return true;
240
299
  }
241
300
  if (field.assignedSignerEmail === currentSignerEmail) {
242
301
  return true;
243
302
  }
244
- if (field.assignedSignerEmail.includes("recipients")) {
245
- return isPrimarySigner;
246
- }
247
- if (field.assignedSignerEmail.includes("signers")) {
248
- return isFinalSigner;
303
+ if (field.assignedSignerEmail === "to-recipients" || field.assignedSignerEmail === "additional-signers") {
304
+ return true;
249
305
  }
250
306
  return false;
251
307
  }
@@ -259,18 +315,15 @@ function shouldFlattenField(field, multiSignerContext) {
259
315
  if (!multiSignerContext.isMultiSigner) {
260
316
  return true;
261
317
  }
262
- const { currentSignerEmail, isPrimarySigner, isFinalSigner } = multiSignerContext;
318
+ const { currentSignerEmail } = multiSignerContext;
263
319
  if (!field.assignedSignerEmail) {
264
- return isFinalSigner;
320
+ return true;
265
321
  }
266
322
  if (field.assignedSignerEmail === currentSignerEmail) {
267
323
  return true;
268
324
  }
269
- if (field.assignedSignerEmail.includes("recipients")) {
270
- return isPrimarySigner;
271
- }
272
- if (field.assignedSignerEmail.includes("signers")) {
273
- return isFinalSigner;
325
+ if (field.assignedSignerEmail === "to-recipients" || field.assignedSignerEmail === "additional-signers") {
326
+ return true;
274
327
  }
275
328
  return false;
276
329
  }
@@ -310,18 +363,23 @@ function findPageIndexWithFallback(pages, pageRef) {
310
363
 
311
364
  // src/utils/pdf-field-type-helpers.ts
312
365
  function detectFieldType(field) {
313
- const typeName = field.constructor.name;
314
- if (typeName === "PDFTextField") {
315
- return "text";
316
- } else if (typeName === "PDFCheckBox") {
366
+ const f = field;
367
+ if (typeof f.check === "function" && typeof f.uncheck === "function") {
317
368
  return "checkbox";
318
- } else if (typeName === "PDFDropdown") {
369
+ }
370
+ if (typeof f.select === "function" && typeof f.getOptions === "function" && typeof f.check !== "function" && typeof f.setOptions !== "function") {
371
+ return "radiogroup";
372
+ }
373
+ if (typeof f.select === "function" && typeof f.getOptions === "function" && (typeof f.setOptions === "function" || typeof f.addOptions === "function")) {
319
374
  return "dropdown";
320
- } else if (typeName === "PDFOptionList") {
375
+ }
376
+ if (typeof f.getOptions === "function" && typeof f.setOptions === "function" && typeof f.select !== "function") {
321
377
  return "optionlist";
322
- } else if (typeName === "PDFRadioGroup") {
323
- return "radiogroup";
324
- } else if (typeName === "PDFSignature") {
378
+ }
379
+ if (typeof f.getText === "function" && typeof f.setText === "function") {
380
+ return "text";
381
+ }
382
+ if (typeof f.getText !== "function" && typeof f.check !== "function" && typeof f.select !== "function") {
325
383
  return "signature";
326
384
  }
327
385
  return "unknown";
@@ -330,13 +388,20 @@ function extractFieldValue(field, fieldType) {
330
388
  try {
331
389
  switch (fieldType) {
332
390
  case "text":
391
+ case "date":
333
392
  return field.getText?.() || "";
334
393
  case "checkbox":
335
394
  return field.isChecked?.() ? "true" : "false";
336
395
  case "dropdown":
337
- case "optionlist":
396
+ case "optionlist": {
397
+ const selected = field.getSelected?.();
398
+ return Array.isArray(selected) ? selected[0] || "" : String(selected || "");
399
+ }
338
400
  case "radiogroup":
339
- return field.getSelected?.()?.[0] || "";
401
+ case "radio": {
402
+ const radioSelected = field.getSelected?.();
403
+ return radioSelected ? String(radioSelected) : "";
404
+ }
340
405
  default:
341
406
  return "";
342
407
  }
@@ -408,12 +473,16 @@ async function validatePdfFormFields(pdfBytes, fieldValues, signatures, extracte
408
473
  const errors = [];
409
474
  for (const field of pdfFormFields) {
410
475
  if (field.required) {
476
+ const extractedField = extractedFields?.find((f) => f.name === field.name);
411
477
  if (multiSignerContext?.isMultiSigner && extractedFields) {
412
- const extractedField = extractedFields.find((f) => f.name === field.name);
413
478
  if (!extractedField) {
414
479
  continue;
415
480
  }
416
481
  }
482
+ const logicalType = extractedField?.type || field.type;
483
+ if (logicalType === "date" || logicalType === "signature" || logicalType === "initials") {
484
+ continue;
485
+ }
417
486
  let hasValue = false;
418
487
  const fieldValue = fieldValues[field.name];
419
488
  const signatureValue = signatures[field.name];
@@ -423,16 +492,17 @@ async function validatePdfFormFields(pdfBytes, fieldValues, signatures, extracte
423
492
  hasValue = !!(signatureValue || fieldValue || mainSignature);
424
493
  } else if (field.name.includes("initials")) {
425
494
  hasValue = !!(signatureValue || fieldValue || mainInitials);
495
+ } else if (field.type === "checkbox" || logicalType === "checkbox") {
496
+ hasValue = fieldValue === "true" || field.value === "true";
426
497
  } else {
427
498
  hasValue = !!(fieldValue || field.value);
428
499
  }
429
500
  if (!hasValue) {
430
- let friendlyName = field.name.replace(/[_-]/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
431
- if (field.name.includes("signature")) {
432
- friendlyName = "Signature";
433
- } else if (field.name.includes("initials")) {
434
- friendlyName = "Initials";
435
- }
501
+ const friendlyName = getFieldDisplayName({
502
+ label: extractedField?.label,
503
+ name: field.name,
504
+ type: logicalType
505
+ });
436
506
  errors.push(`${friendlyName} is required`);
437
507
  }
438
508
  }
@@ -443,7 +513,80 @@ async function validatePdfFormFields(pdfBytes, fieldValues, signatures, extracte
443
513
  return ["Unable to validate form fields. Please ensure all required fields are completed."];
444
514
  }
445
515
  }
446
- async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {}, currentSignerEmail, extractedFormFields, metadata, auditTrail, multiSignerContext) {
516
+ function extractFieldFontSize(fieldName, field, extractedFormFields) {
517
+ const DEFAULT_FONT_SIZE = 10;
518
+ const MIN_FONT_SIZE = 8;
519
+ const MAX_FONT_SIZE = 72;
520
+ const fieldInfo = extractedFormFields?.find((f) => f.name === fieldName);
521
+ if (fieldInfo?.fontSize && fieldInfo.fontSize >= MIN_FONT_SIZE && fieldInfo.fontSize <= MAX_FONT_SIZE) {
522
+ return fieldInfo.fontSize;
523
+ }
524
+ const daFontSize = extractFromDAField(field);
525
+ if (daFontSize !== null) {
526
+ return daFontSize;
527
+ }
528
+ const tuFontSize = extractFromTUField(field);
529
+ if (tuFontSize !== null) {
530
+ return tuFontSize;
531
+ }
532
+ return DEFAULT_FONT_SIZE;
533
+ }
534
+ function extractFromDAField(field) {
535
+ try {
536
+ const fieldWithAcro = field;
537
+ const dict = fieldWithAcro.acroField?.dict;
538
+ if (!dict) return null;
539
+ const daKey = dict.context?.obj?.("DA") || "DA";
540
+ const daEntry = dict.lookup?.(daKey) || dict.get?.(daKey);
541
+ if (!daEntry) return null;
542
+ let daValue = "";
543
+ if (typeof daEntry.decodeText === "function") {
544
+ daValue = daEntry.decodeText();
545
+ } else if (typeof daEntry.asString === "function") {
546
+ daValue = daEntry.asString();
547
+ } else {
548
+ daValue = String(daEntry);
549
+ }
550
+ const daMatch = daValue.match(/\s+(\d+)\s+Tf/i);
551
+ if (daMatch?.[1]) {
552
+ const fontSize = parseInt(daMatch[1], 10);
553
+ if (fontSize >= 8 && fontSize <= 72) {
554
+ return fontSize;
555
+ }
556
+ }
557
+ } catch {
558
+ }
559
+ return null;
560
+ }
561
+ function extractFromTUField(field) {
562
+ try {
563
+ const fieldWithAcro = field;
564
+ const dict = fieldWithAcro.acroField?.dict;
565
+ if (!dict) return null;
566
+ const tuKey = dict.context?.obj?.("TU") || "TU";
567
+ const tuEntry = dict.lookup?.(tuKey) || dict.get?.(tuKey);
568
+ if (!tuEntry) return null;
569
+ let tuValue = "";
570
+ if (typeof tuEntry.decodeText === "function") {
571
+ tuValue = tuEntry.decodeText();
572
+ } else if (typeof tuEntry.asString === "function") {
573
+ tuValue = tuEntry.asString();
574
+ } else {
575
+ tuValue = String(tuEntry);
576
+ }
577
+ tuValue = tuValue.replace(/^\(|\)$|^<|>$/g, "");
578
+ const match = tuValue.match(/\|fontSize:(\d+)$/);
579
+ if (match?.[1]) {
580
+ const fontSize = parseInt(match[1], 10);
581
+ if (fontSize >= 8 && fontSize <= 72) {
582
+ return fontSize;
583
+ }
584
+ }
585
+ } catch {
586
+ }
587
+ return null;
588
+ }
589
+ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {}, _currentSignerEmail, extractedFormFields, metadata, auditTrail, multiSignerContext) {
447
590
  try {
448
591
  const { PDFDocument, rgb, StandardFonts } = await loadPdfLib();
449
592
  const pdfDoc = await PDFDocument.load(pdfBytes);
@@ -460,39 +603,39 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
460
603
  continue;
461
604
  }
462
605
  try {
463
- const fieldTypeName = field.constructor.name;
464
- if (fieldTypeName === "PDFTextField" || fieldTypeName === "PDFTextField2") {
465
- const textField = field;
466
- textField.setText?.(fieldValue);
467
- } else if (fieldTypeName === "PDFCheckBox" || fieldTypeName === "PDFCheckBox2") {
468
- const checkBox = field;
606
+ const f = field;
607
+ const isTextField = typeof f.getText === "function" && typeof f.setText === "function";
608
+ const isCheckbox = typeof f.check === "function" && typeof f.uncheck === "function";
609
+ const isRadio = typeof f.select === "function" && typeof f.getOptions === "function" && typeof f.check !== "function" && typeof f.setOptions !== "function";
610
+ const isDropdown = typeof f.select === "function" && (typeof f.setOptions === "function" || typeof f.addOptions === "function");
611
+ if (isTextField) {
612
+ f.setText?.(fieldValue);
613
+ } else if (isCheckbox) {
469
614
  if (fieldValue === "true" || fieldValue === "Yes" || fieldValue === "checked" || fieldValue === "On") {
470
- checkBox.check?.();
615
+ f.check?.();
471
616
  } else {
472
- checkBox.uncheck?.();
617
+ f.uncheck?.();
473
618
  }
474
- } else if (fieldTypeName === "PDFRadioGroup" || fieldTypeName === "PDFRadioGroup2") {
475
- const radioGroup = field;
619
+ } else if (isRadio) {
476
620
  if (fieldValue === "true" || fieldValue === "false") {
477
621
  continue;
478
622
  }
479
623
  const idxMatch = fieldValue.match(/__RADIO_OPTION_INDEX_(\d+)__/);
480
624
  if (idxMatch && idxMatch[1]) {
481
625
  const selectedIndex = parseInt(idxMatch[1], 10);
482
- const options = radioGroup.getOptions?.() || [];
626
+ const options = f.getOptions?.() || [];
483
627
  if (selectedIndex >= 0 && selectedIndex < options.length) {
484
- radioGroup.select?.(options[selectedIndex]);
628
+ f.select?.(options[selectedIndex]);
485
629
  }
486
630
  } else {
487
- const options = radioGroup.getOptions?.() || [];
631
+ const options = f.getOptions?.() || [];
488
632
  if (options.includes(fieldValue)) {
489
- radioGroup.select?.(fieldValue);
633
+ f.select?.(fieldValue);
490
634
  } else {
491
635
  }
492
636
  }
493
- } else if (fieldTypeName === "PDFDropdown" || fieldTypeName === "PDFDropdown2") {
494
- const dropdown = field;
495
- dropdown.select?.(fieldValue);
637
+ } else if (isDropdown) {
638
+ f.select?.(fieldValue);
496
639
  }
497
640
  } catch (fieldError) {
498
641
  logger.error(`Error setting field "${fieldName}":`, fieldError);
@@ -500,11 +643,6 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
500
643
  }
501
644
  try {
502
645
  form.updateFieldAppearances();
503
- const PDFName3 = pdfLib.PDFName;
504
- const acroForm = pdfDoc.catalog.lookup(PDFName3.of("AcroForm"));
505
- if (acroForm && typeof acroForm === "object" && "set" in acroForm) {
506
- acroForm.set(PDFName3.of("NeedAppearances"), false);
507
- }
508
646
  } catch (appearanceError) {
509
647
  logger.warn("Could not update field appearances:", appearanceError);
510
648
  }
@@ -573,6 +711,20 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
573
711
  const y = fieldPosition.y;
574
712
  const width = Math.max(fieldPosition.width, 80);
575
713
  const height = Math.max(fieldPosition.height, 30);
714
+ const fieldInfo = extractedFormFields?.find((f) => f.name === fieldName);
715
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
716
+ const labelFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
717
+ const labelFontSize = Math.min(10, height * 0.4);
718
+ const labelX = x;
719
+ const labelY = y + height + 5;
720
+ page.drawText(fieldInfo.label, {
721
+ x: labelX,
722
+ y: labelY,
723
+ size: labelFontSize,
724
+ font: labelFont,
725
+ color: rgb(0, 0, 0)
726
+ });
727
+ }
576
728
  const signatureDims = signatureImage.scaleToFit(width - 4, height - 4);
577
729
  const finalX = x;
578
730
  const finalY = y;
@@ -607,7 +759,7 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
607
759
  console.log("[FLATTEN] After pattern matching in fieldPageMap:", foundInitialsFields);
608
760
  if (extractedFormFields && extractedFormFields.length > 0) {
609
761
  foundInitialsFields = foundInitialsFields.filter((fieldName) => {
610
- const fieldInfo = extractedFormFields.find((f) => f.name === fieldName);
762
+ const fieldInfo = extractedFormFields.find((f) => f.name === fieldName) || extractedFormFields.find((f) => f.name === fieldName.replace(/_initials$/i, "")) || extractedFormFields.find((f) => fieldName.startsWith(f.name));
611
763
  if (!fieldInfo) return false;
612
764
  const isActualInitialsField = fieldInfo.type === "initials" || fieldName.toLowerCase().includes("initials");
613
765
  if (!isActualInitialsField) return false;
@@ -636,6 +788,19 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
636
788
  const y = fieldPosition.y;
637
789
  const height = Math.max(fieldPosition.height, 20);
638
790
  const fontSize = Math.min(height * 0.7, 14);
791
+ const fieldInfo = extractedFormFields?.find((f) => f.name === fieldName) || extractedFormFields?.find((f) => f.name === fieldName.replace(/_initials$/i, "")) || extractedFormFields?.find((f) => fieldName.startsWith(f.name)) || extractedFormFields?.find((f) => f.fieldId && fieldName.includes(f.fieldId)) || extractedFormFields?.find((f) => f.type === "initials" && f.name !== "initials_field_main");
792
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
793
+ const labelFontSize = Math.min(10, height * 0.4);
794
+ const labelX = x;
795
+ const labelY = y + height + 5;
796
+ page.drawText(fieldInfo.label, {
797
+ x: labelX,
798
+ y: labelY,
799
+ size: labelFontSize,
800
+ font,
801
+ color: rgb(0, 0, 0)
802
+ });
803
+ }
639
804
  const finalX = x + 2;
640
805
  const finalY = y + (height - fontSize) / 2;
641
806
  console.log("[FLATTEN] \u2705 Drawing initials at:", { x: finalX, y: finalY, fontSize, text: mainInitialsData });
@@ -678,11 +843,16 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
678
843
  let flattenedCount = 0;
679
844
  for (const field of fieldsToFlatten) {
680
845
  const fieldName = field.getName();
681
- const fieldType = field.constructor.name;
846
+ const f = field;
847
+ const isCheckboxField = typeof f.check === "function" && typeof f.uncheck === "function";
848
+ const isRadioField = typeof f.select === "function" && typeof f.getOptions === "function" && typeof f.check !== "function" && typeof f.setOptions !== "function";
849
+ const isDropdownField = typeof f.select === "function" && (typeof f.setOptions === "function" || typeof f.addOptions === "function");
850
+ const isTextField = typeof f.getText === "function" && typeof f.setText === "function";
851
+ const isSignatureType = !isCheckboxField && !isRadioField && !isDropdownField && !isTextField;
682
852
  try {
683
853
  const userEnteredValue = formFieldValues[fieldName];
684
- const isSignatureField = fieldName.toLowerCase().includes("signature") || fieldType.includes("Signature");
685
- const isInitialsField = fieldName.toLowerCase().includes("initials") || fieldType.includes("Initials");
854
+ const isSignatureField = fieldName.toLowerCase().includes("signature") || isSignatureType;
855
+ const isInitialsField = fieldName.toLowerCase().includes("initials");
686
856
  if (isSignatureField || isInitialsField) {
687
857
  form.removeField(field);
688
858
  flattenedCount++;
@@ -690,15 +860,29 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
690
860
  }
691
861
  const fieldWithWidgets = field;
692
862
  const widgets = fieldWithWidgets.acroField?.getWidgets?.() || [];
693
- if (fieldType === "PDFCheckBox" || fieldType === "PDFCheckBox2") {
863
+ if (isCheckboxField) {
694
864
  const stringValue = String(userEnteredValue || "");
695
865
  const isChecked = stringValue === "true" || stringValue === "Yes" || stringValue === "On" || stringValue === "checked";
866
+ const fieldInfo = extractedFormFields?.find((f2) => f2.name === fieldName);
867
+ const labelFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
696
868
  if (isChecked) {
697
869
  for (const widget of widgets) {
698
870
  const result = getWidgetRectangleAndPage(widget, pages);
699
871
  if (!result) continue;
700
872
  const { rect, page } = result;
701
- const checkboxSize = Math.min(rect.width, rect.height) * 0.6;
873
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
874
+ const labelFontSize = Math.min(10, rect.height * 0.4);
875
+ const labelX = rect.x + rect.width + 5;
876
+ const labelY = rect.y + (rect.height - labelFontSize) / 2;
877
+ page.drawText(fieldInfo.label, {
878
+ x: labelX,
879
+ y: labelY,
880
+ size: labelFontSize,
881
+ font: labelFont,
882
+ color: rgb(0, 0, 0)
883
+ });
884
+ }
885
+ const checkboxSize = Math.min(rect.width, rect.height);
702
886
  const checkboxX = rect.x + (rect.width - checkboxSize) / 2;
703
887
  const checkboxY = rect.y + (rect.height - checkboxSize) / 2;
704
888
  page.drawRectangle({
@@ -709,21 +893,51 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
709
893
  borderColor: rgb(0, 0, 0),
710
894
  borderWidth: 1
711
895
  });
712
- const checkSize = checkboxSize * 0.6;
713
- const textWidth = checkSize * 0.6;
714
- const textHeight = checkSize * 0.8;
896
+ const checkFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
897
+ const checkFontSize = checkboxSize * 0.7;
898
+ const textWidth = checkFont.widthOfTextAtSize("X", checkFontSize);
899
+ const textHeight = checkFont.heightAtSize(checkFontSize);
715
900
  page.drawText("X", {
716
901
  x: checkboxX + (checkboxSize - textWidth) / 2,
717
- y: checkboxY + (checkboxSize - textHeight) / 2 + textHeight * 0.2,
718
- size: checkSize,
719
- font: await pdfDoc.embedFont(StandardFonts.HelveticaBold),
902
+ y: checkboxY + (checkboxSize - textHeight) / 2 + textHeight * 0.15,
903
+ size: checkFontSize,
904
+ font: checkFont,
720
905
  color: rgb(0, 0, 0)
721
906
  });
722
907
  }
908
+ } else {
909
+ for (const widget of widgets) {
910
+ const result = getWidgetRectangleAndPage(widget, pages);
911
+ if (!result) continue;
912
+ const { rect, page } = result;
913
+ const checkboxSize = Math.min(rect.width, rect.height);
914
+ const checkboxX = rect.x + (rect.width - checkboxSize) / 2;
915
+ const checkboxY = rect.y + (rect.height - checkboxSize) / 2;
916
+ page.drawRectangle({
917
+ x: checkboxX,
918
+ y: checkboxY,
919
+ width: checkboxSize,
920
+ height: checkboxSize,
921
+ borderColor: rgb(0, 0, 0),
922
+ borderWidth: 1
923
+ });
924
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
925
+ const labelFontSize = Math.min(12, rect.height * 0.6);
926
+ const labelX = rect.x + rect.width + 5;
927
+ const labelY = rect.y + (rect.height - labelFontSize) / 2;
928
+ page.drawText(fieldInfo.label, {
929
+ x: labelX,
930
+ y: labelY,
931
+ size: labelFontSize,
932
+ font: labelFont,
933
+ color: rgb(0, 0, 0)
934
+ });
935
+ }
936
+ }
723
937
  }
724
- } else if (fieldType === "PDFRadioGroup" || fieldType === "PDFRadioGroup2") {
725
- const radioGroup = field;
726
- const fieldInfo = extractedFormFields?.find((f) => f.name === fieldName);
938
+ } else if (isRadioField) {
939
+ const radioGroup = f;
940
+ const fieldInfo = extractedFormFields?.find((f2) => f2.name === fieldName);
727
941
  let actualSelectedValue = "";
728
942
  try {
729
943
  actualSelectedValue = radioGroup.getSelected?.() || "";
@@ -768,14 +982,15 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
768
982
  const result = getWidgetRectangleAndPage(widget, pages);
769
983
  if (!result) continue;
770
984
  const { rect, page, pageIndex } = result;
771
- if (i === 0 && fieldInfo?.label && fieldInfo.label.trim() && !isAutoGeneratedLabel(fieldInfo.label)) {
985
+ if (i === 0 && fieldInfo && hasDrawableLabel(fieldInfo)) {
772
986
  const groupLabelKey = `${pageIndex}-${fieldName}-grouplabel`;
773
987
  if (!drawnGroupLabels.has(groupLabelKey)) {
988
+ const labelFontSize = Math.min(10, rect.height * 0.4);
774
989
  const labelY = rect.y + rect.height + 3;
775
990
  page.drawText(fieldInfo.label, {
776
991
  x: rect.x,
777
992
  y: labelY,
778
- size: 8,
993
+ size: labelFontSize,
779
994
  font: labelFont,
780
995
  color: rgb(0, 0, 0)
781
996
  });
@@ -783,22 +998,21 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
783
998
  }
784
999
  } else if (i === 0 && isAutoGeneratedLabel(fieldInfo?.label || "")) {
785
1000
  }
786
- const radioSize = Math.min(rect.width, rect.height) * 0.5;
787
- const radioX = rect.x + (rect.width - radioSize) / 2;
788
- const radioY = rect.y + (rect.height - radioSize) / 2;
1001
+ const radioSize = Math.min(rect.width, rect.height);
1002
+ const centerX = rect.x + rect.width / 2;
1003
+ const centerY = rect.y + rect.height / 2;
789
1004
  page.drawCircle({
790
- x: radioX + radioSize / 2,
791
- y: radioY + radioSize / 2,
792
- size: radioSize,
1005
+ x: centerX,
1006
+ y: centerY,
1007
+ size: radioSize / 2,
793
1008
  borderColor: rgb(0, 0, 0),
794
1009
  borderWidth: 1
795
1010
  });
796
1011
  if (i === selectedIndex) {
797
- const circleSize = radioSize * 0.5;
798
1012
  page.drawCircle({
799
- x: radioX + radioSize / 2,
800
- y: radioY + radioSize / 2,
801
- size: circleSize,
1013
+ x: centerX,
1014
+ y: centerY,
1015
+ size: radioSize / 4,
802
1016
  color: rgb(0, 0, 0)
803
1017
  });
804
1018
  }
@@ -808,8 +1022,8 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
808
1022
  if (optionText && !drawnOptionLabels.has(optionKey)) {
809
1023
  page.drawText(optionText, {
810
1024
  x: rect.x + rect.width + 4,
811
- y: rect.y + (rect.height - 8) / 2,
812
- size: 8,
1025
+ y: rect.y + (rect.height - 7) / 2,
1026
+ size: 7,
813
1027
  font: labelFont,
814
1028
  color: rgb(0, 0, 0)
815
1029
  });
@@ -819,17 +1033,31 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
819
1033
  }
820
1034
  if (fieldInfo?.options) {
821
1035
  }
822
- } else if (fieldType === "PDFDropdown" || fieldType === "PDFDropdown2") {
1036
+ } else if (isDropdownField) {
823
1037
  const selectedValue = userEnteredValue ? String(userEnteredValue) : "";
824
- if (selectedValue && selectedValue.trim()) {
825
- for (const widget of widgets) {
826
- const result = getWidgetRectangleAndPage(widget, pages);
827
- if (!result) continue;
828
- const { rect, page } = result;
1038
+ const fieldInfo = extractedFormFields?.find((f2) => f2.name === fieldName);
1039
+ const labelFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
1040
+ for (const widget of widgets) {
1041
+ const result = getWidgetRectangleAndPage(widget, pages);
1042
+ if (!result) continue;
1043
+ const { rect, page } = result;
1044
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
1045
+ const labelFontSize = Math.min(10, rect.height * 0.4);
1046
+ const labelX = rect.x;
1047
+ const labelY = rect.y + rect.height + 5;
1048
+ page.drawText(fieldInfo.label, {
1049
+ x: labelX,
1050
+ y: labelY,
1051
+ size: labelFontSize,
1052
+ font: labelFont,
1053
+ color: rgb(0, 0, 0)
1054
+ });
1055
+ }
1056
+ if (selectedValue && selectedValue.trim()) {
829
1057
  page.drawText(selectedValue, {
830
1058
  x: rect.x + 2,
831
1059
  y: rect.y + 2,
832
- size: 10,
1060
+ size: 14,
833
1061
  font: await pdfDoc.embedFont(StandardFonts.Helvetica),
834
1062
  color: rgb(0, 0, 0)
835
1063
  });
@@ -837,15 +1065,31 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
837
1065
  }
838
1066
  } else {
839
1067
  const fieldValue = userEnteredValue ? String(userEnteredValue) : "";
840
- if (fieldValue && fieldValue.trim()) {
841
- for (const widget of widgets) {
842
- const result = getWidgetRectangleAndPage(widget, pages);
843
- if (!result) continue;
844
- const { rect, page } = result;
1068
+ const fieldInfo = extractedFormFields?.find((f2) => f2.name === fieldName);
1069
+ const labelFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
1070
+ const isDateField = fieldName.toLowerCase().includes("date") || fieldName.toLowerCase().includes("_date");
1071
+ const fontSize = isDateField ? 14 : extractFieldFontSize(fieldName, field, extractedFormFields);
1072
+ for (const widget of widgets) {
1073
+ const result = getWidgetRectangleAndPage(widget, pages);
1074
+ if (!result) continue;
1075
+ const { rect, page } = result;
1076
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
1077
+ const labelFontSize = Math.min(10, rect.height * 0.4);
1078
+ const labelX = rect.x;
1079
+ const labelY = rect.y + rect.height + 5;
1080
+ page.drawText(fieldInfo.label, {
1081
+ x: labelX,
1082
+ y: labelY,
1083
+ size: labelFontSize,
1084
+ font: labelFont,
1085
+ color: rgb(0, 0, 0)
1086
+ });
1087
+ }
1088
+ if (fieldValue && fieldValue.trim()) {
845
1089
  page.drawText(fieldValue, {
846
1090
  x: rect.x + 2,
847
1091
  y: rect.y + 2,
848
- size: 10,
1092
+ size: fontSize,
849
1093
  font: await pdfDoc.embedFont(StandardFonts.Helvetica),
850
1094
  color: rgb(0, 0, 0)
851
1095
  });
@@ -1003,7 +1247,6 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1003
1247
  const fields = form.getFields();
1004
1248
  const visibleFields = [];
1005
1249
  let hasAnyInitialsFields = false;
1006
- let hasInitialsFieldsForCurrentSigner = false;
1007
1250
  for (const field of fields) {
1008
1251
  const fieldName = field.getName();
1009
1252
  const fieldWithExtensions = field;
@@ -1029,28 +1272,31 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1029
1272
  };
1030
1273
  })();
1031
1274
  let fieldType = "text" /* TEXT */;
1032
- const fieldTypeName = field.constructor.name;
1275
+ const f = field;
1276
+ const isCheckboxField = typeof f.check === "function" && typeof f.uncheck === "function";
1277
+ const isRadioField = typeof f.select === "function" && typeof f.getOptions === "function" && typeof f.check !== "function" && typeof f.setOptions !== "function";
1278
+ const isDropdownField = typeof f.select === "function" && (typeof f.setOptions === "function" || typeof f.addOptions === "function");
1279
+ const isTextField = typeof f.getText === "function" && typeof f.setText === "function";
1280
+ const isSignatureType = !isCheckboxField && !isRadioField && !isDropdownField && !isTextField;
1033
1281
  const cleanFieldName = fieldName.replace(/_signature$|_initials$|_date$/i, "");
1034
- const isActualSignatureField = fieldTypeName.includes("Signature") || cleanFieldName.toLowerCase().includes("signature") && !fieldTypeName.includes("CheckBox") && !fieldTypeName.includes("Radio");
1035
- const isActualInitialsField = cleanFieldName.toLowerCase().includes("initials") && !fieldTypeName.includes("CheckBox") && !fieldTypeName.includes("Radio");
1282
+ const isActualSignatureField = isSignatureType || cleanFieldName.toLowerCase().includes("signature") && !isCheckboxField && !isRadioField;
1283
+ const isActualInitialsField = cleanFieldName.toLowerCase().includes("initials") && !isCheckboxField && !isRadioField;
1036
1284
  if (isActualSignatureField) {
1037
1285
  fieldType = "signature" /* SIGNATURE */;
1038
1286
  } else if (isActualInitialsField) {
1039
1287
  fieldType = "initials" /* INITIALS */;
1040
1288
  hasAnyInitialsFields = true;
1041
- if (currentSignerEmail && fieldMetadata.signer === currentSignerEmail) {
1042
- hasInitialsFieldsForCurrentSigner = true;
1043
- }
1044
1289
  } else if (cleanFieldName.toLowerCase().includes("date")) {
1045
1290
  fieldType = "date" /* DATE */;
1046
- } else if (fieldTypeName.includes("CheckBox")) {
1291
+ } else if (isCheckboxField) {
1047
1292
  fieldType = "checkbox" /* CHECKBOX */;
1048
- } else if (fieldTypeName.toLowerCase().includes("dropdown")) {
1293
+ } else if (isDropdownField) {
1049
1294
  fieldType = "dropdown" /* DROPDOWN */;
1050
- } else if (fieldTypeName.includes("Radio")) {
1295
+ } else if (isRadioField) {
1051
1296
  fieldType = "radio" /* RADIO */;
1052
1297
  }
1053
1298
  let displayLabel = fieldMetadata.label || "";
1299
+ let isLabelAutoGenerated = false;
1054
1300
  if (!displayLabel || !displayLabel.trim()) {
1055
1301
  try {
1056
1302
  const tuLabel = extractTULabel(fieldWithExtensions);
@@ -1062,6 +1308,7 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1062
1308
  }
1063
1309
  if (!displayLabel || !displayLabel.trim()) {
1064
1310
  displayLabel = generateFallbackLabel(cleanFieldName, fieldType);
1311
+ isLabelAutoGenerated = true;
1065
1312
  }
1066
1313
  const esignField = {
1067
1314
  id: fieldName,
@@ -1073,6 +1320,8 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1073
1320
  type: fieldType,
1074
1321
  label: displayLabel,
1075
1322
  // Use friendly label for display
1323
+ isLabelAutoGenerated,
1324
+ // True when label was generated from field name (should not be drawn on PDF)
1076
1325
  position: { x: 0, y: 0, width: 100, height: 30, page: 1 },
1077
1326
  // Default position
1078
1327
  required: fieldWithExtensions.isRequired?.() ?? false,
@@ -1093,6 +1342,13 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1093
1342
  logger.warn("Error extracting options for field:", error);
1094
1343
  }
1095
1344
  }
1345
+ try {
1346
+ const currentValue = extractFieldValue(f, esignField.type);
1347
+ if (currentValue) {
1348
+ esignField.defaultValue = currentValue;
1349
+ }
1350
+ } catch {
1351
+ }
1096
1352
  visibleFields.push(esignField);
1097
1353
  }
1098
1354
  }
@@ -1109,11 +1365,8 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1109
1365
  placeholder: "Draw or upload your signature",
1110
1366
  assignedSignerEmail: currentSignerEmail
1111
1367
  });
1112
- const shouldCreateInitialsField = hasInitialsFieldsForCurrentSigner || hasAnyInitialsFields && !currentSignerEmail;
1368
+ const shouldCreateInitialsField = hasAnyInitialsFields;
1113
1369
  if (shouldCreateInitialsField) {
1114
- if (hasInitialsFieldsForCurrentSigner) {
1115
- } else {
1116
- }
1117
1370
  mainFields.push({
1118
1371
  id: "initials_field_main",
1119
1372
  fieldId: "initials_field_main",
@@ -1200,8 +1453,12 @@ function decodeFieldName(fieldName) {
1200
1453
  }
1201
1454
  function generateFallbackLabel(decodedFieldName, fieldType) {
1202
1455
  let displayLabel = decodedFieldName;
1203
- displayLabel = displayLabel.replace(/_signature$/i, "").replace(/_initials$/i, "").replace(/_date$/i, "");
1204
- if (/^(text|signature|initials|date)_\d+$/i.test(displayLabel)) {
1456
+ let prevLabel = "";
1457
+ while (displayLabel !== prevLabel) {
1458
+ prevLabel = displayLabel;
1459
+ displayLabel = displayLabel.replace(/_signature$/i, "").replace(/_initials$/i, "").replace(/_date$/i, "");
1460
+ }
1461
+ if (/^(text|signature|initials|date|checkbox|radio|dropdown)_\d+/i.test(displayLabel)) {
1205
1462
  if (fieldType === "signature" /* SIGNATURE */) return "Signature";
1206
1463
  if (fieldType === "initials" /* INITIALS */) return "Initials";
1207
1464
  if (fieldType === "date" /* DATE */) return "Date";
@@ -1209,9 +1466,9 @@ function generateFallbackLabel(decodedFieldName, fieldType) {
1209
1466
  if (fieldType === "checkbox" /* CHECKBOX */) return "Checkbox";
1210
1467
  if (fieldType === "dropdown" /* DROPDOWN */) return "Dropdown";
1211
1468
  if (fieldType === "radio" /* RADIO */) return "Option";
1212
- } else {
1213
- displayLabel = displayLabel.replace(/_/g, " ").replace(/\s+\d+$/g, "").trim().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
1469
+ if (fieldType === "text_label" /* TEXT_LABEL */) return "Text Label";
1214
1470
  }
1471
+ displayLabel = displayLabel.replace(/_/g, " ").replace(/\s+\d+$/g, "").trim().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
1215
1472
  return displayLabel;
1216
1473
  }
1217
1474
  function extractTULabel(field) {
@@ -1306,13 +1563,13 @@ var PdfProcessingError = class _PdfProcessingError extends Error {
1306
1563
 
1307
1564
  // src/utils/attachment-validators.ts
1308
1565
  var DEFAULT_ATTACHMENT_CONSTRAINTS = {
1309
- maxFileSize: 10 * 1024 * 1024,
1310
- // 10MB
1566
+ maxFileSize: 25 * 1024 * 1024,
1567
+ // 25MB
1311
1568
  maxTotalSize: 50 * 1024 * 1024,
1312
1569
  // 50MB
1313
1570
  maxFiles: 10,
1314
1571
  allowedTypes: ["image/*", "application/pdf", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"],
1315
- allowedExtensions: [".pdf", ".jpg", ".jpeg", ".png", ".gif", ".doc", ".docx"]
1572
+ allowedExtensions: [".pdf", ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".doc", ".docx"]
1316
1573
  };
1317
1574
  function validateFile(file, constraints = DEFAULT_ATTACHMENT_CONSTRAINTS) {
1318
1575
  const errors = [];
@@ -1376,6 +1633,35 @@ function isValidISODate(value) {
1376
1633
  }
1377
1634
 
1378
1635
  // src/utils/pdf-viewer-filter.ts
1636
+ function robustlyRemoveField(form, field) {
1637
+ try {
1638
+ const fieldWithAcro = field;
1639
+ if (fieldWithAcro.acroField && typeof fieldWithAcro.acroField.getWidgets === "function") {
1640
+ let widgets = fieldWithAcro.acroField.getWidgets() || [];
1641
+ let safetyCounter = 0;
1642
+ const maxIterations = widgets.length + 5;
1643
+ while (widgets.length > 0 && safetyCounter < maxIterations) {
1644
+ try {
1645
+ const widgetIndex = widgets.length - 1;
1646
+ fieldWithAcro.acroField.removeWidget?.(widgetIndex);
1647
+ widgets = fieldWithAcro.acroField.getWidgets() || [];
1648
+ } catch {
1649
+ break;
1650
+ }
1651
+ safetyCounter++;
1652
+ }
1653
+ }
1654
+ form.removeField(field);
1655
+ return true;
1656
+ } catch (error) {
1657
+ try {
1658
+ form.removeField(field);
1659
+ return true;
1660
+ } catch {
1661
+ return false;
1662
+ }
1663
+ }
1664
+ }
1379
1665
  async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
1380
1666
  try {
1381
1667
  const labelFont = await pdfDoc.embedFont("Helvetica-Bold");
@@ -1385,8 +1671,7 @@ async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
1385
1671
  for (const field of fieldsToLabel) {
1386
1672
  const isRadioField = field.type === "radio" /* RADIO */;
1387
1673
  const hasRadioOptions = isRadioField && field.options && field.options.length > 0;
1388
- const hasCustomLabel = field.label && field.label.trim() && !isAutoGeneratedLabel(field.label);
1389
- if (!hasCustomLabel && !hasRadioOptions) {
1674
+ if (!hasDrawableLabel(field) && !hasRadioOptions) {
1390
1675
  continue;
1391
1676
  }
1392
1677
  const pdfField = form.getFieldMaybe(field.name);
@@ -1397,27 +1682,30 @@ async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
1397
1682
  const widgets = fieldWithExtensions.acroField?.getWidgets?.() || [];
1398
1683
  if (widgets.length === 0) continue;
1399
1684
  if (isRadioField) {
1400
- if (field.label && field.label.trim() && !isAutoGeneratedLabel(field.label)) {
1685
+ if (hasDrawableLabel(field)) {
1401
1686
  const firstWidget = widgets[0];
1402
- if (!firstWidget) continue;
1403
- const firstRect = firstWidget.getRectangle?.();
1404
- if (firstRect) {
1405
- const pageRef = firstWidget.P?.();
1406
- const pageIndex = findPageIndexWithFallback(pages, pageRef);
1407
- if (pageIndex >= 0) {
1408
- const page = pages[pageIndex];
1409
- if (!page) continue;
1410
- const groupLabelKey = `${pageIndex}-${field.name}-grouplabel`;
1411
- if (!drawnOnce.has(groupLabelKey)) {
1412
- const labelY = firstRect.y + firstRect.height + 5;
1413
- page.drawText(field.label, {
1414
- x: firstRect.x,
1415
- y: labelY,
1416
- size: 10,
1417
- font: labelFont,
1418
- color: rgb(0, 0, 0)
1419
- });
1420
- drawnOnce.add(groupLabelKey);
1687
+ if (firstWidget) {
1688
+ const firstRect = firstWidget.getRectangle?.();
1689
+ if (firstRect) {
1690
+ const pageRef = firstWidget.P?.();
1691
+ const pageIndex = findPageIndexWithFallback(pages, pageRef);
1692
+ if (pageIndex >= 0) {
1693
+ const page = pages[pageIndex];
1694
+ if (page) {
1695
+ const groupLabelKey = `${pageIndex}-${field.name}-grouplabel`;
1696
+ if (!drawnOnce.has(groupLabelKey)) {
1697
+ const radioLabelFontSize = Math.min(10, firstRect.height * 0.4);
1698
+ const labelY = firstRect.y + firstRect.height + 5;
1699
+ page.drawText(field.label, {
1700
+ x: firstRect.x,
1701
+ y: labelY,
1702
+ size: radioLabelFontSize,
1703
+ font: labelFont,
1704
+ color: rgb(0, 0, 0)
1705
+ });
1706
+ drawnOnce.add(groupLabelKey);
1707
+ }
1708
+ }
1421
1709
  }
1422
1710
  }
1423
1711
  }
@@ -1430,17 +1718,16 @@ async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
1430
1718
  if (!rect) continue;
1431
1719
  const pageRef = widget.P?.();
1432
1720
  const pageIndex = findPageIndexWithFallback(pages, pageRef);
1433
- if (pageIndex >= 0) {
1721
+ if (pageIndex >= 0 && pageIndex < pages.length) {
1434
1722
  const page = pages[pageIndex];
1435
1723
  if (!page) continue;
1436
- const optionTexts = field.options;
1437
- const optionText = optionTexts[i] || optionTexts[0];
1724
+ const optionText = field.options[i];
1438
1725
  const optionKey = `${pageIndex}-${field.name}-opt-${i}`;
1439
1726
  if (optionText && !drawnOnce.has(optionKey)) {
1440
1727
  page.drawText(optionText, {
1441
- x: rect.x + rect.width + 6,
1442
- y: rect.y + (rect.height - 10) / 2,
1443
- size: 10,
1728
+ x: rect.x + rect.width + 4,
1729
+ y: rect.y + (rect.height - 7) / 2,
1730
+ size: 7,
1444
1731
  font: labelFont,
1445
1732
  color: rgb(0, 0, 0)
1446
1733
  });
@@ -1461,12 +1748,13 @@ async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
1461
1748
  if (!page) continue;
1462
1749
  const key = `${pageIndex}-${field.name}`;
1463
1750
  if (!drawnOnce.has(key)) {
1464
- if (!isAutoGeneratedLabel(field.label)) {
1751
+ if (hasDrawableLabel(field)) {
1752
+ const labelFontSize = Math.min(10, rect.height * 0.4);
1465
1753
  if (field.type === "checkbox" /* CHECKBOX */) {
1466
1754
  page.drawText(field.label, {
1467
1755
  x: rect.x + rect.width + 5,
1468
- y: rect.y + rect.height * 0.2,
1469
- size: 10,
1756
+ y: rect.y + (rect.height - labelFontSize) / 2,
1757
+ size: labelFontSize,
1470
1758
  font: labelFont,
1471
1759
  color: rgb(0, 0, 0)
1472
1760
  });
@@ -1475,9 +1763,9 @@ async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
1475
1763
  page.drawText(field.label, {
1476
1764
  x: rect.x,
1477
1765
  y: labelY,
1478
- size: 9,
1766
+ size: labelFontSize,
1479
1767
  font: labelFont,
1480
- color: rgb(0.3, 0.3, 0.3)
1768
+ color: rgb(0, 0, 0)
1481
1769
  });
1482
1770
  }
1483
1771
  drawnOnce.add(key);
@@ -1538,7 +1826,7 @@ async function filterPdfForCurrentSigner(pdfBytes, allFields, multiSignerContext
1538
1826
  try {
1539
1827
  const pdfField = form.getFieldMaybe(field.name);
1540
1828
  if (pdfField) {
1541
- form.removeField(pdfField);
1829
+ robustlyRemoveField(form, pdfField);
1542
1830
  removedCount++;
1543
1831
  } else {
1544
1832
  notFoundCount++;
@@ -1572,7 +1860,7 @@ function usePdfViewer(multiSignerContext) {
1572
1860
  const [originalPdfBytes, setOriginalPdfBytes] = react.useState(null);
1573
1861
  const [_viewerPdfBytes, setViewerPdfBytes] = react.useState(null);
1574
1862
  const [extractedFields, setExtractedFields] = react.useState([]);
1575
- const loadPdf = react.useCallback(async (url) => {
1863
+ const loadPdf = react.useCallback(async (url, signerEmailForExtraction) => {
1576
1864
  setIsLoading(true);
1577
1865
  setError(null);
1578
1866
  setIsLoaded(false);
@@ -1585,8 +1873,22 @@ function usePdfViewer(multiSignerContext) {
1585
1873
  }
1586
1874
  const bytes = await urlToPdfBytes(url);
1587
1875
  setOriginalPdfBytes(bytes);
1588
- setViewerPdfBytes(bytes);
1589
- await viewerRef.current?.loadPdf(url);
1876
+ const fields = await extractVisibleFormFields(bytes, signerEmailForExtraction);
1877
+ setExtractedFields(fields);
1878
+ const filteredBytes = await filterPdfForCurrentSigner(
1879
+ bytes,
1880
+ fields,
1881
+ multiSignerContext || {
1882
+ isMultiSigner: false,
1883
+ currentSigner: null,
1884
+ currentSignerEmail: "",
1885
+ isPrimarySigner: true,
1886
+ isFinalSigner: true
1887
+ }
1888
+ );
1889
+ setViewerPdfBytes(filteredBytes);
1890
+ const blobUrl = createPdfBlobUrl(filteredBytes);
1891
+ await viewerRef.current?.loadPdf(blobUrl);
1590
1892
  } catch (err) {
1591
1893
  const errorMessage = err instanceof Error ? err.message : "Failed to load PDF";
1592
1894
  logger.error("Error loading PDF:", err);
@@ -1594,7 +1896,7 @@ function usePdfViewer(multiSignerContext) {
1594
1896
  setOriginalPdfBytes(null);
1595
1897
  setViewerPdfBytes(null);
1596
1898
  }
1597
- }, []);
1899
+ }, [multiSignerContext]);
1598
1900
  const handleLoad = react.useCallback(() => {
1599
1901
  setIsLoading(false);
1600
1902
  setIsLoaded(true);
@@ -1630,52 +1932,15 @@ function usePdfViewer(multiSignerContext) {
1630
1932
  return await viewerRef.current.saveDocument();
1631
1933
  }, []);
1632
1934
  const extractFormFields = react.useCallback(
1633
- async (currentSignerEmail) => {
1634
- if (!originalPdfBytes) {
1635
- throw new Error("No PDF loaded");
1636
- }
1637
- try {
1638
- const fields = await extractVisibleFormFields(
1639
- originalPdfBytes,
1640
- currentSignerEmail
1641
- );
1642
- setExtractedFields(fields);
1643
- if (multiSignerContext?.isMultiSigner) {
1644
- const filteredBytes = await filterPdfForCurrentSigner(
1645
- originalPdfBytes,
1646
- fields,
1647
- multiSignerContext
1648
- );
1649
- setViewerPdfBytes(filteredBytes);
1650
- const filteredBlobUrl = createPdfBlobUrl(filteredBytes);
1651
- await viewerRef.current?.loadPdf(filteredBlobUrl);
1652
- } else {
1653
- const labeledBytes = await filterPdfForCurrentSigner(
1654
- originalPdfBytes,
1655
- fields,
1656
- multiSignerContext || {
1657
- isMultiSigner: false,
1658
- currentSigner: null,
1659
- currentSignerEmail: "",
1660
- isPrimarySigner: true,
1661
- isFinalSigner: true
1662
- }
1663
- );
1664
- setViewerPdfBytes(labeledBytes);
1665
- const labeledBlobUrl = createPdfBlobUrl(labeledBytes);
1666
- await viewerRef.current?.loadPdf(labeledBlobUrl);
1667
- }
1668
- if (viewerRef.current && fields.length > 0) {
1669
- await new Promise((resolve) => setTimeout(resolve, 100));
1670
- viewerRef.current.injectPlaceholders(fields);
1671
- }
1672
- return fields;
1673
- } catch (err) {
1674
- const errorMessage = err instanceof Error ? err.message : "Failed to extract form fields";
1675
- throw new Error(errorMessage);
1935
+ async (_currentSignerEmail) => {
1936
+ if (viewerRef.current && extractedFields.length > 0) {
1937
+ await new Promise((resolve) => setTimeout(resolve, 100));
1938
+ viewerRef.current.injectPlaceholders(extractedFields);
1939
+ viewerRef.current.injectRadioLabels(extractedFields);
1676
1940
  }
1941
+ return extractedFields;
1677
1942
  },
1678
- [originalPdfBytes, multiSignerContext]
1943
+ [extractedFields]
1679
1944
  );
1680
1945
  const fillPdf = react.useCallback(
1681
1946
  async (fieldValues, signatures, currentSignerEmail, metadata, auditTrail) => {
@@ -1818,6 +2083,20 @@ function usePdfViewer(multiSignerContext) {
1818
2083
  }
1819
2084
  function useFormFields(fields = []) {
1820
2085
  const [fieldValues, setFieldValues] = react.useState({});
2086
+ const seededFieldsRef = react.useRef(/* @__PURE__ */ new Set());
2087
+ react.useEffect(() => {
2088
+ if (fields.length === 0) return;
2089
+ const defaults = {};
2090
+ for (const field of fields) {
2091
+ if (field.defaultValue && field.defaultValue.trim() && !seededFieldsRef.current.has(field.id)) {
2092
+ defaults[field.id] = field.defaultValue;
2093
+ seededFieldsRef.current.add(field.id);
2094
+ }
2095
+ }
2096
+ if (Object.keys(defaults).length > 0) {
2097
+ setFieldValues((prev) => ({ ...defaults, ...prev }));
2098
+ }
2099
+ }, [fields]);
1821
2100
  const [errors, setErrors] = react.useState([]);
1822
2101
  const [touched, setTouched] = react.useState({});
1823
2102
  const updateField = react.useCallback((fieldId, value) => {
@@ -1857,14 +2136,14 @@ function useFormFields(fields = []) {
1857
2136
  if (!value || value.trim() === "") {
1858
2137
  newErrors.push({
1859
2138
  field: fieldId,
1860
- message: `${field.label || field.name} is required`
2139
+ message: `${getFieldDisplayName(field)} is required`
1861
2140
  });
1862
2141
  }
1863
2142
  }
1864
2143
  if (value && field.maxLength && value.length > field.maxLength) {
1865
2144
  newErrors.push({
1866
2145
  field: fieldId,
1867
- message: `${field.label || field.name} must be at most ${field.maxLength} characters`
2146
+ message: `${getFieldDisplayName(field)} must be at most ${field.maxLength} characters`
1868
2147
  });
1869
2148
  }
1870
2149
  if (value && field.type === "date") {
@@ -1890,13 +2169,13 @@ function useFormFields(fields = []) {
1890
2169
  if (!signatures[field.id]) {
1891
2170
  newErrors.push({
1892
2171
  field: field.id,
1893
- message: `${field.label || field.name} is required`
2172
+ message: `${getFieldDisplayName(field)} is required`
1894
2173
  });
1895
2174
  }
1896
2175
  } else if (!value2 || value2.trim() === "") {
1897
2176
  newErrors.push({
1898
2177
  field: field.id,
1899
- message: `${field.label || field.name} is required`
2178
+ message: `${getFieldDisplayName(field)} is required`
1900
2179
  });
1901
2180
  }
1902
2181
  }
@@ -1904,7 +2183,7 @@ function useFormFields(fields = []) {
1904
2183
  if (value && field.maxLength && value.length > field.maxLength) {
1905
2184
  newErrors.push({
1906
2185
  field: field.id,
1907
- message: `${field.label || field.name} must be at most ${field.maxLength} characters`
2186
+ message: `${getFieldDisplayName(field)} must be at most ${field.maxLength} characters`
1908
2187
  });
1909
2188
  }
1910
2189
  if (value && field.type === "date") {
@@ -2078,7 +2357,7 @@ function useSignatures() {
2078
2357
  for (const field of fields) {
2079
2358
  if (field.required && (field.type === "signature" || field.type === "initials")) {
2080
2359
  if (!signaturesToCheck[field.id]) {
2081
- errors.push(`${field.label || field.name} is required`);
2360
+ errors.push(`${getFieldDisplayName(field)} is required`);
2082
2361
  }
2083
2362
  }
2084
2363
  }
@@ -2196,6 +2475,13 @@ function useAttachments(options = {}) {
2196
2475
  errors.push(...validation.errors);
2197
2476
  continue;
2198
2477
  }
2478
+ const isDuplicate = [...attachments, ...newAttachments].some(
2479
+ (att) => att.name.toLowerCase() === file.name.toLowerCase()
2480
+ );
2481
+ if (isDuplicate) {
2482
+ errors.push(`"${file.name}" is already attached`);
2483
+ continue;
2484
+ }
2199
2485
  const currentTotalSize = [...attachments, ...newAttachments].reduce((sum, att) => sum + att.size, 0);
2200
2486
  if (currentTotalSize + file.size > constraints.maxTotalSize) {
2201
2487
  const maxTotalMB = (constraints.maxTotalSize / 1024 / 1024).toFixed(1);
@@ -2246,6 +2532,9 @@ function useAttachments(options = {}) {
2246
2532
  setAttachments([]);
2247
2533
  setValidationErrors([]);
2248
2534
  }, []);
2535
+ const clearValidationErrors = react.useCallback(() => {
2536
+ setValidationErrors([]);
2537
+ }, []);
2249
2538
  const getTotalSize = react.useCallback(() => {
2250
2539
  return attachments.reduce((sum, att) => sum + att.size, 0);
2251
2540
  }, [attachments]);
@@ -2277,6 +2566,7 @@ function useAttachments(options = {}) {
2277
2566
  addFiles,
2278
2567
  removeAttachment,
2279
2568
  clearAttachments,
2569
+ clearValidationErrors,
2280
2570
  // Utilities
2281
2571
  getTotalSize,
2282
2572
  formatSize,
@@ -2309,6 +2599,10 @@ function useAcknowledgements(fields) {
2309
2599
  const [acknowledgedMap, setAcknowledgedMap] = react.useState(
2310
2600
  /* @__PURE__ */ new Map()
2311
2601
  );
2602
+ const acknowledgedMapRef = react.useRef(acknowledgedMap);
2603
+ react.useEffect(() => {
2604
+ acknowledgedMapRef.current = acknowledgedMap;
2605
+ }, [acknowledgedMap]);
2312
2606
  const getFieldsWithAcknowledgements = react.useCallback((fieldsToFilter) => {
2313
2607
  return fieldsToFilter.filter(
2314
2608
  (field) => field.acknowledgements && field.acknowledgements.length > 0
@@ -2320,26 +2614,27 @@ function useAcknowledgements(fields) {
2320
2614
  const fieldAcks = newMap.get(fieldId) || /* @__PURE__ */ new Set();
2321
2615
  fieldAcks.add(ackId);
2322
2616
  newMap.set(fieldId, fieldAcks);
2617
+ acknowledgedMapRef.current = newMap;
2323
2618
  return newMap;
2324
2619
  });
2325
2620
  }, []);
2326
2621
  const isAcknowledged = react.useCallback((fieldId, ackId) => {
2327
- const fieldAcks = acknowledgedMap.get(fieldId);
2622
+ const fieldAcks = acknowledgedMapRef.current.get(fieldId);
2328
2623
  if (!fieldAcks) return false;
2329
2624
  if (ackId) {
2330
2625
  return fieldAcks.has(ackId);
2331
2626
  }
2332
2627
  return fieldAcks.size > 0;
2333
- }, [acknowledgedMap]);
2628
+ }, []);
2334
2629
  const isFieldFullyAcknowledged = react.useCallback((fieldId) => {
2335
2630
  const field = fields.find((f) => f.id === fieldId);
2336
2631
  if (!field || !field.acknowledgements || field.acknowledgements.length === 0) {
2337
2632
  return true;
2338
2633
  }
2339
- const fieldAcks = acknowledgedMap.get(fieldId);
2634
+ const fieldAcks = acknowledgedMapRef.current.get(fieldId);
2340
2635
  if (!fieldAcks) return false;
2341
2636
  return field.acknowledgements.every((ack) => fieldAcks.has(ack.id));
2342
- }, [fields, acknowledgedMap]);
2637
+ }, [fields]);
2343
2638
  const getFieldAcknowledgementProgress = react.useCallback((fieldId) => {
2344
2639
  const field = fields.find((f) => f.id === fieldId);
2345
2640
  if (!field || !field.acknowledgements || field.acknowledgements.length === 0) {