@unlev/exeq 0.1.2 → 0.1.4

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,68 @@ 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 = new Set([
633
+ "exeq_live_2024",
634
+ process.env.NEXT_PUBLIC_EXEQ_API_KEY
635
+ ].filter(Boolean));
636
+ function isValidApiKey(key) {
637
+ if (!key) return false;
638
+ return VALID_KEYS.has(key);
639
+ }
640
+
663
641
  // src/components/pdf-builder/DesignerView.tsx
664
- var import_jsx_runtime5 = require("react/jsx-runtime");
642
+ var import_jsx_runtime4 = require("react/jsx-runtime");
643
+ var FIELD_TYPE_META = [
644
+ { type: "text", label: "Text", icon: "T" },
645
+ { type: "signature", label: "Signature", icon: "\u270D" },
646
+ { type: "signed-date", label: "Date", icon: "\u{1F4C5}" },
647
+ { type: "checkbox", label: "Checkbox", icon: "\u2611" },
648
+ { type: "initials", label: "Initials", icon: "IN" },
649
+ { type: "blackout", label: "Blackout", icon: "\u25A0", group: "redact" },
650
+ { type: "whiteout", label: "Whiteout", icon: "\u25A1", group: "redact" }
651
+ ];
665
652
  function DesignerView({
653
+ apiKey,
666
654
  initialPdfUrl,
667
655
  initialTemplate,
668
- onSave
656
+ onSave,
657
+ hideHeader,
658
+ headerPortalRef
669
659
  } = {}) {
670
- const [pages, setPages] = (0, import_react4.useState)([]);
671
- const [fields, setFields] = (0, import_react4.useState)(initialTemplate?.fields ?? []);
672
- const [selectedFieldId, setSelectedFieldId] = (0, import_react4.useState)(null);
673
- const [signerRoles, setSignerRoles] = (0, import_react4.useState)(initialTemplate?.signerRoles ?? [...DEFAULT_SIGNER_ROLES]);
674
- const [activeRole, setActiveRole] = (0, import_react4.useState)("Sender");
675
- const [activeFieldType, setActiveFieldType] = (0, import_react4.useState)("text");
676
- const [loading, setLoading] = (0, import_react4.useState)(false);
677
- const [pdfSource, setPdfSource] = (0, import_react4.useState)(null);
678
- (0, import_react4.useEffect)(() => {
660
+ if (!isValidApiKey(apiKey)) {
661
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "designer-layout", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "empty-state", children: [
662
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { children: "Invalid API Key" }),
663
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("p", { children: [
664
+ "A valid API key is required to use Exeq. Visit ",
665
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("a", { href: "https://exeq.org", children: "exeq.org" }),
666
+ " to get one."
667
+ ] })
668
+ ] }) });
669
+ }
670
+ const [pages, setPages] = (0, import_react3.useState)([]);
671
+ const [fields, setFields] = (0, import_react3.useState)(initialTemplate?.fields ?? []);
672
+ const [selectedFieldId, setSelectedFieldId] = (0, import_react3.useState)(null);
673
+ const [signerRoles, setSignerRoles] = (0, import_react3.useState)(initialTemplate?.signerRoles ?? [...DEFAULT_SIGNER_ROLES]);
674
+ const [activeRole, setActiveRole] = (0, import_react3.useState)("Sender");
675
+ const [activeFieldType, setActiveFieldType] = (0, import_react3.useState)("text");
676
+ const [loading, setLoading] = (0, import_react3.useState)(false);
677
+ const [pdfSource, setPdfSource] = (0, import_react3.useState)(null);
678
+ const [rightTab, setRightTab] = (0, import_react3.useState)("properties");
679
+ const [isAddingRole, setIsAddingRole] = (0, import_react3.useState)(false);
680
+ const [newRoleName, setNewRoleName] = (0, import_react3.useState)("");
681
+ const [draggingFieldType, setDraggingFieldType] = (0, import_react3.useState)(null);
682
+ const [panelWidth, setPanelWidth] = (0, import_react3.useState)(380);
683
+ const dragGhostRef = (0, import_react3.useRef)(null);
684
+ const resizingRef = (0, import_react3.useRef)(false);
685
+ (0, import_react3.useEffect)(() => {
679
686
  const pdfUrl = initialPdfUrl || initialTemplate?.pdfUrl;
680
687
  if (pdfUrl) {
681
688
  loadPdf(pdfUrl);
@@ -699,7 +706,7 @@ function DesignerView({
699
706
  window.addEventListener("message", handleMessage);
700
707
  return () => window.removeEventListener("message", handleMessage);
701
708
  }, [initialPdfUrl, initialTemplate]);
702
- const loadPdf = (0, import_react4.useCallback)(async (source) => {
709
+ const loadPdf = (0, import_react3.useCallback)(async (source) => {
703
710
  setLoading(true);
704
711
  try {
705
712
  const rendered = await renderPdfPages(source);
@@ -711,7 +718,7 @@ function DesignerView({
711
718
  setLoading(false);
712
719
  }
713
720
  }, []);
714
- const handleFileUpload = (0, import_react4.useCallback)((e) => {
721
+ const handleFileUpload = (0, import_react3.useCallback)((e) => {
715
722
  const file = e.target.files?.[0];
716
723
  if (!file) return;
717
724
  const reader = new FileReader();
@@ -720,35 +727,45 @@ function DesignerView({
720
727
  };
721
728
  reader.readAsArrayBuffer(file);
722
729
  }, [loadPdf]);
723
- const handlePageClick = (0, import_react4.useCallback)((page, x, y) => {
724
- const field = createField(activeFieldType, activeRole, page, x, y);
730
+ const handlePageClick = (0, import_react3.useCallback)((page, x, y) => {
731
+ const field = createField(activeFieldType, activeRole, page, x, y, fields);
725
732
  setFields((prev) => [...prev, field]);
726
733
  setSelectedFieldId(field.id);
727
- }, [activeFieldType, activeRole]);
728
- const handleFieldMove = (0, import_react4.useCallback)((id, page, x, y) => {
734
+ setRightTab("properties");
735
+ }, [activeFieldType, activeRole, fields]);
736
+ const handleFieldMove = (0, import_react3.useCallback)((id, page, x, y) => {
729
737
  setFields((prev) => prev.map((f) => f.id === id ? { ...f, page, x, y } : f));
730
738
  }, []);
731
- const handleFieldResize = (0, import_react4.useCallback)((id, width, height) => {
739
+ const handleFieldResize = (0, import_react3.useCallback)((id, width, height) => {
732
740
  setFields((prev) => prev.map((f) => f.id === id ? { ...f, width, height } : f));
733
741
  }, []);
734
- const handleFieldUpdate = (0, import_react4.useCallback)((id, updates) => {
735
- setFields((prev) => prev.map((f) => f.id === id ? { ...f, ...updates } : f));
742
+ const handleFieldUpdate = (0, import_react3.useCallback)((id, updates) => {
743
+ setFields((prev) => {
744
+ if (updates.label !== void 0) {
745
+ const otherLabels = prev.filter((f) => f.id !== id).map((f) => f.label);
746
+ updates.label = uniqueLabel(updates.label, otherLabels);
747
+ }
748
+ return prev.map((f) => f.id === id ? { ...f, ...updates } : f);
749
+ });
736
750
  }, []);
737
- const handleFieldDelete = (0, import_react4.useCallback)((id) => {
751
+ const handleFieldDelete = (0, import_react3.useCallback)((id) => {
738
752
  setFields((prev) => prev.filter((f) => f.id !== id));
739
753
  if (selectedFieldId === id) setSelectedFieldId(null);
740
754
  }, [selectedFieldId]);
741
- const handleAddRole = (0, import_react4.useCallback)((role) => {
742
- if (!signerRoles.includes(role)) {
743
- setSignerRoles((prev) => [...prev, role]);
755
+ const handleAddRole = (0, import_react3.useCallback)(() => {
756
+ const name = newRoleName.trim();
757
+ if (name && !signerRoles.includes(name)) {
758
+ setSignerRoles((prev) => [...prev, name]);
744
759
  }
745
- }, [signerRoles]);
746
- const handleRemoveRole = (0, import_react4.useCallback)((role) => {
760
+ setNewRoleName("");
761
+ setIsAddingRole(false);
762
+ }, [newRoleName, signerRoles]);
763
+ const handleRemoveRole = (0, import_react3.useCallback)((role) => {
747
764
  setSignerRoles((prev) => prev.filter((r) => r !== role));
748
765
  setFields((prev) => prev.map((f) => f.assignee === role ? { ...f, assignee: signerRoles[0] } : f));
749
766
  if (activeRole === role) setActiveRole(signerRoles[0]);
750
767
  }, [signerRoles, activeRole]);
751
- const handleExport = (0, import_react4.useCallback)(() => {
768
+ const handleExport = (0, import_react3.useCallback)(() => {
752
769
  const template = {
753
770
  fields,
754
771
  signerRoles,
@@ -768,160 +785,310 @@ function DesignerView({
768
785
  }
769
786
  window.parent?.postMessage({ type: "template-saved", template }, "*");
770
787
  }, [fields, signerRoles, pdfSource, onSave]);
788
+ const handlePaletteDragStart = (0, import_react3.useCallback)((e, type) => {
789
+ setDraggingFieldType(type);
790
+ e.dataTransfer.setData("application/exeq-field-type", type);
791
+ e.dataTransfer.effectAllowed = "copy";
792
+ const ghost = document.createElement("div");
793
+ ghost.className = "palette-drag-ghost";
794
+ ghost.textContent = FIELD_TYPE_META.find((f) => f.type === type)?.label || type;
795
+ ghost.style.cssText = `
796
+ position: fixed; top: -200px; left: -200px;
797
+ padding: 6px 16px; background: ${getSignerColor(activeRole)};
798
+ color: #fff; border-radius: 4px; font-size: 12px;
799
+ font-family: system-ui; pointer-events: none; white-space: nowrap;
800
+ `;
801
+ document.body.appendChild(ghost);
802
+ e.dataTransfer.setDragImage(ghost, 40, 16);
803
+ dragGhostRef.current = ghost;
804
+ }, [activeRole]);
805
+ const handlePaletteDragEnd = (0, import_react3.useCallback)(() => {
806
+ setDraggingFieldType(null);
807
+ if (dragGhostRef.current) {
808
+ document.body.removeChild(dragGhostRef.current);
809
+ dragGhostRef.current = null;
810
+ }
811
+ }, []);
812
+ const handleDropOnPage = (0, import_react3.useCallback)((page, x, y, fieldType) => {
813
+ const field = createField(fieldType, activeRole, page, x, y, fields);
814
+ setFields((prev) => [...prev, field]);
815
+ setSelectedFieldId(field.id);
816
+ setRightTab("properties");
817
+ }, [activeRole, fields]);
818
+ (0, import_react3.useEffect)(() => {
819
+ const handleKeyDown = (e) => {
820
+ if (e.key !== "Delete" && e.key !== "Backspace") return;
821
+ if (!selectedFieldId) return;
822
+ const tag = (document.activeElement?.tagName || "").toLowerCase();
823
+ if (tag === "input" || tag === "textarea" || tag === "select") return;
824
+ if (document.activeElement?.isContentEditable) return;
825
+ e.preventDefault();
826
+ if (window.confirm("Delete this field?")) {
827
+ handleFieldDelete(selectedFieldId);
828
+ }
829
+ };
830
+ window.addEventListener("keydown", handleKeyDown);
831
+ return () => window.removeEventListener("keydown", handleKeyDown);
832
+ }, [selectedFieldId, handleFieldDelete]);
833
+ const handleResizeStart = (0, import_react3.useCallback)((e) => {
834
+ e.preventDefault();
835
+ resizingRef.current = true;
836
+ const startX = e.clientX;
837
+ const startWidth = panelWidth;
838
+ const handleMouseMove = (e2) => {
839
+ if (!resizingRef.current) return;
840
+ const delta = startX - e2.clientX;
841
+ setPanelWidth(Math.max(280, Math.min(600, startWidth + delta)));
842
+ };
843
+ const handleMouseUp = () => {
844
+ resizingRef.current = false;
845
+ window.removeEventListener("mousemove", handleMouseMove);
846
+ window.removeEventListener("mouseup", handleMouseUp);
847
+ };
848
+ window.addEventListener("mousemove", handleMouseMove);
849
+ window.addEventListener("mouseup", handleMouseUp);
850
+ }, [panelWidth]);
851
+ const sortedFields = [...fields].sort((a, b) => {
852
+ if (a.page !== b.page) return a.page - b.page;
853
+ const bandThreshold = 2;
854
+ if (Math.abs(a.y - b.y) > bandThreshold) return a.y - b.y;
855
+ return a.x - b.x;
856
+ });
771
857
  const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
772
- const renderFieldContent = (0, import_react4.useCallback)((field) => {
858
+ const renderFieldContent = (0, import_react3.useCallback)((field) => {
859
+ if (field.type === "blackout" || field.type === "whiteout") {
860
+ return null;
861
+ }
773
862
  if (field.type === "signature" || field.type === "initials") {
774
863
  if (field.value) {
775
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("img", { src: field.value, alt: field.label, className: "field-signature-preview" });
864
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { src: field.value, alt: field.label, className: "field-signature-preview" });
776
865
  }
777
866
  }
778
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "field-overlay-placeholder", children: field.value || field.placeholder });
867
+ const inkColor = field.inkColor || "#000000";
868
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
869
+ "div",
870
+ {
871
+ className: "field-overlay-placeholder",
872
+ style: { color: field.value ? inkColor : void 0, fontSize: `${field.fontSize * 0.6}px` },
873
+ children: field.value || field.placeholder
874
+ }
875
+ );
779
876
  }, []);
780
- return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "designer-layout", children: [
781
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "designer-toolbar", children: [
782
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "toolbar-left", children: [
783
- !pages.length && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "upload-btn", children: [
784
- "Upload PDF",
785
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
786
- ] }),
787
- pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
788
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "upload-btn upload-btn-small", children: [
789
- "Change PDF",
790
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
791
- ] }),
792
- /* @__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)(
793
- "button",
877
+ const headerButtons = pages.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
878
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "header-btn header-btn-outline", children: [
879
+ "Change PDF",
880
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
881
+ ] }),
882
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { onClick: handleExport, className: "header-btn header-btn-primary", children: "Export Template" })
883
+ ] }) : null;
884
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "designer-layout", children: [
885
+ headerPortalRef?.current && headerButtons && (0, import_react_dom.createPortal)(headerButtons, headerPortalRef.current),
886
+ !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 }) }),
887
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "designer-body", children: [
888
+ pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "designer-palette", children: [
889
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "palette-heading", children: "New Field Role" }),
890
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "palette-role-section", children: [
891
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
892
+ "select",
794
893
  {
795
- className: `field-type-btn ${activeFieldType === type ? "active" : ""}`,
796
- onClick: () => setActiveFieldType(type),
797
- children: type === "text" ? "Text" : type === "signature" ? "Signature" : type === "signed-date" ? "Date" : type === "checkbox" ? "Checkbox" : "Initials"
798
- },
799
- type
800
- )) })
801
- ] })
894
+ className: "palette-role-select",
895
+ value: activeRole,
896
+ onChange: (e) => setActiveRole(e.target.value),
897
+ style: { borderColor: getSignerColor(activeRole), color: getSignerColor(activeRole) },
898
+ children: signerRoles.map((role) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("option", { value: role, children: role }, role))
899
+ }
900
+ ),
901
+ isAddingRole ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "palette-role-add-inline", children: [
902
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
903
+ "input",
904
+ {
905
+ type: "text",
906
+ value: newRoleName,
907
+ onChange: (e) => setNewRoleName(e.target.value),
908
+ onKeyDown: (e) => e.key === "Enter" && handleAddRole(),
909
+ placeholder: "Role name",
910
+ autoFocus: true
911
+ }
912
+ ),
913
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { onClick: handleAddRole, children: "Add" }),
914
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { onClick: () => {
915
+ setIsAddingRole(false);
916
+ setNewRoleName("");
917
+ }, children: "Cancel" })
918
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("button", { className: "palette-role-add-link", onClick: () => setIsAddingRole(true), children: "+ Add Role" })
919
+ ] }),
920
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "palette-divider" }),
921
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "palette-heading", children: "Fields" }),
922
+ FIELD_TYPE_META.filter((f) => !f.group).map(({ type, label, icon }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
923
+ "div",
924
+ {
925
+ className: `palette-item ${activeFieldType === type ? "active" : ""}`,
926
+ draggable: true,
927
+ onClick: () => setActiveFieldType(type),
928
+ onDragStart: (e) => handlePaletteDragStart(e, type),
929
+ onDragEnd: handlePaletteDragEnd,
930
+ children: [
931
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "palette-item-icon", children: icon }),
932
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "palette-item-label", children: label })
933
+ ]
934
+ },
935
+ type
936
+ )),
937
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "palette-divider" }),
938
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "palette-heading", children: "Redact" }),
939
+ FIELD_TYPE_META.filter((f) => f.group === "redact").map(({ type, label, icon }) => /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
940
+ "div",
941
+ {
942
+ className: `palette-item ${activeFieldType === type ? "active" : ""}`,
943
+ draggable: true,
944
+ onClick: () => setActiveFieldType(type),
945
+ onDragStart: (e) => handlePaletteDragStart(e, type),
946
+ onDragEnd: handlePaletteDragEnd,
947
+ children: [
948
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "palette-item-icon", children: icon }),
949
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "palette-item-label", children: label })
950
+ ]
951
+ },
952
+ type
953
+ ))
802
954
  ] }),
803
- /* @__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" }) })
804
- ] }),
805
- pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
806
- SignerRoleSelector,
807
- {
808
- roles: signerRoles,
809
- activeRole,
810
- onSetActiveRole: setActiveRole,
811
- onAddRole: handleAddRole,
812
- onRemoveRole: handleRemoveRole
813
- }
814
- ),
815
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "designer-content", children: [
816
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "designer-pdf-area", children: [
817
- loading && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "loading-indicator", children: "Loading PDF..." }),
818
- !pages.length && !loading && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "empty-state", children: [
819
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { children: "Upload a PDF to get started" }),
820
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: "Upload a template PDF, then click on the page to place form fields." }),
821
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "upload-btn upload-btn-large", children: [
822
- "Choose PDF File",
823
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
955
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "designer-pdf-area", children: [
956
+ loading && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "loading-indicator", children: "Loading PDF..." }),
957
+ !pages.length && !loading && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "empty-state", children: [
958
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h2", { children: "Open a PDF to get started" }),
959
+ /* @__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." }),
960
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "upload-btn upload-btn-large", children: [
961
+ "Select PDF",
962
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
824
963
  ] })
825
964
  ] }),
826
- pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
965
+ pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
827
966
  PdfViewer,
828
967
  {
829
968
  pages,
830
969
  fields,
831
970
  selectedFieldId,
832
- onSelectField: setSelectedFieldId,
971
+ onSelectField: (id) => {
972
+ setSelectedFieldId(id);
973
+ if (id) setRightTab("properties");
974
+ },
833
975
  onFieldMove: handleFieldMove,
834
976
  onFieldResize: handleFieldResize,
835
977
  onPageClick: handlePageClick,
978
+ onDropField: handleDropOnPage,
836
979
  mode: "designer",
837
980
  renderFieldContent
838
981
  }
839
982
  )
840
983
  ] }),
841
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "designer-panel", children: [
842
- selectedField ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
843
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
844
- FieldPropertyPanel,
845
- {
846
- field: selectedField,
847
- signerRoles,
848
- onUpdate: handleFieldUpdate,
849
- onDelete: handleFieldDelete
850
- }
851
- ),
852
- selectedField.assignee === activeRole && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "prefill-section", children: [
853
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h4", { children: "Pre-fill Value" }),
854
- selectedField.type === "signature" || selectedField.type === "initials" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
855
- SignatureCanvas,
984
+ pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
985
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "panel-resize-handle", onMouseDown: handleResizeStart }),
986
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "designer-panel", style: { width: panelWidth }, children: [
987
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "panel-tabs", children: [
988
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
989
+ "button",
856
990
  {
857
- width: 300,
858
- height: selectedField.type === "initials" ? 100 : 150,
859
- onSign: (dataUrl) => handleFieldUpdate(selectedField.id, { value: dataUrl }),
860
- initialValue: selectedField.value
991
+ className: `panel-tab ${rightTab === "properties" ? "active" : ""}`,
992
+ onClick: () => setRightTab("properties"),
993
+ children: "Properties"
861
994
  }
862
- ) : selectedField.type === "checkbox" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "panel-checkbox-label", children: [
863
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
864
- "input",
865
- {
866
- type: "checkbox",
867
- checked: selectedField.value === "true",
868
- onChange: (e) => handleFieldUpdate(selectedField.id, { value: e.target.checked ? "true" : "" })
869
- }
870
- ),
871
- "Checked"
872
- ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
873
- "input",
995
+ ),
996
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
997
+ "button",
874
998
  {
875
- type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
876
- value: selectedField.value,
877
- onChange: (e) => handleFieldUpdate(selectedField.id, { value: e.target.value }),
878
- placeholder: `Pre-fill ${selectedField.label}`,
879
- className: "prefill-input"
999
+ className: `panel-tab ${rightTab === "fields" ? "active" : ""}`,
1000
+ onClick: () => setRightTab("fields"),
1001
+ children: [
1002
+ "All Fields",
1003
+ fields.length > 0 ? ` (${fields.length})` : ""
1004
+ ]
880
1005
  }
881
1006
  )
882
- ] })
883
- ] }) : /* @__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." }),
884
- fields.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "field-list", children: [
885
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("h4", { children: [
886
- "All Fields (",
887
- fields.length,
888
- ")"
889
1007
  ] }),
890
- fields.map((f, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
891
- "div",
892
- {
893
- className: `field-list-item ${f.id === selectedFieldId ? "active" : ""}`,
894
- onClick: () => setSelectedFieldId(f.id),
895
- children: [
896
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
897
- "span",
1008
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "panel-tab-content", children: [
1009
+ 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: [
1010
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1011
+ FieldPropertyPanel,
1012
+ {
1013
+ field: selectedField,
1014
+ signerRoles,
1015
+ onUpdate: handleFieldUpdate,
1016
+ onDelete: handleFieldDelete
1017
+ }
1018
+ ),
1019
+ selectedField.type !== "blackout" && selectedField.type !== "whiteout" && /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "prefill-section", children: [
1020
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("h4", { children: "Pre-fill Value" }),
1021
+ selectedField.type === "signature" || selectedField.type === "initials" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1022
+ SignatureCanvas,
898
1023
  {
899
- className: "field-list-dot",
900
- style: { backgroundColor: getSignerColor(f.assignee) }
1024
+ width: panelWidth - 40,
1025
+ height: selectedField.type === "initials" ? 100 : 150,
1026
+ onSign: (dataUrl) => handleFieldUpdate(selectedField.id, { value: dataUrl }),
1027
+ initialValue: selectedField.value,
1028
+ inkColor: selectedField.inkColor
901
1029
  }
902
- ),
903
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "field-list-name", children: f.label }),
904
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "field-list-page", children: [
905
- "p",
906
- f.page + 1
907
- ] }),
908
- f.required && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "field-list-required", children: "*" })
909
- ]
910
- },
911
- f.id
912
- ))
1030
+ ) : selectedField.type === "checkbox" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("label", { className: "panel-checkbox-label", children: [
1031
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1032
+ "input",
1033
+ {
1034
+ type: "checkbox",
1035
+ checked: selectedField.value === "true",
1036
+ onChange: (e) => handleFieldUpdate(selectedField.id, { value: e.target.checked ? "true" : "" })
1037
+ }
1038
+ ),
1039
+ "Checked"
1040
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1041
+ "input",
1042
+ {
1043
+ type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
1044
+ value: selectedField.value,
1045
+ onChange: (e) => handleFieldUpdate(selectedField.id, { value: e.target.value }),
1046
+ placeholder: `Pre-fill ${selectedField.label}`,
1047
+ className: "prefill-input",
1048
+ style: { color: selectedField.inkColor || "#000000" }
1049
+ }
1050
+ )
1051
+ ] })
1052
+ ] }) : /* @__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." }) }),
1053
+ 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)(
1054
+ "div",
1055
+ {
1056
+ className: `field-list-item ${f.id === selectedFieldId ? "active" : ""}`,
1057
+ onClick: () => {
1058
+ setSelectedFieldId(f.id);
1059
+ setRightTab("properties");
1060
+ },
1061
+ children: [
1062
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1063
+ "span",
1064
+ {
1065
+ className: "field-list-dot",
1066
+ style: { backgroundColor: getSignerColor(f.assignee) }
1067
+ }
1068
+ ),
1069
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "field-list-name", children: f.label }),
1070
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "field-list-page", children: [
1071
+ "p",
1072
+ f.page + 1
1073
+ ] }),
1074
+ f.required && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: "field-list-required", children: "*" })
1075
+ ]
1076
+ },
1077
+ f.id
1078
+ )) }) })
1079
+ ] }),
1080
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "powered-by", children: [
1081
+ "Powered by ",
1082
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
1083
+ ] })
913
1084
  ] })
914
1085
  ] })
915
- ] }),
916
- /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "powered-by", children: [
917
- "Powered by ",
918
- /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
919
1086
  ] })
920
1087
  ] });
921
1088
  }
922
1089
 
923
1090
  // src/components/pdf-builder/SignerView.tsx
924
- var import_react5 = require("react");
1091
+ var import_react4 = require("react");
925
1092
 
926
1093
  // src/utils/pdfFiller.ts
927
1094
  var import_pdf_lib = require("pdf-lib");
@@ -936,8 +1103,13 @@ async function generateFilledPdf(pdfSource, fields) {
936
1103
  const pdfDoc = await import_pdf_lib.PDFDocument.load(pdfBytes);
937
1104
  const font = await pdfDoc.embedFont(import_pdf_lib.StandardFonts.Helvetica);
938
1105
  const pages = pdfDoc.getPages();
1106
+ function hexToRgb(hex) {
1107
+ const r = parseInt(hex.slice(1, 3), 16) / 255;
1108
+ const g = parseInt(hex.slice(3, 5), 16) / 255;
1109
+ const b = parseInt(hex.slice(5, 7), 16) / 255;
1110
+ return (0, import_pdf_lib.rgb)(r, g, b);
1111
+ }
939
1112
  for (const field of fields) {
940
- if (!field.value) continue;
941
1113
  const page = pages[field.page];
942
1114
  if (!page) continue;
943
1115
  const pageWidth = page.getWidth();
@@ -946,6 +1118,18 @@ async function generateFilledPdf(pdfSource, fields) {
946
1118
  const y = pageHeight - field.y / 100 * pageHeight - field.height / 100 * pageHeight;
947
1119
  const w = field.width / 100 * pageWidth;
948
1120
  const h = field.height / 100 * pageHeight;
1121
+ if (field.type === "blackout" || field.type === "whiteout") {
1122
+ page.drawRectangle({
1123
+ x,
1124
+ y,
1125
+ width: w,
1126
+ height: h,
1127
+ color: field.type === "blackout" ? (0, import_pdf_lib.rgb)(0, 0, 0) : (0, import_pdf_lib.rgb)(1, 1, 1)
1128
+ });
1129
+ continue;
1130
+ }
1131
+ if (!field.value) continue;
1132
+ const inkColor = field.inkColor ? hexToRgb(field.inkColor) : (0, import_pdf_lib.rgb)(0, 0, 0);
949
1133
  if (field.type === "checkbox") {
950
1134
  if (field.value === "true") {
951
1135
  const inset = Math.min(w, h) * 0.2;
@@ -954,13 +1138,13 @@ async function generateFilledPdf(pdfSource, fields) {
954
1138
  start: { x: x + inset, y: y + inset },
955
1139
  end: { x: x + w - inset, y: y + h - inset },
956
1140
  thickness: lineWidth,
957
- color: (0, import_pdf_lib.rgb)(0, 0, 0)
1141
+ color: inkColor
958
1142
  });
959
1143
  page.drawLine({
960
1144
  start: { x: x + w - inset, y: y + inset },
961
1145
  end: { x: x + inset, y: y + h - inset },
962
1146
  thickness: lineWidth,
963
- color: (0, import_pdf_lib.rgb)(0, 0, 0)
1147
+ color: inkColor
964
1148
  });
965
1149
  }
966
1150
  } else if (field.type === "signature" || field.type === "initials") {
@@ -984,7 +1168,7 @@ async function generateFilledPdf(pdfSource, fields) {
984
1168
  y: y + h * 0.3,
985
1169
  size: fontSize,
986
1170
  font,
987
- color: (0, import_pdf_lib.rgb)(0, 0, 0),
1171
+ color: inkColor,
988
1172
  maxWidth: w - 4
989
1173
  });
990
1174
  }
@@ -1011,7 +1195,7 @@ async function postPdfToCallback(bytes, callbackUrl, filename) {
1011
1195
  }
1012
1196
 
1013
1197
  // src/components/pdf-builder/FieldNavigator.tsx
1014
- var import_jsx_runtime6 = require("react/jsx-runtime");
1198
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1015
1199
  function FieldNavigator({
1016
1200
  fields,
1017
1201
  currentFieldId,
@@ -1037,15 +1221,15 @@ function FieldNavigator({
1037
1221
  if (f.type === "checkbox") return true;
1038
1222
  return !!f.value;
1039
1223
  }).length;
1040
- return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "field-navigator", children: [
1041
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "field-navigator-progress", children: [
1224
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "field-navigator", children: [
1225
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "field-navigator-progress", children: [
1042
1226
  filledCount,
1043
1227
  " of ",
1044
1228
  fields.length,
1045
1229
  " fields completed"
1046
1230
  ] }),
1047
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "field-navigator-controls", children: [
1048
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1231
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "field-navigator-controls", children: [
1232
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1049
1233
  "button",
1050
1234
  {
1051
1235
  onClick: handlePrev,
@@ -1054,8 +1238,8 @@ function FieldNavigator({
1054
1238
  children: "Prev"
1055
1239
  }
1056
1240
  ),
1057
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "field-navigator-position", children: currentIndex >= 0 ? `${currentIndex + 1} / ${fields.length}` : "-" }),
1058
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1241
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "field-navigator-position", children: currentIndex >= 0 ? `${currentIndex + 1} / ${fields.length}` : "-" }),
1242
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1059
1243
  "button",
1060
1244
  {
1061
1245
  onClick: handleNext,
@@ -1065,7 +1249,7 @@ function FieldNavigator({
1065
1249
  }
1066
1250
  )
1067
1251
  ] }),
1068
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1252
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1069
1253
  "button",
1070
1254
  {
1071
1255
  onClick: onSubmit,
@@ -1078,26 +1262,49 @@ function FieldNavigator({
1078
1262
  }
1079
1263
 
1080
1264
  // src/components/pdf-builder/SignerView.tsx
1081
- var import_jsx_runtime7 = require("react/jsx-runtime");
1265
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1082
1266
  function SignerView({
1267
+ apiKey,
1083
1268
  initialPdfUrl,
1084
1269
  initialTemplate,
1085
1270
  initialSigner,
1086
1271
  callbackUrl: initialCallbackUrl,
1087
- onComplete
1272
+ onComplete,
1273
+ initialValues
1088
1274
  } = {}) {
1089
- const [pages, setPages] = (0, import_react5.useState)([]);
1090
- const [fields, setFields] = (0, import_react5.useState)([]);
1091
- const [selectedFieldId, setSelectedFieldId] = (0, import_react5.useState)(null);
1092
- const [signer, setSigner] = (0, import_react5.useState)(initialSigner || "Signer 1");
1093
- const [loading, setLoading] = (0, import_react5.useState)(false);
1094
- const [submitting, setSubmitting] = (0, import_react5.useState)(false);
1095
- const [pdfSource, setPdfSource] = (0, import_react5.useState)(null);
1096
- const [callbackUrl, setCallbackUrl] = (0, import_react5.useState)(initialCallbackUrl || "");
1097
- const containerRef = (0, import_react5.useRef)(null);
1098
- (0, import_react5.useEffect)(() => {
1275
+ if (!isValidApiKey(apiKey)) {
1276
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "signer-layout", children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "empty-state", children: [
1277
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h2", { children: "Invalid API Key" }),
1278
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("p", { children: [
1279
+ "A valid API key is required to use Exeq. Visit ",
1280
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("a", { href: "https://exeq.org", children: "exeq.org" }),
1281
+ " to get one."
1282
+ ] })
1283
+ ] }) });
1284
+ }
1285
+ const [pages, setPages] = (0, import_react4.useState)([]);
1286
+ const [fields, setFields] = (0, import_react4.useState)([]);
1287
+ const [selectedFieldId, setSelectedFieldId] = (0, import_react4.useState)(null);
1288
+ const [signer, setSigner] = (0, import_react4.useState)(initialSigner || "Signer 1");
1289
+ const [loading, setLoading] = (0, import_react4.useState)(false);
1290
+ const [submitting, setSubmitting] = (0, import_react4.useState)(false);
1291
+ const [pdfSource, setPdfSource] = (0, import_react4.useState)(null);
1292
+ const [callbackUrl, setCallbackUrl] = (0, import_react4.useState)(initialCallbackUrl || "");
1293
+ const containerRef = (0, import_react4.useRef)(null);
1294
+ (0, import_react4.useEffect)(() => {
1099
1295
  if (initialTemplate) {
1100
- setFields(initialTemplate.fields);
1296
+ let templateFields = initialTemplate.fields;
1297
+ if (initialValues) {
1298
+ templateFields = templateFields.map((f) => {
1299
+ const byLabel = Object.entries(initialValues).find(
1300
+ ([key]) => key.toLowerCase() === f.label.toLowerCase()
1301
+ );
1302
+ const byId = initialValues[f.id];
1303
+ const value = byLabel?.[1] ?? byId;
1304
+ return value !== void 0 ? { ...f, value } : f;
1305
+ });
1306
+ }
1307
+ setFields(templateFields);
1101
1308
  if (initialPdfUrl) loadPdf(initialPdfUrl);
1102
1309
  return;
1103
1310
  }
@@ -1123,7 +1330,7 @@ function SignerView({
1123
1330
  window.addEventListener("message", handleMessage);
1124
1331
  return () => window.removeEventListener("message", handleMessage);
1125
1332
  }, [initialTemplate, initialPdfUrl]);
1126
- const loadPdf = (0, import_react5.useCallback)(async (source) => {
1333
+ const loadPdf = (0, import_react4.useCallback)(async (source) => {
1127
1334
  setLoading(true);
1128
1335
  try {
1129
1336
  const rendered = await renderPdfPages(source);
@@ -1135,13 +1342,18 @@ function SignerView({
1135
1342
  setLoading(false);
1136
1343
  }
1137
1344
  }, []);
1138
- const editableFields = fields.filter((f) => f.assignee === signer);
1345
+ const editableFields = fields.filter((f) => f.assignee === signer).sort((a, b) => {
1346
+ if (a.page !== b.page) return a.page - b.page;
1347
+ const bandThreshold = 2;
1348
+ if (Math.abs(a.y - b.y) > bandThreshold) return a.y - b.y;
1349
+ return a.x - b.x;
1350
+ });
1139
1351
  const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
1140
1352
  const isFieldEditable = selectedField ? selectedField.assignee === signer : false;
1141
- const handleFieldUpdate = (0, import_react5.useCallback)((id, value) => {
1353
+ const handleFieldUpdate = (0, import_react4.useCallback)((id, value) => {
1142
1354
  setFields((prev) => prev.map((f) => f.id === id ? { ...f, value } : f));
1143
1355
  }, []);
1144
- const handleNavigate = (0, import_react5.useCallback)((fieldId) => {
1356
+ const handleNavigate = (0, import_react4.useCallback)((fieldId) => {
1145
1357
  setSelectedFieldId(fieldId);
1146
1358
  const field = fields.find((f) => f.id === fieldId);
1147
1359
  if (field && containerRef.current) {
@@ -1156,7 +1368,7 @@ function SignerView({
1156
1368
  if (f.type === "checkbox") return true;
1157
1369
  return !!f.value;
1158
1370
  });
1159
- const handleSubmit = (0, import_react5.useCallback)(async () => {
1371
+ const handleSubmit = (0, import_react4.useCallback)(async () => {
1160
1372
  if (!pdfSource || !allRequiredFilled) return;
1161
1373
  setSubmitting(true);
1162
1374
  try {
@@ -1176,25 +1388,25 @@ function SignerView({
1176
1388
  setSubmitting(false);
1177
1389
  }
1178
1390
  }, [pdfSource, fields, callbackUrl, allRequiredFilled, onComplete]);
1179
- const renderFieldContent = (0, import_react5.useCallback)((field) => {
1391
+ const renderFieldContent = (0, import_react4.useCallback)((field) => {
1180
1392
  const editable = field.assignee === signer;
1181
1393
  if (!editable) {
1182
1394
  if (field.type === "signature" || field.type === "initials") {
1183
- 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 });
1395
+ 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 });
1184
1396
  }
1185
1397
  if (field.type === "checkbox") {
1186
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "field-checkbox-display readonly", children: field.value === "true" ? "\u2713" : "" });
1398
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "field-checkbox-display readonly", children: field.value === "true" ? "\u2713" : "" });
1187
1399
  }
1188
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "field-overlay-placeholder readonly", children: field.value || field.placeholder });
1400
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "field-overlay-placeholder readonly", children: field.value || field.placeholder });
1189
1401
  }
1190
1402
  if (field.type === "signature" || field.type === "initials") {
1191
1403
  if (field.value) {
1192
- 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" }) });
1404
+ 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" }) });
1193
1405
  }
1194
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "field-overlay-placeholder editable", children: field.placeholder });
1406
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "field-overlay-placeholder editable", children: field.placeholder });
1195
1407
  }
1196
1408
  if (field.type === "checkbox") {
1197
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1409
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1198
1410
  "div",
1199
1411
  {
1200
1412
  className: "field-checkbox-display editable",
@@ -1207,9 +1419,9 @@ function SignerView({
1207
1419
  );
1208
1420
  }
1209
1421
  if (field.type === "signed-date") {
1210
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "field-overlay-value", children: field.value || (/* @__PURE__ */ new Date()).toLocaleDateString() });
1422
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "field-overlay-value", children: field.value || (/* @__PURE__ */ new Date()).toLocaleDateString() });
1211
1423
  }
1212
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1424
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1213
1425
  "input",
1214
1426
  {
1215
1427
  type: field.textSubtype === "email" ? "email" : field.textSubtype === "number" ? "number" : field.textSubtype === "phone" ? "tel" : field.textSubtype === "date" ? "date" : "text",
@@ -1222,7 +1434,7 @@ function SignerView({
1222
1434
  }
1223
1435
  );
1224
1436
  }, [signer, handleFieldUpdate]);
1225
- (0, import_react5.useEffect)(() => {
1437
+ (0, import_react4.useEffect)(() => {
1226
1438
  const sigFields = fields.filter((f) => f.assignee === signer && f.type === "signature" && f.value);
1227
1439
  if (sigFields.length > 0) {
1228
1440
  const dateStr = (/* @__PURE__ */ new Date()).toLocaleDateString();
@@ -1234,10 +1446,10 @@ function SignerView({
1234
1446
  }));
1235
1447
  }
1236
1448
  }, [fields.filter((f) => f.type === "signature" && f.value).length]);
1237
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "signer-layout", ref: containerRef, children: [
1238
- loading && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "loading-indicator", children: "Loading document..." }),
1239
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "signer-content", children: [
1240
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "signer-pdf-area", children: pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1449
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "signer-layout", ref: containerRef, children: [
1450
+ loading && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "loading-indicator", children: "Loading document..." }),
1451
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "signer-content", children: [
1452
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "signer-pdf-area", children: pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1241
1453
  PdfViewer,
1242
1454
  {
1243
1455
  pages,
@@ -1249,11 +1461,11 @@ function SignerView({
1249
1461
  renderFieldContent
1250
1462
  }
1251
1463
  ) }),
1252
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "signer-panel", children: [
1253
- selectedField && isFieldEditable && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "signer-field-input", children: [
1254
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { children: selectedField.label }),
1255
- selectedField.required && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "required-badge", children: "Required" }),
1256
- (selectedField.type === "signature" || selectedField.type === "initials") && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1464
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "signer-panel", children: [
1465
+ selectedField && isFieldEditable && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "signer-field-input", children: [
1466
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { children: selectedField.label }),
1467
+ selectedField.required && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "required-badge", children: "Required" }),
1468
+ (selectedField.type === "signature" || selectedField.type === "initials") && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1257
1469
  SignatureCanvas,
1258
1470
  {
1259
1471
  width: 280,
@@ -1262,7 +1474,7 @@ function SignerView({
1262
1474
  initialValue: selectedField.value
1263
1475
  }
1264
1476
  ),
1265
- selectedField.type === "text" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1477
+ selectedField.type === "text" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1266
1478
  "input",
1267
1479
  {
1268
1480
  type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
@@ -1272,8 +1484,8 @@ function SignerView({
1272
1484
  className: "signer-text-input"
1273
1485
  }
1274
1486
  ),
1275
- selectedField.type === "checkbox" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "signer-checkbox-label", children: [
1276
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1487
+ selectedField.type === "checkbox" && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("label", { className: "signer-checkbox-label", children: [
1488
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1277
1489
  "input",
1278
1490
  {
1279
1491
  type: "checkbox",
@@ -1283,14 +1495,14 @@ function SignerView({
1283
1495
  ),
1284
1496
  selectedField.placeholder || "Check this box"
1285
1497
  ] }),
1286
- selectedField.type === "signed-date" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "signer-date-display", children: selectedField.value || "Will auto-fill when you sign" })
1498
+ selectedField.type === "signed-date" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "signer-date-display", children: selectedField.value || "Will auto-fill when you sign" })
1287
1499
  ] }),
1288
- selectedField && !isFieldEditable && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "signer-field-readonly", children: [
1289
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { children: selectedField.label }),
1290
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { children: "This field belongs to another signer." })
1500
+ selectedField && !isFieldEditable && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "signer-field-readonly", children: [
1501
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("h3", { children: selectedField.label }),
1502
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("p", { children: "This field belongs to another signer." })
1291
1503
  ] }),
1292
- !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." }),
1293
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1504
+ !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." }),
1505
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1294
1506
  FieldNavigator,
1295
1507
  {
1296
1508
  fields: editableFields,
@@ -1300,12 +1512,87 @@ function SignerView({
1300
1512
  onSubmit: handleSubmit
1301
1513
  }
1302
1514
  ),
1303
- submitting && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "loading-indicator", children: "Generating PDF..." })
1515
+ submitting && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "loading-indicator", children: "Generating PDF..." })
1304
1516
  ] })
1305
1517
  ] }),
