@signiphi/page-assembly 0.2.0-beta.2 → 0.2.0-beta.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs CHANGED
@@ -2,7 +2,7 @@ import * as React7 from 'react';
2
2
  import { forwardRef, useReducer, useState, useRef, useMemo, useEffect, useCallback, useImperativeHandle } from 'react';
3
3
  import { DndProvider, useDrop, useDrag } from 'react-dnd';
4
4
  import { HTML5Backend } from 'react-dnd-html5-backend';
5
- import { ChevronDown, ChevronUp, Check, ChevronRight, Circle, Upload, FolderOpen, FileText, Image as Image$1, Trash2, X, MoreHorizontal, GripVertical } from 'lucide-react';
5
+ import { ChevronDown, ChevronUp, Check, ChevronRight, Circle, Upload, X, FolderOpen, FileText, Image as Image$1, Trash2, MoreHorizontal, GripVertical } from 'lucide-react';
6
6
  import { Slot } from '@radix-ui/react-slot';
7
7
  import { cva } from 'class-variance-authority';
8
8
  import { clsx } from 'clsx';
@@ -14,7 +14,7 @@ import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
14
14
  import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
15
15
  import * as TooltipPrimitive from '@radix-ui/react-tooltip';
16
16
  import * as pdfjsLib from 'pdfjs-dist';
17
- import { PDFDocument } from 'pdf-lib';
17
+ import { PDFDocument, PDFTextField, PDFCheckBox, PDFDropdown, PDFRadioGroup, PDFHexString, PDFName, StandardFonts, rgb, PDFString, PDFOperator, PDFOperatorNames, PDFDict, PDFArray, PDFInvalidObject, PDFObjectParser, PDFRawStream, PDFObjectStreamParser, PDFRef, PDFBool, PDFNumber } from 'pdf-lib';
18
18
 
19
19
  // src/components/PageAssembler.tsx
