@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.js CHANGED
@@ -45,12 +45,14 @@ __export(lib_exports, {
45
45
  generateFilledPdf: () => generateFilledPdf,
46
46
  getSignerColor: () => getSignerColor,
47
47
  postPdfToCallback: () => postPdfToCallback,
48
- renderPdfPages: () => renderPdfPages
48
+ renderPdfPages: () => renderPdfPages,
49
+ uniqueLabel: () => uniqueLabel
49
50
  });
50
51
  module.exports = __toCommonJS(lib_exports);
51
52
 
52
53
  // src/components/pdf-builder/DesignerView.tsx
53
- var import_react4 = require("react");
54
+ var import_react3 = require("react");
55
+ var import_react_dom = require("react-dom");
54
56
 
55
57
  // src/types/pdf-builder.ts
56
58
  function generateId() {
@@ -106,14 +108,44 @@ var FIELD_DEFAULTS = {
106
108
  height: 5,
107
109
  fontSize: 12,
108
110
  placeholder: "Initials"
111
+ },
112
+ blackout: {
113
+ width: 20,
114
+ height: 3,
115
+ fontSize: 12,
116
+ placeholder: ""
117
+ },
118
+ whiteout: {
119
+ width: 20,
120
+ height: 3,
121
+ fontSize: 12,
122
+ placeholder: ""
109
123
  }
110
124
  };
111
- function createField(type, assignee, page, x, y) {
125
+ var TYPE_LABELS = {
126
+ text: "Text Field",
127
+ signature: "Signature",
128
+ "signed-date": "Signed Date",
129
+ checkbox: "Checkbox",
130
+ initials: "Initials",
131
+ blackout: "Blackout",
132
+ whiteout: "Whiteout"
133
+ };
134
+ function uniqueLabel(desired, existingLabels) {
135
+ const lower = desired.toLowerCase();
136
+ if (!existingLabels.some((l) => l.toLowerCase() === lower)) return desired;
137
+ let i = 2;
138
+ while (existingLabels.some((l) => l.toLowerCase() === `${lower} ${i}`)) i++;
139
+ return `${desired} ${i}`;
140
+ }
141
+ function createField(type, assignee, page, x, y, existingFields) {
112
142
  const defaults = FIELD_DEFAULTS[type];
143
+ const baseLabel = TYPE_LABELS[type];
144
+ const existingLabels = existingFields?.map((f) => f.label) ?? [];
113
145
  return {
114
146
  id: generateId(),
115
147
  type,
116
- label: type === "text" ? "Text Field" : type === "signature" ? "Signature" : type === "signed-date" ? "Signed Date" : type === "checkbox" ? "Checkbox" : "Initials",
148
+ label: uniqueLabel(baseLabel, existingLabels),
117
149
  placeholder: defaults.placeholder || "",
118
150
  required: true,
119
151
  assignee,
@@ -168,6 +200,7 @@ function PdfViewer({
168
200
  onFieldMove,
169
201
  onFieldResize,
170
202
  onPageClick,
203
+ onDropField,
171
204
  mode,
172
205
  currentSigner,
173
206
  renderFieldContent
@@ -185,6 +218,21 @@ function PdfViewer({
185
218
  onSelectField(null);
186
219
  }
187
220
  }, [onPageClick, onSelectField]);
221
+ const handleDragOver = (0, import_react.useCallback)((e) => {
222
+ if (e.dataTransfer.types.includes("application/exeq-field-type")) {
223
+ e.preventDefault();
224
+ e.dataTransfer.dropEffect = "copy";
225
+ }
226
+ }, []);
227
+ const handleDrop = (0, import_react.useCallback)((e, pageIndex) => {
228
+ const fieldType = e.dataTransfer.getData("application/exeq-field-type");
229
+ if (!fieldType || !onDropField) return;
230
+ e.preventDefault();
231
+ const rect = e.currentTarget.getBoundingClientRect();
232
+ const x = (e.clientX - rect.left) / rect.width * 100;
233
+ const y = (e.clientY - rect.top) / rect.height * 100;
234
+ onDropField(pageIndex, x, y, fieldType);
235
+ }, [onDropField]);
188
236
  return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pdf-viewer", ref: containerRef, children: pages.map((page, pageIndex) => {
189
237
  const pageFields = fields.filter((f) => f.page === pageIndex);
190
238
  return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
@@ -193,6 +241,8 @@ function PdfViewer({
193
241
  className: "pdf-page",
194
242
  style: { aspectRatio: `${page.width} / ${page.height}` },
195
243
  onClick: (e) => handlePageClick(e, pageIndex),
244
+ onDragOver: handleDragOver,
245
+ onDrop: (e) => handleDrop(e, pageIndex),
196
246
  "data-page": pageIndex,
197
247
  children: [
198
248
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
@@ -238,6 +288,7 @@ function FieldOverlayItem({
238
288
  const dragStartRef = (0, import_react.useRef)(null);
239
289
  const resizeStartRef = (0, import_react.useRef)(null);
240
290
  const color = getSignerColor(field.assignee);
291
+ const isRedact = field.type === "blackout" || field.type === "whiteout";
241
292
  const isEditable = mode === "designer" || mode === "signer" && field.assignee === currentSigner;
242
293
  const isPreFilled = !isEditable && !!field.value;
243
294
  const handleMouseDown = (0, import_react.useCallback)((e) => {
@@ -303,14 +354,15 @@ function FieldOverlayItem({
303
354
  "div",
304
355
  {
305
356
  ref: overlayRef,
306
- className: `field-overlay ${isSelected ? "selected" : ""} ${isEditable ? "editable" : "readonly"} ${isPreFilled ? "prefilled" : ""}`,
357
+ className: `field-overlay ${isSelected ? "selected" : ""} ${isEditable ? "editable" : "readonly"} ${isPreFilled ? "prefilled" : ""} ${isRedact ? "redact" : ""}`,
307
358
  style: {
308
359
  left: `${field.x}%`,
309
360
  top: `${field.y}%`,
310
361
  width: `${field.width}%`,
311
362
  height: `${field.height}%`,
312
- borderColor: color,
313
- backgroundColor: isSelected ? `${color}22` : `${color}11`,
363
+ borderColor: isRedact ? field.type === "blackout" ? "#666" : "#bbb" : color,
364
+ borderStyle: isRedact ? "dashed" : "solid",
365
+ backgroundColor: isRedact ? field.type === "blackout" ? "#000000" : "#ffffff" : isSelected ? `${color}22` : `${color}11`,
314
366
  cursor: mode === "designer" ? "move" : "default"
315
367
  },
316
368
  onClick: (e) => {
@@ -319,7 +371,7 @@ function FieldOverlayItem({
319
371
  },
320
372
  onMouseDown: handleMouseDown,
321
373
  children: [
322
- mode === "designer" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "field-overlay-label", style: { backgroundColor: color }, children: field.label }),
374
+ mode === "designer" && !isRedact && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "field-overlay-label", style: { backgroundColor: color }, children: field.label }),
323
375
  renderContent ? renderContent(field) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "field-overlay-placeholder", children: field.value || field.placeholder }),
324
376
  mode === "designer" && isSelected && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
325
377
  "div",
@@ -335,8 +387,15 @@ function FieldOverlayItem({
335
387
 
336
388
  // src/components/pdf-builder/FieldPropertyPanel.tsx
337
389
  var import_jsx_runtime2 = require("react/jsx-runtime");
390
+ var INK_COLORS = [
391
+ { value: "#000000", label: "Black" },
392
+ { value: "#1a56db", label: "Blue" }
393
+ ];
338
394
  function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
339
395
  const color = getSignerColor(field.assignee);
396
+ const isRedactField = field.type === "blackout" || field.type === "whiteout";
397
+ const showFontSize = field.type === "text" || field.type === "signed-date";
398
+ const showInkColor = field.type === "text" || field.type === "signature" || field.type === "initials" || field.type === "signed-date";
340
399
  return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "field-property-panel", children: [
341
400
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-header", children: [
342
401
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { style: { color }, children: field.label }),
@@ -353,7 +412,7 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
353
412
  }
354
413
  )
355
414
  ] }),
356
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
415
+ !isRedactField && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
357
416
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Field Type" }),
358
417
  /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
359
418
  "select",
@@ -387,7 +446,7 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
387
446
  }
388
447
  )
389
448
  ] }),
390
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
449
+ !isRedactField && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
391
450
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Assigned To" }),
392
451
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
393
452
  "select",
@@ -398,7 +457,7 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
398
457
  }
399
458
  )
400
459
  ] }),
