@unlev/exeq 0.1.2 → 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,67 @@ 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");
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
+ ];
665
651
  function DesignerView({
652
+ apiKey,
666
653
  initialPdfUrl,
667
654
  initialTemplate,
668
- onSave
655
+ onSave,
656
+ hideHeader,
657
+ headerPortalRef
669
658
  } = {}) {
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)(() => {
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)(() => {
679
685
  const pdfUrl = initialPdfUrl || initialTemplate?.pdfUrl;
680
686
  if (pdfUrl) {
681
687
  loadPdf(pdfUrl);
@@ -699,7 +705,7 @@ function DesignerView({
699
705
  window.addEventListener("message", handleMessage);
700
706
  return () => window.removeEventListener("message", handleMessage);
701
707
  }, [initialPdfUrl, initialTemplate]);
702
- const loadPdf = (0, import_react4.useCallback)(async (source) => {
708
+ const loadPdf = (0, import_react3.useCallback)(async (source) => {
703
709
  setLoading(true);
704
710
  try {
705
711
  const rendered = await renderPdfPages(source);
@@ -711,7 +717,7 @@ function DesignerView({
711
717
  setLoading(false);
712
718
  }
713
719
  }, []);
714
- const handleFileUpload = (0, import_react4.useCallback)((e) => {
720
+ const handleFileUpload = (0, import_react3.useCallback)((e) => {
715
721
  const file = e.target.files?.[0];
716
722
  if (!file) return;
717
723
  const reader = new FileReader();
@@ -720,35 +726,45 @@ function DesignerView({
720
726
  };
721
727
  reader.readAsArrayBuffer(file);
722
728
  }, [loadPdf]);
723
- const handlePageClick = (0, import_react4.useCallback)((page, x, y) => {
724
- 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);
725
731
  setFields((prev) => [...prev, field]);
726
732
  setSelectedFieldId(field.id);
727
- }, [activeFieldType, activeRole]);
728
- 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) => {
729
736
  setFields((prev) => prev.map((f) => f.id === id ? { ...f, page, x, y } : f));
730
737
  }, []);
731
- const handleFieldResize = (0, import_react4.useCallback)((id, width, height) => {
738
+ const handleFieldResize = (0, import_react3.useCallback)((id, width, height) => {
732
739
  setFields((prev) => prev.map((f) => f.id === id ? { ...f, width, height } : f));
733
740
  }, []);
734
- const handleFieldUpdate = (0, import_react4.useCallback)((id, updates) => {
735
- 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
+ });
736
749
  }, []);
737
- const handleFieldDelete = (0, import_react4.useCallback)((id) => {
750
+ const handleFieldDelete = (0, import_react3.useCallback)((id) => {
738
751
  setFields((prev) => prev.filter((f) => f.id !== id));
739
752
  if (selectedFieldId === id) setSelectedFieldId(null);
740
753
  }, [selectedFieldId]);
741
- const handleAddRole = (0, import_react4.useCallback)((role) => {
742
- if (!signerRoles.includes(role)) {
743
- 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]);
744
758
  }
745
- }, [signerRoles]);
746
- const handleRemoveRole = (0, import_react4.useCallback)((role) => {
759
+ setNewRoleName("");
760
+ setIsAddingRole(false);
761
+ }, [newRoleName, signerRoles]);
762
+ const handleRemoveRole = (0, import_react3.useCallback)((role) => {
747
763
  setSignerRoles((prev) => prev.filter((r) => r !== role));
748
764
  setFields((prev) => prev.map((f) => f.assignee === role ? { ...f, assignee: signerRoles[0] } : f));
749
765
  if (activeRole === role) setActiveRole(signerRoles[0]);
750
766
  }, [signerRoles, activeRole]);
751
- const handleExport = (0, import_react4.useCallback)(() => {
767
+ const handleExport = (0, import_react3.useCallback)(() => {
752
768
  const template = {
753
769
  fields,
754
770
  signerRoles,
@@ -768,160 +784,310 @@ function DesignerView({
768
784
  }
769
785
  window.parent?.postMessage({ type: "template-saved", template }, "*");
770
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
+ });
771
856
  const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
772
- 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
+ }
773
861
  if (field.type === "signature" || field.type === "initials") {
774
862
  if (field.value) {
775
- 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" });
776
864
  }
777
865
  }
778
- 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
+ );
779
875
  }, []);
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",
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",
794
892
  {
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
- ] })
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
+ ))
802
953
  ] }),
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 })
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 })
824
962
  ] })
