@unlev/exeq 0.1.0

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 ADDED
@@ -0,0 +1,1318 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/lib/index.ts
31
+ var lib_exports = {};
32
+ __export(lib_exports, {
33
+ DEFAULT_SIGNER_ROLES: () => DEFAULT_SIGNER_ROLES,
34
+ DesignerView: () => DesignerView,
35
+ FIELD_DEFAULTS: () => FIELD_DEFAULTS,
36
+ FieldNavigator: () => FieldNavigator,
37
+ FieldPropertyPanel: () => FieldPropertyPanel,
38
+ PdfViewer: () => PdfViewer,
39
+ SIGNER_ROLE_COLORS: () => SIGNER_ROLE_COLORS,
40
+ SignatureCanvas: () => SignatureCanvas,
41
+ SignerRoleSelector: () => SignerRoleSelector,
42
+ SignerView: () => SignerView,
43
+ createField: () => createField,
44
+ downloadPdf: () => downloadPdf,
45
+ generateFilledPdf: () => generateFilledPdf,
46
+ getSignerColor: () => getSignerColor,
47
+ postPdfToCallback: () => postPdfToCallback,
48
+ renderPdfPages: () => renderPdfPages
49
+ });
50
+ module.exports = __toCommonJS(lib_exports);
51
+
52
+ // src/components/pdf-builder/DesignerView.tsx
53
+ var import_react4 = require("react");
54
+
55
+ // src/types/pdf-builder.ts
56
+ function generateId() {
57
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
58
+ return crypto.randomUUID();
59
+ }
60
+ return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, (c) => {
61
+ const r = Math.random() * 16 | 0;
62
+ const v = c === "x" ? r : r & 3 | 8;
63
+ return v.toString(16);
64
+ });
65
+ }
66
+ var DEFAULT_SIGNER_ROLES = ["Sender", "Signer 1"];
67
+ var SIGNER_ROLE_COLORS = {
68
+ "Sender": "#6366f1",
69
+ "Signer 1": "#22c55e",
70
+ "Signer 2": "#f59e0b",
71
+ "Signer 3": "#ef4444",
72
+ "Signer 4": "#06b6d4",
73
+ "Signer 5": "#d946ef"
74
+ };
75
+ function getSignerColor(role) {
76
+ return SIGNER_ROLE_COLORS[role] || "#888888";
77
+ }
78
+ var FIELD_DEFAULTS = {
79
+ text: {
80
+ width: 20,
81
+ height: 3,
82
+ fontSize: 12,
83
+ placeholder: "Enter text",
84
+ textSubtype: "freeform"
85
+ },
86
+ signature: {
87
+ width: 20,
88
+ height: 6,
89
+ fontSize: 12,
90
+ placeholder: "Sign here"
91
+ },
92
+ "signed-date": {
93
+ width: 15,
94
+ height: 3,
95
+ fontSize: 12,
96
+ placeholder: "Date"
97
+ },
98
+ checkbox: {
99
+ width: 2.5,
100
+ height: 2.5,
101
+ fontSize: 12,
102
+ placeholder: ""
103
+ },
104
+ initials: {
105
+ width: 8,
106
+ height: 5,
107
+ fontSize: 12,
108
+ placeholder: "Initials"
109
+ }
110
+ };
111
+ function createField(type, assignee, page, x, y) {
112
+ const defaults = FIELD_DEFAULTS[type];
113
+ return {
114
+ id: generateId(),
115
+ type,
116
+ label: type === "text" ? "Text Field" : type === "signature" ? "Signature" : type === "signed-date" ? "Signed Date" : type === "checkbox" ? "Checkbox" : "Initials",
117
+ placeholder: defaults.placeholder || "",
118
+ required: true,
119
+ assignee,
120
+ page,
121
+ x,
122
+ y,
123
+ width: defaults.width || 20,
124
+ height: defaults.height || 3,
125
+ fontSize: defaults.fontSize || 12,
126
+ value: "",
127
+ ...type === "text" ? { textSubtype: defaults.textSubtype } : {}
128
+ };
129
+ }
130
+
131
+ // src/utils/pdfRenderer.ts
132
+ var pdfjsLib = __toESM(require("pdfjs-dist"));
133
+ if (typeof window !== "undefined") {
134
+ pdfjsLib.GlobalWorkerOptions.workerSrc = `https://cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjsLib.version}/pdf.worker.min.mjs`;
135
+ }
136
+ async function renderPdfPages(source, scale = 2) {
137
+ const loadingTask = pdfjsLib.getDocument(
138
+ typeof source === "string" ? { url: source } : { data: source }
139
+ );
140
+ const pdf = await loadingTask.promise;
141
+ const pages = [];
142
+ for (let i = 1; i <= pdf.numPages; i++) {
143
+ const page = await pdf.getPage(i);
144
+ const viewport = page.getViewport({ scale });
145
+ const canvas = document.createElement("canvas");
146
+ canvas.width = viewport.width;
147
+ canvas.height = viewport.height;
148
+ const ctx = canvas.getContext("2d");
149
+ await page.render({ canvasContext: ctx, viewport }).promise;
150
+ pages.push({
151
+ pageNumber: i,
152
+ dataUrl: canvas.toDataURL("image/png"),
153
+ width: viewport.width,
154
+ height: viewport.height
155
+ });
156
+ }
157
+ return pages;
158
+ }
159
+
160
+ // src/components/pdf-builder/PdfViewer.tsx
161
+ var import_react = require("react");
162
+ var import_jsx_runtime = require("react/jsx-runtime");
163
+ function PdfViewer({
164
+ pages,
165
+ fields,
166
+ selectedFieldId,
167
+ onSelectField,
168
+ onFieldMove,
169
+ onFieldResize,
170
+ onPageClick,
171
+ mode,
172
+ currentSigner,
173
+ renderFieldContent
174
+ }) {
175
+ const containerRef = (0, import_react.useRef)(null);
176
+ const handlePageClick = (0, import_react.useCallback)((e, pageIndex) => {
177
+ const target = e.target;
178
+ if (target.closest(".field-overlay")) return;
179
+ if (onPageClick) {
180
+ const rect = e.currentTarget.getBoundingClientRect();
181
+ const x = (e.clientX - rect.left) / rect.width * 100;
182
+ const y = (e.clientY - rect.top) / rect.height * 100;
183
+ onPageClick(pageIndex, x, y);
184
+ } else {
185
+ onSelectField(null);
186
+ }
187
+ }, [onPageClick, onSelectField]);
188
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "pdf-viewer", ref: containerRef, children: pages.map((page, pageIndex) => {
189
+ const pageFields = fields.filter((f) => f.page === pageIndex);
190
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
191
+ "div",
192
+ {
193
+ className: "pdf-page",
194
+ style: { aspectRatio: `${page.width} / ${page.height}` },
195
+ onClick: (e) => handlePageClick(e, pageIndex),
196
+ "data-page": pageIndex,
197
+ children: [
198
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
199
+ "img",
200
+ {
201
+ src: page.dataUrl,
202
+ alt: `Page ${pageIndex + 1}`,
203
+ className: "pdf-page-image",
204
+ draggable: false
205
+ }
206
+ ),
207
+ pageFields.map((field) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
208
+ FieldOverlayItem,
209
+ {
210
+ field,
211
+ isSelected: field.id === selectedFieldId,
212
+ onSelect: () => onSelectField(field.id),
213
+ onMove: onFieldMove,
214
+ onResize: onFieldResize,
215
+ mode,
216
+ currentSigner,
217
+ renderContent: renderFieldContent
218
+ },
219
+ field.id
220
+ ))
221
+ ]
222
+ },
223
+ pageIndex
224
+ );
225
+ }) });
226
+ }
227
+ function FieldOverlayItem({
228
+ field,
229
+ isSelected,
230
+ onSelect,
231
+ onMove,
232
+ onResize,
233
+ mode,
234
+ currentSigner,
235
+ renderContent
236
+ }) {
237
+ const overlayRef = (0, import_react.useRef)(null);
238
+ const dragStartRef = (0, import_react.useRef)(null);
239
+ const resizeStartRef = (0, import_react.useRef)(null);
240
+ const color = getSignerColor(field.assignee);
241
+ const isEditable = mode === "designer" || mode === "signer" && field.assignee === currentSigner;
242
+ const isPreFilled = !isEditable && !!field.value;
243
+ const handleMouseDown = (0, import_react.useCallback)((e) => {
244
+ if (mode !== "designer" || !onMove) return;
245
+ e.preventDefault();
246
+ e.stopPropagation();
247
+ onSelect();
248
+ const pageEl = overlayRef.current?.closest(".pdf-page");
249
+ if (!pageEl) return;
250
+ dragStartRef.current = {
251
+ startX: e.clientX,
252
+ startY: e.clientY,
253
+ fieldX: field.x,
254
+ fieldY: field.y
255
+ };
256
+ const handleMouseMove = (e2) => {
257
+ if (!dragStartRef.current) return;
258
+ const rect = pageEl.getBoundingClientRect();
259
+ const dx = (e2.clientX - dragStartRef.current.startX) / rect.width * 100;
260
+ const dy = (e2.clientY - dragStartRef.current.startY) / rect.height * 100;
261
+ const newX = Math.max(0, Math.min(100 - field.width, dragStartRef.current.fieldX + dx));
262
+ const newY = Math.max(0, Math.min(100 - field.height, dragStartRef.current.fieldY + dy));
263
+ onMove(field.id, field.page, newX, newY);
264
+ };
265
+ const handleMouseUp = () => {
266
+ dragStartRef.current = null;
267
+ window.removeEventListener("mousemove", handleMouseMove);
268
+ window.removeEventListener("mouseup", handleMouseUp);
269
+ };
270
+ window.addEventListener("mousemove", handleMouseMove);
271
+ window.addEventListener("mouseup", handleMouseUp);
272
+ }, [field, mode, onMove, onSelect]);
273
+ const handleResizeMouseDown = (0, import_react.useCallback)((e) => {
274
+ if (mode !== "designer" || !onResize) return;
275
+ e.preventDefault();
276
+ e.stopPropagation();
277
+ const pageEl = overlayRef.current?.closest(".pdf-page");
278
+ if (!pageEl) return;
279
+ resizeStartRef.current = {
280
+ startX: e.clientX,
281
+ startY: e.clientY,
282
+ fieldW: field.width,
283
+ fieldH: field.height
284
+ };
285
+ const handleMouseMove = (e2) => {
286
+ if (!resizeStartRef.current) return;
287
+ const rect = pageEl.getBoundingClientRect();
288
+ const dw = (e2.clientX - resizeStartRef.current.startX) / rect.width * 100;
289
+ const dh = (e2.clientY - resizeStartRef.current.startY) / rect.height * 100;
290
+ const newW = Math.max(2, Math.min(100 - field.x, resizeStartRef.current.fieldW + dw));
291
+ const newH = Math.max(1.5, Math.min(100 - field.y, resizeStartRef.current.fieldH + dh));
292
+ onResize(field.id, newW, newH);
293
+ };
294
+ const handleMouseUp = () => {
295
+ resizeStartRef.current = null;
296
+ window.removeEventListener("mousemove", handleMouseMove);
297
+ window.removeEventListener("mouseup", handleMouseUp);
298
+ };
299
+ window.addEventListener("mousemove", handleMouseMove);
300
+ window.addEventListener("mouseup", handleMouseUp);
301
+ }, [field, mode, onResize]);
302
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
303
+ "div",
304
+ {
305
+ ref: overlayRef,
306
+ className: `field-overlay ${isSelected ? "selected" : ""} ${isEditable ? "editable" : "readonly"} ${isPreFilled ? "prefilled" : ""}`,
307
+ style: {
308
+ left: `${field.x}%`,
309
+ top: `${field.y}%`,
310
+ width: `${field.width}%`,
311
+ height: `${field.height}%`,
312
+ borderColor: color,
313
+ backgroundColor: isSelected ? `${color}22` : `${color}11`,
314
+ cursor: mode === "designer" ? "move" : "default"
315
+ },
316
+ onClick: (e) => {
317
+ e.stopPropagation();
318
+ onSelect();
319
+ },
320
+ onMouseDown: handleMouseDown,
321
+ children: [
322
+ mode === "designer" && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "field-overlay-label", style: { backgroundColor: color }, children: field.label }),
323
+ renderContent ? renderContent(field) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { className: "field-overlay-placeholder", children: field.value || field.placeholder }),
324
+ mode === "designer" && isSelected && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
325
+ "div",
326
+ {
327
+ className: "field-resize-handle",
328
+ onMouseDown: handleResizeMouseDown
329
+ }
330
+ )
331
+ ]
332
+ }
333
+ );
334
+ }
335
+
336
+ // src/components/pdf-builder/FieldPropertyPanel.tsx
337
+ var import_jsx_runtime2 = require("react/jsx-runtime");
338
+ function FieldPropertyPanel({ field, signerRoles, onUpdate, onDelete }) {
339
+ const color = getSignerColor(field.assignee);
340
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "field-property-panel", children: [
341
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-header", children: [
342
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("h3", { style: { color }, children: field.label }),
343
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("button", { onClick: () => onDelete(field.id), className: "panel-delete-btn", children: "Delete" })
344
+ ] }),
345
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
346
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Label" }),
347
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
348
+ "input",
349
+ {
350
+ type: "text",
351
+ value: field.label,
352
+ onChange: (e) => onUpdate(field.id, { label: e.target.value })
353
+ }
354
+ )
355
+ ] }),
356
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
357
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Field Type" }),
358
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
359
+ "select",
360
+ {
361
+ value: field.type,
362
+ onChange: (e) => onUpdate(field.id, { type: e.target.value }),
363
+ children: [
364
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "text", children: "Text" }),
365
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "signature", children: "Signature" }),
366
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "signed-date", children: "Signed Date" }),
367
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "checkbox", children: "Checkbox" }),
368
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "initials", children: "Initials" })
369
+ ]
370
+ }
371
+ )
372
+ ] }),
373
+ field.type === "text" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
374
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Text Type" }),
375
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
376
+ "select",
377
+ {
378
+ value: field.textSubtype || "freeform",
379
+ onChange: (e) => onUpdate(field.id, { textSubtype: e.target.value }),
380
+ children: [
381
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "freeform", children: "Freeform" }),
382
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "number", children: "Number" }),
383
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "date", children: "Date" }),
384
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "email", children: "Email" }),
385
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: "phone", children: "Phone" })
386
+ ]
387
+ }
388
+ )
389
+ ] }),
390
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
391
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Assigned To" }),
392
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
393
+ "select",
394
+ {
395
+ value: field.assignee,
396
+ onChange: (e) => onUpdate(field.id, { assignee: e.target.value }),
397
+ children: signerRoles.map((role) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("option", { value: role, style: { color: getSignerColor(role) }, children: role }, role))
398
+ }
399
+ )
400
+ ] }),
401
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
402
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Placeholder" }),
403
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
404
+ "input",
405
+ {
406
+ type: "text",
407
+ value: field.placeholder,
408
+ onChange: (e) => onUpdate(field.id, { placeholder: e.target.value })
409
+ }
410
+ )
411
+ ] }),
412
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "panel-field", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("label", { className: "panel-checkbox-label", children: [
413
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
414
+ "input",
415
+ {
416
+ type: "checkbox",
417
+ checked: field.required,
418
+ onChange: (e) => onUpdate(field.id, { required: e.target.checked })
419
+ }
420
+ ),
421
+ "Required"
422
+ ] }) }),
423
+ field.type !== "checkbox" && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "panel-field", children: [
424
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("label", { children: "Font Size (pt)" }),
425
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
426
+ "input",
427
+ {
428
+ type: "number",
429
+ min: "6",
430
+ max: "72",
431
+ value: field.fontSize,
432
+ onChange: (e) => onUpdate(field.id, { fontSize: Number(e.target.value) })
433
+ }
434
+ )
435
+ ] }),
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)(
492
+ "button",
493
+ {
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
+ ]
515
+ },
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
+ )
540
+ ] })
541
+ ] });
542
+ }
543
+
544
+ // src/components/pdf-builder/SignatureCanvas.tsx
545
+ var import_react3 = require("react");
546
+ var import_perfect_freehand = require("perfect-freehand");
547
+ var import_jsx_runtime4 = require("react/jsx-runtime");
548
+ function getSvgPathFromStroke(stroke) {
549
+ if (!stroke.length) return "";
550
+ const d = stroke.reduce(
551
+ (acc, [x0, y0], i, arr) => {
552
+ const [x1, y1] = arr[(i + 1) % arr.length];
553
+ acc.push(x0, y0, (x0 + x1) / 2, (y0 + y1) / 2);
554
+ return acc;
555
+ },
556
+ ["M", ...stroke[0], "Q"]
557
+ );
558
+ d.push("Z");
559
+ return d.join(" ");
560
+ }
561
+ function SignatureCanvas({
562
+ width = 400,
563
+ height = 150,
564
+ onSign,
565
+ initialValue,
566
+ className
567
+ }) {
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)(() => {
573
+ if (initialValue && canvasRef.current) {
574
+ const ctx = canvasRef.current.getContext("2d");
575
+ const img = new Image();
576
+ img.onload = () => {
577
+ ctx.clearRect(0, 0, width, height);
578
+ ctx.drawImage(img, 0, 0, width, height);
579
+ };
580
+ img.src = initialValue;
581
+ }
582
+ }, [initialValue, width, height]);
583
+ (0, import_react3.useEffect)(() => {
584
+ const canvas = canvasRef.current;
585
+ if (!canvas) return;
586
+ const ctx = canvas.getContext("2d");
587
+ ctx.clearRect(0, 0, width, height);
588
+ const allPaths = currentPath ? [...paths, currentPath] : paths;
589
+ for (const path of allPaths) {
590
+ const stroke = (0, import_perfect_freehand.getStroke)(path, {
591
+ size: 3,
592
+ thinning: 0.5,
593
+ smoothing: 0.5,
594
+ streamline: 0.5
595
+ });
596
+ const pathStr = getSvgPathFromStroke(stroke);
597
+ const path2d = new Path2D(pathStr);
598
+ ctx.fillStyle = "#000";
599
+ ctx.fill(path2d);
600
+ }
601
+ }, [paths, currentPath, width, height]);
602
+ const getPoint = (0, import_react3.useCallback)((e) => {
603
+ const rect = canvasRef.current.getBoundingClientRect();
604
+ return [
605
+ e.clientX - rect.left,
606
+ e.clientY - rect.top,
607
+ e.pressure
608
+ ];
609
+ }, []);
610
+ const handlePointerDown = (0, import_react3.useCallback)((e) => {
611
+ e.preventDefault();
612
+ e.target.setPointerCapture(e.pointerId);
613
+ setCurrentPath([getPoint(e)]);
614
+ }, [getPoint]);
615
+ const handlePointerMove = (0, import_react3.useCallback)((e) => {
616
+ if (!currentPath) return;
617
+ e.preventDefault();
618
+ setCurrentPath([...currentPath, getPoint(e)]);
619
+ }, [currentPath, getPoint]);
620
+ const handlePointerUp = (0, import_react3.useCallback)(() => {
621
+ if (!currentPath) return;
622
+ setPaths((prev) => [...prev, currentPath]);
623
+ setCurrentPath(null);
624
+ setIsEmpty(false);
625
+ requestAnimationFrame(() => {
626
+ if (canvasRef.current) {
627
+ onSign(canvasRef.current.toDataURL("image/png"));
628
+ }
629
+ });
630
+ }, [currentPath, onSign]);
631
+ const handleClear = (0, import_react3.useCallback)(() => {
632
+ setPaths([]);
633
+ setCurrentPath(null);
634
+ setIsEmpty(true);
635
+ const canvas = canvasRef.current;
636
+ if (canvas) {
637
+ const ctx = canvas.getContext("2d");
638
+ ctx.clearRect(0, 0, width, height);
639
+ onSign("");
640
+ }
641
+ }, [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)(
644
+ "canvas",
645
+ {
646
+ ref: canvasRef,
647
+ width,
648
+ height,
649
+ className: "signature-canvas",
650
+ onPointerDown: handlePointerDown,
651
+ onPointerMove: handlePointerMove,
652
+ onPointerUp: handlePointerUp,
653
+ style: { touchAction: "none" }
654
+ }
655
+ ),
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" })
659
+ ] })
660
+ ] });
661
+ }
662
+
663
+ // src/components/pdf-builder/DesignerView.tsx
664
+ var import_jsx_runtime5 = require("react/jsx-runtime");
665
+ function DesignerView() {
666
+ const [pages, setPages] = (0, import_react4.useState)([]);
667
+ const [fields, setFields] = (0, import_react4.useState)([]);
668
+ const [selectedFieldId, setSelectedFieldId] = (0, import_react4.useState)(null);
669
+ const [signerRoles, setSignerRoles] = (0, import_react4.useState)([...DEFAULT_SIGNER_ROLES]);
670
+ const [activeRole, setActiveRole] = (0, import_react4.useState)("Sender");
671
+ const [activeFieldType, setActiveFieldType] = (0, import_react4.useState)("text");
672
+ const [loading, setLoading] = (0, import_react4.useState)(false);
673
+ const [pdfSource, setPdfSource] = (0, import_react4.useState)(null);
674
+ (0, import_react4.useEffect)(() => {
675
+ const params = new URLSearchParams(window.location.search);
676
+ const pdfUrl = params.get("pdf");
677
+ if (pdfUrl) {
678
+ loadPdf(pdfUrl);
679
+ }
680
+ const handleMessage = (e) => {
681
+ if (e.data?.type === "load-pdf" && e.data.url) {
682
+ loadPdf(e.data.url);
683
+ }
684
+ if (e.data?.type === "load-template") {
685
+ if (e.data.fields) setFields(e.data.fields);
686
+ if (e.data.signerRoles) setSignerRoles(e.data.signerRoles);
687
+ if (e.data.pdfUrl) loadPdf(e.data.pdfUrl);
688
+ }
689
+ };
690
+ window.addEventListener("message", handleMessage);
691
+ return () => window.removeEventListener("message", handleMessage);
692
+ }, []);
693
+ const loadPdf = (0, import_react4.useCallback)(async (source) => {
694
+ setLoading(true);
695
+ try {
696
+ const rendered = await renderPdfPages(source);
697
+ setPages(rendered);
698
+ setPdfSource(source);
699
+ } catch (err) {
700
+ console.error("Failed to load PDF:", err);
701
+ } finally {
702
+ setLoading(false);
703
+ }
704
+ }, []);
705
+ const handleFileUpload = (0, import_react4.useCallback)((e) => {
706
+ const file = e.target.files?.[0];
707
+ if (!file) return;
708
+ const reader = new FileReader();
709
+ reader.onload = () => {
710
+ if (reader.result) loadPdf(reader.result);
711
+ };
712
+ reader.readAsArrayBuffer(file);
713
+ }, [loadPdf]);
714
+ const handlePageClick = (0, import_react4.useCallback)((page, x, y) => {
715
+ const field = createField(activeFieldType, activeRole, page, x, y);
716
+ setFields((prev) => [...prev, field]);
717
+ setSelectedFieldId(field.id);
718
+ }, [activeFieldType, activeRole]);
719
+ const handleFieldMove = (0, import_react4.useCallback)((id, page, x, y) => {
720
+ setFields((prev) => prev.map((f) => f.id === id ? { ...f, page, x, y } : f));
721
+ }, []);
722
+ const handleFieldResize = (0, import_react4.useCallback)((id, width, height) => {
723
+ setFields((prev) => prev.map((f) => f.id === id ? { ...f, width, height } : f));
724
+ }, []);
725
+ const handleFieldUpdate = (0, import_react4.useCallback)((id, updates) => {
726
+ setFields((prev) => prev.map((f) => f.id === id ? { ...f, ...updates } : f));
727
+ }, []);
728
+ const handleFieldDelete = (0, import_react4.useCallback)((id) => {
729
+ setFields((prev) => prev.filter((f) => f.id !== id));
730
+ if (selectedFieldId === id) setSelectedFieldId(null);
731
+ }, [selectedFieldId]);
732
+ const handleAddRole = (0, import_react4.useCallback)((role) => {
733
+ if (!signerRoles.includes(role)) {
734
+ setSignerRoles((prev) => [...prev, role]);
735
+ }
736
+ }, [signerRoles]);
737
+ const handleRemoveRole = (0, import_react4.useCallback)((role) => {
738
+ setSignerRoles((prev) => prev.filter((r) => r !== role));
739
+ setFields((prev) => prev.map((f) => f.assignee === role ? { ...f, assignee: signerRoles[0] } : f));
740
+ if (activeRole === role) setActiveRole(signerRoles[0]);
741
+ }, [signerRoles, activeRole]);
742
+ const handleExport = (0, import_react4.useCallback)(() => {
743
+ const template = {
744
+ fields,
745
+ signerRoles,
746
+ pdfUrl: typeof pdfSource === "string" ? pdfSource : ""
747
+ };
748
+ const json = JSON.stringify(template, null, 2);
749
+ const blob = new Blob([json], { type: "application/json" });
750
+ const url = URL.createObjectURL(blob);
751
+ const a = document.createElement("a");
752
+ a.href = url;
753
+ a.download = "template.json";
754
+ a.click();
755
+ URL.revokeObjectURL(url);
756
+ window.parent?.postMessage({ type: "template-saved", template }, "*");
757
+ }, [fields, signerRoles, pdfSource]);
758
+ const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
759
+ const renderFieldContent = (0, import_react4.useCallback)((field) => {
760
+ if (field.type === "signature" || field.type === "initials") {
761
+ if (field.value) {
762
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("img", { src: field.value, alt: field.label, className: "field-signature-preview" });
763
+ }
764
+ }
765
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "field-overlay-placeholder", children: field.value || field.placeholder });
766
+ }, []);
767
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "designer-layout", children: [
768
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "designer-toolbar", children: [
769
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "toolbar-left", children: [
770
+ !pages.length && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "upload-btn", children: [
771
+ "Upload PDF",
772
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
773
+ ] }),
774
+ pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
775
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "upload-btn upload-btn-small", children: [
776
+ "Change PDF",
777
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
778
+ ] }),
779
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "field-type-selector", children: ["text", "signature", "signed-date", "checkbox", "initials"].map((type) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
780
+ "button",
781
+ {
782
+ className: `field-type-btn ${activeFieldType === type ? "active" : ""}`,
783
+ onClick: () => setActiveFieldType(type),
784
+ children: type === "text" ? "Text" : type === "signature" ? "Signature" : type === "signed-date" ? "Date" : type === "checkbox" ? "Checkbox" : "Initials"
785
+ },
786
+ type
787
+ )) })
788
+ ] })
789
+ ] }),
790
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "toolbar-right", children: pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("button", { onClick: handleExport, className: "export-btn", children: "Export Template" }) })
791
+ ] }),
792
+ pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
793
+ SignerRoleSelector,
794
+ {
795
+ roles: signerRoles,
796
+ activeRole,
797
+ onSetActiveRole: setActiveRole,
798
+ onAddRole: handleAddRole,
799
+ onRemoveRole: handleRemoveRole
800
+ }
801
+ ),
802
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "designer-content", children: [
803
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "designer-pdf-area", children: [
804
+ loading && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "loading-indicator", children: "Loading PDF..." }),
805
+ !pages.length && !loading && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "empty-state", children: [
806
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h2", { children: "Upload a PDF to get started" }),
807
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("p", { children: "Upload a template PDF, then click on the page to place form fields." }),
808
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "upload-btn upload-btn-large", children: [
809
+ "Choose PDF File",
810
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("input", { type: "file", accept: ".pdf", onChange: handleFileUpload, hidden: true })
811
+ ] })
812
+ ] }),
813
+ pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
814
+ PdfViewer,
815
+ {
816
+ pages,
817
+ fields,
818
+ selectedFieldId,
819
+ onSelectField: setSelectedFieldId,
820
+ onFieldMove: handleFieldMove,
821
+ onFieldResize: handleFieldResize,
822
+ onPageClick: handlePageClick,
823
+ mode: "designer",
824
+ renderFieldContent
825
+ }
826
+ )
827
+ ] }),
828
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "designer-panel", children: [
829
+ selectedField ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
830
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
831
+ FieldPropertyPanel,
832
+ {
833
+ field: selectedField,
834
+ signerRoles,
835
+ onUpdate: handleFieldUpdate,
836
+ onDelete: handleFieldDelete
837
+ }
838
+ ),
839
+ selectedField.assignee === activeRole && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "prefill-section", children: [
840
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("h4", { children: "Pre-fill Value" }),
841
+ selectedField.type === "signature" || selectedField.type === "initials" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
842
+ SignatureCanvas,
843
+ {
844
+ width: 300,
845
+ height: selectedField.type === "initials" ? 100 : 150,
846
+ onSign: (dataUrl) => handleFieldUpdate(selectedField.id, { value: dataUrl }),
847
+ initialValue: selectedField.value
848
+ }
849
+ ) : selectedField.type === "checkbox" ? /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("label", { className: "panel-checkbox-label", children: [
850
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
851
+ "input",
852
+ {
853
+ type: "checkbox",
854
+ checked: selectedField.value === "true",
855
+ onChange: (e) => handleFieldUpdate(selectedField.id, { value: e.target.checked ? "true" : "" })
856
+ }
857
+ ),
858
+ "Checked"
859
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
860
+ "input",
861
+ {
862
+ type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
863
+ value: selectedField.value,
864
+ onChange: (e) => handleFieldUpdate(selectedField.id, { value: e.target.value }),
865
+ placeholder: `Pre-fill ${selectedField.label}`,
866
+ className: "prefill-input"
867
+ }
868
+ )
869
+ ] })
870
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("div", { className: "panel-empty", children: pages.length > 0 ? "Click on the PDF to place a field, or select an existing field to edit its properties." : "Upload a PDF to begin designing your template." }),
871
+ fields.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "field-list", children: [
872
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("h4", { children: [
873
+ "All Fields (",
874
+ fields.length,
875
+ ")"
876
+ ] }),
877
+ fields.map((f, i) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
878
+ "div",
879
+ {
880
+ className: `field-list-item ${f.id === selectedFieldId ? "active" : ""}`,
881
+ onClick: () => setSelectedFieldId(f.id),
882
+ children: [
883
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
884
+ "span",
885
+ {
886
+ className: "field-list-dot",
887
+ style: { backgroundColor: getSignerColor(f.assignee) }
888
+ }
889
+ ),
890
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "field-list-name", children: f.label }),
891
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("span", { className: "field-list-page", children: [
892
+ "p",
893
+ f.page + 1
894
+ ] }),
895
+ f.required && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "field-list-required", children: "*" })
896
+ ]
897
+ },
898
+ f.id
899
+ ))
900
+ ] })
901
+ ] })
902
+ ] }),
903
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "powered-by", children: [
904
+ "Powered by ",
905
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
906
+ ] })
907
+ ] });
908
+ }
909
+
910
+ // src/components/pdf-builder/SignerView.tsx
911
+ var import_react5 = require("react");
912
+
913
+ // src/utils/pdfFiller.ts
914
+ var import_pdf_lib = require("pdf-lib");
915
+ async function generateFilledPdf(pdfSource, fields) {
916
+ let pdfBytes;
917
+ if (typeof pdfSource === "string") {
918
+ const res = await fetch(pdfSource);
919
+ pdfBytes = await res.arrayBuffer();
920
+ } else {
921
+ pdfBytes = pdfSource;
922
+ }
923
+ const pdfDoc = await import_pdf_lib.PDFDocument.load(pdfBytes);
924
+ const font = await pdfDoc.embedFont(import_pdf_lib.StandardFonts.Helvetica);
925
+ const pages = pdfDoc.getPages();
926
+ for (const field of fields) {
927
+ if (!field.value) continue;
928
+ const page = pages[field.page];
929
+ if (!page) continue;
930
+ const pageWidth = page.getWidth();
931
+ const pageHeight = page.getHeight();
932
+ const x = field.x / 100 * pageWidth;
933
+ const y = pageHeight - field.y / 100 * pageHeight - field.height / 100 * pageHeight;
934
+ const w = field.width / 100 * pageWidth;
935
+ const h = field.height / 100 * pageHeight;
936
+ if (field.type === "checkbox") {
937
+ if (field.value === "true") {
938
+ const inset = Math.min(w, h) * 0.2;
939
+ const lineWidth = Math.min(w, h) * 0.08;
940
+ page.drawLine({
941
+ start: { x: x + inset, y: y + inset },
942
+ end: { x: x + w - inset, y: y + h - inset },
943
+ thickness: lineWidth,
944
+ color: (0, import_pdf_lib.rgb)(0, 0, 0)
945
+ });
946
+ page.drawLine({
947
+ start: { x: x + w - inset, y: y + inset },
948
+ end: { x: x + inset, y: y + h - inset },
949
+ thickness: lineWidth,
950
+ color: (0, import_pdf_lib.rgb)(0, 0, 0)
951
+ });
952
+ }
953
+ } else if (field.type === "signature" || field.type === "initials") {
954
+ if (field.value.startsWith("data:image/png")) {
955
+ const pngBytes = Uint8Array.from(
956
+ atob(field.value.split(",")[1]),
957
+ (c) => c.charCodeAt(0)
958
+ );
959
+ const pngImage = await pdfDoc.embedPng(pngBytes);
960
+ page.drawImage(pngImage, {
961
+ x,
962
+ y,
963
+ width: w,
964
+ height: h
965
+ });
966
+ }
967
+ } else {
968
+ const fontSize = Math.min(field.fontSize, h * 0.7);
969
+ page.drawText(field.value, {
970
+ x: x + 2,
971
+ y: y + h * 0.3,
972
+ size: fontSize,
973
+ font,
974
+ color: (0, import_pdf_lib.rgb)(0, 0, 0),
975
+ maxWidth: w - 4
976
+ });
977
+ }
978
+ }
979
+ return pdfDoc.save();
980
+ }
981
+ function downloadPdf(bytes, filename) {
982
+ const blob = new Blob([bytes.slice().buffer], { type: "application/pdf" });
983
+ const url = URL.createObjectURL(blob);
984
+ const a = document.createElement("a");
985
+ a.href = url;
986
+ a.download = filename;
987
+ a.click();
988
+ URL.revokeObjectURL(url);
989
+ }
990
+ async function postPdfToCallback(bytes, callbackUrl, filename) {
991
+ const blob = new Blob([bytes.slice().buffer], { type: "application/pdf" });
992
+ const formData = new FormData();
993
+ formData.append("file", blob, filename);
994
+ await fetch(callbackUrl, {
995
+ method: "POST",
996
+ body: formData
997
+ });
998
+ }
999
+
1000
+ // src/components/pdf-builder/FieldNavigator.tsx
1001
+ var import_jsx_runtime6 = require("react/jsx-runtime");
1002
+ function FieldNavigator({
1003
+ fields,
1004
+ currentFieldId,
1005
+ onNavigate,
1006
+ allRequiredFilled,
1007
+ onSubmit
1008
+ }) {
1009
+ const currentIndex = fields.findIndex((f) => f.id === currentFieldId);
1010
+ const hasPrev = currentIndex > 0;
1011
+ const hasNext = currentIndex < fields.length - 1;
1012
+ const handlePrev = () => {
1013
+ if (hasPrev) {
1014
+ onNavigate(fields[currentIndex - 1].id);
1015
+ }
1016
+ };
1017
+ const handleNext = () => {
1018
+ if (hasNext) {
1019
+ onNavigate(fields[currentIndex + 1].id);
1020
+ }
1021
+ };
1022
+ const filledCount = fields.filter((f) => {
1023
+ if (!f.required) return true;
1024
+ if (f.type === "checkbox") return true;
1025
+ return !!f.value;
1026
+ }).length;
1027
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "field-navigator", children: [
1028
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "field-navigator-progress", children: [
1029
+ filledCount,
1030
+ " of ",
1031
+ fields.length,
1032
+ " fields completed"
1033
+ ] }),
1034
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "field-navigator-controls", children: [
1035
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1036
+ "button",
1037
+ {
1038
+ onClick: handlePrev,
1039
+ disabled: !hasPrev,
1040
+ className: "nav-btn",
1041
+ children: "Prev"
1042
+ }
1043
+ ),
1044
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "field-navigator-position", children: currentIndex >= 0 ? `${currentIndex + 1} / ${fields.length}` : "-" }),
1045
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1046
+ "button",
1047
+ {
1048
+ onClick: handleNext,
1049
+ disabled: !hasNext,
1050
+ className: "nav-btn",
1051
+ children: "Next"
1052
+ }
1053
+ )
1054
+ ] }),
1055
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1056
+ "button",
1057
+ {
1058
+ onClick: onSubmit,
1059
+ disabled: !allRequiredFilled,
1060
+ className: "submit-btn",
1061
+ children: "Complete & Download"
1062
+ }
1063
+ )
1064
+ ] });
1065
+ }
1066
+
1067
+ // src/components/pdf-builder/SignerView.tsx
1068
+ var import_jsx_runtime7 = require("react/jsx-runtime");
1069
+ function SignerView({
1070
+ initialPdfUrl,
1071
+ initialTemplate,
1072
+ initialSigner,
1073
+ callbackUrl: initialCallbackUrl,
1074
+ onComplete
1075
+ } = {}) {
1076
+ const [pages, setPages] = (0, import_react5.useState)([]);
1077
+ const [fields, setFields] = (0, import_react5.useState)([]);
1078
+ const [selectedFieldId, setSelectedFieldId] = (0, import_react5.useState)(null);
1079
+ const [signer, setSigner] = (0, import_react5.useState)(initialSigner || "Signer 1");
1080
+ const [loading, setLoading] = (0, import_react5.useState)(false);
1081
+ const [submitting, setSubmitting] = (0, import_react5.useState)(false);
1082
+ const [pdfSource, setPdfSource] = (0, import_react5.useState)(null);
1083
+ const [callbackUrl, setCallbackUrl] = (0, import_react5.useState)(initialCallbackUrl || "");
1084
+ const containerRef = (0, import_react5.useRef)(null);
1085
+ (0, import_react5.useEffect)(() => {
1086
+ if (initialTemplate) {
1087
+ setFields(initialTemplate.fields);
1088
+ if (initialPdfUrl) loadPdf(initialPdfUrl);
1089
+ return;
1090
+ }
1091
+ const params = new URLSearchParams(window.location.search);
1092
+ const pdfUrl = params.get("pdf");
1093
+ const fieldsUrl = params.get("fields");
1094
+ const signerParam = params.get("signer");
1095
+ const cbUrl = params.get("callbackUrl");
1096
+ if (signerParam) setSigner(signerParam);
1097
+ if (cbUrl) setCallbackUrl(cbUrl);
1098
+ if (pdfUrl) loadPdf(pdfUrl);
1099
+ if (fieldsUrl) {
1100
+ fetch(fieldsUrl).then((r) => r.json()).then((template) => setFields(template.fields)).catch((err) => console.error("Failed to load fields:", err));
1101
+ }
1102
+ const handleMessage = (e) => {
1103
+ if (e.data?.type === "load-signing") {
1104
+ if (e.data.fields) setFields(e.data.fields);
1105
+ if (e.data.pdfUrl) loadPdf(e.data.pdfUrl);
1106
+ if (e.data.signer) setSigner(e.data.signer);
1107
+ if (e.data.callbackUrl) setCallbackUrl(e.data.callbackUrl);
1108
+ }
1109
+ };
1110
+ window.addEventListener("message", handleMessage);
1111
+ return () => window.removeEventListener("message", handleMessage);
1112
+ }, [initialTemplate, initialPdfUrl]);
1113
+ const loadPdf = (0, import_react5.useCallback)(async (source) => {
1114
+ setLoading(true);
1115
+ try {
1116
+ const rendered = await renderPdfPages(source);
1117
+ setPages(rendered);
1118
+ setPdfSource(source);
1119
+ } catch (err) {
1120
+ console.error("Failed to load PDF:", err);
1121
+ } finally {
1122
+ setLoading(false);
1123
+ }
1124
+ }, []);
1125
+ const editableFields = fields.filter((f) => f.assignee === signer);
1126
+ const selectedField = fields.find((f) => f.id === selectedFieldId) || null;
1127
+ const isFieldEditable = selectedField ? selectedField.assignee === signer : false;
1128
+ const handleFieldUpdate = (0, import_react5.useCallback)((id, value) => {
1129
+ setFields((prev) => prev.map((f) => f.id === id ? { ...f, value } : f));
1130
+ }, []);
1131
+ const handleNavigate = (0, import_react5.useCallback)((fieldId) => {
1132
+ setSelectedFieldId(fieldId);
1133
+ const field = fields.find((f) => f.id === fieldId);
1134
+ if (field && containerRef.current) {
1135
+ const pageEl = containerRef.current.querySelector(`[data-page="${field.page}"]`);
1136
+ if (pageEl) {
1137
+ pageEl.scrollIntoView({ behavior: "smooth", block: "center" });
1138
+ }
1139
+ }
1140
+ }, [fields]);
1141
+ const allRequiredFilled = editableFields.every((f) => {
1142
+ if (!f.required) return true;
1143
+ if (f.type === "checkbox") return true;
1144
+ return !!f.value;
1145
+ });
1146
+ const handleSubmit = (0, import_react5.useCallback)(async () => {
1147
+ if (!pdfSource || !allRequiredFilled) return;
1148
+ setSubmitting(true);
1149
+ try {
1150
+ const pdfBytes = await generateFilledPdf(pdfSource, fields);
1151
+ const blob = new Blob([pdfBytes.slice().buffer], { type: "application/pdf" });
1152
+ downloadPdf(pdfBytes, "signed-document.pdf");
1153
+ if (callbackUrl) {
1154
+ await postPdfToCallback(pdfBytes, callbackUrl, "signed-document.pdf");
1155
+ }
1156
+ if (onComplete) {
1157
+ onComplete(blob);
1158
+ }
1159
+ window.parent?.postMessage({ type: "signing-complete" }, "*");
1160
+ } catch (err) {
1161
+ console.error("Failed to generate PDF:", err);
1162
+ } finally {
1163
+ setSubmitting(false);
1164
+ }
1165
+ }, [pdfSource, fields, callbackUrl, allRequiredFilled, onComplete]);
1166
+ const renderFieldContent = (0, import_react5.useCallback)((field) => {
1167
+ const editable = field.assignee === signer;
1168
+ if (!editable) {
1169
+ if (field.type === "signature" || field.type === "initials") {
1170
+ return field.value ? /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("img", { src: field.value, alt: field.label, className: "field-signature-preview" }) : /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "field-overlay-placeholder readonly", children: field.placeholder });
1171
+ }
1172
+ if (field.type === "checkbox") {
1173
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "field-checkbox-display readonly", children: field.value === "true" ? "\u2713" : "" });
1174
+ }
1175
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "field-overlay-placeholder readonly", children: field.value || field.placeholder });
1176
+ }
1177
+ if (field.type === "signature" || field.type === "initials") {
1178
+ if (field.value) {
1179
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "field-signature-filled", onClick: () => handleFieldUpdate(field.id, ""), children: /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("img", { src: field.value, alt: field.label, className: "field-signature-preview" }) });
1180
+ }
1181
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "field-overlay-placeholder editable", children: field.placeholder });
1182
+ }
1183
+ if (field.type === "checkbox") {
1184
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1185
+ "div",
1186
+ {
1187
+ className: "field-checkbox-display editable",
1188
+ onClick: (e) => {
1189
+ e.stopPropagation();
1190
+ handleFieldUpdate(field.id, field.value === "true" ? "" : "true");
1191
+ },
1192
+ children: field.value === "true" ? "\u2713" : ""
1193
+ }
1194
+ );
1195
+ }
1196
+ if (field.type === "signed-date") {
1197
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "field-overlay-value", children: field.value || (/* @__PURE__ */ new Date()).toLocaleDateString() });
1198
+ }
1199
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1200
+ "input",
1201
+ {
1202
+ type: field.textSubtype === "email" ? "email" : field.textSubtype === "number" ? "number" : field.textSubtype === "phone" ? "tel" : field.textSubtype === "date" ? "date" : "text",
1203
+ className: "field-inline-input",
1204
+ value: field.value,
1205
+ placeholder: field.placeholder,
1206
+ onChange: (e) => handleFieldUpdate(field.id, e.target.value),
1207
+ onClick: (e) => e.stopPropagation(),
1208
+ style: { fontSize: `${field.fontSize * 0.6}px` }
1209
+ }
1210
+ );
1211
+ }, [signer, handleFieldUpdate]);
1212
+ (0, import_react5.useEffect)(() => {
1213
+ const sigFields = fields.filter((f) => f.assignee === signer && f.type === "signature" && f.value);
1214
+ if (sigFields.length > 0) {
1215
+ const dateStr = (/* @__PURE__ */ new Date()).toLocaleDateString();
1216
+ setFields((prev) => prev.map((f) => {
1217
+ if (f.assignee === signer && f.type === "signed-date" && !f.value) {
1218
+ return { ...f, value: dateStr };
1219
+ }
1220
+ return f;
1221
+ }));
1222
+ }
1223
+ }, [fields.filter((f) => f.type === "signature" && f.value).length]);
1224
+ return /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "signer-layout", ref: containerRef, children: [
1225
+ loading && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "loading-indicator", children: "Loading document..." }),
1226
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "signer-content", children: [
1227
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "signer-pdf-area", children: pages.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1228
+ PdfViewer,
1229
+ {
1230
+ pages,
1231
+ fields,
1232
+ selectedFieldId,
1233
+ onSelectField: setSelectedFieldId,
1234
+ mode: "signer",
1235
+ currentSigner: signer,
1236
+ renderFieldContent
1237
+ }
1238
+ ) }),
1239
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "signer-panel", children: [
1240
+ selectedField && isFieldEditable && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "signer-field-input", children: [
1241
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { children: selectedField.label }),
1242
+ selectedField.required && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("span", { className: "required-badge", children: "Required" }),
1243
+ (selectedField.type === "signature" || selectedField.type === "initials") && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1244
+ SignatureCanvas,
1245
+ {
1246
+ width: 280,
1247
+ height: selectedField.type === "initials" ? 80 : 120,
1248
+ onSign: (dataUrl) => handleFieldUpdate(selectedField.id, dataUrl),
1249
+ initialValue: selectedField.value
1250
+ }
1251
+ ),
1252
+ selectedField.type === "text" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1253
+ "input",
1254
+ {
1255
+ type: selectedField.textSubtype === "email" ? "email" : selectedField.textSubtype === "number" ? "number" : selectedField.textSubtype === "phone" ? "tel" : selectedField.textSubtype === "date" ? "date" : "text",
1256
+ value: selectedField.value,
1257
+ onChange: (e) => handleFieldUpdate(selectedField.id, e.target.value),
1258
+ placeholder: selectedField.placeholder,
1259
+ className: "signer-text-input"
1260
+ }
1261
+ ),
1262
+ selectedField.type === "checkbox" && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("label", { className: "signer-checkbox-label", children: [
1263
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1264
+ "input",
1265
+ {
1266
+ type: "checkbox",
1267
+ checked: selectedField.value === "true",
1268
+ onChange: (e) => handleFieldUpdate(selectedField.id, e.target.checked ? "true" : "")
1269
+ }
1270
+ ),
1271
+ selectedField.placeholder || "Check this box"
1272
+ ] }),
1273
+ selectedField.type === "signed-date" && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "signer-date-display", children: selectedField.value || "Will auto-fill when you sign" })
1274
+ ] }),
1275
+ selectedField && !isFieldEditable && /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "signer-field-readonly", children: [
1276
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("h3", { children: selectedField.label }),
1277
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("p", { children: "This field belongs to another signer." })
1278
+ ] }),
1279
+ !selectedField && editableFields.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "panel-empty", children: "Select a field to fill it in, or use the navigation below." }),
1280
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)(
1281
+ FieldNavigator,
1282
+ {
1283
+ fields: editableFields,
1284
+ currentFieldId: selectedFieldId,
1285
+ onNavigate: handleNavigate,
1286
+ allRequiredFilled,
1287
+ onSubmit: handleSubmit
1288
+ }
1289
+ ),
1290
+ submitting && /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("div", { className: "loading-indicator", children: "Generating PDF..." })
1291
+ ] })
1292
+ ] }),
1293
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsxs)("div", { className: "powered-by", children: [
1294
+ "Powered by ",
1295
+ /* @__PURE__ */ (0, import_jsx_runtime7.jsx)("a", { href: "https://exeq.org", target: "_blank", rel: "noopener noreferrer", children: "Exeq.org" })
1296
+ ] })
1297
+ ] });
1298
+ }
1299
+ // Annotate the CommonJS export names for ESM import in node:
1300
+ 0 && (module.exports = {
1301
+ DEFAULT_SIGNER_ROLES,
1302
+ DesignerView,
1303
+ FIELD_DEFAULTS,
1304
+ FieldNavigator,
1305
+ FieldPropertyPanel,
1306
+ PdfViewer,
1307
+ SIGNER_ROLE_COLORS,
1308
+ SignatureCanvas,
1309
+ SignerRoleSelector,
1310
+ SignerView,
1311
+ createField,
1312
+ downloadPdf,
1313
+ generateFilledPdf,
1314
+ getSignerColor,
1315
+ postPdfToCallback,
1316
+ renderPdfPages
1317
+ });
1318
+ //# sourceMappingURL=index.js.map