401
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
460
+ !isRedactField && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
402
461
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Placeholder" }),
403
462
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
404
463
  "input",
@@ -409,7 +468,7 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
409
468
  }
410
469
  )
411
470
  ] }),
412
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "panel-field", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "panel-checkbox-label", children: [
471
+ !isRedactField && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "panel-field", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "panel-checkbox-label", children: [
413
472
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
414
473
  "input",
415
474
  {
@@ -420,7 +479,7 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
420
479
  ),
421
480
  "Required"
422
481
  ] }) }),
423
- field.type !== "checkbox" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
482
+ showFontSize && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
424
483
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Font Size (pt)" }),
425
484
  /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
426
485
  "input",
@@ -433,118 +492,26 @@ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
433
492
  }
434
493
  )
435
494
  ] }),
436
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field-group", children: [
437
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
438
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Width (%)" }),
439
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
440
- "input",
441
- {
442
- type: "number",
443
- min: "1",
444
- max: "100",
445
- step: "0.5",
446
- value: field.width,
447
- onChange: (e) => onUpdate(field.id, { width: Number(e.target.value) })
448
- }
449
- )
450
- ] }),
451
- /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
452
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Height (%)" }),
453
- /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
454
- "input",
455
- {
456
- type: "number",
457
- min: "1",
458
- max: "100",
459
- step: "0.5",
460
- value: field.height,
461
- onChange: (e) => onUpdate(field.id, { height: Number(e.target.value) })
462
- }
463
- )
464
- ] })
465
- ] })
466
- ] });
467
- }
468
-
469
- // src/components/pdf-builder/SignerRoleSelector.tsx
470
- var import_react2 = require("react");
471
- var import_jsx_runtime3 = require("react/jsx-runtime");
472
- function SignerRoleSelector({
473
- roles,
474
- activeRole,
475
- onSetActiveRole,
476
- onAddRole,
477
- onRemoveRole
478
- }) {
479
- const [isAdding, setIsAdding] = (0, import_react2.useState)(false);
480
- const [newRoleName, setNewRoleName] = (0, import_react2.useState)("");
481
- const handleAdd = () => {
482
- if (newRoleName.trim()) {
483
- onAddRole(newRoleName.trim());
484
- setNewRoleName("");
485
- setIsAdding(false);
486
- }
487
- };
488
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "signer-role-selector", children: [
489
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "signer-role-label", children: "Signer Roles" }),
490
- /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "signer-role-list", children: [
491
- roles.map((role) => /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
495
+ showInkColor && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
496
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Ink Color" }),
497
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ink-color-picker", children: INK_COLORS.map((c) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
492
498
  "button",
493
499
  {
494
- className: `signer-role-chip ${role === activeRole ? "active" : ""}`,
495
- style: {
496
- borderColor: getSignerColor(role),
497
- backgroundColor: role === activeRole ? getSignerColor(role) : "transparent",
498
- color: role === activeRole ? "#fff" : getSignerColor(role)
499
- },
500
- onClick: () => onSetActiveRole(role),
501
- children: [
502
- role,
503
- roles.length > 1 && role !== "Sender" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
504
- "span",
505
- {
506
- className: "signer-role-remove",
507
- onClick: (e) => {
508
- e.stopPropagation();
509
- onRemoveRole(role);
510
- },
511
- children: "\xD7"
512
- }
513
- )
514
- ]
500
+ className: `ink-color-swatch ${(field.inkColor || "#000000") === c.value ? "active" : ""}`,
501
+ style: { backgroundColor: c.value },
502
+ onClick: () => onUpdate(field.id, { inkColor: c.value }),
503
+ title: c.label
515
504
  },
516
- role
517
- )),
518
- isAdding ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "signer-role-add-input", children: [
519
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
520
- "input",
521
- {
522
- type: "text",
523
- value: newRoleName,
524
- onChange: (e) => setNewRoleName(e.target.value),
525
- onKeyDown: (e) => e.key === "Enter" && handleAdd(),
526
- placeholder: "Role name",
527
- autoFocus: true
528
- }
529
- ),
530
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { onClick: handleAdd, children: "Add" }),
531
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { onClick: () => setIsAdding(false), children: "Cancel" })
532
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
533
- "button",
534
- {
535
- className: "signer-role-add-btn",
536
- onClick: () => setIsAdding(true),
537
- children: "+ Add Role"
538
- }
539
- )
505
+ c.value
506
+ )) })
540
507
  ] })
541
508
  ] });
542
509
  }
543
510
 
544
511
  // src/components/pdf-builder/SignatureCanvas.tsx
545
- var import_react3 = require("react");
512
+ var import_react2 = require("react");
546
513
  var import_perfect_freehand = require("perfect-freehand");