825
963
  ] }),
826
- pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
964
+ pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
827
965
  PdfViewer,
828
966
  {
829
967
  pages,
830
968
  fields,
831
969
  selectedFieldId,
832
- onSelectField: setSelectedFieldId,
970
+ onSelectField: (id) => {
971
+ setSelectedFieldId(id);
972
+ if (id) setRightTab("properties");
973
+ },
833
974
  onFieldMove: handleFieldMove,
834
975
  onFieldResize: handleFieldResize,
835
976
  onPageClick: handlePageClick,
977
+ onDropField: handleDropOnPage,
836
978
  mode: "designer",
837
979
  renderFieldContent
838
980
  }
839
981
  )
840
982
  ] }),
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,
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",
856
989
  {
857
- width: 300,
858
- height: selectedField.type === "initials" ? 100 : 150,
859
- onSign: (dataUrl) => handleFieldUpdate(selectedField.id, { value: dataUrl }),
860
- initialValue: selectedField.value
990
+ className: `panel-tab ${rightTab === "properties" ? "active" : ""}`,
991
+ onClick: () => setRightTab("properties"),
992
+ children: "Properties"
861
993
  }
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",
994
+ ),
995
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
996
+ "button",
874
997
  {
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"
998
+ className: `panel-tab ${rightTab === "fields" ? "active" : ""}`,
999
+ onClick: () => setRightTab("fields"),
1000
+ children: [
1001
+ "All Fields",
1002
+ fields.length > 0 ? ` (${fields.length})` : ""
1003
+ ]
880
1004
  }
881
1005
  )
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
1006
  ] }),
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",
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,
898
1022
  {
899
- className: "field-list-dot",
900
- 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
901
1028
  }
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
- ))
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
+ ] })
913
1083
  ] })
914
1084
  ] })
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
1085
  ] })
920
1086
  ] });
921
1087
  }
922
1088
 
923
1089
  // src/components/pdf-builder/SignerView.tsx
924
- var import_react5 = require("react");
1090
+ var import_react4 = require("react");
925
1091
 
926
1092
  // src/utils/pdfFiller.ts
927
1093
  var import_pdf_lib = require("pdf-lib");
@@ -936,8 +1102,13 @@ async function generateFilledPdf(pdfSource, fields) {
936
1102
  const pdfDoc = await import_pdf_lib.PDFDocument.load(pdfBytes);
937
1103
  const font = await pdfDoc.embedFont(import_pdf_lib.StandardFonts.Helvetica);
938
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
+ }
939
1111
  for (const field of fields) {
940
- if (!field.value) continue;
941
1112
  const page = pages[field.page];
942
1113
  if (!page) continue;
943
1114
  const pageWidth = page.getWidth();
@@ -946,6 +1117,18 @@ async function generateFilledPdf(pdfSource, fields) {
946
1117
  const y = pageHeight - field.y / 100 * pageHeight - field.height / 100 * pageHeight;
947
1118
  const w = field.width / 100 * pageWidth;
948
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);
949
1132
  if (field.type === "checkbox") {
950
1133
  if (field.value === "true") {
951
1134
  const inset = Math.min(w, h) * 0.2;
@@ -954,13 +1137,13 @@ async function generateFilledPdf(pdfSource, fields) {
954
1137
  start: { x: x + inset, y: y + inset },
955
1138
  end: { x: x + w - inset, y: y + h - inset },
956
1139
  thickness: lineWidth,
957
- color: (0, import_pdf_lib.rgb)(0, 0, 0)
1140
+ color: inkColor
958
1141
  });
959
1142
  page.drawLine({
960
1143
  start: { x: x + w - inset, y: y + inset },
961
1144
  end: { x: x + inset, y: y + h - inset },
962
1145
  thickness: lineWidth,
963
- color: (0, import_pdf_lib.rgb)(0, 0, 0)
1146
+ color: inkColor
964
1147
  });
965
1148
  }
966
1149
  } else if (field.type === "signature" || field.type === "initials") {
@@ -984,7 +1167,7 @@ async function generateFilledPdf(pdfSource, fields) {
984
1167
  y: y + h * 0.3,
985
1168
  size: fontSize,
986
1169
  font,
987
- color: (0, import_pdf_lib.rgb)(0, 0, 0),
1170
+ color: inkColor,
988
1171
  maxWidth: w - 4
989
1172
  });
990
1173
  }
@@ -1011,7 +1194,7 @@ async function postPdfToCallback(bytes, callbackUrl, filename) {
1011
1194
  }
