viewdoc 0.2.0 → 0.3.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.
Files changed (48) hide show
  1. package/README.md +8 -2
  2. package/dist/DocxViewer-F3MW62BA.js +10 -0
  3. package/dist/PdfViewer-6YF7AER3.js +9 -0
  4. package/dist/XlsxViewer-7X25BBRT.js +10 -0
  5. package/dist/{chunk-LE4YTMPN.js → chunk-3EUNNQXJ.js} +36 -9
  6. package/dist/chunk-3EUNNQXJ.js.map +1 -0
  7. package/dist/{chunk-4VVVAGYE.js → chunk-4BFQ7U2R.js} +15 -5
  8. package/dist/chunk-4BFQ7U2R.js.map +1 -0
  9. package/dist/{chunk-KRN2POC4.js → chunk-AIDVQYXH.js} +8 -5
  10. package/dist/chunk-AIDVQYXH.js.map +1 -0
  11. package/dist/chunk-DKFD7M4R.js +12 -0
  12. package/dist/chunk-DKFD7M4R.js.map +1 -0
  13. package/dist/{chunk-OA35SYM6.js → chunk-HOF2XEU5.js} +43 -6
  14. package/dist/chunk-HOF2XEU5.js.map +1 -0
  15. package/dist/docx.cjs +58 -8
  16. package/dist/docx.cjs.map +1 -1
  17. package/dist/docx.d.cts +3 -1
  18. package/dist/docx.d.ts +3 -1
  19. package/dist/docx.js +4 -2
  20. package/dist/index.cjs +155 -57
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.cts +5 -1
  23. package/dist/index.d.ts +5 -1
  24. package/dist/index.js +27 -9
  25. package/dist/index.js.map +1 -1
  26. package/dist/pdf.cjs +46 -8
  27. package/dist/pdf.cjs.map +1 -1
  28. package/dist/pdf.d.cts +3 -1
  29. package/dist/pdf.d.ts +3 -1
  30. package/dist/pdf.js +3 -2
  31. package/dist/styles.css +15 -0
  32. package/dist/styles.css.map +1 -1
  33. package/dist/xlsx.cjs +77 -12
  34. package/dist/xlsx.cjs.map +1 -1
  35. package/dist/xlsx.d.cts +9 -1
  36. package/dist/xlsx.d.ts +9 -1
  37. package/dist/xlsx.js +4 -2
  38. package/package.json +5 -1
  39. package/dist/DocxViewer-VQN3BESX.js +0 -8
  40. package/dist/PdfViewer-NVAQBI2Y.js +0 -8
  41. package/dist/XlsxViewer-SQMAF5UA.js +0 -8
  42. package/dist/chunk-4VVVAGYE.js.map +0 -1
  43. package/dist/chunk-KRN2POC4.js.map +0 -1
  44. package/dist/chunk-LE4YTMPN.js.map +0 -1
  45. package/dist/chunk-OA35SYM6.js.map +0 -1
  46. /package/dist/{DocxViewer-VQN3BESX.js.map → DocxViewer-F3MW62BA.js.map} +0 -0
  47. /package/dist/{PdfViewer-NVAQBI2Y.js.map → PdfViewer-6YF7AER3.js.map} +0 -0
  48. /package/dist/{XlsxViewer-SQMAF5UA.js.map → XlsxViewer-7X25BBRT.js.map} +0 -0
package/dist/index.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ 'use client'
1
2
  "use strict";
2
3
  var __create = Object.create;
3
4
  var __defProp = Object.defineProperty;
@@ -297,9 +298,10 @@ function ViewerShell({
297
298
  document.addEventListener("fullscreenchange", onChange);
298
299
  return () => document.removeEventListener("fullscreenchange", onChange);
299
300
  }, []);