547
- var import_jsx_runtime4 = require("react/jsx-runtime");
514
+ var import_jsx_runtime3 = require("react/jsx-runtime");
548
515
  function getSvgPathFromStroke(stroke) {
549
516
  if (!stroke.length) return "";
550
517
  const d = stroke.reduce(
@@ -563,13 +530,14 @@ function SignatureCanvas({
563
530
  height = 150,
564
531
  onSign,
565
532
  initialValue,
566
- className
533
+ className,
534
+ inkColor = "#000000"
567
535
  }) {
568
- const canvasRef = (0, import_react3.useRef)(null);
569
- const [paths, setPaths] = (0, import_react3.useState)([]);
570
- const [currentPath, setCurrentPath] = (0, import_react3.useState)(null);
571
- const [isEmpty, setIsEmpty] = (0, import_react3.useState)(!initialValue);
572
- (0, import_react3.useEffect)(() => {
536
+ const canvasRef = (0, import_react2.useRef)(null);
537
+ const [paths, setPaths] = (0, import_react2.useState)([]);
538
+ const [currentPath, setCurrentPath] = (0, import_react2.useState)(null);
539
+ const [isEmpty, setIsEmpty] = (0, import_react2.useState)(!initialValue);
540
+ (0, import_react2.useEffect)(() => {
573
541
  if (initialValue && canvasRef.current) {
574
542
  const ctx = canvasRef.current.getContext("2d");
575
543
  const img = new Image();
@@ -580,7 +548,7 @@ function SignatureCanvas({
580
548
  img.src = initialValue;
581
549
  }
582
550
  }, [initialValue, width, height]);
583
- (0, import_react3.useEffect)(() => {
551
+ (0, import_react2.useEffect)(() => {
584
552
  const canvas = canvasRef.current;
585
553
  if (!canvas) return;
586
554
  const ctx = canvas.getContext("2d");
@@ -595,11 +563,11 @@ function SignatureCanvas({
595
563
  });
596
564
  const pathStr = getSvgPathFromStroke(stroke);
597
565
  const path2d = new Path2D(pathStr);
598
- ctx.fillStyle = "#000";
566
+ ctx.fillStyle = inkColor;
599
567
  ctx.fill(path2d);
600
568
  }
601
569
  }, [paths, currentPath, width, height]);
602
- const getPoint = (0, import_react3.useCallback)((e) => {
570
+ const getPoint = (0, import_react2.useCallback)((e) => {
603
571
  const rect = canvasRef.current.getBoundingClientRect();
604
572
  return [
605
573
  e.clientX - rect.left,
@@ -607,17 +575,17 @@ function SignatureCanvas({
607
575
  e.pressure
608
576
  ];
609
577
  }, []);
610
- const handlePointerDown = (0, import_react3.useCallback)((e) => {
578
+ const handlePointerDown = (0, import_react2.useCallback)((e) => {
611
579
  e.preventDefault();
612
580
  e.target.setPointerCapture(e.pointerId);
613
581
  setCurrentPath([getPoint(e)]);
614
582
  }, [getPoint]);
615
- const handlePointerMove = (0, import_react3.useCallback)((e) => {
583
+ const handlePointerMove = (0, import_react2.useCallback)((e) => {
616
584
  if (!currentPath) return;
617
585
  e.preventDefault();
618
586
  setCurrentPath([...currentPath, getPoint(e)]);
619
587
  }, [currentPath, getPoint]);
620
- const handlePointerUp = (0, import_react3.useCallback)(() => {
588
+ const handlePointerUp = (0, import_react2.useCallback)(() => {
621
589
  if (!currentPath) return;
622
590
  setPaths((prev) => [...prev, currentPath]);
623
591
  setCurrentPath(null);
@@ -628,7 +596,7 @@ function SignatureCanvas({
628
596
  }
629
597
  });
630
598
  }, [currentPath, onSign]);
631
- const handleClear = (0, import_react3.useCallback)(() => {
599
+ const handleClear = (0, import_react2.useCallback)(() => {
632
600
  setPaths([]);
633
601
  setCurrentPath(null);
634
602
  setIsEmpty(true);
@@ -639,8 +607,8 @@ function SignatureCanvas({
639
607
  onSign("");
640
608
  }
641
609
  }, [width, height, onSign]);
642
- return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `signature-canvas-wrapper ${className || ""}`, children: [
643
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
610
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `signature-canvas-wrapper ${className || ""}`, children: [
611
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
644
612
  "canvas",
645
613
  {
646
614
  ref: canvasRef,
@@ -653,29 +621,76 @@ function SignatureCanvas({
653
621
  style: { touchAction: "none" }
654
622
  }
655
623
  ),
656
- /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "signature-canvas-actions", children: [
657
- !isEmpty && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { type: "button", onClick: handleClear, className: "signature-clear-btn", children: "Clear" }),
658
- isEmpty && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "signature-hint", children: "Draw your signature above" })
624
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "signature-canvas-actions", children: [
625
+ !isEmpty && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", onClick: handleClear, className: "signature-clear-btn", children: "Clear" }),
626
+ isEmpty && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "signature-hint", children: "Draw your signature above" })
659
627
  ] })
660
628
  ] });
661
629
  }
662
630
 
631
+ // src/utils/apiKey.ts
632
+ var VALID_KEYS = /* @__PURE__ */ new Set([
633
+ "exeq_live_2024"
634
+ ]);
635
+ function isValidApiKey(key) {
636
+ if (!key) return false;
637
+ return VALID_KEYS.has(key);
638
+ }
639
+
663
640
  // src/components/pdf-builder/DesignerView.tsx
664
- var import_jsx_runtime5 = require("react/jsx-runtime");
665
- function DesignerView() {
666
- const [pages, setPages] = (0, import_react4.useState)([]);
667
- const [fields, setFields] = (0, import_react4.useState)([]);
668
- const [selectedFieldId, setSelectedFieldId] = (0, import_react4.useState)(null);
669
- const [signerRoles, setSignerRoles] = (0, import_react4.useState)([...DEFAULT_SIGNER_ROLES]);
670
- const [activeRole, setActiveRole] = (0, import_react4.useState)("Sender");
671
- const [activeFieldType, setActiveFieldType] = (0, import_react4.useState)("text");
672
- const [loading, setLoading] = (0, import_react4.useState)(false);
673
- const [pdfSource, setPdfSource] = (0, import_react4.useState)(null);
674
- (0, import_react4.useEffect)(() => {
675
- const params = new URLSearchParams(window.location.search);
676
- const pdfUrl = params.get("pdf");
641
+ var import_jsx_runtime4 = require("react/jsx-runtime");
642
+ var FIELD_TYPE_META = [
643
+ { type: "text", label: "Text", icon: "T" },
644
+ { type: "signature", label: "Signature", icon: "\u270D" },
645
+ { type: "signed-date", label: "Date", icon: "\u{1F4C5}" },
646
+ { type: "checkbox", label: "Checkbox", icon: "\u2611" },
647
+ { type: "initials", label: "Initials", icon: "IN" },
648
+ { type: "blackout", label: "Blackout", icon: "\u25A0", group: "redact" },
649
+ { type: "whiteout", label: "Whiteout", icon: "\u25A1", group: "redact" }
650
+ ];
651
+ function DesignerView({
652
+ apiKey,
653
+ initialPdfUrl,
654
+ initialTemplate,
655
+ onSave,
656
+ hideHeader,
657
+ headerPortalRef
658
+ } = {}) {
659
+ if (!isValidApiKey(apiKey)) {
660
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "designer-layout", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "empty-state", children: [
661
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { children: "Invalid API Key" }),
662
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { children: [
663
+ "A valid API key is required to use Exeq. Visit ",
664
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("a", { href: "https://exeq.org", children: "exeq.org" }),
665
+ " to get one."
666
+ ] })
667
+ ] }) });
668
+ }
669
+ const [pages, setPages] = (0, import_react3.useState)([]);
670
+ const [fields, setFields] = (0, import_react3.useState)(initialTemplate?.fields ?? []);
671
+ const [selectedFieldId, setSelectedFieldId] = (0, import_react3.useState)(null);
672
+ const [signerRoles, setSignerRoles] = (0, import_react3.useState)(initialTemplate?.signerRoles ?? [...DEFAULT_SIGNER_ROLES]);
673
+ const [activeRole, setActiveRole] = (0, import_react3.useState)("Sender");
674
+ const [activeFieldType, setActiveFieldType] = (0, import_react3.useState)("text");
675
+ const [loading, setLoading] = (0, import_react3.useState)(false);
676
+ const [pdfSource, setPdfSource] = (0, import_react3.useState)(null);
677
+ const [rightTab, setRightTab] = (0, import_react3.useState)("properties");
678
+ const [isAddingRole, setIsAddingRole] = (0, import_react3.useState)(false);
679
+ const [newRoleName, setNewRoleName] = (0, import_react3.useState)("");
680
+ const [draggingFieldType, setDraggingFieldType] = (0, import_react3.useState)(null);
681
+ const [panelWidth, setPanelWidth] = (0, import_react3.useState)(380);
682
+ const dragGhostRef = (0, import_react3.useRef)(null);
683
+ const resizingRef = (0, import_react3.useRef)(false);
684
+ (0, import_react3.useEffect)(() => {
685
+ const pdfUrl = initialPdfUrl || initialTemplate?.pdfUrl;
677
686
  if (pdfUrl) {
678
687
  loadPdf(pdfUrl);
688
+ return;
689
+ }
690
+ const params = new URLSearchParams(window.location.search);
691
+ const paramPdf = params.get("pdf");
692
+ if (paramPdf) {
693
+ loadPdf(paramPdf);
679
694
  }
680
695
  const handleMessage = (e) => {
681
696
  if (e.data?.type === "load-pdf" && e.data.url) {
@@ -689,8 +704,8 @@ function DesignerView() {
689
704
  };
690
705
  window.addEventListener("message", handleMessage);
691
706
  return () => window.removeEventListener("message", handleMessage);
692
- }, []);
693
- const loadPdf = (0, import_react4.useCallback)(async (source) => {
707
+ }, [initialPdfUrl, initialTemplate]);
708
+ const loadPdf = (0, import_react3.useCallback)(async (source) => {
694
709
  setLoading(true);
695
710
  try {
696
711
  const rendered = await renderPdfPages(source);
@@ -702,7 +717,7 @@ function DesignerView() {
702
717
  setLoading(false);
703
718
  }
704
719
  }, []);
705
- const handleFileUpload = (0, import_react4.useCallback)((e) => {
720
+ const handleFileUpload = (0, import_react3.useCallback)((e) => {
706
721
  const file = e.target.files?.[0];
707
722
  if (!file) return;
708
723
  const reader = new FileReader();
@@ -711,204 +726,368 @@ function DesignerView() {
711
726
  };
712
727
  reader.readAsArrayBuffer(file);
713
728
  }, [loadPdf]);
714
- const handlePageClick = (0, import_react4.useCallback)((page, x, y) => {
715
- const field = createField(activeFieldType, activeRole, page, x, y);
729
+ const handlePageClick = (0, import_react3.useCallback)((page, x, y) => {
730
+ const field = createField(activeFieldType, activeRole, page, x, y, fields);
716
731
  setFields((prev) => [...prev, field]);
717
732
  setSelectedFieldId(field.id);
718
- }, [activeFieldType, activeRole]);
719
- const handleFieldMove = (0, import_react4.useCallback)((id, page, x, y) => {
733
+ setRightTab("properties");
734
+ }, [activeFieldType, activeRole, fields]);
735
+ const handleFieldMove = (0, import_react3.useCallback)((id, page, x, y) => {
720
736
  setFields((prev) => prev.map((f) => f.id === id ? { ...f, page, x, y } : f));
721
737
  }, []);
722
- const handleFieldResize = (0, import_react4.useCallback)((id, width, height) => {
738
+ const handleFieldResize = (0, import_react3.useCallback)((id, width, height) => {
723
739
  setFields((prev) => prev.map((f) => f.id === id ? { ...f, width, height } : f));
724
740
  }, []);
725
- const handleFieldUpdate = (0, import_react4.useCallback)((id, updates) => {
726
- setFields((prev) => prev.map((f) => f.id === id ? { ...f, ...updates } : f));
741
+ const handleFieldUpdate = (0, import_react3.useCallback)((id, updates) => {
742
+ setFields((prev) => {
743
+ if (updates.label !== void 0) {
744
+ const otherLabels = prev.filter((f) => f.id !== id).map((f) => f.label);
745
+ updates.label = uniqueLabel(updates.label, otherLabels);
746
+ }
747
+ return prev.map((f) => f.id === id ? { ...f, ...updates } : f);
748
+ });
727
749
  }, []);
728
- const handleFieldDelete = (0, import_react4.useCallback)((id) => {
750
+ const handleFieldDelete = (0, import_react3.useCallback)((id) => {
729
751
  setFields((prev) => prev.filter((f) => f.id !== id));
730
752
  if (selectedFieldId === id) setSelectedFieldId(null);
731
753
  }, [selectedFieldId]);
732
- const handleAddRole = (0, import_react4.useCallback)((role) => {
733
- if (!signerRoles.includes(role)) {
734
- setSignerRoles((prev) => [...prev, role]);
754
+ const handleAddRole = (0, import_react3.useCallback)(() => {
755
+ const name = newRoleName.trim();
756
+ if (name && !signerRoles.includes(name)) {
757
+ setSignerRoles((prev) => [...prev, name]);
735
758
  }
736
- }, [signerRoles]);
737
- const handleRemoveRole = (0, import_react4.useCallback)((role) => {
759
+ setNewRoleName("");
760
+ setIsAddingRole(false);
761
+ }, [newRoleName, signerRoles]);
762
+ const handleRemoveRole = (0, import_react3.useCallback)((role) => {
738
763
  setSignerRoles((prev) => prev.filter((r) => r !== role));
739
764
  setFields((prev) => prev.map((f) => f.assignee === role ? { ...f, assignee: signerRoles[0] } : f));
740
765
  if (activeRole === role) setActiveRole(signerRoles[0]);
741
766
  }, [signerRoles, activeRole]);
742
- const handleExport = (0, import_react4.useCallback)(() => {
767
+ const handleExport = (0, import_react3.useCallback)(() => {
743
768
  const template = {
744
769
  fields,
745
770
  signerRoles,
746
771
  pdfUrl: typeof pdfSource === "string" ? pdfSource : ""
747
772
  };
748
- const json = JSON.stringify(template, null, 2);
749
- const blob = new Blob([json], { type: "application/json" });
750
- const url = URL.createObjectURL(blob);
751
- const a = document.createElement("a");
752
- a.href = url;
753
- a.download = "template.json";
754
- a.click();
755
- URL.revokeObjectURL(url);
773
+ if (onSave) {
774
+ onSave(template);
775
+ } else {
776
+ const json = JSON.stringify(template, null, 2);
777
+ const blob = new Blob([json], { type: "application/json" });
778
+ const url = URL.createObjectURL(blob);
779
+ const a = document.createElement("a");
780
+ a.href = url;
781
+ a.download = "template.json";
782
+ a.click();
783
+ URL.revokeObjectURL(url);
784
+ }
756
785
  window.parent?.postMessage({ type: "template-saved", template }, "*");
757
- }, [fields, signerRoles, pdfSource]);
786
+ }, [fields, signerRoles, pdfSource, onSave]);
787
+ const handlePaletteDragStart = (0, import_react3.useCallback)((e, type) => {
788
+ setDraggingFieldType(type);
789
+ e.dataTransfer.setData("application/exeq-field-type", type);
790
+ e.dataTransfer.effectAllowed = "copy";
791
+ const ghost = document.createElement("div");
792
+ ghost.className = "palette-drag-ghost";
793
+ ghost.textContent = FIELD_TYPE_META.find((f) => f.type === type)?.label || type;
794
+ ghost.style.cssText = `
795
+ position: fixed; top: -200px; left: -200px;
796
+ padding: 6px 16px; background: ${getSignerColor(activeRole)};
797
+ color: #fff; border-radius: 4px; font-size: 12px;
798
+ font-family: system-ui; pointer-events: none; white-space: nowrap;
799
+ `;
800
+ document.body.appendChild(ghost);
801
+ e.dataTransfer.setDragImage(ghost, 40, 16);
802
+ dragGhostRef.current = ghost;
803
+ }, [activeRole]);
804
+ const handlePaletteDragEnd = (0, import_react3.useCallback)(() => {
805
+ setDraggingFieldType(null);
806
+ if (dragGhostRef.current) {
807
+ document.body.removeChild(dragGhostRef.current);
808
+ dragGhostRef.current = null;
809
+ }
810
+ }, []);
811
+ const handleDropOnPage = (0, import_react3.useCallback)((page, x, y, fieldType) => {
812
+ const field = createField(fieldType, activeRole, page, x, y, fields);
813
+ setFields((prev) => [...prev, field]);
814
+ setSelectedFieldId(field.id);
815
+ setRightTab("properties");
816
+ }, [activeRole, fields]);
817
+ (0, import_react3.useEffect)(() => {
818
+ const handleKeyDown = (e) => {
819
+ if (e.key !== "Delete" && e.key !== "Backspace") return;
820
+ if (!selectedFieldId) return;
821
+ const tag = (document.activeElement?.tagName || "").toLowerCase();
822
+ if (tag === "input" || tag === "textarea" || tag === "select") return;
823
+ if (document.activeElement?.isContentEditable) return;
824
+ e.preventDefault();
825
+ if (window.confirm("Delete this field?")) {
826
+ handleFieldDelete(selectedFieldId);
827
+ }
828
+ };
829
+ window.addEventListener("keydown", handleKeyDown);
830
+ return () => window.removeEventListener("keydown", handleKeyDown);
831
+ }, [selectedFieldId, handleFieldDelete]);
832
+ const handleResizeStart = (0, import_react3.useCallback)((e) => {
833
+ e.preventDefault();
834
+ resizingRef.current = true;
835
+ const startX = e.clientX;
836
+ const startWidth = panelWidth;
837
+ const handleMouseMove = (e2) => {
838
+ if (!resizingRef.current) return;
839
+ const delta = startX - e2.clientX;
840
+ setPanelWidth(Math.max(280, Math.min(600, startWidth + delta)));
841
+ };
842
+ const handleMouseUp = () => {
843
+ resizingRef.current = false;
844
+ window.removeEventListener("mousemove", handleMouseMove);
845
+ window.removeEventListener("mouseup", handleMouseUp);
846
+ };
847
+ window.addEventListener("mousemove", handleMouseMove);
848
+ window.addEventListener("mouseup", handleMouseUp);
849
+ }, [panelWidth]);
850
+ const sortedFields = [...fields].sort((a, b) => {
851
+ if (a.page !== b.page) return a.page - b.page;
852
+ const bandThreshold = 2;
853
+ if (Math.abs(a.y - b.y) > bandThreshold) return a.y - b.y;
854
+ return a.x - b.x;
855
+ });
758
856
  const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
759
- const renderFieldContent = (0, import_react4.useCallback)((field) => {
857
+ const renderFieldContent = (0, import_react3.useCallback)((field) => {
858
+ if (field.type === "blackout" || field.type === "whiteout") {
859
+ return null;
860
+ }
760
861
  if (field.type === "signature" || field.type === "initials") {
761
862
  if (field.value) {
762
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("img", { src: field.value, alt: field.label, className: "field-signature-preview" });
863
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { src: field.value, alt: field.label, className: "field-signature-preview" });
763
864
  }
764
865
  }
765
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "field-overlay-placeholder", children: field.value || field.placeholder });
866
+ const inkColor = field.inkColor || "#000000";
867
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
868
+ "div",
869
+ {
870
+ className: "field-overlay-placeholder",
871
+ style: { color: field.value ? inkColor : void 0, fontSize: `${field.fontSize * 0.6}px` },
872
+ children: field.value || field.placeholder
873
+ }
874
+ );
766
875
  }, []);
767
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "designer-layout", children: [
768
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "designer-toolbar", children: [
769
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "toolbar-left", children: [
770
- !pages.length && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "upload-btn", children: [
771
- "Upload PDF",
772
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
773
- ] }),
774
- pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
775
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "upload-btn upload-btn-small", children: [
776
- "Change PDF",
777
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
778
- ] }),
779
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "field-type-selector", children: ["text", "signature", "signed-date", "checkbox", "initials"].map((type) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
780
- "button",
876
+ const headerButtons = pages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
877
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "header-btn header-btn-outline", children: [
878
+ "Change PDF",
879
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
880
+ ] }),
881
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { onClick: handleExport, className: "header-btn header-btn-primary", children: "Export Template" })
882
+ ] }) : null;
883
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "designer-layout", children: [
884
+ headerPortalRef?.current && headerButtons && (0, import_react_dom.createPortal)(headerButtons, headerPortalRef.current),
885
+ !hideHeader && !headerPortalRef && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "designer-header", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "designer-header-right", children: headerButtons }) }),
886
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "designer-body", children: [
887
+ pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "designer-palette", children: [
888
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "palette-heading", children: "New Field Role" }),
889
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "palette-role-section", children: [
890
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
891
+ "select",
781
892
  {
782
- className: `field-type-btn ${activeFieldType === type ? "active" : ""}`,
783
- onClick: () => setActiveFieldType(type),
784
- children: type === "text" ? "Text" : type === "signature" ? "Signature" : type === "signed-date" ? "Date" : type === "checkbox" ? "Checkbox" : "Initials"
785
- },
786
- type
787
- )) })
788
- ] })
893
+ className: "palette-role-select",
894
+ value: activeRole,
895
+ onChange: (e) => setActiveRole(e.target.value),
896
+ style: { borderColor: getSignerColor(activeRole), color: getSignerColor(activeRole) },
897
+ children: signerRoles.map((role) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: role, children: role }, role))
898
+ }
899
+ ),
900
+ isAddingRole ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "palette-role-add-inline", children: [
901
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
902
+ "input",
903
+ {
904
+ type: "text",
905
+ value: newRoleName,
906
+ onChange: (e) => setNewRoleName(e.target.value),
907
+ onKeyDown: (e) => e.key === "Enter" && handleAddRole(),
908
+ placeholder: "Role name",
909
+ autoFocus: true
910
+ }
911
+ ),
912
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { onClick: handleAddRole, children: "Add" }),
913
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { onClick: () => {
914
+ setIsAddingRole(false);
915
+ setNewRoleName("");
916
+ }, children: "Cancel" })
917
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "palette-role-add-link", onClick: () => setIsAddingRole(true), children: "+ Add Role" })
918
+ ] }),
919
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "palette-divider" }),
920
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "palette-heading", children: "Fields" }),
921
+ FIELD_TYPE_META.filter((f) => !f.group).map(({ type, label, icon }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
922
+ "div",
923
+ {
924
+ className: `palette-item ${activeFieldType === type ? "active" : ""}`,
925
+ draggable: true,
926
+ onClick: () => setActiveFieldType(type),
927
+ onDragStart: (e) => handlePaletteDragStart(e, type),
928
+ onDragEnd: handlePaletteDragEnd,
929
+ children: [
930
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "palette-item-icon", children: icon }),
931
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "palette-item-label", children: label })
932
+ ]
933
+ },
934
+ type
935
+ )),
936
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "palette-divider" }),
937
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "palette-heading", children: "Redact" }),
938
+ FIELD_TYPE_META.filter((f) => f.group === "redact").map(({ type, label, icon }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
939
+ "div",
940
+ {
941
+ className: `palette-item ${activeFieldType === type ? "active" : ""}`,
942
+ draggable: true,
943
+ onClick: () => setActiveFieldType(type),
944
+ onDragStart: (e) => handlePaletteDragStart(e, type),
945
+ onDragEnd: handlePaletteDragEnd,
946
+ children: [
947
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "palette-item-icon", children: icon }),
948
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "palette-item-label", children: label })
949
+ ]
950
+ },
951
+ type
952
+ ))
789
953
  ] }),
