@unlev/exeq 0.1.1 → 0.1.3

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
@@ -1,5 +1,6 @@
1
1
  // src/components/pdf-builder/DesignerView.tsx
2
- import { useState as useState3, useCallback as useCallback3, useEffect as useEffect2 } from "react";
2
+ import { useState as useState2, useCallback as useCallback3, useEffect as useEffect2, useRef as useRef3 } from "react";
3
+ import { createPortal } from "react-dom";
3
4
 
4
5
  // src/types/pdf-builder.ts
5
6
  function generateId() {
@@ -55,14 +56,44 @@ var FIELD_DEFAULTS = {
55
56
  height: 5,
56
57
  fontSize: 12,
57
58
  placeholder: "Initials"
59
+ },
60
+ blackout: {
61
+ width: 20,
62
+ height: 3,
63
+ fontSize: 12,
64
+ placeholder: ""
65
+ },
66
+ whiteout: {
67
+ width: 20,
68
+ height: 3,
69
+ fontSize: 12,
70
+ placeholder: ""
58
71
  }
59
72
  };
60
- function createField(type, assignee, page, x, y) {
73
+ var TYPE_LABELS = {
74
+ text: "Text Field",
75
+ signature: "Signature",
76
+ "signed-date": "Signed Date",
77
+ checkbox: "Checkbox",
78
+ initials: "Initials",
79
+ blackout: "Blackout",
80
+ whiteout: "Whiteout"
81
+ };
82
+ function uniqueLabel(desired, existingLabels) {
83
+ const lower = desired.toLowerCase();
84
+ if (!existingLabels.some((l) => l.toLowerCase() === lower)) return desired;
85
+ let i = 2;
86
+ while (existingLabels.some((l) => l.toLowerCase() === `${lower} ${i}`)) i++;
87
+ return `${desired} ${i}`;
88
+ }
89
+ function createField(type, assignee, page, x, y, existingFields) {
61
90
  const defaults = FIELD_DEFAULTS[type];
91
+ const baseLabel = TYPE_LABELS[type];
92
+ const existingLabels = existingFields?.map((f) => f.label) ?? [];
62
93
  return {
63
94
  id: generateId(),
64
95
  type,
65
- label: type === "text" ? "Text Field" : type === "signature" ? "Signature" : type === "signed-date" ? "Signed Date" : type === "checkbox" ? "Checkbox" : "Initials",
96
+ label: uniqueLabel(baseLabel, existingLabels),
66
97
  placeholder: defaults.placeholder || "",
67
98
  required: true,
68
99
  assignee,
@@ -117,6 +148,7 @@ function PdfViewer({
117
148
  onFieldMove,
118
149
  onFieldResize,
119
150
  onPageClick,
151
+ onDropField,
120
152
  mode,
121
153
  currentSigner,
122
154
  renderFieldContent
@@ -134,6 +166,21 @@ function PdfViewer({
134
166
  onSelectField(null);
135
167
  }
136
168
  }, [onPageClick, onSelectField]);
169
+ const handleDragOver = useCallback((e) => {
170
+ if (e.dataTransfer.types.includes("application/exeq-field-type")) {
171
+ e.preventDefault();
172
+ e.dataTransfer.dropEffect = "copy";
173
+ }
174
+ }, []);
175
+ const handleDrop = useCallback((e, pageIndex) => {
176
+ const fieldType = e.dataTransfer.getData("application/exeq-field-type");
177
+ if (!fieldType || !onDropField) return;
178
+ e.preventDefault();
179
+ const rect = e.currentTarget.getBoundingClientRect();
180
+ const x = (e.clientX - rect.left) / rect.width * 100;
181
+ const y = (e.clientY - rect.top) / rect.height * 100;
182
+ onDropField(pageIndex, x, y, fieldType);
183
+ }, [onDropField]);
137
184
  return /* @__PURE__ */ jsx("div", { className: "pdf-viewer", ref: containerRef, children: pages.map((page, pageIndex) => {
138
185
  const pageFields = fields.filter((f) => f.page === pageIndex);
139
186
  return /* @__PURE__ */ jsxs(
@@ -142,6 +189,8 @@ function PdfViewer({
142
189
  className: "pdf-page",
143
190
  style: { aspectRatio: `${page.width} / ${page.height}` },
144
191
  onClick: (e) => handlePageClick(e, pageIndex),
192
+ onDragOver: handleDragOver,
193
+ onDrop: (e) => handleDrop(e, pageIndex),
145
194
  "data-page": pageIndex,
146
195
  children: [
147
196
  /* @__PURE__ */ jsx(
@@ -187,6 +236,7 @@ function FieldOverlayItem({
187
236
  const dragStartRef = useRef(null);
188
237
  const resizeStartRef = useRef(null);
189
238
  const color = getSignerColor(field.assignee);
239
+ const isRedact = field.type === "blackout" || field.type === "whiteout";
190
240
  const isEditable = mode === "designer" || mode === "signer" && field.assignee === currentSigner;
191
241
  const isPreFilled = !isEditable && !!field.value;
192
242
  const handleMouseDown = useCallback((e) => {
@@ -252,14 +302,15 @@ function FieldOverlayItem({
252
302
  "div",
253
303
  {
254
304
  ref: overlayRef,
255
- className: `field-overlay ${isSelected ? "selected" : ""} ${isEditable ? "editable" : "readonly"} ${isPreFilled ? "prefilled" : ""}`,
305
+ className: `field-overlay ${isSelected ? "selected" : ""} ${isEditable ? "editable" : "readonly"} ${isPreFilled ? "prefilled" : ""} ${isRedact ? "redact" : ""}`,
256
306
  style: {
257
307
  left: `${field.x}%`,
258
308
  top: `${field.y}%`,
259
309
  width: `${field.width}%`,
260
310
  height: `${field.height}%`,
261
- borderColor: color,
262
- backgroundColor: isSelected ? `${color}22` : `${color}11`,
311
+ borderColor: isRedact ? field.type === "blackout" ? "#666" : "#bbb" : color,
312
+ borderStyle: isRedact ? "dashed" : "solid",
313
+ backgroundColor: isRedact ? field.type === "blackout" ? "#000000" : "#ffffff" : isSelected ? `${color}22` : `${color}11`,
263
314
  cursor: mode === "designer" ? "move" : "default"
264
315
  },
265
316
  onClick: (e) => {
@@ -268,7 +319,7 @@ function FieldOverlayItem({
268
319
  },
269
320
  onMouseDown: handleMouseDown,
270
321
  children: [
271
- mode === "designer" && /* @__PURE__ */ jsx("div", { className: "field-overlay-label", style: { backgroundColor: color }, children: field.label }),
322
+ mode === "designer" && !isRedact && /* @__PURE__ */ jsx("div", { className: "field-overlay-label", style: { backgroundColor: color }, children: field.label }),
272
323
  renderContent ? renderContent(field) : /* @__PURE__ */ jsx("div", { className: "field-overlay-placeholder", children: field.value || field.placeholder }),
273
324
  mode === "designer" && isSelected && /* @__PURE__ */ jsx(
274
325
  "div",
@@ -284,8 +335,15 @@ function FieldOverlayItem({
284
335
 
285
336
  // src/components/pdf-builder/FieldPropertyPanel.tsx
286
337
  import { jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
338
+ var INK_COLORS = [
339
+ { value: "#000000", label: "Black" },
340
+ { value: "#1a56db", label: "Blue" }
341
+ ];
287
342
  function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
288
343
  const color = getSignerColor(field.assignee);
344
+ const isRedactField = field.type === "blackout" || field.type === "whiteout";
345
+ const showFontSize = field.type === "text" || field.type === "signed-date";
346
+ const showInkColor = field.type === "text" || field.type === "signature" || field.type === "initials" || field.type === "signed-date";
289
347
  return /* @__PURE__ */ jsxs2("div", { className: "field-property-panel", children: [
290
348
  /* @__PURE__ */ jsxs2("div", { className: "panel-header", children: [
291
349
  /* @__PURE__ */ jsx2("h3", { style: { color }, children: field.label }),
@@ -302,7 +360,7 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
302
360
  }
303
361
  )
304
362
  ] }),
305
- /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
363
+ !isRedactField && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
306
364
  /* @__PURE__ */ jsx2("label", { children: "Field Type" }),
307
365
  /* @__PURE__ */ jsxs2(
308
366
  "select",
@@ -336,7 +394,7 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
336
394
  }
337
395
  )
338
396
  ] }),
339
- /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
397
+ !isRedactField && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
340
398
  /* @__PURE__ */ jsx2("label", { children: "Assigned To" }),
341
399
  /* @__PURE__ */ jsx2(
342
400
  "select",
@@ -347,7 +405,7 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
347
405
  }
348
406
  )
349
407
  ] }),
350
- /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
408
+ !isRedactField && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
351
409
  /* @__PURE__ */ jsx2("label", { children: "Placeholder" }),
352
410
  /* @__PURE__ */ jsx2(
353
411
  "input",
@@ -358,7 +416,7 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
358
416
  }
359
417
  )
360
418
  ] }),
361
- /* @__PURE__ */ jsx2("div", { className: "panel-field", children: /* @__PURE__ */ jsxs2("label", { className: "panel-checkbox-label", children: [
419
+ !isRedactField && /* @__PURE__ */ jsx2("div", { className: "panel-field", children: /* @__PURE__ */ jsxs2("label", { className: "panel-checkbox-label", children: [
362
420
  /* @__PURE__ */ jsx2(
363
421
  "input",
364
422
  {
@@ -369,7 +427,7 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
369
427
  ),
370
428
  "Required"
371
429
  ] }) }),
372
- field.type !== "checkbox" && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
430
+ showFontSize && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
373
431
  /* @__PURE__ */ jsx2("label", { children: "Font Size (pt)" }),
374
432
  /* @__PURE__ */ jsx2(
375
433
  "input",
@@ -382,118 +440,26 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
382
440
  }
383
441
  )
384
442
  ] }),
385
- /* @__PURE__ */ jsxs2("div", { className: "panel-field-group", children: [
386
- /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
387
- /* @__PURE__ */ jsx2("label", { children: "Width (%)" }),
388
- /* @__PURE__ */ jsx2(
389
- "input",
390
- {
391
- type: "number",
392
- min: "1",
393
- max: "100",
394
- step: "0.5",
395
- value: field.width,
396
- onChange: (e) => onUpdate(field.id, { width: Number(e.target.value) })
397
- }
398
- )
399
- ] }),
400
- /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
401
- /* @__PURE__ */ jsx2("label", { children: "Height (%)" }),
402
- /* @__PURE__ */ jsx2(
403
- "input",
404
- {
405
- type: "number",
406
- min: "1",
407
- max: "100",
408
- step: "0.5",
409
- value: field.height,
410
- onChange: (e) => onUpdate(field.id, { height: Number(e.target.value) })
411
- }
412
- )
413
- ] })
414
- ] })
415
- ] });
416
- }
417
-
418
- // src/components/pdf-builder/SignerRoleSelector.tsx
419
- import { useState } from "react";
420
- import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
421
- function SignerRoleSelector({
422
- roles,
423
- activeRole,
424
- onSetActiveRole,
425
- onAddRole,
426
- onRemoveRole
427
- }) {
428
- const [isAdding, setIsAdding] = useState(false);
429
- const [newRoleName, setNewRoleName] = useState("");
430
- const handleAdd = () => {
431
- if (newRoleName.trim()) {
432
- onAddRole(newRoleName.trim());
433
- setNewRoleName("");
434
- setIsAdding(false);
435
- }
436
- };
437
- return /* @__PURE__ */ jsxs3("div", { className: "signer-role-selector", children: [
438
- /* @__PURE__ */ jsx3("div", { className: "signer-role-label", children: "Signer Roles" }),
439
- /* @__PURE__ */ jsxs3("div", { className: "signer-role-list", children: [
440
- roles.map((role) => /* @__PURE__ */ jsxs3(
443
+ showInkColor && /* @__PURE__ */ jsxs2("div", { className: "panel-field", children: [
444
+ /* @__PURE__ */ jsx2("label", { children: "Ink Color" }),
445
+ /* @__PURE__ */ jsx2("div", { className: "ink-color-picker", children: INK_COLORS.map((c) => /* @__PURE__ */ jsx2(
441
446
  "button",
442
447
  {
443
- className: `signer-role-chip ${role === activeRole ? "active" : ""}`,
444
- style: {
445
- borderColor: getSignerColor(role),
446
- backgroundColor: role === activeRole ? getSignerColor(role) : "transparent",
447
- color: role === activeRole ? "#fff" : getSignerColor(role)
448
- },
449
- onClick: () => onSetActiveRole(role),
450
- children: [
451
- role,
452
- roles.length > 1 && role !== "Sender" && /* @__PURE__ */ jsx3(
453
- "span",
454
- {
455
- className: "signer-role-remove",
456
- onClick: (e) => {
457
- e.stopPropagation();
458
- onRemoveRole(role);
459
- },
460
- children: "\xD7"
461
- }
462
- )
463
- ]
448
+ className: `ink-color-swatch ${(field.inkColor || "#000000") === c.value ? "active" : ""}`,
449
+ style: { backgroundColor: c.value },
450
+ onClick: () => onUpdate(field.id, { inkColor: c.value }),
451
+ title: c.label
464
452
  },
465
- role
466
- )),
467
- isAdding ? /* @__PURE__ */ jsxs3("div", { className: "signer-role-add-input", children: [
468
- /* @__PURE__ */ jsx3(
469
- "input",
470
- {
471
- type: "text",
472
- value: newRoleName,
473
- onChange: (e) => setNewRoleName(e.target.value),
474
- onKeyDown: (e) => e.key === "Enter" && handleAdd(),
475
- placeholder: "Role name",
476
- autoFocus: true
477
- }
478
- ),
479
- /* @__PURE__ */ jsx3("button", { onClick: handleAdd, children: "Add" }),
480
- /* @__PURE__ */ jsx3("button", { onClick: () => setIsAdding(false), children: "Cancel" })
481
- ] }) : /* @__PURE__ */ jsx3(
482
- "button",
483
- {
484
- className: "signer-role-add-btn",
485
- onClick: () => setIsAdding(true),
486
- children: "+ Add Role"
487
- }
488
- )
453
+ c.value
454
+ )) })
489
455
  ] })
490
456
  ] });
491
457
  }
492
458
 
493
459
  // src/components/pdf-builder/SignatureCanvas.tsx
494
- import { useRef as useRef2, useState as useState2, useCallback as useCallback2, useEffect } from "react";
460
+ import { useRef as useRef2, useState, useCallback as useCallback2, useEffect } from "react";
495
461
  import { getStroke } from "perfect-freehand";
496
- import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
462
+ import { jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
497
463
  function getSvgPathFromStroke(stroke) {
498
464
  if (!stroke.length) return "";
499
465
  const d = stroke.reduce(
@@ -512,12 +478,13 @@ function SignatureCanvas({
512
478
  height = 150,
513
479
  onSign,
514
480
  initialValue,
515
- className
481
+ className,
482
+ inkColor = "#000000"
516
483
  }) {
517
484
  const canvasRef = useRef2(null);
518
- const [paths, setPaths] = useState2([]);
519
- const [currentPath, setCurrentPath] = useState2(null);
520
- const [isEmpty, setIsEmpty] = useState2(!initialValue);
485
+ const [paths, setPaths] = useState([]);
486
+ const [currentPath, setCurrentPath] = useState(null);
487
+ const [isEmpty, setIsEmpty] = useState(!initialValue);
521
488
  useEffect(() => {
522
489
  if (initialValue && canvasRef.current) {
523
490
  const ctx = canvasRef.current.getContext("2d");
@@ -544,7 +511,7 @@ function SignatureCanvas({
544
511
  });
545
512
  const pathStr = getSvgPathFromStroke(stroke);
546
513
  const path2d = new Path2D(pathStr);
547
- ctx.fillStyle = "#000";
514
+ ctx.fillStyle = inkColor;
548
515
  ctx.fill(path2d);
549
516
  }
550
517
  }, [paths, currentPath, width, height]);
@@ -588,8 +555,8 @@ function SignatureCanvas({
588
555
  onSign("");
589
556
  }
590
557
  }, [width, height, onSign]);
591
- return /* @__PURE__ */ jsxs4("div", { className: `signature-canvas-wrapper ${className || ""}`, children: [
592
- /* @__PURE__ */ jsx4(
558
+ return /* @__PURE__ */ jsxs3("div", { className: `signature-canvas-wrapper ${className || ""}`, children: [
559
+ /* @__PURE__ */ jsx3(
593
560
  "canvas",
594
561
  {
595
562
  ref: canvasRef,
@@ -602,29 +569,76 @@ function SignatureCanvas({
602
569
  style: { touchAction: "none" }
603
570
  }
604
571
  ),
605
- /* @__PURE__ */ jsxs4("div", { className: "signature-canvas-actions", children: [
606
- !isEmpty && /* @__PURE__ */ jsx4("button", { type: "button", onClick: handleClear, className: "signature-clear-btn", children: "Clear" }),
607
- isEmpty && /* @__PURE__ */ jsx4("span", { className: "signature-hint", children: "Draw your signature above" })
572
+ /* @__PURE__ */ jsxs3("div", { className: "signature-canvas-actions", children: [
573
+ !isEmpty && /* @__PURE__ */ jsx3("button", { type: "button", onClick: handleClear, className: "signature-clear-btn", children: "Clear" }),
574
+ isEmpty && /* @__PURE__ */ jsx3("span", { className: "signature-hint", children: "Draw your signature above" })
608
575
  ] })
609
576
  ] });
610
577
  }
611
578
 
579
+ // src/utils/apiKey.ts
580
+ var VALID_KEYS = /* @__PURE__ */ new Set([
581
+ "exeq_live_2024"
582
+ ]);
583
+ function isValidApiKey(key) {
584
+ if (!key) return false;
585
+ return VALID_KEYS.has(key);
586
+ }
587
+
612
588
  // src/components/pdf-builder/DesignerView.tsx
613
- import { Fragment, jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
614
- function DesignerView() {
615
- const [pages, setPages] = useState3([]);
616
- const [fields, setFields] = useState3([]);
617
- const [selectedFieldId, setSelectedFieldId] = useState3(null);
618
- const [signerRoles, setSignerRoles] = useState3([...DEFAULT_SIGNER_ROLES]);
619
- const [activeRole, setActiveRole] = useState3("Sender");
620
- const [activeFieldType, setActiveFieldType] = useState3("text");
621
- const [loading, setLoading] = useState3(false);
622
- const [pdfSource, setPdfSource] = useState3(null);
589
+ import { Fragment, jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
590
+ var FIELD_TYPE_META = [
591
+ { type: "text", label: "Text", icon: "T" },
592
+ { type: "signature", label: "Signature", icon: "\u270D" },
593
+ { type: "signed-date", label: "Date", icon: "\u{1F4C5}" },
594
+ { type: "checkbox", label: "Checkbox", icon: "\u2611" },
595
+ { type: "initials", label: "Initials", icon: "IN" },
596
+ { type: "blackout", label: "Blackout", icon: "\u25A0", group: "redact" },
597
+ { type: "whiteout", label: "Whiteout", icon: "\u25A1", group: "redact" }
598
+ ];
599
+ function DesignerView({
600
+ apiKey,
601
+ initialPdfUrl,
602
+ initialTemplate,
603
+ onSave,
604
+ hideHeader,
605
+ headerPortalRef
606
+ } = {}) {
607
+ if (!isValidApiKey(apiKey)) {
608
+ return /* @__PURE__ */ jsx4("div", { className: "designer-layout", children: /* @__PURE__ */ jsxs4("div", { className: "empty-state", children: [
609
+ /* @__PURE__ */ jsx4("h2", { children: "Invalid API Key" }),
610
+ /* @__PURE__ */ jsxs4("p", { children: [
611
+ "A valid API key is required to use Exeq. Visit ",
612
+ /* @__PURE__ */ jsx4("a", { href: "https://exeq.org", children: "exeq.org" }),
613
+ " to get one."
614
+ ] })
615
+ ] }) });
616
+ }
617
+ const [pages, setPages] = useState2([]);
618
+ const [fields, setFields] = useState2(initialTemplate?.fields ?? []);
619
+ const [selectedFieldId, setSelectedFieldId] = useState2(null);
620
+ const [signerRoles, setSignerRoles] = useState2(initialTemplate?.signerRoles ?? [...DEFAULT_SIGNER_ROLES]);
621
+ const [activeRole, setActiveRole] = useState2("Sender");
622
+ const [activeFieldType, setActiveFieldType] = useState2("text");
623
+ const [loading, setLoading] = useState2(false);
624
+ const [pdfSource, setPdfSource] = useState2(null);
625
+ const [rightTab, setRightTab] = useState2("properties");
626
+ const [isAddingRole, setIsAddingRole] = useState2(false);
627
+ const [newRoleName, setNewRoleName] = useState2("");
628
+ const [draggingFieldType, setDraggingFieldType] = useState2(null);
629
+ const [panelWidth, setPanelWidth] = useState2(380);
630
+ const dragGhostRef = useRef3(null);
631
+ const resizingRef = useRef3(false);
623
632
  useEffect2(() => {
624
- const params = new URLSearchParams(window.location.search);
625
- const pdfUrl = params.get("pdf");
633
+ const pdfUrl = initialPdfUrl || initialTemplate?.pdfUrl;
626
634
  if (pdfUrl) {
627
635
  loadPdf(pdfUrl);
636
+ return;
637
+ }
638
+ const params = new URLSearchParams(window.location.search);
639
+ const paramPdf = params.get("pdf");
640
+ if (paramPdf) {
641
+ loadPdf(paramPdf);
628
642
  }
629
643
  const handleMessage = (e) => {
630
644
  if (e.data?.type === "load-pdf" && e.data.url) {
@@ -638,7 +652,7 @@ function DesignerView() {
638
652
  };
639
653
  window.addEventListener("message", handleMessage);
640
654
  return () => window.removeEventListener("message", handleMessage);
641
- }, []);
655
+ }, [initialPdfUrl, initialTemplate]);
642
656
  const loadPdf = useCallback3(async (source) => {
643
657
  setLoading(true);
644
658
  try {
@@ -661,10 +675,11 @@ function DesignerView() {
661
675
  reader.readAsArrayBuffer(file);
662
676
  }, [loadPdf]);
663
677
  const handlePageClick = useCallback3((page, x, y) => {
664
- const field = createField(activeFieldType, activeRole, page, x, y);
678
+ const field = createField(activeFieldType, activeRole, page, x, y, fields);
665
679
  setFields((prev) => [...prev, field]);
666
680
  setSelectedFieldId(field.id);
667
- }, [activeFieldType, activeRole]);
681
+ setRightTab("properties");
682
+ }, [activeFieldType, activeRole, fields]);
668
683
  const handleFieldMove = useCallback3((id, page, x, y) => {
669
684
  setFields((prev) => prev.map((f) => f.id === id ? { ...f, page, x, y } : f));
670
685
  }, []);
@@ -672,17 +687,26 @@ function DesignerView() {
672
687
  setFields((prev) => prev.map((f) => f.id === id ? { ...f, width, height } : f));
673
688
  }, []);
674
689
  const handleFieldUpdate = useCallback3((id, updates) => {
675
- setFields((prev) => prev.map((f) => f.id === id ? { ...f, ...updates } : f));
690
+ setFields((prev) => {
691
+ if (updates.label !== void 0) {
692
+ const otherLabels = prev.filter((f) => f.id !== id).map((f) => f.label);
693
+ updates.label = uniqueLabel(updates.label, otherLabels);
694
+ }
695
+ return prev.map((f) => f.id === id ? { ...f, ...updates } : f);
696
+ });
676
697
  }, []);
677
698
  const handleFieldDelete = useCallback3((id) => {
678
699
  setFields((prev) => prev.filter((f) => f.id !== id));
679
700
  if (selectedFieldId === id) setSelectedFieldId(null);
680
701
  }, [selectedFieldId]);
681
- const handleAddRole = useCallback3((role) => {
682
- if (!signerRoles.includes(role)) {
683
- setSignerRoles((prev) => [...prev, role]);
702
+ const handleAddRole = useCallback3(() => {
703
+ const name = newRoleName.trim();
704
+ if (name && !signerRoles.includes(name)) {
705
+ setSignerRoles((prev) => [...prev, name]);
684
706
  }
685
- }, [signerRoles]);
707
+ setNewRoleName("");
708
+ setIsAddingRole(false);
709
+ }, [newRoleName, signerRoles]);
686
710
  const handleRemoveRole = useCallback3((role) => {
687
711
  setSignerRoles((prev) => prev.filter((r) => r !== role));
688
712
  setFields((prev) => prev.map((f) => f.assignee === role ? { ...f, assignee: signerRoles[0] } : f));
@@ -694,170 +718,324 @@ function DesignerView() {
694
718
  signerRoles,
695
719
  pdfUrl: typeof pdfSource === "string" ? pdfSource : ""
696
720
  };
697
- const json = JSON.stringify(template, null, 2);
698
- const blob = new Blob([json], { type: "application/json" });
699
- const url = URL.createObjectURL(blob);
700
- const a = document.createElement("a");
701
- a.href = url;
702
- a.download = "template.json";
703
- a.click();
704
- URL.revokeObjectURL(url);
721
+ if (onSave) {
722
+ onSave(template);
723
+ } else {
724
+ const json = JSON.stringify(template, null, 2);
725
+ const blob = new Blob([json], { type: "application/json" });
726
+ const url = URL.createObjectURL(blob);
727
+ const a = document.createElement("a");
728
+ a.href = url;
729
+ a.download = "template.json";
730
+ a.click();
731
+ URL.revokeObjectURL(url);
732
+ }
705
733
  window.parent?.postMessage({ type: "template-saved", template }, "*");
706
- }, [fields, signerRoles, pdfSource]);
734
+ }, [fields, signerRoles, pdfSource, onSave]);
735
+ const handlePaletteDragStart = useCallback3((e, type) => {
736
+ setDraggingFieldType(type);
737
+ e.dataTransfer.setData("application/exeq-field-type", type);
738
+ e.dataTransfer.effectAllowed = "copy";
739
+ const ghost = document.createElement("div");
740
+ ghost.className = "palette-drag-ghost";
741
+ ghost.textContent = FIELD_TYPE_META.find((f) => f.type === type)?.label || type;
742
+ ghost.style.cssText = `
743
+ position: fixed; top: -200px; left: -200px;
744
+ padding: 6px 16px; background: ${getSignerColor(activeRole)};
745
+ color: #fff; border-radius: 4px; font-size: 12px;
746
+ font-family: system-ui; pointer-events: none; white-space: nowrap;
747
+ `;
748
+ document.body.appendChild(ghost);
749
+ e.dataTransfer.setDragImage(ghost, 40, 16);
750
+ dragGhostRef.current = ghost;
751
+ }, [activeRole]);
752
+ const handlePaletteDragEnd = useCallback3(() => {
753
+ setDraggingFieldType(null);
754
+ if (dragGhostRef.current) {
755
+ document.body.removeChild(dragGhostRef.current);
756
+ dragGhostRef.current = null;
757
+ }
758
+ }, []);
759
+ const handleDropOnPage = useCallback3((page, x, y, fieldType) => {
760
+ const field = createField(fieldType, activeRole, page, x, y, fields);
761
+ setFields((prev) => [...prev, field]);
762
+ setSelectedFieldId(field.id);
763
+ setRightTab("properties");
764
+ }, [activeRole, fields]);
765
+ useEffect2(() => {
766
+ const handleKeyDown = (e) => {
767
+ if (e.key !== "Delete" && e.key !== "Backspace") return;
768
+ if (!selectedFieldId) return;
769
+ const tag = (document.activeElement?.tagName || "").toLowerCase();
770
+ if (tag === "input" || tag === "textarea" || tag === "select") return;
771
+ if (document.activeElement?.isContentEditable) return;
772
+ e.preventDefault();
773
+ if (window.confirm("Delete this field?")) {
774
+ handleFieldDelete(selectedFieldId);
775
+ }
776
+ };
777
+ window.addEventListener("keydown", handleKeyDown);
778
+ return () => window.removeEventListener("keydown", handleKeyDown);
779
+ }, [selectedFieldId, handleFieldDelete]);
780
+ const handleResizeStart = useCallback3((e) => {
781
+ e.preventDefault();
782
+ resizingRef.current = true;
783
+ const startX = e.clientX;
784
+ const startWidth = panelWidth;
785
+ const handleMouseMove = (e2) => {
786
+ if (!resizingRef.current) return;
787
+ const delta = startX - e2.clientX;
788
+ setPanelWidth(Math.max(280, Math.min(600, startWidth + delta)));
789
+ };
790
+ const handleMouseUp = () => {
791
+ resizingRef.current = false;
792
+ window.removeEventListener("mousemove", handleMouseMove);
793
+ window.removeEventListener("mouseup", handleMouseUp);
794
+ };
795
+ window.addEventListener("mousemove", handleMouseMove);
796
+ window.addEventListener("mouseup", handleMouseUp);
797
+ }, [panelWidth]);
798
+ const sortedFields = [...fields].sort((a, b) => {
799
+ if (a.page !== b.page) return a.page - b.page;
800
+ const bandThreshold = 2;
801
+ if (Math.abs(a.y - b.y) > bandThreshold) return a.y - b.y;
802
+ return a.x - b.x;
803
+ });
707
804
  const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
708
805
  const renderFieldContent = useCallback3((field) => {
806
+ if (field.type === "blackout" || field.type === "whiteout") {
807
+ return null;
808
+ }
709
809
  if (field.type === "signature" || field.type === "initials") {
710
810
  if (field.value) {
711
- return /* @__PURE__ */ jsx5("img", { src: field.value, alt: field.label, className: "field-signature-preview" });
811
+ return /* @__PURE__ */ jsx4("img", { src: field.value, alt: field.label, className: "field-signature-preview" });
712
812
  }
713
813
  }
714
- return /* @__PURE__ */ jsx5("div", { className: "field-overlay-placeholder", children: field.value || field.placeholder });
814
+ const inkColor = field.inkColor || "#000000";
815
+ return /* @__PURE__ */ jsx4(
816
+ "div",
817
+ {
818
+ className: "field-overlay-placeholder",
819
+ style: { color: field.value ? inkColor : void 0, fontSize: `${field.fontSize * 0.6}px` },
820
+ children: field.value || field.placeholder
821
+ }
822
+ );
715
823
  }, []);
716
- return /* @__PURE__ */ jsxs5("div", { className: "designer-layout", children: [
717
- /* @__PURE__ */ jsxs5("div", { className: "designer-toolbar", children: [
718
- /* @__PURE__ */ jsxs5("div", { className: "toolbar-left", children: [
719
- !pages.length && /* @__PURE__ */ jsxs5("label", { className: "upload-btn", children: [
720
- "Upload PDF",
721
- /* @__PURE__ */ jsx5("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
722
- ] }),
723
- pages.length > 0 && /* @__PURE__ */ jsxs5(Fragment, { children: [
724
- /* @__PURE__ */ jsxs5("label", { className: "upload-btn upload-btn-small", children: [
725
- "Change PDF",
726
- /* @__PURE__ */ jsx5("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
727
- ] }),
728
- /* @__PURE__ */ jsx5("div", { className: "field-type-selector", children: ["text", "signature", "signed-date", "checkbox", "initials"].map((type) => /* @__PURE__ */ jsx5(
729
- "button",
824
+ const headerButtons = pages.length > 0 ? /* @__PURE__ */ jsxs4(Fragment, { children: [
825
+ /* @__PURE__ */ jsxs4("label", { className: "header-btn header-btn-outline", children: [
826
+ "Change PDF",
827
+ /* @__PURE__ */ jsx4("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
828
+ ] }),
829
+ /* @__PURE__ */ jsx4("button", { onClick: handleExport, className: "header-btn header-btn-primary", children: "Export Template" })
830
+ ] }) : null;
831
+ return /* @__PURE__ */ jsxs4("div", { className: "designer-layout", children: [
832
+ headerPortalRef?.current && headerButtons && createPortal(headerButtons, headerPortalRef.current),
833
+ !hideHeader && !headerPortalRef && /* @__PURE__ */ jsx4("div", { className: "designer-header", children: /* @__PURE__ */ jsx4("div", { className: "designer-header-right", children: headerButtons }) }),
834
+ /* @__PURE__ */ jsxs4("div", { className: "designer-body", children: [
835
+ pages.length > 0 && /* @__PURE__ */ jsxs4("div", { className: "designer-palette", children: [
836
+ /* @__PURE__ */ jsx4("div", { className: "palette-heading", children: "New Field Role" }),
837
+ /* @__PURE__ */ jsxs4("div", { className: "palette-role-section", children: [
838
+ /* @__PURE__ */ jsx4(
839
+ "select",
730
840
  {
731
- className: `field-type-btn ${activeFieldType === type ? "active" : ""}`,
732
- onClick: () => setActiveFieldType(type),
733
- children: type === "text" ? "Text" : type === "signature" ? "Signature" : type === "signed-date" ? "Date" : type === "checkbox" ? "Checkbox" : "Initials"
734
- },
735
- type
736
- )) })
737
- ] })
841
+ className: "palette-role-select",
842
+ value: activeRole,
843
+ onChange: (e) => setActiveRole(e.target.value),
844
+ style: { borderColor: getSignerColor(activeRole), color: getSignerColor(activeRole) },
845
+ children: signerRoles.map((role) => /* @__PURE__ */ jsx4("option", { value: role, children: role }, role))
846
+ }
847
+ ),
848
+ isAddingRole ? /* @__PURE__ */ jsxs4("div", { className: "palette-role-add-inline", children: [
849
+ /* @__PURE__ */ jsx4(
850
+ "input",
851
+ {
852
+ type: "text",
853
+ value: newRoleName,
854
+ onChange: (e) => setNewRoleName(e.target.value),
855
+ onKeyDown: (e) => e.key === "Enter" && handleAddRole(),
856
+ placeholder: "Role name",
857
+ autoFocus: true
858
+ }
859
+ ),
860
+ /* @__PURE__ */ jsx4("button", { onClick: handleAddRole, children: "Add" }),
861
+ /* @__PURE__ */ jsx4("button", { onClick: () => {
862
+ setIsAddingRole(false);
863
+ setNewRoleName("");
864
+ }, children: "Cancel" })
865
+ ] }) : /* @__PURE__ */ jsx4("button", { className: "palette-role-add-link", onClick: () => setIsAddingRole(true), children: "+ Add Role" })
866
+ ] }),
867
+ /* @__PURE__ */ jsx4("div", { className: "palette-divider" }),
868
+ /* @__PURE__ */ jsx4("div", { className: "palette-heading", children: "Fields" }),
869
+ FIELD_TYPE_META.filter((f) => !f.group).map(({ type, label, icon }) => /* @__PURE__ */ jsxs4(
870
+ "div",
871
+ {
872
+ className: `palette-item ${activeFieldType === type ? "active" : ""}`,
873
+ draggable: true,
874
+ onClick: () => setActiveFieldType(type),
875
+ onDragStart: (e) => handlePaletteDragStart(e, type),
876
+ onDragEnd: handlePaletteDragEnd,
877
+ children: [
878
+ /* @__PURE__ */ jsx4("span", { className: "palette-item-icon", children: icon }),
879
+ /* @__PURE__ */ jsx4("span", { className: "palette-item-label", children: label })
880
+ ]
881
+ },
882
+ type
883
+ )),
884
+ /* @__PURE__ */ jsx4("div", { className: "palette-divider" }),
885
+ /* @__PURE__ */ jsx4("div", { className: "palette-heading", children: "Redact" }),
886
+ FIELD_TYPE_META.filter((f) => f.group === "redact").map(({ type, label, icon }) => /* @__PURE__ */ jsxs4(
887
+ "div",
888
+ {
889
+ className: `palette-item ${activeFieldType === type ? "active" : ""}`,
890
+ draggable: true,
891
+ onClick: () => setActiveFieldType(type),
892
+ onDragStart: (e) => handlePaletteDragStart(e, type),
893
+ onDragEnd: handlePaletteDragEnd,
894
+ children: [
895
+ /* @__PURE__ */ jsx4("span", { className: "palette-item-icon", children: icon }),
896
+ /* @__PURE__ */ jsx4("span", { className: "palette-item-label", children: label })
897
+ ]
898
+ },
899
+ type
900
+ ))
738
901
  ] }),
739
- /* @__PURE__ */ jsx5("div", { className: "toolbar-right", children: pages.length > 0 && /* @__PURE__ */ jsx5("button", { onClick: handleExport, className: "export-btn", children: "Export Template" }) })
740
- ] }),
741
- pages.length > 0 && /* @__PURE__ */ jsx5(
742
- SignerRoleSelector,
743
- {
744
- roles: signerRoles,
745
- activeRole,
746
- onSetActiveRole: setActiveRole,
747
- onAddRole: handleAddRole,
748
- onRemoveRole: handleRemoveRole
749
- }
750
- ),
751
- /* @__PURE__ */ jsxs5("div", { className: "designer-content", children: [
752
- /* @__PURE__ */ jsxs5("div", { className: "designer-pdf-area", children: [
753
- loading && /* @__PURE__ */ jsx5("div", { className: "loading-indicator", children: "Loading PDF..." }),
754
- !pages.length && !loading && /* @__PURE__ */ jsxs5("div", { className: "empty-state", children: [
755
- /* @__PURE__ */ jsx5("h2", { children: "Upload a PDF to get started" }),
756
- /* @__PURE__ */ jsx5("p", { children: "Upload a template PDF, then click on the page to place form fields." }),
757
- /* @__PURE__ */ jsxs5("label", { className: "upload-btn upload-btn-large", children: [
758
- "Choose PDF File",
759
- /* @__PURE__ */ jsx5("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
902
+ /* @__PURE__ */ jsxs4("div", { className: "designer-pdf-area", children: [
903
+ loading && /* @__PURE__ */ jsx4("div", { className: "loading-indicator", children: "Loading PDF..." }),
904
+ !pages.length && !loading && /* @__PURE__ */ jsxs4("div", { className: "empty-state", children: [
905
+ /* @__PURE__ */ jsx4("h2", { children: "Open a PDF to get started" }),
906
+ /* @__PURE__ */ jsx4("p", { children: "Select a PDF from your device to begin. Your file stays on your computer \u2014 nothing is uploaded." }),
907
+ /* @__PURE__ */ jsxs4("label", { className: "upload-btn upload-btn-large", children: [
908
+ "Select PDF",
909
+ /* @__PURE__ */ jsx4("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
760
910
  ] })
761
911
  ] }),
762
- pages.length > 0 && /* @__PURE__ */ jsx5(
912
+ pages.length > 0 && /* @__PURE__ */ jsx4(
763
913
  PdfViewer,
764
914
  {
765
915
  pages,
766
916
  fields,
767
917
  selectedFieldId,
768
- onSelectField: setSelectedFieldId,
918
+ onSelectField: (id) => {
919
+ setSelectedFieldId(id);
920
+ if (id) setRightTab("properties");
921
+ },
769
922
  onFieldMove: handleFieldMove,
770
923
  onFieldResize: handleFieldResize,
771
924
  onPageClick: handlePageClick,
925
+ onDropField: handleDropOnPage,
772
926
  mode: "designer",
773
927
  renderFieldContent
774
928
  }
775
929
  )
776
930
  ] }),
777
- /* @__PURE__ */ jsxs5("div", { className: "designer-panel", children: [
778
- selectedField ? /* @__PURE__ */ jsxs5(Fragment, { children: [
779
- /* @__PURE__ */ jsx5(
780
- FieldPropertyPanel,
781
- {
782
- field: selectedField,
783
- signerRoles,
784
- onUpdate: handleFieldUpdate,
785
- onDelete: handleFieldDelete
786
- }
787
- ),
788
- selectedField.assignee === activeRole && /* @__PURE__ */ jsxs5("div", { className: "prefill-section", children: [
789
- /* @__PURE__ */ jsx5("h4", { children: "Pre-fill Value" }),
790
- selectedField.type === "signature" || selectedField.type === "initials" ? /* @__PURE__ */ jsx5(
791
- SignatureCanvas,
931
+ pages.length > 0 && /* @__PURE__ */ jsxs4(Fragment, { children: [
932
+ /* @__PURE__ */ jsx4("div", { className: "panel-resize-handle", onMouseDown: handleResizeStart }),
933
+ /* @__PURE__ */ jsxs4("div", { className: "designer-panel", style: { width: panelWidth }, children: [
934
+ /* @__PURE__ */ jsxs4("div", { className: "panel-tabs", children: [
935
+ /* @__PURE__ */ jsx4(
936
+ "button",
792
937
  {
793
- width: 300,
794
- height: selectedField.type === "initials" ? 100 : 150,
795
- onSign: (dataUrl) => handleFieldUpdate(selectedField.id, { value: dataUrl }),
796
- initialValue: selectedField.value
938
+ className: `panel-tab ${rightTab === "properties" ? "active" : ""}`,
939
+ onClick: () => setRightTab("properties"),
940
+ children: "Properties"
797
941
  }
798
- ) : selectedField.type === "checkbox" ? /* @__PURE__ */ jsxs5("label", { className: "panel-checkbox-label", children: [
799
- /* @__PURE__ */ jsx5(
800
- "input",
801
- {
802
- type: "checkbox",
803
- checked: selectedField.value === "true",
804
- onChange: (e) => handleFieldUpdate(selectedField.id, { value: e.target.checked ? "true" : "" })
805
- }
806
- ),
807
- "Checked"
808
- ] }) : /* @__PURE__ */ jsx5(
809
- "input",
942
+ ),
943
+ /* @__PURE__ */ jsxs4(
944
+ "button",
810
945
  {
811
- type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
812
- value: selectedField.value,
813
- onChange: (e) => handleFieldUpdate(selectedField.id, { value: e.target.value }),
814
- placeholder: `Pre-fill ${selectedField.label}`,
815
- className: "prefill-input"
946
+ className: `panel-tab ${rightTab === "fields" ? "active" : ""}`,
947
+ onClick: () => setRightTab("fields"),
948
+ children: [
949
+ "All Fields",
950
+ fields.length > 0 ? ` (${fields.length})` : ""
951
+ ]
816
952
  }
817
953
  )
818
- ] })
819
- ] }) : /* @__PURE__ */ jsx5("div", { className: "panel-empty", children: pages.length > 0 ? "Click on the PDF to place a field, or select an existing field to edit its properties." : "Upload a PDF to begin designing your template." }),
820
- fields.length > 0 && /* @__PURE__ */ jsxs5("div", { className: "field-list", children: [
821
- /* @__PURE__ */ jsxs5("h4", { children: [
822
- "All Fields (",
823
- fields.length,
824
- ")"
825
954
  ] }),
826
- fields.map((f, i) => /* @__PURE__ */ jsxs5(
827
- "div",
828
- {
829
- className: `field-list-item ${f.id === selectedFieldId ? "active" : ""}`,
830
- onClick: () => setSelectedFieldId(f.id),
831
- children: [
832
- /* @__PURE__ */ jsx5(
833
- "span",
955
+ /* @__PURE__ */ jsxs4("div", { className: "panel-tab-content", children: [
956
+ rightTab === "properties" && /* @__PURE__ */ jsx4(Fragment, { children: selectedField ? /* @__PURE__ */ jsxs4(Fragment, { children: [
957
+ /* @__PURE__ */ jsx4(
958
+ FieldPropertyPanel,
959
+ {
960
+ field: selectedField,
961
+ signerRoles,
962
+ onUpdate: handleFieldUpdate,
963
+ onDelete: handleFieldDelete
964
+ }
965
+ ),
966
+ selectedField.type !== "blackout" && selectedField.type !== "whiteout" && /* @__PURE__ */ jsxs4("div", { className: "prefill-section", children: [
967
+ /* @__PURE__ */ jsx4("h4", { children: "Pre-fill Value" }),
968
+ selectedField.type === "signature" || selectedField.type === "initials" ? /* @__PURE__ */ jsx4(
969
+ SignatureCanvas,
834
970
  {
835
- className: "field-list-dot",
836
- style: { backgroundColor: getSignerColor(f.assignee) }
971
+ width: panelWidth - 40,
972
+ height: selectedField.type === "initials" ? 100 : 150,
973
+ onSign: (dataUrl) => handleFieldUpdate(selectedField.id, { value: dataUrl }),
974
+ initialValue: selectedField.value,
975
+ inkColor: selectedField.inkColor
837
976
  }
838
- ),
839
- /* @__PURE__ */ jsx5("span", { className: "field-list-name", children: f.label }),
840
- /* @__PURE__ */ jsxs5("span", { className: "field-list-page", children: [
841
- "p",
842
- f.page + 1
843
- ] }),
844
- f.required && /* @__PURE__ */ jsx5("span", { className: "field-list-required", children: "*" })
845
- ]
846
- },
847
- f.id
848
- ))
977
+ ) : selectedField.type === "checkbox" ? /* @__PURE__ */ jsxs4("label", { className: "panel-checkbox-label", children: [
978
+ /* @__PURE__ */ jsx4(
979
+ "input",
980
+ {
981
+ type: "checkbox",
982
+ checked: selectedField.value === "true",
983
+ onChange: (e) => handleFieldUpdate(selectedField.id, { value: e.target.checked ? "true" : "" })
984
+ }
985
+ ),
986
+ "Checked"
987
+ ] }) : /* @__PURE__ */ jsx4(
988
+ "input",
989
+ {
990
+ type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
991
+ value: selectedField.value,
992
+ onChange: (e) => handleFieldUpdate(selectedField.id, { value: e.target.value }),
993
+ placeholder: `Pre-fill ${selectedField.label}`,
994
+ className: "prefill-input",
995
+ style: { color: selectedField.inkColor || "#000000" }
996
+ }
997
+ )
998
+ ] })
999
+ ] }) : /* @__PURE__ */ jsx4("div", { className: "panel-empty", children: "Click on the PDF to place a field, or select an existing field to edit its properties." }) }),
1000
+ rightTab === "fields" && /* @__PURE__ */ jsx4(Fragment, { children: fields.length === 0 ? /* @__PURE__ */ jsx4("div", { className: "panel-empty", children: "No fields yet. Drag a field from the left palette onto the PDF, or click on the PDF to place one." }) : /* @__PURE__ */ jsx4("div", { className: "field-list", children: sortedFields.map((f) => /* @__PURE__ */ jsxs4(
1001
+ "div",
1002
+ {
1003
+ className: `field-list-item ${f.id === selectedFieldId ? "active" : ""}`,
1004
+ onClick: () => {
1005
+ setSelectedFieldId(f.id);
1006
+ setRightTab("properties");
1007
+ },
1008
+ children: [
1009
+ /* @__PURE__ */ jsx4(
1010
+ "span",
1011
+ {
1012
+ className: "field-list-dot",
1013
+ style: { backgroundColor: getSignerColor(f.assignee) }
1014
+ }
1015
+ ),
1016
+ /* @__PURE__ */ jsx4("span", { className: "field-list-name", children: f.label }),
1017
+ /* @__PURE__ */ jsxs4("span", { className: "field-list-page", children: [
1018
+ "p",
1019
+ f.page + 1
1020
+ ] }),
1021
+ f.required && /* @__PURE__ */ jsx4("span", { className: "field-list-required", children: "*" })
1022
+ ]
1023
+ },
1024
+ f.id
1025
+ )) }) })
1026
+ ] }),
1027
+ /* @__PURE__ */ jsxs4("div", { className: "powered-by", children: [
1028
+ "Powered by ",
1029
+ /* @__PURE__ */ jsx4("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
1030
+ ] })
849
1031
  ] })
850
1032
  ] })
851
- ] }),
852
- /* @__PURE__ */ jsxs5("div", { className: "powered-by", children: [
853
- "Powered by ",
854
- /* @__PURE__ */ jsx5("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
855
1033
  ] })
856
1034
  ] });
857
1035
  }
858
1036
 
859
1037
  // src/components/pdf-builder/SignerView.tsx
860
- import { useState as useState4, useCallback as useCallback4, useEffect as useEffect3, useRef as useRef3 } from "react";
1038
+ import { useState as useState3, useCallback as useCallback4, useEffect as useEffect3, useRef as useRef4 } from "react";
861
1039
 
862
1040
  // src/utils/pdfFiller.ts
863
1041
  import { PDFDocument, rgb, StandardFonts } from "pdf-lib";
@@ -872,8 +1050,13 @@ async function generateFilledPdf(pdfSource, fields) {
872
1050
  const pdfDoc = await PDFDocument.load(pdfBytes);
873
1051
  const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
874
1052
  const pages = pdfDoc.getPages();
1053
+ function hexToRgb(hex) {
1054
+ const r = parseInt(hex.slice(1, 3), 16) / 255;
1055
+ const g = parseInt(hex.slice(3, 5), 16) / 255;
1056
+ const b = parseInt(hex.slice(5, 7), 16) / 255;
1057
+ return rgb(r, g, b);
1058
+ }
875
1059
  for (const field of fields) {
876
- if (!field.value) continue;
877
1060
  const page = pages[field.page];
878
1061
  if (!page) continue;
879
1062
  const pageWidth = page.getWidth();
@@ -882,6 +1065,18 @@ async function generateFilledPdf(pdfSource, fields) {
882
1065
  const y = pageHeight - field.y / 100 * pageHeight - field.height / 100 * pageHeight;
883
1066
  const w = field.width / 100 * pageWidth;
884
1067
  const h = field.height / 100 * pageHeight;
1068
+ if (field.type === "blackout" || field.type === "whiteout") {
1069
+ page.drawRectangle({
1070
+ x,
1071
+ y,
1072
+ width: w,
1073
+ height: h,
1074
+ color: field.type === "blackout" ? rgb(0, 0, 0) : rgb(1, 1, 1)
1075
+ });
1076
+ continue;
1077
+ }
1078
+ if (!field.value) continue;
1079
+ const inkColor = field.inkColor ? hexToRgb(field.inkColor) : rgb(0, 0, 0);
885
1080
  if (field.type === "checkbox") {
886
1081
  if (field.value === "true") {
887
1082
  const inset = Math.min(w, h) * 0.2;
@@ -890,13 +1085,13 @@ async function generateFilledPdf(pdfSource, fields) {
890
1085
  start: { x: x + inset, y: y + inset },
891
1086
  end: { x: x + w - inset, y: y + h - inset },
892
1087
  thickness: lineWidth,
893
- color: rgb(0, 0, 0)
1088
+ color: inkColor
894
1089
  });
895
1090
  page.drawLine({
896
1091
  start: { x: x + w - inset, y: y + inset },
897
1092
  end: { x: x + inset, y: y + h - inset },
898
1093
  thickness: lineWidth,
899
- color: rgb(0, 0, 0)
1094
+ color: inkColor
900
1095
  });
901
1096
  }
902
1097
  } else if (field.type === "signature" || field.type === "initials") {
@@ -920,7 +1115,7 @@ async function generateFilledPdf(pdfSource, fields) {
920
1115
  y: y + h * 0.3,
921
1116
  size: fontSize,
922
1117
  font,
923
- color: rgb(0, 0, 0),
1118
+ color: inkColor,
924
1119
  maxWidth: w - 4
925
1120
  });
926
1121
  }
@@ -947,7 +1142,7 @@ async function postPdfToCallback(bytes, callbackUrl, filename) {
947
1142
  }
948
1143
 
949
1144
  // src/components/pdf-builder/FieldNavigator.tsx
950
- import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1145
+ import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
951
1146
  function FieldNavigator({
952
1147
  fields,
953
1148
  currentFieldId,
@@ -973,15 +1168,15 @@ function FieldNavigator({
973
1168
  if (f.type === "checkbox") return true;
974
1169
  return !!f.value;
975
1170
  }).length;
976
- return /* @__PURE__ */ jsxs6("div", { className: "field-navigator", children: [
977
- /* @__PURE__ */ jsxs6("div", { className: "field-navigator-progress", children: [
1171
+ return /* @__PURE__ */ jsxs5("div", { className: "field-navigator", children: [
1172
+ /* @__PURE__ */ jsxs5("div", { className: "field-navigator-progress", children: [
978
1173
  filledCount,
979
1174
  " of ",
980
1175
  fields.length,
981
1176
  " fields completed"
982
1177
  ] }),
983
- /* @__PURE__ */ jsxs6("div", { className: "field-navigator-controls", children: [
984
- /* @__PURE__ */ jsx6(
1178
+ /* @__PURE__ */ jsxs5("div", { className: "field-navigator-controls", children: [
1179
+ /* @__PURE__ */ jsx5(
985
1180
  "button",
986
1181
  {
987
1182
  onClick: handlePrev,
@@ -990,8 +1185,8 @@ function FieldNavigator({
990
1185
  children: "Prev"
991
1186
  }
992
1187
  ),
993
- /* @__PURE__ */ jsx6("span", { className: "field-navigator-position", children: currentIndex >= 0 ? `${currentIndex + 1} / ${fields.length}` : "-" }),
994
- /* @__PURE__ */ jsx6(
1188
+ /* @__PURE__ */ jsx5("span", { className: "field-navigator-position", children: currentIndex >= 0 ? `${currentIndex + 1} / ${fields.length}` : "-" }),
1189
+ /* @__PURE__ */ jsx5(
995
1190
  "button",
996
1191
  {
997
1192
  onClick: handleNext,
@@ -1001,7 +1196,7 @@ function FieldNavigator({
1001
1196
  }
1002
1197
  )
1003
1198
  ] }),
1004
- /* @__PURE__ */ jsx6(
1199
+ /* @__PURE__ */ jsx5(
1005
1200
  "button",
1006
1201
  {
1007
1202
  onClick: onSubmit,
@@ -1014,26 +1209,49 @@ function FieldNavigator({
1014
1209
  }
1015
1210
 
1016
1211
  // src/components/pdf-builder/SignerView.tsx
1017
- import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1212
+ import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
1018
1213
  function SignerView({
1214
+ apiKey,
1019
1215
  initialPdfUrl,
1020
1216
  initialTemplate,
1021
1217
  initialSigner,
1022
1218
  callbackUrl: initialCallbackUrl,
1023
- onComplete
1219
+ onComplete,
1220
+ initialValues
1024
1221
  } = {}) {
1025
- const [pages, setPages] = useState4([]);
1026
- const [fields, setFields] = useState4([]);
1027
- const [selectedFieldId, setSelectedFieldId] = useState4(null);
1028
- const [signer, setSigner] = useState4(initialSigner || "Signer 1");
1029
- const [loading, setLoading] = useState4(false);
1030
- const [submitting, setSubmitting] = useState4(false);
1031
- const [pdfSource, setPdfSource] = useState4(null);
1032
- const [callbackUrl, setCallbackUrl] = useState4(initialCallbackUrl || "");
1033
- const containerRef = useRef3(null);
1222
+ if (!isValidApiKey(apiKey)) {
1223
+ return /* @__PURE__ */ jsx6("div", { className: "signer-layout", children: /* @__PURE__ */ jsxs6("div", { className: "empty-state", children: [
1224
+ /* @__PURE__ */ jsx6("h2", { children: "Invalid API Key" }),
1225
+ /* @__PURE__ */ jsxs6("p", { children: [
1226
+ "A valid API key is required to use Exeq. Visit ",
1227
+ /* @__PURE__ */ jsx6("a", { href: "https://exeq.org", children: "exeq.org" }),
1228
+ " to get one."
1229
+ ] })
1230
+ ] }) });
1231
+ }
1232
+ const [pages, setPages] = useState3([]);
1233
+ const [fields, setFields] = useState3([]);
1234
+ const [selectedFieldId, setSelectedFieldId] = useState3(null);
1235
+ const [signer, setSigner] = useState3(initialSigner || "Signer 1");
1236
+ const [loading, setLoading] = useState3(false);
1237
+ const [submitting, setSubmitting] = useState3(false);
1238
+ const [pdfSource, setPdfSource] = useState3(null);
1239
+ const [callbackUrl, setCallbackUrl] = useState3(initialCallbackUrl || "");
1240
+ const containerRef = useRef4(null);
1034
1241
  useEffect3(() => {
1035
1242
  if (initialTemplate) {
1036
- setFields(initialTemplate.fields);
1243
+ let templateFields = initialTemplate.fields;
1244
+ if (initialValues) {
1245
+ templateFields = templateFields.map((f) => {
1246
+ const byLabel = Object.entries(initialValues).find(
1247
+ ([key]) => key.toLowerCase() === f.label.toLowerCase()
1248
+ );
1249
+ const byId = initialValues[f.id];
1250
+ const value = byLabel?.[1] ?? byId;
1251
+ return value !== void 0 ? { ...f, value } : f;
1252
+ });
1253
+ }
1254
+ setFields(templateFields);
1037
1255
  if (initialPdfUrl) loadPdf(initialPdfUrl);
1038
1256
  return;
1039
1257
  }
@@ -1071,7 +1289,12 @@ function SignerView({
1071
1289
  setLoading(false);
1072
1290
  }
1073
1291
  }, []);
1074
- const editableFields = fields.filter((f) => f.assignee === signer);
1292
+ const editableFields = fields.filter((f) => f.assignee === signer).sort((a, b) => {
1293
+ if (a.page !== b.page) return a.page - b.page;
1294
+ const bandThreshold = 2;
1295
+ if (Math.abs(a.y - b.y) > bandThreshold) return a.y - b.y;
1296
+ return a.x - b.x;
1297
+ });
1075
1298
  const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
1076
1299
  const isFieldEditable = selectedField ? selectedField.assignee === signer : false;
1077
1300
  const handleFieldUpdate = useCallback4((id, value) => {
@@ -1116,21 +1339,21 @@ function SignerView({
1116
1339
  const editable = field.assignee === signer;
1117
1340
  if (!editable) {
1118
1341
  if (field.type === "signature" || field.type === "initials") {
1119
- return field.value ? /* @__PURE__ */ jsx7("img", { src: field.value, alt: field.label, className: "field-signature-preview" }) : /* @__PURE__ */ jsx7("div", { className: "field-overlay-placeholder readonly", children: field.placeholder });
1342
+ return field.value ? /* @__PURE__ */ jsx6("img", { src: field.value, alt: field.label, className: "field-signature-preview" }) : /* @__PURE__ */ jsx6("div", { className: "field-overlay-placeholder readonly", children: field.placeholder });
1120
1343
  }
1121
1344
  if (field.type === "checkbox") {
1122
- return /* @__PURE__ */ jsx7("div", { className: "field-checkbox-display readonly", children: field.value === "true" ? "\u2713" : "" });
1345
+ return /* @__PURE__ */ jsx6("div", { className: "field-checkbox-display readonly", children: field.value === "true" ? "\u2713" : "" });
1123
1346
  }
1124
- return /* @__PURE__ */ jsx7("div", { className: "field-overlay-placeholder readonly", children: field.value || field.placeholder });
1347
+ return /* @__PURE__ */ jsx6("div", { className: "field-overlay-placeholder readonly", children: field.value || field.placeholder });
1125
1348
  }
1126
1349
  if (field.type === "signature" || field.type === "initials") {
1127
1350
  if (field.value) {
1128
- return /* @__PURE__ */ jsx7("div", { className: "field-signature-filled", onClick: () => handleFieldUpdate(field.id, ""), children: /* @__PURE__ */ jsx7("img", { src: field.value, alt: field.label, className: "field-signature-preview" }) });
1351
+ return /* @__PURE__ */ jsx6("div", { className: "field-signature-filled", onClick: () => handleFieldUpdate(field.id, ""), children: /* @__PURE__ */ jsx6("img", { src: field.value, alt: field.label, className: "field-signature-preview" }) });
1129
1352
  }
1130
- return /* @__PURE__ */ jsx7("div", { className: "field-overlay-placeholder editable", children: field.placeholder });
1353
+ return /* @__PURE__ */ jsx6("div", { className: "field-overlay-placeholder editable", children: field.placeholder });
1131
1354
  }
1132
1355
  if (field.type === "checkbox") {
1133
- return /* @__PURE__ */ jsx7(
1356
+ return /* @__PURE__ */ jsx6(
1134
1357
  "div",
1135
1358
  {
1136
1359
  className: "field-checkbox-display editable",
@@ -1143,9 +1366,9 @@ function SignerView({
1143
1366
  );
1144
1367
  }
1145
1368
  if (field.type === "signed-date") {
1146
- return /* @__PURE__ */ jsx7("div", { className: "field-overlay-value", children: field.value || (/* @__PURE__ */ new Date()).toLocaleDateString() });
1369
+ return /* @__PURE__ */ jsx6("div", { className: "field-overlay-value", children: field.value || (/* @__PURE__ */ new Date()).toLocaleDateString() });
1147
1370
  }
1148
- return /* @__PURE__ */ jsx7(
1371
+ return /* @__PURE__ */ jsx6(
1149
1372
  "input",
1150
1373
  {
1151
1374
  type: field.textSubtype === "email" ? "email" : field.textSubtype === "number" ? "number" : field.textSubtype === "phone" ? "tel" : field.textSubtype === "date" ? "date" : "text",
@@ -1170,10 +1393,10 @@ function SignerView({
1170
1393
  }));
1171
1394
  }
1172
1395
  }, [fields.filter((f) => f.type === "signature" && f.value).length]);
1173
- return /* @__PURE__ */ jsxs7("div", { className: "signer-layout", ref: containerRef, children: [
1174
- loading && /* @__PURE__ */ jsx7("div", { className: "loading-indicator", children: "Loading document..." }),
1175
- /* @__PURE__ */ jsxs7("div", { className: "signer-content", children: [
1176
- /* @__PURE__ */ jsx7("div", { className: "signer-pdf-area", children: pages.length > 0 && /* @__PURE__ */ jsx7(
1396
+ return /* @__PURE__ */ jsxs6("div", { className: "signer-layout", ref: containerRef, children: [
1397
+ loading && /* @__PURE__ */ jsx6("div", { className: "loading-indicator", children: "Loading document..." }),
1398
+ /* @__PURE__ */ jsxs6("div", { className: "signer-content", children: [
1399
+ /* @__PURE__ */ jsx6("div", { className: "signer-pdf-area", children: pages.length > 0 && /* @__PURE__ */ jsx6(
1177
1400
  PdfViewer,
1178
1401
  {
1179
1402
  pages,
@@ -1185,11 +1408,11 @@ function SignerView({
1185
1408
  renderFieldContent
1186
1409
  }
1187
1410
  ) }),
1188
- /* @__PURE__ */ jsxs7("div", { className: "signer-panel", children: [
1189
- selectedField && isFieldEditable && /* @__PURE__ */ jsxs7("div", { className: "signer-field-input", children: [
1190
- /* @__PURE__ */ jsx7("h3", { children: selectedField.label }),
1191
- selectedField.required && /* @__PURE__ */ jsx7("span", { className: "required-badge", children: "Required" }),
1192
- (selectedField.type === "signature" || selectedField.type === "initials") && /* @__PURE__ */ jsx7(
1411
+ /* @__PURE__ */ jsxs6("div", { className: "signer-panel", children: [
1412
+ selectedField && isFieldEditable && /* @__PURE__ */ jsxs6("div", { className: "signer-field-input", children: [
1413
+ /* @__PURE__ */ jsx6("h3", { children: selectedField.label }),
1414
+ selectedField.required && /* @__PURE__ */ jsx6("span", { className: "required-badge", children: "Required" }),
1415
+ (selectedField.type === "signature" || selectedField.type === "initials") && /* @__PURE__ */ jsx6(
1193
1416
  SignatureCanvas,
1194
1417
  {
1195
1418
  width: 280,
@@ -1198,7 +1421,7 @@ function SignerView({
1198
1421
  initialValue: selectedField.value
1199
1422
  }
1200
1423
  ),
1201
- selectedField.type === "text" && /* @__PURE__ */ jsx7(
1424
+ selectedField.type === "text" && /* @__PURE__ */ jsx6(
1202
1425
  "input",
1203
1426
  {
1204
1427
  type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
@@ -1208,8 +1431,8 @@ function SignerView({
1208
1431
  className: "signer-text-input"
1209
1432
  }
1210
1433
  ),
1211
- selectedField.type === "checkbox" && /* @__PURE__ */ jsxs7("label", { className: "signer-checkbox-label", children: [
1212
- /* @__PURE__ */ jsx7(
1434
+ selectedField.type === "checkbox" && /* @__PURE__ */ jsxs6("label", { className: "signer-checkbox-label", children: [
1435
+ /* @__PURE__ */ jsx6(
1213
1436
  "input",
1214
1437
  {
1215
1438
  type: "checkbox",
@@ -1219,14 +1442,14 @@ function SignerView({
1219
1442
  ),
1220
1443
  selectedField.placeholder || "Check this box"
1221
1444
  ] }),
1222
- selectedField.type === "signed-date" && /* @__PURE__ */ jsx7("div", { className: "signer-date-display", children: selectedField.value || "Will auto-fill when you sign" })
1445
+ selectedField.type === "signed-date" && /* @__PURE__ */ jsx6("div", { className: "signer-date-display", children: selectedField.value || "Will auto-fill when you sign" })
1223
1446
  ] }),
1224
- selectedField && !isFieldEditable && /* @__PURE__ */ jsxs7("div", { className: "signer-field-readonly", children: [
1225
- /* @__PURE__ */ jsx7("h3", { children: selectedField.label }),
1226
- /* @__PURE__ */ jsx7("p", { children: "This field belongs to another signer." })
1447
+ selectedField && !isFieldEditable && /* @__PURE__ */ jsxs6("div", { className: "signer-field-readonly", children: [
1448
+ /* @__PURE__ */ jsx6("h3", { children: selectedField.label }),
1449
+ /* @__PURE__ */ jsx6("p", { children: "This field belongs to another signer." })
1227
1450
  ] }),
1228
- !selectedField && editableFields.length > 0 && /* @__PURE__ */ jsx7("div", { className: "panel-empty", children: "Select a field to fill it in, or use the navigation below." }),
1229
- /* @__PURE__ */ jsx7(
1451
+ !selectedField && editableFields.length > 0 && /* @__PURE__ */ jsx6("div", { className: "panel-empty", children: "Select a field to fill it in, or use the navigation below." }),
1452
+ /* @__PURE__ */ jsx6(
1230
1453
  FieldNavigator,
1231
1454
  {
1232
1455
  fields: editableFields,
@@ -1236,12 +1459,87 @@ function SignerView({
1236
1459
  onSubmit: handleSubmit
1237
1460
  }
1238
1461
  ),
1239
- submitting && /* @__PURE__ */ jsx7("div", { className: "loading-indicator", children: "Generating PDF..." })
1462
+ submitting && /* @__PURE__ */ jsx6("div", { className: "loading-indicator", children: "Generating PDF..." })
1240
1463
  ] })
1241
1464
  ] }),
1242
- /* @__PURE__ */ jsxs7("div", { className: "powered-by", children: [
1465
+ /* @__PURE__ */ jsxs6("div", { className: "powered-by", children: [
1243
1466
  "Powered by ",
1244
- /* @__PURE__ */ jsx7("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
1467
+ /* @__PURE__ */ jsx6("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
1468
+ ] })
1469
+ ] });
1470
+ }
1471
+
1472
+ // src/components/pdf-builder/SignerRoleSelector.tsx
1473
+ import { useState as useState4 } from "react";
1474
+ import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
1475
+ function SignerRoleSelector({
1476
+ roles,
1477
+ activeRole,
1478
+ onSetActiveRole,
1479
+ onAddRole,
1480
+ onRemoveRole
1481
+ }) {
1482
+ const [isAdding, setIsAdding] = useState4(false);
1483
+ const [newRoleName, setNewRoleName] = useState4("");
1484
+ const handleAdd = () => {
1485
+ if (newRoleName.trim()) {
1486
+ onAddRole(newRoleName.trim());
1487
+ setNewRoleName("");
1488
+ setIsAdding(false);
1489
+ }
1490
+ };
1491
+ return /* @__PURE__ */ jsxs7("div", { className: "signer-role-selector", children: [
1492
+ /* @__PURE__ */ jsx7("div", { className: "signer-role-label", children: "Signer Roles" }),
1493
+ /* @__PURE__ */ jsxs7("div", { className: "signer-role-list", children: [
1494
+ roles.map((role) => /* @__PURE__ */ jsxs7(
1495
+ "button",
1496
+ {
1497
+ className: `signer-role-chip ${role === activeRole ? "active" : ""}`,
1498
+ style: {
1499
+ borderColor: getSignerColor(role),
1500
+ backgroundColor: role === activeRole ? getSignerColor(role) : "transparent",
1501
+ color: role === activeRole ? "#fff" : getSignerColor(role)
1502
+ },
1503
+ onClick: () => onSetActiveRole(role),
1504
+ children: [
1505
+ role,
1506
+ roles.length > 1 && role !== "Sender" && /* @__PURE__ */ jsx7(
1507
+ "span",
1508
+ {
1509
+ className: "signer-role-remove",
1510
+ onClick: (e) => {
1511
+ e.stopPropagation();
1512
+ onRemoveRole(role);
1513
+ },
1514
+ children: "\xD7"
1515
+ }
1516
+ )
1517
+ ]
1518
+ },
1519
+ role
1520
+ )),
1521
+ isAdding ? /* @__PURE__ */ jsxs7("div", { className: "signer-role-add-input", children: [
1522
+ /* @__PURE__ */ jsx7(
1523
+ "input",
1524
+ {
1525
+ type: "text",
1526
+ value: newRoleName,
1527
+ onChange: (e) => setNewRoleName(e.target.value),
1528
+ onKeyDown: (e) => e.key === "Enter" && handleAdd(),
1529
+ placeholder: "Role name",
1530
+ autoFocus: true
1531
+ }
1532
+ ),
1533
+ /* @__PURE__ */ jsx7("button", { onClick: handleAdd, children: "Add" }),
1534
+ /* @__PURE__ */ jsx7("button", { onClick: () => setIsAdding(false), children: "Cancel" })
1535
+ ] }) : /* @__PURE__ */ jsx7(
1536
+ "button",
1537
+ {
1538
+ className: "signer-role-add-btn",
1539
+ onClick: () => setIsAdding(true),
1540
+ children: "+ Add Role"
1541
+ }
1542
+ )
1245
1543
  ] })
1246
1544
  ] });
1247
1545
  }
@@ -1261,6 +1559,7 @@ export {
1261
1559
  generateFilledPdf,
1262
1560
  getSignerColor,
1263
1561
  postPdfToCallback,
1264
- renderPdfPages
1562
+ renderPdfPages,
1563
+ uniqueLabel
1265
1564
  };
1266
1565
  //# sourceMappingURL=index.mjs.map