20
20
  function cn(...inputs) {
@@ -220,8 +220,9 @@ var SelectContent = React7.forwardRef(({ className, children, position = "popper
220
220
  position,
221
221
  style: {
222
222
  zIndex: 99999,
223
- backgroundColor: "#ffffff",
224
- border: "1px solid #e2e8f0",
223
+ backgroundColor: "hsl(var(--xpa-popover))",
224
+ color: "hsl(var(--xpa-popover-foreground))",
225
+ border: "1px solid hsl(var(--xpa-border))",
225
226
  borderRadius: "0.5rem",
226
227
  boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)",
227
228
  marginTop: "4px"
@@ -315,16 +316,23 @@ var AlertDialogOverlay = React7.forwardRef(({ className, ...props }, ref) => /*
315
316
  }
316
317
  ));
317
318
  AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
318
- var AlertDialogContent = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxs(AlertDialogPortal, { container: document.querySelector(".signiphi-page-assembly"), children: [
319
+ var CENTERED = {
320
+ position: "fixed",
321
+ left: "50%",
322
+ top: "50%",
323
+ transform: "translate(-50%, -50%)"
324
+ };
325
+ var AlertDialogContent = React7.forwardRef(({ className, style, ...props }, ref) => /* @__PURE__ */ jsxs(AlertDialogPortal, { container: document.querySelector(".signiphi-page-assembly"), children: [
319
326
  /* @__PURE__ */ jsx(AlertDialogOverlay, {}),
320
327
  /* @__PURE__ */ jsx(
321
328
  AlertDialogPrimitive.Content,
322
329
  {
323
330
  ref,
324
331
  className: cn(
325
- "fixed left-[50%] top-[50%] z-[9999] grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
332
+ "z-[9999] grid w-full max-w-lg gap-4 border bg-background p-6 shadow-lg sm:rounded-lg",
326
333
  className
327
334
  ),
335
+ style: { ...CENTERED, ...style },
328
336
  ...props
329
337
  }
330
338
  )
@@ -440,8 +448,9 @@ var DropdownMenuContent = React7.forwardRef(({ className, sideOffset = 4, ...pro
440
448
  ),
441
449
  style: {
442
450
  zIndex: 99999,
443
- backgroundColor: "#ffffff",
444
- border: "1px solid #e2e8f0",
451
+ backgroundColor: "hsl(var(--xpa-popover))",
452
+ color: "hsl(var(--xpa-popover-foreground))",
453
+ border: "1px solid hsl(var(--xpa-border))",
445
454
  borderRadius: "0.5rem",
446
455
  boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)",
447
456
  padding: "6px"
@@ -547,6 +556,623 @@ var TooltipContent = React7.forwardRef(({ className, sideOffset = 4, ...props },
547
556
  }
548
557
  ));
549
558
  TooltipContent.displayName = TooltipPrimitive.Content.displayName;
559
+ var PdfPasswordRequiredError = class extends Error {
560
+ constructor() {
561
+ const message = "This PDF is password-protected. Please remove the password and upload it again.";
562
+ super(message);
563
+ this.code = "PDF_PASSWORD_REQUIRED";
564
+ this.recoverable = true;
565
+ this.name = "PdfPasswordRequiredError";
566
+ this.userMessage = message;
567
+ }
568
+ };
569
+ var UnsupportedPdfEncryptionError = class extends Error {
570
+ constructor(detail) {
571
+ super(`This PDF uses an unsupported encryption scheme (${detail}).`);
572
+ this.name = "UnsupportedPdfEncryptionError";
573
+ this.detail = detail;
574
+ }
575
+ };
576
+ function concatBytes(...parts) {
577
+ let total = 0;
578
+ for (const p of parts) total += p.length;
579
+ const out = new Uint8Array(total);
580
+ let offset = 0;
581
+ for (const p of parts) {
582
+ out.set(p, offset);
583
+ offset += p.length;
584
+ }
585
+ return out;
586
+ }
587
+ var MD5_S = [
588
+ 7,
589
+ 12,
590
+ 17,
591
+ 22,
592
+ 7,
593
+ 12,
594
+ 17,
595
+ 22,
596
+ 7,
597
+ 12,
598
+ 17,
599
+ 22,
600
+ 7,
601
+ 12,
602
+ 17,
603
+ 22,
604
+ 5,
605
+ 9,
606
+ 14,
607
+ 20,
608
+ 5,
609
+ 9,
610
+ 14,
611
+ 20,
612
+ 5,
613
+ 9,
614
+ 14,
615
+ 20,
616
+ 5,
617
+ 9,
618
+ 14,
619
+ 20,
620
+ 4,
621
+ 11,
622
+ 16,
623
+ 23,
624
+ 4,
625
+ 11,
626
+ 16,
627
+ 23,
628
+ 4,
629
+ 11,
630
+ 16,
631
+ 23,
632
+ 4,
633
+ 11,
634
+ 16,
635
+ 23,
636
+ 6,
637
+ 10,
638
+ 15,
639
+ 21,
640
+ 6,
641
+ 10,
642
+ 15,
643
+ 21,
644
+ 6,
645
+ 10,
646
+ 15,
647
+ 21,
648
+ 6,
649
+ 10,
650
+ 15,
651
+ 21
652
+ ];
653
+ var MD5_K = (() => {
654
+ const k = new Uint32Array(64);
655
+ for (let i = 0; i < 64; i++) k[i] = Math.floor(Math.abs(Math.sin(i + 1)) * 4294967296);
656
+ return k;
657
+ })();
658
+ function md5(input) {
659
+ const bitLen = input.length * 8;
660
+ const paddedLen = (input.length + 8 >> 6) + 1 << 6;
661
+ const buf = new Uint8Array(paddedLen);
662
+ buf.set(input);
663
+ buf[input.length] = 128;
664
+ const dv = new DataView(buf.buffer);
665
+ dv.setUint32(paddedLen - 8, bitLen >>> 0, true);
666
+ dv.setUint32(paddedLen - 4, Math.floor(bitLen / 4294967296), true);
667
+ let a0 = 1732584193;
668
+ let b0 = 4023233417;
669
+ let c0 = 2562383102;
670
+ let d0 = 271733878;
671
+ const M = new Uint32Array(16);
672
+ for (let offset = 0; offset < paddedLen; offset += 64) {
673
+ for (let i = 0; i < 16; i++) M[i] = dv.getUint32(offset + i * 4, true);
674
+ let A = a0;
675
+ let B = b0;
676
+ let C = c0;
677
+ let D = d0;
678
+ for (let i = 0; i < 64; i++) {
679
+ let F;
680
+ let g;
681
+ if (i < 16) {
682
+ F = B & C | ~B & D;
683
+ g = i;
684
+ } else if (i < 32) {
685
+ F = D & B | ~D & C;
686
+ g = 5 * i + 1 & 15;
687
+ } else if (i < 48) {
688
+ F = B ^ C ^ D;
689
+ g = 3 * i + 5 & 15;
690
+ } else {
691
+ F = C ^ (B | ~D);
692
+ g = 7 * i & 15;
693
+ }
694
+ const tmp = D;
695
+ D = C;
696
+ C = B;
697
+ const x = A + F + MD5_K[i] + M[g] | 0;
698
+ const s = MD5_S[i];
699
+ B = B + (x << s | x >>> 32 - s) | 0;
700
+ A = tmp;
701
+ }
702
+ a0 = a0 + A | 0;
703
+ b0 = b0 + B | 0;
704
+ c0 = c0 + C | 0;
705
+ d0 = d0 + D | 0;
706
+ }
707
+ const out = new Uint8Array(16);
708
+ const ov = new DataView(out.buffer);
709
+ ov.setUint32(0, a0 >>> 0, true);
710
+ ov.setUint32(4, b0 >>> 0, true);
711
+ ov.setUint32(8, c0 >>> 0, true);
712
+ ov.setUint32(12, d0 >>> 0, true);
713
+ return out;
714
+ }
715
+ function rc4(key, data) {
716
+ const S = new Uint8Array(256);
717
+ for (let i2 = 0; i2 < 256; i2++) S[i2] = i2;
718
+ let j = 0;
719
+ for (let i2 = 0; i2 < 256; i2++) {
720
+ j = j + S[i2] + key[i2 % key.length] & 255;
721
+ const t = S[i2];
722
+ S[i2] = S[j];
723
+ S[j] = t;
724
+ }
725
+ const out = new Uint8Array(data.length);
726
+ let i = 0;
727
+ j = 0;
728
+ for (let k = 0; k < data.length; k++) {
729
+ i = i + 1 & 255;
730
+ j = j + S[i] & 255;
731
+ const t = S[i];
732
+ S[i] = S[j];
733
+ S[j] = t;
734
+ out[k] = data[k] ^ S[S[i] + S[j] & 255];
735
+ }
736
+ return out;
737
+ }
738
+ function getSubtle() {
739
+ const subtle = globalThis.crypto?.subtle;
740
+ if (!subtle) throw new UnsupportedPdfEncryptionError("WebCrypto is unavailable in this environment");
741
+ return subtle;
742
+ }
743
+ async function sha2(algorithm, data) {
744
+ return new Uint8Array(await getSubtle().digest(algorithm, data));
745
+ }
746
+ async function importAesKey(rawKey, usages) {
747
+ return getSubtle().importKey("raw", rawKey, "AES-CBC", false, usages);
748
+ }
749
+ async function aesCbcEncryptNoPad(rawKey, iv, data) {
750
+ const key = await importAesKey(rawKey, ["encrypt"]);
751
+ const out = new Uint8Array(await getSubtle().encrypt({ name: "AES-CBC", iv }, key, data));
752
+ return out.slice(0, data.length);
753
+ }
754
+ async function aesCbcDecryptNoPad(rawKey, iv, data) {
755
+ const key = await importAesKey(rawKey, ["encrypt", "decrypt"]);
756
+ const lastBlock = data.slice(data.length - 16);
757
+ const padBlock = new Uint8Array(
758
+ await getSubtle().encrypt({ name: "AES-CBC", iv: lastBlock }, key, new Uint8Array(0))
759
+ );
760
+ const joined = concatBytes(data, padBlock);
761
+ return new Uint8Array(await getSubtle().decrypt({ name: "AES-CBC", iv }, key, joined));
762
+ }
763
+ async function aesDecryptPdfData(rawKey, data) {
764
+ if (data.length < 32) return new Uint8Array(0);
765
+ const iv = data.slice(0, 16);
766
+ let ct = data.slice(16);
767
+ if (ct.length % 16 !== 0) ct = ct.slice(0, ct.length - ct.length % 16);
768
+ try {
769
+ const key = await importAesKey(rawKey, ["decrypt"]);
770
+ return new Uint8Array(await getSubtle().decrypt({ name: "AES-CBC", iv }, key, ct));
771
+ } catch {
772
+ const plain = await aesCbcDecryptNoPad(rawKey, iv, ct);
773
+ const padLen = plain[plain.length - 1];
774
+ return padLen >= 1 && padLen <= 16 ? plain.slice(0, plain.length - padLen) : plain;
775
+ }
776
+ }
777
+ var PAD = new Uint8Array([
778
+ 40,
779
+ 191,
780
+ 78,
781
+ 94,
782
+ 78,
783
+ 117,
784
+ 138,
785
+ 65,
786
+ 100,
787
+ 0,
788
+ 78,
789
+ 86,
790
+ 255,
791
+ 250,
792
+ 1,
793
+ 8,
794
+ 46,
795
+ 46,
796
+ 0,
797
+ 182,
798
+ 208,
799
+ 104,
800
+ 62,
801
+ 128,
802
+ 47,
803
+ 12,
804
+ 169,
805
+ 254,
806
+ 100,
807
+ 83,
808
+ 105,
809
+ 122
810
+ ]);
811
+ var bytesEqual = (a, b) => {
812
+ if (a.length !== b.length) return false;
813
+ let diff = 0;
814
+ for (let i = 0; i < a.length; i++) diff |= a[i] ^ b[i];
815
+ return diff === 0;
816
+ };
817
+ function legacyFileKey(paddedPwd, params) {
818
+ const { revision, keyLen, O, P, id0, encryptMetadata } = params;
819
+ const pBytes = new Uint8Array(4);
820
+ new DataView(pBytes.buffer).setInt32(0, P | 0, true);
821
+ const parts = [paddedPwd, O.slice(0, 32), pBytes, id0];
822
+ if (revision >= 4 && !encryptMetadata) parts.push(new Uint8Array([255, 255, 255, 255]));
823
+ let h = md5(concatBytes(...parts));
824
+ if (revision >= 3) for (let i = 0; i < 50; i++) h = md5(h.slice(0, keyLen));
825
+ return h.slice(0, keyLen);
826
+ }
827
+ function legacyComputeU(fileKey, revision, id0) {
828
+ if (revision === 2) return rc4(fileKey, PAD);
829
+ let d = md5(concatBytes(PAD, id0));
830
+ d = rc4(fileKey, d);
831
+ for (let i = 1; i <= 19; i++) d = rc4(fileKey.map((b) => b ^ i), d);
832
+ return d;
833
+ }
834
+ function legacyUMatches(fileKey, params) {
835
+ const computed = legacyComputeU(fileKey, params.revision, params.id0);
836
+ return params.revision === 2 ? bytesEqual(computed, params.U.slice(0, 32)) : bytesEqual(computed.slice(0, 16), params.U.slice(0, 16));
837
+ }
838
+ function legacyAuthenticateEmptyPassword(params) {
839
+ const userKey = legacyFileKey(PAD, params);
840
+ if (legacyUMatches(userKey, params)) return userKey;
841
+ throw new PdfPasswordRequiredError();
842
+ }
843
+ async function hash2B(password, salt, udata) {
844
+ let k = await sha2("SHA-256", concatBytes(password, salt, udata));
845
+ let e = new Uint8Array(0);
846
+ let i = 0;
847
+ do {
848
+ const block = concatBytes(password, k, udata);
849
+ const k1 = new Uint8Array(block.length * 64);
850
+ for (let rep = 0; rep < 64; rep++) k1.set(block, rep * block.length);
851
+ e = await aesCbcEncryptNoPad(k.slice(0, 16), k.slice(16, 32), k1);
852
+ let sum = 0;
853
+ for (let b = 0; b < 16; b++) sum += e[b];
854
+ const mod = sum % 3;
855
+ k = await sha2(mod === 0 ? "SHA-256" : mod === 1 ? "SHA-384" : "SHA-512", e);
856
+ i++;
857
+ } while (i < 64 || e[e.length - 1] > i - 32);
858
+ return k.slice(0, 32);
859
+ }
860
+ async function v5AuthenticateEmptyPassword(revision, U, UE) {
861
+ const pwd = new Uint8Array(0);
862
+ const zeroIv = new Uint8Array(16);
863
+ const hash = async (salt, udata) => revision === 5 ? sha2("SHA-256", concatBytes(pwd, salt, udata)) : hash2B(pwd, salt, udata);
864
+ const userHash = await hash(U.slice(32, 40), new Uint8Array(0));
865
+ if (bytesEqual(userHash, U.slice(0, 32))) {
866
+ const intermediate = await hash(U.slice(40, 48), new Uint8Array(0));
867
+ return aesCbcDecryptNoPad(intermediate, zeroIv, UE.slice(0, 32));
868
+ }
869
+ throw new PdfPasswordRequiredError();
870
+ }
871
+ var identityCipher = async (data) => data;
872
+ function rc4ObjectCipher(fileKey) {
873
+ return async (data, ref) => {
874
+ const ext = new Uint8Array([
875
+ ref.objectNumber & 255,
876
+ ref.objectNumber >> 8 & 255,
877
+ ref.objectNumber >> 16 & 255,
878
+ ref.generationNumber & 255,
879
+ ref.generationNumber >> 8 & 255
880
+ ]);
881
+ const key = md5(concatBytes(fileKey, ext)).slice(0, Math.min(fileKey.length + 5, 16));
882
+ return rc4(key, data);
883
+ };
884
+ }
885
+ var AES_SALT = new Uint8Array([115, 65, 108, 84]);
886
+ function aes128ObjectCipher(fileKey) {
887
+ return async (data, ref) => {
888
+ const ext = new Uint8Array([
889
+ ref.objectNumber & 255,
890
+ ref.objectNumber >> 8 & 255,
891
+ ref.objectNumber >> 16 & 255,
892
+ ref.generationNumber & 255,
893
+ ref.generationNumber >> 8 & 255
894
+ ]);
895
+ const key = md5(concatBytes(fileKey, ext, AES_SALT)).slice(0, 16);
896
+ return aesDecryptPdfData(key, data);
897
+ };
898
+ }
899
+ function aes256ObjectCipher(fileKey) {
900
+ return async (data) => aesDecryptPdfData(fileKey, data);
901
+ }
902
+ function dictNumber(dict, key) {
903
+ const value = dict.lookup(PDFName.of(key));
904
+ return value instanceof PDFNumber ? value.asNumber() : void 0;
905
+ }
906
+ function dictBytes(dict, key) {
907
+ const value = dict.lookup(PDFName.of(key));
908
+ return value instanceof PDFString || value instanceof PDFHexString ? value.asBytes() : void 0;
909
+ }
910
+ async function buildDocumentCiphers(encDict, id0) {
911
+ const filter = encDict.lookup(PDFName.of("Filter"));
912
+ if (filter !== PDFName.of("Standard")) {
913
+ throw new UnsupportedPdfEncryptionError(`security handler ${String(filter)}`);
914
+ }
915
+ const V = dictNumber(encDict, "V") ?? 0;
916
+ const R = dictNumber(encDict, "R") ?? 0;
917
+ const O = dictBytes(encDict, "O");
918
+ const U = dictBytes(encDict, "U");
919
+ const P = dictNumber(encDict, "P") ?? -1;
920
+ const metadataValue = encDict.lookup(PDFName.of("EncryptMetadata"));
921
+ const encryptMetadata = metadataValue instanceof PDFBool ? metadataValue.asBoolean() : true;
922
+ if (!O || !U) throw new UnsupportedPdfEncryptionError("missing /O or /U");
923
+ if (V === 1 || V === 2) {
924
+ const keyLen = V === 1 ? 5 : (dictNumber(encDict, "Length") ?? 40) / 8;
925
+ const fileKey = legacyAuthenticateEmptyPassword({ revision: R, keyLen, O, U, P, id0, encryptMetadata });
926
+ const cipher = rc4ObjectCipher(fileKey);
927
+ return { stringCipher: cipher, streamCipher: cipher, encryptMetadata };
928
+ }
929
+ if (V === 4 || V === 5) {
930
+ const cf = encDict.lookup(PDFName.of("CF"));
931
+ const cfmOf = (filterKey) => {
932
+ const streamFilter = encDict.lookup(PDFName.of(filterKey));
933
+ if (!(streamFilter instanceof PDFName) || streamFilter === PDFName.of("Identity")) return void 0;
934
+ if (!(cf instanceof PDFDict)) throw new UnsupportedPdfEncryptionError("missing /CF");
935
+ const cfDict = cf.lookup(streamFilter);
936
+ if (!(cfDict instanceof PDFDict)) throw new UnsupportedPdfEncryptionError(`missing crypt filter ${streamFilter.toString()}`);
937
+ const cfm = cfDict.lookup(PDFName.of("CFM"));
938
+ return cfm instanceof PDFName ? cfm : void 0;
939
+ };
940
+ let fileKey;
941
+ if (V === 5) {
942
+ const UE = dictBytes(encDict, "UE");
943
+ if (!UE) throw new UnsupportedPdfEncryptionError("missing /UE");
944
+ fileKey = await v5AuthenticateEmptyPassword(R, U, UE);
945
+ } else {
946
+ const cryptFilterLengthBits = () => {
947
+ if (!(cf instanceof PDFDict)) return void 0;
948
+ for (const filterKey of ["StmF", "StrF"]) {
949
+ const filter2 = encDict.lookup(PDFName.of(filterKey));
950
+ if (!(filter2 instanceof PDFName) || filter2 === PDFName.of("Identity")) continue;
951
+ const cfDict = cf.lookup(filter2);
952
+ if (!(cfDict instanceof PDFDict)) continue;
953
+ const len = dictNumber(cfDict, "Length");
954
+ if (len === void 0) continue;
955
+ return len < 40 ? len * 8 : len;
956
+ }
957
+ return void 0;
958
+ };
959
+ const keyLenBits = cryptFilterLengthBits() ?? dictNumber(encDict, "Length") ?? 128;
960
+ fileKey = legacyAuthenticateEmptyPassword({
961
+ revision: R,
962
+ keyLen: keyLenBits / 8,
963
+ O,
964
+ U,
965
+ P,
966
+ id0,
967
+ encryptMetadata
968
+ });
969
+ }
970
+ const cipherFor = (cfm) => {
971
+ if (!cfm || cfm === PDFName.of("Identity")) return identityCipher;
972
+ if (cfm === PDFName.of("V2")) return rc4ObjectCipher(fileKey);
973
+ if (cfm === PDFName.of("AESV2")) return aes128ObjectCipher(fileKey);
974
+ if (cfm === PDFName.of("AESV3")) return aes256ObjectCipher(fileKey);
975
+ throw new UnsupportedPdfEncryptionError(`crypt filter method ${cfm.toString()}`);
976
+ };
977
+ return {
978
+ streamCipher: cipherFor(cfmOf("StmF")),
979
+ stringCipher: cipherFor(cfmOf("StrF")),
980
+ encryptMetadata
981
+ };
982
+ }
983
+ throw new UnsupportedPdfEncryptionError(`/V ${V}`);
984
+ }
985
+ var toHexString = (bytes) => {
986
+ let hex = "";
987
+ for (let i = 0; i < bytes.length; i++) hex += bytes[i].toString(16).padStart(2, "0");
988
+ return new PDFHexString(hex.toUpperCase());
989
+ };
990
+ function filterIncludesCrypt(dict) {
991
+ const filter = dict.lookup(PDFName.of("Filter"));
992
+ if (filter === PDFName.of("Crypt")) return true;
993
+ if (filter instanceof PDFArray) {
994
+ for (let i = 0; i < filter.size(); i++) {
995
+ if (filter.get(i) === PDFName.of("Crypt")) return true;
996
+ }
997
+ }
998
+ return false;
999
+ }
1000
+ function cryptFilterIsIdentity(dict) {
1001
+ const parms = dict.lookup(PDFName.of("DecodeParms")) ?? dict.lookup(PDFName.of("DP"));
1002
+ const identityParms = (p) => {
1003
+ if (!(p instanceof PDFDict)) return true;
1004
+ const name = p.lookup(PDFName.of("Name"));
1005
+ return !(name instanceof PDFName) || name === PDFName.of("Identity");
1006
+ };
1007
+ if (parms instanceof PDFArray) {
1008
+ for (let i = 0; i < parms.size(); i++) {
1009
+ if (!identityParms(parms.lookup(i))) return false;
1010
+ }
1011
+ return true;
1012
+ }
1013
+ return identityParms(parms);
1014
+ }
1015
+ async function decryptStringsWithin(obj, ref, stringCipher) {
1016
+ if (obj instanceof PDFDict) {
1017
+ const isSignature = obj.lookup(PDFName.of("Type")) === PDFName.of("Sig");
1018
+ for (const [key, value] of obj.entries()) {
1019
+ if (value instanceof PDFString || value instanceof PDFHexString) {
1020
+ if (isSignature && key === PDFName.of("Contents")) continue;
1021
+ obj.set(key, toHexString(await stringCipher(value.asBytes(), ref)));
1022
+ } else {
1023
+ await decryptStringsWithin(value, ref, stringCipher);
1024
+ }
1025
+ }
1026
+ } else if (obj instanceof PDFArray) {
1027
+ for (let i = 0; i < obj.size(); i++) {
1028
+ const value = obj.get(i);
1029
+ if (value instanceof PDFString || value instanceof PDFHexString) {
1030
+ obj.set(i, toHexString(await stringCipher(value.asBytes(), ref)));
1031
+ } else {
1032
+ await decryptStringsWithin(value, ref, stringCipher);
1033
+ }
1034
+ }
1035
+ }
1036
+ }
1037
+ async function decryptLoadedDocument(doc) {
1038
+ const context = doc.context;
1039
+ const encryptRef = context.trailerInfo.Encrypt;
1040
+ const encDict = context.lookup(encryptRef);
1041
+ if (!(encDict instanceof PDFDict)) {
1042
+ throw new UnsupportedPdfEncryptionError("malformed /Encrypt dictionary");
1043
+ }
1044
+ const idArray = context.lookup(context.trailerInfo.ID);
1045
+ const idFirst = idArray instanceof PDFArray ? idArray.get(0) : void 0;
1046
+ const id0 = idFirst instanceof PDFString || idFirst instanceof PDFHexString ? idFirst.asBytes() : new Uint8Array(0);
1047
+ const { stringCipher, streamCipher, encryptMetadata } = await buildDocumentCiphers(encDict, id0);
1048
+ for (const [ref, obj] of context.enumerateIndirectObjects()) {
1049
+ if (!(obj instanceof PDFInvalidObject)) continue;
1050
+ const raw = new Uint8Array(obj.sizeInBytes());
1051
+ obj.copyBytesInto(raw, 0);
1052
+ try {
1053
+ const parsed = PDFObjectParser.forBytes(raw, context).parseObject();
1054
+ if (parsed instanceof PDFRawStream && parsed.dict.lookup(PDFName.of("Type")) === PDFName.of("ObjStm")) {
1055
+ context.assign(ref, parsed);
1056
+ }
1057
+ } catch {
1058
+ }
1059
+ }
1060
+ const decryptedObjectStreams = [];
1061
+ const structuralStreamRefs = [];
1062
+ for (const [ref, obj] of context.enumerateIndirectObjects()) {
1063
+ if (ref === encryptRef || obj === encDict) continue;
1064
+ if (obj instanceof PDFRawStream) {
1065
+ const type = obj.dict.lookup(PDFName.of("Type"));
1066
+ if (type === PDFName.of("XRef")) {
1067
+ structuralStreamRefs.push(ref);
1068
+ continue;
1069
+ }
1070
+ const isSkippedMetadata = !encryptMetadata && type === PDFName.of("Metadata");
1071
+ if (filterIncludesCrypt(obj.dict)) {
1072
+ if (!cryptFilterIsIdentity(obj.dict)) {
1073
+ throw new UnsupportedPdfEncryptionError("stream-specific /Crypt filter");
1074
+ }
1075
+ } else if (!isSkippedMetadata) {
1076
+ const decrypted = await streamCipher(obj.contents, ref);
1077
+ const replacement = PDFRawStream.of(obj.dict, decrypted);
1078
+ context.assign(ref, replacement);
1079
+ if (type === PDFName.of("ObjStm")) {
1080
+ decryptedObjectStreams.push([ref, replacement]);
1081
+ }
1082
+ }
1083
+ await decryptStringsWithin(obj.dict, ref, stringCipher);
1084
+ } else if (obj instanceof PDFString || obj instanceof PDFHexString) {
1085
+ context.assign(ref, toHexString(await stringCipher(obj.asBytes(), ref)));
1086
+ } else {
1087
+ await decryptStringsWithin(obj, ref, stringCipher);
1088
+ }
1089
+ }
1090
+ for (const [ref, stream] of decryptedObjectStreams) {
1091
+ await PDFObjectStreamParser.forStream(stream).parseIntoContext();
1092
+ context.delete(ref);
1093
+ }
1094
+ for (const ref of structuralStreamRefs) context.delete(ref);
1095
+ delete context.trailerInfo.Encrypt;
1096
+ if (encryptRef instanceof PDFRef) context.delete(encryptRef);
1097
+ return doc.save({
1098
+ useObjectStreams: false,
1099
+ addDefaultPage: false,
1100
+ updateFieldAppearances: false
1101
+ });
1102
+ }
1103
+ async function decryptPdf(pdfBytes) {
1104
+ const doc = await PDFDocument.load(pdfBytes.slice(), {
1105
+ ignoreEncryption: true,
1106
+ updateMetadata: false
1107
+ });
1108
+ if (!doc.isEncrypted) return pdfBytes;
1109
+ return decryptLoadedDocument(doc);
1110
+ }
1111
+ var SLASH = 47;
1112
+ var HASH = 35;
1113
+ var ENCRYPT_NAME = "Encrypt";
1114
+ function hexDigit(byte) {
1115
+ if (byte === void 0) return -1;
1116
+ if (byte >= 48 && byte <= 57) return byte - 48;
1117
+ if (byte >= 65 && byte <= 70) return byte - 65 + 10;
1118
+ if (byte >= 97 && byte <= 102) return byte - 97 + 10;
1119
+ return -1;
1120
+ }
1121
+ function matchesEncryptNameAt(bytes, start) {
1122
+ let at = start;
1123
+ for (let k = 0; k < ENCRYPT_NAME.length; k++) {
1124
+ const want = ENCRYPT_NAME.charCodeAt(k);
1125
+ const byte = bytes[at];
1126
+ if (byte === want) {
1127
+ at += 1;
1128
+ continue;
1129
+ }
1130
+ if (byte === HASH) {
1131
+ const high = hexDigit(bytes[at + 1]);
1132
+ const low = hexDigit(bytes[at + 2]);
1133
+ if (high >= 0 && low >= 0 && (high << 4 | low) === want) {
1134
+ at += 3;
1135
+ continue;
1136
+ }
1137
+ }
1138
+ return false;
1139
+ }
1140
+ return true;
1141
+ }
1142
+ function containsEncryptMarker(bytes) {
1143
+ for (let i = 0; i < bytes.length; i++) {
1144
+ if (bytes[i] !== SLASH) continue;
1145
+ if (matchesEncryptNameAt(bytes, i + 1)) return true;
1146
+ }
1147
+ return false;
1148
+ }
1149
+ async function normalizePdfEncryption(pdfBytes, options) {
1150
+ if (!containsEncryptMarker(pdfBytes)) return pdfBytes;
1151
+ const doc = await PDFDocument.load(pdfBytes.slice(), {
1152
+ ignoreEncryption: true,
1153
+ updateMetadata: false
1154
+ });
1155
+ if (!doc.isEncrypted) return pdfBytes;
1156
+ options?.logger?.info("[PDF_DECRYPT] Encrypted PDF detected during ingestion; decrypting");
1157
+ return decryptLoadedDocument(doc);
1158
+ }
1159
+
1160
+ // src/utils/pdf-decrypt.ts
1161
+ var PdfPasswordRequiredError2 = PdfPasswordRequiredError;
1162
+ var UnsupportedPdfEncryptionError2 = UnsupportedPdfEncryptionError;
1163
+ var decryptPdf2 = decryptPdf;
1164
+ var consoleLogger = {
1165
+ info(message, ...args) {
1166
+ if (typeof process === "undefined" || process.env?.NODE_ENV !== "production") {
1167
+ console.info(`[PageAssembler] ${message}`, ...args);
1168
+ }
1169
+ }
1170
+ };
1171
+ function normalizePdfEncryption2(pdfBytes) {
1172
+ return normalizePdfEncryption(pdfBytes, { logger: consoleLogger });
1173
+ }
1174
+
1175
+ // src/utils/pdf-helpers.ts
550
1176
  if (typeof window !== "undefined") {
551
1177
  pdfjsLib.GlobalWorkerOptions.workerSrc = "/pdfjs/build/pdf.worker.mjs";
552
1178
  }
@@ -618,6 +1244,68 @@ async function pdfToImages(pdfBytes, options) {
618
1244
  throw error;
619
1245
  }
620
1246
  }
1247
+ var MAX_RASTER_PAGE_COUNT = 500;
1248
+ var MAX_RASTER_CANVAS_PIXELS = 4096 * 4096;
1249
+ var PREFERRED_RASTER_SCALE = 2;
1250
+ function computeRasterScale(width, height) {
1251
+ const area = width * height;
1252
+ if (!Number.isFinite(area) || area <= 0) return PREFERRED_RASTER_SCALE;
1253
+ const preferredPixels = area * PREFERRED_RASTER_SCALE * PREFERRED_RASTER_SCALE;
1254
+ if (preferredPixels <= MAX_RASTER_CANVAS_PIXELS) return PREFERRED_RASTER_SCALE;
1255
+ return Math.sqrt(MAX_RASTER_CANVAS_PIXELS / area);
1256
+ }
1257
+ async function rasterizePdfToPdf(pdfBytes) {
1258
+ if (typeof window !== "undefined") {
1259
+ pdfjsLib.GlobalWorkerOptions.workerSrc = "/pdfjs/build/pdf.worker.mjs";
1260
+ }
1261
+ let sourceDoc;
1262
+ try {
1263
+ sourceDoc = await pdfjsLib.getDocument({ data: pdfBytes.slice(0), verbosity: 0 }).promise;
1264
+ } catch (error) {
1265
+ if (error?.name === "PasswordException") {
1266
+ throw new PdfPasswordRequiredError2();
1267
+ }
1268
+ throw error;
1269
+ }
1270
+ try {
1271
+ if (sourceDoc.numPages > MAX_RASTER_PAGE_COUNT) {
1272
+ throw new Error(
1273
+ `Cannot rebuild PDF: ${sourceDoc.numPages} pages exceeds the ${MAX_RASTER_PAGE_COUNT}-page limit`
1274
+ );
1275
+ }
1276
+ const outDoc = await PDFDocument.create();
1277
+ for (let pageNum = 1; pageNum <= sourceDoc.numPages; pageNum++) {
1278
+ const page = await sourceDoc.getPage(pageNum);
1279
+ const base = page.getViewport({ scale: 1 });
1280
+ const scale = computeRasterScale(base.width, base.height);
1281
+ const viewport = page.getViewport({ scale });
1282
+ const canvas = document.createElement("canvas");
1283
+ canvas.width = viewport.width;
1284
+ canvas.height = viewport.height;
1285
+ const context = canvas.getContext("2d");
1286
+ await page.render({ canvasContext: context, viewport }).promise;
1287
+ const png = await outDoc.embedPng(canvas.toDataURL("image/png"));
1288
+ const outPage = outDoc.addPage([base.width, base.height]);
1289
+ outPage.drawImage(png, { x: 0, y: 0, width: base.width, height: base.height });
1290
+ }
1291
+ return new Uint8Array(await outDoc.save({ useObjectStreams: false }));
1292
+ } finally {
1293
+ await sourceDoc.destroy();
1294
+ }
1295
+ }
1296
+ async function normalizeUploadedPdfBytes(pdfBytes) {
1297
+ try {
1298
+ const normalized = await normalizePdfEncryption2(pdfBytes);
1299
+ return normalized === pdfBytes ? pdfBytes : new Uint8Array(normalized);
1300
+ } catch (error) {
1301
+ if (error instanceof PdfPasswordRequiredError2) throw error;
1302
+ console.warn(
1303
+ "[PageAssembler] Structural decryption failed; rebuilding document from rendered pages:",
1304
+ error
1305
+ );
1306
+ return rasterizePdfToPdf(pdfBytes);
1307
+ }
1308
+ }
621
1309
  async function imageToPdf(imageFile) {
622
1310
  return new Promise((resolve, reject) => {
623
1311
  const img = new Image();
@@ -693,6 +1381,701 @@ function downloadPdf(pdfBytes, filename) {
693
1381
  a.click();
694
1382
  URL.revokeObjectURL(url);
695
1383
  }
1384
+ var KEY = "SigniphiMetadata";
1385
+ function readMetadata(doc, pdfLib) {
1386
+ let blockPresent = false;
1387
+ try {
1388
+ const infoRef = doc.context.trailerInfo.Info;
1389
+ if (!infoRef) return { metadata: null, blockPresent };
1390
+ const info = doc.context.lookup(infoRef);
1391
+ if (!info || typeof info.get !== "function") return { metadata: null, blockPresent };
1392
+ const value = info.get(pdfLib.PDFName.of(KEY));
1393
+ if (!value) return { metadata: null, blockPresent };
1394
+ blockPresent = true;
1395
+ const json = typeof value.decodeText === "function" ? value.decodeText() : stripDelimiters(value.toString());
1396
+ return { metadata: JSON.parse(json), blockPresent };
1397
+ } catch {
1398
+ return { metadata: null, blockPresent };
1399
+ }
1400
+ }
1401
+ function writeMetadata(doc, metadata, pdfLib) {
1402
+ const info = doc.context.lookup(doc.context.trailerInfo.Info);
1403
+ if (!info || typeof info.set !== "function") {
1404
+ throw new Error("Cannot write SigniphiMetadata: the PDF has no Info dictionary");
1405
+ }
1406
+ info.set(pdfLib.PDFName.of(KEY), pdfLib.PDFHexString.fromText(JSON.stringify(metadata)));
1407
+ }
1408
+ function stripDelimiters(raw) {
1409
+ const wrapped = raw.startsWith("(") && raw.endsWith(")") || raw.startsWith("<") && raw.endsWith(">");
1410
+ return wrapped ? raw.slice(1, -1) : raw;
1411
+ }
1412
+ function textLabelPlacements(source) {
1413
+ const fields = source.metadata?.fields;
1414
+ if (!fields || !source.pages?.length) return [];
1415
+ const out = [];
1416
+ for (const [name, entry] of Object.entries(fields)) {
1417
+ if (entry.type !== "text_label" || !entry.position) continue;
1418
+ for (const { source: page, combined } of source.pages) {
1419
+ if (page === entry.position.page) out.push({ name, page: combined });
1420
+ }
1421
+ }
1422
+ return out;
1423
+ }
1424
+ function toCombinedPage(entry, page) {
1425
+ if (!entry.position && !entry.radioPositions && !entry.radioOptions) return entry;
1426
+ const carried = { ...entry };
1427
+ if (entry.position) carried.position = { ...entry.position, page };
1428
+ if (entry.radioPositions) carried.radioPositions = entry.radioPositions.map((p) => ({ ...p, page }));
1429
+ if (entry.radioOptions) {
1430
+ carried.radioOptions = entry.radioOptions.map((o) => ({ ...o, position: { ...o.position, page } }));
1431
+ }
1432
+ return carried;
1433
+ }
1434
+ function assembleMetadata(sources) {
1435
+ const merged = { version: "1.0", fields: {}, fieldIdIndex: {} };
1436
+ let radioLabelsBaked;
1437
+ let textLabelsBaked;
1438
+ let checkboxLabelsBaked;
1439
+ let legacyRadioStripped;
1440
+ for (const source of sources) {
1441
+ const meta = source.metadata;
1442
+ if (!meta || source.fields.length === 0 && !source.pages?.length) continue;
1443
+ if (meta.radioLabelsBaked === false) radioLabelsBaked = false;
1444
+ else if (meta.radioLabelsBaked === true && radioLabelsBaked === void 0) radioLabelsBaked = true;
1445
+ if (meta.textLabelsBaked === false) textLabelsBaked = false;
1446
+ else if (meta.textLabelsBaked === true && textLabelsBaked === void 0) textLabelsBaked = true;
1447
+ if (meta.checkboxLabelsBaked === false) checkboxLabelsBaked = false;
1448
+ else if (meta.checkboxLabelsBaked === true && checkboxLabelsBaked === void 0) checkboxLabelsBaked = true;
1449
+ legacyRadioStripped = meta.legacyRadioStripped === true && legacyRadioStripped !== false;
1450
+ }
1451
+ for (const source of sources) {
1452
+ const meta = source.metadata;
1453
+ if (!meta) continue;
1454
+ const sourceTextLabelsBaked = meta.textLabelsBaked !== false;
1455
+ for (const { name, page } of [...source.fields, ...textLabelPlacements(source)]) {
1456
+ const key = `${name}_p${page}`;
1457
+ const entry = meta.fields?.[name];
1458
+ if (!entry) continue;
1459
+ const carried = toCombinedPage(entry, page);
1460
+ merged.fields[key] = textLabelsBaked === false && sourceTextLabelsBaked ? { ...carried, textLabelBaked: true } : carried;
1461
+ if (entry.fieldId) merged.fieldIdIndex[entry.fieldId] = key;
1462
+ }
1463
+ }
1464
+ if (radioLabelsBaked !== void 0) merged.radioLabelsBaked = radioLabelsBaked;
1465
+ if (textLabelsBaked !== void 0) merged.textLabelsBaked = textLabelsBaked;
1466
+ if (checkboxLabelsBaked !== void 0) merged.checkboxLabelsBaked = checkboxLabelsBaked;
1467
+ if (legacyRadioStripped) merged.legacyRadioStripped = true;
1468
+ const hasEntries = Object.keys(merged.fields).length > 0;
1469
+ const hasMarkers = radioLabelsBaked !== void 0 || textLabelsBaked !== void 0 || checkboxLabelsBaked !== void 0;
1470
+ if (!hasEntries && !hasMarkers) return null;
1471
+ if (Object.keys(merged.fieldIdIndex).length === 0) delete merged.fieldIdIndex;
1472
+ return merged;
1473
+ }
1474
+ function mergeSourceMetadata(sources) {
1475
+ return assembleMetadata(sources);
1476
+ }
1477
+ function getSigniphiMetadata(pdfDoc) {
1478
+ const { metadata, blockPresent } = readMetadata(pdfDoc, { PDFName: PDFName });
1479
+ if (blockPresent && !metadata) {
1480
+ console.error("Failed to load Signiphi metadata: the block is present but will not parse");
1481
+ }
1482
+ return metadata;
1483
+ }
1484
+ function getBaseFieldName(encodedName) {
1485
+ let baseName = encodedName;
1486
+ if (baseName.includes("__SIGNER__")) {
1487
+ baseName = baseName.split("__SIGNER__")[0];
1488
+ }
1489
+ if (baseName.includes("__LABEL__")) {
1490
+ baseName = baseName.split("__LABEL__")[0];
1491
+ }
1492
+ if (baseName.includes("__PLACEHOLDER__")) {
1493
+ baseName = baseName.split("__PLACEHOLDER__")[0];
1494
+ }
1495
+ return baseName;
1496
+ }
1497
+ function stripFieldTypeSuffix(value) {
1498
+ const stripped = value.replace(/_(signature|initials|date)$/i, "");
1499
+ return stripped || value;
1500
+ }
1501
+ function getSignerFromFieldName(encodedName) {
1502
+ if (encodedName.includes("__SIGNER__")) {
1503
+ const parts = encodedName.split("__SIGNER__");
1504
+ if (parts[1]) {
1505
+ const segment = parts[1].split("__")[0] || parts[1];
1506
+ const isTerminal = segment === parts[1];
1507
+ return isTerminal ? stripFieldTypeSuffix(segment) : segment;
1508
+ }
1509
+ }
1510
+ return void 0;
1511
+ }
1512
+ function determineFieldType(fieldName, widgetType) {
1513
+ const baseName = getBaseFieldName(fieldName);
1514
+ const nameLower = baseName.toLowerCase();
1515
+ if (nameLower.includes("_signature") || nameLower.includes("signature")) {
1516
+ return "signature";
1517
+ }
1518
+ if (nameLower.includes("_initials") || nameLower.includes("initials")) {
1519
+ return "initials";
1520
+ }
1521
+ if (nameLower.includes("_date") || nameLower.includes("date")) {
1522
+ return "date";
1523
+ }
1524
+ if (widgetType === "checkbox" || nameLower.includes("checkbox")) {
1525
+ return "checkbox";
1526
+ }
1527
+ if (widgetType === "radio" || nameLower.includes("radio")) {
1528
+ return "radio";
1529
+ }
1530
+ if (widgetType === "dropdown" || widgetType === "select" || nameLower.includes("dropdown")) {
1531
+ return "dropdown";
1532
+ }
1533
+ return "text";
1534
+ }
1535
+ async function extractFieldsFromPdf(pdfBytes) {
1536
+ const pdfDoc = await PDFDocument.load(pdfBytes);
1537
+ const fields = [];
1538
+ const metadata = getSigniphiMetadata(pdfDoc);
1539
+ const form = pdfDoc.getForm();
1540
+ const pdfFields = form.getFields();
1541
+ const pages = pdfDoc.getPages();
1542
+ const pageHeights = pages.map((p) => p.getHeight());
1543
+ const pageIndexByRef = new Map(pages.map((p, i) => [p.ref, i + 1]));
1544
+ const pageNumberOf = (ref) => ref && pageIndexByRef.get(ref) || 1;
1545
+ for (const field of pdfFields) {
1546
+ const fieldName = field.getName();
1547
+ const widgets = field.acroField.getWidgets();
1548
+ const baseName = getBaseFieldName(fieldName);
1549
+ const signerFromName = getSignerFromFieldName(fieldName);
1550
+ let required = false;
1551
+ let options;
1552
+ let widgetType;
1553
+ if (field instanceof PDFTextField) {
1554
+ required = field.isRequired();
1555
+ console.log(`[field-extraction] PDFTextField "${fieldName}" isRequired=${required}`);
1556
+ } else if (field instanceof PDFCheckBox) {
1557
+ widgetType = "checkbox";
1558
+ required = field.isRequired();
1559
+ console.log(`[field-extraction] PDFCheckBox "${fieldName}" isRequired=${required}`);
1560
+ } else if (field instanceof PDFDropdown) {
1561
+ widgetType = "dropdown";
1562
+ required = field.isRequired();
1563
+ options = field.getOptions();
1564
+ console.log(`[field-extraction] PDFDropdown "${fieldName}" isRequired=${required}`);
1565
+ } else if (field instanceof PDFRadioGroup) {
1566
+ widgetType = "radio";
1567
+ required = field.isRequired();
1568
+ options = field.getOptions();
1569
+ console.log(`[field-extraction] PDFRadioGroup "${fieldName}" isRequired=${required}`);
1570
+ } else {
1571
+ console.log(`[field-extraction] Unknown field type "${fieldName}" - defaulting required=false`);
1572
+ }
1573
+ const optionPositionsByPage = /* @__PURE__ */ new Map();
1574
+ if (widgetType === "radio") {
1575
+ for (const w of widgets) {
1576
+ const pageNumber = pageNumberOf(w.P());
1577
+ const r = w.getRectangle();
1578
+ const top = pageHeights[pageNumber - 1] - r.y - r.height;
1579
+ const bucket = optionPositionsByPage.get(pageNumber) ?? [];
1580
+ bucket.push({ x: r.x, y: top, width: r.width, height: r.height });
1581
+ optionPositionsByPage.set(pageNumber, bucket);
1582
+ }
1583
+ }
1584
+ for (const widget of widgets) {
1585
+ const rect = widget.getRectangle();
1586
+ const pageNumber = pageNumberOf(widget.P());
1587
+ const pageQualifiedKey = `${baseName}_p${pageNumber}`;
1588
+ let fieldMetadata = metadata?.fields?.[pageQualifiedKey];
1589
+ if (!fieldMetadata) {
1590
+ fieldMetadata = metadata?.fields?.[fieldName];
1591
+ }
1592
+ if (!fieldMetadata && baseName !== fieldName) {
1593
+ fieldMetadata = metadata?.fields?.[baseName];
1594
+ }
1595
+ let fieldRequired = required;
1596
+ let fieldOptions = options;
1597
+ if (fieldMetadata?.required !== void 0) {
1598
+ fieldRequired = fieldMetadata.required;
1599
+ }
1600
+ if (fieldMetadata?.options?.length) {
1601
+ fieldOptions = fieldMetadata.options;
1602
+ }
1603
+ const pageHeight = pageHeights[pageNumber - 1];
1604
+ const extractedField = {
1605
+ name: baseName,
1606
+ // Use clean base name without __SIGNER__ encoding (signer stored in signer property)
1607
+ // Uses base name internally. A stored date_signed is a declaration the
1608
+ // name can't express, so it wins.
1609
+ type: fieldMetadata?.type === "date_signed" ? "date_signed" : determineFieldType(fieldName, widgetType),
1610
+ page: pageNumber,
1611
+ x: rect.x,
1612
+ y: pageHeight - rect.y - rect.height,
1613
+ // Convert from PDF coords (bottom-left) to top-left
1614
+ width: rect.width,
1615
+ height: rect.height,
1616
+ required: fieldRequired,
1617
+ options: fieldOptions,
1618
+ signer: signerFromName
1619
+ // Extract signer from encoded name
1620
+ };
1621
+ if (widgetType === "radio") {
1622
+ extractedField.optionPositions = optionPositionsByPage.get(pageNumber) ?? [];
1623
+ }
1624
+ if (fieldMetadata) {
1625
+ if (fieldMetadata.fieldId) {
1626
+ extractedField.fieldId = fieldMetadata.fieldId;
1627
+ }
1628
+ if (fieldMetadata.label) {
1629
+ extractedField.label = fieldMetadata.label;
1630
+ }
1631
+ if (!extractedField.signer && fieldMetadata.signer) {
1632
+ extractedField.signer = fieldMetadata.signer;
1633
+ }
1634
+ if (fieldMetadata.placeholder) {
1635
+ extractedField.placeholder = fieldMetadata.placeholder;
1636
+ }
1637
+ }
1638
+ fields.push(extractedField);
1639
+ }
1640
+ }
1641
+ const deduplicatedFields = fields.reduce((acc, field) => {
1642
+ const newBaseName = getBaseFieldName(field.name);
1643
+ const existingIndex = acc.findIndex((f) => {
1644
+ const existingBaseName = getBaseFieldName(f.name);
1645
+ return existingBaseName === newBaseName && f.page === field.page;
1646
+ });
1647
+ if (existingIndex === -1) {
1648
+ acc.push(field);
1649
+ } else {
1650
+ const existing = acc[existingIndex];
1651
+ acc[existingIndex] = {
1652
+ ...existing,
1653
+ // Prefer non-empty values from either field
1654
+ label: existing.label || field.label,
1655
+ placeholder: existing.placeholder || field.placeholder,
1656
+ required: existing.required || field.required,
1657
+ options: existing.options?.length ? existing.options : field.options,
1658
+ fieldId: existing.fieldId || field.fieldId,
1659
+ // Prefer signer from the field that has it
1660
+ signer: existing.signer || field.signer
1661
+ };
1662
+ }
1663
+ return acc;
1664
+ }, []);
1665
+ return deduplicatedFields;
1666
+ }
1667
+ function setSigniphiMetadata(pdfDoc, metadata) {
1668
+ try {
1669
+ writeMetadata(pdfDoc, metadata, { PDFName: PDFName, PDFHexString: PDFHexString });
1670
+ } catch (error) {
1671
+ console.error("Failed to set Signiphi metadata:", error);
1672
+ throw error;
1673
+ }
1674
+ }
1675
+ var SIGNIPHI_CHECKBOX_LABEL_MARKER = "SiPL_Check";
1676
+ var RADIO_BORDER_WIDTH = 1;
1677
+ var FormFieldType = /* @__PURE__ */ ((FormFieldType2) => {
1678
+ FormFieldType2["TEXT"] = "text";
1679
+ FormFieldType2["SIGNATURE"] = "signature";
1680
+ FormFieldType2["INITIALS"] = "initials";
1681
+ FormFieldType2["DATE"] = "date";
1682
+ FormFieldType2["CHECKBOX"] = "checkbox";
1683
+ FormFieldType2["RADIO"] = "radio";
1684
+ FormFieldType2["DROPDOWN"] = "dropdown";
1685
+ FormFieldType2["TEXT_LABEL"] = "text_label";
1686
+ FormFieldType2["DATE_SIGNED"] = "date_signed";
1687
+ return FormFieldType2;
1688
+ })(FormFieldType || {});
1689
+ function parseEncodedFieldName(encodedName) {
1690
+ let baseName = encodedName;
1691
+ let label;
1692
+ let signer;
1693
+ if (baseName.includes("__SIGNER__")) {
1694
+ const parts = baseName.split("__SIGNER__");
1695
+ baseName = parts[0];
1696
+ signer = parts[1];
1697
+ }
1698
+ if (baseName.includes("__LABEL__")) {
1699
+ const parts = baseName.split("__LABEL__");
1700
+ baseName = parts[0];
1701
+ label = parts[1];
1702
+ }
1703
+ if (baseName.includes("__PLACEHOLDER__")) {
1704
+ const parts = baseName.split("__PLACEHOLDER__");
1705
+ baseName = parts[0];
1706
+ }
1707
+ return { baseName, label, signer };
1708
+ }
1709
+ function encodeFieldNameWithSigner(fieldName, signerEmail) {
1710
+ const { baseName, signer: existingSigner } = parseEncodedFieldName(fieldName);
1711
+ const finalSigner = existingSigner || signerEmail;
1712
+ if (!finalSigner) return baseName;
1713
+ return `${baseName}__SIGNER__${finalSigner}`;
1714
+ }
1715
+ function encodeFieldNameWithLabelAndSigner(fieldName, label, signerEmail) {
1716
+ const { baseName, label: existingLabel, signer: existingSigner } = parseEncodedFieldName(fieldName);
1717
+ const finalLabel = existingLabel || label;
1718
+ const finalSigner = existingSigner || signerEmail;
1719
+ let name = baseName;
1720
+ if (finalLabel && finalLabel.trim()) {
1721
+ name = `${name}__LABEL__${finalLabel.trim()}`;
1722
+ }
1723
+ if (finalSigner) {
1724
+ name = `${name}__SIGNER__${finalSigner}`;
1725
+ }
1726
+ return name;
1727
+ }
1728
+ async function addFormFieldsToPdf(pdfBytes, formFields, options = {}) {
1729
+ const { removeExistingFields = false, drawLabels = false } = options;
1730
+ const pdfDoc = await PDFDocument.load(pdfBytes);
1731
+ const form = pdfDoc.getForm();
1732
+ const pages = pdfDoc.getPages();
1733
+ if (removeExistingFields) {
1734
+ const existingFields = form.getFields();
1735
+ for (const field of existingFields) {
1736
+ try {
1737
+ const acroField = field.acroField;
1738
+ if (acroField && typeof acroField.getWidgets === "function") {
1739
+ let widgets = acroField.getWidgets();
1740
+ let safetyCounter = 0;
1741
+ const maxIterations = widgets.length + 5;
1742
+ while (widgets.length > 0 && safetyCounter < maxIterations) {
1743
+ try {
1744
+ acroField.removeWidget(widgets.length - 1);
1745
+ widgets = acroField.getWidgets();
1746
+ } catch {
1747
+ break;
1748
+ }
1749
+ safetyCounter++;
1750
+ }
1751
+ }
1752
+ form.removeField(field);
1753
+ } catch (error) {
1754
+ console.warn(`Failed to remove field ${field.getName()}:`, error);
1755
+ try {
1756
+ form.removeField(field);
1757
+ } catch {
1758
+ }
1759
+ }
1760
+ }
1761
+ }
1762
+ const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
1763
+ const bakedCheckboxCaptions = /* @__PURE__ */ new Map();
1764
+ const fieldNamePages = /* @__PURE__ */ new Map();
1765
+ for (const field of formFields) {
1766
+ const baseName = field.name;
1767
+ if (!fieldNamePages.has(baseName)) {
1768
+ fieldNamePages.set(baseName, /* @__PURE__ */ new Set());
1769
+ }
1770
+ fieldNamePages.get(baseName).add(field.position.page);
1771
+ }
1772
+ const conflictingNames = /* @__PURE__ */ new Set();
1773
+ for (const [name, pages2] of fieldNamePages) {
1774
+ if (pages2.size > 1) {
1775
+ conflictingNames.add(name);
1776
+ }
1777
+ }
1778
+ for (const field of formFields) {
1779
+ const pageIndex = field.position.page - 1;
1780
+ if (pageIndex < 0 || pageIndex >= pages.length) {
1781
+ console.warn(`Field ${field.name} references page ${field.position.page} but PDF only has ${pages.length} pages`);
1782
+ continue;
1783
+ }
1784
+ const page = pages[pageIndex];
1785
+ const { height: pageHeight } = page.getSize();
1786
+ const pdfX = field.position.x;
1787
+ const pdfY = pageHeight - field.position.y - field.position.height;
1788
+ const uniqueFieldName = conflictingNames.has(field.name) ? `${field.name}_p${field.position.page}` : field.name;
1789
+ try {
1790
+ const fieldType = typeof field.type === "string" ? field.type : field.type;
1791
+ switch (fieldType) {
1792
+ case "text" /* TEXT */: {
1793
+ const encodedName = encodeFieldNameWithSigner(uniqueFieldName, field.assignedSignerEmail);
1794
+ const textField = form.createTextField(encodedName);
1795
+ textField.addToPage(page, {
1796
+ x: pdfX,
1797
+ y: pdfY,
1798
+ width: field.position.width,
1799
+ height: field.position.height,
1800
+ borderColor: rgb(0.5, 0.5, 0.5),
1801
+ backgroundColor: rgb(1, 1, 1)
1802
+ });
1803
+ if (field.label && field.label.trim()) {
1804
+ textField.acroField.dict.set(PDFName.of("TU"), PDFString.of(field.label));
1805
+ if (drawLabels) {
1806
+ const fontSize = Math.min(10, field.position.height * 0.4);
1807
+ const labelY = pdfY + field.position.height + 5;
1808
+ page.drawText(field.label, { x: pdfX, y: labelY, size: fontSize, font, color: rgb(0, 0, 0) });
1809
+ }
1810
+ }
1811
+ if (field.defaultValue && field.defaultValue.trim()) {
1812
+ textField.setText(field.defaultValue);
1813
+ }
1814
+ if (field.fontSize && field.fontSize >= 8 && field.fontSize <= 72) {
1815
+ try {
1816
+ textField.setFontSize(field.fontSize);
1817
+ } catch {
1818
+ }
1819
+ }
1820
+ if (field.multiline) {
1821
+ try {
1822
+ textField.enableMultiline();
1823
+ } catch {
1824
+ }
1825
+ }
1826
+ if (field.maxLength && field.maxLength > 0) {
1827
+ try {
1828
+ textField.setMaxLength(field.maxLength);
1829
+ } catch {
1830
+ }
1831
+ }
1832
+ if (field.required) {
1833
+ textField.enableRequired();
1834
+ }
1835
+ break;
1836
+ }
1837
+ case "checkbox" /* CHECKBOX */: {
1838
+ const encodedName = encodeFieldNameWithSigner(uniqueFieldName, field.assignedSignerEmail);
1839
+ const checkBox = form.createCheckBox(encodedName);
1840
+ checkBox.addToPage(page, {
1841
+ x: pdfX,
1842
+ y: pdfY,
1843
+ width: field.position.width,
1844
+ height: field.position.height,
1845
+ borderColor: rgb(0.5, 0.5, 0.5),
1846
+ backgroundColor: rgb(1, 1, 1)
1847
+ });
1848
+ if (field.defaultValue === "true" || field.defaultValue === "checked") {
1849
+ checkBox.check();
1850
+ }
1851
+ if (field.required) {
1852
+ checkBox.enableRequired();
1853
+ }
1854
+ if (field.label && field.label.trim()) {
1855
+ try {
1856
+ checkBox.acroField.dict.set(PDFName.of("TU"), PDFString.of(field.label));
1857
+ } catch {
1858
+ }
1859
+ if (drawLabels) {
1860
+ const fontSize = Math.max(1, field.position.height * 0.4);
1861
+ const labelX = pdfX + field.position.width + 5;
1862
+ const labelY = pdfY + (field.position.height - fontSize) / 2;
1863
+ page.pushOperators(
1864
+ PDFOperator.of(PDFOperatorNames.BeginMarkedContent, [PDFName.of(SIGNIPHI_CHECKBOX_LABEL_MARKER)])
1865
+ );
1866
+ page.drawText(field.label, { x: labelX, y: labelY, size: fontSize, font, color: rgb(0, 0, 0) });
1867
+ page.pushOperators(PDFOperator.of(PDFOperatorNames.EndMarkedContent));
1868
+ bakedCheckboxCaptions.set(encodedName, field);
1869
+ }
1870
+ }
1871
+ break;
1872
+ }
1873
+ case "signature" /* SIGNATURE */: {
1874
+ const baseWithSuffix = field.name.endsWith("_signature") ? field.name : `${field.name}_signature`;
1875
+ const finalName = conflictingNames.has(field.name) ? `${baseWithSuffix}_p${field.position.page}` : baseWithSuffix;
1876
+ const encodedName = encodeFieldNameWithLabelAndSigner(finalName, field.label, field.assignedSignerEmail);
1877
+ const sigField = form.createTextField(encodedName);
1878
+ sigField.addToPage(page, {
1879
+ x: pdfX,
1880
+ y: pdfY,
1881
+ width: field.position.width,
1882
+ height: field.position.height,
1883
+ borderColor: rgb(0, 0, 1),
1884
+ backgroundColor: rgb(0.9, 0.9, 1)
1885
+ });
1886
+ if (field.label && field.label.trim()) {
1887
+ sigField.acroField.dict.set(PDFName.of("TU"), PDFString.of(field.label));
1888
+ }
1889
+ sigField.enableReadOnly();
1890
+ if (field.required) {
1891
+ sigField.enableRequired();
1892
+ }
1893
+ break;
1894
+ }
1895
+ case "initials" /* INITIALS */: {
1896
+ const baseWithSuffix = field.name.endsWith("_initials") ? field.name : `${field.name}_initials`;
1897
+ const finalName = conflictingNames.has(field.name) ? `${baseWithSuffix}_p${field.position.page}` : baseWithSuffix;
1898
+ const encodedName = encodeFieldNameWithLabelAndSigner(finalName, field.label, field.assignedSignerEmail);
1899
+ const initField = form.createTextField(encodedName);
1900
+ initField.addToPage(page, {
1901
+ x: pdfX,
1902
+ y: pdfY,
1903
+ width: field.position.width,
1904
+ height: field.position.height,
1905
+ borderColor: rgb(0.5, 0, 0.5),
1906
+ backgroundColor: rgb(1, 0.95, 1)
1907
+ });
1908
+ if (field.label && field.label.trim()) {
1909
+ initField.acroField.dict.set(PDFName.of("TU"), PDFString.of(field.label));
1910
+ }
1911
+ initField.enableReadOnly();
1912
+ if (field.required) {
1913
+ initField.enableRequired();
1914
+ }
1915
+ break;
1916
+ }
1917
+ // DATE_SIGNED is the same widget; its metadata `type` (carried by the
1918
+ // merge, not by this pass) is what tells pdf-signer to stamp it.
1919
+ case "date" /* DATE */:
1920
+ case "date_signed" /* DATE_SIGNED */: {
1921
+ const baseWithSuffix = field.name.endsWith("_date") ? field.name : `${field.name}_date`;
1922
+ const finalName = conflictingNames.has(field.name) ? `${baseWithSuffix}_p${field.position.page}` : baseWithSuffix;
1923
+ const encodedName = encodeFieldNameWithLabelAndSigner(finalName, field.label, field.assignedSignerEmail);
1924
+ const dateField = form.createTextField(encodedName);
1925
+ dateField.addToPage(page, {
1926
+ x: pdfX,
1927
+ y: pdfY,
1928
+ width: field.position.width,
1929
+ height: field.position.height,
1930
+ borderColor: rgb(0, 0.5, 0),
1931
+ backgroundColor: rgb(0.95, 1, 0.95)
1932
+ });
1933
+ if (field.label && field.label.trim()) {
1934
+ dateField.acroField.dict.set(PDFName.of("TU"), PDFString.of(field.label));
1935
+ }
1936
+ if (field.required) {
1937
+ dateField.enableRequired();
1938
+ }
1939
+ break;
1940
+ }
1941
+ case "dropdown" /* DROPDOWN */: {
1942
+ const encodedName = encodeFieldNameWithSigner(uniqueFieldName, field.assignedSignerEmail);
1943
+ const dropdown = form.createDropdown(encodedName);
1944
+ dropdown.addToPage(page, {
1945
+ x: pdfX,
1946
+ y: pdfY,
1947
+ width: field.position.width,
1948
+ height: field.position.height,
1949
+ borderColor: rgb(0.5, 0.5, 0.5),
1950
+ backgroundColor: rgb(1, 1, 1)
1951
+ });
1952
+ if (field.options && field.options.length > 0) {
1953
+ dropdown.addOptions(field.options);
1954
+ if (field.defaultValue && field.options.includes(field.defaultValue)) {
1955
+ dropdown.select(field.defaultValue);
1956
+ }
1957
+ }
1958
+ if (field.label && field.label.trim()) {
1959
+ try {
1960
+ dropdown.acroField.dict.set(PDFName.of("TU"), PDFString.of(field.label));
1961
+ } catch {
1962
+ }
1963
+ if (drawLabels) {
1964
+ const fontSize = Math.min(10, field.position.height * 0.4);
1965
+ const labelY = pdfY + field.position.height + 5;
1966
+ page.drawText(field.label, { x: pdfX, y: labelY, size: fontSize, font, color: rgb(0, 0, 0) });
1967
+ }
1968
+ }
1969
+ if (field.required) {
1970
+ dropdown.enableRequired();
1971
+ }
1972
+ break;
1973
+ }
1974
+ case "radio" /* RADIO */: {
1975
+ const encodedName = encodeFieldNameWithSigner(uniqueFieldName, field.assignedSignerEmail);
1976
+ const radioGroup = form.createRadioGroup(encodedName);
1977
+ if (field.options && field.options.length > 0 && field.optionPositions?.length === field.options.length) {
1978
+ const optionPositions = field.optionPositions;
1979
+ field.options.forEach((option, index) => {
1980
+ const at = optionPositions[index];
1981
+ radioGroup.addOptionToPage(option, page, {
1982
+ x: at.x + RADIO_BORDER_WIDTH / 2,
1983
+ y: pageHeight - at.y - at.height + RADIO_BORDER_WIDTH / 2,
1984
+ width: at.width - RADIO_BORDER_WIDTH,
1985
+ height: at.height - RADIO_BORDER_WIDTH,
1986
+ borderWidth: RADIO_BORDER_WIDTH,
1987
+ borderColor: rgb(0.5, 0.5, 0.5),
1988
+ backgroundColor: rgb(1, 1, 1)
1989
+ });
1990
+ });
1991
+ if (field.defaultValue && field.options.includes(field.defaultValue)) {
1992
+ radioGroup.select(field.defaultValue);
1993
+ }
1994
+ } else if (field.options && field.options.length > 0) {
1995
+ const optionHeight = Math.min(20, field.position.height / field.options.length);
1996
+ const spacing = field.position.height / field.options.length;
1997
+ field.options.forEach((option, index) => {
1998
+ const optionY = pdfY + field.position.height - (index + 1) * spacing + (spacing - optionHeight) / 2;
1999
+ radioGroup.addOptionToPage(option, page, {
2000
+ x: pdfX,
2001
+ y: optionY,
2002
+ width: optionHeight,
2003
+ height: optionHeight,
2004
+ borderColor: rgb(0.5, 0.5, 0.5),
2005
+ backgroundColor: rgb(1, 1, 1)
2006
+ });
2007
+ if (drawLabels) {
2008
+ const labelX = pdfX + optionHeight + 5;
2009
+ const labelY = optionY + optionHeight / 4;
2010
+ page.drawText(option, { x: labelX, y: labelY, size: 10, font, color: rgb(0, 0, 0) });
2011
+ }
2012
+ });
2013
+ if (field.defaultValue && field.options.includes(field.defaultValue)) {
2014
+ radioGroup.select(field.defaultValue);
2015
+ }
2016
+ }
2017
+ if (field.label && field.label.trim()) {
2018
+ try {
2019
+ radioGroup.acroField.dict.set(PDFName.of("TU"), PDFString.of(field.label));
2020
+ } catch {
2021
+ }
2022
+ if (drawLabels) {
2023
+ const fontSize = 10;
2024
+ const labelY = pdfY + field.position.height + 5;
2025
+ page.drawText(field.label, { x: pdfX, y: labelY, size: fontSize, font, color: rgb(0, 0, 0) });
2026
+ }
2027
+ }
2028
+ if (field.required) {
2029
+ radioGroup.enableRequired();
2030
+ }
2031
+ break;
2032
+ }
2033
+ default:
2034
+ console.warn(`Unknown field type: ${fieldType} for field ${field.name}`);
2035
+ }
2036
+ } catch (error) {
2037
+ console.error(`Error adding field ${field.name}:`, error);
2038
+ }
2039
+ }
2040
+ recordBakedCheckboxCaptions(pdfDoc, bakedCheckboxCaptions);
2041
+ return await pdfDoc.save();
2042
+ }
2043
+ function recordBakedCheckboxCaptions(pdfDoc, captions) {
2044
+ if (captions.size === 0) return;
2045
+ try {
2046
+ const metadata = getSigniphiMetadata(pdfDoc);
2047
+ if (!metadata) return;
2048
+ metadata.fields ?? (metadata.fields = {});
2049
+ for (const [name, field] of captions) {
2050
+ metadata.fields[name] = {
2051
+ signer: field.assignedSignerEmail,
2052
+ label: field.label,
2053
+ ...metadata.fields[name],
2054
+ checkboxCaptionBaked: true
2055
+ };
2056
+ }
2057
+ setSigniphiMetadata(pdfDoc, metadata);
2058
+ } catch (error) {
2059
+ console.warn("Failed to record baked checkbox captions:", error);
2060
+ }
2061
+ }
2062
+ function mapFieldPositionsAfterAssembly(fields, pageMapping) {
2063
+ return fields.map((field) => {
2064
+ const mappingKey = String(field.position.page - 1);
2065
+ const newPageNumber = pageMapping.get(mappingKey);
2066
+ if (newPageNumber !== void 0) {
2067
+ return {
2068
+ ...field,
2069
+ position: { ...field.position, page: newPageNumber }
2070
+ };
2071
+ }
2072
+ return field;
2073
+ }).filter((field) => {
2074
+ const mappingKey = String(field.position.page - 1);
2075
+ return pageMapping.has(mappingKey) || pageMapping.size === 0;
2076
+ });
2077
+ }
2078
+ var VISIBLE_PAGE_ROWS = 2;
696
2079
  var isMultiSelectModifier = (event) => {
697
2080
  return event.ctrlKey || event.metaKey;
698
2081
  };
@@ -948,7 +2331,7 @@ var DraggablePageThumbnail = ({
948
2331
  ref,
949
2332
  "data-handler-id": handlerId,
950
2333
  className: cn(
951
- "relative group cursor-pointer border-2 rounded-lg overflow-hidden bg-white transition-all",
2334
+ "relative group cursor-pointer border-2 rounded-lg overflow-hidden bg-card transition-all",
952
2335
  isDragging && "opacity-50 scale-95",
953
2336
  isSelected ? "border-primary shadow-md" : "border-gray-200 hover:border-gray-300"
954
2337
  ),
@@ -976,7 +2359,7 @@ var DraggablePageThumbnail = ({
976
2359
  }
977
2360
  },
978
2361
  onContextMenu: handleContextMenu,
979
- className: "h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary focus:ring-2 bg-white/90 cursor-pointer"
2362
+ className: "h-4 w-4 rounded border-border text-primary focus:ring-primary focus:ring-2 bg-card/90 cursor-pointer"
980
2363
  }
981
2364
  )
982
2365
  }
@@ -1019,21 +2402,48 @@ var ReorderablePageGrid = ({
1019
2402
  newPages.splice(hoverIndex, 0, draggedPage);
1020
2403
  onReorderPages(file.id, newPages);
1021
2404
  }, [file.id, file.pages, onReorderPages]);
1022
- return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4", children: file.pages.map((page, index) => /* @__PURE__ */ jsx(
1023
- DraggablePageThumbnail,
2405
+ const gridRef = useRef(null);
2406
+ const [maxHeight, setMaxHeight] = useState(void 0);
2407
+ useEffect(() => {
2408
+ const grid = gridRef.current;
2409
+ if (!grid || typeof ResizeObserver === "undefined") return;
2410
+ const measure = () => {
2411
+ const firstThumbnail = grid.firstElementChild;
2412
+ if (!firstThumbnail) return;
2413
+ const rowHeight = firstThumbnail.getBoundingClientRect().height;
2414
+ if (rowHeight <= 0) return;
2415
+ const rowGap = parseFloat(getComputedStyle(grid).rowGap) || 0;
2416
+ setMaxHeight(Math.ceil(rowHeight * VISIBLE_PAGE_ROWS + rowGap * (VISIBLE_PAGE_ROWS - 1)));
2417
+ };
2418
+ measure();
2419
+ const observer = new ResizeObserver(measure);
2420
+ observer.observe(grid);
2421
+ if (grid.firstElementChild) observer.observe(grid.firstElementChild);
2422
+ return () => observer.disconnect();
2423
+ }, [file.pages]);
2424
+ return /* @__PURE__ */ jsx(
2425
+ "div",
1024
2426
  {
1025
- page,
1026
- fileId: file.id,
1027
- index,
1028
- isSelected: selectedPageIds.includes(page.id),
1029
- onSingleSelect: onSingleSelectPage,
1030
- onToggleSelect: onTogglePageSelect,
1031
- onRangeSelect: onRangeSelectPage,
1032
- movePageInFile,
1033
- isDraggingPageRef
1034
- },
1035
- page.id
1036
- )) });
2427
+ ref: gridRef,
2428
+ className: "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4 overflow-y-auto overflow-x-hidden",
2429
+ style: { maxHeight },
2430
+ children: file.pages.map((page, index) => /* @__PURE__ */ jsx(
2431
+ DraggablePageThumbnail,
2432
+ {
2433
+ page,
2434
+ fileId: file.id,
2435
+ index,
2436
+ isSelected: selectedPageIds.includes(page.id),
2437
+ onSingleSelect: onSingleSelectPage,
2438
+ onToggleSelect: onTogglePageSelect,
2439
+ onRangeSelect: onRangeSelectPage,
2440
+ movePageInFile,
2441
+ isDraggingPageRef
2442
+ },
2443
+ page.id
2444
+ ))
2445
+ }
2446
+ );
1037
2447
  };
1038
2448
  var FileDropZone = ({
1039
2449
  targetIndex,
@@ -1089,6 +2499,7 @@ var FileCard = ({
1089
2499
  const [newName, setNewName] = useState(file.name);
1090
2500
  const fileSelectedPages = file.pages.filter((p) => selectedPageIds.includes(p.id));
1091
2501
  const allPagesSelected = file.pages.length > 0 && fileSelectedPages.length === file.pages.length;
2502
+ const pendingDropdownAction = useRef(null);
1092
2503
  const [{ isDraggingFile }, dragFile] = useDrag({
1093
2504
  type: DND_TYPES.FILE,
1094
2505
  item: () => {
@@ -1193,23 +2604,38 @@ var FileCard = ({
1193
2604
  ] }) }),
1194
2605
  /* @__PURE__ */ jsxs(DropdownMenu, { children: [
1195
2606
  /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "hover:bg-gray-100 rounded-lg", children: /* @__PURE__ */ jsx(MoreHorizontal, { className: "h-4 w-4" }) }) }),
1196
- /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", className: "w-52", children: [
1197
- /* @__PURE__ */ jsxs(DropdownMenuItem, { onClick: () => setIsRenaming(true), children: [
1198
- /* @__PURE__ */ jsx("svg", { className: "h-4 w-4 mr-2", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" }) }),
1199
- "Rename File"
1200
- ] }),
1201
- /* @__PURE__ */ jsxs(
1202
- DropdownMenuItem,
1203
- {
1204
- onClick: () => onRemoveFile(file.id),
1205
- className: "text-red-600 hover:text-red-700 hover:bg-red-50 focus:text-red-700 focus:bg-red-50",
1206
- children: [
1207
- /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4 mr-2" }),
1208
- "Remove File"
1209
- ]
1210
- }
1211
- )
1212
- ] })
2607
+ /* @__PURE__ */ jsxs(
2608
+ DropdownMenuContent,
2609
+ {
2610
+ align: "end",
2611
+ className: "w-52",
2612
+ onCloseAutoFocus: () => {
2613
+ pendingDropdownAction.current?.();
2614
+ pendingDropdownAction.current = null;
2615
+ },
2616
+ children: [
2617
+ /* @__PURE__ */ jsxs(DropdownMenuItem, { onSelect: () => {
2618
+ pendingDropdownAction.current = () => setIsRenaming(true);
2619
+ }, children: [
2620
+ /* @__PURE__ */ jsx("svg", { className: "h-4 w-4 mr-2", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" }) }),
2621
+ "Rename File"
2622
+ ] }),
2623
+ /* @__PURE__ */ jsxs(
2624
+ DropdownMenuItem,
2625
+ {
2626
+ onSelect: () => {
2627
+ pendingDropdownAction.current = () => onRemoveFile(file.id);
2628
+ },
2629
+ className: "text-red-600 hover:text-red-700 hover:bg-red-50 focus:text-red-700 focus:bg-red-50",
2630
+ children: [
2631
+ /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4 mr-2" }),
2632
+ "Remove File"
2633
+ ]
2634
+ }
2635
+ )
2636
+ ]
2637
+ }
2638
+ )
1213
2639
  ] })
1214
2640
  ] })
1215
2641
  ] }) }),