790
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "toolbar-right", children: pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { onClick: handleExport, className: "export-btn", children: "Export Template" }) })
791
- ] }),
792
- pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
793
- SignerRoleSelector,
794
- {
795
- roles: signerRoles,
796
- activeRole,
797
- onSetActiveRole: setActiveRole,
798
- onAddRole: handleAddRole,
799
- onRemoveRole: handleRemoveRole
800
- }
801
- ),
802
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "designer-content", children: [
803
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "designer-pdf-area", children: [
804
- loading && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "loading-indicator", children: "Loading PDF..." }),
805
- !pages.length && !loading && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "empty-state", children: [
806
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { children: "Upload a PDF to get started" }),
807
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: "Upload a template PDF, then click on the page to place form fields." }),
808
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "upload-btn upload-btn-large", children: [
809
- "Choose PDF File",
810
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
954
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "designer-pdf-area", children: [
955
+ loading && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "loading-indicator", children: "Loading PDF..." }),
956
+ !pages.length && !loading && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "empty-state", children: [
957
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { children: "Open a PDF to get started" }),
958
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { children: "Select a PDF from your device to begin. Your file stays on your computer \u2014 nothing is uploaded." }),
959
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "upload-btn upload-btn-large", children: [
960
+ "Select PDF",
961
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
811
962
  ] })
