@signiphi/pdf-signer 0.1.1

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.
Files changed (192) hide show
  1. package/INSTALLING_LOCALLY.md +184 -0
  2. package/LICENSE +2 -0
  3. package/README.md +1093 -0
  4. package/assets/viewer.html +314 -0
  5. package/dist/__tests__/helpers/fixtures.d.ts +43 -0
  6. package/dist/__tests__/helpers/fixtures.d.ts.map +1 -0
  7. package/dist/__tests__/helpers/mocks.d.ts +333 -0
  8. package/dist/__tests__/helpers/mocks.d.ts.map +1 -0
  9. package/dist/__tests__/setup.d.ts +6 -0
  10. package/dist/__tests__/setup.d.ts.map +1 -0
  11. package/dist/components/AttachmentUpload.d.ts +17 -0
  12. package/dist/components/AttachmentUpload.d.ts.map +1 -0
  13. package/dist/components/EditableFieldsPanel.d.ts +30 -0
  14. package/dist/components/EditableFieldsPanel.d.ts.map +1 -0
  15. package/dist/components/ErrorBoundary.d.ts +67 -0
  16. package/dist/components/ErrorBoundary.d.ts.map +1 -0
  17. package/dist/components/FormFieldsView.d.ts +42 -0
  18. package/dist/components/FormFieldsView.d.ts.map +1 -0
  19. package/dist/components/PdfViewerStyled.d.ts +16 -0
  20. package/dist/components/PdfViewerStyled.d.ts.map +1 -0
  21. package/dist/components/PoweredBySigniphi.d.ts +11 -0
  22. package/dist/components/PoweredBySigniphi.d.ts.map +1 -0
  23. package/dist/components/SignatureCanvas.d.ts +12 -0
  24. package/dist/components/SignatureCanvas.d.ts.map +1 -0
  25. package/dist/components/SignatureInitialsBox.d.ts +23 -0
  26. package/dist/components/SignatureInitialsBox.d.ts.map +1 -0
  27. package/dist/components/SignatureModal.d.ts +22 -0
  28. package/dist/components/SignatureModal.d.ts.map +1 -0
  29. package/dist/components/SigningInstructions.d.ts +12 -0
  30. package/dist/components/SigningInstructions.d.ts.map +1 -0
  31. package/dist/components/SubmissionForm.d.ts +52 -0
  32. package/dist/components/SubmissionForm.d.ts.map +1 -0
  33. package/dist/components/ViewToggleToolbar.d.ts +30 -0
  34. package/dist/components/ViewToggleToolbar.d.ts.map +1 -0
  35. package/dist/components/form-fields/CheckboxRenderer.d.ts +10 -0
  36. package/dist/components/form-fields/CheckboxRenderer.d.ts.map +1 -0
  37. package/dist/components/form-fields/DateFieldRenderer.d.ts +14 -0
  38. package/dist/components/form-fields/DateFieldRenderer.d.ts.map +1 -0
  39. package/dist/components/form-fields/DropdownRenderer.d.ts +14 -0
  40. package/dist/components/form-fields/DropdownRenderer.d.ts.map +1 -0
  41. package/dist/components/form-fields/FormFieldRenderer.d.ts +20 -0
  42. package/dist/components/form-fields/FormFieldRenderer.d.ts.map +1 -0
  43. package/dist/components/form-fields/InitialsFieldRenderer.d.ts +14 -0
  44. package/dist/components/form-fields/InitialsFieldRenderer.d.ts.map +1 -0
  45. package/dist/components/form-fields/RadioGroupRenderer.d.ts +10 -0
  46. package/dist/components/form-fields/RadioGroupRenderer.d.ts.map +1 -0
  47. package/dist/components/form-fields/SignatureFieldRenderer.d.ts +16 -0
  48. package/dist/components/form-fields/SignatureFieldRenderer.d.ts.map +1 -0
  49. package/dist/components/form-fields/TextFieldRenderer.d.ts +14 -0
  50. package/dist/components/form-fields/TextFieldRenderer.d.ts.map +1 -0
  51. package/dist/components/form-fields/TextLabelRenderer.d.ts +14 -0
  52. package/dist/components/form-fields/TextLabelRenderer.d.ts.map +1 -0
  53. package/dist/components/form-fields/index.d.ts +14 -0
  54. package/dist/components/form-fields/index.d.ts.map +1 -0
  55. package/dist/components/index.d.ts +14 -0
  56. package/dist/components/index.d.ts.map +1 -0
  57. package/dist/components/index.js +6297 -0
  58. package/dist/components/index.js.map +1 -0
  59. package/dist/components/index.mjs +6248 -0
  60. package/dist/components/index.mjs.map +1 -0
  61. package/dist/core/PdfViewerCore.d.ts +19 -0
  62. package/dist/core/PdfViewerCore.d.ts.map +1 -0
  63. package/dist/core/SignatureCaptureCore.d.ts +37 -0
  64. package/dist/core/SignatureCaptureCore.d.ts.map +1 -0
  65. package/dist/core/index.d.ts +3 -0
  66. package/dist/core/index.d.ts.map +1 -0
  67. package/dist/core/index.js +907 -0
  68. package/dist/core/index.js.map +1 -0
  69. package/dist/core/index.mjs +884 -0
  70. package/dist/core/index.mjs.map +1 -0
  71. package/dist/hooks/index.d.ts +8 -0
  72. package/dist/hooks/index.d.ts.map +1 -0
  73. package/dist/hooks/index.js +2167 -0
  74. package/dist/hooks/index.js.map +1 -0
  75. package/dist/hooks/index.mjs +2139 -0
  76. package/dist/hooks/index.mjs.map +1 -0
  77. package/dist/hooks/useAttachments.d.ts +25 -0
  78. package/dist/hooks/useAttachments.d.ts.map +1 -0
  79. package/dist/hooks/useFieldFiltering.d.ts +29 -0
  80. package/dist/hooks/useFieldFiltering.d.ts.map +1 -0
  81. package/dist/hooks/useFormFields.d.ts +23 -0
  82. package/dist/hooks/useFormFields.d.ts.map +1 -0
  83. package/dist/hooks/useMultiSignerContext.d.ts +25 -0
  84. package/dist/hooks/useMultiSignerContext.d.ts.map +1 -0
  85. package/dist/hooks/usePdfViewer.d.ts +52 -0
  86. package/dist/hooks/usePdfViewer.d.ts.map +1 -0
  87. package/dist/hooks/useSignatureCapture.d.ts +17 -0
  88. package/dist/hooks/useSignatureCapture.d.ts.map +1 -0
  89. package/dist/hooks/useSignatures.d.ts +25 -0
  90. package/dist/hooks/useSignatures.d.ts.map +1 -0
  91. package/dist/index.css +4929 -0
  92. package/dist/index.css.map +1 -0
  93. package/dist/index.d.ts +17 -0
  94. package/dist/index.d.ts.map +1 -0
  95. package/dist/index.js +7220 -0
  96. package/dist/index.js.map +1 -0
  97. package/dist/index.mjs +7093 -0
  98. package/dist/index.mjs.map +1 -0
  99. package/dist/integrations/index.d.ts +6 -0
  100. package/dist/integrations/index.d.ts.map +1 -0
  101. package/dist/integrations/index.js +242 -0
  102. package/dist/integrations/index.js.map +1 -0
  103. package/dist/integrations/index.mjs +218 -0
  104. package/dist/integrations/index.mjs.map +1 -0
  105. package/dist/integrations/next-config.d.ts +46 -0
  106. package/dist/integrations/next-config.d.ts.map +1 -0
  107. package/dist/integrations/vite-plugin.d.ts +48 -0
  108. package/dist/integrations/vite-plugin.d.ts.map +1 -0
  109. package/dist/lib/index.d.ts +3 -0
  110. package/dist/lib/index.d.ts.map +1 -0
  111. package/dist/lib/ui/alert.d.ts +9 -0
  112. package/dist/lib/ui/alert.d.ts.map +1 -0
  113. package/dist/lib/ui/button.d.ts +12 -0
  114. package/dist/lib/ui/button.d.ts.map +1 -0
  115. package/dist/lib/ui/calendar.d.ts +10 -0
  116. package/dist/lib/ui/calendar.d.ts.map +1 -0
  117. package/dist/lib/ui/card.d.ts +9 -0
  118. package/dist/lib/ui/card.d.ts.map +1 -0
  119. package/dist/lib/ui/checkbox.d.ts +5 -0
  120. package/dist/lib/ui/checkbox.d.ts.map +1 -0
  121. package/dist/lib/ui/dialog.d.ts +20 -0
  122. package/dist/lib/ui/dialog.d.ts.map +1 -0
  123. package/dist/lib/ui/index.d.ts +12 -0
  124. package/dist/lib/ui/index.d.ts.map +1 -0
  125. package/dist/lib/ui/input.d.ts +6 -0
  126. package/dist/lib/ui/input.d.ts.map +1 -0
  127. package/dist/lib/ui/label.d.ts +6 -0
  128. package/dist/lib/ui/label.d.ts.map +1 -0
  129. package/dist/lib/ui/popover.d.ts +7 -0
  130. package/dist/lib/ui/popover.d.ts.map +1 -0
  131. package/dist/lib/ui/radio-group.d.ts +6 -0
  132. package/dist/lib/ui/radio-group.d.ts.map +1 -0
  133. package/dist/lib/ui/select.d.ts +14 -0
  134. package/dist/lib/ui/select.d.ts.map +1 -0
  135. package/dist/lib/utils.d.ts +7 -0
  136. package/dist/lib/utils.d.ts.map +1 -0
  137. package/dist/styles/index.css +5004 -0
  138. package/dist/types/index.d.ts +265 -0
  139. package/dist/types/index.d.ts.map +1 -0
  140. package/dist/types/index.js +26 -0
  141. package/dist/types/index.js.map +1 -0
  142. package/dist/types/index.mjs +23 -0
  143. package/dist/types/index.mjs.map +1 -0
  144. package/dist/utils/attachment-validators.d.ts +118 -0
  145. package/dist/utils/attachment-validators.d.ts.map +1 -0
  146. package/dist/utils/audit-trail.d.ts +27 -0
  147. package/dist/utils/audit-trail.d.ts.map +1 -0
  148. package/dist/utils/date-validation.d.ts +30 -0
  149. package/dist/utils/date-validation.d.ts.map +1 -0
  150. package/dist/utils/errors.d.ts +106 -0
  151. package/dist/utils/errors.d.ts.map +1 -0
  152. package/dist/utils/field-extraction.d.ts +27 -0
  153. package/dist/utils/field-extraction.d.ts.map +1 -0
  154. package/dist/utils/field-visibility.d.ts +104 -0
  155. package/dist/utils/field-visibility.d.ts.map +1 -0
  156. package/dist/utils/index.d.ts +17 -0
  157. package/dist/utils/index.d.ts.map +1 -0
  158. package/dist/utils/index.js +2501 -0
  159. package/dist/utils/index.js.map +1 -0
  160. package/dist/utils/index.mjs +2404 -0
  161. package/dist/utils/index.mjs.map +1 -0
  162. package/dist/utils/logger.d.ts +16 -0
  163. package/dist/utils/logger.d.ts.map +1 -0
  164. package/dist/utils/pdf-field-type-helpers.d.ts +78 -0
  165. package/dist/utils/pdf-field-type-helpers.d.ts.map +1 -0
  166. package/dist/utils/pdf-helpers.d.ts +38 -0
  167. package/dist/utils/pdf-helpers.d.ts.map +1 -0
  168. package/dist/utils/pdf-lib-loader.d.ts +45 -0
  169. package/dist/utils/pdf-lib-loader.d.ts.map +1 -0
  170. package/dist/utils/pdf-manipulation.d.ts +93 -0
  171. package/dist/utils/pdf-manipulation.d.ts.map +1 -0
  172. package/dist/utils/pdf-validators.d.ts +149 -0
  173. package/dist/utils/pdf-validators.d.ts.map +1 -0
  174. package/dist/utils/pdf-viewer-filter.d.ts +35 -0
  175. package/dist/utils/pdf-viewer-filter.d.ts.map +1 -0
  176. package/dist/utils/pdf-widget-helpers.d.ts +98 -0
  177. package/dist/utils/pdf-widget-helpers.d.ts.map +1 -0
  178. package/dist/utils/pdfjs-config.d.ts +56 -0
  179. package/dist/utils/pdfjs-config.d.ts.map +1 -0
  180. package/dist/utils/pdfjs-version-check.d.ts +28 -0
  181. package/dist/utils/pdfjs-version-check.d.ts.map +1 -0
  182. package/dist/utils/performance-monitor.d.ts +172 -0
  183. package/dist/utils/performance-monitor.d.ts.map +1 -0
  184. package/dist/utils/tracking.d.ts +89 -0
  185. package/dist/utils/tracking.d.ts.map +1 -0
  186. package/package.json +180 -0
  187. package/scripts/analyze-bundle.js +271 -0
  188. package/scripts/copy-utils.js +227 -0
  189. package/scripts/copy-utils.test.js +164 -0
  190. package/scripts/postinstall.js +109 -0
  191. package/scripts/setup.js +108 -0
  192. package/src/styles/index.css +139 -0