@@ -1261,7 +2687,7 @@ var BulkActionBar = ({
1261
2687
  /* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Move selected pages to another file" }) })
1262
2688
  ] }) }),
1263
2689
  /* @__PURE__ */ jsxs(Select, { onValueChange: onMovePages, children: [
1264
- /* @__PURE__ */ jsx(SelectTrigger, { id: "move-select", className: "w-48 bg-white", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Choose file..." }) }),
2690
+ /* @__PURE__ */ jsx(SelectTrigger, { id: "move-select", className: "w-48 bg-card", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Choose file..." }) }),
1265
2691
  /* @__PURE__ */ jsx(SelectContent, { children: files.map((file) => /* @__PURE__ */ jsx(SelectItem, { value: file.id, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1266
2692
  file.type === "pdf" ? /* @__PURE__ */ jsx(FileText, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(Image$1, { className: "h-4 w-4" }),
1267
2693
  /* @__PURE__ */ jsx("span", { className: "truncate", children: file.name })
@@ -1290,7 +2716,7 @@ var BulkActionBar = ({
1290
2716
  variant: "outline",
1291
2717
  size: "sm",
1292
2718
  onClick: onClearSelection,
1293
- className: "bg-white",
2719
+ className: "bg-card",
1294
2720
  children: "Clear Selection"
1295
2721
  }
1296
2722
  )
@@ -1318,6 +2744,7 @@ var PageAssembler = forwardRef(
1318
2744
  );
1319
2745
  const [isLoading, setIsLoading] = useState(false);
1320
2746
  const [processingStatus, setProcessingStatus] = useState("");
2747
+ const [uploadError, setUploadError] = useState(null);
1321
2748
  const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
1322
2749
  const [showRemoveFileConfirm, setShowRemoveFileConfirm] = useState(false);
1323
2750
  const [fileToRemove, setFileToRemove] = useState(null);
@@ -1325,6 +2752,7 @@ var PageAssembler = forwardRef(
1325
2752
  const processedInitialPdfRef = useRef(null);
1326
2753
  const isDraggingPage = useRef(false);
1327
2754
  const onChangeRef = useRef(onChange);
2755
+ const originalBytesCache = useRef(/* @__PURE__ */ new Map());
1328
2756
  const summary = useMemo(() => ({
1329
2757
  totalFiles: state.files.length,
1330
2758
  totalPages: state.files.reduce((sum, file) => sum + file.pages.length, 0)
@@ -1358,6 +2786,7 @@ var PageAssembler = forwardRef(
1358
2786
  } else {
1359
2787
  pdfBytes = new Uint8Array(pdf);
1360
2788
  }
2789
+ pdfBytes = await normalizeUploadedPdfBytes(pdfBytes);
1361
2790
  const defensiveCopy = new Uint8Array(pdfBytes);
1362
2791
  const pages = await pdfToImages(defensiveCopy, { hideFormFields: true });
1363
2792
  if (pages.length === 0) {
@@ -1394,6 +2823,7 @@ var PageAssembler = forwardRef(
1394
2823
  originalFileId: fileId
1395
2824
  }))
1396
2825
  };
2826
+ originalBytesCache.current.set(fileId, originalBytes);
1397
2827
  dispatch({ type: "ADD_FILE", payload: file });
1398
2828
  } catch (error) {
1399
2829
  console.error("Error processing initial PDF:", error);
@@ -1402,8 +2832,9 @@ var PageAssembler = forwardRef(
1402
2832
  setProcessingStatus("");
1403
2833
  }
1404
2834
  }, []);
2835
+ const hasRestorableState = !!initialState && initialState.files.length > 0;
1405
2836
  useEffect(() => {
1406
- if (initialPdf && !initialState) {
2837
+ if (initialPdf && state.files.length === 0 && !hasRestorableState) {
1407
2838
  const pdfIdentifier = initialPdf instanceof Uint8Array ? `uint8array-${initialPdf.length}-${initialPdf[0]}-${initialPdf[initialPdf.length - 1]}` : `${initialPdf.constructor.name}-${initialPdf.size || initialPdf.byteLength}`;
1408
2839
  if (processedInitialPdfRef.current === pdfIdentifier) {
1409
2840
  return;
@@ -1411,15 +2842,16 @@ var PageAssembler = forwardRef(
1411
2842
  processedInitialPdfRef.current = pdfIdentifier;
1412
2843
  processInitialPdf(initialPdf);
1413
2844
  }
1414
- }, [initialPdf, initialState, processInitialPdf]);
2845
+ }, [initialPdf, state.files.length, hasRestorableState, processInitialPdf]);
1415
2846
  useEffect(() => {
1416
- if (initialState) {
2847
+ if (initialState && initialState.files.length > 0) {
1417
2848
  dispatch({ type: "RESET", payload: initialState });
1418
2849
  }
1419
2850
  }, [initialState]);
1420
2851
  const processFiles = useCallback(async (files) => {
1421
2852
  const fileArray = Array.from(files);
1422
2853
  const maxFileSize = (limits?.maxFileSizeMB || 10) * 1024 * 1024;
2854
+ setUploadError(null);
1423
2855
  setIsLoading(true);
1424
2856
  setProcessingStatus(`Processing ${fileArray.length} file${fileArray.length !== 1 ? "s" : ""}...`);
1425
2857
  for (const file of fileArray) {
@@ -1444,8 +2876,9 @@ var PageAssembler = forwardRef(
1444
2876
  if (file.type === "application/pdf") {
1445
2877
  setProcessingStatus(`Processing PDF: ${file.name}`);
1446
2878
  const arrayBuffer = await file.arrayBuffer();
1447
- const pdfBytes = new Uint8Array(arrayBuffer);
2879
+ const pdfBytes = await normalizeUploadedPdfBytes(new Uint8Array(arrayBuffer));
1448
2880
  fileInfo.originalBytes = new Uint8Array(pdfBytes);
2881
+ originalBytesCache.current.set(fileInfo.id, fileInfo.originalBytes);
1449
2882
  const defensiveCopy = new Uint8Array(pdfBytes);
1450
2883
  const pages = await pdfToImages(defensiveCopy, { hideFormFields: true });
1451
2884
  fileInfo.pages = pages.map((page, index) => ({
@@ -1462,6 +2895,7 @@ var PageAssembler = forwardRef(
1462
2895
  const arrayBuffer = await file.arrayBuffer();
1463
2896
  const imageBytes = new Uint8Array(arrayBuffer);
1464
2897
  fileInfo.originalBytes = new Uint8Array(imageBytes);
2898
+ originalBytesCache.current.set(fileInfo.id, fileInfo.originalBytes);
1465
2899
  const thumbUrl = URL.createObjectURL(file);
1466
2900
  const img = new Image();
1467
2901
  const dimensions = await new Promise((resolve) => {
@@ -1492,6 +2926,11 @@ var PageAssembler = forwardRef(
1492
2926
  dispatch({ type: "ADD_FILE", payload: fileInfo });
1493
2927
  } catch (error) {
1494
2928
  console.error(`Error processing file ${file.name}:`, error);
2929
+ if (error instanceof PdfPasswordRequiredError2) {
2930
+ setUploadError(`${file.name}: ${error.userMessage}`);
2931
+ } else {
2932
+ setUploadError(`Failed to process "${file.name}". The file may be corrupted or unsupported.`);
2933
+ }
1495
2934
  }
1496
2935
  }
1497
2936
  setIsLoading(false);
@@ -1589,21 +3028,207 @@ var PageAssembler = forwardRef(
1589
3028
  setIsLoading(true);
1590
3029
  setProcessingStatus("Assembling PDF...");
1591
3030
  try {
1592
- const { PDFDocument: PDFDocument2 } = await import('pdf-lib');
1593
- const combinedDoc = await PDFDocument2.create();
3031
+ const { PDFDocument: PDFDocument5, PDFName: PDFName4 } = await import('pdf-lib');
1594
3032
  const loadedPdfs = /* @__PURE__ */ new Map();
1595
3033
  const fileMap = /* @__PURE__ */ new Map();
3034
+ const pdfFiles = state.files.filter((f) => f.type === "pdf" && f.originalBytes?.length > 0);
3035
+ const imageFiles = state.files.filter((f) => f.type === "image");
3036
+ const isSinglePdfScenario = pdfFiles.length === 1 && imageFiles.length === 0;
1596
3037
  for (const file of state.files) {
1597
3038
  fileMap.set(file.id, file);
1598
3039
  if (file.type === "pdf" && file.originalBytes && file.originalBytes.length > 0) {
1599
3040
  try {
1600
- const sourcePdf = await PDFDocument2.load(file.originalBytes);
3041
+ const sourcePdf = await PDFDocument5.load(file.originalBytes);
1601
3042
  loadedPdfs.set(file.id, sourcePdf);
1602
3043
  } catch (error) {
1603
3044
  console.error(`Error loading PDF for file ${file.id}:`, error);
1604
3045
  }
1605
3046
  }
1606
3047
  }
3048
+ const referencedFileIds = /* @__PURE__ */ new Set();
3049
+ for (const file of state.files) {
3050
+ for (const page of file.pages) {
3051
+ const originalFileId = page.originalFileId || file.id;
3052
+ if (!loadedPdfs.has(originalFileId)) {
3053
+ referencedFileIds.add(originalFileId);
3054
+ }
3055
+ }
3056
+ }
3057
+ for (const fileId of referencedFileIds) {
3058
+ const cachedBytes = originalBytesCache.current.get(fileId);
3059
+ if (cachedBytes && cachedBytes.length > 0) {
3060
+ try {
3061
+ console.log(`[PageAssembler] Loading PDF from cache for deleted file ${fileId}`);
3062
+ const sourcePdf = await PDFDocument5.load(cachedBytes);
3063
+ loadedPdfs.set(fileId, sourcePdf);
3064
+ } catch (error) {
3065
+ console.error(`Error loading cached PDF for file ${fileId}:`, error);
3066
+ }
3067
+ }
3068
+ }
3069
+ console.log("[PageAssembler] Checking single PDF scenario:", {
3070
+ isSinglePdfScenario,
3071
+ pdfFilesCount: pdfFiles.length,
3072
+ imageFilesCount: imageFiles.length
3073
+ });
3074
+ if (isSinglePdfScenario && pdfFiles[0]) {
3075
+ const sourceFile = pdfFiles[0];
3076
+ const sourcePdf = loadedPdfs.get(sourceFile.id);
3077
+ console.log("[PageAssembler] Single PDF check:", {
3078
+ sourcePdfLoaded: !!sourcePdf,
3079
+ pagesCount: sourceFile.pages.length,
3080
+ originalBytesLength: sourceFile.originalBytes?.length
3081
+ });
3082
+ if (sourcePdf) {
3083
+ const desiredPageIndices = sourceFile.pages.map((p) => p.srcIndex);
3084
+ const originalPageCount = sourcePdf.getPageCount();
3085
+ const allPagesFromSameFile = sourceFile.pages.every(
3086
+ (p) => (p.originalFileId || sourceFile.id) === sourceFile.id
3087
+ );
3088
+ const isOriginalOrder = desiredPageIndices.length === originalPageCount && desiredPageIndices.every((idx, i) => idx === i);
3089
+ console.log("[PageAssembler] Original bytes check:", {
3090
+ allPagesFromSameFile,
3091
+ isOriginalOrder,
3092
+ desiredPageIndices,
3093
+ originalPageCount
3094
+ });
3095
+ if (allPagesFromSameFile && isOriginalOrder) {
3096
+ console.log("[PageAssembler] \u2705 Returning ORIGINAL bytes (form fields preserved)");
3097
+ const arrayBuffer2 = sourceFile.originalBytes.buffer.slice(
3098
+ sourceFile.originalBytes.byteOffset,
3099
+ sourceFile.originalBytes.byteOffset + sourceFile.originalBytes.byteLength
3100
+ );
3101
+ const blob2 = new Blob([arrayBuffer2], { type: "application/pdf" });
3102
+ return blob2;
3103
+ } else {
3104
+ console.log("[PageAssembler] \u26A0\uFE0F Pages modified, will use copyPages (form fields may be lost)");
3105
+ }
3106
+ }
3107
+ }
3108
+ console.log("[PageAssembler] Starting multi-PDF assembly with field preservation");
3109
+ const allExtractedFields = [];
3110
+ const metadataSources = [];
3111
+ const filePageUsage = /* @__PURE__ */ new Map();
3112
+ for (const file of state.files) {
3113
+ for (const page of file.pages) {
3114
+ const originalFileId = page.originalFileId || file.id;
3115
+ if (!filePageUsage.has(originalFileId)) {
3116
+ filePageUsage.set(originalFileId, /* @__PURE__ */ new Map());
3117
+ }
3118
+ const pageUsage = filePageUsage.get(originalFileId);
3119
+ if (!pageUsage.has(page.srcIndex)) {
3120
+ pageUsage.set(page.srcIndex, []);
3121
+ }
3122
+ pageUsage.get(page.srcIndex).push(page);
3123
+ }
3124
+ }
3125
+ let combinedPageIndex = 0;
3126
+ const pageIdToNewPageNumber = /* @__PURE__ */ new Map();
3127
+ for (const file of state.files) {
3128
+ for (const page of file.pages) {
3129
+ combinedPageIndex++;
3130
+ pageIdToNewPageNumber.set(page.id, combinedPageIndex);
3131
+ }
3132
+ }
3133
+ console.log("[PageAssembler] Page mapping:", Object.fromEntries(pageIdToNewPageNumber));
3134
+ const extractAndMapFields = async (fileId, fileName, pdfBytes2, sourcePdf) => {
3135
+ const sourceMetadata = sourcePdf ? getSigniphiMetadata(sourcePdf) : null;
3136
+ if (sourcePdf && !sourceMetadata) {
3137
+ console.warn(`[PageAssembler] No readable SigniphiMetadata in "${fileName}"`);
3138
+ }
3139
+ const pageUsageForFile = filePageUsage.get(fileId) || /* @__PURE__ */ new Map();
3140
+ const placements = [];
3141
+ const pageLandings = [];
3142
+ for (const [srcIndex, pageInstances] of pageUsageForFile) {
3143
+ for (const pageInstance of pageInstances) {
3144
+ const combined = pageIdToNewPageNumber.get(pageInstance.id);
3145
+ if (combined !== void 0) pageLandings.push({ source: srcIndex + 1, combined });
3146
+ }
3147
+ }
3148
+ metadataSources.push({ metadata: sourceMetadata, fields: placements, pages: pageLandings });
3149
+ console.log(`[PageAssembler] Extracting fields from "${fileName}"`);
3150
+ const extractedFields = await extractFieldsFromPdf(pdfBytes2);
3151
+ console.log(`[PageAssembler] Found ${extractedFields.length} fields in "${fileName}"`);
3152
+ for (const field of extractedFields) {
3153
+ const originalPage = field.page;
3154
+ const srcIndex = originalPage - 1;
3155
+ const pageInstances = pageUsageForFile.get(srcIndex) || [];
3156
+ for (const pageInstance of pageInstances) {
3157
+ const newPageNumber = pageIdToNewPageNumber.get(pageInstance.id);
3158
+ if (newPageNumber !== void 0) {
3159
+ const trackedField = {
3160
+ name: field.name,
3161
+ type: field.type,
3162
+ position: {
3163
+ x: field.x,
3164
+ y: field.y,
3165
+ width: field.width,
3166
+ height: field.height,
3167
+ page: newPageNumber
3168
+ // Updated to new page number
3169
+ },
3170
+ pageId: pageInstance.id,
3171
+ originalFileId: fileId,
3172
+ label: field.label,
3173
+ required: field.required,
3174
+ options: field.options,
3175
+ optionPositions: field.optionPositions,
3176
+ placeholder: field.placeholder,
3177
+ fieldId: field.fieldId,
3178
+ assignedSignerEmail: field.signer
3179
+ };
3180
+ allExtractedFields.push(trackedField);
3181
+ console.log(`[PageAssembler] Mapped field "${field.name}" from page ${originalPage} to page ${newPageNumber}`);
3182
+ placements.push({ name: field.name, page: newPageNumber });
3183
+ }
3184
+ }
3185
+ }
3186
+ };
3187
+ const processedFileIds = /* @__PURE__ */ new Set();
3188
+ for (const file of state.files) {
3189
+ if (file.type !== "pdf" || !file.originalBytes || file.originalBytes.length === 0) continue;
3190
+ try {
3191
+ await extractAndMapFields(file.id, file.name, file.originalBytes, loadedPdfs.get(file.id));
3192
+ processedFileIds.add(file.id);
3193
+ } catch (extractError) {
3194
+ console.error(`Error extracting fields from ${file.name}:`, extractError);
3195
+ }
3196
+ }
3197
+ for (const fileId of referencedFileIds) {
3198
+ if (processedFileIds.has(fileId)) continue;
3199
+ const cachedBytes = originalBytesCache.current.get(fileId);
3200
+ if (cachedBytes && cachedBytes.length > 0) {
3201
+ try {
3202
+ console.log(`[PageAssembler] Extracting fields from cached file ${fileId}`);
3203
+ await extractAndMapFields(fileId, `Cached file ${fileId}`, cachedBytes, loadedPdfs.get(fileId));
3204
+ } catch (extractError) {
3205
+ console.error(`Error extracting fields from cached file ${fileId}:`, extractError);
3206
+ }
3207
+ }
3208
+ }
3209
+ console.log(`[PageAssembler] Total extracted fields: ${allExtractedFields.length}`);
3210
+ console.log(`[PageAssembler] Field names:`, allExtractedFields.map((f) => `${f.name} (page ${f.position.page})`));
3211
+ const combinedDoc = await PDFDocument5.create();
3212
+ const copyOrderBySource = /* @__PURE__ */ new Map();
3213
+ for (const file of state.files) {
3214
+ for (const page of file.pages) {
3215
+ const originalFileId = page.originalFileId || file.id;
3216
+ const sourcePdf = fileMap.get(originalFileId)?.type === "pdf" ? loadedPdfs.get(originalFileId) : void 0;
3217
+ if (!sourcePdf || page.srcIndex < 0 || page.srcIndex >= sourcePdf.getPageCount()) continue;
3218
+ const order = copyOrderBySource.get(originalFileId) ?? [];
3219
+ order.push(page.srcIndex);
3220
+ copyOrderBySource.set(originalFileId, order);
3221
+ }
3222
+ }
3223
+ const copiedPagesBySource = /* @__PURE__ */ new Map();
3224
+ for (const [fileId, order] of copyOrderBySource) {
3225
+ try {
3226
+ copiedPagesBySource.set(fileId, await combinedDoc.copyPages(loadedPdfs.get(fileId), order));
3227
+ } catch (copyError) {
3228
+ console.error(`Error copying pages from ${fileId}:`, copyError);
3229
+ throw new Error(`Failed to copy page: ${copyError instanceof Error ? copyError.message : String(copyError)}`);
3230
+ }
3231
+ }
1607
3232
  for (const file of state.files) {
1608
3233
  if (file.pages.length === 0) continue;
1609
3234
  for (const page of file.pages) {
@@ -1621,18 +3246,21 @@ var PageAssembler = forwardRef(
1621
3246
  }
1622
3247
  const sourcePageCount = sourcePdf.getPageCount();
1623
3248
  if (page.srcIndex >= 0 && page.srcIndex < sourcePageCount) {
3249
+ const copiedPage = copiedPagesBySource.get(originalFileId).shift();
1624
3250
  try {
1625
- const copiedPages = await combinedDoc.copyPages(sourcePdf, [page.srcIndex]);
1626
- copiedPages.forEach((copiedPage) => combinedDoc.addPage(copiedPage));
1627
- } catch (copyError) {
1628
- console.error(`Error copying page ${page.srcIndex}:`, copyError);
1629
- throw new Error(`Failed to copy page: ${copyError instanceof Error ? copyError.message : String(copyError)}`);
3251
+ const annotsRef = copiedPage.node.get(PDFName4.of("Annots"));
3252
+ if (annotsRef) {
3253
+ copiedPage.node.delete(PDFName4.of("Annots"));
3254
+ }
3255
+ } catch (annotError) {
3256
+ console.warn("Could not remove annotations from copied page:", annotError);
1630
3257
  }
3258
+ combinedDoc.addPage(copiedPage);
1631
3259
  }
1632
3260
  } else if (sourceFile.type === "image" && sourceFile.originalFile) {
1633
3261
  try {
1634
3262
  const imagePdfBytes = await imageToPdf(sourceFile.originalFile);
1635
- const imagePdf = await PDFDocument2.load(imagePdfBytes);
3263
+ const imagePdf = await PDFDocument5.load(imagePdfBytes);
1636
3264
  const copiedPages = await combinedDoc.copyPages(imagePdf, [0]);
1637
3265
  copiedPages.forEach((copiedPage) => combinedDoc.addPage(copiedPage));
1638
3266
  } catch (imageError) {
@@ -1642,7 +3270,41 @@ var PageAssembler = forwardRef(
1642
3270
  }
1643
3271
  }
1644
3272
  }
1645
- const pdfBytes = await combinedDoc.save();
3273
+ const mergedMetadata = mergeSourceMetadata(metadataSources);
3274
+ if (mergedMetadata) {
3275
+ try {
3276
+ setSigniphiMetadata(combinedDoc, mergedMetadata);
3277
+ } catch (metadataError) {
3278
+ console.warn("Could not write merged metadata:", metadataError);
3279
+ }
3280
+ } else {
3281
+ console.warn("[PageAssembler] No source metadata survived the merge; the assembled document will read as unprepared downstream");
3282
+ }
3283
+ let pdfBytes = await combinedDoc.save();
3284
+ if (allExtractedFields.length > 0) {
3285
+ console.log(`[PageAssembler] Re-adding ${allExtractedFields.length} fields to combined document`);
3286
+ const fieldsForPdf = allExtractedFields.map((field) => ({
3287
+ name: field.name,
3288
+ type: field.type,
3289
+ position: field.position,
3290
+ label: field.label,
3291
+ required: field.required,
3292
+ options: field.options,
3293
+ optionPositions: field.optionPositions,
3294
+ placeholder: field.placeholder,
3295
+ assignedSignerEmail: field.assignedSignerEmail
3296
+ }));
3297
+ try {
3298
+ pdfBytes = await addFormFieldsToPdf(pdfBytes, fieldsForPdf, {
3299
+ removeExistingFields: true,
3300
+ // Remove any orphaned field references
3301
+ drawLabels: false
3302
+ });
3303
+ console.log("[PageAssembler] \u2705 Successfully re-added form fields to combined PDF");
3304
+ } catch (addFieldsError) {
3305
+ console.error("[PageAssembler] Error re-adding fields:", addFieldsError);
3306
+ }
3307
+ }
1646
3308
  const arrayBuffer = pdfBytes.buffer.slice(pdfBytes.byteOffset, pdfBytes.byteOffset + pdfBytes.byteLength);
1647
3309
  const blob = new Blob([arrayBuffer], { type: "application/pdf" });
1648
3310
  return blob;
@@ -1697,6 +3359,10 @@ var PageAssembler = forwardRef(
1697
3359
  /* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-4 w-4 border-b-2 border-primary" }),
1698
3360
  /* @__PURE__ */ jsx("span", { className: "text-sm", children: processingStatus })
1699
3361
  ] }) }),
3362
+ uploadError && !isLoading && /* @__PURE__ */ jsx(Card, { className: "p-4 border-destructive/50 bg-destructive/5", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
3363
+ /* @__PURE__ */ jsx(X, { className: "h-4 w-4 text-destructive flex-shrink-0" }),
3364
+ /* @__PURE__ */ jsx("span", { className: "text-sm text-destructive", children: uploadError })
3365
+ ] }) }),
1700
3366
  /* @__PURE__ */ jsx(
1701
3367
  BulkActionBar,
1702
3368
  {
@@ -1831,8 +3497,7 @@ function debounce(func, wait) {
1831
3497
  timeout = setTimeout(() => func(...args), wait);
1832
3498
  });
1833
3499
  }
1834
- var PageAssembler_default = PageAssembler;
1835
3500
 
1836
- export { Button, PageAssembler, createPdfBlobUrl, PageAssembler_default as default, downloadPdf, imageToPdf, pdfToImages };
3501
+ export { Button, FormFieldType, PageAssembler, PdfPasswordRequiredError2 as PdfPasswordRequiredError, UnsupportedPdfEncryptionError2 as UnsupportedPdfEncryptionError, addFormFieldsToPdf, createPdfBlobUrl, decryptPdf2 as decryptPdf, downloadPdf, extractFieldsFromPdf, getSigniphiMetadata, imageToPdf, mapFieldPositionsAfterAssembly, normalizePdfEncryption2 as normalizePdfEncryption, normalizeUploadedPdfBytes, pdfToImages, rasterizePdfToPdf, setSigniphiMetadata };
1837
3502
  //# sourceMappingURL=index.mjs.map
1838
3503
  //# sourceMappingURL=index.mjs.map