812
963
  ] }),
813
- pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
964
+ pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
814
965
  PdfViewer,
815
966
  {
816
967
  pages,
817
968
  fields,
818
969
  selectedFieldId,
819
- onSelectField: setSelectedFieldId,
970
+ onSelectField: (id) => {
971
+ setSelectedFieldId(id);
972
+ if (id) setRightTab("properties");
973
+ },
820
974
  onFieldMove: handleFieldMove,
821
975
  onFieldResize: handleFieldResize,
822
976
  onPageClick: handlePageClick,
977
+ onDropField: handleDropOnPage,
823
978
  mode: "designer",
824
979
  renderFieldContent
825
980
  }
826
981
  )
827
982
  ] }),
828
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "designer-panel", children: [
829
- selectedField ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
830
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
831
- FieldPropertyPanel,
832
- {
833
- field: selectedField,
834
- signerRoles,
835
- onUpdate: handleFieldUpdate,
836
- onDelete: handleFieldDelete
837
- }
838
- ),
839
- selectedField.assignee === activeRole && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "prefill-section", children: [
840
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h4", { children: "Pre-fill Value" }),
841
- selectedField.type === "signature" || selectedField.type === "initials" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
842
- SignatureCanvas,
983
+ pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
984
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "panel-resize-handle", onMouseDown: handleResizeStart }),
985
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "designer-panel", style: { width: panelWidth }, children: [
986
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "panel-tabs", children: [
987
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
988
+ "button",
843
989
  {
844
- width: 300,
845
- height: selectedField.type === "initials" ? 100 : 150,
846
- onSign: (dataUrl) => handleFieldUpdate(selectedField.id, { value: dataUrl }),
847
- initialValue: selectedField.value
990
+ className: `panel-tab ${rightTab === "properties" ? "active" : ""}`,
991
+ onClick: () => setRightTab("properties"),
992
+ children: "Properties"
848
993
  }
849
- ) : selectedField.type === "checkbox" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "panel-checkbox-label", children: [
850
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
851
- "input",
852
- {
853
- type: "checkbox",
854
- checked: selectedField.value === "true",
855
- onChange: (e) => handleFieldUpdate(selectedField.id, { value: e.target.checked ? "true" : "" })
856
- }
857
- ),
858
- "Checked"
859
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
860
- "input",
994
+ ),
995
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
996
+ "button",
861
997
  {
862
- type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
863
- value: selectedField.value,
864
- onChange: (e) => handleFieldUpdate(selectedField.id, { value: e.target.value }),
865
- placeholder: `Pre-fill ${selectedField.label}`,
866
- className: "prefill-input"
998
+ className: `panel-tab ${rightTab === "fields" ? "active" : ""}`,
999
+ onClick: () => setRightTab("fields"),
1000
+ children: [
1001
+ "All Fields",
1002
+ fields.length > 0 ? ` (${fields.length})` : ""
1003
+ ]
867
1004
  }
868
1005
  )
