@signiphi/page-assembly 0.2.0-beta.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.
package/dist/index.mjs ADDED
@@ -0,0 +1,1837 @@
1
+ import * as React7 from 'react';
2
+ import { forwardRef, useReducer, useState, useRef, useMemo, useEffect, useCallback, useImperativeHandle } from 'react';
3
+ import { DndProvider, useDrop, useDrag } from 'react-dnd';
4
+ import { HTML5Backend } from 'react-dnd-html5-backend';
5
+ import { ChevronDown, ChevronUp, Check, ChevronRight, Circle, Upload, FolderOpen, FileText, Image as Image$1, Trash2, X, MoreHorizontal, GripVertical } from 'lucide-react';
6
+ import { Slot } from '@radix-ui/react-slot';
7
+ import { cva } from 'class-variance-authority';
8
+ import { clsx } from 'clsx';
9
+ import { twMerge } from 'tailwind-merge';
10
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
11
+ import * as LabelPrimitive from '@radix-ui/react-label';
12
+ import * as SelectPrimitive from '@radix-ui/react-select';
13
+ import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
14
+ import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
15
+ import * as TooltipPrimitive from '@radix-ui/react-tooltip';
16
+ import * as pdfjsLib from 'pdfjs-dist';
17
+ import { PDFDocument } from 'pdf-lib';
18
+
19
+ // src/components/PageAssembler.tsx
20
+ function cn(...inputs) {
21
+ return twMerge(clsx(inputs));
22
+ }
23
+ var buttonVariants = cva(
24
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
25
+ {
26
+ variants: {
27
+ variant: {
28
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
29
+ destructive: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
30
+ outline: "border border-input bg-background hover:bg-accent hover:text-accent-foreground",
31
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
32
+ ghost: "hover:bg-accent hover:text-accent-foreground",
33
+ link: "text-primary underline-offset-4 hover:underline"
34
+ },
35
+ size: {
36
+ default: "h-10 px-4 py-2",
37
+ sm: "h-9 rounded-md px-3",
38
+ lg: "h-11 md:h-12 rounded-md px-8",
39
+ icon: "h-10 w-10"
40
+ }
41
+ },
42
+ defaultVariants: {
43
+ variant: "default",
44
+ size: "default"
45
+ }
46
+ }
47
+ );
48
+ var Button = React7.forwardRef(
49
+ ({ className, variant, size, asChild = false, ...props }, ref) => {
50
+ const Comp = asChild ? Slot : "button";
51
+ return /* @__PURE__ */ jsx(
52
+ Comp,
53
+ {
54
+ className: cn(buttonVariants({ variant, size }), className),
55
+ ref,
56
+ ...props
57
+ }
58
+ );
59
+ }
60
+ );
61
+ Button.displayName = "Button";
62
+ var Card = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
63
+ "div",
64
+ {
65
+ ref,
66
+ className: cn(
67
+ "rounded-lg bg-card text-card-foreground shadow-sm",
68
+ className
69
+ ),
70
+ style: {
71
+ border: "1px solid hsl(var(--xpa-border))",
72
+ ...props.style || {}
73
+ },
74
+ ...props
75
+ }
76
+ ));
77
+ Card.displayName = "Card";
78
+ var CardHeader = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
79
+ "div",
80
+ {
81
+ ref,
82
+ className: cn("flex flex-col space-y-1.5 p-6", className),
83
+ ...props
84
+ }
85
+ ));
86
+ CardHeader.displayName = "CardHeader";
87
+ var CardTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
88
+ "h3",
89
+ {
90
+ ref,
91
+ className: cn(
92
+ "text-2xl font-semibold leading-none tracking-tight",
93
+ className
94
+ ),
95
+ ...props
96
+ }
97
+ ));
98
+ CardTitle.displayName = "CardTitle";
99
+ var CardDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
100
+ "p",
101
+ {
102
+ ref,
103
+ className: cn("text-sm text-muted-foreground", className),
104
+ ...props
105
+ }
106
+ ));
107
+ CardDescription.displayName = "CardDescription";
108
+ var CardContent = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx("div", { ref, className: cn("p-6 pt-0", className), ...props }));
109
+ CardContent.displayName = "CardContent";
110
+ var CardFooter = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
111
+ "div",
112
+ {
113
+ ref,
114
+ className: cn("flex items-center p-6 pt-0", className),
115
+ ...props
116
+ }
117
+ ));
118
+ CardFooter.displayName = "CardFooter";
119
+ var Input = React7.forwardRef(
120
+ ({ className, type, ...props }, ref) => {
121
+ return /* @__PURE__ */ jsx(
122
+ "input",
123
+ {
124
+ type,
125
+ className: cn(
126
+ "flex h-10 w-full rounded-md border-2 border-input bg-background px-3 py-2 text-sm file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground transition-all duration-200 focus:outline-none focus:border-primary focus:ring-0 focus:shadow-[0_0_0_3px_rgba(0,153,205,0.15)] focus-visible:outline-none focus-visible:border-primary focus-visible:ring-0 focus-visible:shadow-[0_0_0_3px_rgba(0,153,205,0.15)] disabled:cursor-not-allowed disabled:opacity-50",
127
+ className
128
+ ),
129
+ ref,
130
+ ...props
131
+ }
132
+ );
133
+ }
134
+ );
135
+ Input.displayName = "Input";
136
+ var labelVariants = cva(
137
+ "text-sm font-medium leading-none peer-disabled:cursor-not-allowed peer-disabled:opacity-70"
138
+ );
139
+ var Label = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
140
+ LabelPrimitive.Root,
141
+ {
142
+ ref,
143
+ className: cn(labelVariants(), className),
144
+ ...props
145
+ }
146
+ ));
147
+ Label.displayName = LabelPrimitive.Root.displayName;
148
+ var badgeVariants = cva(
149
+ "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2",
150
+ {
151
+ variants: {
152
+ variant: {
153
+ default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
154
+ secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
155
+ destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
156
+ outline: "text-foreground"
157
+ }
158
+ },
159
+ defaultVariants: {
160
+ variant: "default"
161
+ }
162
+ }
163
+ );
164
+ function Badge({ className, variant, ...props }) {
165
+ return /* @__PURE__ */ jsx("div", { className: cn(badgeVariants({ variant }), className), ...props });
166
+ }
167
+ var Select = SelectPrimitive.Root;
168
+ var SelectValue = SelectPrimitive.Value;
169
+ var SelectTrigger = React7.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
170
+ SelectPrimitive.Trigger,
171
+ {
172
+ ref,
173
+ className: cn(
174
+ "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
175
+ className
176
+ ),
177
+ ...props,
178
+ children: [
179
+ children,
180
+ /* @__PURE__ */ jsx(SelectPrimitive.Icon, { asChild: true, children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4 opacity-50" }) })
181
+ ]
182
+ }
183
+ ));
184
+ SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
185
+ var SelectScrollUpButton = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
186
+ SelectPrimitive.ScrollUpButton,
187
+ {
188
+ ref,
189
+ className: cn(
190
+ "flex cursor-default items-center justify-center py-1",
191
+ className
192
+ ),
193
+ ...props,
194
+ children: /* @__PURE__ */ jsx(ChevronUp, { className: "h-4 w-4" })
195
+ }
196
+ ));
197
+ SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
198
+ var SelectScrollDownButton = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
199
+ SelectPrimitive.ScrollDownButton,
200
+ {
201
+ ref,
202
+ className: cn(
203
+ "flex cursor-default items-center justify-center py-1",
204
+ className
205
+ ),
206
+ ...props,
207
+ children: /* @__PURE__ */ jsx(ChevronDown, { className: "h-4 w-4" })
208
+ }
209
+ ));
210
+ SelectScrollDownButton.displayName = SelectPrimitive.ScrollDownButton.displayName;
211
+ var SelectContent = React7.forwardRef(({ className, children, position = "popper", ...props }, ref) => /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
212
+ SelectPrimitive.Content,
213
+ {
214
+ ref,
215
+ className: cn(
216
+ "relative max-h-96 min-w-[12rem] overflow-hidden data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
217
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
218
+ className
219
+ ),
220
+ position,
221
+ style: {
222
+ zIndex: 99999,
223
+ backgroundColor: "#ffffff",
224
+ border: "1px solid #e2e8f0",
225
+ borderRadius: "0.5rem",
226
+ boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)",
227
+ marginTop: "4px"
228
+ },
229
+ ...props,
230
+ children: [
231
+ /* @__PURE__ */ jsx(SelectScrollUpButton, {}),
232
+ /* @__PURE__ */ jsx(
233
+ SelectPrimitive.Viewport,
234
+ {
235
+ className: cn(
236
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]"
237
+ ),
238
+ style: { padding: "6px" },
239
+ children
240
+ }
241
+ ),
242
+ /* @__PURE__ */ jsx(SelectScrollDownButton, {})
243
+ ]
244
+ }
245
+ ) }));
246
+ SelectContent.displayName = SelectPrimitive.Content.displayName;
247
+ var SelectLabel = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
248
+ SelectPrimitive.Label,
249
+ {
250
+ ref,
251
+ className: cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className),
252
+ ...props
253
+ }
254
+ ));
255
+ SelectLabel.displayName = SelectPrimitive.Label.displayName;
256
+ var SelectItem = React7.forwardRef(({ className, children, ...props }, ref) => {
257
+ const [isHovered, setIsHovered] = React7.useState(false);
258
+ return /* @__PURE__ */ jsxs(
259
+ SelectPrimitive.Item,
260
+ {
261
+ ref,
262
+ className: cn(
263
+ "relative flex w-full cursor-pointer select-none items-center outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
264
+ className
265
+ ),
266
+ style: {
267
+ backgroundColor: isHovered ? "#f1f5f9" : "transparent",
268
+ color: "#1e293b",
269
+ padding: "8px 12px",
270
+ borderRadius: "6px",
271
+ fontSize: "14px",
272
+ marginBottom: "2px"
273
+ },
274
+ onMouseEnter: () => setIsHovered(true),
275
+ onMouseLeave: () => setIsHovered(false),
276
+ ...props,
277
+ children: [
278
+ /* @__PURE__ */ jsx(SelectPrimitive.ItemText, { children }),
279
+ /* @__PURE__ */ jsx(
280
+ SelectPrimitive.ItemIndicator,
281
+ {
282
+ style: {
283
+ marginLeft: "auto",
284
+ paddingLeft: "8px",
285
+ color: "#0099cd"
286
+ },
287
+ children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" })
288
+ }
289
+ )
290
+ ]
291
+ }
292
+ );
293
+ });
294
+ SelectItem.displayName = SelectPrimitive.Item.displayName;
295
+ var SelectSeparator = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
296
+ SelectPrimitive.Separator,
297
+ {
298
+ ref,
299
+ className: cn("-mx-1 my-1 h-px bg-muted", className),
300
+ ...props
301
+ }
302
+ ));
303
+ SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
304
+ var AlertDialog = AlertDialogPrimitive.Root;
305
+ var AlertDialogPortal = AlertDialogPrimitive.Portal;
306
+ var AlertDialogOverlay = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
307
+ AlertDialogPrimitive.Overlay,
308
+ {
309
+ className: cn(
310
+ "fixed inset-0 z-[9998] bg-black/80 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0",
311
+ className
312
+ ),
313
+ ...props,
314
+ ref
315
+ }
316
+ ));
317
+ AlertDialogOverlay.displayName = AlertDialogPrimitive.Overlay.displayName;
318
+ var AlertDialogContent = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsxs(AlertDialogPortal, { container: document.querySelector(".signiphi-page-assembly"), children: [
319
+ /* @__PURE__ */ jsx(AlertDialogOverlay, {}),
320
+ /* @__PURE__ */ jsx(
321
+ AlertDialogPrimitive.Content,
322
+ {
323
+ ref,
324
+ className: cn(
325
+ "fixed left-[50%] top-[50%] z-[9999] grid w-full max-w-lg translate-x-[-50%] translate-y-[-50%] gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg",
326
+ className
327
+ ),
328
+ ...props
329
+ }
330
+ )
331
+ ] }));
332
+ AlertDialogContent.displayName = AlertDialogPrimitive.Content.displayName;
333
+ var AlertDialogHeader = ({
334
+ className,
335
+ ...props
336
+ }) => /* @__PURE__ */ jsx(
337
+ "div",
338
+ {
339
+ className: cn(
340
+ "flex flex-col space-y-2 text-center sm:text-left",
341
+ className
342
+ ),
343
+ ...props
344
+ }
345
+ );
346
+ AlertDialogHeader.displayName = "AlertDialogHeader";
347
+ var AlertDialogFooter = ({
348
+ className,
349
+ ...props
350
+ }) => /* @__PURE__ */ jsx(
351
+ "div",
352
+ {
353
+ className: cn(
354
+ "flex flex-col-reverse sm:flex-row sm:justify-end sm:space-x-2",
355
+ className
356
+ ),
357
+ ...props
358
+ }
359
+ );
360
+ AlertDialogFooter.displayName = "AlertDialogFooter";
361
+ var AlertDialogTitle = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
362
+ AlertDialogPrimitive.Title,
363
+ {
364
+ ref,
365
+ className: cn("text-lg font-semibold", className),
366
+ ...props
367
+ }
368
+ ));
369
+ AlertDialogTitle.displayName = AlertDialogPrimitive.Title.displayName;
370
+ var AlertDialogDescription = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
371
+ AlertDialogPrimitive.Description,
372
+ {
373
+ ref,
374
+ className: cn("text-sm text-muted-foreground", className),
375
+ ...props
376
+ }
377
+ ));
378
+ AlertDialogDescription.displayName = AlertDialogPrimitive.Description.displayName;
379
+ var AlertDialogAction = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
380
+ AlertDialogPrimitive.Action,
381
+ {
382
+ ref,
383
+ className: cn(buttonVariants(), className),
384
+ ...props
385
+ }
386
+ ));
387
+ AlertDialogAction.displayName = AlertDialogPrimitive.Action.displayName;
388
+ var AlertDialogCancel = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
389
+ AlertDialogPrimitive.Cancel,
390
+ {
391
+ ref,
392
+ className: cn(
393
+ buttonVariants({ variant: "outline" }),
394
+ "mt-2 sm:mt-0",
395
+ className
396
+ ),
397
+ ...props
398
+ }
399
+ ));
400
+ AlertDialogCancel.displayName = AlertDialogPrimitive.Cancel.displayName;
401
+ var DropdownMenu = DropdownMenuPrimitive.Root;
402
+ var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
403
+ var DropdownMenuSubTrigger = React7.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsxs(
404
+ DropdownMenuPrimitive.SubTrigger,
405
+ {
406
+ ref,
407
+ className: cn(
408
+ "flex cursor-default gap-2 select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
409
+ inset && "pl-8",
410
+ className
411
+ ),
412
+ ...props,
413
+ children: [
414
+ children,
415
+ /* @__PURE__ */ jsx(ChevronRight, { className: "ml-auto" })
416
+ ]
417
+ }
418
+ ));
419
+ DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
420
+ var DropdownMenuSubContent = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
421
+ DropdownMenuPrimitive.SubContent,
422
+ {
423
+ ref,
424
+ className: cn(
425
+ "z-50 min-w-[8rem] overflow-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
426
+ className
427
+ ),
428
+ ...props
429
+ }
430
+ ));
431
+ DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
432
+ var DropdownMenuContent = React7.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx(
433
+ DropdownMenuPrimitive.Content,
434
+ {
435
+ ref,
436
+ sideOffset,
437
+ className: cn(
438
+ "min-w-[12rem] overflow-hidden data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
439
+ className
440
+ ),
441
+ style: {
442
+ zIndex: 99999,
443
+ backgroundColor: "#ffffff",
444
+ border: "1px solid #e2e8f0",
445
+ borderRadius: "0.5rem",
446
+ boxShadow: "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1)",
447
+ padding: "6px"
448
+ },
449
+ ...props
450
+ }
451
+ ) }));
452
+ DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
453
+ var DropdownMenuItem = React7.forwardRef(({ className, inset, ...props }, ref) => {
454
+ const [isHovered, setIsHovered] = React7.useState(false);
455
+ return /* @__PURE__ */ jsx(
456
+ DropdownMenuPrimitive.Item,
457
+ {
458
+ ref,
459
+ className: cn(
460
+ "relative flex cursor-pointer select-none items-center gap-2 outline-none transition-colors data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
461
+ inset && "pl-8",
462
+ className
463
+ ),
464
+ style: {
465
+ backgroundColor: isHovered ? "#f1f5f9" : "transparent",
466
+ color: "#1e293b",
467
+ padding: "8px 12px",
468
+ borderRadius: "6px",
469
+ fontSize: "14px",
470
+ marginBottom: "2px"
471
+ },
472
+ onMouseEnter: () => setIsHovered(true),
473
+ onMouseLeave: () => setIsHovered(false),
474
+ ...props
475
+ }
476
+ );
477
+ });
478
+ DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
479
+ var DropdownMenuCheckboxItem = React7.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ jsxs(
480
+ DropdownMenuPrimitive.CheckboxItem,
481
+ {
482
+ ref,
483
+ className: cn(
484
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
485
+ className
486
+ ),
487
+ checked,
488
+ ...props,
489
+ children: [
490
+ /* @__PURE__ */ jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "h-4 w-4" }) }) }),
491
+ children
492
+ ]
493
+ }
494
+ ));
495
+ DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
496
+ var DropdownMenuRadioItem = React7.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
497
+ DropdownMenuPrimitive.RadioItem,
498
+ {
499
+ ref,
500
+ className: cn(
501
+ "relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
502
+ className
503
+ ),
504
+ ...props,
505
+ children: [
506
+ /* @__PURE__ */ jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Circle, { className: "h-2 w-2 fill-current" }) }) }),
507
+ children
508
+ ]
509
+ }
510
+ ));
511
+ DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
512
+ var DropdownMenuLabel = React7.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx(
513
+ DropdownMenuPrimitive.Label,
514
+ {
515
+ ref,
516
+ className: cn(
517
+ "px-2 py-1.5 text-sm font-semibold",
518
+ inset && "pl-8",
519
+ className
520
+ ),
521
+ ...props
522
+ }
523
+ ));
524
+ DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
525
+ var DropdownMenuSeparator = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
526
+ DropdownMenuPrimitive.Separator,
527
+ {
528
+ ref,
529
+ className: cn("-mx-1 my-1 h-px bg-muted", className),
530
+ ...props
531
+ }
532
+ ));
533
+ DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
534
+ var TooltipProvider = TooltipPrimitive.Provider;
535
+ var Tooltip = TooltipPrimitive.Root;
536
+ var TooltipTrigger = TooltipPrimitive.Trigger;
537
+ var TooltipContent = React7.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(
538
+ TooltipPrimitive.Content,
539
+ {
540
+ ref,
541
+ sideOffset,
542
+ className: cn(
543
+ "z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
544
+ className
545
+ ),
546
+ ...props
547
+ }
548
+ ));
549
+ TooltipContent.displayName = TooltipPrimitive.Content.displayName;
550
+ if (typeof window !== "undefined") {
551
+ pdfjsLib.GlobalWorkerOptions.workerSrc = "/pdfjs/build/pdf.worker.mjs";
552
+ }
553
+ async function pdfToImages(pdfBytes, options) {
554
+ try {
555
+ const header = new TextDecoder().decode(pdfBytes.slice(0, 10));
556
+ if (!header.startsWith("%PDF-")) {
557
+ console.error("Invalid PDF: Does not start with %PDF- header");
558
+ throw new Error("Invalid PDF format: Missing PDF header");
559
+ }
560
+ if (typeof window !== "undefined") {
561
+ pdfjsLib.GlobalWorkerOptions.workerSrc = "/pdfjs/build/pdf.worker.mjs";
562
+ }
563
+ const loadingTask = pdfjsLib.getDocument({
564
+ data: pdfBytes,
565
+ verbosity: 0
566
+ });
567
+ const pdfDoc = await loadingTask.promise;
568
+ const pages = [];
569
+ let pdfLibDoc = null;
570
+ try {
571
+ pdfLibDoc = await PDFDocument.load(pdfBytes);
572
+ } catch {
573
+ }
574
+ for (let pageNum = 1; pageNum <= pdfDoc.numPages; pageNum++) {
575
+ const page = await pdfDoc.getPage(pageNum);
576
+ const viewport = page.getViewport({ scale: 2 });
577
+ let pdfWidth = viewport.width / 2;
578
+ let pdfHeight = viewport.height / 2;
579
+ if (pdfLibDoc) {
580
+ try {
581
+ const pdfLibPage = pdfLibDoc.getPage(pageNum - 1);
582
+ const pdfLibWidth = pdfLibPage.getWidth();
583
+ const pdfLibHeight = pdfLibPage.getHeight();
584
+ pdfWidth = pdfLibWidth;
585
+ pdfHeight = pdfLibHeight;
586
+ } catch {
587
+ }
588
+ }
589
+ const canvas = document.createElement("canvas");
590
+ const context = canvas.getContext("2d");
591
+ canvas.height = viewport.height;
592
+ canvas.width = viewport.width;
593
+ const hideFormFields = options?.hideFormFields ?? false;
594
+ const renderContext = {
595
+ canvasContext: context,
596
+ viewport,
597
+ // Hide annotations/form fields when specified
598
+ renderInteractiveForms: !hideFormFields,
599
+ // Additional options to ensure form fields are hidden
600
+ ...hideFormFields && {
601
+ annotationMode: 0,
602
+ // Disable annotations
603
+ renderAnnotations: false,
604
+ renderForms: false
605
+ }
606
+ };
607
+ await page.render(renderContext).promise;
608
+ const imageUrl = canvas.toDataURL("image/png");
609
+ pages.push({
610
+ imageUrl,
611
+ width: pdfWidth,
612
+ height: pdfHeight
613
+ });
614
+ }
615
+ return pages;
616
+ } catch (error) {
617
+ console.error("Error converting PDF to images with PDF.js:", error);
618
+ throw error;
619
+ }
620
+ }
621
+ async function imageToPdf(imageFile) {
622
+ return new Promise((resolve, reject) => {
623
+ const img = new Image();
624
+ const canvas = document.createElement("canvas");
625
+ const ctx = canvas.getContext("2d");
626
+ img.onload = async () => {
627
+ try {
628
+ canvas.width = img.width;
629
+ canvas.height = img.height;
630
+ ctx.drawImage(img, 0, 0);
631
+ canvas.toBlob(
632
+ async (blob) => {
633
+ if (!blob) {
634
+ reject(new Error("Failed to convert image to blob"));
635
+ return;
636
+ }
637
+ const imageBytes = new Uint8Array(await blob.arrayBuffer());
638
+ const pdfDoc = await PDFDocument.create();
639
+ const embeddedImage = await pdfDoc.embedPng(imageBytes);
640
+ const { width, height } = embeddedImage.scale(1);
641
+ const maxWidth = 595;
642
+ const maxHeight = 842;
643
+ let pageWidth = width;
644
+ let pageHeight = height;
645
+ if (width > maxWidth || height > maxHeight) {
646
+ const scale = Math.min(maxWidth / width, maxHeight / height);
647
+ pageWidth = width * scale;
648
+ pageHeight = height * scale;
649
+ }
650
+ const page = pdfDoc.addPage([
651
+ Math.max(pageWidth, 100),
652
+ Math.max(pageHeight, 100)
653
+ ]);
654
+ const x = (page.getWidth() - pageWidth) / 2;
655
+ const y = (page.getHeight() - pageHeight) / 2;
656
+ page.drawImage(embeddedImage, {
657
+ x: Math.max(x, 0),
658
+ y: Math.max(y, 0),
659
+ width: pageWidth,
660
+ height: pageHeight
661
+ });
662
+ const pdfBytes = await pdfDoc.save();
663
+ resolve(pdfBytes);
664
+ },
665
+ "image/png",
666
+ 0.95
667
+ );
668
+ } catch (error) {
669
+ reject(error);
670
+ }
671
+ };
672
+ img.onerror = () => {
673
+ reject(new Error("Failed to load image"));
674
+ };
675
+ const objectUrl = URL.createObjectURL(imageFile);
676
+ img.src = objectUrl;
677
+ });
678
+ }
679
+ function createPdfBlobUrl(pdfBytes) {
680
+ const blob = new Blob([pdfBytes], {
681
+ type: "application/pdf"
682
+ });
683
+ return URL.createObjectURL(blob);
684
+ }
685
+ function downloadPdf(pdfBytes, filename) {
686
+ const blob = new Blob([pdfBytes], {
687
+ type: "application/pdf"
688
+ });
689
+ const url = URL.createObjectURL(blob);
690
+ const a = document.createElement("a");
691
+ a.href = url;
692
+ a.download = filename;
693
+ a.click();
694
+ URL.revokeObjectURL(url);
695
+ }
696
+ var isMultiSelectModifier = (event) => {
697
+ return event.ctrlKey || event.metaKey;
698
+ };
699
+ var isRangeSelectModifier = (event) => {
700
+ return event.shiftKey;
701
+ };
702
+ var handleContextMenu = (event) => {
703
+ if (event.ctrlKey && /Mac|iPod|iPhone|iPad/.test(navigator.userAgent)) {
704
+ event.preventDefault();
705
+ }
706
+ };
707
+ var getPageRange = (files, startPageId, endPageId) => {
708
+ const allPages = [];
709
+ files.forEach((file) => {
710
+ file.pages.forEach((page) => {
711
+ allPages.push({ id: page.id, fileId: file.id });
712
+ });
713
+ });
714
+ const startIndex = allPages.findIndex((p) => p.id === startPageId);
715
+ const endIndex = allPages.findIndex((p) => p.id === endPageId);
716
+ if (startIndex === -1 || endIndex === -1) return [];
717
+ const start = Math.min(startIndex, endIndex);
718
+ const end = Math.max(startIndex, endIndex);
719
+ return allPages.slice(start, end + 1).map((p) => p.id);
720
+ };
721
+ function assemblerReducer(state, action) {
722
+ switch (action.type) {
723
+ case "ADD_FILE": {
724
+ const existingFileById = state.files.find((f) => f.id === action.payload.id);
725
+ if (existingFileById) return state;
726
+ return {
727
+ ...state,
728
+ files: [...state.files, action.payload]
729
+ };
730
+ }
731
+ case "REMOVE_FILE":
732
+ return {
733
+ ...state,
734
+ files: state.files.filter((f) => f.id !== action.payload),
735
+ selection: state.selection.filter(
736
+ (pageId) => !state.files.find((f) => f.id === action.payload)?.pages.some((p) => p.id === pageId)
737
+ )
738
+ };
739
+ case "UPDATE_FILE":
740
+ return {
741
+ ...state,
742
+ files: state.files.map(
743
+ (f) => f.id === action.payload.fileId ? { ...f, ...action.payload.updates } : f
744
+ )
745
+ };
746
+ case "REORDER_PAGES":
747
+ return {
748
+ ...state,
749
+ files: state.files.map(
750
+ (f) => f.id === action.payload.fileId ? { ...f, pages: action.payload.newOrder } : f
751
+ )
752
+ };
753
+ case "REORDER_FILES":
754
+ return {
755
+ ...state,
756
+ files: action.payload.newOrder
757
+ };
758
+ case "MOVE_PAGES": {
759
+ const { pageIds, targetFileId } = action.payload;
760
+ const existingPageIds = state.files.flatMap((f) => f.pages.map((p) => p.id));
761
+ const pagesToActuallyMove = pageIds.filter((id) => existingPageIds.includes(id));
762
+ if (pagesToActuallyMove.length === 0) return state;
763
+ const pagesToMove = [];
764
+ for (const file of state.files) {
765
+ for (const page of file.pages) {
766
+ if (pagesToActuallyMove.includes(page.id)) {
767
+ pagesToMove.push(page);
768
+ }
769
+ }
770
+ }
771
+ const updatedFiles = state.files.map((file) => {
772
+ if (file.id === targetFileId) {
773
+ const existingPageIds2 = file.pages.map((p) => p.id);
774
+ const newPages = pagesToMove.filter((p) => !existingPageIds2.includes(p.id));
775
+ return {
776
+ ...file,
777
+ pages: [...file.pages, ...newPages]
778
+ };
779
+ } else {
780
+ return {
781
+ ...file,
782
+ pages: file.pages.filter((page) => !pagesToActuallyMove.includes(page.id))
783
+ };
784
+ }
785
+ });
786
+ return {
787
+ ...state,
788
+ files: updatedFiles,
789
+ selection: []
790
+ };
791
+ }
792
+ case "DELETE_PAGES": {
793
+ const pageIdsToDelete = action.payload;
794
+ const existingPageIds = state.files.flatMap((f) => f.pages.map((p) => p.id));
795
+ const pagesToActuallyDelete = pageIdsToDelete.filter((id) => existingPageIds.includes(id));
796
+ if (pagesToActuallyDelete.length === 0) return state;
797
+ const updatedFiles = state.files.map((file) => {
798
+ const remainingPages = file.pages.filter((page) => !pagesToActuallyDelete.includes(page.id));
799
+ return {
800
+ ...file,
801
+ pages: remainingPages
802
+ };
803
+ }).filter((file) => file.pages.length > 0);
804
+ return {
805
+ ...state,
806
+ files: updatedFiles,
807
+ selection: state.selection.filter((id) => !pagesToActuallyDelete.includes(id))
808
+ };
809
+ }
810
+ case "SET_SELECTION":
811
+ return {
812
+ ...state,
813
+ selection: action.payload
814
+ };
815
+ case "TOGGLE_PAGE_SELECTION": {
816
+ const { pageId } = action.payload;
817
+ const isSelected = state.selection.includes(pageId);
818
+ if (isSelected) {
819
+ return {
820
+ ...state,
821
+ selection: state.selection.filter((id) => id !== pageId),
822
+ lastSelectedPageId: pageId
823
+ };
824
+ } else {
825
+ return {
826
+ ...state,
827
+ selection: [...state.selection, pageId],
828
+ lastSelectedPageId: pageId
829
+ };
830
+ }
831
+ }
832
+ case "RANGE_SELECT_PAGES": {
833
+ const { pageId, files } = action.payload;
834
+ if (!state.lastSelectedPageId) {
835
+ return {
836
+ ...state,
837
+ selection: [pageId],
838
+ lastSelectedPageId: pageId
839
+ };
840
+ }
841
+ const rangePageIds = getPageRange(files, state.lastSelectedPageId, pageId);
842
+ const newSelection = [.../* @__PURE__ */ new Set([...state.selection, ...rangePageIds])];
843
+ return {
844
+ ...state,
845
+ selection: newSelection,
846
+ lastSelectedPageId: pageId
847
+ };
848
+ }
849
+ case "SINGLE_SELECT_PAGE": {
850
+ return {
851
+ ...state,
852
+ selection: [action.payload],
853
+ lastSelectedPageId: action.payload
854
+ };
855
+ }
856
+ case "CLEAR_SELECTION":
857
+ return {
858
+ ...state,
859
+ selection: [],
860
+ lastSelectedPageId: void 0
861
+ };
862
+ case "RENAME_FILE":
863
+ return {
864
+ ...state,
865
+ files: state.files.map(
866
+ (f) => f.id === action.payload.fileId ? { ...f, name: action.payload.name } : f
867
+ )
868
+ };
869
+ case "RESET":
870
+ return action.payload || { files: [], selection: [], lastSelectedPageId: void 0 };
871
+ default:
872
+ return state;
873
+ }
874
+ }
875
+ var DND_TYPES = {
876
+ PAGE: "page",
877
+ FILE: "file"
878
+ };
879
+ var DraggablePageThumbnail = ({
880
+ page,
881
+ fileId,
882
+ index,
883
+ isSelected,
884
+ onSingleSelect,
885
+ onToggleSelect,
886
+ onRangeSelect,
887
+ movePageInFile,
888
+ isDraggingPageRef
889
+ }) => {
890
+ const ref = useRef(null);
891
+ const [{ isDragging }, drag] = useDrag({
892
+ type: DND_TYPES.PAGE,
893
+ item: () => {
894
+ if (isDraggingPageRef?.current !== void 0) {
895
+ isDraggingPageRef.current = true;
896
+ }
897
+ return { pageId: page.id, fileId, index };
898
+ },
899
+ collect: (monitor) => ({
900
+ isDragging: monitor.isDragging()
901
+ }),
902
+ end: () => {
903
+ if (isDraggingPageRef?.current !== void 0) {
904
+ isDraggingPageRef.current = false;
905
+ }
906
+ }
907
+ });
908
+ const [{ handlerId }, drop] = useDrop({
909
+ accept: DND_TYPES.PAGE,
910
+ collect(monitor) {
911
+ return {
912
+ handlerId: monitor.getHandlerId()
913
+ };
914
+ },
915
+ hover(item, monitor) {
916
+ if (!ref.current) return;
917
+ if (item.fileId !== fileId) return;
918
+ const dragIndex = item.index;
919
+ const hoverIndex = index;
920
+ if (dragIndex === hoverIndex) return;
921
+ const hoverBoundingRect = ref.current?.getBoundingClientRect();
922
+ const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2;
923
+ const clientOffset = monitor.getClientOffset();
924
+ const hoverClientY = clientOffset.y - hoverBoundingRect.top;
925
+ if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) return;
926
+ if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) return;
927
+ movePageInFile(dragIndex, hoverIndex);
928
+ item.index = hoverIndex;
929
+ }
930
+ });
931
+ useEffect(() => {
932
+ drag(drop(ref));
933
+ }, [drag, drop]);
934
+ const handleClick = (e) => {
935
+ e.preventDefault();
936
+ handleContextMenu(e);
937
+ if (isRangeSelectModifier(e)) {
938
+ onRangeSelect(page.id);
939
+ } else if (isMultiSelectModifier(e)) {
940
+ onToggleSelect(page.id);
941
+ } else {
942
+ onSingleSelect(page.id);
943
+ }
944
+ };
945
+ return /* @__PURE__ */ jsxs(
946
+ "div",
947
+ {
948
+ ref,
949
+ "data-handler-id": handlerId,
950
+ className: cn(
951
+ "relative group cursor-pointer border-2 rounded-lg overflow-hidden bg-white transition-all",
952
+ isDragging && "opacity-50 scale-95",
953
+ isSelected ? "border-primary shadow-md" : "border-gray-200 hover:border-gray-300"
954
+ ),
955
+ onClick: handleClick,
956
+ children: [
957
+ /* @__PURE__ */ jsx("div", { className: "absolute top-1 right-1 opacity-0 group-hover:opacity-100 transition-opacity bg-black/50 rounded p-1 z-10", children: /* @__PURE__ */ jsx(GripVertical, { className: "h-3 w-3 text-white" }) }),
958
+ /* @__PURE__ */ jsx(
959
+ "div",
960
+ {
961
+ className: "absolute top-2 left-2 z-10",
962
+ onClick: (e) => e.stopPropagation(),
963
+ children: /* @__PURE__ */ jsx(
964
+ "input",
965
+ {
966
+ type: "checkbox",
967
+ checked: isSelected,
968
+ onChange: (e) => {
969
+ const nativeEvent = e.nativeEvent;
970
+ if (nativeEvent && "shiftKey" in nativeEvent && nativeEvent.shiftKey) {
971
+ onRangeSelect(page.id);
972
+ } else if (nativeEvent && ("ctrlKey" in nativeEvent && nativeEvent.ctrlKey || "metaKey" in nativeEvent && nativeEvent.metaKey)) {
973
+ onToggleSelect(page.id);
974
+ } else {
975
+ onSingleSelect(page.id);
976
+ }
977
+ },
978
+ onContextMenu: handleContextMenu,
979
+ className: "h-4 w-4 rounded border-gray-300 text-primary focus:ring-primary focus:ring-2 bg-white/90 cursor-pointer"
980
+ }
981
+ )
982
+ }
983
+ ),
984
+ /* @__PURE__ */ jsxs("div", { className: "aspect-[3/4] relative", children: [
985
+ /* @__PURE__ */ jsx(
986
+ "img",
987
+ {
988
+ src: page.thumbUrl,
989
+ alt: `Page ${index + 1}`,
990
+ className: "w-full h-full object-cover"
991
+ }
992
+ ),
993
+ /* @__PURE__ */ jsx(
994
+ Badge,
995
+ {
996
+ variant: "secondary",
997
+ className: "absolute bottom-1 right-1 text-xs px-1 py-0",
998
+ children: index + 1
999
+ }
1000
+ )
1001
+ ] })
1002
+ ]
1003
+ }
1004
+ );
1005
+ };
1006
+ var ReorderablePageGrid = ({
1007
+ file,
1008
+ selectedPageIds,
1009
+ onSingleSelectPage,
1010
+ onTogglePageSelect,
1011
+ onRangeSelectPage,
1012
+ onReorderPages,
1013
+ isDraggingPageRef
1014
+ }) => {
1015
+ const movePageInFile = useCallback((dragIndex, hoverIndex) => {
1016
+ const newPages = [...file.pages];
1017
+ const draggedPage = newPages[dragIndex];
1018
+ newPages.splice(dragIndex, 1);
1019
+ newPages.splice(hoverIndex, 0, draggedPage);
1020
+ onReorderPages(file.id, newPages);
1021
+ }, [file.id, file.pages, onReorderPages]);
1022
+ return /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4", children: file.pages.map((page, index) => /* @__PURE__ */ jsx(
1023
+ DraggablePageThumbnail,
1024
+ {
1025
+ page,
1026
+ fileId: file.id,
1027
+ index,
1028
+ isSelected: selectedPageIds.includes(page.id),
1029
+ onSingleSelect: onSingleSelectPage,
1030
+ onToggleSelect: onTogglePageSelect,
1031
+ onRangeSelect: onRangeSelectPage,
1032
+ movePageInFile,
1033
+ isDraggingPageRef
1034
+ },
1035
+ page.id
1036
+ )) });
1037
+ };
1038
+ var FileDropZone = ({
1039
+ targetIndex,
1040
+ onReorderFiles,
1041
+ files
1042
+ }) => {
1043
+ const [{ isOver, canDrop }, drop] = useDrop({
1044
+ accept: DND_TYPES.FILE,
1045
+ drop: (item) => {
1046
+ const currentIndex = files.findIndex((f) => f.id === item.fileId);
1047
+ if (currentIndex === -1) return;
1048
+ let newIndex = targetIndex;
1049
+ if (currentIndex < targetIndex) {
1050
+ newIndex = targetIndex - 1;
1051
+ }
1052
+ if (currentIndex === newIndex) return;
1053
+ const newFiles = [...files];
1054
+ const [movedFile] = newFiles.splice(currentIndex, 1);
1055
+ newFiles.splice(newIndex, 0, movedFile);
1056
+ onReorderFiles(newFiles);
1057
+ },
1058
+ collect: (monitor) => ({
1059
+ isOver: monitor.isOver(),
1060
+ canDrop: monitor.canDrop()
1061
+ })
1062
+ });
1063
+ const isActive = isOver && canDrop;
1064
+ return /* @__PURE__ */ jsx(
1065
+ "div",
1066
+ {
1067
+ ref: drop,
1068
+ className: cn(
1069
+ "h-2 transition-all duration-200 mx-4",
1070
+ isActive ? "h-8 bg-primary/20 border-2 border-dashed border-primary rounded-lg" : ""
1071
+ ),
1072
+ children: isActive && /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center h-full text-primary text-sm font-medium", children: "Drop here to reorder" })
1073
+ }
1074
+ );
1075
+ };
1076
+ var FileCard = ({
1077
+ file,
1078
+ selectedPageIds,
1079
+ onSingleSelectPage,
1080
+ onTogglePageSelect,
1081
+ onRangeSelectPage,
1082
+ onSelectAllInFile,
1083
+ onRemoveFile,
1084
+ onRenameFile,
1085
+ onReorderPages,
1086
+ isDraggingPageRef
1087
+ }) => {
1088
+ const [isRenaming, setIsRenaming] = useState(false);
1089
+ const [newName, setNewName] = useState(file.name);
1090
+ const fileSelectedPages = file.pages.filter((p) => selectedPageIds.includes(p.id));
1091
+ const allPagesSelected = file.pages.length > 0 && fileSelectedPages.length === file.pages.length;
1092
+ const [{ isDraggingFile }, dragFile] = useDrag({
1093
+ type: DND_TYPES.FILE,
1094
+ item: () => {
1095
+ if (isDraggingPageRef?.current !== void 0) {
1096
+ isDraggingPageRef.current = true;
1097
+ }
1098
+ return { fileId: file.id, file };
1099
+ },
1100
+ collect: (monitor) => ({
1101
+ isDraggingFile: monitor.isDragging()
1102
+ }),
1103
+ end: () => {
1104
+ if (isDraggingPageRef?.current !== void 0) {
1105
+ isDraggingPageRef.current = false;
1106
+ }
1107
+ }
1108
+ });
1109
+ const [, drop] = useDrop({
1110
+ accept: DND_TYPES.PAGE,
1111
+ drop: (_item) => {
1112
+ }
1113
+ });
1114
+ const handleRename = () => {
1115
+ if (newName.trim() && newName !== file.name) {
1116
+ onRenameFile(file.id, newName.trim());
1117
+ }
1118
+ setIsRenaming(false);
1119
+ };
1120
+ const combineRefs = useCallback((el) => {
1121
+ drop(el);
1122
+ dragFile(el);
1123
+ }, [drop, dragFile]);
1124
+ return /* @__PURE__ */ jsxs(
1125
+ Card,
1126
+ {
1127
+ className: cn(
1128
+ "mb-6 transition-all cursor-move",
1129
+ isDraggingFile ? "opacity-50 rotate-2 scale-105 shadow-2xl z-50" : "opacity-100"
1130
+ ),
1131
+ ref: combineRefs,
1132
+ children: [
1133
+ /* @__PURE__ */ jsx(CardHeader, { className: "pb-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1134
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1135
+ file.type === "pdf" ? /* @__PURE__ */ jsx(FileText, { className: "h-5 w-5 text-red-500" }) : /* @__PURE__ */ jsx(Image$1, { className: "h-5 w-5 text-blue-500" }),
1136
+ isRenaming ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1137
+ /* @__PURE__ */ jsx(
1138
+ Input,
1139
+ {
1140
+ value: newName,
1141
+ onChange: (e) => setNewName(e.target.value),
1142
+ onKeyDown: (e) => {
1143
+ if (e.key === "Enter") handleRename();
1144
+ if (e.key === "Escape") {
1145
+ setIsRenaming(false);
1146
+ setNewName(file.name);
1147
+ }
1148
+ },
1149
+ className: "h-8 text-sm",
1150
+ autoFocus: true
1151
+ }
1152
+ ),
1153
+ /* @__PURE__ */ jsx(Button, { type: "button", size: "sm", variant: "ghost", onClick: handleRename, children: /* @__PURE__ */ jsx(Check, { className: "h-3 w-3" }) }),
1154
+ /* @__PURE__ */ jsx(
1155
+ Button,
1156
+ {
1157
+ type: "button",
1158
+ size: "sm",
1159
+ variant: "ghost",
1160
+ onClick: () => {
1161
+ setIsRenaming(false);
1162
+ setNewName(file.name);
1163
+ },
1164
+ children: /* @__PURE__ */ jsx(X, { className: "h-3 w-3" })
1165
+ }
1166
+ )
1167
+ ] }) : /* @__PURE__ */ jsxs("div", { children: [
1168
+ /* @__PURE__ */ jsx(CardTitle, { className: "text-base", children: file.name }),
1169
+ /* @__PURE__ */ jsxs("p", { className: "text-sm text-muted-foreground", children: [
1170
+ file.pages.length,
1171
+ " page",
1172
+ file.pages.length !== 1 ? "s" : ""
1173
+ ] })
1174
+ ] })
1175
+ ] }),
1176
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1177
+ /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
1178
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(
1179
+ Button,
1180
+ {
1181
+ type: "button",
1182
+ variant: "outline",
1183
+ size: "sm",
1184
+ onClick: () => onSelectAllInFile(file.id),
1185
+ disabled: file.pages.length === 0,
1186
+ children: [
1187
+ allPagesSelected && /* @__PURE__ */ jsx(Check, { className: "h-3 w-3 mr-2" }),
1188
+ "Select All"
1189
+ ]
1190
+ }
1191
+ ) }),
1192
+ /* @__PURE__ */ jsx(TooltipContent, { children: "Select all pages in this file" })
1193
+ ] }) }),
1194
+ /* @__PURE__ */ jsxs(DropdownMenu, { children: [
1195
+ /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "hover:bg-gray-100 rounded-lg", children: /* @__PURE__ */ jsx(MoreHorizontal, { className: "h-4 w-4" }) }) }),
1196
+ /* @__PURE__ */ jsxs(DropdownMenuContent, { align: "end", className: "w-52", children: [
1197
+ /* @__PURE__ */ jsxs(DropdownMenuItem, { onClick: () => setIsRenaming(true), children: [
1198
+ /* @__PURE__ */ jsx("svg", { className: "h-4 w-4 mr-2", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", strokeWidth: 2, d: "M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" }) }),
1199
+ "Rename File"
1200
+ ] }),
1201
+ /* @__PURE__ */ jsxs(
1202
+ DropdownMenuItem,
1203
+ {
1204
+ onClick: () => onRemoveFile(file.id),
1205
+ className: "text-red-600 hover:text-red-700 hover:bg-red-50 focus:text-red-700 focus:bg-red-50",
1206
+ children: [
1207
+ /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4 mr-2" }),
1208
+ "Remove File"
1209
+ ]
1210
+ }
1211
+ )
1212
+ ] })
1213
+ ] })
1214
+ ] })
1215
+ ] }) }),
1216
+ /* @__PURE__ */ jsx(CardContent, { children: /* @__PURE__ */ jsx(
1217
+ ReorderablePageGrid,
1218
+ {
1219
+ file,
1220
+ selectedPageIds,
1221
+ onSingleSelectPage,
1222
+ onTogglePageSelect,
1223
+ onRangeSelectPage,
1224
+ onReorderPages,
1225
+ isDraggingPageRef
1226
+ }
1227
+ ) })
1228
+ ]
1229
+ }
1230
+ );
1231
+ };
1232
+ var BulkActionBar = ({
1233
+ selectedCount,
1234
+ files,
1235
+ onMovePages,
1236
+ onDeletePages,
1237
+ onClearSelection
1238
+ }) => {
1239
+ if (selectedCount === 0) return null;
1240
+ return /* @__PURE__ */ jsx("div", { className: "sticky top-4 z-10 mb-4", children: /* @__PURE__ */ jsx(Card, { className: "p-4 shadow-lg border-primary/20 bg-blue-50", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between flex-wrap gap-4", children: [
1241
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4 flex-wrap", children: [
1242
+ /* @__PURE__ */ jsxs(
1243
+ "span",
1244
+ {
1245
+ className: "inline-flex items-center text-sm font-medium text-white rounded-full",
1246
+ style: {
1247
+ backgroundColor: "#0099cd",
1248
+ padding: "6px 14px"
1249
+ },
1250
+ children: [
1251
+ selectedCount,
1252
+ " page",
1253
+ selectedCount !== 1 ? "s" : "",
1254
+ " selected"
1255
+ ]
1256
+ }
1257
+ ),
1258
+ files.length > 1 && /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1259
+ /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsxs(Tooltip, { children: [
1260
+ /* @__PURE__ */ jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsx(Label, { htmlFor: "move-select", className: "text-sm font-medium text-gray-700", children: "Move to file:" }) }),
1261
+ /* @__PURE__ */ jsx(TooltipContent, { children: /* @__PURE__ */ jsx("p", { children: "Move selected pages to another file" }) })
1262
+ ] }) }),
1263
+ /* @__PURE__ */ jsxs(Select, { onValueChange: onMovePages, children: [
1264
+ /* @__PURE__ */ jsx(SelectTrigger, { id: "move-select", className: "w-48 bg-white", children: /* @__PURE__ */ jsx(SelectValue, { placeholder: "Choose file..." }) }),
1265
+ /* @__PURE__ */ jsx(SelectContent, { children: files.map((file) => /* @__PURE__ */ jsx(SelectItem, { value: file.id, children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1266
+ file.type === "pdf" ? /* @__PURE__ */ jsx(FileText, { className: "h-4 w-4" }) : /* @__PURE__ */ jsx(Image$1, { className: "h-4 w-4" }),
1267
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: file.name })
1268
+ ] }) }, file.id)) })
1269
+ ] })
1270
+ ] })
1271
+ ] }),
1272
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1273
+ /* @__PURE__ */ jsxs(
1274
+ Button,
1275
+ {
1276
+ type: "button",
1277
+ variant: "destructive",
1278
+ size: "sm",
1279
+ onClick: onDeletePages,
1280
+ children: [
1281
+ /* @__PURE__ */ jsx(Trash2, { className: "h-4 w-4 mr-2" }),
1282
+ "Delete Selected"
1283
+ ]
1284
+ }
1285
+ ),
1286
+ /* @__PURE__ */ jsx(
1287
+ Button,
1288
+ {
1289
+ type: "button",
1290
+ variant: "outline",
1291
+ size: "sm",
1292
+ onClick: onClearSelection,
1293
+ className: "bg-white",
1294
+ children: "Clear Selection"
1295
+ }
1296
+ )
1297
+ ] })
1298
+ ] }) }) });
1299
+ };
1300
+ var PageAssembler = forwardRef(
1301
+ (props, ref) => {
1302
+ const {
1303
+ initialPdf,
1304
+ initialState,
1305
+ onChange,
1306
+ onValidityChange,
1307
+ limits,
1308
+ disableFileUpload = false,
1309
+ className
1310
+ } = props;
1311
+ const [state, dispatch] = useReducer(
1312
+ assemblerReducer,
1313
+ initialState || {
1314
+ files: [],
1315
+ selection: [],
1316
+ lastSelectedPageId: void 0
1317
+ }
1318
+ );
1319
+ const [isLoading, setIsLoading] = useState(false);
1320
+ const [processingStatus, setProcessingStatus] = useState("");
1321
+ const [showDeleteConfirm, setShowDeleteConfirm] = useState(false);
1322
+ const [showRemoveFileConfirm, setShowRemoveFileConfirm] = useState(false);
1323
+ const [fileToRemove, setFileToRemove] = useState(null);
1324
+ const fileInputRef = useRef(null);
1325
+ const processedInitialPdfRef = useRef(null);
1326
+ const isDraggingPage = useRef(false);
1327
+ const onChangeRef = useRef(onChange);
1328
+ const summary = useMemo(() => ({
1329
+ totalFiles: state.files.length,
1330
+ totalPages: state.files.reduce((sum, file) => sum + file.pages.length, 0)
1331
+ }), [state.files]);
1332
+ const isValid = summary.totalPages > 0;
1333
+ useEffect(() => {
1334
+ onChangeRef.current = onChange;
1335
+ }, [onChange]);
1336
+ const debouncedOnChange = useMemo(
1337
+ () => debounce((newState) => {
1338
+ onChangeRef.current?.(newState);
1339
+ }, 200),
1340
+ []
1341
+ );
1342
+ useEffect(() => {
1343
+ debouncedOnChange(state);
1344
+ }, [state, debouncedOnChange]);
1345
+ useEffect(() => {
1346
+ onValidityChange?.(isValid);
1347
+ }, [isValid, onValidityChange]);
1348
+ const processInitialPdf = useCallback(async (pdf) => {
1349
+ setIsLoading(true);
1350
+ setProcessingStatus("Processing initial PDF...");
1351
+ try {
1352
+ let pdfBytes;
1353
+ if (pdf instanceof Blob) {
1354
+ const arrayBuffer = await pdf.arrayBuffer();
1355
+ pdfBytes = new Uint8Array(arrayBuffer);
1356
+ } else if (pdf instanceof ArrayBuffer) {
1357
+ pdfBytes = new Uint8Array(pdf);
1358
+ } else {
1359
+ pdfBytes = new Uint8Array(pdf);
1360
+ }
1361
+ const defensiveCopy = new Uint8Array(pdfBytes);
1362
+ const pages = await pdfToImages(defensiveCopy, { hideFormFields: true });
1363
+ if (pages.length === 0) {
1364
+ throw new Error("Failed to process PDF pages");
1365
+ }
1366
+ let originalBytes;
1367
+ let originalFile;
1368
+ try {
1369
+ new Uint8Array(pdfBytes.buffer || pdfBytes, 0, 1);
1370
+ originalBytes = new Uint8Array(pdfBytes);
1371
+ originalFile = new File([pdfBytes.buffer], "Initial Document.pdf", { type: "application/pdf" });
1372
+ } catch (error) {
1373
+ const errorMessage = error instanceof Error ? error.message : String(error);
1374
+ if (errorMessage.includes("detached") || errorMessage.includes("ArrayBuffer")) {
1375
+ throw new Error("Initial PDF data is no longer available. Please try refreshing the page.");
1376
+ }
1377
+ throw error;
1378
+ }
1379
+ const fileName = pdf instanceof File ? pdf.name : "Initial Document.pdf";
1380
+ const fileId = generateId();
1381
+ const file = {
1382
+ id: fileId,
1383
+ name: fileName,
1384
+ type: "pdf",
1385
+ originalFile,
1386
+ originalBytes,
1387
+ pages: pages.map((page, index) => ({
1388
+ id: generateId(),
1389
+ srcIndex: index,
1390
+ thumbUrl: page.imageUrl,
1391
+ width: page.width,
1392
+ height: page.height,
1393
+ mime: "application/pdf",
1394
+ originalFileId: fileId
1395
+ }))
1396
+ };
1397
+ dispatch({ type: "ADD_FILE", payload: file });
1398
+ } catch (error) {
1399
+ console.error("Error processing initial PDF:", error);
1400
+ } finally {
1401
+ setIsLoading(false);
1402
+ setProcessingStatus("");
1403
+ }
1404
+ }, []);
1405
+ useEffect(() => {
1406
+ if (initialPdf && !initialState) {
1407
+ const pdfIdentifier = initialPdf instanceof Uint8Array ? `uint8array-${initialPdf.length}-${initialPdf[0]}-${initialPdf[initialPdf.length - 1]}` : `${initialPdf.constructor.name}-${initialPdf.size || initialPdf.byteLength}`;
1408
+ if (processedInitialPdfRef.current === pdfIdentifier) {
1409
+ return;
1410
+ }
1411
+ processedInitialPdfRef.current = pdfIdentifier;
1412
+ processInitialPdf(initialPdf);
1413
+ }
1414
+ }, [initialPdf, initialState, processInitialPdf]);
1415
+ useEffect(() => {
1416
+ if (initialState) {
1417
+ dispatch({ type: "RESET", payload: initialState });
1418
+ }
1419
+ }, [initialState]);
1420
+ const processFiles = useCallback(async (files) => {
1421
+ const fileArray = Array.from(files);
1422
+ const maxFileSize = (limits?.maxFileSizeMB || 10) * 1024 * 1024;
1423
+ setIsLoading(true);
1424
+ setProcessingStatus(`Processing ${fileArray.length} file${fileArray.length !== 1 ? "s" : ""}...`);
1425
+ for (const file of fileArray) {
1426
+ try {
1427
+ if (file.size > maxFileSize) {
1428
+ console.error(`File ${file.name} is too large`);
1429
+ continue;
1430
+ }
1431
+ const validTypes = ["application/pdf", "image/png", "image/jpeg", "image/jpg", "image/webp"];
1432
+ if (!validTypes.includes(file.type)) {
1433
+ console.error(`Unsupported file type: ${file.name}`);
1434
+ continue;
1435
+ }
1436
+ const fileInfo = {
1437
+ id: generateId(),
1438
+ name: file.name,
1439
+ type: file.type === "application/pdf" ? "pdf" : "image",
1440
+ originalFile: file,
1441
+ originalBytes: new Uint8Array(),
1442
+ pages: []
1443
+ };
1444
+ if (file.type === "application/pdf") {
1445
+ setProcessingStatus(`Processing PDF: ${file.name}`);
1446
+ const arrayBuffer = await file.arrayBuffer();
1447
+ const pdfBytes = new Uint8Array(arrayBuffer);
1448
+ fileInfo.originalBytes = new Uint8Array(pdfBytes);
1449
+ const defensiveCopy = new Uint8Array(pdfBytes);
1450
+ const pages = await pdfToImages(defensiveCopy, { hideFormFields: true });
1451
+ fileInfo.pages = pages.map((page, index) => ({
1452
+ id: generateId(),
1453
+ srcIndex: index,
1454
+ thumbUrl: page.imageUrl,
1455
+ width: page.width,
1456
+ height: page.height,
1457
+ mime: file.type,
1458
+ originalFileId: fileInfo.id
1459
+ }));
1460
+ } else {
1461
+ setProcessingStatus(`Processing image: ${file.name}`);
1462
+ const arrayBuffer = await file.arrayBuffer();
1463
+ const imageBytes = new Uint8Array(arrayBuffer);
1464
+ fileInfo.originalBytes = new Uint8Array(imageBytes);
1465
+ const thumbUrl = URL.createObjectURL(file);
1466
+ const img = new Image();
1467
+ const dimensions = await new Promise((resolve) => {
1468
+ img.onload = () => resolve({ width: img.width, height: img.height });
1469
+ img.src = thumbUrl;
1470
+ });
1471
+ const maxWidth = 595;
1472
+ const maxHeight = 842;
1473
+ let pdfWidth = dimensions.width;
1474
+ let pdfHeight = dimensions.height;
1475
+ if (dimensions.width > maxWidth || dimensions.height > maxHeight) {
1476
+ const scaleX = maxWidth / dimensions.width;
1477
+ const scaleY = maxHeight / dimensions.height;
1478
+ const scale = Math.min(scaleX, scaleY);
1479
+ pdfWidth = dimensions.width * scale;
1480
+ pdfHeight = dimensions.height * scale;
1481
+ }
1482
+ fileInfo.pages = [{
1483
+ id: generateId(),
1484
+ srcIndex: 0,
1485
+ thumbUrl,
1486
+ width: Math.max(pdfWidth, 100),
1487
+ height: Math.max(pdfHeight, 100),
1488
+ mime: file.type,
1489
+ originalFileId: fileInfo.id
1490
+ }];
1491
+ }
1492
+ dispatch({ type: "ADD_FILE", payload: fileInfo });
1493
+ } catch (error) {
1494
+ console.error(`Error processing file ${file.name}:`, error);
1495
+ }
1496
+ }
1497
+ setIsLoading(false);
1498
+ setProcessingStatus("");
1499
+ }, [limits]);
1500
+ const handleFileUpload = useCallback(() => {
1501
+ fileInputRef.current?.click();
1502
+ }, []);
1503
+ const handleFileChange = useCallback((e) => {
1504
+ const files = e.target.files;
1505
+ if (files) {
1506
+ processFiles(files);
1507
+ }
1508
+ if (fileInputRef.current) {
1509
+ fileInputRef.current.value = "";
1510
+ }
1511
+ }, [processFiles]);
1512
+ const handleDrop = useCallback((e) => {
1513
+ e.preventDefault();
1514
+ const files = e.dataTransfer.files;
1515
+ if (files && files.length > 0) {
1516
+ if (isDraggingPage.current) {
1517
+ return;
1518
+ }
1519
+ processFiles(files);
1520
+ }
1521
+ }, [processFiles]);
1522
+ const handleDragOver = useCallback((e) => {
1523
+ e.preventDefault();
1524
+ }, []);
1525
+ const handleSingleSelectPage = useCallback((pageId) => {
1526
+ dispatch({ type: "SINGLE_SELECT_PAGE", payload: pageId });
1527
+ }, []);
1528
+ const handleTogglePageSelect = useCallback((pageId) => {
1529
+ dispatch({ type: "TOGGLE_PAGE_SELECTION", payload: { pageId, files: state.files } });
1530
+ }, [state.files]);
1531
+ const handleRangeSelectPage = useCallback((pageId) => {
1532
+ dispatch({ type: "RANGE_SELECT_PAGES", payload: { pageId, files: state.files } });
1533
+ }, [state.files]);
1534
+ const handleSelectAllInFile = useCallback((fileId) => {
1535
+ const file = state.files.find((f) => f.id === fileId);
1536
+ if (!file) return;
1537
+ const filePageIds = file.pages.map((p) => p.id);
1538
+ const allSelected = filePageIds.every((id) => state.selection.includes(id));
1539
+ if (allSelected) {
1540
+ dispatch({
1541
+ type: "SET_SELECTION",
1542
+ payload: state.selection.filter((id) => !filePageIds.includes(id))
1543
+ });
1544
+ } else {
1545
+ const newSelection = [.../* @__PURE__ */ new Set([...state.selection, ...filePageIds])];
1546
+ dispatch({ type: "SET_SELECTION", payload: newSelection });
1547
+ }
1548
+ }, [state.files, state.selection]);
1549
+ const handleMovePages = useCallback((targetFileId) => {
1550
+ if (state.selection.length === 0) return;
1551
+ dispatch({ type: "MOVE_PAGES", payload: { pageIds: state.selection, targetFileId } });
1552
+ }, [state.selection]);
1553
+ const handleDeletePages = useCallback(() => {
1554
+ setShowDeleteConfirm(true);
1555
+ }, []);
1556
+ const confirmDeletePages = useCallback(() => {
1557
+ dispatch({ type: "DELETE_PAGES", payload: state.selection });
1558
+ setShowDeleteConfirm(false);
1559
+ }, [state.selection]);
1560
+ const handleRemoveFile = useCallback((fileId) => {
1561
+ const file = state.files.find((f) => f.id === fileId);
1562
+ if (!file) return;
1563
+ if (file.pages.length > 0) {
1564
+ setFileToRemove({
1565
+ id: file.id,
1566
+ name: file.name,
1567
+ pageCount: file.pages.length
1568
+ });
1569
+ setShowRemoveFileConfirm(true);
1570
+ } else {
1571
+ dispatch({ type: "REMOVE_FILE", payload: fileId });
1572
+ }
1573
+ }, [state.files]);
1574
+ const confirmRemoveFile = useCallback(() => {
1575
+ if (fileToRemove) {
1576
+ dispatch({ type: "REMOVE_FILE", payload: fileToRemove.id });
1577
+ setFileToRemove(null);
1578
+ }
1579
+ setShowRemoveFileConfirm(false);
1580
+ }, [fileToRemove]);
1581
+ const handleRenameFile = useCallback((fileId, name) => {
1582
+ dispatch({ type: "RENAME_FILE", payload: { fileId, name } });
1583
+ }, []);
1584
+ const handleReorderFiles = useCallback((newOrder) => {
1585
+ dispatch({ type: "REORDER_FILES", payload: { newOrder } });
1586
+ }, []);
1587
+ useImperativeHandle(ref, () => ({
1588
+ assemble: async () => {
1589
+ setIsLoading(true);
1590
+ setProcessingStatus("Assembling PDF...");
1591
+ try {
1592
+ const { PDFDocument: PDFDocument2 } = await import('pdf-lib');
1593
+ const combinedDoc = await PDFDocument2.create();
1594
+ const loadedPdfs = /* @__PURE__ */ new Map();
1595
+ const fileMap = /* @__PURE__ */ new Map();
1596
+ for (const file of state.files) {
1597
+ fileMap.set(file.id, file);
1598
+ if (file.type === "pdf" && file.originalBytes) {
1599
+ try {
1600
+ const sourcePdf = await PDFDocument2.load(file.originalBytes);
1601
+ loadedPdfs.set(file.id, sourcePdf);
1602
+ } catch (error) {
1603
+ console.error(`Error loading PDF for file ${file.id}:`, error);
1604
+ }
1605
+ }
1606
+ }
1607
+ for (const file of state.files) {
1608
+ if (file.pages.length === 0) continue;
1609
+ for (const page of file.pages) {
1610
+ const originalFileId = page.originalFileId || file.id;
1611
+ const sourceFile = fileMap.get(originalFileId);
1612
+ if (!sourceFile) {
1613
+ console.warn(`Source file not found for page ${page.id}`);
1614
+ continue;
1615
+ }
1616
+ if (sourceFile.type === "pdf") {
1617
+ const sourcePdf = loadedPdfs.get(originalFileId);
1618
+ if (!sourcePdf) {
1619
+ console.warn(`Source PDF not loaded for page ${page.id}`);
1620
+ continue;
1621
+ }
1622
+ const sourcePageCount = sourcePdf.getPageCount();
1623
+ if (page.srcIndex >= 0 && page.srcIndex < sourcePageCount) {
1624
+ try {
1625
+ const copiedPages = await combinedDoc.copyPages(sourcePdf, [page.srcIndex]);
1626
+ copiedPages.forEach((copiedPage) => combinedDoc.addPage(copiedPage));
1627
+ } catch (copyError) {
1628
+ console.error(`Error copying page ${page.srcIndex}:`, copyError);
1629
+ throw new Error(`Failed to copy page: ${copyError instanceof Error ? copyError.message : String(copyError)}`);
1630
+ }
1631
+ }
1632
+ } else if (sourceFile.type === "image" && sourceFile.originalFile) {
1633
+ try {
1634
+ const imagePdfBytes = await imageToPdf(sourceFile.originalFile);
1635
+ const imagePdf = await PDFDocument2.load(imagePdfBytes);
1636
+ const copiedPages = await combinedDoc.copyPages(imagePdf, [0]);
1637
+ copiedPages.forEach((copiedPage) => combinedDoc.addPage(copiedPage));
1638
+ } catch (imageError) {
1639
+ console.error(`Error converting image page:`, imageError);
1640
+ throw new Error(`Failed to convert image: ${imageError instanceof Error ? imageError.message : String(imageError)}`);
1641
+ }
1642
+ }
1643
+ }
1644
+ }
1645
+ const pdfBytes = await combinedDoc.save();
1646
+ const blob = new Blob([pdfBytes.buffer], { type: "application/pdf" });
1647
+ return blob;
1648
+ } catch (error) {
1649
+ console.error("Error assembling PDF:", error);
1650
+ throw error;
1651
+ } finally {
1652
+ setIsLoading(false);
1653
+ setProcessingStatus("");
1654
+ }
1655
+ },
1656
+ getSummary: () => summary,
1657
+ isBusy: () => isLoading,
1658
+ getState: () => state
1659
+ }), [state, summary, isLoading]);
1660
+ return /* @__PURE__ */ jsx(TooltipProvider, { children: /* @__PURE__ */ jsx(DndProvider, { backend: HTML5Backend, children: /* @__PURE__ */ jsxs("div", { className: cn("space-y-6 pb-6", className), children: [
1661
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
1662
+ /* @__PURE__ */ jsxs("div", { children: [
1663
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-medium", children: "Page Assembly" }),
1664
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: "Upload, arrange, and combine pages into a single PDF document" })
1665
+ ] }),
1666
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-4", children: [
1667
+ summary.totalPages > 0 && /* @__PURE__ */ jsxs("span", { className: "text-sm text-gray-500", children: [
1668
+ summary.totalFiles,
1669
+ " file",
1670
+ summary.totalFiles !== 1 ? "s" : "",
1671
+ ", ",
1672
+ summary.totalPages,
1673
+ " page",
1674
+ summary.totalPages !== 1 ? "s" : ""
1675
+ ] }),
1676
+ !disableFileUpload && /* @__PURE__ */ jsxs(Fragment, { children: [
1677
+ /* @__PURE__ */ jsxs(Button, { type: "button", onClick: handleFileUpload, disabled: isLoading, children: [
1678
+ /* @__PURE__ */ jsx(Upload, { className: "h-4 w-4 mr-2" }),
1679
+ "Add Files"
1680
+ ] }),
1681
+ /* @__PURE__ */ jsx(
1682
+ "input",
1683
+ {
1684
+ ref: fileInputRef,
1685
+ type: "file",
1686
+ multiple: true,
1687
+ accept: ".pdf,image/*",
1688
+ onChange: handleFileChange,
1689
+ className: "hidden"
1690
+ }
1691
+ )
1692
+ ] })
1693
+ ] })
1694
+ ] }),
1695
+ isLoading && /* @__PURE__ */ jsx(Card, { className: "p-4", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
1696
+ /* @__PURE__ */ jsx("div", { className: "animate-spin rounded-full h-4 w-4 border-b-2 border-primary" }),
1697
+ /* @__PURE__ */ jsx("span", { className: "text-sm", children: processingStatus })
1698
+ ] }) }),
1699
+ /* @__PURE__ */ jsx(
1700
+ BulkActionBar,
1701
+ {
1702
+ selectedCount: state.selection.length,
1703
+ files: state.files,
1704
+ onMovePages: handleMovePages,
1705
+ onDeletePages: handleDeletePages,
1706
+ onClearSelection: () => dispatch({ type: "CLEAR_SELECTION" })
1707
+ }
1708
+ ),
1709
+ state.files.length > 0 ? /* @__PURE__ */ jsxs(
1710
+ "div",
1711
+ {
1712
+ onDrop: handleDrop,
1713
+ onDragOver: handleDragOver,
1714
+ children: [
1715
+ /* @__PURE__ */ jsx(
1716
+ FileDropZone,
1717
+ {
1718
+ targetIndex: 0,
1719
+ onReorderFiles: handleReorderFiles,
1720
+ files: state.files
1721
+ }
1722
+ ),
1723
+ state.files.map((file, index) => /* @__PURE__ */ jsxs("div", { children: [
1724
+ /* @__PURE__ */ jsx(
1725
+ FileCard,
1726
+ {
1727
+ file,
1728
+ selectedPageIds: state.selection,
1729
+ onSingleSelectPage: handleSingleSelectPage,
1730
+ onTogglePageSelect: handleTogglePageSelect,
1731
+ onRangeSelectPage: handleRangeSelectPage,
1732
+ onSelectAllInFile: handleSelectAllInFile,
1733
+ onRemoveFile: handleRemoveFile,
1734
+ onRenameFile: handleRenameFile,
1735
+ onReorderPages: (fileId, newOrder) => dispatch({ type: "REORDER_PAGES", payload: { fileId, newOrder } }),
1736
+ isDraggingPageRef: isDraggingPage
1737
+ }
1738
+ ),
1739
+ /* @__PURE__ */ jsx(
1740
+ FileDropZone,
1741
+ {
1742
+ targetIndex: index + 1,
1743
+ onReorderFiles: handleReorderFiles,
1744
+ files: state.files
1745
+ }
1746
+ )
1747
+ ] }, file.id))
1748
+ ]
1749
+ }
1750
+ ) : !disableFileUpload && /* @__PURE__ */ jsx(
1751
+ Card,
1752
+ {
1753
+ className: "p-12 border-dashed border-2 border-gray-300 hover:border-gray-400 transition-colors cursor-pointer",
1754
+ onClick: handleFileUpload,
1755
+ onDrop: handleDrop,
1756
+ onDragOver: handleDragOver,
1757
+ children: /* @__PURE__ */ jsxs("div", { className: "text-center", children: [
1758
+ /* @__PURE__ */ jsx(FolderOpen, { className: "h-12 w-12 mx-auto text-gray-400 mb-4" }),
1759
+ /* @__PURE__ */ jsx("h3", { className: "text-lg font-medium text-gray-900 mb-2", children: "No files added" }),
1760
+ /* @__PURE__ */ jsx("p", { className: "text-gray-500 mb-4", children: "Upload PDF files and images to start building your document" }),
1761
+ /* @__PURE__ */ jsxs(Button, { type: "button", variant: "outline", children: [
1762
+ /* @__PURE__ */ jsx(Upload, { className: "h-4 w-4 mr-2" }),
1763
+ "Choose Files"
1764
+ ] })
1765
+ ] })
1766
+ }
1767
+ ),
1768
+ /* @__PURE__ */ jsx(AlertDialog, { open: showDeleteConfirm, onOpenChange: setShowDeleteConfirm, children: /* @__PURE__ */ jsxs(AlertDialogContent, { children: [
1769
+ /* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
1770
+ /* @__PURE__ */ jsx(AlertDialogTitle, { children: "Delete Pages" }),
1771
+ /* @__PURE__ */ jsxs(AlertDialogDescription, { children: [
1772
+ "Are you sure you want to delete ",
1773
+ state.selection.length,
1774
+ " selected page",
1775
+ state.selection.length !== 1 ? "s" : "",
1776
+ "? This action cannot be undone."
1777
+ ] })
1778
+ ] }),
1779
+ /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [
1780
+ /* @__PURE__ */ jsx(AlertDialogCancel, { children: "Cancel" }),
1781
+ /* @__PURE__ */ jsx(
1782
+ AlertDialogAction,
1783
+ {
1784
+ className: "bg-destructive text-white hover:bg-destructive/90",
1785
+ onClick: confirmDeletePages,
1786
+ children: "Delete"
1787
+ }
1788
+ )
1789
+ ] })
1790
+ ] }) }),
1791
+ /* @__PURE__ */ jsx(AlertDialog, { open: showRemoveFileConfirm, onOpenChange: setShowRemoveFileConfirm, children: /* @__PURE__ */ jsxs(AlertDialogContent, { children: [
1792
+ /* @__PURE__ */ jsxs(AlertDialogHeader, { children: [
1793
+ /* @__PURE__ */ jsx(AlertDialogTitle, { children: "Remove File" }),
1794
+ /* @__PURE__ */ jsxs(AlertDialogDescription, { children: [
1795
+ 'Are you sure you want to remove "',
1796
+ fileToRemove?.name,
1797
+ '" and all its ',
1798
+ fileToRemove?.pageCount,
1799
+ " page",
1800
+ fileToRemove?.pageCount !== 1 ? "s" : "",
1801
+ "? This action cannot be undone."
1802
+ ] })
1803
+ ] }),
1804
+ /* @__PURE__ */ jsxs(AlertDialogFooter, { children: [
1805
+ /* @__PURE__ */ jsx(AlertDialogCancel, { onClick: () => {
1806
+ setFileToRemove(null);
1807
+ setShowRemoveFileConfirm(false);
1808
+ }, children: "Cancel" }),
1809
+ /* @__PURE__ */ jsx(
1810
+ AlertDialogAction,
1811
+ {
1812
+ className: "bg-destructive text-destructive-foreground hover:bg-destructive/90",
1813
+ onClick: confirmRemoveFile,
1814
+ children: "Remove File"
1815
+ }
1816
+ )
1817
+ ] })
1818
+ ] }) })
1819
+ ] }) }) });
1820
+ }
1821
+ );
1822
+ PageAssembler.displayName = "PageAssembler";
1823
+ function generateId() {
1824
+ return `${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
1825
+ }
1826
+ function debounce(func, wait) {
1827
+ let timeout;
1828
+ return ((...args) => {
1829
+ clearTimeout(timeout);
1830
+ timeout = setTimeout(() => func(...args), wait);
1831
+ });
1832
+ }
1833
+ var PageAssembler_default = PageAssembler;
1834
+
1835
+ export { Button, PageAssembler, createPdfBlobUrl, PageAssembler_default as default, downloadPdf, imageToPdf, pdfToImages };
1836
+ //# sourceMappingURL=index.mjs.map
1837
+ //# sourceMappingURL=index.mjs.map