1012
1195
 
1013
1196
  // src/components/pdf-builder/FieldNavigator.tsx
1014
- var import_jsx_runtime6 = require("react/jsx-runtime");
1197
+ var import_jsx_runtime5 = require("react/jsx-runtime");
1015
1198
  function FieldNavigator({
1016
1199
  fields,
1017
1200
  currentFieldId,
@@ -1037,15 +1220,15 @@ function FieldNavigator({
1037
1220
  if (f.type === "checkbox") return true;
1038
1221
  return !!f.value;
1039
1222
  }).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: [
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: [
1042
1225
  filledCount,
1043
1226
  " of ",
1044
1227
  fields.length,
1045
1228
  " fields completed"
1046
1229
  ] }),
1047
- /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "field-navigator-controls", children: [
1048
- /* @__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)(
1049
1232
  "button",
1050
1233
  {
1051
1234
  onClick: handlePrev,
@@ -1054,8 +1237,8 @@ function FieldNavigator({
1054
1237
  children: "Prev"
1055
1238
  }
1056
1239
  ),
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)(
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)(
1059
1242
  "button",
1060
1243
  {
1061
1244
  onClick: handleNext,
@@ -1065,7 +1248,7 @@ function FieldNavigator({
1065
1248
  }
1066
1249
  )
1067
1250
  ] }),
1068
- /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1251
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
1069
1252
  "button",
1070
1253
  {
1071
1254
  onClick: onSubmit,
@@ -1078,26 +1261,49 @@ function FieldNavigator({
1078
1261
  }
1079
1262
 
1080
1263
  // src/components/pdf-builder/SignerView.tsx
1081
- var import_jsx_runtime7 = require("react/jsx-runtime");
1264
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1082
1265
  function SignerView({
1266
+ apiKey,
1083
1267
  initialPdfUrl,
1084
1268
  initialTemplate,
1085
1269
  initialSigner,
1086
1270
  callbackUrl: initialCallbackUrl,
1087
- onComplete
1271
+ onComplete,
1272
+ initialValues
1088
1273
  } = {}) {
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)(() => {
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)(() => {
1099
1294
  if (initialTemplate) {
1100
- 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);
1101
1307
  if (initialPdfUrl) loadPdf(initialPdfUrl);
1102
1308
  return;
1103
1309
  }
@@ -1123,7 +1329,7 @@ function SignerView({
1123
1329
  window.addEventListener("message", handleMessage);
1124
1330
  return () => window.removeEventListener("message", handleMessage);
1125
1331
  }, [initialTemplate, initialPdfUrl]);
1126
- const loadPdf = (0, import_react5.useCallback)(async (source) => {
1332
+ const loadPdf = (0, import_react4.useCallback)(async (source) => {
1127
1333
  setLoading(true);
1128
1334
  try {
1129
1335
  const rendered = await renderPdfPages(source);
@@ -1135,13 +1341,18 @@ function SignerView({
1135
1341
  setLoading(false);
1136
1342
  }
1137
1343
  }, []);
1138
- 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
+ });
1139
1350
  const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
1140
1351
  const isFieldEditable = selectedField ? selectedField.assignee === signer : false;
1141
- const handleFieldUpdate = (0, import_react5.useCallback)((id, value) => {
1352
+ const handleFieldUpdate = (0, import_react4.useCallback)((id, value) => {
1142
1353
  setFields((prev) => prev.map((f) => f.id === id ? { ...f, value } : f));
1143
1354
  }, []);
1144
- const handleNavigate = (0, import_react5.useCallback)((fieldId) => {
1355
+ const handleNavigate = (0, import_react4.useCallback)((fieldId) => {
1145
1356
  setSelectedFieldId(fieldId);
1146
1357
  const field = fields.find((f) => f.id === fieldId);
1147
1358
  if (field && containerRef.current) {
@@ -1156,7 +1367,7 @@ function SignerView({
1156
1367
  if (f.type === "checkbox") return true;
1157
1368
  return !!f.value;
1158
1369
  });
1159
- const handleSubmit = (0, import_react5.useCallback)(async () => {
1370
+ const handleSubmit = (0, import_react4.useCallback)(async () => {
1160
1371
  if (!pdfSource || !allRequiredFilled) return;
1161
1372
  setSubmitting(true);
1162
1373
  try {
@@ -1176,25 +1387,25 @@ function SignerView({
1176
1387
  setSubmitting(false);
1177
1388
  }
1178
1389
  }, [pdfSource, fields, callbackUrl, allRequiredFilled, onComplete]);
1179
- const renderFieldContent = (0, import_react5.useCallback)((field) => {
1390
+ const renderFieldContent = (0, import_react4.useCallback)((field) => {
1180
1391
  const editable = field.assignee === signer;
1181
1392
  if (!editable) {
1182
1393
  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 });
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 });
1184
1395
  }
1185
1396
  if (field.type === "checkbox") {
1186
- 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" : "" });
1187
1398
  }
1188
- 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 });
1189
1400
  }
1190
1401
  if (field.type === "signature" || field.type === "initials") {
1191
1402
  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" }) });
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" }) });
1193
1404
  }
1194
- 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 });
1195
1406
  }
1196
1407
  if (field.type === "checkbox") {
1197
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1408
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1198
1409
  "div",
1199
1410
  {
1200
1411
  className: "field-checkbox-display editable",
@@ -1207,9 +1418,9 @@ function SignerView({
1207
1418
  );
1208
1419
  }
1209
1420
  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() });
1421
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("div", { className: "field-overlay-value", children: field.value || (/* @__PURE__ */ new Date()).toLocaleDateString() });
1211
1422
  }