869
- ] })
870
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("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." }),
871
- fields.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "field-list", children: [
872
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("h4", { children: [
873
- "All Fields (",
874
- fields.length,
875
- ")"
876
1006
  ] }),
877
- fields.map((f, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
878
- "div",
879
- {
880
- className: `field-list-item ${f.id === selectedFieldId ? "active" : ""}`,
881
- onClick: () => setSelectedFieldId(f.id),
882
- children: [
883
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
884
- "span",
1007
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "panel-tab-content", children: [
1008
+ rightTab === "properties" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: selectedField ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
1009
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1010
+ FieldPropertyPanel,
1011
+ {
1012
+ field: selectedField,
1013
+ signerRoles,
1014
+ onUpdate: handleFieldUpdate,
1015
+ onDelete: handleFieldDelete
1016
+ }
1017
+ ),
1018
+ selectedField.type !== "blackout" && selectedField.type !== "whiteout" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "prefill-section", children: [
1019
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h4", { children: "Pre-fill Value" }),
1020
+ selectedField.type === "signature" || selectedField.type === "initials" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1021
+ SignatureCanvas,
885
1022
  {
886
- className: "field-list-dot",
887
- style: { backgroundColor: getSignerColor(f.assignee) }
1023
+ width: panelWidth - 40,
1024
+ height: selectedField.type === "initials" ? 100 : 150,
1025
+ onSign: (dataUrl) => handleFieldUpdate(selectedField.id, { value: dataUrl }),
1026
+ initialValue: selectedField.value,
1027
+ inkColor: selectedField.inkColor
888
1028
  }
889
- ),
890
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "field-list-name", children: f.label }),
891
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "field-list-page", children: [
892
- "p",
893
- f.page + 1
894
- ] }),
895
- f.required && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "field-list-required", children: "*" })
896
- ]
897
- },
898
- f.id
899
- ))
1029
+ ) : selectedField.type === "checkbox" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "panel-checkbox-label", children: [
1030
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1031
+ "input",
1032
+ {
1033
+ type: "checkbox",
1034
+ checked: selectedField.value === "true",
1035
+ onChange: (e) => handleFieldUpdate(selectedField.id, { value: e.target.checked ? "true" : "" })
1036
+ }
1037
+ ),
1038
+ "Checked"
1039
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1040
+ "input",
1041
+ {
1042
+ type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
1043
+ value: selectedField.value,
1044
+ onChange: (e) => handleFieldUpdate(selectedField.id, { value: e.target.value }),
1045
+ placeholder: `Pre-fill ${selectedField.label}`,
1046
+ className: "prefill-input",
1047
+ style: { color: selectedField.inkColor || "#000000" }
1048
+ }
1049
+ )
1050
+ ] })
1051
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "panel-empty", children: "Click on the PDF to place a field, or select an existing field to edit its properties." }) }),
1052
+ rightTab === "fields" && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_jsx_runtime4.Fragment, { children: fields.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("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__ */ (0, import_jsx_runtime4.jsx)("div", { className: "field-list", children: sortedFields.map((f) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
1053
+ "div",
1054
+ {
1055
+ className: `field-list-item ${f.id === selectedFieldId ? "active" : ""}`,
1056
+ onClick: () => {
1057
+ setSelectedFieldId(f.id);
1058
+ setRightTab("properties");
1059
+ },
1060
+ children: [
1061
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1062
+ "span",
1063
+ {
1064
+ className: "field-list-dot",
1065
+ style: { backgroundColor: getSignerColor(f.assignee) }
1066
+ }
1067
+ ),
1068
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "field-list-name", children: f.label }),
1069
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "field-list-page", children: [
1070
+ "p",
1071
+ f.page + 1
1072
+ ] }),
1073
+ f.required && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "field-list-required", children: "*" })
1074
+ ]
1075
+ },
1076
+ f.id
1077
+ )) }) })
1078
+ ] }),
1079
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "powered-by", children: [
1080
+ "Powered by ",
1081
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
1082
+ ] })
900
1083
  ] })
901
1084
  ] })
902
- ] }),
903
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "powered-by", children: [
904
- "Powered by ",
905
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
906
1085
  ] })
907
1086
  ] });
908
1087
  }
909
1088
 
910
1089
  // src/components/pdf-builder/SignerView.tsx
911
- var import_react5 = require("react");
1090
+ var import_react4 = require("react");
912
1091
 
913
1092
  // src/utils/pdfFiller.ts
914
1093
  var import_pdf_lib = require("pdf-lib");