1306
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "powered-by", children: [
1518
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "powered-by", children: [
1307
1519
  "Powered by ",
1308
- /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
1520
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
1521
+ ] })
1522
+ ] });
1523
+ }
1524
+
1525
+ // src/components/pdf-builder/SignerRoleSelector.tsx
1526
+ var import_react5 = require("react");
1527
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1528
+ function SignerRoleSelector({
1529
+ roles,
1530
+ activeRole,
1531
+ onSetActiveRole,
1532
+ onAddRole,
1533
+ onRemoveRole
1534
+ }) {
1535
+ const [isAdding, setIsAdding] = (0, import_react5.useState)(false);
1536
+ const [newRoleName, setNewRoleName] = (0, import_react5.useState)("");
1537
+ const handleAdd = () => {
1538
+ if (newRoleName.trim()) {
1539
+ onAddRole(newRoleName.trim());
1540
+ setNewRoleName("");
1541
+ setIsAdding(false);
1542
+ }
1543
+ };
1544
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "signer-role-selector", children: [
1545
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "signer-role-label", children: "Signer Roles" }),
1546
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "signer-role-list", children: [
1547
+ roles.map((role) => /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)(
1548
+ "button",
1549
+ {
1550
+ className: `signer-role-chip ${role === activeRole ? "active" : ""}`,
1551
+ style: {
1552
+ borderColor: getSignerColor(role),
1553
+ backgroundColor: role === activeRole ? getSignerColor(role) : "transparent",
1554
+ color: role === activeRole ? "#fff" : getSignerColor(role)
1555
+ },
1556
+ onClick: () => onSetActiveRole(role),
1557
+ children: [
1558
+ role,
1559
+ roles.length > 1 && role !== "Sender" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1560
+ "span",
1561
+ {
1562
+ className: "signer-role-remove",
1563
+ onClick: (e) => {
1564
+ e.stopPropagation();
1565
+ onRemoveRole(role);
1566
+ },
1567
+ children: "\xD7"
1568
+ }
1569
+ )
1570
+ ]
1571
+ },
1572
+ role
1573
+ )),
1574
+ isAdding ? /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "signer-role-add-input", children: [
1575
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1576
+ "input",
1577
+ {
1578
+ type: "text",
1579
+ value: newRoleName,
1580
+ onChange: (e) => setNewRoleName(e.target.value),
1581
+ onKeyDown: (e) => e.key === "Enter" && handleAdd(),
1582
+ placeholder: "Role name",
1583
+ autoFocus: true
1584
+ }
1585
+ ),
1586
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { onClick: handleAdd, children: "Add" }),
1587
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("button", { onClick: () => setIsAdding(false), children: "Cancel" })
1588
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1589
+ "button",
1590
+ {
1591
+ className: "signer-role-add-btn",
1592
+ onClick: () => setIsAdding(true),
1593
+ children: "+ Add Role"
1594
+ }
1595
+ )
1309
1596
  ] })
1310
1597
  ] });
1311
1598
  }
@@ -1326,6 +1613,7 @@ function SignerView({
1326
1613
  generateFilledPdf,
1327
1614
  getSignerColor,
1328
1615
  postPdfToCallback,
1329
- renderPdfPages
1616
+ renderPdfPages,
1617
+ uniqueLabel
1330
1618
  });
1331
1619
  //# sourceMappingURL=index.js.map