@josephomills/esign 0.2.2 → 0.4.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.
@@ -0,0 +1,487 @@
1
+ "use client";
2
+ 'use strict';
3
+
4
+ var SignaturePadLib = require('signature_pad');
5
+ var react = require('react');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+
8
+ function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
9
+
10
+ var SignaturePadLib__default = /*#__PURE__*/_interopDefault(SignaturePadLib);
11
+
12
+ // src/ui/SignaturePad.tsx
13
+ function SignaturePad({ primaryColor = "#4f46e5", onChange }) {
14
+ const [mode, setMode] = react.useState("draw");
15
+ const [typed, setTyped] = react.useState("");
16
+ const canvasRef = react.useRef(null);
17
+ const padRef = react.useRef(null);
18
+ react.useEffect(() => {
19
+ if (mode !== "draw") return;
20
+ const canvas = canvasRef.current;
21
+ if (!canvas) return;
22
+ const pad = new SignaturePadLib__default.default(canvas, { penColor: "#111827", minWidth: 0.8, maxWidth: 2.2 });
23
+ padRef.current = pad;
24
+ const resize = () => {
25
+ const ratio = Math.max(window.devicePixelRatio || 1, 1);
26
+ const rect = canvas.getBoundingClientRect();
27
+ canvas.width = Math.floor(rect.width * ratio);
28
+ canvas.height = Math.floor(rect.height * ratio);
29
+ canvas.getContext("2d")?.scale(ratio, ratio);
30
+ pad.clear();
31
+ onChange(null);
32
+ };
33
+ pad.addEventListener(
34
+ "endStroke",
35
+ () => onChange(pad.isEmpty() ? null : pad.toDataURL("image/png"))
36
+ );
37
+ resize();
38
+ window.addEventListener("resize", resize);
39
+ return () => {
40
+ window.removeEventListener("resize", resize);
41
+ pad.off();
42
+ padRef.current = null;
43
+ };
44
+ }, [mode, onChange]);
45
+ react.useEffect(() => {
46
+ if (mode !== "type") return;
47
+ const name = typed.trim();
48
+ if (!name) {
49
+ onChange(null);
50
+ return;
51
+ }
52
+ const c = document.createElement("canvas");
53
+ c.width = 720;
54
+ c.height = 200;
55
+ const ctx = c.getContext("2d");
56
+ if (!ctx) return;
57
+ ctx.fillStyle = "#111827";
58
+ ctx.font = "72px 'Segoe Script', 'Brush Script MT', cursive";
59
+ ctx.textBaseline = "middle";
60
+ ctx.fillText(name, 24, 110);
61
+ onChange(c.toDataURL("image/png"));
62
+ }, [mode, typed, onChange]);
63
+ const tab = (m, label) => /* @__PURE__ */ jsxRuntime.jsx(
64
+ "button",
65
+ {
66
+ type: "button",
67
+ onClick: () => {
68
+ setMode(m);
69
+ onChange(null);
70
+ },
71
+ style: {
72
+ flex: 1,
73
+ padding: "8px 12px",
74
+ border: "none",
75
+ borderBottom: mode === m ? `2px solid ${primaryColor}` : "2px solid transparent",
76
+ background: "transparent",
77
+ fontWeight: mode === m ? 600 : 400,
78
+ color: mode === m ? primaryColor : "#6b7280",
79
+ cursor: "pointer"
80
+ },
81
+ children: label
82
+ }
83
+ );
84
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { border: "1px solid #e5e7eb", borderRadius: 12, overflow: "hidden" }, children: [
85
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", borderBottom: "1px solid #f3f4f6" }, children: [
86
+ tab("draw", "Draw"),
87
+ tab("type", "Type")
88
+ ] }),
89
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { position: "relative", background: "#fff" }, children: [
90
+ mode === "draw" ? /* @__PURE__ */ jsxRuntime.jsx(
91
+ "canvas",
92
+ {
93
+ ref: canvasRef,
94
+ style: { width: "100%", height: 180, touchAction: "none", display: "block" }
95
+ }
96
+ ) : /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: 16 }, children: /* @__PURE__ */ jsxRuntime.jsx(
97
+ "input",
98
+ {
99
+ value: typed,
100
+ onChange: (e) => setTyped(e.target.value),
101
+ placeholder: "Type your name",
102
+ style: {
103
+ width: "100%",
104
+ fontSize: 40,
105
+ fontFamily: "'Segoe Script', 'Brush Script MT', cursive",
106
+ border: "none",
107
+ borderBottom: "1px solid #e5e7eb",
108
+ outline: "none",
109
+ padding: "8px 0"
110
+ }
111
+ }
112
+ ) }),
113
+ /* @__PURE__ */ jsxRuntime.jsx(
114
+ "button",
115
+ {
116
+ type: "button",
117
+ onClick: () => {
118
+ padRef.current?.clear();
119
+ setTyped("");
120
+ onChange(null);
121
+ },
122
+ style: {
123
+ position: "absolute",
124
+ top: 8,
125
+ right: 8,
126
+ fontSize: 12,
127
+ color: "#6b7280",
128
+ background: "rgba(255,255,255,0.8)",
129
+ border: "1px solid #e5e7eb",
130
+ borderRadius: 6,
131
+ padding: "2px 8px",
132
+ cursor: "pointer"
133
+ },
134
+ children: "Clear"
135
+ }
136
+ )
137
+ ] })
138
+ ] });
139
+ }
140
+ function ConsentBlock({ value, onChange, primaryColor = "#4f46e5" }) {
141
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 12 }, children: [
142
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", flexDirection: "column", gap: 4 }, children: [
143
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 13, fontWeight: 600, color: "#374151" }, children: "Full legal name" }),
144
+ /* @__PURE__ */ jsxRuntime.jsx(
145
+ "input",
146
+ {
147
+ value: value.signerName,
148
+ onChange: (e) => onChange({ ...value, signerName: e.target.value }),
149
+ placeholder: "e.g. Ada Lovelace",
150
+ style: {
151
+ padding: "10px 12px",
152
+ border: "1px solid #d1d5db",
153
+ borderRadius: 8,
154
+ fontSize: 15,
155
+ outline: "none"
156
+ }
157
+ }
158
+ )
159
+ ] }),
160
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { display: "flex", gap: 10, alignItems: "flex-start", cursor: "pointer" }, children: [
161
+ /* @__PURE__ */ jsxRuntime.jsx(
162
+ "input",
163
+ {
164
+ type: "checkbox",
165
+ checked: value.consent,
166
+ onChange: (e) => onChange({ ...value, consent: e.target.checked }),
167
+ style: { marginTop: 3, accentColor: primaryColor, width: 16, height: 16 }
168
+ }
169
+ ),
170
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 13, color: "#4b5563", lineHeight: 1.5 }, children: "I agree to sign this document electronically. I understand my electronic signature is legally binding and that an audit record (time, IP, and a tamper-evident hash) is kept with the signed document." })
171
+ ] })
172
+ ] });
173
+ }
174
+ function PdfViewer({ url, placement, workerSrc, primaryColor = "#4f46e5" }) {
175
+ const containerRef = react.useRef(null);
176
+ const [failed, setFailed] = react.useState(false);
177
+ react.useEffect(() => {
178
+ let cancelled = false;
179
+ void (async () => {
180
+ try {
181
+ const pdfjs = await import('pdfjs-dist');
182
+ pdfjs.GlobalWorkerOptions.workerSrc = workerSrc ?? `https://unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;
183
+ const doc = await pdfjs.getDocument({ url }).promise;
184
+ const container = containerRef.current;
185
+ if (!container || cancelled) return;
186
+ container.innerHTML = "";
187
+ const width = container.clientWidth || 640;
188
+ const ratio = Math.max(window.devicePixelRatio || 1, 1);
189
+ for (let p = 1; p <= doc.numPages; p++) {
190
+ const page = await doc.getPage(p);
191
+ if (cancelled) return;
192
+ const base = page.getViewport({ scale: 1 });
193
+ const viewport = page.getViewport({ scale: width / base.width });
194
+ const canvas = document.createElement("canvas");
195
+ canvas.width = Math.floor(viewport.width * ratio);
196
+ canvas.height = Math.floor(viewport.height * ratio);
197
+ canvas.style.width = "100%";
198
+ canvas.style.display = "block";
199
+ canvas.style.borderRadius = "4px";
200
+ const wrap = document.createElement("div");
201
+ wrap.style.position = "relative";
202
+ wrap.style.marginBottom = "14px";
203
+ wrap.style.boxShadow = "0 1px 4px rgba(0,0,0,0.12)";
204
+ wrap.appendChild(canvas);
205
+ if (placement && placement.page === p) {
206
+ const box = document.createElement("div");
207
+ box.style.cssText = `position:absolute;left:${placement.x * 100}%;top:${placement.y * 100}%;width:${placement.w * 100}%;height:${placement.h * 100}%;border:2px dashed ${primaryColor};background:${primaryColor}1a;border-radius:4px;pointer-events:none;`;
208
+ const label = document.createElement("span");
209
+ label.textContent = "Signature";
210
+ label.style.cssText = `position:absolute;top:-18px;left:0;font-size:11px;font-weight:600;color:${primaryColor};`;
211
+ box.appendChild(label);
212
+ wrap.appendChild(box);
213
+ }
214
+ container.appendChild(wrap);
215
+ const ctx = canvas.getContext("2d");
216
+ if (!ctx) continue;
217
+ ctx.scale(ratio, ratio);
218
+ await page.render({ canvasContext: ctx, viewport, canvas }).promise;
219
+ }
220
+ } catch {
221
+ if (!cancelled) setFailed(true);
222
+ }
223
+ })();
224
+ return () => {
225
+ cancelled = true;
226
+ };
227
+ }, [url, placement, workerSrc, primaryColor]);
228
+ if (failed) {
229
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: 8 }, children: [
230
+ /* @__PURE__ */ jsxRuntime.jsx(
231
+ "iframe",
232
+ {
233
+ src: `${url}#toolbar=0`,
234
+ title: "Document",
235
+ style: { width: "100%", height: 520, border: "1px solid #e5e7eb", borderRadius: 8 }
236
+ }
237
+ ),
238
+ /* @__PURE__ */ jsxRuntime.jsx("a", { href: url, target: "_blank", rel: "noreferrer", style: { fontSize: 13, color: primaryColor }, children: "Download to read" })
239
+ ] });
240
+ }
241
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { ref: containerRef, style: { width: "100%" } });
242
+ }
243
+ function FieldDesigner({
244
+ pdfData,
245
+ value,
246
+ onChange,
247
+ workerSrc,
248
+ primaryColor = "#4f46e5"
249
+ }) {
250
+ const [numPages, setNumPages] = react.useState(0);
251
+ const [page, setPage] = react.useState(value?.page ?? 1);
252
+ const [failed, setFailed] = react.useState(false);
253
+ const stageRef = react.useRef(null);
254
+ const canvasRef = react.useRef(null);
255
+ const docRef = react.useRef(null);
256
+ const [drag, setDrag] = react.useState(null);
257
+ const startRef = react.useRef(null);
258
+ react.useEffect(() => {
259
+ let cancelled = false;
260
+ void (async () => {
261
+ try {
262
+ const pdfjs = await import('pdfjs-dist');
263
+ pdfjs.GlobalWorkerOptions.workerSrc = workerSrc ?? `https://unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;
264
+ const doc = await pdfjs.getDocument({ data: pdfData.slice() }).promise;
265
+ if (cancelled) return;
266
+ docRef.current = doc;
267
+ setNumPages(doc.numPages);
268
+ setPage((p) => Math.min(p, doc.numPages));
269
+ } catch {
270
+ if (!cancelled) setFailed(true);
271
+ }
272
+ })();
273
+ return () => {
274
+ cancelled = true;
275
+ };
276
+ }, [pdfData, workerSrc]);
277
+ react.useEffect(() => {
278
+ let cancelled = false;
279
+ void (async () => {
280
+ const doc = docRef.current;
281
+ const canvas = canvasRef.current;
282
+ if (!doc || !canvas) return;
283
+ const pageObj = await doc.getPage(page);
284
+ if (cancelled) return;
285
+ const width = stageRef.current?.clientWidth || 640;
286
+ const base = pageObj.getViewport({ scale: 1 });
287
+ const viewport = pageObj.getViewport({ scale: width / base.width });
288
+ const ratio = Math.max(window.devicePixelRatio || 1, 1);
289
+ canvas.width = Math.floor(viewport.width * ratio);
290
+ canvas.height = Math.floor(viewport.height * ratio);
291
+ canvas.style.width = "100%";
292
+ canvas.style.display = "block";
293
+ const ctx = canvas.getContext("2d");
294
+ if (!ctx) return;
295
+ ctx.scale(ratio, ratio);
296
+ await pageObj.render({ canvasContext: ctx, viewport, canvas }).promise;
297
+ })();
298
+ return () => {
299
+ cancelled = true;
300
+ };
301
+ }, [page, numPages]);
302
+ function rel(e) {
303
+ const rect = stageRef.current.getBoundingClientRect();
304
+ return {
305
+ x: Math.min(Math.max((e.clientX - rect.left) / rect.width, 0), 1),
306
+ y: Math.min(Math.max((e.clientY - rect.top) / rect.height, 0), 1)
307
+ };
308
+ }
309
+ function onDown(e) {
310
+ startRef.current = rel(e);
311
+ setDrag({ ...startRef.current, w: 0, h: 0 });
312
+ }
313
+ function onMove(e) {
314
+ if (!startRef.current) return;
315
+ const p = rel(e);
316
+ const s = startRef.current;
317
+ setDrag({ x: Math.min(s.x, p.x), y: Math.min(s.y, p.y), w: Math.abs(p.x - s.x), h: Math.abs(p.y - s.y) });
318
+ }
319
+ function onUp() {
320
+ if (drag && drag.w > 0.02 && drag.h > 0.01) {
321
+ onChange({ page, x: drag.x, y: drag.y, w: drag.w, h: drag.h });
322
+ }
323
+ startRef.current = null;
324
+ setDrag(null);
325
+ }
326
+ const box = drag ?? (value?.page === page ? value : null);
327
+ if (failed) {
328
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { color: "#b91c1c", fontSize: 14 }, children: "Could not render this PDF for placement. Please check the file is a valid, unencrypted PDF." });
329
+ }
330
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
331
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: 8, marginBottom: 8 }, children: [
332
+ /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontSize: 13, color: "#6b7280" }, children: "Drag a box where the signature goes." }),
333
+ numPages > 1 ? /* @__PURE__ */ jsxRuntime.jsxs("label", { style: { marginLeft: "auto", fontSize: 13, color: "#374151" }, children: [
334
+ "Page",
335
+ " ",
336
+ /* @__PURE__ */ jsxRuntime.jsx("select", { value: page, onChange: (e) => setPage(Number(e.target.value)), children: Array.from({ length: numPages }, (_, i) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: i + 1, children: i + 1 }, i)) })
337
+ ] }) : null
338
+ ] }),
339
+ /* @__PURE__ */ jsxRuntime.jsxs(
340
+ "div",
341
+ {
342
+ ref: stageRef,
343
+ onMouseDown: onDown,
344
+ onMouseMove: onMove,
345
+ onMouseUp: onUp,
346
+ onMouseLeave: onUp,
347
+ style: {
348
+ position: "relative",
349
+ cursor: "crosshair",
350
+ userSelect: "none",
351
+ border: "1px solid #e5e7eb",
352
+ borderRadius: 6,
353
+ overflow: "hidden"
354
+ },
355
+ children: [
356
+ /* @__PURE__ */ jsxRuntime.jsx("canvas", { ref: canvasRef }),
357
+ box ? /* @__PURE__ */ jsxRuntime.jsx(
358
+ "div",
359
+ {
360
+ style: {
361
+ position: "absolute",
362
+ left: `${box.x * 100}%`,
363
+ top: `${box.y * 100}%`,
364
+ width: `${box.w * 100}%`,
365
+ height: `${box.h * 100}%`,
366
+ border: `2px solid ${primaryColor}`,
367
+ background: `${primaryColor}22`,
368
+ borderRadius: 3,
369
+ pointerEvents: "none"
370
+ }
371
+ }
372
+ ) : null
373
+ ]
374
+ }
375
+ )
376
+ ] });
377
+ }
378
+ function SigningExperience(props) {
379
+ const primary = props.branding?.primaryColor ?? "#4f46e5";
380
+ const [signaturePng, setSignaturePng] = react.useState(null);
381
+ const [consent, setConsent] = react.useState({ signerName: "", consent: false });
382
+ const [submitting, setSubmitting] = react.useState(false);
383
+ const [error, setError] = react.useState(null);
384
+ const canSubmit = !!signaturePng && consent.consent && consent.signerName.trim().length > 0 && !submitting;
385
+ async function submit() {
386
+ if (!canSubmit) return;
387
+ setSubmitting(true);
388
+ setError(null);
389
+ try {
390
+ const res = await fetch(props.submitUrl, {
391
+ method: "POST",
392
+ headers: { "content-type": "application/json" },
393
+ body: JSON.stringify({
394
+ signaturePng,
395
+ signerName: consent.signerName.trim(),
396
+ consent: true
397
+ })
398
+ });
399
+ if (!res.ok) {
400
+ const body = await res.json().catch(() => null);
401
+ throw new Error(body?.error?.message ?? "Could not submit your signature.");
402
+ }
403
+ props.onSigned?.();
404
+ } catch (e) {
405
+ setError(e instanceof Error ? e.message : "Something went wrong.");
406
+ } finally {
407
+ setSubmitting(false);
408
+ }
409
+ }
410
+ return /* @__PURE__ */ jsxRuntime.jsxs(
411
+ "div",
412
+ {
413
+ style: {
414
+ maxWidth: 760,
415
+ margin: "0 auto",
416
+ padding: "24px 16px 64px",
417
+ fontFamily: "system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif",
418
+ color: "#111827"
419
+ },
420
+ children: [
421
+ /* @__PURE__ */ jsxRuntime.jsxs("header", { style: { display: "flex", alignItems: "center", gap: 12, marginBottom: 8 }, children: [
422
+ props.branding?.logoUrl ? /* @__PURE__ */ jsxRuntime.jsx("img", { src: props.branding.logoUrl, alt: "", style: { height: 32 } }) : null,
423
+ props.branding?.appName ? /* @__PURE__ */ jsxRuntime.jsx("span", { style: { fontWeight: 600, color: "#374151" }, children: props.branding.appName }) : null
424
+ ] }),
425
+ /* @__PURE__ */ jsxRuntime.jsx("h1", { style: { fontSize: 22, margin: "8px 0 4px" }, children: props.documentTitle }),
426
+ /* @__PURE__ */ jsxRuntime.jsx("p", { style: { color: "#6b7280", fontSize: 14, marginTop: 0 }, children: "Review the document below, then sign \u2014 no account or login needed." }),
427
+ /* @__PURE__ */ jsxRuntime.jsx("section", { style: { margin: "16px 0" }, children: /* @__PURE__ */ jsxRuntime.jsx(
428
+ PdfViewer,
429
+ {
430
+ url: props.sourceUrl,
431
+ placement: props.placement,
432
+ workerSrc: props.workerSrc,
433
+ primaryColor: primary
434
+ }
435
+ ) }),
436
+ /* @__PURE__ */ jsxRuntime.jsxs("section", { style: { display: "flex", flexDirection: "column", gap: 16 }, children: [
437
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
438
+ /* @__PURE__ */ jsxRuntime.jsx("label", { style: { fontSize: 13, fontWeight: 600, color: "#374151" }, children: "Your signature" }),
439
+ /* @__PURE__ */ jsxRuntime.jsx("div", { style: { marginTop: 6 }, children: /* @__PURE__ */ jsxRuntime.jsx(SignaturePad, { primaryColor: primary, onChange: setSignaturePng }) })
440
+ ] }),
441
+ /* @__PURE__ */ jsxRuntime.jsx(ConsentBlock, { value: consent, onChange: setConsent, primaryColor: primary }),
442
+ error ? /* @__PURE__ */ jsxRuntime.jsx(
443
+ "div",
444
+ {
445
+ style: {
446
+ background: "#fef2f2",
447
+ border: "1px solid #fecaca",
448
+ color: "#b91c1c",
449
+ borderRadius: 8,
450
+ padding: "10px 12px",
451
+ fontSize: 14
452
+ },
453
+ children: error
454
+ }
455
+ ) : null,
456
+ /* @__PURE__ */ jsxRuntime.jsx(
457
+ "button",
458
+ {
459
+ type: "button",
460
+ onClick: submit,
461
+ disabled: !canSubmit,
462
+ style: {
463
+ padding: "14px 16px",
464
+ borderRadius: 10,
465
+ border: "none",
466
+ background: canSubmit ? primary : "#c7c9d1",
467
+ color: "#fff",
468
+ fontSize: 16,
469
+ fontWeight: 600,
470
+ cursor: canSubmit ? "pointer" : "not-allowed"
471
+ },
472
+ children: submitting ? "Signing\u2026" : "Sign document"
473
+ }
474
+ )
475
+ ] })
476
+ ]
477
+ }
478
+ );
479
+ }
480
+
481
+ exports.ConsentBlock = ConsentBlock;
482
+ exports.FieldDesigner = FieldDesigner;
483
+ exports.PdfViewer = PdfViewer;
484
+ exports.SignaturePad = SignaturePad;
485
+ exports.SigningExperience = SigningExperience;
486
+ //# sourceMappingURL=index.cjs.map
487
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/ui/SignaturePad.tsx","../../src/ui/ConsentBlock.tsx","../../src/ui/PdfViewer.tsx","../../src/ui/FieldDesigner.tsx","../../src/ui/SigningExperience.tsx"],"names":["useState","useRef","useEffect","SignaturePadLib","jsx","jsxs"],"mappings":"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","file":"index.cjs","sourcesContent":["import SignaturePadLib from \"signature_pad\";\nimport { useEffect, useRef, useState } from \"react\";\n\nexport interface SignaturePadProps {\n primaryColor?: string;\n /** Emits a PNG data URL of the current signature, or null when empty. */\n onChange: (pngDataUrl: string | null) => void;\n}\n\n/**\n * Capture a signature by drawing (signature_pad, touch + mouse, DPI-aware) or by\n * typing a name rendered in a script font. Both yield the same PNG data URL that\n * the seal step stamps onto the document.\n */\nexport function SignaturePad({ primaryColor = \"#4f46e5\", onChange }: SignaturePadProps) {\n const [mode, setMode] = useState<\"draw\" | \"type\">(\"draw\");\n const [typed, setTyped] = useState(\"\");\n const canvasRef = useRef<HTMLCanvasElement>(null);\n const padRef = useRef<SignaturePadLib | null>(null);\n\n useEffect(() => {\n if (mode !== \"draw\") return;\n const canvas = canvasRef.current;\n if (!canvas) return;\n const pad = new SignaturePadLib(canvas, { penColor: \"#111827\", minWidth: 0.8, maxWidth: 2.2 });\n padRef.current = pad;\n const resize = () => {\n const ratio = Math.max(window.devicePixelRatio || 1, 1);\n const rect = canvas.getBoundingClientRect();\n canvas.width = Math.floor(rect.width * ratio);\n canvas.height = Math.floor(rect.height * ratio);\n canvas.getContext(\"2d\")?.scale(ratio, ratio);\n pad.clear();\n onChange(null);\n };\n pad.addEventListener(\"endStroke\", () =>\n onChange(pad.isEmpty() ? null : pad.toDataURL(\"image/png\")),\n );\n resize();\n window.addEventListener(\"resize\", resize);\n return () => {\n window.removeEventListener(\"resize\", resize);\n pad.off();\n padRef.current = null;\n };\n }, [mode, onChange]);\n\n useEffect(() => {\n if (mode !== \"type\") return;\n const name = typed.trim();\n if (!name) {\n onChange(null);\n return;\n }\n const c = document.createElement(\"canvas\");\n c.width = 720;\n c.height = 200;\n const ctx = c.getContext(\"2d\");\n if (!ctx) return;\n ctx.fillStyle = \"#111827\";\n ctx.font = \"72px 'Segoe Script', 'Brush Script MT', cursive\";\n ctx.textBaseline = \"middle\";\n ctx.fillText(name, 24, 110);\n onChange(c.toDataURL(\"image/png\"));\n }, [mode, typed, onChange]);\n\n const tab = (m: \"draw\" | \"type\", label: string) => (\n <button\n type=\"button\"\n onClick={() => {\n setMode(m);\n onChange(null);\n }}\n style={{\n flex: 1,\n padding: \"8px 12px\",\n border: \"none\",\n borderBottom: mode === m ? `2px solid ${primaryColor}` : \"2px solid transparent\",\n background: \"transparent\",\n fontWeight: mode === m ? 600 : 400,\n color: mode === m ? primaryColor : \"#6b7280\",\n cursor: \"pointer\",\n }}\n >\n {label}\n </button>\n );\n\n return (\n <div style={{ border: \"1px solid #e5e7eb\", borderRadius: 12, overflow: \"hidden\" }}>\n <div style={{ display: \"flex\", borderBottom: \"1px solid #f3f4f6\" }}>\n {tab(\"draw\", \"Draw\")}\n {tab(\"type\", \"Type\")}\n </div>\n <div style={{ position: \"relative\", background: \"#fff\" }}>\n {mode === \"draw\" ? (\n <canvas\n ref={canvasRef}\n style={{ width: \"100%\", height: 180, touchAction: \"none\", display: \"block\" }}\n />\n ) : (\n <div style={{ padding: 16 }}>\n <input\n value={typed}\n onChange={(e) => setTyped(e.target.value)}\n placeholder=\"Type your name\"\n style={{\n width: \"100%\",\n fontSize: 40,\n fontFamily: \"'Segoe Script', 'Brush Script MT', cursive\",\n border: \"none\",\n borderBottom: \"1px solid #e5e7eb\",\n outline: \"none\",\n padding: \"8px 0\",\n }}\n />\n </div>\n )}\n <button\n type=\"button\"\n onClick={() => {\n padRef.current?.clear();\n setTyped(\"\");\n onChange(null);\n }}\n style={{\n position: \"absolute\",\n top: 8,\n right: 8,\n fontSize: 12,\n color: \"#6b7280\",\n background: \"rgba(255,255,255,0.8)\",\n border: \"1px solid #e5e7eb\",\n borderRadius: 6,\n padding: \"2px 8px\",\n cursor: \"pointer\",\n }}\n >\n Clear\n </button>\n </div>\n </div>\n );\n}\n","export interface ConsentValue {\n signerName: string;\n consent: boolean;\n}\n\nexport interface ConsentBlockProps {\n value: ConsentValue;\n onChange: (value: ConsentValue) => void;\n primaryColor?: string;\n}\n\n/**\n * The electronic-signature intent record: a typed full legal name + an explicit\n * consent checkbox. Together with the audit trail this is what makes the simple\n * e-signature legally valid.\n */\nexport function ConsentBlock({ value, onChange, primaryColor = \"#4f46e5\" }: ConsentBlockProps) {\n return (\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 12 }}>\n <label style={{ display: \"flex\", flexDirection: \"column\", gap: 4 }}>\n <span style={{ fontSize: 13, fontWeight: 600, color: \"#374151\" }}>Full legal name</span>\n <input\n value={value.signerName}\n onChange={(e) => onChange({ ...value, signerName: e.target.value })}\n placeholder=\"e.g. Ada Lovelace\"\n style={{\n padding: \"10px 12px\",\n border: \"1px solid #d1d5db\",\n borderRadius: 8,\n fontSize: 15,\n outline: \"none\",\n }}\n />\n </label>\n <label style={{ display: \"flex\", gap: 10, alignItems: \"flex-start\", cursor: \"pointer\" }}>\n <input\n type=\"checkbox\"\n checked={value.consent}\n onChange={(e) => onChange({ ...value, consent: e.target.checked })}\n style={{ marginTop: 3, accentColor: primaryColor, width: 16, height: 16 }}\n />\n <span style={{ fontSize: 13, color: \"#4b5563\", lineHeight: 1.5 }}>\n I agree to sign this document electronically. I understand my electronic\n signature is legally binding and that an audit record (time, IP, and a\n tamper-evident hash) is kept with the signed document.\n </span>\n </label>\n </div>\n );\n}\n","import { useEffect, useRef, useState } from \"react\";\n\nexport interface ViewerPlacement {\n page: number;\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nexport interface PdfViewerProps {\n /** Token-gated source PDF URL. */\n url: string;\n /** Optional signature-box hint, drawn on its page at normalized coords. */\n placement?: ViewerPlacement;\n /** Override the pdf.js worker (defaults to the unpkg .mjs for the bundled version). */\n workerSrc?: string;\n primaryColor?: string;\n}\n\n/**\n * Render the PDF to canvases (pdf.js, responsive, DPI-aware) with the signature\n * box overlaid where the manager placed it. Falls back to an <iframe> + download\n * link if pdf.js fails to load — so the document is always readable.\n */\nexport function PdfViewer({ url, placement, workerSrc, primaryColor = \"#4f46e5\" }: PdfViewerProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const [failed, setFailed] = useState(false);\n\n useEffect(() => {\n let cancelled = false;\n void (async () => {\n try {\n const pdfjs = await import(\"pdfjs-dist\");\n pdfjs.GlobalWorkerOptions.workerSrc =\n workerSrc ?? `https://unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;\n const doc = await pdfjs.getDocument({ url }).promise;\n const container = containerRef.current;\n if (!container || cancelled) return;\n container.innerHTML = \"\";\n const width = container.clientWidth || 640;\n const ratio = Math.max(window.devicePixelRatio || 1, 1);\n\n for (let p = 1; p <= doc.numPages; p++) {\n const page = await doc.getPage(p);\n if (cancelled) return;\n const base = page.getViewport({ scale: 1 });\n const viewport = page.getViewport({ scale: width / base.width });\n\n const canvas = document.createElement(\"canvas\");\n canvas.width = Math.floor(viewport.width * ratio);\n canvas.height = Math.floor(viewport.height * ratio);\n canvas.style.width = \"100%\";\n canvas.style.display = \"block\";\n canvas.style.borderRadius = \"4px\";\n\n const wrap = document.createElement(\"div\");\n wrap.style.position = \"relative\";\n wrap.style.marginBottom = \"14px\";\n wrap.style.boxShadow = \"0 1px 4px rgba(0,0,0,0.12)\";\n wrap.appendChild(canvas);\n\n if (placement && placement.page === p) {\n const box = document.createElement(\"div\");\n box.style.cssText = `position:absolute;left:${placement.x * 100}%;top:${\n placement.y * 100\n }%;width:${placement.w * 100}%;height:${placement.h * 100}%;border:2px dashed ${primaryColor};background:${primaryColor}1a;border-radius:4px;pointer-events:none;`;\n const label = document.createElement(\"span\");\n label.textContent = \"Signature\";\n label.style.cssText = `position:absolute;top:-18px;left:0;font-size:11px;font-weight:600;color:${primaryColor};`;\n box.appendChild(label);\n wrap.appendChild(box);\n }\n\n container.appendChild(wrap);\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) continue;\n ctx.scale(ratio, ratio);\n await page.render({ canvasContext: ctx, viewport, canvas }).promise;\n }\n } catch {\n if (!cancelled) setFailed(true);\n }\n })();\n return () => {\n cancelled = true;\n };\n }, [url, placement, workerSrc, primaryColor]);\n\n if (failed) {\n return (\n <div style={{ display: \"flex\", flexDirection: \"column\", gap: 8 }}>\n <iframe\n src={`${url}#toolbar=0`}\n title=\"Document\"\n style={{ width: \"100%\", height: 520, border: \"1px solid #e5e7eb\", borderRadius: 8 }}\n />\n <a href={url} target=\"_blank\" rel=\"noreferrer\" style={{ fontSize: 13, color: primaryColor }}>\n Download to read\n </a>\n </div>\n );\n }\n\n return <div ref={containerRef} style={{ width: \"100%\" }} />;\n}\n","import { useEffect, useRef, useState } from \"react\";\n\nexport interface DesignerPlacement {\n page: number;\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\nexport interface FieldDesignerProps {\n /** The chosen PDF's bytes (read client-side before upload). */\n pdfData: Uint8Array;\n value: DesignerPlacement | null;\n onChange: (placement: DesignerPlacement) => void;\n workerSrc?: string;\n primaryColor?: string;\n}\n\ninterface DragBox {\n x: number;\n y: number;\n w: number;\n h: number;\n}\n\n/**\n * Drag a signature box onto a pdf.js-rendered page. Emits a normalized\n * placement `{ page, x, y, w, h }` (0–1 fractions, top-left origin) that the seal\n * step converts to pdf coordinates — so it stays accurate at any size.\n */\nexport function FieldDesigner({\n pdfData,\n value,\n onChange,\n workerSrc,\n primaryColor = \"#4f46e5\",\n}: FieldDesignerProps) {\n const [numPages, setNumPages] = useState(0);\n const [page, setPage] = useState(value?.page ?? 1);\n const [failed, setFailed] = useState(false);\n const stageRef = useRef<HTMLDivElement>(null);\n const canvasRef = useRef<HTMLCanvasElement>(null);\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const docRef = useRef<any>(null);\n const [drag, setDrag] = useState<DragBox | null>(null);\n const startRef = useRef<{ x: number; y: number } | null>(null);\n\n // Load the document once.\n useEffect(() => {\n let cancelled = false;\n void (async () => {\n try {\n const pdfjs = await import(\"pdfjs-dist\");\n pdfjs.GlobalWorkerOptions.workerSrc =\n workerSrc ?? `https://unpkg.com/pdfjs-dist@${pdfjs.version}/build/pdf.worker.min.mjs`;\n const doc = await pdfjs.getDocument({ data: pdfData.slice() }).promise;\n if (cancelled) return;\n docRef.current = doc;\n setNumPages(doc.numPages);\n setPage((p) => Math.min(p, doc.numPages));\n } catch {\n if (!cancelled) setFailed(true);\n }\n })();\n return () => {\n cancelled = true;\n };\n }, [pdfData, workerSrc]);\n\n // Render the current page.\n useEffect(() => {\n let cancelled = false;\n void (async () => {\n const doc = docRef.current;\n const canvas = canvasRef.current;\n if (!doc || !canvas) return;\n const pageObj = await doc.getPage(page);\n if (cancelled) return;\n const width = stageRef.current?.clientWidth || 640;\n const base = pageObj.getViewport({ scale: 1 });\n const viewport = pageObj.getViewport({ scale: width / base.width });\n const ratio = Math.max(window.devicePixelRatio || 1, 1);\n canvas.width = Math.floor(viewport.width * ratio);\n canvas.height = Math.floor(viewport.height * ratio);\n canvas.style.width = \"100%\";\n canvas.style.display = \"block\";\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) return;\n ctx.scale(ratio, ratio);\n await pageObj.render({ canvasContext: ctx, viewport, canvas }).promise;\n })();\n return () => {\n cancelled = true;\n };\n }, [page, numPages]);\n\n function rel(e: React.MouseEvent): { x: number; y: number } {\n const rect = stageRef.current!.getBoundingClientRect();\n return {\n x: Math.min(Math.max((e.clientX - rect.left) / rect.width, 0), 1),\n y: Math.min(Math.max((e.clientY - rect.top) / rect.height, 0), 1),\n };\n }\n\n function onDown(e: React.MouseEvent) {\n startRef.current = rel(e);\n setDrag({ ...startRef.current, w: 0, h: 0 });\n }\n function onMove(e: React.MouseEvent) {\n if (!startRef.current) return;\n const p = rel(e);\n const s = startRef.current;\n setDrag({ x: Math.min(s.x, p.x), y: Math.min(s.y, p.y), w: Math.abs(p.x - s.x), h: Math.abs(p.y - s.y) });\n }\n function onUp() {\n if (drag && drag.w > 0.02 && drag.h > 0.01) {\n onChange({ page, x: drag.x, y: drag.y, w: drag.w, h: drag.h });\n }\n startRef.current = null;\n setDrag(null);\n }\n\n const box = drag ?? (value?.page === page ? value : null);\n\n if (failed) {\n return (\n <div style={{ color: \"#b91c1c\", fontSize: 14 }}>\n Could not render this PDF for placement. Please check the file is a valid, unencrypted PDF.\n </div>\n );\n }\n\n return (\n <div>\n <div style={{ display: \"flex\", alignItems: \"center\", gap: 8, marginBottom: 8 }}>\n <span style={{ fontSize: 13, color: \"#6b7280\" }}>Drag a box where the signature goes.</span>\n {numPages > 1 ? (\n <label style={{ marginLeft: \"auto\", fontSize: 13, color: \"#374151\" }}>\n Page{\" \"}\n <select value={page} onChange={(e) => setPage(Number(e.target.value))}>\n {Array.from({ length: numPages }, (_, i) => (\n <option key={i} value={i + 1}>\n {i + 1}\n </option>\n ))}\n </select>\n </label>\n ) : null}\n </div>\n <div\n ref={stageRef}\n onMouseDown={onDown}\n onMouseMove={onMove}\n onMouseUp={onUp}\n onMouseLeave={onUp}\n style={{\n position: \"relative\",\n cursor: \"crosshair\",\n userSelect: \"none\",\n border: \"1px solid #e5e7eb\",\n borderRadius: 6,\n overflow: \"hidden\",\n }}\n >\n <canvas ref={canvasRef} />\n {box ? (\n <div\n style={{\n position: \"absolute\",\n left: `${box.x * 100}%`,\n top: `${box.y * 100}%`,\n width: `${box.w * 100}%`,\n height: `${box.h * 100}%`,\n border: `2px solid ${primaryColor}`,\n background: `${primaryColor}22`,\n borderRadius: 3,\n pointerEvents: \"none\",\n }}\n />\n ) : null}\n </div>\n </div>\n );\n}\n","import { useState } from \"react\";\n\nimport { ConsentBlock, type ConsentValue } from \"./ConsentBlock\";\nimport { PdfViewer, type ViewerPlacement } from \"./PdfViewer\";\nimport { SignaturePad } from \"./SignaturePad\";\n\nexport interface SigningBranding {\n appName?: string;\n logoUrl?: string;\n primaryColor?: string;\n}\n\nexport interface SigningExperienceProps {\n /** Token-gated source PDF URL (GET /api/esign/source/<token>). */\n sourceUrl: string;\n /** Submit endpoint (POST /sign/<token>/submit). */\n submitUrl: string;\n documentTitle: string;\n placement?: ViewerPlacement;\n branding?: SigningBranding;\n /** Called after a successful signature submit (host navigates to /done). */\n onSigned?: () => void;\n workerSrc?: string;\n}\n\n/**\n * The full host-branded signer page body: read the PDF (placement highlighted),\n * draw/type a signature, consent, and submit. Self-contained styling so it sits\n * cleanly inside any host shell.\n */\nexport function SigningExperience(props: SigningExperienceProps) {\n const primary = props.branding?.primaryColor ?? \"#4f46e5\";\n const [signaturePng, setSignaturePng] = useState<string | null>(null);\n const [consent, setConsent] = useState<ConsentValue>({ signerName: \"\", consent: false });\n const [submitting, setSubmitting] = useState(false);\n const [error, setError] = useState<string | null>(null);\n\n const canSubmit =\n !!signaturePng && consent.consent && consent.signerName.trim().length > 0 && !submitting;\n\n async function submit() {\n if (!canSubmit) return;\n setSubmitting(true);\n setError(null);\n try {\n const res = await fetch(props.submitUrl, {\n method: \"POST\",\n headers: { \"content-type\": \"application/json\" },\n body: JSON.stringify({\n signaturePng,\n signerName: consent.signerName.trim(),\n consent: true,\n }),\n });\n if (!res.ok) {\n const body = (await res.json().catch(() => null)) as {\n error?: { message?: string };\n } | null;\n throw new Error(body?.error?.message ?? \"Could not submit your signature.\");\n }\n props.onSigned?.();\n } catch (e) {\n setError(e instanceof Error ? e.message : \"Something went wrong.\");\n } finally {\n setSubmitting(false);\n }\n }\n\n return (\n <div\n style={{\n maxWidth: 760,\n margin: \"0 auto\",\n padding: \"24px 16px 64px\",\n fontFamily:\n \"system-ui, -apple-system, Segoe UI, Roboto, Helvetica, Arial, sans-serif\",\n color: \"#111827\",\n }}\n >\n <header style={{ display: \"flex\", alignItems: \"center\", gap: 12, marginBottom: 8 }}>\n {props.branding?.logoUrl ? (\n <img src={props.branding.logoUrl} alt=\"\" style={{ height: 32 }} />\n ) : null}\n {props.branding?.appName ? (\n <span style={{ fontWeight: 600, color: \"#374151\" }}>{props.branding.appName}</span>\n ) : null}\n </header>\n\n <h1 style={{ fontSize: 22, margin: \"8px 0 4px\" }}>{props.documentTitle}</h1>\n <p style={{ color: \"#6b7280\", fontSize: 14, marginTop: 0 }}>\n Review the document below, then sign — no account or login needed.\n </p>\n\n <section style={{ margin: \"16px 0\" }}>\n <PdfViewer\n url={props.sourceUrl}\n placement={props.placement}\n workerSrc={props.workerSrc}\n primaryColor={primary}\n />\n </section>\n\n <section style={{ display: \"flex\", flexDirection: \"column\", gap: 16 }}>\n <div>\n <label style={{ fontSize: 13, fontWeight: 600, color: \"#374151\" }}>Your signature</label>\n <div style={{ marginTop: 6 }}>\n <SignaturePad primaryColor={primary} onChange={setSignaturePng} />\n </div>\n </div>\n\n <ConsentBlock value={consent} onChange={setConsent} primaryColor={primary} />\n\n {error ? (\n <div\n style={{\n background: \"#fef2f2\",\n border: \"1px solid #fecaca\",\n color: \"#b91c1c\",\n borderRadius: 8,\n padding: \"10px 12px\",\n fontSize: 14,\n }}\n >\n {error}\n </div>\n ) : null}\n\n <button\n type=\"button\"\n onClick={submit}\n disabled={!canSubmit}\n style={{\n padding: \"14px 16px\",\n borderRadius: 10,\n border: \"none\",\n background: canSubmit ? primary : \"#c7c9d1\",\n color: \"#fff\",\n fontSize: 16,\n fontWeight: 600,\n cursor: canSubmit ? \"pointer\" : \"not-allowed\",\n }}\n >\n {submitting ? \"Signing…\" : \"Sign document\"}\n </button>\n </section>\n </div>\n );\n}\n"]}