@@ -923,8 +1102,13 @@ async function generateFilledPdf(pdfSource, fields) {
923
1102
  const pdfDoc = await import_pdf_lib.PDFDocument.load(pdfBytes);
924
1103
  const font = await pdfDoc.embedFont(import_pdf_lib.StandardFonts.Helvetica);
925
1104
  const pages = pdfDoc.getPages();
1105
+ function hexToRgb(hex) {
1106
+ const r = parseInt(hex.slice(1, 3), 16) / 255;
1107
+ const g = parseInt(hex.slice(3, 5), 16) / 255;
1108
+ const b = parseInt(hex.slice(5, 7), 16) / 255;
1109
+ return (0, import_pdf_lib.rgb)(r, g, b);
1110
+ }
926
1111
  for (const field of fields) {
927
- if (!field.value) continue;
928
1112
  const page = pages[field.page];
929
1113
  if (!page) continue;
930
1114
  const pageWidth = page.getWidth();
@@ -933,6 +1117,18 @@ async function generateFilledPdf(pdfSource, fields) {
933
1117
  const y = pageHeight - field.y / 100 * pageHeight - field.height / 100 * pageHeight;
934
1118
  const w = field.width / 100 * pageWidth;
935
1119
  const h = field.height / 100 * pageHeight;
1120
+ if (field.type === "blackout" || field.type === "whiteout") {
1121
+ page.drawRectangle({
1122
+ x,
1123
+ y,
1124
+ width: w,
1125
+ height: h,
1126
+ color: field.type === "blackout" ? (0, import_pdf_lib.rgb)(0, 0, 0) : (0, import_pdf_lib.rgb)(1, 1, 1)
1127
+ });
1128
+ continue;
1129
+ }
1130
+ if (!field.value) continue;
1131
+ const inkColor = field.inkColor ? hexToRgb(field.inkColor) : (0, import_pdf_lib.rgb)(0, 0, 0);
936
1132
  if (field.type === "checkbox") {
937
1133
  if (field.value === "true") {
938
1134
  const inset = Math.min(w, h) * 0.2;
@@ -941,13 +1137,13 @@ async function generateFilledPdf(pdfSource, fields) {
941
1137
  start: { x: x + inset, y: y + inset },
942
1138
  end: { x: x + w - inset, y: y + h - inset },
943
1139
  thickness: lineWidth,
944
- color: (0, import_pdf_lib.rgb)(0, 0, 0)
1140
+ color: inkColor
945
1141
  });
946
1142
  page.drawLine({
947
1143
  start: { x: x + w - inset, y: y + inset },
948
1144
  end: { x: x + inset, y: y + h - inset },
949
1145
  thickness: lineWidth,
950
- color: (0, import_pdf_lib.rgb)(0, 0, 0)
1146
+ color: inkColor
951
1147
  });
952
1148
  }
953
1149
  } else if (field.type === "signature" || field.type === "initials") {
@@ -971,7 +1167,7 @@ async function generateFilledPdf(pdfSource, fields) {
971
1167
  y: y + h * 0.3,
972
1168
  size: fontSize,
973
1169
  font,
974
- color: (0, import_pdf_lib.rgb)(0, 0, 0),
1170
+ color: inkColor,
975
1171
  maxWidth: w - 4
976
1172
  });
977
1173
  }
@@ -998,7 +1194,7 @@ async function postPdfToCallback(bytes, callbackUrl, filename) {
998
1194
  }
999
1195
 
1000
1196
  // src/components/pdf-builder/FieldNavigator.tsx
1001
- var import_jsx_runtime6 = require("react/jsx-runtime");
1197
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1002
1198
  function FieldNavigator({
1003
1199
  fields,
1004
1200
  currentFieldId,
@@ -1024,15 +1220,15 @@ function FieldNavigator({
1024
1220
  if (f.type === "checkbox") return true;
1025
1221
  return !!f.value;
1026
1222
  }).length;
1027
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "field-navigator", children: [
1028
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "field-navigator-progress", children: [
1223
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "field-navigator", children: [
1224
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "field-navigator-progress", children: [
1029
1225
  filledCount,
1030
1226
  " of ",
1031
1227
  fields.length,
1032
1228
  " fields completed"
1033
1229
  ] }),
1034
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "field-navigator-controls", children: [
1035
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1230
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "field-navigator-controls", children: [
1231
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1036
1232
  "button",
1037
1233
  {
1038
1234
  onClick: handlePrev,
@@ -1041,8 +1237,8 @@ function FieldNavigator({
1041
1237
  children: "Prev"
1042
1238
  }
1043
1239
  ),
1044
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "field-navigator-position", children: currentIndex >= 0 ? `${currentIndex + 1} / ${fields.length}` : "-" }),
1045
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1240
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "field-navigator-position", children: currentIndex >= 0 ? `${currentIndex + 1} / ${fields.length}` : "-" }),
1241
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1046
1242
  "button",
1047
1243
  {
1048
1244
  onClick: handleNext,
@@ -1052,7 +1248,7 @@ function FieldNavigator({
1052
1248
  }
1053
1249
  )
1054
1250
  ] }),
1055
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1251
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1056
1252
  "button",
1057
1253
  {
1058
1254
  onClick: onSubmit,
@@ -1065,26 +1261,49 @@ function FieldNavigator({
1065
1261
  }
1066
1262
 
1067
1263
  // src/components/pdf-builder/SignerView.tsx
1068
- var import_jsx_runtime7 = require("react/jsx-runtime");
1264
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1069
1265
  function SignerView({
1266
+ apiKey,
1070
1267
  initialPdfUrl,
1071
1268
  initialTemplate,
1072
1269
  initialSigner,
1073
1270
  callbackUrl: initialCallbackUrl,
1074
- onComplete
1271
+ onComplete,
1272
+ initialValues
1075
1273
  } = {}) {
1076
- const [pages, setPages] = (0, import_react5.useState)([]);
1077
- const [fields, setFields] = (0, import_react5.useState)([]);
1078
- const [selectedFieldId, setSelectedFieldId] = (0, import_react5.useState)(null);
1079
- const [signer, setSigner] = (0, import_react5.useState)(initialSigner || "Signer 1");
1080
- const [loading, setLoading] = (0, import_react5.useState)(false);
1081
- const [submitting, setSubmitting] = (0, import_react5.useState)(false);
1082
- const [pdfSource, setPdfSource] = (0, import_react5.useState)(null);
1083
- const [callbackUrl, setCallbackUrl] = (0, import_react5.useState)(initialCallbackUrl || "");
1084
- const containerRef = (0, import_react5.useRef)(null);
1085
- (0, import_react5.useEffect)(() => {
1274
+ if (!isValidApiKey(apiKey)) {
1275
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "signer-layout", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "empty-state", children: [
1276
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { children: "Invalid API Key" }),
1277
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { children: [
1278
+ "A valid API key is required to use Exeq. Visit ",
1279
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("a", { href: "https://exeq.org", children: "exeq.org" }),
1280
+ " to get one."
1281
+ ] })
1282
+ ] }) });
1283
+ }
1284
+ const [pages, setPages] = (0, import_react4.useState)([]);
1285
+ const [fields, setFields] = (0, import_react4.useState)([]);
1286
+ const [selectedFieldId, setSelectedFieldId] = (0, import_react4.useState)(null);
1287
+ const [signer, setSigner] = (0, import_react4.useState)(initialSigner || "Signer 1");
1288
+ const [loading, setLoading] = (0, import_react4.useState)(false);
1289
+ const [submitting, setSubmitting] = (0, import_react4.useState)(false);
1290
+ const [pdfSource, setPdfSource] = (0, import_react4.useState)(null);
1291
+ const [callbackUrl, setCallbackUrl] = (0, import_react4.useState)(initialCallbackUrl || "");
1292
+ const containerRef = (0, import_react4.useRef)(null);
1293
+ (0, import_react4.useEffect)(() => {
1086
1294
  if (initialTemplate) {
1087
- setFields(initialTemplate.fields);
1295
+ let templateFields = initialTemplate.fields;
1296
+ if (initialValues) {
1297
+ templateFields = templateFields.map((f) => {
1298
+ const byLabel = Object.entries(initialValues).find(
1299
+ ([key]) => key.toLowerCase() === f.label.toLowerCase()
1300
+ );
1301
+ const byId = initialValues[f.id];
1302
+ const value = byLabel?.[1] ?? byId;
1303
+ return value !== void 0 ? { ...f, value } : f;
1304
+ });
1305
+ }
1306
+ setFields(templateFields);
1088
1307
  if (initialPdfUrl) loadPdf(initialPdfUrl);
1089
1308
  return;
1090
1309
  }
@@ -1110,7 +1329,7 @@ function SignerView({
1110
1329
  window.addEventListener("message", handleMessage);
1111
1330
  return () => window.removeEventListener("message", handleMessage);
1112
1331
  }, [initialTemplate, initialPdfUrl]);
1113
- const loadPdf = (0, import_react5.useCallback)(async (source) => {
1332
+ const loadPdf = (0, import_react4.useCallback)(async (source) => {
1114
1333
  setLoading(true);
1115
1334
  try {
1116
1335
  const rendered = await renderPdfPages(source);
@@ -1122,13 +1341,18 @@ function SignerView({
1122
1341
  setLoading(false);
1123
1342
  }
1124
1343
  }, []);
1125
- const editableFields = fields.filter((f) => f.assignee === signer);
1344
+ const editableFields = fields.filter((f) => f.assignee === signer).sort((a, b) => {
1345
+ if (a.page !== b.page) return a.page - b.page;
1346
+ const bandThreshold = 2;
1347
+ if (Math.abs(a.y - b.y) > bandThreshold) return a.y - b.y;
1348
+ return a.x - b.x;
1349
+ });
1126
1350
  const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
1127
1351
  const isFieldEditable = selectedField ? selectedField.assignee === signer : false;
1128
- const handleFieldUpdate = (0, import_react5.useCallback)((id, value) => {
1352
+ const handleFieldUpdate = (0, import_react4.useCallback)((id, value) => {
1129
1353
  setFields((prev) => prev.map((f) => f.id === id ? { ...f, value } : f));
1130
1354
  }, []);
1131
- const handleNavigate = (0, import_react5.useCallback)((fieldId) => {
1355
+ const handleNavigate = (0, import_react4.useCallback)((fieldId) => {
1132
1356
  setSelectedFieldId(fieldId);
1133
1357
  const field = fields.find((f) => f.id === fieldId);
1134
1358
  if (field && containerRef.current) {
@@ -1143,7 +1367,7 @@ function SignerView({
1143
1367
  if (f.type === "checkbox") return true;
1144
1368
  return !!f.value;
1145
1369
  });
1146
- const handleSubmit = (0, import_react5.useCallback)(async () => {
1370
+ const handleSubmit = (0, import_react4.useCallback)(async () => {
1147
1371
  if (!pdfSource || !allRequiredFilled) return;
1148
1372
  setSubmitting(true);
1149
1373
  try {
@@ -1163,25 +1387,25 @@ function SignerView({
1163
1387
  setSubmitting(false);
1164
1388
  }
1165
1389
  }, [pdfSource, fields, callbackUrl, allRequiredFilled, onComplete]);
1166
- const renderFieldContent = (0, import_react5.useCallback)((field) => {
1390
+ const renderFieldContent = (0, import_react4.useCallback)((field) => {
1167
1391
  const editable = field.assignee === signer;
1168
1392
  if (!editable) {
1169
1393
  if (field.type === "signature" || field.type === "initials") {
1170
- return field.value ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("img", { src: field.value, alt: field.label, className: "field-signature-preview" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "field-overlay-placeholder readonly", children: field.placeholder });
1394
+ return field.value ? /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("img", { src: field.value, alt: field.label, className: "field-signature-preview" }) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "field-overlay-placeholder readonly", children: field.placeholder });
1171
1395
  }
1172
1396
  if (field.type === "checkbox") {
1173
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "field-checkbox-display readonly", children: field.value === "true" ? "\u2713" : "" });
1397
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "field-checkbox-display readonly", children: field.value === "true" ? "\u2713" : "" });
1174
1398
  }
1175
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "field-overlay-placeholder readonly", children: field.value || field.placeholder });
1399
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "field-overlay-placeholder readonly", children: field.value || field.placeholder });
1176
1400
  }
1177
1401
  if (field.type === "signature" || field.type === "initials") {
1178
1402
  if (field.value) {
1179
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "field-signature-filled", onClick: () => handleFieldUpdate(field.id, ""), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("img", { src: field.value, alt: field.label, className: "field-signature-preview" }) });
1403
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "field-signature-filled", onClick: () => handleFieldUpdate(field.id, ""), children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("img", { src: field.value, alt: field.label, className: "field-signature-preview" }) });
1180
1404
  }
1181
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "field-overlay-placeholder editable", children: field.placeholder });
1405
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "field-overlay-placeholder editable", children: field.placeholder });
1182
1406
  }
1183
1407
  if (field.type === "checkbox") {
1184
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1408
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1185
1409
  "div",
1186
1410
  {
1187
1411
  className: "field-checkbox-display editable",
@@ -1194,9 +1418,9 @@ function SignerView({
1194
1418
  );
1195
1419
  }
1196
1420
  if (field.type === "signed-date") {
1197
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "field-overlay-value", children: field.value || (/* @__PURE__ */ new Date()).toLocaleDateString() });
1421
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "field-overlay-value", children: field.value || (/* @__PURE__ */ new Date()).toLocaleDateString() });
1198
1422
  }
1199
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1423
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1200
1424
  "input",
1201
1425
  {
1202
1426
  type: field.textSubtype === "email" ? "email" : field.textSubtype === "number" ? "number" : field.textSubtype === "phone" ? "tel" : field.textSubtype === "date" ? "date" : "text",
@@ -1209,7 +1433,7 @@ function SignerView({
1209
1433
  }
1210
1434
  );
1211
1435
  }, [signer, handleFieldUpdate]);