package/README.md ADDED
@@ -0,0 +1,1093 @@
1
+ # @signiphi/pdf-signer
2
+
3
+ Flexible React components for PDF viewing, form filling, and signature capture. Built with React, TypeScript, and PDF.js.
4
+
5
+ ## Features
6
+
7
+ - 📄 **PDF Viewing** - Render PDFs with PDF.js viewer
8
+ - ✍️ **Signature Capture** - Draw or upload signatures
9
+ - 📝 **Form Filling** - Fill PDF form fields
10
+ - 🎨 **Flexible Styling** - Use headless components or pre-styled components
11
+ - 📱 **Responsive** - Works on desktop and mobile devices
12
+ - 🔧 **TypeScript** - Fully typed API
13
+ - 🌳 **Tree-shakeable** - Import only what you need
14
+ - ⚡ **Performance Optimized** - Lazy loading and bundle size optimization
15
+ - 📊 **Performance Monitoring** - Built-in performance tracking utilities
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ npm install @signiphi/pdf-signer
21
+ # or
22
+ pnpm add @signiphi/pdf-signer
23
+ # or
24
+ yarn add @signiphi/pdf-signer
25
+ ```
26
+
27
+ ### Peer Dependencies
28
+
29
+ This package requires the following peer dependencies:
30
+
31
+ ```bash
32
+ npm install react react-dom @radix-ui/react-dialog @radix-ui/react-label @radix-ui/react-select @radix-ui/react-checkbox @radix-ui/react-radio-group @radix-ui/react-tabs lucide-react
33
+ ```
34
+
35
+ If using styled components, you'll also need Tailwind CSS:
36
+
37
+ ```bash
38
+ npm install -D tailwindcss
39
+ ```
40
+
41
+ ### PDF.js Setup
42
+
43
+ **The package works out of the box!** PDF.js viewer files are automatically copied to your `public/pdfjs/` directory when you install the package.
44
+
45
+ #### Automatic Setup (Default)
46
+
47
+ After installation, PDF.js viewer files are automatically set up:
48
+
49
+ ```bash
50
+ npm install @signiphi/pdf-signer
51
+ # PDF.js files automatically copied to public/pdfjs/
52
+ ```
53
+
54
+ **What happens automatically:**
55
+ - ✅ Viewer files copied to `public/pdfjs/` via postinstall script
56
+ - ✅ Worker loaded from `node_modules` (bundled by your build tool)
57
+ - ✅ No manual configuration needed
58
+ - ✅ Works offline
59
+ - ✅ No CORS issues
60
+
61
+ **Default configuration:**
62
+ ```typescript
63
+ {
64
+ viewerBasePath: '/pdfjs', // Viewer files in public/pdfjs/
65
+ workerSrc: 'node_modules/pdfjs-dist/build/pdf.worker.mjs' // Worker from node_modules
66
+ }
67
+ ```
68
+
69
+ #### Manual Setup (If Postinstall Fails)
70
+
71
+ If the automatic setup doesn't work (e.g., in some CI/CD environments or local file installations), run:
72
+
73
+ ```bash
74
+ npx signiphi-setup
75
+ ```
76
+
77
+ **Note for local installations:** When installing from a local directory using `file:` protocol (e.g., `npm install file:../pdf-signer`), npm may not run the postinstall script automatically. You must run `npx signiphi-setup` manually after installation. See [INSTALLING_LOCALLY.md](./INSTALLING_LOCALLY.md) for details.
78
+
79
+ Or with a custom directory:
80
+
81
+ ```bash
82
+ npx signiphi-setup public/my-custom-path
83
+ npx signiphi-setup --force # Force reinstall
84
+ ```
85
+
86
+ ## Framework Compatibility
87
+
88
+ ### ✅ Supported Frameworks
89
+
90
+ This package works with all modern React frameworks:
91
+ - ✅ **Vite** - Works out of the box
92
+ - ✅ **Create React App (CRA)** - Works out of the box
93
+ - ✅ **Next.js** - Requires client-side component (see below)
94
+ - ✅ **Remix** - Use with `ClientOnly` wrapper
95
+ - ✅ **Gatsby** - Works out of the box
96
+ - ✅ **Any React 18+ project**
97
+
98
+ ### Next.js Usage
99
+
100
+ This package uses browser APIs (`window`, `iframe`, `document`) and **must run client-side only**.
101
+
102
+ #### App Router (Next.js 13+)
103
+
104
+ Add `'use client'` directive to components using the PDF viewer:
105
+
106
+ ```tsx
107
+ 'use client';
108
+
109
+ import { PdfViewerStyled } from '@signiphi/pdf-signer';
110
+ import '@signiphi/pdf-signer/styles';
111
+
112
+ export default function PdfPage() {
113
+ return (
114
+ <PdfViewerStyled
115
+ pdfUrl="/document.pdf"
116
+ onLoad={() => console.log('PDF loaded')}
117
+ />
118
+ );
119
+ }
120
+ ```
121
+
122
+ #### Pages Router (Next.js 12 and below)
123
+
124
+ Use dynamic import with `ssr: false`:
125
+
126
+ ```tsx
127
+ import dynamic from 'next/dynamic';
128
+
129
+ const PdfViewerStyled = dynamic(
130
+ () => import('@signiphi/pdf-signer').then(mod => mod.PdfViewerStyled),
131
+ { ssr: false }
132
+ );
133
+
134
+ export default function PdfPage() {
135
+ return <PdfViewerStyled pdfUrl="/document.pdf" />;
136
+ }
137
+ ```
138
+
139
+ #### Next.js Configuration (Optional)
140
+
141
+ For automatic PDF.js file copying during build:
142
+
143
+ ```javascript
144
+ // next.config.js
145
+ const { withSigniphiPdfJs } = require('@signiphi/pdf-signer/next');
146
+
147
+ module.exports = withSigniphiPdfJs({
148
+ // your next.js config
149
+ });
150
+ ```
151
+
152
+ ### Framework Plugins (Optional)
153
+
154
+ For additional build-time reliability:
155
+
156
+ **Vite:**
157
+ ```typescript
158
+ // vite.config.ts
159
+ import { defineConfig } from 'vite';
160
+ import { signiphiPdfJs } from '@signiphi/pdf-signer/vite';
161
+
162
+ export default defineConfig({
163
+ plugins: [signiphiPdfJs()],
164
+ });
165
+ ```
166
+
167
+ **Next.js:**
168
+ ```javascript
169
+ // next.config.js
170
+ const { withSigniphiPdfJs } = require('@signiphi/pdf-signer/next');
171
+
172
+ module.exports = withSigniphiPdfJs({
173
+ // your next.js config
174
+ });
175
+ ```
176
+
177
+ #### Custom Configuration (Advanced)
178
+
179
+ If you need to use a different path:
180
+
181
+ ```typescript
182
+ import { setPdfJsConfig } from '@signiphi/pdf-signer';
183
+
184
+ // Custom path
185
+ setPdfJsConfig({
186
+ viewerBasePath: '/custom/path/pdfjs',
187
+ workerSrc: '/custom/path/pdfjs/build/pdf.worker.mjs'
188
+ });
189
+ ```
190
+
191
+ **Troubleshooting:**
192
+
193
+ If PDF.js files weren't copied automatically:
194
+
195
+ 1. **Check if postinstall ran:**
196
+ ```bash
197
+ # Look for this message during install:
198
+ # ✓ @signiphi/pdf-signer: PDF.js files copied to /path/to/public/pdfjs
199
+ ```
200
+
201
+ 2. **Verify pdfjs-dist is installed:**
202
+ ```bash
203
+ npm list pdfjs-dist
204
+ # Should show: pdfjs-dist@5.3.93
205
+ ```
206
+
207
+ 3. **Run manual setup:**
208
+ ```bash
209
+ npx signiphi-setup
210
+ ```
211
+
212
+ 4. **Check that `public/pdfjs/` directory exists:**
213
+ ```bash
214
+ ls public/pdfjs/
215
+ # Should show: build/, web/, .version
216
+ ```
217
+
218
+ 5. **If using CI/CD with `--ignore-scripts`:**
219
+ ```bash
220
+ # Postinstall won't run, use manual setup:
221
+ npx signiphi-setup
222
+ ```
223
+
224
+ **Note:** CDN loading is not recommended due to CORS issues. The package uses local files by default.
225
+
226
+ ## Quick Start
227
+
228
+ ### Using Styled Components (Easiest)
229
+
230
+ ```tsx
231
+ import { PdfViewer, SignatureCanvas } from '@signiphi/pdf-signer';
232
+ import { useRef } from 'react';
233
+ import type { PdfViewerRef, SignatureCanvasRef } from '@signiphi/pdf-signer';
234
+
235
+ function App() {
236
+ const pdfViewerRef = useRef<PdfViewerRef>(null);
237
+ const signatureRef = useRef<SignatureCanvasRef>(null);
238
+
239
+ const handleLoadPdf = async () => {
240
+ await pdfViewerRef.current?.loadPdf('/path/to/document.pdf');
241
+ };
242
+
243
+ const handleSaveSignature = (dataUrl: string) => {
244
+ console.log('Signature saved:', dataUrl);
245
+ };
246
+
247
+ return (
248
+ <div>
249
+ <PdfViewer
250
+ ref={pdfViewerRef}
251
+ onLoad={() => console.log('PDF loaded')}
252
+ onError={(error) => console.error('Error:', error)}
253
+ />
254
+
255
+ <SignatureCanvas
256
+ ref={signatureRef}
257
+ onSignature={handleSaveSignature}
258
+ />
259
+ </div>
260
+ );
261
+ }
262
+ ```
263
+
264
+ ### Using Hooks
265
+
266
+ ```tsx
267
+ import { usePdfViewer, useSignatureCapture } from '@signiphi/pdf-signer';
268
+
269
+ function App() {
270
+ const {
271
+ viewerRef,
272
+ isLoading,
273
+ error,
274
+ loadPdf,
275
+ getFormFieldValues,
276
+ } = usePdfViewer();
277
+
278
+ const {
279
+ canvasRef,
280
+ signatureDataUrl,
281
+ saveSignature,
282
+ clear,
283
+ } = useSignatureCapture();
284
+
285
+ const handleSubmit = async () => {
286
+ const formValues = await getFormFieldValues();
287
+ const signature = saveSignature();
288
+
289
+ console.log('Form values:', formValues);
290
+ console.log('Signature:', signature);
291
+ };
292
+
293
+ return (
294
+ <div>
295
+ <PdfViewer ref={viewerRef} />
296
+ <SignatureCanvas ref={canvasRef} />
297
+ <button onClick={handleSubmit}>Submit</button>
298
+ </div>
299
+ );
300
+ }
301
+ ```
302
+
303
+ ### Using Headless Components (Maximum Flexibility)
304
+
305
+ ```tsx
306
+ import { PdfViewerCore, SignatureCaptureCore } from '@signiphi/pdf-signer/headless';
307
+ import type { PdfViewerRef, SignatureCanvasRef } from '@signiphi/pdf-signer';
308
+
309
+ function CustomPdfViewer() {
310
+ const viewerRef = useRef<PdfViewerRef>(null);
311
+
312
+ return (
313
+ <PdfViewerCore
314
+ ref={viewerRef}
315
+ onLoad={() => console.log('Loaded')}
316
+ onError={(err) => console.error(err)}
317
+ >
318
+ {({ iframeRef, handleIframeLoad }) => (
319
+ <div className="my-custom-viewer">
320
+ <iframe
321
+ ref={iframeRef}
322
+ onLoad={handleIframeLoad}
323
+ className="w-full h-screen"
324
+ />
325
+ </div>
326
+ )}
327
+ </PdfViewerCore>
328
+ );
329
+ }
330
+ ```
331
+
332
+ ## Error Handling
333
+
334
+ The package includes comprehensive error handling with custom error classes and an ErrorBoundary component.
335
+
336
+ ### ErrorBoundary Component
337
+
338
+ Wrap your PDF components in an ErrorBoundary to catch and handle errors gracefully:
339
+
340
+ ```tsx
341
+ import { ErrorBoundary, PdfViewerStyled } from '@signiphi/pdf-signer';
342
+
343
+ function App() {
344
+ return (
345
+ <ErrorBoundary
346
+ fallback={({ error, resetErrorBoundary }) => (
347
+ <div>
348
+ <h2>Something went wrong</h2>
349
+ <p>{error.message}</p>
350
+ <button onClick={resetErrorBoundary}>Try Again</button>
351
+ </div>
352
+ )}
353
+ onError={(error, errorInfo) => {
354
+ // Log to your error tracking service
355
+ console.error('PDF Error:', error, errorInfo);
356
+ }}
357
+ >
358
+ <PdfViewerStyled pdfUrl="/document.pdf" formFields={fields} />
359
+ </ErrorBoundary>
360
+ );
361
+ }
362
+ ```
363
+
364
+ ### Custom Error Classes
365
+
366
+ The package provides specific error types for better error handling:
367
+
368
+ ```typescript
369
+ import {
370
+ PdfValidationError,
371
+ PdfProcessingError,
372
+ FormFieldError,
373
+ AttachmentValidationError,
374
+ isPdfValidationError,
375
+ getErrorMessage,
376
+ } from '@signiphi/pdf-signer/utils';
377
+
378
+ try {
379
+ // PDF operations
380
+ await loadPdf(url);
381
+ } catch (err) {
382
+ if (isPdfValidationError(err)) {
383
+ console.error('PDF validation failed:', err.message);
384
+ } else if (isFormFieldError(err)) {
385
+ console.error('Field error:', err.fieldName, err.message);
386
+ } else {
387
+ console.error('Unknown error:', getErrorMessage(err));
388
+ }
389
+ }
390
+ ```
391
+
392
+ ## Validation
393
+
394
+ ### PDF Validation
395
+
396
+ Validate PDF files before loading:
397
+
398
+ ```typescript
399
+ import { validatePdfBytes, validatePdfUrl } from '@signiphi/pdf-signer/utils';
400
+
401
+ // Validate PDF URL format
402
+ const urlResult = validatePdfUrl('/documents/contract.pdf');
403
+ if (!urlResult.valid) {
404
+ console.error(urlResult.error);
405
+ }
406
+
407
+ // Validate PDF bytes (after fetching)
408
+ const pdfBytes = await fetch(url).then(r => r.arrayBuffer());
409
+ const bytesResult = validatePdfBytes(new Uint8Array(pdfBytes));
410
+ if (!bytesResult.valid) {
411
+ console.error(bytesResult.error);
412
+ }
413
+ ```
414
+
415
+ ### Form Field Validation
416
+
417
+ Validate form fields and field values:
418
+
419
+ ```typescript
420
+ import {
421
+ validateFormField,
422
+ validateFieldValues,
423
+ validateSignatures,
424
+ } from '@signiphi/pdf-signer/utils';
425
+
426
+ // Validate field definition
427
+ const fieldResult = validateFormField({
428
+ id: 'field1',
429
+ name: 'firstName',
430
+ type: FormFieldType.TEXT,
431
+ label: 'First Name',
432
+ position: { x: 100, y: 200, width: 150, height: 30, page: 1 },
433
+ required: true,
434
+ });
435
+
436
+ if (!fieldResult.valid) {
437
+ console.error('Field errors:', fieldResult.errors);
438
+ }
439
+
440
+ // Validate field values
441
+ const valuesResult = validateFieldValues({
442
+ field1: 'value1',
443
+ field2: 'value2',
444
+ });
445
+
446
+ // Validate signatures
447
+ const sigsResult = validateSignatures({
448
+ sig1: 'data:image/png;base64,...',
449
+ });
450
+
451
+ if (!sigsResult.valid) {
452
+ console.error('Signature errors:', sigsResult.errors);
453
+ }
454
+ ```
455
+
456
+ ### Attachment Validation
457
+
458
+ Validate file uploads with custom constraints:
459
+
460
+ ```typescript
461
+ import {
462
+ validateFile,
463
+ validateFiles,
464
+ validateFileOrThrow,
465
+ formatFileSize,
466
+ DEFAULT_ATTACHMENT_CONSTRAINTS,
467
+ } from '@signiphi/pdf-signer/utils';
468
+
469
+ // Validate single file
470
+ const result = validateFile(file, {
471
+ maxFileSize: 10 * 1024 * 1024, // 10MB
472
+ maxTotalSize: 50 * 1024 * 1024, // 50MB
473
+ maxFiles: 10,
474
+ allowedTypes: ['image/*', 'application/pdf'],
475
+ allowedExtensions: ['.pdf', '.jpg', '.png'],
476
+ });
477
+
478
+ if (!result.valid) {
479
+ result.errors.forEach(error => console.error(error));
480
+ }
481
+
482
+ // Throw on validation failure
483
+ try {
484
+ validateFileOrThrow(file, constraints);
485
+ // File is valid, proceed with upload
486
+ } catch (err) {
487
+ if (isAttachmentValidationError(err)) {
488
+ alert(err.message);
489
+ console.log('File:', err.fileName);
490
+ }
491
+ }
492
+
493
+ // Validate multiple files with existing attachments
494
+ const filesResult = validateFiles(newFiles, existingAttachments, constraints);
495
+ ```
496
+
497
+ ## TypeScript Support
498
+
499
+ The package is built with TypeScript and provides full type safety.
500
+
501
+ ### Strict Type Checking
502
+
503
+ The package is built with strict TypeScript settings enabled:
504
+ - `strict: true`
505
+ - `noImplicitReturns: true`
506
+ - `noUncheckedIndexedAccess: true`
507
+
508
+ This ensures maximum type safety and helps catch errors at compile time.
509
+
510
+ ### Type Imports
511
+
512
+ Import types for use in your code:
513
+
514
+ ```typescript
515
+ import type {
516
+ FormField,
517
+ FormFieldType,
518
+ FormFieldPosition,
519
+ MultiSignerContext,
520
+ Attachment,
521
+ AttachmentConstraints,
522
+ EsignFormField,
523
+ Signer,
524
+ SubmissionData,
525
+ } from '@signiphi/pdf-signer/types';
526
+
527
+ // Define form fields with full type safety
528
+ const fields: FormField[] = [
529
+ {
530
+ id: 'field1',
531
+ name: 'firstName',
532
+ type: FormFieldType.TEXT,
533
+ label: 'First Name',
534
+ position: { x: 100, y: 200, width: 150, height: 30, page: 1 },
535
+ required: true,
536
+ }
537
+ ];
538
+
539
+ // Multi-signer context
540
+ const context: MultiSignerContext = {
541
+ isMultiSigner: true,
542
+ currentSignerEmail: 'user@example.com',
543
+ isPrimarySigner: true,
544
+ isFinalSigner: false,
545
+ };
546
+ ```
547
+
548
+ ### Type Guards
549
+
550
+ Use type guards to safely handle errors:
551
+
552
+ ```typescript
553
+ import {
554
+ isPdfValidationError,
555
+ isPdfProcessingError,
556
+ isFormFieldError,
557
+ isAttachmentValidationError,
558
+ } from '@signiphi/pdf-signer/utils';
559
+
560
+ try {
561
+ // Operations
562
+ } catch (err) {
563
+ if (isPdfValidationError(err)) {
564
+ // TypeScript knows err is PdfValidationError here
565
+ console.error('PDF error:', err.message, err.details);
566
+ } else if (isFormFieldError(err)) {
567
+ // TypeScript knows err is FormFieldError here
568
+ console.error('Field error:', err.fieldName, err.message);
569
+ }
570
+ }
571
+ ```
572
+
573
+ ## API Reference
574
+
575
+ ### Components
576
+
577
+ #### `<PdfViewer />`
578
+
579
+ Pre-styled PDF viewer component.
580
+
581
+ **Props:**
582
+ - `className?: string` - Additional CSS classes
583
+ - `onLoad?: () => void` - Callback when PDF loads
584
+ - `onError?: (error: string) => void` - Callback on error
585
+
586
+ **Ref Methods:**
587
+ - `loadPdf(url: string): Promise<void>` - Load a PDF from URL
588
+ - `getFormFieldValues(): Promise<Record<string, string>>` - Get form field values
589
+ - `getAllFieldNames(): Promise<string[]>` - Get all field names
590
+ - `saveDocument(): Promise<Uint8Array | null>` - Save modified PDF
591
+
592
+ #### `<SignatureCanvas />`
593
+
594
+ Pre-styled signature canvas component.
595
+
596
+ **Props:**
597
+ - `width?: number` - Canvas width (default: 400)
598
+ - `height?: number` - Canvas height (default: 200)
599
+ - `onSignature?: (dataUrl: string) => void` - Callback when signature is saved
600
+ - `onCancel?: () => void` - Callback when cancelled
601
+ - `title?: string` - Placeholder text
602
+ - `className?: string` - Additional CSS classes
603
+ - `showActions?: boolean` - Show action buttons (default: true)
604
+
605
+ **Ref Methods:**
606
+ - `clear(): void` - Clear the canvas
607
+ - `getSignatureDataUrl(): string | null` - Get signature as data URL
608
+ - `isEmpty(): boolean` - Check if canvas is empty
609
+
610
+ ### Hooks
611
+
612
+ #### `usePdfViewer()`
613
+
614
+ Hook for managing PDF viewer state.
615
+
616
+ **Returns:**
617
+ ```typescript
618
+ {
619
+ viewerRef: RefObject<PdfViewerRef>;
620
+ isLoading: boolean;
621
+ error: string | null;
622
+ isLoaded: boolean;
623
+ loadPdf: (url: string) => Promise<void>;
624
+ handleLoad: () => void;
625
+ handleError: (error: string) => void;
626
+ getFormFieldValues: () => Promise<Record<string, string>>;
627
+ getAllFieldNames: () => Promise<string[]>;
628
+ saveDocument: () => Promise<Uint8Array | null>;
629
+ }
630
+ ```
631
+
632
+ #### `useFormFields(fields: FormField[])`
633
+
634
+ Hook for managing form field state and validation.
635
+
636
+ **Returns:**
637
+ ```typescript
638
+ {
639
+ fieldValues: Record<string, string>;
640
+ errors: ValidationError[];
641
+ hasErrors: boolean;
642
+ updateField: (fieldId: string, value: string) => void;
643
+ updateMultipleFields: (values: Record<string, string>) => void;
644
+ validateFields: () => boolean;
645
+ resetFields: () => void;
646
+ getFieldValue: (fieldId: string) => string;
647
+ }
648
+ ```
649
+
650
+ #### `useSignatureCapture()`
651
+
652
+ Hook for managing signature capture state.
653
+
654
+ **Returns:**
655
+ ```typescript
656
+ {
657
+ canvasRef: RefObject<SignatureCanvasRef>;
658
+ signatureDataUrl: string | null;
659
+ uploadedImage: string | null;
660
+ uploadError: string | null;
661
+ clear: () => void;
662
+ saveSignature: () => string | null;
663
+ isEmpty: () => boolean;
664
+ handleFileUpload: (file: File) => void;
665
+ clearUpload: () => void;
666
+ reset: () => void;
667
+ }
668
+ ```
669
+
670
+ ### Configuration API
671
+
672
+ #### `setPdfJsConfig(config)`
673
+
674
+ Configure PDF.js paths globally.
675
+
676
+ ```typescript
677
+ import { setPdfJsConfig } from '@signiphi/pdf-signer';
678
+
679
+ setPdfJsConfig({
680
+ viewerBasePath: '/pdfjs', // Path to PDF.js viewer
681
+ workerSrc: '/pdfjs/build/pdf.worker.mjs' // Path to worker (optional, auto-calculated)
682
+ });
683
+ ```
684
+
685
+ #### `getPdfJsConfig()`
686
+
687
+ Get current PDF.js configuration.
688
+
689
+ ```typescript
690
+ import { getPdfJsConfig } from '@signiphi/pdf-signer';
691
+
692
+ const config = getPdfJsConfig();
693
+ console.log(config.viewerBasePath); // Current viewer path
694
+ ```
695
+
696
+ #### `resetPdfJsConfig()`
697
+
698
+ Reset configuration to CDN defaults.
699
+
700
+ ```typescript
701
+ import { resetPdfJsConfig } from '@signiphi/pdf-signer';
702
+
703
+ resetPdfJsConfig(); // Resets to CDN
704
+ ```
705
+
706
+ ### Types
707
+
708
+ ```typescript
709
+ import type {
710
+ FormField,
711
+ FormFieldType,
712
+ FormFieldPosition,
713
+ PdfViewerRef,
714
+ SignatureCanvasRef,
715
+ PdfPage,
716
+ Submission,
717
+ ValidationError,
718
+ PdfJsConfig,
719
+ } from '@signiphi/pdf-signer';
720
+ ```
721
+
722
+ ## Tailwind Configuration
723
+
724
+ The package uses **Tailwind CSS v4** with CSS-based configuration.
725
+
726
+ ### Option 1: Using Vite (Recommended)
727
+
728
+ If using Vite, install the Tailwind Vite plugin:
729
+
730
+ ```bash
731
+ npm install -D @tailwindcss/vite tailwindcss
732
+ ```
733
+
734
+ Update your `vite.config.ts`:
735
+
736
+ ```ts
737
+ import { defineConfig } from 'vite';
738
+ import react from '@vitejs/plugin-react';
739
+ import tailwindcss from '@tailwindcss/vite';
740
+
741
+ export default defineConfig({
742
+ plugins: [react(), tailwindcss()],
743
+ });
744
+ ```
745
+
746
+ ### Option 2: Using PostCSS
747
+
748
+ Install dependencies:
749
+
750
+ ```bash
751
+ npm install -D @tailwindcss/postcss tailwindcss
752
+ ```
753
+
754
+ Create/update `postcss.config.js`:
755
+
756
+ ```js
757
+ module.exports = {
758
+ plugins: {
759
+ '@tailwindcss/postcss': {},
760
+ },
761
+ };
762
+ ```
763
+
764
+ ### Import Styles
765
+
766
+ In your main entry file:
767
+
768
+ ```tsx
769
+ import '@signiphi/pdf-signer/styles';
770
+ ```
771
+
772
+ That's it! Tailwind v4 uses CSS-based configuration, so no `tailwind.config.js` file is needed.
773
+
774
+ ## Design System
775
+
776
+ The package uses the **DockMaster Design System** for consistent branding and styling.
777
+
778
+ ### Colors
779
+
780
+ The package uses OKLCH color space with the following palette:
781
+
782
+ - **Primary Blue**: `#0099CD` - Buttons, links, interactive elements
783
+ - **Light Blue**: `#BDD9E8` - Borders, secondary backgrounds
784
+ - **Dark Blue**: `#161848` - Text, foreground elements
785
+ - **Yellow**: `#FFC844` - Accent color, highlights
786
+ - **Greys**: Various shades for borders, backgrounds, and muted content
787
+
788
+ **Dark mode** is fully supported with adjusted colors for optimal readability.
789
+
790
+ ### Typography
791
+
792
+ #### Body Font
793
+ Uses system font stack for optimal performance and native feel.
794
+
795
+ #### Signature Font
796
+ **Dancing Script** (Google Fonts) is used for signature and initials fields.
797
+
798
+ ✅ **Automatically included** - The font is imported when you import the package styles, no additional setup needed!
799
+
800
+ ### Border Radius
801
+
802
+ Default radius: `0.625rem` (10px)
803
+
804
+ ### Responsive Design
805
+
806
+ All components are fully responsive with breakpoints:
807
+ - **Mobile**: < 640px
808
+ - **Tablet**: 640px - 1024px
809
+ - **Desktop**: 1024px+
810
+
811
+ Components adapt their layout, spacing, and font sizes for optimal viewing on all devices.
812
+
813
+ ### Dark Mode
814
+
815
+ Enable dark mode by adding the `dark` class to your root element:
816
+
817
+ ```tsx
818
+ <html className="dark">
819
+ {/* Your app */}
820
+ </html>
821
+ ```
822
+
823
+ Or use a theme provider like `next-themes`:
824
+
825
+ ```tsx
826
+ import { ThemeProvider } from 'next-themes';
827
+
828
+ function App() {
829
+ return (
830
+ <ThemeProvider attribute="class">
831
+ <SubmissionForm {...props} />
832
+ </ThemeProvider>
833
+ );
834
+ }
835
+ ```
836
+
837
+ For more details, see **[DESIGN_SYSTEM.md](./DESIGN_SYSTEM.md)**.
838
+
839
+ ## Examples
840
+
841
+ See the `examples/demo` directory for a complete working example.
842
+
843
+ ## Multi-Signer Support
844
+
845
+ The package supports multi-signer workflows where multiple people sign the same document in sequence. Each signer only sees and fills their assigned fields.
846
+
847
+ ### Quick Start
848
+
849
+ ```tsx
850
+ import { SubmissionForm, type Signer } from '@signiphi/pdf-signer';
851
+
852
+ function DocumentSigningPage() {
853
+ // Backend provides current signer information
854
+ const currentSigner: Signer = {
855
+ id: "1",
856
+ email: "user@example.com",
857
+ name: "John Doe",
858
+ signOrder: 1, // 1 = primary, 2+ = secondary
859
+ status: "pending"
860
+ };
861
+
862
+ return (
863
+ <SubmissionForm
864
+ pdfUrl="/contract.pdf"
865
+ // Multi-signer props
866
+ currentSigner={currentSigner}
867
+ isMultipleSignature={true}
868
+ totalSigners={3}
869
+ // Callback
870
+ onSubmit={async (data) => {
871
+ await submitToBackend(data);
872
+ }}
873
+ />
874
+ );
875
+ }
876
+ ```
877
+
878
+ ### Key Features
879
+
880
+ - **Field Filtering**: Each signer only sees fields assigned to them
881
+ - **Partial Flattening**: Each signer flattens only their fields, leaving others editable
882
+ - **Role-Based Visibility**: Primary, secondary, and final signer roles with different field access
883
+ - **PDF Viewer Filtering**: Modified PDF shown to each signer (fields removed), original preserved
884
+
885
+ ### Signer Roles
886
+
887
+ #### Primary Signer (`signOrder === 1`)
888
+ - Usually the document creator or main party
889
+ - Sees: Own assigned fields + "recipients" group fields
890
+
891
+ #### Secondary Signer (`signOrder > 1`, not final)
892
+ - Middle signers in the chain
893
+ - Sees: Only their own assigned fields
894
+
895
+ #### Final Signer (`signOrder === totalSigners`)
896
+ - Last person in the signing chain
897
+ - Sees: Own assigned fields + "signers" group fields + unassigned fields
898
+
899
+ ### Field Assignment Patterns
900
+
901
+ ```typescript
902
+ // Direct assignment to specific signer
903
+ const field = {
904
+ id: 'buyer_signature',
905
+ assignedSignerEmail: 'buyer@example.com',
906
+ // ...
907
+ };
908
+
909
+ // Recipients group (all primaries)
910
+ const field = {
911
+ id: 'company_name',
912
+ assignedSignerEmail: 'recipients',
913
+ // ...
914
+ };
915
+
916
+ // Signers group (final signer only)
917
+ const field = {
918
+ id: 'completion_date',
919
+ assignedSignerEmail: 'signers',
920
+ // ...
921
+ };
922
+ ```
923
+
924
+ ### Complete Guide
925
+
926
+ For detailed information including:
927
+ - Visibility rules matrix
928
+ - Backend integration examples
929
+ - Testing strategies
930
+ - Advanced topics
931
+
932
+ See **[MULTI_SIGNER.md](./MULTI_SIGNER.md)** for the complete guide.
933
+
934
+ ## Testing
935
+
936
+ The package includes comprehensive test coverage:
937
+
938
+ ### Test Coverage
939
+
940
+ - **Utility Tests** (~182 tests) - Validation, PDF manipulation, field visibility, error handling
941
+ - **Hook Tests** (~150 tests) - All React hooks with full integration testing
942
+ - `useSignatures` - Signature state management
943
+ - `useAttachments` - File attachment handling
944
+ - `useFormFields` - Form field state and validation
945
+ - `useMultiSignerContext` - Multi-signer context logic
946
+ - `useFieldFiltering` - Field visibility filtering
947
+ - `usePdfViewer` - PDF viewer operations
948
+
949
+ ### Running Tests
950
+
951
+ ```bash
952
+ # Run all tests
953
+ pnpm test
954
+
955
+ # Run with coverage
956
+ pnpm test:coverage
957
+
958
+ # Run in watch mode
959
+ pnpm test:watch
960
+ ```
961
+
962
+ ### Test Environment
963
+
964
+ Tests use Vitest with **happy-dom** for React component testing. The test suite includes:
965
+ - Unit tests for utilities and hooks
966
+ - Integration tests for multi-signer workflows
967
+ - Mocked PDF.js and pdf-lib for fast, reliable testing
968
+ - Comprehensive edge case coverage
969
+
970
+ **Why happy-dom?** We use happy-dom instead of jsdom for better Windows compatibility, faster execution, and lighter memory footprint while maintaining full DOM API support.
971
+
972
+ ## Browser Support
973
+
974
+ - Chrome/Edge 90+
975
+ - Firefox 88+
976
+ - Safari 14+
977
+ - Mobile browsers with touch support
978
+
979
+ ## Performance & Bundle Size
980
+
981
+ ### Optimized Bundle Sizes
982
+
983
+ The package is optimized for minimal bundle size impact:
984
+
985
+ | Entry Point | Raw Size | Gzipped | Use Case |
986
+ |-------------|----------|---------|----------|
987
+ | Main (`@signiphi/pdf-signer`) | 193 KB | 38 KB | Full package |
988
+ | Headless (`/headless`) | 21 KB | 5 KB | Core functionality only |
989
+ | Components (`/components`) | 167 KB | 33 KB | UI components only |
990
+ | Hooks (`/hooks`) | 71 KB | 14 KB | React hooks only |
991
+ | Utils (`/utils`) | 73 KB | 15 KB | Utilities only |
992
+
993
+ ### Lazy Loading
994
+
995
+ The package uses lazy loading for heavy dependencies to minimize initial bundle size:
996
+
997
+ ```typescript
998
+ // pdf-lib (~150 KB) is only loaded when actually needed
999
+ import { fillPdfWithSignatures } from '@signiphi/pdf-signer';
1000
+
1001
+ // pdf-lib loads automatically on first use
1002
+ await fillPdfWithSignatures(pdfBytes, signatures, formValues);
1003
+ ```
1004
+
1005
+ **Benefits:**
1006
+ - ✅ View-only users never load pdf-lib (~150 KB savings)
1007
+ - ✅ Form filling users only pay the cost when needed
1008
+ - ✅ Automatic code splitting
1009
+
1010
+ ### Tree-Shaking
1011
+
1012
+ Import only what you need using specific entry points:
1013
+
1014
+ ```typescript
1015
+ // Import just the headless core (no UI)
1016
+ import { PdfViewerCore } from '@signiphi/pdf-signer/headless';
1017
+
1018
+ // Import just hooks (no components)
1019
+ import { usePdfViewer } from '@signiphi/pdf-signer/hooks';
1020
+
1021
+ // Import just utilities (no React code)
1022
+ import { validatePdfBytes } from '@signiphi/pdf-signer/utils';
1023
+
1024
+ // Import just components (no hooks)
1025
+ import { SignatureCanvas } from '@signiphi/pdf-signer/components';
1026
+ ```
1027
+
1028
+ ### Performance Monitoring
1029
+
1030
+ Built-in performance monitoring utilities for tracking critical operations:
1031
+
1032
+ ```typescript
1033
+ import { createPerformanceMonitor } from '@signiphi/pdf-signer';
1034
+
1035
+ // Create a monitor
1036
+ const monitor = createPerformanceMonitor();
1037
+
1038
+ // Track PDF loading
1039
+ const endTimer = monitor.startTimer('pdf-load');
1040
+ await loadPdf(url);
1041
+ endTimer();
1042
+
1043
+ // Get metrics
1044
+ const metrics = monitor.getMetrics();
1045
+ console.log(`PDF load avg: ${metrics['pdf-load'].avg}ms`);
1046
+ ```
1047
+
1048
+ **Track:**
1049
+ - PDF loading time
1050
+ - Form field updates
1051
+ - Signature capture performance
1052
+ - Custom operations
1053
+
1054
+ ### Bundle Analysis
1055
+
1056
+ Analyze your bundle size:
1057
+
1058
+ ```bash
1059
+ # Analyze bundle sizes
1060
+ pnpm analyze
1061
+
1062
+ # Check against budgets
1063
+ pnpm analyze:check
1064
+
1065
+ # Build and analyze
1066
+ pnpm build:analyze
1067
+ ```
1068
+
1069
+ ### Production Builds
1070
+
1071
+ For production, set `NODE_ENV=production` to enable minification:
1072
+
1073
+ ```bash
1074
+ NODE_ENV=production pnpm build
1075
+ ```
1076
+
1077
+ This reduces bundle size by ~20-30% through minification.
1078
+
1079
+ ## Contributing
1080
+
1081
+ Contributions are welcome! Please open an issue or pull request.
1082
+
1083
+ ## Documentation
1084
+
1085
+ - **[API.md](./API.md)** - Comprehensive API reference for all components, hooks, and utilities
1086
+ - **[TROUBLESHOOTING.md](./TROUBLESHOOTING.md)** - Common issues and solutions
1087
+ - **[MULTI_SIGNER.md](./MULTI_SIGNER.md)** - Complete multi-signer implementation guide
1088
+ - **[DESIGN_SYSTEM.md](./DESIGN_SYSTEM.md)** - Design system and styling guide
1089
+
1090
+ ## Support
1091
+
1092
+ For issues and questions, please open a GitHub issue or refer to the TROUBLESHOOTING.md guide.
1093
+