1212
- return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1423
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1213
1424
  "input",
1214
1425
  {
1215
1426
  type: field.textSubtype === "email" ? "email" : field.textSubtype === "number" ? "number" : field.textSubtype === "phone" ? "tel" : field.textSubtype === "date" ? "date" : "text",
@@ -1222,7 +1433,7 @@ function SignerView({
1222
1433
  }
1223
1434
  );
1224
1435
  }, [signer, handleFieldUpdate]);
1225
- (0, import_react5.useEffect)(() => {
1436
+ (0, import_react4.useEffect)(() => {
1226
1437
  const sigFields = fields.filter((f) => f.assignee === signer && f.type === "signature" && f.value);
1227
1438
  if (sigFields.length > 0) {
1228
1439
  const dateStr = (/* @__PURE__ */ new Date()).toLocaleDateString();
@@ -1234,10 +1445,10 @@ function SignerView({
1234
1445
  }));
1235
1446
  }
1236
1447
  }, [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)(
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)(
1241
1452
  PdfViewer,
1242
1453
  {
1243
1454
  pages,
@@ -1249,11 +1460,11 @@ function SignerView({
1249
1460
  renderFieldContent
1250
1461
  }
1251
1462
  ) }),
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)(
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)(
1257
1468
  SignatureCanvas,
1258
1469
  {
1259
1470
  width: 280,
@@ -1262,7 +1473,7 @@ function SignerView({
1262
1473
  initialValue: selectedField.value
1263
1474
  }
1264
1475
  ),
1265
- selectedField.type === "text" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1476
+ selectedField.type === "text" && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1266
1477
  "input",
1267
1478
  {
1268
1479
  type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
@@ -1272,8 +1483,8 @@ function SignerView({
1272
1483
  className: "signer-text-input"
1273
1484
  }
1274
1485
  ),
1275
- selectedField.type === "checkbox" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "signer-checkbox-label", children: [
1276
- /* @__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)(
1277
1488
  "input",
1278
1489
  {
1279
1490
  type: "checkbox",
@@ -1283,14 +1494,14 @@ function SignerView({
1283
1494
  ),
1284
1495
  selectedField.placeholder || "Check this box"
1285
1496
  ] }),
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" })
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" })
1287
1498
  ] }),
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." })
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." })
1291
1502
  ] }),
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)(
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)(
1294
1505
  FieldNavigator,
1295
1506
  {
1296
1507
  fields: editableFields,
@@ -1300,12 +1511,87 @@ function SignerView({
1300
1511
  onSubmit: handleSubmit
1301
1512
  }
1302
1513
  ),
1303
- 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..." })
1304
1515
  ] })
1305
1516
  ] }),
1306
- /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "powered-by", children: [
1517
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "powered-by", children: [
1307
1518
  "Powered by ",
1308
- /* @__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
+ )
1309
1595
  ] })
1310
1596
  ] });
1311
1597
  }
@@ -1326,6 +1612,7 @@ function SignerView({
1326
1612
  generateFilledPdf,
1327
1613
  getSignerColor,
1328
1614
  postPdfToCallback,
1329
- renderPdfPages
1615
+ renderPdfPages,
1616
+ uniqueLabel
1330
1617
  });
1331
1618
  //# sourceMappingURL=index.js.map