1212
- (0, import_react5.useEffect)(() => {
1436
+ (0, import_react4.useEffect)(() => {
1213
1437
  const sigFields = fields.filter((f) => f.assignee === signer && f.type === "signature" && f.value);
1214
1438
  if (sigFields.length > 0) {
1215
1439
  const dateStr = (/* @__PURE__ */ new Date()).toLocaleDateString();
@@ -1221,10 +1445,10 @@ function SignerView({
1221
1445
  }));
1222
1446
  }
1223
1447
  }, [fields.filter((f) => f.type === "signature" && f.value).length]);
1224
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "signer-layout", ref: containerRef, children: [
1225
- loading && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "loading-indicator", children: "Loading document..." }),
1226
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "signer-content", children: [
1227
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "signer-pdf-area", children: pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1448
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "signer-layout", ref: containerRef, children: [
1449
+ loading && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "loading-indicator", children: "Loading document..." }),
1450
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "signer-content", children: [
1451
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "signer-pdf-area", children: pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1228
1452
  PdfViewer,
1229
1453
  {
1230
1454
  pages,
@@ -1236,11 +1460,11 @@ function SignerView({
1236
1460
  renderFieldContent
1237
1461
  }
1238
1462
  ) }),
1239
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "signer-panel", children: [
1240
- selectedField && isFieldEditable && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "signer-field-input", children: [
1241
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { children: selectedField.label }),
1242
- selectedField.required && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "required-badge", children: "Required" }),
1243
- (selectedField.type === "signature" || selectedField.type === "initials") && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1463
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "signer-panel", children: [
1464
+ selectedField && isFieldEditable && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "signer-field-input", children: [
1465
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { children: selectedField.label }),
1466
+ selectedField.required && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "required-badge", children: "Required" }),
1467
+ (selectedField.type === "signature" || selectedField.type === "initials") && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1244
1468
  SignatureCanvas,
1245
1469
  {
1246
1470
  width: 280,
@@ -1249,7 +1473,7 @@ function SignerView({
1249
1473
  initialValue: selectedField.value
1250
1474
  }
1251
1475
  ),
1252
- selectedField.type === "text" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1476
+ selectedField.type === "text" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1253
1477
  "input",
1254
1478
  {
1255
1479
  type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
@@ -1259,8 +1483,8 @@ function SignerView({
1259
1483
  className: "signer-text-input"
1260
1484
  }
1261
1485
  ),
1262
- selectedField.type === "checkbox" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "signer-checkbox-label", children: [
1263
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1486
+ selectedField.type === "checkbox" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "signer-checkbox-label", children: [
1487
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1264
1488
  "input",
1265
1489
  {
1266
1490
  type: "checkbox",
@@ -1270,14 +1494,14 @@ function SignerView({
1270
1494
  ),
1271
1495
  selectedField.placeholder || "Check this box"
1272
1496
  ] }),
1273
- selectedField.type === "signed-date" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "signer-date-display", children: selectedField.value || "Will auto-fill when you sign" })
1497
+ selectedField.type === "signed-date" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "signer-date-display", children: selectedField.value || "Will auto-fill when you sign" })
1274
1498
  ] }),
1275
- selectedField && !isFieldEditable && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "signer-field-readonly", children: [
1276
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { children: selectedField.label }),
1277
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { children: "This field belongs to another signer." })
1499
+ selectedField && !isFieldEditable && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "signer-field-readonly", children: [
1500
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { children: selectedField.label }),
1501
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: "This field belongs to another signer." })
1278
1502
  ] }),
1279
- !selectedField && editableFields.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "panel-empty", children: "Select a field to fill it in, or use the navigation below." }),
1280
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1503
+ !selectedField && editableFields.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "panel-empty", children: "Select a field to fill it in, or use the navigation below." }),
1504
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1281
1505
  FieldNavigator,
1282
1506
  {
1283
1507
  fields: editableFields,
@@ -1287,12 +1511,87 @@ function SignerView({
1287
1511
  onSubmit: handleSubmit
1288
1512
  }
1289
1513
  ),
1290
- submitting && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "loading-indicator", children: "Generating PDF..." })
1514
+ submitting && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "loading-indicator", children: "Generating PDF..." })
1291
1515
  ] })
1292
1516
  ] }),
1293
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "powered-by", children: [
1517
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "powered-by", children: [
1294
1518
  "Powered by ",
1295
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
1519
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
1520
+ ] })
1521
+ ] });
1522
+ }
1523
+
1524
+ // src/components/pdf-builder/SignerRoleSelector.tsx
1525
+ var import_react5 = require("react");
1526
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1527
+ function SignerRoleSelector({
1528
+ roles,
1529
+ activeRole,
1530
+ onSetActiveRole,
1531
+ onAddRole,
1532
+ onRemoveRole
1533
+ }) {
1534
+ const [isAdding, setIsAdding] = (0, import_react5.useState)(false);
1535
+ const [newRoleName, setNewRoleName] = (0, import_react5.useState)("");
1536
+ const handleAdd = () => {
1537
+ if (newRoleName.trim()) {
1538
+ onAddRole(newRoleName.trim());
1539
+ setNewRoleName("");
1540
+ setIsAdding(false);
1541
+ }
1542
+ };
1543
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "signer-role-selector", children: [
1544
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "signer-role-label", children: "Signer Roles" }),
1545
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "signer-role-list", children: [
1546
+ roles.map((role) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1547
+ "button",
1548
+ {
1549
+ className: `signer-role-chip ${role === activeRole ? "active" : ""}`,
1550
+ style: {
1551
+ borderColor: getSignerColor(role),
1552
+ backgroundColor: role === activeRole ? getSignerColor(role) : "transparent",
1553
+ color: role === activeRole ? "#fff" : getSignerColor(role)
1554
+ },
1555
+ onClick: () => onSetActiveRole(role),
1556
+ children: [
1557
+ role,
1558
+ roles.length > 1 && role !== "Sender" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1559
+ "span",
1560
+ {
1561
+ className: "signer-role-remove",
1562
+ onClick: (e) => {
1563
+ e.stopPropagation();
1564
+ onRemoveRole(role);
1565
+ },
1566
+ children: "\xD7"
1567
+ }
1568
+ )
1569
+ ]
1570
+ },
1571
+ role
1572
+ )),
1573
+ isAdding ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "signer-role-add-input", children: [
1574
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1575
+ "input",
1576
+ {
1577
+ type: "text",
1578
+ value: newRoleName,
1579
+ onChange: (e) => setNewRoleName(e.target.value),
1580
+ onKeyDown: (e) => e.key === "Enter" && handleAdd(),
1581
+ placeholder: "Role name",
1582
+ autoFocus: true
1583
+ }
1584
+ ),
1585
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { onClick: handleAdd, children: "Add" }),
1586
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { onClick: () => setIsAdding(false), children: "Cancel" })
1587
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1588
+ "button",
1589
+ {
1590
+ className: "signer-role-add-btn",
1591
+ onClick: () => setIsAdding(true),
1592
+ children: "+ Add Role"
1593
+ }
1594
+ )
1296
1595
  ] })
1297
1596
  ] });
1298
1597
  }
@@ -1313,6 +1612,7 @@ function SignerView({
1313
1612
  generateFilledPdf,
1314
1613
  getSignerColor,
1315
1614
  postPdfToCallback,
1316
- renderPdfPages
1615
+ renderPdfPages,
1616
+ uniqueLabel
1317
1617
  });
1318
1618
  //# sourceMappingURL=index.js.map