301
+ const fullscreenSupported = typeof document !== "undefined" && typeof document.fullscreenEnabled === "boolean" ? document.fullscreenEnabled : false;
300
302
  const onFullscreen = (0, import_react2.useCallback)(() => {
301
303
  const el = rootRef.current;
302
- if (!el) return;
304
+ if (!el || typeof el.requestFullscreen !== "function") return;
303
305
  if (document.fullscreenElement) {
304
306
  document.exitFullscreen();
305
307
  } else {
@@ -325,7 +327,7 @@ function ViewerShell({
325
327
  onZoomIn: zoomPan.zoomIn,
326
328
  onZoomOut: zoomPan.zoomOut,
327
329
  onReset: zoomPan.resetZoom,
328
- onFullscreen: enableFullscreen ? onFullscreen : void 0,
330
+ onFullscreen: enableFullscreen && fullscreenSupported ? onFullscreen : void 0,
329
331
  onDownload,
330
332
  isFullscreen,
331
333
  showZoomControls: enableZoomControls,
@@ -374,6 +376,9 @@ var init_ViewerShell = __esm({
374
376
  });
375
377
 
376
378
  // src/core/useZoomPan.ts
379
+ function pointerDistance(a, b) {
380
+ return Math.hypot(a.x - b.x, a.y - b.y);
381
+ }
377
382
  function useZoomPan(options = {}) {
378
383
  const {
379
384
  minScale = DEFAULT_MIN,
@@ -381,7 +386,8 @@ function useZoomPan(options = {}) {
381
386
  zoomStep = DEFAULT_STEP,
382
387
  enablePan = true,
383
388
  enableWheelZoom = true,
384
- enableDragScroll = false
389
+ enableDragScroll = false,
390
+ enablePinchZoom = true
385
391
  } = options;
386
392
  const [state, setState] = (0, import_react3.useState)({ scale: 1, translateX: 0, translateY: 0 });
387
393
  const containerRef = (0, import_react3.useRef)(null);
@@ -395,6 +401,12 @@ function useZoomPan(options = {}) {
395
401
  const scrollDragState = (0, import_react3.useRef)(
396
402
  { dragging: false, startX: 0, startY: 0, originLeft: 0, originTop: 0 }
397
403
  );
404
+ const activePointers = (0, import_react3.useRef)(/* @__PURE__ */ new Map());
405
+ const pinchState = (0, import_react3.useRef)({
406
+ pinching: false,
407
+ startDistance: 0,
408
+ startScale: 1
409
+ });
398
410
  const clamp = (0, import_react3.useCallback)(
399
411
  (value) => Math.min(maxScale, Math.max(minScale, value)),
400
412
  [minScale, maxScale]
@@ -418,6 +430,16 @@ function useZoomPan(options = {}) {
418
430
  );
419
431
  const onPointerDown = (0, import_react3.useCallback)(
420
432
  (e) => {
433
+ if (enablePinchZoom) {
434
+ activePointers.current.set(e.pointerId, { x: e.clientX, y: e.clientY });
435
+ if (activePointers.current.size === 2) {
436
+ const [a, b] = Array.from(activePointers.current.values());
437
+ pinchState.current = { pinching: true, startDistance: pointerDistance(a, b), startScale: state.scale };
438
+ dragState.current.dragging = false;
439
+ scrollDragState.current.dragging = false;
440
+ return;
441
+ }
442
+ }
421
443
  if (enablePan) {
422
444
  dragState.current = {
423
445
  dragging: true,
@@ -438,10 +460,20 @@ function useZoomPan(options = {}) {
438
460
  e.target.setPointerCapture(e.pointerId);
439
461
  }
440
462
  },
441
- [enablePan, enableDragScroll, state.translateX, state.translateY]
463
+ [enablePan, enableDragScroll, enablePinchZoom, state.translateX, state.translateY, state.scale]
442
464
  );
443
465
  const onPointerMove = (0, import_react3.useCallback)(
444
466
  (e) => {
467
+ if (enablePinchZoom && activePointers.current.has(e.pointerId)) {
468
+ activePointers.current.set(e.pointerId, { x: e.clientX, y: e.clientY });
469
+ }
470
+ if (pinchState.current.pinching && activePointers.current.size === 2) {
471
+ const [a, b] = Array.from(activePointers.current.values());
472
+ const distance = pointerDistance(a, b);
473
+ const ratio = distance / (pinchState.current.startDistance || distance);
474
+ setScale(pinchState.current.startScale * ratio);
475
+ return;
476
+ }
445
477
  if (enablePan) {
446
478
  if (!dragState.current.dragging) return;
447
479
  const dx = e.clientX - dragState.current.startX;
@@ -459,10 +491,14 @@ function useZoomPan(options = {}) {
459
491
  containerRef.current.scrollTop = scrollDragState.current.originTop - dy;
460
492
  }
461
493
  },
462
- [enablePan, enableDragScroll]
494
+ [enablePan, enableDragScroll, enablePinchZoom, setScale]
463
495
  );
464
496
  const onPointerUp = (0, import_react3.useCallback)(
465
497
  (e) => {
498
+ activePointers.current.delete(e.pointerId);
499
+ if (activePointers.current.size < 2) {
500
+ pinchState.current.pinching = false;
501
+ }
466
502
  if (enablePan) {
467
503
  dragState.current.dragging = false;
468
504
  e.target.releasePointerCapture(e.pointerId);
@@ -496,9 +532,9 @@ var init_useZoomPan = __esm({
496
532
 
497
533
  // src/viewers/PdfPage.tsx
498
534
  function PdfPage({ doc, pageNumber, renderScale = 1.5 }) {
499
- const canvasRef = (0, import_react4.useRef)(null);
500
- const renderTaskRef = (0, import_react4.useRef)(null);
501
- (0, import_react4.useEffect)(() => {
535
+ const canvasRef = (0, import_react5.useRef)(null);
536
+ const renderTaskRef = (0, import_react5.useRef)(null);
537
+ (0, import_react5.useEffect)(() => {
502
538
  let cancelled = false;
503
539
  doc.getPage(pageNumber).then((page) => {
504
540
  if (cancelled) return;
@@ -523,21 +559,21 @@ function PdfPage({ doc, pageNumber, renderScale = 1.5 }) {
523
559
  }, [doc, pageNumber, renderScale]);
524
560
  return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("canvas", { ref: canvasRef, className: "vd-pdf-page", "data-page-number": pageNumber });
525
561
  }
526
- var import_react4, import_jsx_runtime5;
562
+ var import_react5, import_jsx_runtime5;
527
563
  var init_PdfPage = __esm({
528
564
  "src/viewers/PdfPage.tsx"() {
529
565
  "use strict";
530
- import_react4 = require("react");
566
+ import_react5 = require("react");
531
567
  import_jsx_runtime5 = require("react/jsx-runtime");
532
568
  }
533
569
  });
534
570
 
535
571
  // src/viewers/usePdfDocument.ts
536
572
  function usePdfDocument(uri) {
537
- const [doc, setDoc] = (0, import_react5.useState)(null);
538
- const [error, setError] = (0, import_react5.useState)(null);
539
- const [loading, setLoading] = (0, import_react5.useState)(true);
540
- (0, import_react5.useEffect)(() => {
573
+ const [doc, setDoc] = (0, import_react6.useState)(null);
574
+ const [error, setError] = (0, import_react6.useState)(null);
575
+ const [loading, setLoading] = (0, import_react6.useState)(true);
576
+ (0, import_react6.useEffect)(() => {
541
577
  let cancelled = false;
542
578
  setLoading(true);
543
579
  setError(null);
@@ -560,11 +596,11 @@ function usePdfDocument(uri) {
560
596
  }, [uri]);
561
597
  return { doc, numPages: doc?.numPages ?? 0, error, loading };
562
598
  }
563
- var import_react5, pdfjsLib, import_meta;
599
+ var import_react6, pdfjsLib, import_meta;
564
600
  var init_usePdfDocument = __esm({
565
601
  "src/viewers/usePdfDocument.ts"() {
566
602
  "use strict";
567
- import_react5 = require("react");
603
+ import_react6 = require("react");
568
604
  pdfjsLib = __toESM(require("pdfjs-dist"), 1);
569
605
  import_meta = {};
570
606
  pdfjsLib.GlobalWorkerOptions.workerSrc = new URL("pdfjs-dist/build/pdf.worker.min.mjs", import_meta.url).toString();
@@ -573,8 +609,8 @@ var init_usePdfDocument = __esm({
573
609
 
574
610
  // src/viewers/useVisiblePage.ts
575
611
  function useVisiblePage(containerRef, numPages) {
576
- const [currentPage, setCurrentPage] = (0, import_react6.useState)(1);
577
- (0, import_react6.useEffect)(() => {
612
+ const [currentPage, setCurrentPage] = (0, import_react7.useState)(1);
613
+ (0, import_react7.useEffect)(() => {
578
614
  const root = containerRef.current;
579
615
  if (!root || numPages === 0) return;
580
616
  const observer = new IntersectionObserver(
@@ -593,11 +629,11 @@ function useVisiblePage(containerRef, numPages) {
593
629
  }, [containerRef, numPages]);
594
630
  return currentPage;
595
631
  }
596
- var import_react6;
632
+ var import_react7;
597
633
  var init_useVisiblePage = __esm({
598
634
  "src/viewers/useVisiblePage.ts"() {
599
635
  "use strict";
600
- import_react6 = require("react");
636
+ import_react7 = require("react");
601
637
  }
602
638
  });
603
639
 
@@ -628,6 +664,7 @@ function PdfViewer({
628
664
  renderScale,
629
665
  enableZoomControls = true,
630
666
  enableWheelZoom = true,
667
+ enablePinchZoom = true,
631
668
  enableFullscreen = true,
632
669
  enableDownload = true,
633
670
  onDownload,
@@ -642,7 +679,8 @@ function PdfViewer({
642
679
  zoomStep,
643
680
  enablePan: false,
644
681
  enableDragScroll: true,
645
- enableWheelZoom
682
+ enableWheelZoom,
683
+ enablePinchZoom
646
684
  });
647
685
  const { doc, numPages, error, loading } = usePdfDocument(uri);
648
686
  const currentPage = useVisiblePage(zoomPan.containerRef, numPages);
@@ -670,8 +708,8 @@ function PdfViewer({
670
708
  numPages
671
709
  ] }) : void 0,
672
710
  children: [
673
- loading && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "vd-pdf-page-indicator", children: "Loading PDF\u2026" }),
674
- error && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "vd-pdf-page-indicator", children: [
711
+ loading && /* @__PURE__ */ (0, import_jsx_runtime6.jsx)("span", { className: "vd-doc-status", children: "Loading PDF\u2026" }),
712
+ error && /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("span", { className: "vd-doc-status", children: [
675
713
  "Failed to load PDF: ",
676
714
  error.message
677
715
  ] }),
@@ -693,12 +731,24 @@ var init_PdfViewer = __esm({
693
731
  }
694
732
  });
695
733
 
734
+ // src/core/sanitizeHtml.ts
735
+ function sanitizeUntrustedHtml(html) {
736
+ return import_dompurify.default.sanitize(html);
737
+ }
738
+ var import_dompurify;
739
+ var init_sanitizeHtml = __esm({
740
+ "src/core/sanitizeHtml.ts"() {
741
+ "use strict";
742
+ import_dompurify = __toESM(require("dompurify"), 1);
743
+ }
744
+ });
745
+
696
746
  // src/viewers/useDocxHtml.ts
697
747
  function useDocxHtml(uri) {
698
- const [html, setHtml] = (0, import_react7.useState)(null);
699
- const [error, setError] = (0, import_react7.useState)(null);
700
- const [loading, setLoading] = (0, import_react7.useState)(true);
701
- (0, import_react7.useEffect)(() => {
748
+ const [html, setHtml] = (0, import_react8.useState)(null);
749
+ const [error, setError] = (0, import_react8.useState)(null);
750
+ const [loading, setLoading] = (0, import_react8.useState)(true);
751
+ (0, import_react8.useEffect)(() => {
702
752
  let cancelled = false;
703
753
  setLoading(true);
704
754
  setError(null);
@@ -706,9 +756,13 @@ function useDocxHtml(uri) {
706
756
  fetch(uri).then((res) => {
707
757
  if (!res.ok) throw new Error(`Failed to fetch document: ${res.status} ${res.statusText}`);
708
758
  return res.arrayBuffer();
709
- }).then((arrayBuffer) => import_mammoth.default.convertToHtml({ arrayBuffer })).then((result) => {
759
+ }).then(
760
+ (arrayBuffer) => import_mammoth.default.convertToHtml({ arrayBuffer }).catch(() => {
761
+ throw new Error("This file could not be read as a Word document (.docx).");
762
+ })
763
+ ).then((result) => {
710
764
  if (cancelled) return;
711
- setHtml(result.value);
765
+ setHtml(sanitizeUntrustedHtml(result.value));
712
766
  setLoading(false);
713
767
  }).catch((err) => {
714
768
  if (!cancelled) {
@@ -722,12 +776,13 @@ function useDocxHtml(uri) {
722
776
  }, [uri]);
723
777
  return { html, error, loading };
724
778
  }
725
- var import_react7, import_mammoth;
779
+ var import_react8, import_mammoth;
726
780
  var init_useDocxHtml = __esm({
727
781
  "src/viewers/useDocxHtml.ts"() {
728
782
  "use strict";
729
- import_react7 = require("react");
783
+ import_react8 = require("react");
730
784
  import_mammoth = __toESM(require("mammoth"), 1);
785
+ init_sanitizeHtml();
731
786
  }
732
787
  });
733
788
 
@@ -757,6 +812,7 @@ function DocxViewer({
757
812
  zoomStep,
758
813
  enableZoomControls = true,
759
814
  enableWheelZoom = true,
815
+ enablePinchZoom = true,
760
816
  enableFullscreen = true,
761
817
  enableDownload = true,
762
818
  onDownload,
@@ -771,7 +827,8 @@ function DocxViewer({
771
827
  zoomStep,
772
828
  enablePan: false,
773
829
  enableDragScroll: true,
774
- enableWheelZoom
830
+ enableWheelZoom,
831
+ enablePinchZoom
775
832
  });
776
833
  const { html, error, loading } = useDocxHtml(uri);
777
834
  const handleDownload = enableDownload ? onDownload ?? (() => defaultDownload3(uri, fileName)) : void 0;
@@ -815,10 +872,10 @@ var init_DocxViewer = __esm({
815
872
 
816
873
  // src/viewers/useXlsxWorkbook.ts
817
874
  function useXlsxWorkbook(uri) {
818
- const [workbook, setWorkbook] = (0, import_react8.useState)(null);
819
- const [error, setError] = (0, import_react8.useState)(null);
820
- const [loading, setLoading] = (0, import_react8.useState)(true);
821
- (0, import_react8.useEffect)(() => {
875
+ const [workbook, setWorkbook] = (0, import_react9.useState)(null);
876
+ const [error, setError] = (0, import_react9.useState)(null);
877
+ const [loading, setLoading] = (0, import_react9.useState)(true);
878
+ (0, import_react9.useEffect)(() => {
822
879
  let cancelled = false;
823
880
  setLoading(true);
824
881
  setError(null);
@@ -843,11 +900,11 @@ function useXlsxWorkbook(uri) {
843
900
  }, [uri]);
844
901
  return { workbook, error, loading };
845
902
  }
846
- var import_react8, XLSX;
903
+ var import_react9, XLSX;
847
904
  var init_useXlsxWorkbook = __esm({
848
905
  "src/viewers/useXlsxWorkbook.ts"() {
849
906
  "use strict";
850
- import_react8 = require("react");
907
+ import_react9 = require("react");
851
908
  XLSX = __toESM(require("xlsx"), 1);
852
909
  }
853
910
  });
@@ -878,13 +935,15 @@ function XlsxViewer({
878
935
  zoomStep,
879
936
  enableZoomControls = true,
880
937
  enableWheelZoom = true,
938
+ enablePinchZoom = true,
881
939
  enableFullscreen = true,
882
940
  enableDownload = true,
883
941
  onDownload,
884
942
  theme,
885
943
  floating = true,
886
944
  windowDraggable = true,
887
- defaultPosition
945
+ defaultPosition,
946
+ maxRows = 2e3
888
947
  }) {
889
948
  const zoomPan = useZoomPan({
890
949
  minScale,
@@ -892,18 +951,32 @@ function XlsxViewer({
892
951
  zoomStep,
893
952
  enablePan: false,
894
953
  enableDragScroll: true,
895
- enableWheelZoom
954
+ enableWheelZoom,
955
+ enablePinchZoom
896
956
  });
897
957
  const { workbook, error, loading } = useXlsxWorkbook(uri);
898
- const [activeSheet, setActiveSheet] = (0, import_react9.useState)(0);
958
+ const [activeSheet, setActiveSheet] = (0, import_react10.useState)(0);
899
959
  const handleDownload = enableDownload ? onDownload ?? (() => defaultDownload4(uri, fileName)) : void 0;
900
- const tableHtml = (0, import_react9.useMemo)(() => {
901
- if (!workbook) return null;
960
+ const { tableHtml, truncatedRowCount, totalRowCount } = (0, import_react10.useMemo)(() => {
961
+ if (!workbook) return { tableHtml: null, truncatedRowCount: null, totalRowCount: 0 };
902
962
  const sheetName = workbook.SheetNames[activeSheet];
903
963
  const sheet = workbook.Sheets[sheetName];
904
- if (!sheet) return null;
905
- return XLSX2.utils.sheet_to_html(sheet, { editable: false });
906
- }, [workbook, activeSheet]);
964
+ if (!sheet || !sheet["!ref"]) return { tableHtml: null, truncatedRowCount: null, totalRowCount: 0 };
965
+ const range = XLSX2.utils.decode_range(sheet["!ref"]);
966
+ const totalRows = range.e.r - range.s.r + 1;
967
+ let sheetToRender = sheet;
968
+ let truncated = null;
969
+ if (totalRows > maxRows) {
970
+ truncated = maxRows;
971
+ const clampedRange = { s: range.s, e: { r: range.s.r + maxRows - 1, c: range.e.c } };
972
+ sheetToRender = { ...sheet, "!ref": XLSX2.utils.encode_range(clampedRange) };
973
+ }
974
+ return {
975
+ tableHtml: sanitizeUntrustedHtml(XLSX2.utils.sheet_to_html(sheetToRender, { editable: false })),
976
+ truncatedRowCount: truncated,
977
+ totalRowCount: totalRows
978
+ };
979
+ }, [workbook, activeSheet, maxRows]);
907
980
  const wrapperStyle = floating ? { ...style } : { display: "flex", alignItems: "center", justifyContent: "center", width: "100%", height: "100%", ...style };
908
981
  return /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className, style: wrapperStyle, children: /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)(
909
982
  ViewerShell,
@@ -937,19 +1010,27 @@ function XlsxViewer({
937
1010
  },
938
1011
  name
939
1012
  )) }),
1013
+ truncatedRowCount != null && /* @__PURE__ */ (0, import_jsx_runtime8.jsxs)("div", { className: "vd-xlsx-truncated-notice", children: [
1014
+ "Showing the first ",
1015
+ truncatedRowCount.toLocaleString(),
1016
+ " of ",
1017
+ totalRowCount.toLocaleString(),
1018
+ " rows. Download the file to see the rest."
1019
+ ] }),
940
1020
  tableHtml && /* @__PURE__ */ (0, import_jsx_runtime8.jsx)("div", { className: "vd-xlsx-table", dangerouslySetInnerHTML: { __html: tableHtml } })
941
1021
  ] })
942
1022
  ]
943
1023
  }
944
1024
  ) });
945
1025
  }
946
- var import_react9, XLSX2, import_jsx_runtime8;
1026
+ var import_react10, XLSX2, import_jsx_runtime8;
947
1027
  var init_XlsxViewer = __esm({
948
1028
  "src/viewers/XlsxViewer.tsx"() {
949
1029
  "use strict";
950
- import_react9 = require("react");
1030
+ import_react10 = require("react");
951
1031
  XLSX2 = __toESM(require("xlsx"), 1);
952
1032
  init_ViewerShell();
1033
+ init_sanitizeHtml();
953
1034
  init_useZoomPan();
954
1035
  init_useXlsxWorkbook();
955
1036
  import_jsx_runtime8 = require("react/jsx-runtime");
@@ -971,9 +1052,10 @@ __export(src_exports, {
971
1052
  module.exports = __toCommonJS(src_exports);
972
1053
 
973
1054
  // src/DocViewer.tsx
974
- var import_react10 = require("react");
1055
+ var import_react11 = require("react");
975
1056
 
976
1057
  // src/viewers/ImageViewer.tsx
1058
+ var import_react4 = require("react");
977
1059
  init_ViewerShell();
978
1060
  init_useZoomPan();
979
1061
  var import_jsx_runtime4 = require("react/jsx-runtime");
@@ -998,6 +1080,7 @@ function ImageViewer({
998
1080
  zoomStep,
999
1081
  enableZoomControls = true,
1000
1082
  enableWheelZoom = true,
1083
+ enablePinchZoom = true,
1001
1084
  enablePan = true,
1002
1085
  enableFullscreen = true,
1003
1086
  enableDownload = true,
@@ -1007,7 +1090,9 @@ function ImageViewer({
1007
1090
  windowDraggable = true,
1008
1091
  defaultPosition
1009
1092
  }) {
1010
- const zoomPan = useZoomPan({ minScale, maxScale, zoomStep, enablePan, enableWheelZoom });
1093
+ const zoomPan = useZoomPan({ minScale, maxScale, zoomStep, enablePan, enableWheelZoom, enablePinchZoom });
1094
+ const [failed, setFailed] = (0, import_react4.useState)(false);
1095
+ (0, import_react4.useEffect)(() => setFailed(false), [uri]);
1011
1096
  const handleDownload = enableDownload ? onDownload ?? (() => defaultDownload(uri, fileName)) : void 0;
1012
1097
  const wrapperStyle = floating ? { ...style } : { display: "flex", alignItems: "center", justifyContent: "center", width: "100%", height: "100%", ...style };
1013
1098
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className, style: wrapperStyle, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
@@ -1024,7 +1109,20 @@ function ImageViewer({
1024
1109
  floating,
1025
1110
  draggable: windowDraggable,
1026
1111
  defaultPosition,
1027
- children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("img", { className: "vd-image", src: uri, alt: fileName ?? "document image", draggable: false })
1112
+ children: failed ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("span", { className: "vd-doc-status", children: [
1113
+ 'Failed to load image "',
1114
+ fileName ?? uri,
1115
+ '".'
1116
+ ] }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
1117
+ "img",
1118
+ {
1119
+ className: "vd-image",
1120
+ src: uri,
1121
+ alt: fileName ?? "document image",
1122
+ draggable: false,
1123
+ onError: () => setFailed(true)
1124
+ }
1125
+ )
1028
1126
  }
1029
1127
  ) });
1030
1128
  }
@@ -1071,20 +1169,20 @@ function detectFileType(uri, hint) {
1071
1169
 
1072
1170
  // src/DocViewer.tsx
1073
1171
  var import_jsx_runtime9 = require("react/jsx-runtime");
1074
- var PdfViewer2 = (0, import_react10.lazy)(() => Promise.resolve().then(() => (init_PdfViewer(), PdfViewer_exports)).then((m) => ({ default: m.PdfViewer })));
1075
- var DocxViewer2 = (0, import_react10.lazy)(() => Promise.resolve().then(() => (init_DocxViewer(), DocxViewer_exports)).then((m) => ({ default: m.DocxViewer })));
1076
- var XlsxViewer2 = (0, import_react10.lazy)(() => Promise.resolve().then(() => (init_XlsxViewer(), XlsxViewer_exports)).then((m) => ({ default: m.XlsxViewer })));
1172
+ var PdfViewer2 = (0, import_react11.lazy)(() => Promise.resolve().then(() => (init_PdfViewer(), PdfViewer_exports)).then((m) => ({ default: m.PdfViewer })));
1173
+ var DocxViewer2 = (0, import_react11.lazy)(() => Promise.resolve().then(() => (init_DocxViewer(), DocxViewer_exports)).then((m) => ({ default: m.DocxViewer })));
1174
+ var XlsxViewer2 = (0, import_react11.lazy)(() => Promise.resolve().then(() => (init_XlsxViewer(), XlsxViewer_exports)).then((m) => ({ default: m.XlsxViewer })));
1077
1175
  function DocViewer({ uri, fileName, type, fallback, loadingFallback, ...rest }) {
1078
1176
  const detected = detectFileType(uri, type ?? fileName?.split(".").pop());
1079
1177
  switch (detected) {
1080
1178
  case "image":
1081
1179
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(ImageViewer, { uri, fileName, ...rest });
1082
1180
  case "pdf":
1083
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react10.Suspense, { fallback: loadingFallback ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PdfViewer2, { uri, fileName, ...rest }) });
1181
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react11.Suspense, { fallback: loadingFallback ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(PdfViewer2, { uri, fileName, ...rest }) });
1084
1182
  case "docx":
1085
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react10.Suspense, { fallback: loadingFallback ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(DocxViewer2, { uri, fileName, ...rest }) });
1183
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react11.Suspense, { fallback: loadingFallback ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(DocxViewer2, { uri, fileName, ...rest }) });
1086
1184
  case "xlsx":
1087
- return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react10.Suspense, { fallback: loadingFallback ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(XlsxViewer2, { uri, fileName, ...rest }) });
1185
+ return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_react11.Suspense, { fallback: loadingFallback ?? null, children: /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(XlsxViewer2, { uri, fileName, ...rest }) });
1088
1186
  default:
1089
1187
  return /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(import_jsx_runtime9.Fragment, { children: fallback ?? /* @__PURE__ */ (0, import_jsx_runtime9.jsxs)("div", { className: "vd-doc-status", children: [
1090
1188
  'Unable to detect file type for "',