@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
@@ -1,4 +1,4 @@
1
- import { useRef, useState, useCallback, useMemo } from 'react';
1
+ import { useRef, useState, useCallback, useEffect, useMemo } from 'react';
2
2
  import * as pdfjsLib2 from 'pdfjs-dist';
3
3
  import { parseISO, isValid } from 'date-fns';
4
4
 
@@ -55,6 +55,7 @@ function validatePdfBytes(pdfBytes) {
55
55
  }
56
56
  function isAutoGeneratedLabel(label) {
57
57
  if (!label || !label.trim()) return true;
58
+ const trimmed = label.trim();
58
59
  const autoLabels = [
59
60
  "Signature",
60
61
  "Initials",
@@ -65,7 +66,65 @@ function isAutoGeneratedLabel(label) {
65
66
  "Option",
66
67
  "Radio"
67
68
  ];
68
- return autoLabels.includes(label.trim());
69
+ if (autoLabels.includes(trimmed)) return true;
70
+ if (/^\d{10,}$/.test(trimmed)) return true;
71
+ const typeTimestampPattern = new RegExp(
72
+ `^(${autoLabels.join("|")})[\\s_-]?\\d{10,}$`,
73
+ "i"
74
+ );
75
+ if (typeTimestampPattern.test(trimmed)) return true;
76
+ const typeNumberPattern = new RegExp(
77
+ `^(${autoLabels.join("|")})[\\s_-]?\\d{1,3}$`,
78
+ "i"
79
+ );
80
+ if (typeNumberPattern.test(trimmed)) return true;
81
+ const typeWordsJoined = autoLabels.join("|");
82
+ const corruptedPattern = new RegExp(
83
+ `^(${typeWordsJoined})[\\s_-]?\\d{10,}[\\s_-]?(${typeWordsJoined})+`,
84
+ "i"
85
+ );
86
+ if (corruptedPattern.test(trimmed)) return true;
87
+ const repeatedTypePattern = new RegExp(
88
+ `^((${typeWordsJoined})[\\s_-]+)+(${typeWordsJoined})$`,
89
+ "i"
90
+ );
91
+ if (repeatedTypePattern.test(trimmed)) return true;
92
+ return false;
93
+ }
94
+ function hasDrawableLabel(field) {
95
+ if (!field.label || !field.label.trim()) return false;
96
+ if (field.isLabelAutoGenerated) return false;
97
+ if (isAutoGeneratedLabel(field.label)) return false;
98
+ return true;
99
+ }
100
+ function getFieldDisplayName(field) {
101
+ if (field.label && !isAutoGeneratedLabel(field.label)) {
102
+ return field.label;
103
+ }
104
+ const typeNames = {
105
+ "text": "Text field",
106
+ "signature": "Signature",
107
+ "initials": "Initials",
108
+ "date": "Date field",
109
+ "checkbox": "Checkbox",
110
+ "dropdown": "Dropdown",
111
+ "radio": "Radio selection",
112
+ "radiogroup": "Radio selection",
113
+ "text_label": "Text label"
114
+ };
115
+ if (field.type) {
116
+ const typeName = typeNames[field.type.toLowerCase()];
117
+ if (typeName) {
118
+ return typeName;
119
+ }
120
+ }
121
+ if (field.name) {
122
+ let cleaned = field.name.replace(/_?(signature|initials|date)$/i, "").replace(/[_-]\d{10,}$/, "").replace(/[_-]/g, " ").trim();
123
+ if (cleaned && !isAutoGeneratedLabel(cleaned)) {
124
+ return cleaned.replace(/\b\w/g, (l) => l.toUpperCase());
125
+ }
126
+ }
127
+ return "This field";
69
128
  }
70
129
  function validateFieldValues(values) {
71
130
  const errors = [];
@@ -212,18 +271,15 @@ function isFieldVisibleToSigner(field, multiSignerContext) {
212
271
  if (!multiSignerContext.isMultiSigner) {
213
272
  return true;
214
273
  }
215
- const { currentSignerEmail, isPrimarySigner, isFinalSigner } = multiSignerContext;
274
+ const { currentSignerEmail } = multiSignerContext;
216
275
  if (!field.assignedSignerEmail) {
217
- return isFinalSigner;
276
+ return true;
218
277
  }
219
278
  if (field.assignedSignerEmail === currentSignerEmail) {
220
279
  return true;
221
280
  }
222
- if (field.assignedSignerEmail.includes("recipients")) {
223
- return isPrimarySigner;
224
- }
225
- if (field.assignedSignerEmail.includes("signers")) {
226
- return isFinalSigner;
281
+ if (field.assignedSignerEmail === "to-recipients" || field.assignedSignerEmail === "additional-signers") {
282
+ return true;
227
283
  }
228
284
  return false;
229
285
  }
@@ -237,18 +293,15 @@ function shouldFlattenField(field, multiSignerContext) {
237
293
  if (!multiSignerContext.isMultiSigner) {
238
294
  return true;
239
295
  }
240
- const { currentSignerEmail, isPrimarySigner, isFinalSigner } = multiSignerContext;
296
+ const { currentSignerEmail } = multiSignerContext;
241
297
  if (!field.assignedSignerEmail) {
242
- return isFinalSigner;
298
+ return true;
243
299
  }
244
300
  if (field.assignedSignerEmail === currentSignerEmail) {
245
301
  return true;
246
302
  }
247
- if (field.assignedSignerEmail.includes("recipients")) {
248
- return isPrimarySigner;
249
- }
250
- if (field.assignedSignerEmail.includes("signers")) {
251
- return isFinalSigner;
303
+ if (field.assignedSignerEmail === "to-recipients" || field.assignedSignerEmail === "additional-signers") {
304
+ return true;
252
305
  }
253
306
  return false;
254
307
  }
@@ -288,18 +341,23 @@ function findPageIndexWithFallback(pages, pageRef) {
288
341
 
289
342
  // src/utils/pdf-field-type-helpers.ts
290
343
  function detectFieldType(field) {
291
- const typeName = field.constructor.name;
292
- if (typeName === "PDFTextField") {
293
- return "text";
294
- } else if (typeName === "PDFCheckBox") {
344
+ const f = field;
345
+ if (typeof f.check === "function" && typeof f.uncheck === "function") {
295
346
  return "checkbox";
296
- } else if (typeName === "PDFDropdown") {
347
+ }
348
+ if (typeof f.select === "function" && typeof f.getOptions === "function" && typeof f.check !== "function" && typeof f.setOptions !== "function") {
349
+ return "radiogroup";
350
+ }
351
+ if (typeof f.select === "function" && typeof f.getOptions === "function" && (typeof f.setOptions === "function" || typeof f.addOptions === "function")) {
297
352
  return "dropdown";
298
- } else if (typeName === "PDFOptionList") {
353
+ }
354
+ if (typeof f.getOptions === "function" && typeof f.setOptions === "function" && typeof f.select !== "function") {
299
355
  return "optionlist";
300
- } else if (typeName === "PDFRadioGroup") {
301
- return "radiogroup";
302
- } else if (typeName === "PDFSignature") {
356
+ }
357
+ if (typeof f.getText === "function" && typeof f.setText === "function") {
358
+ return "text";
359
+ }
360
+ if (typeof f.getText !== "function" && typeof f.check !== "function" && typeof f.select !== "function") {
303
361
  return "signature";
304
362
  }
305
363
  return "unknown";
@@ -308,13 +366,20 @@ function extractFieldValue(field, fieldType) {
308
366
  try {
309
367
  switch (fieldType) {
310
368
  case "text":
369
+ case "date":
311
370
  return field.getText?.() || "";
312
371
  case "checkbox":
313
372
  return field.isChecked?.() ? "true" : "false";
314
373
  case "dropdown":
315
- case "optionlist":
374
+ case "optionlist": {
375
+ const selected = field.getSelected?.();
376
+ return Array.isArray(selected) ? selected[0] || "" : String(selected || "");
377
+ }
316
378
  case "radiogroup":
317
- return field.getSelected?.()?.[0] || "";
379
+ case "radio": {
380
+ const radioSelected = field.getSelected?.();
381
+ return radioSelected ? String(radioSelected) : "";
382
+ }
318
383
  default:
319
384
  return "";
320
385
  }
@@ -386,12 +451,16 @@ async function validatePdfFormFields(pdfBytes, fieldValues, signatures, extracte
386
451
  const errors = [];
387
452
  for (const field of pdfFormFields) {
388
453
  if (field.required) {
454
+ const extractedField = extractedFields?.find((f) => f.name === field.name);
389
455
  if (multiSignerContext?.isMultiSigner && extractedFields) {
390
- const extractedField = extractedFields.find((f) => f.name === field.name);
391
456
  if (!extractedField) {
392
457
  continue;
393
458
  }
394
459
  }
460
+ const logicalType = extractedField?.type || field.type;
461
+ if (logicalType === "date" || logicalType === "signature" || logicalType === "initials") {
462
+ continue;
463
+ }
395
464
  let hasValue = false;
396
465
  const fieldValue = fieldValues[field.name];
397
466
  const signatureValue = signatures[field.name];
@@ -401,16 +470,17 @@ async function validatePdfFormFields(pdfBytes, fieldValues, signatures, extracte
401
470
  hasValue = !!(signatureValue || fieldValue || mainSignature);
402
471
  } else if (field.name.includes("initials")) {
403
472
  hasValue = !!(signatureValue || fieldValue || mainInitials);
473
+ } else if (field.type === "checkbox" || logicalType === "checkbox") {
474
+ hasValue = fieldValue === "true" || field.value === "true";
404
475
  } else {
405
476
  hasValue = !!(fieldValue || field.value);
406
477
  }
407
478
  if (!hasValue) {
408
- let friendlyName = field.name.replace(/[_-]/g, " ").replace(/\b\w/g, (l) => l.toUpperCase());
409
- if (field.name.includes("signature")) {
410
- friendlyName = "Signature";
411
- } else if (field.name.includes("initials")) {
412
- friendlyName = "Initials";
413
- }
479
+ const friendlyName = getFieldDisplayName({
480
+ label: extractedField?.label,
481
+ name: field.name,
482
+ type: logicalType
483
+ });
414
484
  errors.push(`${friendlyName} is required`);
415
485
  }
416
486
  }
@@ -421,7 +491,80 @@ async function validatePdfFormFields(pdfBytes, fieldValues, signatures, extracte
421
491
  return ["Unable to validate form fields. Please ensure all required fields are completed."];
422
492
  }
423
493
  }
424
- async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {}, currentSignerEmail, extractedFormFields, metadata, auditTrail, multiSignerContext) {
494
+ function extractFieldFontSize(fieldName, field, extractedFormFields) {
495
+ const DEFAULT_FONT_SIZE = 10;
496
+ const MIN_FONT_SIZE = 8;
497
+ const MAX_FONT_SIZE = 72;
498
+ const fieldInfo = extractedFormFields?.find((f) => f.name === fieldName);
499
+ if (fieldInfo?.fontSize && fieldInfo.fontSize >= MIN_FONT_SIZE && fieldInfo.fontSize <= MAX_FONT_SIZE) {
500
+ return fieldInfo.fontSize;
501
+ }
502
+ const daFontSize = extractFromDAField(field);
503
+ if (daFontSize !== null) {
504
+ return daFontSize;
505
+ }
506
+ const tuFontSize = extractFromTUField(field);
507
+ if (tuFontSize !== null) {
508
+ return tuFontSize;
509
+ }
510
+ return DEFAULT_FONT_SIZE;
511
+ }
512
+ function extractFromDAField(field) {
513
+ try {
514
+ const fieldWithAcro = field;
515
+ const dict = fieldWithAcro.acroField?.dict;
516
+ if (!dict) return null;
517
+ const daKey = dict.context?.obj?.("DA") || "DA";
518
+ const daEntry = dict.lookup?.(daKey) || dict.get?.(daKey);
519
+ if (!daEntry) return null;
520
+ let daValue = "";
521
+ if (typeof daEntry.decodeText === "function") {
522
+ daValue = daEntry.decodeText();
523
+ } else if (typeof daEntry.asString === "function") {
524
+ daValue = daEntry.asString();
525
+ } else {
526
+ daValue = String(daEntry);
527
+ }
528
+ const daMatch = daValue.match(/\s+(\d+)\s+Tf/i);
529
+ if (daMatch?.[1]) {
530
+ const fontSize = parseInt(daMatch[1], 10);
531
+ if (fontSize >= 8 && fontSize <= 72) {
532
+ return fontSize;
533
+ }
534
+ }
535
+ } catch {
536
+ }
537
+ return null;
538
+ }
539
+ function extractFromTUField(field) {
540
+ try {
541
+ const fieldWithAcro = field;
542
+ const dict = fieldWithAcro.acroField?.dict;
543
+ if (!dict) return null;
544
+ const tuKey = dict.context?.obj?.("TU") || "TU";
545
+ const tuEntry = dict.lookup?.(tuKey) || dict.get?.(tuKey);
546
+ if (!tuEntry) return null;
547
+ let tuValue = "";
548
+ if (typeof tuEntry.decodeText === "function") {
549
+ tuValue = tuEntry.decodeText();
550
+ } else if (typeof tuEntry.asString === "function") {
551
+ tuValue = tuEntry.asString();
552
+ } else {
553
+ tuValue = String(tuEntry);
554
+ }
555
+ tuValue = tuValue.replace(/^\(|\)$|^<|>$/g, "");
556
+ const match = tuValue.match(/\|fontSize:(\d+)$/);
557
+ if (match?.[1]) {
558
+ const fontSize = parseInt(match[1], 10);
559
+ if (fontSize >= 8 && fontSize <= 72) {
560
+ return fontSize;
561
+ }
562
+ }
563
+ } catch {
564
+ }
565
+ return null;
566
+ }
567
+ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {}, _currentSignerEmail, extractedFormFields, metadata, auditTrail, multiSignerContext) {
425
568
  try {
426
569
  const { PDFDocument, rgb, StandardFonts } = await loadPdfLib();
427
570
  const pdfDoc = await PDFDocument.load(pdfBytes);
@@ -438,39 +581,39 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
438
581
  continue;
439
582
  }
440
583
  try {
441
- const fieldTypeName = field.constructor.name;
442
- if (fieldTypeName === "PDFTextField" || fieldTypeName === "PDFTextField2") {
443
- const textField = field;
444
- textField.setText?.(fieldValue);
445
- } else if (fieldTypeName === "PDFCheckBox" || fieldTypeName === "PDFCheckBox2") {
446
- const checkBox = field;
584
+ const f = field;
585
+ const isTextField = typeof f.getText === "function" && typeof f.setText === "function";
586
+ const isCheckbox = typeof f.check === "function" && typeof f.uncheck === "function";
587
+ const isRadio = typeof f.select === "function" && typeof f.getOptions === "function" && typeof f.check !== "function" && typeof f.setOptions !== "function";
588
+ const isDropdown = typeof f.select === "function" && (typeof f.setOptions === "function" || typeof f.addOptions === "function");
589
+ if (isTextField) {
590
+ f.setText?.(fieldValue);
591
+ } else if (isCheckbox) {
447
592
  if (fieldValue === "true" || fieldValue === "Yes" || fieldValue === "checked" || fieldValue === "On") {
448
- checkBox.check?.();
593
+ f.check?.();
449
594
  } else {
450
- checkBox.uncheck?.();
595
+ f.uncheck?.();
451
596
  }
452
- } else if (fieldTypeName === "PDFRadioGroup" || fieldTypeName === "PDFRadioGroup2") {
453
- const radioGroup = field;
597
+ } else if (isRadio) {
454
598
  if (fieldValue === "true" || fieldValue === "false") {
455
599
  continue;
456
600
  }
457
601
  const idxMatch = fieldValue.match(/__RADIO_OPTION_INDEX_(\d+)__/);
458
602
  if (idxMatch && idxMatch[1]) {
459
603
  const selectedIndex = parseInt(idxMatch[1], 10);
460
- const options = radioGroup.getOptions?.() || [];
604
+ const options = f.getOptions?.() || [];
461
605
  if (selectedIndex >= 0 && selectedIndex < options.length) {
462
- radioGroup.select?.(options[selectedIndex]);
606
+ f.select?.(options[selectedIndex]);
463
607
  }
464
608
  } else {
465
- const options = radioGroup.getOptions?.() || [];
609
+ const options = f.getOptions?.() || [];
466
610
  if (options.includes(fieldValue)) {
467
- radioGroup.select?.(fieldValue);
611
+ f.select?.(fieldValue);
468
612
  } else {
469
613
  }
470
614
  }
471
- } else if (fieldTypeName === "PDFDropdown" || fieldTypeName === "PDFDropdown2") {
472
- const dropdown = field;
473
- dropdown.select?.(fieldValue);
615
+ } else if (isDropdown) {
616
+ f.select?.(fieldValue);
474
617
  }
475
618
  } catch (fieldError) {
476
619
  logger.error(`Error setting field "${fieldName}":`, fieldError);
@@ -478,11 +621,6 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
478
621
  }
479
622
  try {
480
623
  form.updateFieldAppearances();
481
- const PDFName3 = pdfLib.PDFName;
482
- const acroForm = pdfDoc.catalog.lookup(PDFName3.of("AcroForm"));
483
- if (acroForm && typeof acroForm === "object" && "set" in acroForm) {
484
- acroForm.set(PDFName3.of("NeedAppearances"), false);
485
- }
486
624
  } catch (appearanceError) {
487
625
  logger.warn("Could not update field appearances:", appearanceError);
488
626
  }
@@ -551,6 +689,20 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
551
689
  const y = fieldPosition.y;
552
690
  const width = Math.max(fieldPosition.width, 80);
553
691
  const height = Math.max(fieldPosition.height, 30);
692
+ const fieldInfo = extractedFormFields?.find((f) => f.name === fieldName);
693
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
694
+ const labelFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
695
+ const labelFontSize = Math.min(10, height * 0.4);
696
+ const labelX = x;
697
+ const labelY = y + height + 5;
698
+ page.drawText(fieldInfo.label, {
699
+ x: labelX,
700
+ y: labelY,
701
+ size: labelFontSize,
702
+ font: labelFont,
703
+ color: rgb(0, 0, 0)
704
+ });
705
+ }
554
706
  const signatureDims = signatureImage.scaleToFit(width - 4, height - 4);
555
707
  const finalX = x;
556
708
  const finalY = y;
@@ -585,7 +737,7 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
585
737
  console.log("[FLATTEN] After pattern matching in fieldPageMap:", foundInitialsFields);
586
738
  if (extractedFormFields && extractedFormFields.length > 0) {
587
739
  foundInitialsFields = foundInitialsFields.filter((fieldName) => {
588
- const fieldInfo = extractedFormFields.find((f) => f.name === fieldName);
740
+ const fieldInfo = extractedFormFields.find((f) => f.name === fieldName) || extractedFormFields.find((f) => f.name === fieldName.replace(/_initials$/i, "")) || extractedFormFields.find((f) => fieldName.startsWith(f.name));
589
741
  if (!fieldInfo) return false;
590
742
  const isActualInitialsField = fieldInfo.type === "initials" || fieldName.toLowerCase().includes("initials");
591
743
  if (!isActualInitialsField) return false;
@@ -614,6 +766,19 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
614
766
  const y = fieldPosition.y;
615
767
  const height = Math.max(fieldPosition.height, 20);
616
768
  const fontSize = Math.min(height * 0.7, 14);
769
+ 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");
770
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
771
+ const labelFontSize = Math.min(10, height * 0.4);
772
+ const labelX = x;
773
+ const labelY = y + height + 5;
774
+ page.drawText(fieldInfo.label, {
775
+ x: labelX,
776
+ y: labelY,
777
+ size: labelFontSize,
778
+ font,
779
+ color: rgb(0, 0, 0)
780
+ });
781
+ }
617
782
  const finalX = x + 2;
618
783
  const finalY = y + (height - fontSize) / 2;
619
784
  console.log("[FLATTEN] \u2705 Drawing initials at:", { x: finalX, y: finalY, fontSize, text: mainInitialsData });
@@ -656,11 +821,16 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
656
821
  let flattenedCount = 0;
657
822
  for (const field of fieldsToFlatten) {
658
823
  const fieldName = field.getName();
659
- const fieldType = field.constructor.name;
824
+ const f = field;
825
+ const isCheckboxField = typeof f.check === "function" && typeof f.uncheck === "function";
826
+ const isRadioField = typeof f.select === "function" && typeof f.getOptions === "function" && typeof f.check !== "function" && typeof f.setOptions !== "function";
827
+ const isDropdownField = typeof f.select === "function" && (typeof f.setOptions === "function" || typeof f.addOptions === "function");
828
+ const isTextField = typeof f.getText === "function" && typeof f.setText === "function";
829
+ const isSignatureType = !isCheckboxField && !isRadioField && !isDropdownField && !isTextField;
660
830
  try {
661
831
  const userEnteredValue = formFieldValues[fieldName];
662
- const isSignatureField = fieldName.toLowerCase().includes("signature") || fieldType.includes("Signature");
663
- const isInitialsField = fieldName.toLowerCase().includes("initials") || fieldType.includes("Initials");
832
+ const isSignatureField = fieldName.toLowerCase().includes("signature") || isSignatureType;
833
+ const isInitialsField = fieldName.toLowerCase().includes("initials");
664
834
  if (isSignatureField || isInitialsField) {
665
835
  form.removeField(field);
666
836
  flattenedCount++;
@@ -668,15 +838,29 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
668
838
  }
669
839
  const fieldWithWidgets = field;
670
840
  const widgets = fieldWithWidgets.acroField?.getWidgets?.() || [];
671
- if (fieldType === "PDFCheckBox" || fieldType === "PDFCheckBox2") {
841
+ if (isCheckboxField) {
672
842
  const stringValue = String(userEnteredValue || "");
673
843
  const isChecked = stringValue === "true" || stringValue === "Yes" || stringValue === "On" || stringValue === "checked";
844
+ const fieldInfo = extractedFormFields?.find((f2) => f2.name === fieldName);
845
+ const labelFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
674
846
  if (isChecked) {
675
847
  for (const widget of widgets) {
676
848
  const result = getWidgetRectangleAndPage(widget, pages);
677
849
  if (!result) continue;
678
850
  const { rect, page } = result;
679
- const checkboxSize = Math.min(rect.width, rect.height) * 0.6;
851
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
852
+ const labelFontSize = Math.min(10, rect.height * 0.4);
853
+ const labelX = rect.x + rect.width + 5;
854
+ const labelY = rect.y + (rect.height - labelFontSize) / 2;
855
+ page.drawText(fieldInfo.label, {
856
+ x: labelX,
857
+ y: labelY,
858
+ size: labelFontSize,
859
+ font: labelFont,
860
+ color: rgb(0, 0, 0)
861
+ });
862
+ }
863
+ const checkboxSize = Math.min(rect.width, rect.height);
680
864
  const checkboxX = rect.x + (rect.width - checkboxSize) / 2;
681
865
  const checkboxY = rect.y + (rect.height - checkboxSize) / 2;
682
866
  page.drawRectangle({
@@ -687,21 +871,51 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
687
871
  borderColor: rgb(0, 0, 0),
688
872
  borderWidth: 1
689
873
  });
690
- const checkSize = checkboxSize * 0.6;
691
- const textWidth = checkSize * 0.6;
692
- const textHeight = checkSize * 0.8;
874
+ const checkFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
875
+ const checkFontSize = checkboxSize * 0.7;
876
+ const textWidth = checkFont.widthOfTextAtSize("X", checkFontSize);
877
+ const textHeight = checkFont.heightAtSize(checkFontSize);
693
878
  page.drawText("X", {
694
879
  x: checkboxX + (checkboxSize - textWidth) / 2,
695
- y: checkboxY + (checkboxSize - textHeight) / 2 + textHeight * 0.2,
696
- size: checkSize,
697
- font: await pdfDoc.embedFont(StandardFonts.HelveticaBold),
880
+ y: checkboxY + (checkboxSize - textHeight) / 2 + textHeight * 0.15,
881
+ size: checkFontSize,
882
+ font: checkFont,
698
883
  color: rgb(0, 0, 0)
699
884
  });
700
885
  }
886
+ } else {
887
+ for (const widget of widgets) {
888
+ const result = getWidgetRectangleAndPage(widget, pages);
889
+ if (!result) continue;
890
+ const { rect, page } = result;
891
+ const checkboxSize = Math.min(rect.width, rect.height);
892
+ const checkboxX = rect.x + (rect.width - checkboxSize) / 2;
893
+ const checkboxY = rect.y + (rect.height - checkboxSize) / 2;
894
+ page.drawRectangle({
895
+ x: checkboxX,
896
+ y: checkboxY,
897
+ width: checkboxSize,
898
+ height: checkboxSize,
899
+ borderColor: rgb(0, 0, 0),
900
+ borderWidth: 1
901
+ });
902
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
903
+ const labelFontSize = Math.min(12, rect.height * 0.6);
904
+ const labelX = rect.x + rect.width + 5;
905
+ const labelY = rect.y + (rect.height - labelFontSize) / 2;
906
+ page.drawText(fieldInfo.label, {
907
+ x: labelX,
908
+ y: labelY,
909
+ size: labelFontSize,
910
+ font: labelFont,
911
+ color: rgb(0, 0, 0)
912
+ });
913
+ }
914
+ }
701
915
  }
702
- } else if (fieldType === "PDFRadioGroup" || fieldType === "PDFRadioGroup2") {
703
- const radioGroup = field;
704
- const fieldInfo = extractedFormFields?.find((f) => f.name === fieldName);
916
+ } else if (isRadioField) {
917
+ const radioGroup = f;
918
+ const fieldInfo = extractedFormFields?.find((f2) => f2.name === fieldName);
705
919
  let actualSelectedValue = "";
706
920
  try {
707
921
  actualSelectedValue = radioGroup.getSelected?.() || "";
@@ -746,14 +960,15 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
746
960
  const result = getWidgetRectangleAndPage(widget, pages);
747
961
  if (!result) continue;
748
962
  const { rect, page, pageIndex } = result;
749
- if (i === 0 && fieldInfo?.label && fieldInfo.label.trim() && !isAutoGeneratedLabel(fieldInfo.label)) {
963
+ if (i === 0 && fieldInfo && hasDrawableLabel(fieldInfo)) {
750
964
  const groupLabelKey = `${pageIndex}-${fieldName}-grouplabel`;
751
965
  if (!drawnGroupLabels.has(groupLabelKey)) {
966
+ const labelFontSize = Math.min(10, rect.height * 0.4);
752
967
  const labelY = rect.y + rect.height + 3;
753
968
  page.drawText(fieldInfo.label, {
754
969
  x: rect.x,
755
970
  y: labelY,
756
- size: 8,
971
+ size: labelFontSize,
757
972
  font: labelFont,
758
973
  color: rgb(0, 0, 0)
759
974
  });
@@ -761,22 +976,21 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
761
976
  }
762
977
  } else if (i === 0 && isAutoGeneratedLabel(fieldInfo?.label || "")) {
763
978
  }
764
- const radioSize = Math.min(rect.width, rect.height) * 0.5;
765
- const radioX = rect.x + (rect.width - radioSize) / 2;
766
- const radioY = rect.y + (rect.height - radioSize) / 2;
979
+ const radioSize = Math.min(rect.width, rect.height);
980
+ const centerX = rect.x + rect.width / 2;
981
+ const centerY = rect.y + rect.height / 2;
767
982
  page.drawCircle({
768
- x: radioX + radioSize / 2,
769
- y: radioY + radioSize / 2,
770
- size: radioSize,
983
+ x: centerX,
984
+ y: centerY,
985
+ size: radioSize / 2,
771
986
  borderColor: rgb(0, 0, 0),
772
987
  borderWidth: 1
773
988
  });
774
989
  if (i === selectedIndex) {
775
- const circleSize = radioSize * 0.5;
776
990
  page.drawCircle({
777
- x: radioX + radioSize / 2,
778
- y: radioY + radioSize / 2,
779
- size: circleSize,
991
+ x: centerX,
992
+ y: centerY,
993
+ size: radioSize / 4,
780
994
  color: rgb(0, 0, 0)
781
995
  });
782
996
  }
@@ -786,8 +1000,8 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
786
1000
  if (optionText && !drawnOptionLabels.has(optionKey)) {
787
1001
  page.drawText(optionText, {
788
1002
  x: rect.x + rect.width + 4,
789
- y: rect.y + (rect.height - 8) / 2,
790
- size: 8,
1003
+ y: rect.y + (rect.height - 7) / 2,
1004
+ size: 7,
791
1005
  font: labelFont,
792
1006
  color: rgb(0, 0, 0)
793
1007
  });
@@ -797,17 +1011,31 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
797
1011
  }
798
1012
  if (fieldInfo?.options) {
799
1013
  }
800
- } else if (fieldType === "PDFDropdown" || fieldType === "PDFDropdown2") {
1014
+ } else if (isDropdownField) {
801
1015
  const selectedValue = userEnteredValue ? String(userEnteredValue) : "";
802
- if (selectedValue && selectedValue.trim()) {
803
- for (const widget of widgets) {
804
- const result = getWidgetRectangleAndPage(widget, pages);
805
- if (!result) continue;
806
- const { rect, page } = result;
1016
+ const fieldInfo = extractedFormFields?.find((f2) => f2.name === fieldName);
1017
+ const labelFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
1018
+ for (const widget of widgets) {
1019
+ const result = getWidgetRectangleAndPage(widget, pages);
1020
+ if (!result) continue;
1021
+ const { rect, page } = result;
1022
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
1023
+ const labelFontSize = Math.min(10, rect.height * 0.4);
1024
+ const labelX = rect.x;
1025
+ const labelY = rect.y + rect.height + 5;
1026
+ page.drawText(fieldInfo.label, {
1027
+ x: labelX,
1028
+ y: labelY,
1029
+ size: labelFontSize,
1030
+ font: labelFont,
1031
+ color: rgb(0, 0, 0)
1032
+ });
1033
+ }
1034
+ if (selectedValue && selectedValue.trim()) {
807
1035
  page.drawText(selectedValue, {
808
1036
  x: rect.x + 2,
809
1037
  y: rect.y + 2,
810
- size: 10,
1038
+ size: 14,
811
1039
  font: await pdfDoc.embedFont(StandardFonts.Helvetica),
812
1040
  color: rgb(0, 0, 0)
813
1041
  });
@@ -815,15 +1043,31 @@ async function fillPdfWithSignatures(pdfBytes, signatures, formFieldValues = {},
815
1043
  }
816
1044
  } else {
817
1045
  const fieldValue = userEnteredValue ? String(userEnteredValue) : "";
818
- if (fieldValue && fieldValue.trim()) {
819
- for (const widget of widgets) {
820
- const result = getWidgetRectangleAndPage(widget, pages);
821
- if (!result) continue;
822
- const { rect, page } = result;
1046
+ const fieldInfo = extractedFormFields?.find((f2) => f2.name === fieldName);
1047
+ const labelFont = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
1048
+ const isDateField = fieldName.toLowerCase().includes("date") || fieldName.toLowerCase().includes("_date");
1049
+ const fontSize = isDateField ? 14 : extractFieldFontSize(fieldName, field, extractedFormFields);
1050
+ for (const widget of widgets) {
1051
+ const result = getWidgetRectangleAndPage(widget, pages);
1052
+ if (!result) continue;
1053
+ const { rect, page } = result;
1054
+ if (fieldInfo && hasDrawableLabel(fieldInfo)) {
1055
+ const labelFontSize = Math.min(10, rect.height * 0.4);
1056
+ const labelX = rect.x;
1057
+ const labelY = rect.y + rect.height + 5;
1058
+ page.drawText(fieldInfo.label, {
1059
+ x: labelX,
1060
+ y: labelY,
1061
+ size: labelFontSize,
1062
+ font: labelFont,
1063
+ color: rgb(0, 0, 0)
1064
+ });
1065
+ }
1066
+ if (fieldValue && fieldValue.trim()) {
823
1067
  page.drawText(fieldValue, {
824
1068
  x: rect.x + 2,
825
1069
  y: rect.y + 2,
826
- size: 10,
1070
+ size: fontSize,
827
1071
  font: await pdfDoc.embedFont(StandardFonts.Helvetica),
828
1072
  color: rgb(0, 0, 0)
829
1073
  });
@@ -981,7 +1225,6 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
981
1225
  const fields = form.getFields();
982
1226
  const visibleFields = [];
983
1227
  let hasAnyInitialsFields = false;
984
- let hasInitialsFieldsForCurrentSigner = false;
985
1228
  for (const field of fields) {
986
1229
  const fieldName = field.getName();
987
1230
  const fieldWithExtensions = field;
@@ -1007,28 +1250,31 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1007
1250
  };
1008
1251
  })();
1009
1252
  let fieldType = "text" /* TEXT */;
1010
- const fieldTypeName = field.constructor.name;
1253
+ const f = field;
1254
+ const isCheckboxField = typeof f.check === "function" && typeof f.uncheck === "function";
1255
+ const isRadioField = typeof f.select === "function" && typeof f.getOptions === "function" && typeof f.check !== "function" && typeof f.setOptions !== "function";
1256
+ const isDropdownField = typeof f.select === "function" && (typeof f.setOptions === "function" || typeof f.addOptions === "function");
1257
+ const isTextField = typeof f.getText === "function" && typeof f.setText === "function";
1258
+ const isSignatureType = !isCheckboxField && !isRadioField && !isDropdownField && !isTextField;
1011
1259
  const cleanFieldName = fieldName.replace(/_signature$|_initials$|_date$/i, "");
1012
- const isActualSignatureField = fieldTypeName.includes("Signature") || cleanFieldName.toLowerCase().includes("signature") && !fieldTypeName.includes("CheckBox") && !fieldTypeName.includes("Radio");
1013
- const isActualInitialsField = cleanFieldName.toLowerCase().includes("initials") && !fieldTypeName.includes("CheckBox") && !fieldTypeName.includes("Radio");
1260
+ const isActualSignatureField = isSignatureType || cleanFieldName.toLowerCase().includes("signature") && !isCheckboxField && !isRadioField;
1261
+ const isActualInitialsField = cleanFieldName.toLowerCase().includes("initials") && !isCheckboxField && !isRadioField;
1014
1262
  if (isActualSignatureField) {
1015
1263
  fieldType = "signature" /* SIGNATURE */;
1016
1264
  } else if (isActualInitialsField) {
1017
1265
  fieldType = "initials" /* INITIALS */;
1018
1266
  hasAnyInitialsFields = true;
1019
- if (currentSignerEmail && fieldMetadata.signer === currentSignerEmail) {
1020
- hasInitialsFieldsForCurrentSigner = true;
1021
- }
1022
1267
  } else if (cleanFieldName.toLowerCase().includes("date")) {
1023
1268
  fieldType = "date" /* DATE */;
1024
- } else if (fieldTypeName.includes("CheckBox")) {
1269
+ } else if (isCheckboxField) {
1025
1270
  fieldType = "checkbox" /* CHECKBOX */;
1026
- } else if (fieldTypeName.toLowerCase().includes("dropdown")) {
1271
+ } else if (isDropdownField) {
1027
1272
  fieldType = "dropdown" /* DROPDOWN */;
1028
- } else if (fieldTypeName.includes("Radio")) {
1273
+ } else if (isRadioField) {
1029
1274
  fieldType = "radio" /* RADIO */;
1030
1275
  }
1031
1276
  let displayLabel = fieldMetadata.label || "";
1277
+ let isLabelAutoGenerated = false;
1032
1278
  if (!displayLabel || !displayLabel.trim()) {
1033
1279
  try {
1034
1280
  const tuLabel = extractTULabel(fieldWithExtensions);
@@ -1040,6 +1286,7 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1040
1286
  }
1041
1287
  if (!displayLabel || !displayLabel.trim()) {
1042
1288
  displayLabel = generateFallbackLabel(cleanFieldName, fieldType);
1289
+ isLabelAutoGenerated = true;
1043
1290
  }
1044
1291
  const esignField = {
1045
1292
  id: fieldName,
@@ -1051,6 +1298,8 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1051
1298
  type: fieldType,
1052
1299
  label: displayLabel,
1053
1300
  // Use friendly label for display
1301
+ isLabelAutoGenerated,
1302
+ // True when label was generated from field name (should not be drawn on PDF)
1054
1303
  position: { x: 0, y: 0, width: 100, height: 30, page: 1 },
1055
1304
  // Default position
1056
1305
  required: fieldWithExtensions.isRequired?.() ?? false,
@@ -1071,6 +1320,13 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1071
1320
  logger.warn("Error extracting options for field:", error);
1072
1321
  }
1073
1322
  }
1323
+ try {
1324
+ const currentValue = extractFieldValue(f, esignField.type);
1325
+ if (currentValue) {
1326
+ esignField.defaultValue = currentValue;
1327
+ }
1328
+ } catch {
1329
+ }
1074
1330
  visibleFields.push(esignField);
1075
1331
  }
1076
1332
  }
@@ -1087,11 +1343,8 @@ async function extractVisibleFormFields(pdfBytes, currentSignerEmail) {
1087
1343
  placeholder: "Draw or upload your signature",
1088
1344
  assignedSignerEmail: currentSignerEmail
1089
1345
  });
1090
- const shouldCreateInitialsField = hasInitialsFieldsForCurrentSigner || hasAnyInitialsFields && !currentSignerEmail;
1346
+ const shouldCreateInitialsField = hasAnyInitialsFields;
1091
1347
  if (shouldCreateInitialsField) {
1092
- if (hasInitialsFieldsForCurrentSigner) {
1093
- } else {
1094
- }
1095
1348
  mainFields.push({
1096
1349
  id: "initials_field_main",
1097
1350
  fieldId: "initials_field_main",
@@ -1178,8 +1431,12 @@ function decodeFieldName(fieldName) {
1178
1431
  }
1179
1432
  function generateFallbackLabel(decodedFieldName, fieldType) {
1180
1433
  let displayLabel = decodedFieldName;
1181
- displayLabel = displayLabel.replace(/_signature$/i, "").replace(/_initials$/i, "").replace(/_date$/i, "");
1182
- if (/^(text|signature|initials|date)_\d+$/i.test(displayLabel)) {
1434
+ let prevLabel = "";
1435
+ while (displayLabel !== prevLabel) {
1436
+ prevLabel = displayLabel;
1437
+ displayLabel = displayLabel.replace(/_signature$/i, "").replace(/_initials$/i, "").replace(/_date$/i, "");
1438
+ }
1439
+ if (/^(text|signature|initials|date|checkbox|radio|dropdown)_\d+/i.test(displayLabel)) {
1183
1440
  if (fieldType === "signature" /* SIGNATURE */) return "Signature";
1184
1441
  if (fieldType === "initials" /* INITIALS */) return "Initials";
1185
1442
  if (fieldType === "date" /* DATE */) return "Date";
@@ -1187,9 +1444,9 @@ function generateFallbackLabel(decodedFieldName, fieldType) {
1187
1444
  if (fieldType === "checkbox" /* CHECKBOX */) return "Checkbox";
1188
1445
  if (fieldType === "dropdown" /* DROPDOWN */) return "Dropdown";
1189
1446
  if (fieldType === "radio" /* RADIO */) return "Option";
1190
- } else {
1191
- displayLabel = displayLabel.replace(/_/g, " ").replace(/\s+\d+$/g, "").trim().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
1447
+ if (fieldType === "text_label" /* TEXT_LABEL */) return "Text Label";
1192
1448
  }
1449
+ displayLabel = displayLabel.replace(/_/g, " ").replace(/\s+\d+$/g, "").trim().split(" ").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join(" ");
1193
1450
  return displayLabel;
1194
1451
  }
1195
1452
  function extractTULabel(field) {
@@ -1284,13 +1541,13 @@ var PdfProcessingError = class _PdfProcessingError extends Error {
1284
1541
 
1285
1542
  // src/utils/attachment-validators.ts
1286
1543
  var DEFAULT_ATTACHMENT_CONSTRAINTS = {
1287
- maxFileSize: 10 * 1024 * 1024,
1288
- // 10MB
1544
+ maxFileSize: 25 * 1024 * 1024,
1545
+ // 25MB
1289
1546
  maxTotalSize: 50 * 1024 * 1024,
1290
1547
  // 50MB
1291
1548
  maxFiles: 10,
1292
1549
  allowedTypes: ["image/*", "application/pdf", "application/msword", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"],
1293
- allowedExtensions: [".pdf", ".jpg", ".jpeg", ".png", ".gif", ".doc", ".docx"]
1550
+ allowedExtensions: [".pdf", ".jpg", ".jpeg", ".png", ".gif", ".bmp", ".webp", ".doc", ".docx"]
1294
1551
  };
1295
1552
  function validateFile(file, constraints = DEFAULT_ATTACHMENT_CONSTRAINTS) {
1296
1553
  const errors = [];
@@ -1354,6 +1611,35 @@ function isValidISODate(value) {
1354
1611
  }
1355
1612
 
1356
1613
  // src/utils/pdf-viewer-filter.ts
1614
+ function robustlyRemoveField(form, field) {
1615
+ try {
1616
+ const fieldWithAcro = field;
1617
+ if (fieldWithAcro.acroField && typeof fieldWithAcro.acroField.getWidgets === "function") {
1618
+ let widgets = fieldWithAcro.acroField.getWidgets() || [];
1619
+ let safetyCounter = 0;
1620
+ const maxIterations = widgets.length + 5;
1621
+ while (widgets.length > 0 && safetyCounter < maxIterations) {
1622
+ try {
1623
+ const widgetIndex = widgets.length - 1;
1624
+ fieldWithAcro.acroField.removeWidget?.(widgetIndex);
1625
+ widgets = fieldWithAcro.acroField.getWidgets() || [];
1626
+ } catch {
1627
+ break;
1628
+ }
1629
+ safetyCounter++;
1630
+ }
1631
+ }
1632
+ form.removeField(field);
1633
+ return true;
1634
+ } catch (error) {
1635
+ try {
1636
+ form.removeField(field);
1637
+ return true;
1638
+ } catch {
1639
+ return false;
1640
+ }
1641
+ }
1642
+ }
1357
1643
  async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
1358
1644
  try {
1359
1645
  const labelFont = await pdfDoc.embedFont("Helvetica-Bold");
@@ -1363,8 +1649,7 @@ async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
1363
1649
  for (const field of fieldsToLabel) {
1364
1650
  const isRadioField = field.type === "radio" /* RADIO */;
1365
1651
  const hasRadioOptions = isRadioField && field.options && field.options.length > 0;
1366
- const hasCustomLabel = field.label && field.label.trim() && !isAutoGeneratedLabel(field.label);
1367
- if (!hasCustomLabel && !hasRadioOptions) {
1652
+ if (!hasDrawableLabel(field) && !hasRadioOptions) {
1368
1653
  continue;
1369
1654
  }
1370
1655
  const pdfField = form.getFieldMaybe(field.name);
@@ -1375,27 +1660,30 @@ async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
1375
1660
  const widgets = fieldWithExtensions.acroField?.getWidgets?.() || [];
1376
1661
  if (widgets.length === 0) continue;
1377
1662
  if (isRadioField) {
1378
- if (field.label && field.label.trim() && !isAutoGeneratedLabel(field.label)) {
1663
+ if (hasDrawableLabel(field)) {
1379
1664
  const firstWidget = widgets[0];
1380
- if (!firstWidget) continue;
1381
- const firstRect = firstWidget.getRectangle?.();
1382
- if (firstRect) {
1383
- const pageRef = firstWidget.P?.();
1384
- const pageIndex = findPageIndexWithFallback(pages, pageRef);
1385
- if (pageIndex >= 0) {
1386
- const page = pages[pageIndex];
1387
- if (!page) continue;
1388
- const groupLabelKey = `${pageIndex}-${field.name}-grouplabel`;
1389
- if (!drawnOnce.has(groupLabelKey)) {
1390
- const labelY = firstRect.y + firstRect.height + 5;
1391
- page.drawText(field.label, {
1392
- x: firstRect.x,
1393
- y: labelY,
1394
- size: 10,
1395
- font: labelFont,
1396
- color: rgb(0, 0, 0)
1397
- });
1398
- drawnOnce.add(groupLabelKey);
1665
+ if (firstWidget) {
1666
+ const firstRect = firstWidget.getRectangle?.();
1667
+ if (firstRect) {
1668
+ const pageRef = firstWidget.P?.();
1669
+ const pageIndex = findPageIndexWithFallback(pages, pageRef);
1670
+ if (pageIndex >= 0) {
1671
+ const page = pages[pageIndex];
1672
+ if (page) {
1673
+ const groupLabelKey = `${pageIndex}-${field.name}-grouplabel`;
1674
+ if (!drawnOnce.has(groupLabelKey)) {
1675
+ const radioLabelFontSize = Math.min(10, firstRect.height * 0.4);
1676
+ const labelY = firstRect.y + firstRect.height + 5;
1677
+ page.drawText(field.label, {
1678
+ x: firstRect.x,
1679
+ y: labelY,
1680
+ size: radioLabelFontSize,
1681
+ font: labelFont,
1682
+ color: rgb(0, 0, 0)
1683
+ });
1684
+ drawnOnce.add(groupLabelKey);
1685
+ }
1686
+ }
1399
1687
  }
1400
1688
  }
1401
1689
  }
@@ -1408,17 +1696,16 @@ async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
1408
1696
  if (!rect) continue;
1409
1697
  const pageRef = widget.P?.();
1410
1698
  const pageIndex = findPageIndexWithFallback(pages, pageRef);
1411
- if (pageIndex >= 0) {
1699
+ if (pageIndex >= 0 && pageIndex < pages.length) {
1412
1700
  const page = pages[pageIndex];
1413
1701
  if (!page) continue;
1414
- const optionTexts = field.options;
1415
- const optionText = optionTexts[i] || optionTexts[0];
1702
+ const optionText = field.options[i];
1416
1703
  const optionKey = `${pageIndex}-${field.name}-opt-${i}`;
1417
1704
  if (optionText && !drawnOnce.has(optionKey)) {
1418
1705
  page.drawText(optionText, {
1419
- x: rect.x + rect.width + 6,
1420
- y: rect.y + (rect.height - 10) / 2,
1421
- size: 10,
1706
+ x: rect.x + rect.width + 4,
1707
+ y: rect.y + (rect.height - 7) / 2,
1708
+ size: 7,
1422
1709
  font: labelFont,
1423
1710
  color: rgb(0, 0, 0)
1424
1711
  });
@@ -1439,12 +1726,13 @@ async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
1439
1726
  if (!page) continue;
1440
1727
  const key = `${pageIndex}-${field.name}`;
1441
1728
  if (!drawnOnce.has(key)) {
1442
- if (!isAutoGeneratedLabel(field.label)) {
1729
+ if (hasDrawableLabel(field)) {
1730
+ const labelFontSize = Math.min(10, rect.height * 0.4);
1443
1731
  if (field.type === "checkbox" /* CHECKBOX */) {
1444
1732
  page.drawText(field.label, {
1445
1733
  x: rect.x + rect.width + 5,
1446
- y: rect.y + rect.height * 0.2,
1447
- size: 10,
1734
+ y: rect.y + (rect.height - labelFontSize) / 2,
1735
+ size: labelFontSize,
1448
1736
  font: labelFont,
1449
1737
  color: rgb(0, 0, 0)
1450
1738
  });
@@ -1453,9 +1741,9 @@ async function drawFieldLabelsOnPdf(pdfDoc, fieldsToLabel, rgb) {
1453
1741
  page.drawText(field.label, {
1454
1742
  x: rect.x,
1455
1743
  y: labelY,
1456
- size: 9,
1744
+ size: labelFontSize,
1457
1745
  font: labelFont,
1458
- color: rgb(0.3, 0.3, 0.3)
1746
+ color: rgb(0, 0, 0)
1459
1747
  });
1460
1748
  }
1461
1749
  drawnOnce.add(key);
@@ -1516,7 +1804,7 @@ async function filterPdfForCurrentSigner(pdfBytes, allFields, multiSignerContext
1516
1804
  try {
1517
1805
  const pdfField = form.getFieldMaybe(field.name);
1518
1806
  if (pdfField) {
1519
- form.removeField(pdfField);
1807
+ robustlyRemoveField(form, pdfField);
1520
1808
  removedCount++;
1521
1809
  } else {
1522
1810
  notFoundCount++;
@@ -1550,7 +1838,7 @@ function usePdfViewer(multiSignerContext) {
1550
1838
  const [originalPdfBytes, setOriginalPdfBytes] = useState(null);
1551
1839
  const [_viewerPdfBytes, setViewerPdfBytes] = useState(null);
1552
1840
  const [extractedFields, setExtractedFields] = useState([]);
1553
- const loadPdf = useCallback(async (url) => {
1841
+ const loadPdf = useCallback(async (url, signerEmailForExtraction) => {
1554
1842
  setIsLoading(true);
1555
1843
  setError(null);
1556
1844
  setIsLoaded(false);
@@ -1563,8 +1851,22 @@ function usePdfViewer(multiSignerContext) {
1563
1851
  }
1564
1852
  const bytes = await urlToPdfBytes(url);
1565
1853
  setOriginalPdfBytes(bytes);
1566
- setViewerPdfBytes(bytes);
1567
- await viewerRef.current?.loadPdf(url);
1854
+ const fields = await extractVisibleFormFields(bytes, signerEmailForExtraction);
1855
+ setExtractedFields(fields);
1856
+ const filteredBytes = await filterPdfForCurrentSigner(
1857
+ bytes,
1858
+ fields,
1859
+ multiSignerContext || {
1860
+ isMultiSigner: false,
1861
+ currentSigner: null,
1862
+ currentSignerEmail: "",
1863
+ isPrimarySigner: true,
1864
+ isFinalSigner: true
1865
+ }
1866
+ );
1867
+ setViewerPdfBytes(filteredBytes);
1868
+ const blobUrl = createPdfBlobUrl(filteredBytes);
1869
+ await viewerRef.current?.loadPdf(blobUrl);
1568
1870
  } catch (err) {
1569
1871
  const errorMessage = err instanceof Error ? err.message : "Failed to load PDF";
1570
1872
  logger.error("Error loading PDF:", err);
@@ -1572,7 +1874,7 @@ function usePdfViewer(multiSignerContext) {
1572
1874
  setOriginalPdfBytes(null);
1573
1875
  setViewerPdfBytes(null);
1574
1876
  }
1575
- }, []);
1877
+ }, [multiSignerContext]);
1576
1878
  const handleLoad = useCallback(() => {
1577
1879
  setIsLoading(false);
1578
1880
  setIsLoaded(true);
@@ -1608,52 +1910,15 @@ function usePdfViewer(multiSignerContext) {
1608
1910
  return await viewerRef.current.saveDocument();
1609
1911
  }, []);
1610
1912
  const extractFormFields = useCallback(
1611
- async (currentSignerEmail) => {
1612
- if (!originalPdfBytes) {
1613
- throw new Error("No PDF loaded");
1614
- }
1615
- try {
1616
- const fields = await extractVisibleFormFields(
1617
- originalPdfBytes,
1618
- currentSignerEmail
1619
- );
1620
- setExtractedFields(fields);
1621
- if (multiSignerContext?.isMultiSigner) {
1622
- const filteredBytes = await filterPdfForCurrentSigner(
1623
- originalPdfBytes,
1624
- fields,
1625
- multiSignerContext
1626
- );
1627
- setViewerPdfBytes(filteredBytes);
1628
- const filteredBlobUrl = createPdfBlobUrl(filteredBytes);
1629
- await viewerRef.current?.loadPdf(filteredBlobUrl);
1630
- } else {
1631
- const labeledBytes = await filterPdfForCurrentSigner(
1632
- originalPdfBytes,
1633
- fields,
1634
- multiSignerContext || {
1635
- isMultiSigner: false,
1636
- currentSigner: null,
1637
- currentSignerEmail: "",
1638
- isPrimarySigner: true,
1639
- isFinalSigner: true
1640
- }
1641
- );
1642
- setViewerPdfBytes(labeledBytes);
1643
- const labeledBlobUrl = createPdfBlobUrl(labeledBytes);
1644
- await viewerRef.current?.loadPdf(labeledBlobUrl);
1645
- }
1646
- if (viewerRef.current && fields.length > 0) {
1647
- await new Promise((resolve) => setTimeout(resolve, 100));
1648
- viewerRef.current.injectPlaceholders(fields);
1649
- }
1650
- return fields;
1651
- } catch (err) {
1652
- const errorMessage = err instanceof Error ? err.message : "Failed to extract form fields";
1653
- throw new Error(errorMessage);
1913
+ async (_currentSignerEmail) => {
1914
+ if (viewerRef.current && extractedFields.length > 0) {
1915
+ await new Promise((resolve) => setTimeout(resolve, 100));
1916
+ viewerRef.current.injectPlaceholders(extractedFields);
1917
+ viewerRef.current.injectRadioLabels(extractedFields);
1654
1918
  }
1919
+ return extractedFields;
1655
1920
  },
1656
- [originalPdfBytes, multiSignerContext]
1921
+ [extractedFields]
1657
1922
  );
1658
1923
  const fillPdf = useCallback(
1659
1924
  async (fieldValues, signatures, currentSignerEmail, metadata, auditTrail) => {
@@ -1796,6 +2061,20 @@ function usePdfViewer(multiSignerContext) {
1796
2061
  }
1797
2062
  function useFormFields(fields = []) {
1798
2063
  const [fieldValues, setFieldValues] = useState({});
2064
+ const seededFieldsRef = useRef(/* @__PURE__ */ new Set());
2065
+ useEffect(() => {
2066
+ if (fields.length === 0) return;
2067
+ const defaults = {};
2068
+ for (const field of fields) {
2069
+ if (field.defaultValue && field.defaultValue.trim() && !seededFieldsRef.current.has(field.id)) {
2070
+ defaults[field.id] = field.defaultValue;
2071
+ seededFieldsRef.current.add(field.id);
2072
+ }
2073
+ }
2074
+ if (Object.keys(defaults).length > 0) {
2075
+ setFieldValues((prev) => ({ ...defaults, ...prev }));
2076
+ }
2077
+ }, [fields]);
1799
2078
  const [errors, setErrors] = useState([]);
1800
2079
  const [touched, setTouched] = useState({});
1801
2080
  const updateField = useCallback((fieldId, value) => {
@@ -1835,14 +2114,14 @@ function useFormFields(fields = []) {
1835
2114
  if (!value || value.trim() === "") {
1836
2115
  newErrors.push({
1837
2116
  field: fieldId,
1838
- message: `${field.label || field.name} is required`
2117
+ message: `${getFieldDisplayName(field)} is required`
1839
2118
  });
1840
2119
  }
1841
2120
  }
1842
2121
  if (value && field.maxLength && value.length > field.maxLength) {
1843
2122
  newErrors.push({
1844
2123
  field: fieldId,
1845
- message: `${field.label || field.name} must be at most ${field.maxLength} characters`
2124
+ message: `${getFieldDisplayName(field)} must be at most ${field.maxLength} characters`
1846
2125
  });
1847
2126
  }
1848
2127
  if (value && field.type === "date") {
@@ -1868,13 +2147,13 @@ function useFormFields(fields = []) {
1868
2147
  if (!signatures[field.id]) {
1869
2148
  newErrors.push({
1870
2149
  field: field.id,
1871
- message: `${field.label || field.name} is required`
2150
+ message: `${getFieldDisplayName(field)} is required`
1872
2151
  });
1873
2152
  }
1874
2153
  } else if (!value2 || value2.trim() === "") {
1875
2154
  newErrors.push({
1876
2155
  field: field.id,
1877
- message: `${field.label || field.name} is required`
2156
+ message: `${getFieldDisplayName(field)} is required`
1878
2157
  });
1879
2158
  }
1880
2159
  }
@@ -1882,7 +2161,7 @@ function useFormFields(fields = []) {
1882
2161
  if (value && field.maxLength && value.length > field.maxLength) {
1883
2162
  newErrors.push({
1884
2163
  field: field.id,
1885
- message: `${field.label || field.name} must be at most ${field.maxLength} characters`
2164
+ message: `${getFieldDisplayName(field)} must be at most ${field.maxLength} characters`
1886
2165
  });
1887
2166
  }
1888
2167
  if (value && field.type === "date") {
@@ -2056,7 +2335,7 @@ function useSignatures() {
2056
2335
  for (const field of fields) {
2057
2336
  if (field.required && (field.type === "signature" || field.type === "initials")) {
2058
2337
  if (!signaturesToCheck[field.id]) {
2059
- errors.push(`${field.label || field.name} is required`);
2338
+ errors.push(`${getFieldDisplayName(field)} is required`);
2060
2339
  }
2061
2340
  }
2062
2341
  }
@@ -2174,6 +2453,13 @@ function useAttachments(options = {}) {
2174
2453
  errors.push(...validation.errors);
2175
2454
  continue;
2176
2455
  }
2456
+ const isDuplicate = [...attachments, ...newAttachments].some(
2457
+ (att) => att.name.toLowerCase() === file.name.toLowerCase()
2458
+ );
2459
+ if (isDuplicate) {
2460
+ errors.push(`"${file.name}" is already attached`);
2461
+ continue;
2462
+ }
2177
2463
  const currentTotalSize = [...attachments, ...newAttachments].reduce((sum, att) => sum + att.size, 0);
2178
2464
  if (currentTotalSize + file.size > constraints.maxTotalSize) {
2179
2465
  const maxTotalMB = (constraints.maxTotalSize / 1024 / 1024).toFixed(1);
@@ -2224,6 +2510,9 @@ function useAttachments(options = {}) {
2224
2510
  setAttachments([]);
2225
2511
  setValidationErrors([]);
2226
2512
  }, []);
2513
+ const clearValidationErrors = useCallback(() => {
2514
+ setValidationErrors([]);
2515
+ }, []);
2227
2516
  const getTotalSize = useCallback(() => {
2228
2517
  return attachments.reduce((sum, att) => sum + att.size, 0);
2229
2518
  }, [attachments]);
@@ -2255,6 +2544,7 @@ function useAttachments(options = {}) {
2255
2544
  addFiles,
2256
2545
  removeAttachment,
2257
2546
  clearAttachments,
2547
+ clearValidationErrors,
2258
2548
  // Utilities
2259
2549
  getTotalSize,
2260
2550
  formatSize,
@@ -2287,6 +2577,10 @@ function useAcknowledgements(fields) {
2287
2577
  const [acknowledgedMap, setAcknowledgedMap] = useState(
2288
2578
  /* @__PURE__ */ new Map()
2289
2579
  );
2580
+ const acknowledgedMapRef = useRef(acknowledgedMap);
2581
+ useEffect(() => {
2582
+ acknowledgedMapRef.current = acknowledgedMap;
2583
+ }, [acknowledgedMap]);
2290
2584
  const getFieldsWithAcknowledgements = useCallback((fieldsToFilter) => {
2291
2585
  return fieldsToFilter.filter(
2292
2586
  (field) => field.acknowledgements && field.acknowledgements.length > 0
@@ -2298,26 +2592,27 @@ function useAcknowledgements(fields) {
2298
2592
  const fieldAcks = newMap.get(fieldId) || /* @__PURE__ */ new Set();
2299
2593
  fieldAcks.add(ackId);
2300
2594
  newMap.set(fieldId, fieldAcks);
2595
+ acknowledgedMapRef.current = newMap;
2301
2596
  return newMap;
2302
2597
  });
2303
2598
  }, []);
2304
2599
  const isAcknowledged = useCallback((fieldId, ackId) => {
2305
- const fieldAcks = acknowledgedMap.get(fieldId);
2600
+ const fieldAcks = acknowledgedMapRef.current.get(fieldId);
2306
2601
  if (!fieldAcks) return false;
2307
2602
  if (ackId) {
2308
2603
  return fieldAcks.has(ackId);
2309
2604
  }
2310
2605
  return fieldAcks.size > 0;
2311
- }, [acknowledgedMap]);
2606
+ }, []);
2312
2607
  const isFieldFullyAcknowledged = useCallback((fieldId) => {
2313
2608
  const field = fields.find((f) => f.id === fieldId);
2314
2609
  if (!field || !field.acknowledgements || field.acknowledgements.length === 0) {
2315
2610
  return true;
2316
2611
  }
2317
- const fieldAcks = acknowledgedMap.get(fieldId);
2612
+ const fieldAcks = acknowledgedMapRef.current.get(fieldId);
2318
2613
  if (!fieldAcks) return false;
2319
2614
  return field.acknowledgements.every((ack) => fieldAcks.has(ack.id));
2320
- }, [fields, acknowledgedMap]);
2615
+ }, [fields]);
2321
2616
  const getFieldAcknowledgementProgress = useCallback((fieldId) => {
2322
2617
  const field = fields.find((f) => f.id === fieldId);
2323
2618
  if (!field || !field.acknowledgements || field.acknowledgements.length === 0) {