@scanfit/browser 0.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.js ADDED
@@ -0,0 +1,993 @@
1
+ import { jsxs as r, jsx as e, Fragment as H } from "react/jsx-runtime";
2
+ import { useState as C, useEffect as L, useCallback as Z, useSyncExternalStore as ie, useRef as D, useMemo as ne, useId as ae } from "react";
3
+ import { createScanSession as se } from "./core.js";
4
+ import { v as oe } from "./chunks/geometry-57LNKdIq.js";
5
+ import { c as M, F as le } from "./chunks/types-Bf5l1UNl.js";
6
+ const _ = {
7
+ pages: [],
8
+ status: "idle",
9
+ progress: 0,
10
+ result: null,
11
+ error: null
12
+ }, ce = {};
13
+ function de(t = ce, d) {
14
+ const [s, o] = C(null);
15
+ L(() => {
16
+ if (d) {
17
+ o(null);
18
+ return;
19
+ }
20
+ const v = se(t);
21
+ return o(v), () => v.dispose();
22
+ }, [
23
+ d,
24
+ t.detector,
25
+ t.detectorModule,
26
+ t.detectorOptions?.minConfidence,
27
+ t.detectorOptions?.maxComponents,
28
+ t.detectorOptions?.maxCandidates,
29
+ t.workerUrl,
30
+ t.limits?.maxPages,
31
+ t.limits?.maxFileBytes,
32
+ t.limits?.maxPixels,
33
+ t.limits?.maxSessionBytes
34
+ ]);
35
+ const n = d ?? s, u = Z(
36
+ (v) => n ? n.subscribe(v) : () => {
37
+ },
38
+ [n]
39
+ ), w = Z(() => n?.getSnapshot() ?? _, [n]), g = ie(u, w, () => _);
40
+ return { session: n, ...g };
41
+ }
42
+ function z(t) {
43
+ const [d, s] = C("");
44
+ return L(() => {
45
+ if (!t) {
46
+ s("");
47
+ return;
48
+ }
49
+ const o = URL.createObjectURL(t);
50
+ return s(o), () => URL.revokeObjectURL(o);
51
+ }, [t]), d;
52
+ }
53
+ const ue = {
54
+ title: "Scan your documents",
55
+ subtitle: "A few photos. One upload-ready PDF.",
56
+ addPhotos: "Add photos",
57
+ camera: "Use camera",
58
+ close: "Close scanner",
59
+ cancel: "Cancel",
60
+ emptyTitle: "Your paperwork, simplified.",
61
+ emptyDescription: "Take a photo or add images to create a PDF that fits your upload limit.",
62
+ choosePhotos: "Choose photos",
63
+ formats: "JPEG, PNG, or WebP",
64
+ privacy: "Processed on your device. Nothing is uploaded.",
65
+ page: "Page",
66
+ pages: "Pages",
67
+ edit: "Adjust corners",
68
+ preview: "Preview page",
69
+ rotate: "Rotate clockwise",
70
+ retake: "Replace photo",
71
+ remove: "Remove page",
72
+ moveUp: "Move page earlier",
73
+ moveDown: "Move page later",
74
+ natural: "Natural color",
75
+ grayscale: "Grayscale",
76
+ contrast: "More contrast",
77
+ filter: "Appearance",
78
+ corner: "Corner",
79
+ topLeft: "Top left",
80
+ topRight: "Top right",
81
+ bottomRight: "Bottom right",
82
+ bottomLeft: "Bottom left",
83
+ nudgeUp: "Nudge up",
84
+ nudgeDown: "Nudge down",
85
+ nudgeLeft: "Nudge left",
86
+ nudgeRight: "Nudge right",
87
+ resetCrop: "Use full image",
88
+ cropHelp: "Changes apply immediately. Drag a corner, use arrow keys, or select a corner and tap the arrows. Shift + arrow moves farther.",
89
+ prepare: "Prepare PDF",
90
+ preparing: "Preparing your PDF…",
91
+ importing: "Preparing your photos…",
92
+ pageSize: "Paper size",
93
+ a4: "A4",
94
+ letter: "US Letter",
95
+ image: "Match image",
96
+ limit: "Upload limit",
97
+ actualSize: "PDF size",
98
+ imageBytes: "Image contribution",
99
+ quality: "JPEG quality",
100
+ dimensions: "Dimensions",
101
+ qualityFloor: "Minimum JPEG quality",
102
+ resolutionFloor: "Minimum long edge (native size if smaller)",
103
+ invalidLimit: "maxBytes must be a positive integer number of bytes.",
104
+ reviewTitle: "Ready for your final look.",
105
+ reviewDescription: "These previews use the exact compressed images embedded in your PDF. Zoom in and check small text, signatures, and stamps.",
106
+ cannotFitTitle: "This document needs more room.",
107
+ cannotFitDescription: "We could not fit these pages within the limit and quality settings. Crop away excess background, retake a page, or remove pages you do not need.",
108
+ confirm: "Use this PDF",
109
+ back: "Back to editing",
110
+ zoom: "Preview zoom",
111
+ qualityNote: "Quality settings are not a readability guarantee. Please inspect the final result.",
112
+ noRecovery: "This session is held in memory. Refreshing or closing loses unfinished scans.",
113
+ discard: "Discard these unfinished scans? They are not saved and cannot be restored.",
114
+ removeConfirm: "Remove this page?",
115
+ cameraTitle: "Capture a page",
116
+ shutter: "Take photo",
117
+ cameraStarting: "Starting camera…",
118
+ cameraHelp: "Place the page on a contrasting background. Keep all four corners visible.",
119
+ cameraError: "The camera is unavailable or permission was denied. You can still choose photos.",
120
+ correctedPreview: "Corrected document preview",
121
+ sourcePreview: "Source document with editable corners",
122
+ manualCrop: "Check the four corners before continuing.",
123
+ detectorUnavailable: "Automatic detection was unavailable. Adjust the corners manually.",
124
+ blurry: "This photo may be blurry. Inspect the text or retake it.",
125
+ dark: "This photo may be too dark. Try better lighting.",
126
+ lowResolution: "This photo has limited resolution. Inspect small text carefully."
127
+ }, X = (t) => t < 1e3 ? `${t} B` : t < 1e6 ? `${(t / 1e3).toFixed(1)} kB` : `${(t / 1e6).toFixed(2)} MB`;
128
+ function he({
129
+ page: t,
130
+ onApply: d,
131
+ messages: s,
132
+ disabled: o
133
+ }) {
134
+ const n = z(t.preview), [u, w] = C(() => M(t.edits.corners)), [g, v] = C(0), i = D(null), l = D(null);
135
+ L(
136
+ () => w(M(t.edits.corners)),
137
+ [t.edits.corners]
138
+ );
139
+ const b = [s.topLeft, s.topRight, s.bottomRight, s.bottomLeft];
140
+ function p(c, h, m) {
141
+ const f = M(u);
142
+ f[c] = {
143
+ x: Math.max(0, Math.min(1, h)),
144
+ y: Math.max(0, Math.min(1, m))
145
+ };
146
+ try {
147
+ oe(f);
148
+ } catch {
149
+ return;
150
+ }
151
+ w(f), d(f);
152
+ }
153
+ function P(c, h, m = 1) {
154
+ const f = u[g];
155
+ p(
156
+ g,
157
+ f.x + c * m / t.width,
158
+ f.y + h * m / t.height
159
+ );
160
+ }
161
+ const x = 600, y = x * t.height / t.width;
162
+ return /* @__PURE__ */ r("div", { className: "sf-crop", children: [
163
+ /* @__PURE__ */ r(
164
+ "div",
165
+ {
166
+ className: "sf-image-wrap",
167
+ ref: i,
168
+ style: { aspectRatio: `${t.width}/${t.height}` },
169
+ children: [
170
+ n ? /* @__PURE__ */ e("img", { src: n, alt: s.sourcePreview, draggable: !1 }) : null,
171
+ /* @__PURE__ */ e(
172
+ "svg",
173
+ {
174
+ className: "sf-outline",
175
+ viewBox: "0 0 100 100",
176
+ preserveAspectRatio: "none",
177
+ "aria-hidden": "true",
178
+ children: /* @__PURE__ */ e(
179
+ "polygon",
180
+ {
181
+ points: u.map((c) => `${c.x * 100},${c.y * 100}`).join(" ")
182
+ }
183
+ )
184
+ }
185
+ ),
186
+ u.map((c, h) => /* @__PURE__ */ e(
187
+ "button",
188
+ {
189
+ type: "button",
190
+ className: `sf-handle ${g === h ? "sf-selected" : ""}`,
191
+ "aria-label": `${s.corner}: ${b[h]}`,
192
+ "aria-pressed": g === h,
193
+ "aria-keyshortcuts": "ArrowUp ArrowDown ArrowLeft ArrowRight",
194
+ disabled: o,
195
+ style: { left: `${c.x * 100}%`, top: `${c.y * 100}%` },
196
+ onFocus: () => v(h),
197
+ onPointerDown: (m) => {
198
+ o || (v(h), l.current = {
199
+ index: h,
200
+ rect: i.current.getBoundingClientRect()
201
+ }, m.currentTarget.setPointerCapture(m.pointerId));
202
+ },
203
+ onPointerMove: (m) => {
204
+ const f = l.current;
205
+ f?.index === h && p(
206
+ h,
207
+ (m.clientX - f.rect.left) / f.rect.width,
208
+ (m.clientY - f.rect.top) / f.rect.height
209
+ );
210
+ },
211
+ onPointerUp: () => {
212
+ l.current = null;
213
+ },
214
+ onPointerCancel: () => {
215
+ l.current = null;
216
+ },
217
+ onKeyDown: (m) => {
218
+ const T = {
219
+ ArrowLeft: [-1, 0],
220
+ ArrowRight: [1, 0],
221
+ ArrowUp: [0, -1],
222
+ ArrowDown: [0, 1]
223
+ }[m.key];
224
+ if (T) {
225
+ m.preventDefault();
226
+ const E = m.shiftKey ? 10 : 1;
227
+ p(
228
+ h,
229
+ c.x + T[0] * E / t.width,
230
+ c.y + T[1] * E / t.height
231
+ );
232
+ }
233
+ }
234
+ },
235
+ h
236
+ ))
237
+ ]
238
+ }
239
+ ),
240
+ /* @__PURE__ */ r("div", { className: "sf-corner-tools", children: [
241
+ /* @__PURE__ */ e(
242
+ "div",
243
+ {
244
+ className: "sf-magnifier",
245
+ "aria-hidden": "true",
246
+ style: {
247
+ backgroundImage: n ? `url("${n}")` : void 0,
248
+ backgroundSize: `${x}px ${y}px`,
249
+ backgroundPosition: `${48 - u[g].x * x}px ${48 - u[g].y * y}px`
250
+ },
251
+ children: /* @__PURE__ */ e("span", { children: "+" })
252
+ }
253
+ ),
254
+ /* @__PURE__ */ r("div", { children: [
255
+ /* @__PURE__ */ r("label", { className: "sf-label", children: [
256
+ s.corner,
257
+ /* @__PURE__ */ e(
258
+ "select",
259
+ {
260
+ value: g,
261
+ disabled: o,
262
+ onChange: (c) => v(Number(c.target.value)),
263
+ children: b.map((c, h) => /* @__PURE__ */ e("option", { value: h, children: c }, c))
264
+ }
265
+ )
266
+ ] }),
267
+ /* @__PURE__ */ e("div", { className: "sf-nudges", children: [
268
+ [s.nudgeLeft, -1, 0, "←"],
269
+ [s.nudgeUp, 0, -1, "↑"],
270
+ [s.nudgeDown, 0, 1, "↓"],
271
+ [s.nudgeRight, 1, 0, "→"]
272
+ ].map(([c, h, m, f]) => /* @__PURE__ */ e(
273
+ "button",
274
+ {
275
+ type: "button",
276
+ "aria-label": String(c),
277
+ disabled: o,
278
+ onClick: () => P(Number(h), Number(m), 5),
279
+ children: f
280
+ },
281
+ String(c)
282
+ )) })
283
+ ] }),
284
+ /* @__PURE__ */ e("div", { className: "sf-crop-actions", children: /* @__PURE__ */ e(
285
+ "button",
286
+ {
287
+ type: "button",
288
+ disabled: o,
289
+ onClick: () => {
290
+ const c = M(le);
291
+ w(c), d(c);
292
+ },
293
+ children: s.resetCrop
294
+ }
295
+ ) })
296
+ ] }),
297
+ /* @__PURE__ */ e("p", { className: "sf-hint", children: s.cropHelp }),
298
+ /* @__PURE__ */ r("p", { className: "sf-file-input", role: "status", "aria-live": "polite", "aria-atomic": "true", children: [
299
+ b[g],
300
+ ": x ",
301
+ (u[g].x * 100).toFixed(1),
302
+ "%, y ",
303
+ (u[g].y * 100).toFixed(1),
304
+ "%"
305
+ ] })
306
+ ] });
307
+ }
308
+ function me({
309
+ onCapture: t,
310
+ onClose: d,
311
+ messages: s
312
+ }) {
313
+ const o = D(null), n = D(null), u = D(!1), [w, g] = C(!1), [v, i] = C(!1), [l, b] = C(!1), p = D(d);
314
+ p.current = d;
315
+ function P() {
316
+ n.current?.getTracks().forEach((y) => y.stop()), n.current = null;
317
+ }
318
+ L(() => {
319
+ u.current = !0;
320
+ let y = !0;
321
+ const c = () => {
322
+ document.hidden && (P(), p.current());
323
+ };
324
+ return document.addEventListener("visibilitychange", c), (async () => {
325
+ try {
326
+ if (!navigator.mediaDevices?.getUserMedia)
327
+ throw new Error("Camera unavailable.");
328
+ const h = await navigator.mediaDevices.getUserMedia({
329
+ audio: !1,
330
+ video: {
331
+ facingMode: { ideal: "environment" },
332
+ width: { ideal: 2560 },
333
+ height: { ideal: 1920 }
334
+ }
335
+ });
336
+ if (!y) {
337
+ h.getTracks().forEach((m) => m.stop());
338
+ return;
339
+ }
340
+ n.current = h, o.current.srcObject = h, await o.current.play(), y && g(!0);
341
+ } catch {
342
+ y && (P(), i(!0));
343
+ }
344
+ })(), () => {
345
+ y = !1, u.current = !1, document.removeEventListener("visibilitychange", c), P();
346
+ };
347
+ }, []);
348
+ async function x() {
349
+ if (!o.current || !w || l) return;
350
+ b(!0);
351
+ const y = document.createElement("canvas");
352
+ y.width = o.current.videoWidth, y.height = o.current.videoHeight;
353
+ try {
354
+ y.getContext("2d").drawImage(o.current, 0, 0);
355
+ const c = await new Promise(
356
+ (h, m) => y.toBlob(
357
+ (f) => f ? h(f) : m(new Error()),
358
+ "image/jpeg",
359
+ 0.95
360
+ )
361
+ );
362
+ P(), u.current && t(c);
363
+ } catch {
364
+ u.current && (i(!0), b(!1));
365
+ } finally {
366
+ y.width = y.height = 0;
367
+ }
368
+ }
369
+ return /* @__PURE__ */ r("section", { className: "sf-camera", "aria-label": s.cameraTitle, children: [
370
+ /* @__PURE__ */ r("div", { className: "sf-section-heading", children: [
371
+ /* @__PURE__ */ e("h3", { children: s.cameraTitle }),
372
+ /* @__PURE__ */ e("button", { type: "button", onClick: d, children: s.cancel })
373
+ ] }),
374
+ v ? /* @__PURE__ */ e("p", { role: "alert", children: s.cameraError }) : /* @__PURE__ */ r(H, { children: [
375
+ /* @__PURE__ */ e("video", { ref: o, playsInline: !0, muted: !0, autoPlay: !0 }),
376
+ /* @__PURE__ */ e("p", { children: w ? s.cameraHelp : s.cameraStarting }),
377
+ /* @__PURE__ */ e(
378
+ "button",
379
+ {
380
+ type: "button",
381
+ className: "sf-primary",
382
+ disabled: !w || l,
383
+ onClick: () => {
384
+ x();
385
+ },
386
+ children: s.shutter
387
+ }
388
+ )
389
+ ] })
390
+ ] });
391
+ }
392
+ const V = {
393
+ "manual-crop": "manualCrop",
394
+ "detection-unavailable": "detectorUnavailable",
395
+ "possibly-blurry": "blurry",
396
+ "possibly-dark": "dark",
397
+ "low-resolution": "lowResolution"
398
+ };
399
+ function pe({
400
+ page: t,
401
+ index: d,
402
+ selected: s,
403
+ onSelect: o,
404
+ m: n
405
+ }) {
406
+ const u = z(t.thumbnail);
407
+ return /* @__PURE__ */ r(
408
+ "button",
409
+ {
410
+ type: "button",
411
+ className: `sf-thumbnail ${s ? "sf-current" : ""}`,
412
+ "aria-pressed": s,
413
+ "aria-label": `${n.page} ${d + 1}`,
414
+ onClick: o,
415
+ children: [
416
+ u ? /* @__PURE__ */ e("img", { src: u, alt: "" }) : null,
417
+ /* @__PURE__ */ e("span", { children: String(d + 1).padStart(2, "0") })
418
+ ]
419
+ }
420
+ );
421
+ }
422
+ function ge({
423
+ session: t,
424
+ page: d,
425
+ m: s
426
+ }) {
427
+ const [o, n] = C(null), [u, w] = C(""), g = z(o);
428
+ return L(() => {
429
+ const v = new AbortController();
430
+ return n(null), w(""), t.renderPage(d.id, v.signal).then((i) => {
431
+ v.signal.aborted || n(i);
432
+ }).catch((i) => {
433
+ v.signal.aborted || w(i.message);
434
+ }), () => v.abort();
435
+ }, [
436
+ t,
437
+ d.id,
438
+ d.edits.corners,
439
+ d.edits.filter,
440
+ d.edits.rotation
441
+ ]), /* @__PURE__ */ e("div", { className: "sf-processed", children: u ? /* @__PURE__ */ e("p", { role: "alert", children: u }) : g ? /* @__PURE__ */ e("img", { src: g, alt: s.correctedPreview }) : /* @__PURE__ */ e("p", { role: "status", children: s.preparing }) });
442
+ }
443
+ function fe({
444
+ report: t,
445
+ ready: d,
446
+ onBack: s,
447
+ onConfirm: o,
448
+ m: n
449
+ }) {
450
+ const [u, w] = C(0), [g, v] = C(100), i = D(null), l = t.pages[u] ?? t.pages[0], b = z(l.preview);
451
+ return L(() => i.current?.focus(), []), /* @__PURE__ */ r("section", { className: "sf-review", children: [
452
+ /* @__PURE__ */ r("div", { className: `sf-result-banner ${d ? "" : "sf-warning"}`, children: [
453
+ /* @__PURE__ */ e("span", { className: "sf-result-icon", "aria-hidden": "true", children: d ? "✓" : "!" }),
454
+ /* @__PURE__ */ r("div", { children: [
455
+ /* @__PURE__ */ e("h3", { tabIndex: -1, ref: i, children: d ? n.reviewTitle : n.cannotFitTitle }),
456
+ /* @__PURE__ */ e("p", { children: d ? n.reviewDescription : n.cannotFitDescription })
457
+ ] })
458
+ ] }),
459
+ /* @__PURE__ */ r("div", { className: "sf-review-layout", children: [
460
+ /* @__PURE__ */ r("div", { className: "sf-final-stage", children: [
461
+ /* @__PURE__ */ r("div", { className: "sf-section-heading", children: [
462
+ /* @__PURE__ */ r("label", { children: [
463
+ n.page,
464
+ " ",
465
+ /* @__PURE__ */ e(
466
+ "select",
467
+ {
468
+ value: u,
469
+ onChange: (p) => w(Number(p.target.value)),
470
+ children: t.pages.map((p, P) => /* @__PURE__ */ e("option", { value: P, children: P + 1 }, p.id))
471
+ }
472
+ )
473
+ ] }),
474
+ /* @__PURE__ */ r("label", { children: [
475
+ n.zoom,
476
+ " ",
477
+ /* @__PURE__ */ e(
478
+ "select",
479
+ {
480
+ value: g,
481
+ onChange: (p) => v(Number(p.target.value)),
482
+ children: [100, 150, 200, 300].map((p) => /* @__PURE__ */ r("option", { value: p, children: [
483
+ p,
484
+ "%"
485
+ ] }, p))
486
+ }
487
+ )
488
+ ] })
489
+ ] }),
490
+ /* @__PURE__ */ e("div", { className: "sf-zoom-viewport", children: b ? /* @__PURE__ */ e(
491
+ "img",
492
+ {
493
+ src: b,
494
+ alt: `${n.correctedPreview} ${u + 1}`,
495
+ style: { width: `${g}%`, maxWidth: "none" }
496
+ }
497
+ ) : null })
498
+ ] }),
499
+ /* @__PURE__ */ r("aside", { className: "sf-report", children: [
500
+ /* @__PURE__ */ e("div", { className: "sf-size-number", children: X(t.bytes) }),
501
+ /* @__PURE__ */ r("p", { children: [
502
+ n.actualSize,
503
+ ": ",
504
+ t.bytes.toLocaleString(),
505
+ " B",
506
+ /* @__PURE__ */ e("br", {}),
507
+ n.limit,
508
+ ": ",
509
+ t.maxBytes.toLocaleString(),
510
+ " B"
511
+ ] }),
512
+ /* @__PURE__ */ e("div", { className: "sf-budget-track", children: /* @__PURE__ */ e(
513
+ "span",
514
+ {
515
+ style: {
516
+ width: `${Math.min(100, t.bytes / t.maxBytes * 100)}%`
517
+ }
518
+ }
519
+ ) }),
520
+ /* @__PURE__ */ e("h4", { children: n.imageBytes }),
521
+ /* @__PURE__ */ e("ol", { children: t.pages.map((p, P) => /* @__PURE__ */ r("li", { children: [
522
+ /* @__PURE__ */ r("button", { type: "button", onClick: () => w(P), children: [
523
+ n.page,
524
+ " ",
525
+ P + 1
526
+ ] }),
527
+ /* @__PURE__ */ r("span", { children: [
528
+ p.imageBytes.toLocaleString(),
529
+ " B"
530
+ ] })
531
+ ] }, p.id)) }),
532
+ /* @__PURE__ */ r("dl", { children: [
533
+ /* @__PURE__ */ e("dt", { children: n.dimensions }),
534
+ /* @__PURE__ */ r("dd", { children: [
535
+ l.width,
536
+ " × ",
537
+ l.height
538
+ ] }),
539
+ /* @__PURE__ */ e("dt", { children: n.quality }),
540
+ /* @__PURE__ */ r("dd", { children: [
541
+ Math.round(l.quality * 100),
542
+ "%"
543
+ ] }),
544
+ /* @__PURE__ */ e("dt", { children: n.qualityFloor }),
545
+ /* @__PURE__ */ r("dd", { children: [
546
+ Math.round(t.limits.minQuality * 100),
547
+ "%"
548
+ ] }),
549
+ /* @__PURE__ */ e("dt", { children: n.resolutionFloor }),
550
+ /* @__PURE__ */ r("dd", { children: [
551
+ t.limits.minLongEdge,
552
+ " px"
553
+ ] })
554
+ ] }),
555
+ /* @__PURE__ */ e("p", { className: "sf-hint", children: n.qualityNote }),
556
+ l.warnings.map((p) => /* @__PURE__ */ e("p", { className: "sf-hint", children: n[V[p]] }, p))
557
+ ] })
558
+ ] }),
559
+ /* @__PURE__ */ r("div", { className: "sf-footer", children: [
560
+ /* @__PURE__ */ e("button", { type: "button", onClick: s, children: n.back }),
561
+ d ? /* @__PURE__ */ r("button", { type: "button", className: "sf-primary", onClick: o, children: [
562
+ n.confirm,
563
+ " ",
564
+ /* @__PURE__ */ e("span", { "aria-hidden": "true", children: "↗" })
565
+ ] }) : null
566
+ ] })
567
+ ] });
568
+ }
569
+ function Ce(t) {
570
+ const {
571
+ session: d,
572
+ options: s,
573
+ maxBytes: o,
574
+ onComplete: n,
575
+ onClose: u,
576
+ onError: w,
577
+ dir: g = "ltr",
578
+ className: v = ""
579
+ } = t, i = ne(
580
+ () => ({ ...ue, ...t.messages }),
581
+ [t.messages]
582
+ ), { session: l, pages: b, status: p, progress: P, result: x } = de(
583
+ s,
584
+ d
585
+ ), [y, c] = C(""), [h, m] = C("crop"), [f, T] = C(!1), [E, $] = C(""), [Q, ee] = C("a4"), [G, B] = C(), I = D(null), W = D(null), O = ae(), k = p !== "idle", N = b.find((a) => a.id === y) ?? b[0], F = N ? b.indexOf(N) : -1, j = Number.isSafeInteger(o) && o > 0, K = t.qualityLimits?.minQuality, Y = t.qualityLimits?.minLongEdge;
586
+ L(() => {
587
+ l?.cancel();
588
+ }, [l, o, K, Y]);
589
+ function q(a) {
590
+ const S = a instanceof Error ? a : new Error(String(a));
591
+ $(S.message), w?.(S);
592
+ }
593
+ async function A(a, S) {
594
+ if (l) {
595
+ $(""), T(!1), m("crop");
596
+ try {
597
+ const U = await l.addFiles(a, { replacePageId: S });
598
+ U.length && c(U[U.length - 1].id);
599
+ } catch (U) {
600
+ q(U);
601
+ } finally {
602
+ B(void 0);
603
+ }
604
+ }
605
+ }
606
+ function R(a) {
607
+ $("");
608
+ try {
609
+ a();
610
+ } catch (S) {
611
+ q(S);
612
+ }
613
+ }
614
+ function te() {
615
+ (!b.length || window.confirm(i.discard)) && u?.();
616
+ }
617
+ async function re() {
618
+ if (!(!l || !j)) {
619
+ m("crop"), $("");
620
+ try {
621
+ await l.exportPdf({ maxBytes: o, pageSize: Q, minQuality: K, minLongEdge: Y });
622
+ } catch (a) {
623
+ q(a);
624
+ }
625
+ }
626
+ }
627
+ const J = x && x.status !== "cancelled" ? x.report : null;
628
+ return /* @__PURE__ */ r(
629
+ "section",
630
+ {
631
+ className: `sf-scanner ${v}`,
632
+ dir: g,
633
+ "aria-labelledby": O,
634
+ children: [
635
+ /* @__PURE__ */ r("header", { className: "sf-header", children: [
636
+ t.renderHeader ? /* @__PURE__ */ r("div", { children: [
637
+ /* @__PURE__ */ e("h2", { id: O, className: "sf-file-input", children: i.title }),
638
+ t.renderHeader({ pageCount: b.length, maxBytes: o })
639
+ ] }) : /* @__PURE__ */ r("div", { children: [
640
+ /* @__PURE__ */ e("h2", { id: O, children: i.title }),
641
+ /* @__PURE__ */ e("p", { children: i.subtitle })
642
+ ] }),
643
+ u ? /* @__PURE__ */ e(
644
+ "button",
645
+ {
646
+ type: "button",
647
+ className: "sf-icon-button",
648
+ "aria-label": i.close,
649
+ onClick: te,
650
+ children: "×"
651
+ }
652
+ ) : null
653
+ ] }),
654
+ /* @__PURE__ */ e(
655
+ "input",
656
+ {
657
+ ref: I,
658
+ className: "sf-file-input",
659
+ tabIndex: -1,
660
+ "aria-hidden": "true",
661
+ "aria-label": i.addPhotos,
662
+ type: "file",
663
+ accept: "image/jpeg,image/png,image/webp",
664
+ multiple: !0,
665
+ onChange: (a) => {
666
+ const S = Array.from(a.target.files ?? []);
667
+ a.target.value = "", A(S);
668
+ }
669
+ }
670
+ ),
671
+ /* @__PURE__ */ e(
672
+ "input",
673
+ {
674
+ ref: W,
675
+ className: "sf-file-input",
676
+ tabIndex: -1,
677
+ "aria-hidden": "true",
678
+ "aria-label": i.retake,
679
+ type: "file",
680
+ accept: "image/jpeg,image/png,image/webp",
681
+ capture: "environment",
682
+ onChange: (a) => {
683
+ const S = Array.from(a.target.files ?? []);
684
+ a.target.value = "", S.length && A(S, G);
685
+ }
686
+ }
687
+ ),
688
+ E ? /* @__PURE__ */ e("div", { className: "sf-error", role: "alert", children: E }) : null,
689
+ j ? null : /* @__PURE__ */ e("div", { className: "sf-error", role: "alert", children: i.invalidLimit }),
690
+ k ? /* @__PURE__ */ r("div", { className: "sf-progress", role: "status", children: [
691
+ /* @__PURE__ */ r("div", { children: [
692
+ /* @__PURE__ */ e("span", { children: p === "importing" ? i.importing : i.preparing }),
693
+ /* @__PURE__ */ e("button", { type: "button", onClick: () => l?.cancel(), children: i.cancel })
694
+ ] }),
695
+ /* @__PURE__ */ e("progress", { max: 1, value: P })
696
+ ] }) : null,
697
+ f ? /* @__PURE__ */ e(
698
+ me,
699
+ {
700
+ messages: i,
701
+ onClose: () => T(!1),
702
+ onCapture: (a) => {
703
+ A([a], G);
704
+ }
705
+ }
706
+ ) : J ? /* @__PURE__ */ e(
707
+ fe,
708
+ {
709
+ report: J,
710
+ ready: x?.status === "ready" && x.file.size <= o,
711
+ m: i,
712
+ onBack: () => l?.cancel(),
713
+ onConfirm: () => {
714
+ x?.status === "ready" && x.file.size <= o && n({ file: x.file, report: x.report });
715
+ }
716
+ }
717
+ ) : b.length ? /* @__PURE__ */ r(H, { children: [
718
+ /* @__PURE__ */ r("div", { className: "sf-toolbar", children: [
719
+ /* @__PURE__ */ r("div", { className: "sf-count", children: [
720
+ b.length,
721
+ " ",
722
+ i.pages.toLowerCase()
723
+ ] }),
724
+ /* @__PURE__ */ r("div", { children: [
725
+ /* @__PURE__ */ r(
726
+ "button",
727
+ {
728
+ type: "button",
729
+ disabled: k,
730
+ onClick: () => I.current?.click(),
731
+ children: [
732
+ "+ ",
733
+ i.addPhotos
734
+ ]
735
+ }
736
+ ),
737
+ /* @__PURE__ */ e(
738
+ "button",
739
+ {
740
+ type: "button",
741
+ disabled: k,
742
+ onClick: () => {
743
+ B(void 0), T(!0);
744
+ },
745
+ children: i.camera
746
+ }
747
+ )
748
+ ] })
749
+ ] }),
750
+ /* @__PURE__ */ r("div", { className: "sf-workspace", children: [
751
+ /* @__PURE__ */ e("aside", { className: "sf-pages", "aria-label": i.pages, children: b.map((a, S) => /* @__PURE__ */ r("div", { children: [
752
+ /* @__PURE__ */ e(
753
+ pe,
754
+ {
755
+ page: a,
756
+ index: S,
757
+ selected: a.id === N?.id,
758
+ onSelect: () => {
759
+ c(a.id), m("crop");
760
+ },
761
+ m: i
762
+ }
763
+ ),
764
+ t.renderPageSummary?.(a, S)
765
+ ] }, a.id)) }),
766
+ /* @__PURE__ */ e("div", { className: "sf-editor", children: N && l ? /* @__PURE__ */ r(H, { children: [
767
+ /* @__PURE__ */ r("div", { className: "sf-editor-heading", children: [
768
+ /* @__PURE__ */ r("div", { className: "sf-tabs", role: "group", "aria-label": i.page, children: [
769
+ /* @__PURE__ */ e(
770
+ "button",
771
+ {
772
+ type: "button",
773
+ "aria-pressed": h === "crop",
774
+ onClick: () => m("crop"),
775
+ children: i.edit
776
+ }
777
+ ),
778
+ /* @__PURE__ */ e(
779
+ "button",
780
+ {
781
+ type: "button",
782
+ "aria-pressed": h === "preview",
783
+ disabled: k,
784
+ onClick: () => m("preview"),
785
+ children: i.preview
786
+ }
787
+ )
788
+ ] }),
789
+ /* @__PURE__ */ r("span", { children: [
790
+ i.page,
791
+ " ",
792
+ F + 1
793
+ ] })
794
+ ] }),
795
+ h === "crop" ? /* @__PURE__ */ e(
796
+ he,
797
+ {
798
+ page: N,
799
+ disabled: k,
800
+ messages: i,
801
+ onApply: (a) => R(() => l.updatePage(N.id, { corners: a }))
802
+ },
803
+ N.id
804
+ ) : /* @__PURE__ */ e(ge, { session: l, page: N, m: i }),
805
+ /* @__PURE__ */ r("div", { className: "sf-page-actions", children: [
806
+ /* @__PURE__ */ r("label", { children: [
807
+ i.filter,
808
+ /* @__PURE__ */ r(
809
+ "select",
810
+ {
811
+ value: N.edits.filter,
812
+ disabled: k,
813
+ onChange: (a) => R(
814
+ () => l.updatePage(N.id, {
815
+ filter: a.target.value
816
+ })
817
+ ),
818
+ children: [
819
+ /* @__PURE__ */ e("option", { value: "natural", children: i.natural }),
820
+ /* @__PURE__ */ e("option", { value: "grayscale", children: i.grayscale }),
821
+ /* @__PURE__ */ e("option", { value: "contrast", children: i.contrast })
822
+ ]
823
+ }
824
+ )
825
+ ] }),
826
+ /* @__PURE__ */ e(
827
+ "button",
828
+ {
829
+ type: "button",
830
+ disabled: k,
831
+ onClick: () => R(
832
+ () => l.updatePage(N.id, {
833
+ rotation: (N.edits.rotation + 90) % 360
834
+ })
835
+ ),
836
+ children: i.rotate
837
+ }
838
+ ),
839
+ /* @__PURE__ */ e(
840
+ "button",
841
+ {
842
+ type: "button",
843
+ disabled: k,
844
+ onClick: () => {
845
+ B(N.id), W.current?.click();
846
+ },
847
+ children: i.retake
848
+ }
849
+ ),
850
+ /* @__PURE__ */ e(
851
+ "button",
852
+ {
853
+ type: "button",
854
+ disabled: k,
855
+ "aria-label": i.moveUp,
856
+ onClick: () => R(() => l.movePage(N.id, F - 1)),
857
+ hidden: F === 0,
858
+ children: "↑"
859
+ }
860
+ ),
861
+ /* @__PURE__ */ e(
862
+ "button",
863
+ {
864
+ type: "button",
865
+ disabled: k,
866
+ "aria-label": i.moveDown,
867
+ onClick: () => R(() => l.movePage(N.id, F + 1)),
868
+ hidden: F === b.length - 1,
869
+ children: "↓"
870
+ }
871
+ ),
872
+ /* @__PURE__ */ e(
873
+ "button",
874
+ {
875
+ type: "button",
876
+ className: "sf-danger",
877
+ disabled: k,
878
+ onClick: () => {
879
+ window.confirm(i.removeConfirm) && R(() => l.removePage(N.id));
880
+ },
881
+ children: i.remove
882
+ }
883
+ )
884
+ ] }),
885
+ N.warnings.map((a) => /* @__PURE__ */ e("p", { className: "sf-hint", children: i[V[a]] }, a))
886
+ ] }) : null })
887
+ ] }),
888
+ /* @__PURE__ */ r("footer", { className: "sf-footer", children: [
889
+ /* @__PURE__ */ r("label", { children: [
890
+ i.pageSize,
891
+ /* @__PURE__ */ r(
892
+ "select",
893
+ {
894
+ value: Q,
895
+ disabled: k,
896
+ onChange: (a) => ee(a.target.value),
897
+ children: [
898
+ /* @__PURE__ */ e("option", { value: "a4", children: i.a4 }),
899
+ /* @__PURE__ */ e("option", { value: "letter", children: i.letter }),
900
+ /* @__PURE__ */ e("option", { value: "image", children: i.image })
901
+ ]
902
+ }
903
+ )
904
+ ] }),
905
+ /* @__PURE__ */ r("div", { className: "sf-export-actions", children: [
906
+ /* @__PURE__ */ r("span", { children: [
907
+ i.limit,
908
+ ": ",
909
+ /* @__PURE__ */ e("strong", { children: X(o) })
910
+ ] }),
911
+ /* @__PURE__ */ r(
912
+ "button",
913
+ {
914
+ type: "button",
915
+ className: "sf-primary",
916
+ disabled: k || !j,
917
+ onClick: () => {
918
+ re();
919
+ },
920
+ children: [
921
+ i.prepare,
922
+ " ",
923
+ /* @__PURE__ */ e("span", { "aria-hidden": "true", children: "→" })
924
+ ]
925
+ }
926
+ )
927
+ ] })
928
+ ] })
929
+ ] }) : /* @__PURE__ */ r(
930
+ "div",
931
+ {
932
+ className: "sf-empty",
933
+ onDragOver: (a) => a.preventDefault(),
934
+ onDrop: (a) => {
935
+ a.preventDefault(), k || A(Array.from(a.dataTransfer.files));
936
+ },
937
+ children: [
938
+ /* @__PURE__ */ r("div", { className: "sf-paper-illustration", "aria-hidden": "true", children: [
939
+ /* @__PURE__ */ e("span", {}),
940
+ /* @__PURE__ */ e("span", {}),
941
+ /* @__PURE__ */ e("span", {}),
942
+ /* @__PURE__ */ e("span", {}),
943
+ /* @__PURE__ */ e("i", { children: "+" })
944
+ ] }),
945
+ /* @__PURE__ */ e("h3", { children: i.emptyTitle }),
946
+ /* @__PURE__ */ e("p", { children: i.emptyDescription }),
947
+ /* @__PURE__ */ r("div", { className: "sf-empty-actions", children: [
948
+ /* @__PURE__ */ r(
949
+ "button",
950
+ {
951
+ type: "button",
952
+ className: "sf-primary",
953
+ disabled: !l || k,
954
+ onClick: () => I.current?.click(),
955
+ children: [
956
+ i.choosePhotos,
957
+ " ",
958
+ /* @__PURE__ */ e("span", { "aria-hidden": "true", children: "↑" })
959
+ ]
960
+ }
961
+ ),
962
+ /* @__PURE__ */ e(
963
+ "button",
964
+ {
965
+ type: "button",
966
+ disabled: !l || k,
967
+ onClick: () => {
968
+ B(void 0), T(!0);
969
+ },
970
+ children: i.camera
971
+ }
972
+ )
973
+ ] }),
974
+ /* @__PURE__ */ e("small", { children: i.formats })
975
+ ]
976
+ }
977
+ ),
978
+ /* @__PURE__ */ r("div", { className: "sf-privacy", children: [
979
+ /* @__PURE__ */ e("span", { "aria-hidden": "true", children: "◈" }),
980
+ " ",
981
+ i.privacy,
982
+ /* @__PURE__ */ e("small", { children: i.noRecovery })
983
+ ] })
984
+ ]
985
+ }
986
+ );
987
+ }
988
+ export {
989
+ Ce as DocumentScanner,
990
+ ue as defaultMessages,
991
+ X as formatBytes,
992
+ de as useScanSession
993
+ };