@files-preview-app/preview-file 1.1.3 → 1.1.5

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.
@@ -1,19 +1,19 @@
1
- # Third-Party Licenses
2
-
3
- This project uses the following open-source libraries. All dependencies use strictly permissive licenses (MIT, Apache-2.0, or BSD) that allow free commercial and personal use with zero copyright claims or restrictions.
4
-
5
- | Package | Version | License | Author/Org | URL |
6
- |---|---|---|---|---|
7
- | pdfjs-dist | ^4.x | Apache-2.0 | Mozilla | https://github.com/nicolo-ribaudo/pdfjs-dist |
8
- | docx-preview | ^0.3.x | Apache-2.0 | Volodymyr Baydalka | https://github.com/VolodymyrBaydalka/docxjs |
9
- | exceljs | ^4.x | MIT | Guyon Roche | https://github.com/exceljs/exceljs |
10
- | pptx-browser | ^4.x | MIT | Christophervr | https://github.com/christophervr/pptx-browser |
11
- | papaparse | ^5.x | MIT | Matt Holt | https://github.com/mholt/PapaParse |
12
- | fflate | ^0.8.x | MIT | Arjun Barrett | https://github.com/101arrowz/fflate |
13
- | marked | ^18.x | MIT | Christopher Jeffrey | https://github.com/markedjs/marked |
14
- | three | ^0.186.x | MIT | Ricardo Cabello (Mr.doob) | https://github.com/mrdoob/three.js |
15
- | highlight.js | ^11.x | BSD-3-Clause | Ivan Sagalaev | https://github.com/highlightjs/highlight.js |
16
- | @panzoom/panzoom | ^4.x | MIT | Timmy Willison | https://github.com/timmywil/panzoom |
17
- | dompurify | ^3.x | Apache-2.0 | Cure53 | https://github.com/cure53/DOMPurify |
18
-
1
+ # Third-Party Licenses
2
+
3
+ This project uses the following open-source libraries. All dependencies use strictly permissive licenses (MIT, Apache-2.0, or BSD) that allow free commercial and personal use with zero copyright claims or restrictions.
4
+
5
+ | Package | Version | License | Author/Org | URL |
6
+ |---|---|---|---|---|
7
+ | pdfjs-dist | ^4.x | Apache-2.0 | Mozilla | https://github.com/nicolo-ribaudo/pdfjs-dist |
8
+ | docx-preview | ^0.3.x | Apache-2.0 | Volodymyr Baydalka | https://github.com/VolodymyrBaydalka/docxjs |
9
+ | exceljs | ^4.x | MIT | Guyon Roche | https://github.com/exceljs/exceljs |
10
+ | pptx-browser | ^4.x | MIT | Christophervr | https://github.com/christophervr/pptx-browser |
11
+ | papaparse | ^5.x | MIT | Matt Holt | https://github.com/mholt/PapaParse |
12
+ | fflate | ^0.8.x | MIT | Arjun Barrett | https://github.com/101arrowz/fflate |
13
+ | marked | ^18.x | MIT | Christopher Jeffrey | https://github.com/markedjs/marked |
14
+ | three | ^0.186.x | MIT | Ricardo Cabello (Mr.doob) | https://github.com/mrdoob/three.js |
15
+ | highlight.js | ^11.x | BSD-3-Clause | Ivan Sagalaev | https://github.com/highlightjs/highlight.js |
16
+ | @panzoom/panzoom | ^4.x | MIT | Timmy Willison | https://github.com/timmywil/panzoom |
17
+ | dompurify | ^3.x | Apache-2.0 | Cure53 | https://github.com/cure53/DOMPurify |
18
+
19
19
  All licenses are included in their respective `node_modules/<package>/LICENSE` files.
package/dist/angular.cjs CHANGED
@@ -419,8 +419,39 @@ function createElement(tag, attrs, ...children) {
419
419
  }
420
420
  return el;
421
421
  }
422
+ var ICON_ZOOM_IN = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="11" y1="8" x2="11" y2="14"></line><line x1="8" y1="11" x2="14" y2="11"></line></svg>`;
423
+ var ICON_ZOOM_OUT = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line><line x1="8" y1="11" x2="14" y2="11"></line></svg>`;
424
+ var ICON_FIT_PAGE = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" y1="3" x2="14" y2="10"></line><line x1="3" y1="21" x2="10" y2="14"></line></svg>`;
425
+ var ICON_ROTATE_CW = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"></polyline><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path></svg>`;
426
+ var ICON_ROTATE_CCW = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="1 4 1 10 7 10"></polyline><path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"></path></svg>`;
427
+ var ICON_DOWNLOAD = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"></path><polyline points="7 10 12 15 17 10"></polyline><line x1="12" y1="15" x2="12" y2="3"></line></svg>`;
428
+ var ICON_PRINT = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="6 9 6 2 18 2 18 9"></polyline><path d="M6 18H4a2 2 0 0 1-2-2v-5a2 2 0 0 1 2-2h16a2 2 0 0 1 2 2v5a2 2 0 0 1-2 2h-2"></path><rect x="6" y="14" width="12" height="8"></rect></svg>`;
422
429
  var ICON_PAGE_PREV = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="15 18 9 12 15 6"></polyline></svg>`;
423
430
  var ICON_PAGE_NEXT = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="9 18 15 12 9 6"></polyline></svg>`;
431
+ var ICON_PLAY = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="5 3 19 12 5 21 5 3"></polygon></svg>`;
432
+ var ICON_PAUSE = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="6" y="4" width="4" height="16"></rect><rect x="14" y="4" width="4" height="16"></rect></svg>`;
433
+ var ICON_THUMBNAILS = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="7" height="7"></rect><rect x="14" y="3" width="7" height="7"></rect><rect x="14" y="14" width="7" height="7"></rect><rect x="3" y="14" width="7" height="7"></rect></svg>`;
434
+ var ICON_CLOSE = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"></line><line x1="6" y1="6" x2="18" y2="18"></line></svg>`;
435
+ var ICON_FULLSCREEN = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"></path></svg>`;
436
+ var ICON_COPY = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>`;
437
+ var ICON_MAP = {
438
+ "zoom-in": ICON_ZOOM_IN,
439
+ "zoom-out": ICON_ZOOM_OUT,
440
+ "fit-page": ICON_FIT_PAGE,
441
+ "fit-slide": ICON_FIT_PAGE,
442
+ "rotate-cw": ICON_ROTATE_CW,
443
+ "rotate-ccw": ICON_ROTATE_CCW,
444
+ "download": ICON_DOWNLOAD,
445
+ "print": ICON_PRINT,
446
+ "thumbnails": ICON_THUMBNAILS,
447
+ "fullscreen": ICON_FULLSCREEN,
448
+ "play": ICON_PLAY,
449
+ "pause": ICON_PAUSE,
450
+ "close": ICON_CLOSE,
451
+ "copy": ICON_COPY,
452
+ "prev": ICON_PAGE_PREV,
453
+ "next": ICON_PAGE_NEXT
454
+ };
424
455
  var ToolbarController = class {
425
456
  el;
426
457
  toolbarEl;
@@ -531,10 +562,11 @@ var ToolbarController = class {
531
562
  type: "button",
532
563
  "data-action-id": id
533
564
  });
534
- if (iconHtml.startsWith("<svg")) {
535
- btn.innerHTML = sanitizeSVG(iconHtml);
565
+ const svg = ICON_MAP[iconHtml] || iconHtml;
566
+ if (svg && svg.startsWith("<svg")) {
567
+ btn.innerHTML = sanitizeSVG(svg);
536
568
  } else {
537
- btn.textContent = iconHtml;
569
+ btn.textContent = svg || title;
538
570
  }
539
571
  btn.addEventListener("click", onClick);
540
572
  return btn;
@@ -615,6 +647,9 @@ var FilePreviewViewer = class {
615
647
  contentEl = null;
616
648
  currentBuffer = null;
617
649
  currentMetadata = null;
650
+ keyHandler = null;
651
+ currentContainer = null;
652
+ currentOptions = {};
618
653
  /**
619
654
  * Register a preview plugin.
620
655
  */
@@ -669,24 +704,47 @@ var FilePreviewViewer = class {
669
704
  emit: (event, payload) => this.eventEmitter.emit(event, payload)
670
705
  });
671
706
  this.activeInstance = instance;
707
+ this.hideLoading();
708
+ this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
672
709
  if (options.showToolbar !== false && this.toolbar) {
673
710
  const actions = matchedPlugin.getToolbarActions(instance);
711
+ const hasFullscreen = actions.some((a) => a.id === "fullscreen");
712
+ if (!hasFullscreen && this.wrapperEl) {
713
+ actions.push({
714
+ id: "fullscreen",
715
+ icon: "fullscreen",
716
+ label: "Toggle Fullscreen",
717
+ type: "button",
718
+ group: "view",
719
+ execute: () => {
720
+ if (!document.fullscreenElement) {
721
+ this.wrapperEl?.requestFullscreen?.();
722
+ } else {
723
+ document.exitFullscreen?.();
724
+ }
725
+ }
726
+ });
727
+ }
674
728
  this.toolbar.update(actions);
675
729
  this.toolbar.show();
676
730
  }
677
731
  if (instance.getThumbnails && this.thumbnailPanel) {
678
- const thumbnails = await Promise.resolve(instance.getThumbnails());
679
- if (thumbnails && thumbnails.length > 0) {
680
- this.thumbnailPanel.update(thumbnails, (index) => {
681
- instance.goToPage?.(index + 1);
682
- });
683
- if (options.showThumbnails) {
684
- this.thumbnailPanel.show();
732
+ Promise.resolve().then(async () => {
733
+ try {
734
+ const thumbnails = await Promise.resolve(instance.getThumbnails());
735
+ if (thumbnails && thumbnails.length > 0 && this.thumbnailPanel) {
736
+ this.thumbnailPanel.update(thumbnails, (index) => {
737
+ instance.goToPage?.(index + 1);
738
+ });
739
+ if (options.showThumbnails) {
740
+ this.thumbnailPanel.show();
741
+ }
742
+ }
743
+ } catch (e) {
744
+ console.warn("[FilePreview] Non-critical thumbnail load error:", e);
685
745
  }
686
- }
746
+ });
687
747
  }
688
- this.hideLoading();
689
- this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
690
748
  return instance;
691
749
  } catch (error) {
692
750
  this.hideLoading();
@@ -708,6 +766,10 @@ var FilePreviewViewer = class {
708
766
  * Destroy the viewer and clean up all resources.
709
767
  */
710
768
  destroy() {
769
+ if (this.keyHandler) {
770
+ window.removeEventListener("keydown", this.keyHandler);
771
+ this.keyHandler = null;
772
+ }
711
773
  this.abort();
712
774
  this.destroyInstance();
713
775
  this.toolbar?.destroy();
@@ -723,6 +785,7 @@ var FilePreviewViewer = class {
723
785
  this.thumbnailPanel = null;
724
786
  this.currentBuffer = null;
725
787
  this.currentMetadata = null;
788
+ this.currentContainer = null;
726
789
  }
727
790
  /**
728
791
  * Get the currently active preview instance.
@@ -752,6 +815,8 @@ var FilePreviewViewer = class {
752
815
  return null;
753
816
  }
754
817
  setupDOM(container, options) {
818
+ this.currentContainer = container;
819
+ this.currentOptions = options;
755
820
  if (this.wrapperEl?.parentNode === container) return;
756
821
  if (this.wrapperEl?.parentNode) {
757
822
  this.wrapperEl.parentNode.removeChild(this.wrapperEl);
@@ -760,6 +825,7 @@ var FilePreviewViewer = class {
760
825
  const toolbarPos = options.toolbarPosition ?? "top";
761
826
  this.wrapperEl = document.createElement("div");
762
827
  this.wrapperEl.className = `fp-viewer ${themeClass} ${options.className ?? ""}`.trim();
828
+ this.wrapperEl.tabIndex = 0;
763
829
  const toolbarEl = document.createElement("div");
764
830
  toolbarEl.className = "fp-toolbar-container";
765
831
  this.contentEl = document.createElement("div");
@@ -781,6 +847,59 @@ var FilePreviewViewer = class {
781
847
  container.appendChild(this.wrapperEl);
782
848
  this.toolbar = new ToolbarController(toolbarEl);
783
849
  this.thumbnailPanel = new ThumbnailPanel(thumbnailEl);
850
+ this.setupKeyboardShortcuts();
851
+ this.setupDragAndDrop(container, options);
852
+ }
853
+ setupKeyboardShortcuts() {
854
+ if (this.keyHandler) return;
855
+ this.keyHandler = (e) => {
856
+ if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement || e.target?.isContentEditable) {
857
+ return;
858
+ }
859
+ if (!this.activeInstance) return;
860
+ if (e.key === "ArrowRight" || e.key === "PageDown") {
861
+ const cur = this.activeInstance.getCurrentPage?.() ?? 1;
862
+ this.activeInstance.goToPage?.(cur + 1);
863
+ } else if (e.key === "ArrowLeft" || e.key === "PageUp") {
864
+ const cur = this.activeInstance.getCurrentPage?.() ?? 1;
865
+ this.activeInstance.goToPage?.(Math.max(1, cur - 1));
866
+ } else if (e.key === "+" || e.key === "=") {
867
+ this.activeInstance.zoomIn?.();
868
+ } else if (e.key === "-" || e.key === "_") {
869
+ this.activeInstance.zoomOut?.();
870
+ } else if (e.key === "0") {
871
+ this.activeInstance.fitToPage?.();
872
+ } else if (e.key === "r" || e.key === "R") {
873
+ this.activeInstance.rotateCW?.();
874
+ } else if (e.key === "f" || e.key === "F") {
875
+ if (!document.fullscreenElement) {
876
+ this.wrapperEl?.requestFullscreen?.();
877
+ } else {
878
+ document.exitFullscreen?.();
879
+ }
880
+ }
881
+ };
882
+ window.addEventListener("keydown", this.keyHandler);
883
+ }
884
+ setupDragAndDrop(container, options) {
885
+ if (!this.wrapperEl) return;
886
+ this.wrapperEl.addEventListener("dragover", (e) => {
887
+ e.preventDefault();
888
+ this.wrapperEl?.classList.add("fp-dragover");
889
+ });
890
+ this.wrapperEl.addEventListener("dragleave", (e) => {
891
+ if (e.relatedTarget === null || !this.wrapperEl?.contains(e.relatedTarget)) {
892
+ this.wrapperEl?.classList.remove("fp-dragover");
893
+ }
894
+ });
895
+ this.wrapperEl.addEventListener("drop", (e) => {
896
+ e.preventDefault();
897
+ this.wrapperEl?.classList.remove("fp-dragover");
898
+ const file = e.dataTransfer?.files?.[0];
899
+ if (file) {
900
+ this.preview(container, file, options);
901
+ }
902
+ });
784
903
  }
785
904
  showLoading() {
786
905
  if (!this.contentEl) return;
@@ -799,10 +918,27 @@ var FilePreviewViewer = class {
799
918
  this.contentEl.innerHTML = "";
800
919
  const errorEl = document.createElement("div");
801
920
  errorEl.className = "fp-error";
921
+ errorEl.style.display = "flex";
922
+ errorEl.style.flexDirection = "column";
923
+ errorEl.style.alignItems = "center";
924
+ errorEl.style.justifyContent = "center";
925
+ errorEl.style.padding = "32px";
926
+ errorEl.style.textAlign = "center";
927
+ const hasDownload = !!(this.currentBuffer && this.currentMetadata);
802
928
  errorEl.innerHTML = `
803
- <div class="fp-error-icon">\u26A0\uFE0F</div>
804
- <div class="fp-error-message">${message}</div>
929
+ <div class="fp-error-icon" style="font-size:36px;margin-bottom:8px;">\u26A0\uFE0F</div>
930
+ <div class="fp-error-message" style="font-size:16px;font-weight:600;margin-bottom:6px;">Cannot preview file</div>
931
+ <div class="fp-error-sub" style="font-size:13px;color:#64748b;max-width:420px;line-height:1.5;margin-bottom:16px;">${message}</div>
932
+ ${hasDownload ? '<button class="fp-error-download-btn" style="padding:8px 16px;background:#3b82f6;color:#ffffff;border:none;border-radius:6px;cursor:pointer;font-weight:500;font-size:13px;transition:background 0.2s;">Download Original File</button>' : ""}
805
933
  `;
934
+ if (hasDownload && this.currentBuffer) {
935
+ const btn = errorEl.querySelector(".fp-error-download-btn");
936
+ if (btn) {
937
+ btn.addEventListener("click", () => {
938
+ downloadFile(this.currentBuffer, this.currentMetadata?.name || "download", this.currentMetadata?.mimeType);
939
+ });
940
+ }
941
+ }
806
942
  this.contentEl.appendChild(errorEl);
807
943
  }
808
944
  };
@@ -1723,6 +1859,16 @@ var CodePlugin = class {
1723
1859
  instance.zoomIn?.();
1724
1860
  }
1725
1861
  },
1862
+ {
1863
+ id: "copy",
1864
+ icon: "copy",
1865
+ label: "Copy Content",
1866
+ type: "button",
1867
+ group: "actions",
1868
+ execute: () => {
1869
+ instance.copy?.();
1870
+ }
1871
+ },
1726
1872
  {
1727
1873
  id: "download",
1728
1874
  icon: "download",
@@ -1810,6 +1956,9 @@ var CodePlugin = class {
1810
1956
  },
1811
1957
  print: () => {
1812
1958
  window.print();
1959
+ },
1960
+ copy: () => {
1961
+ navigator.clipboard?.writeText(text);
1813
1962
  }
1814
1963
  };
1815
1964
  }
@@ -2060,6 +2209,14 @@ var MarkdownPlugin = class {
2060
2209
  group: "zoom",
2061
2210
  execute: () => instance.fitToPage?.()
2062
2211
  },
2212
+ {
2213
+ id: "copy",
2214
+ icon: "copy",
2215
+ label: "Copy Markdown",
2216
+ type: "button",
2217
+ group: "actions",
2218
+ execute: () => instance.copy?.()
2219
+ },
2063
2220
  {
2064
2221
  id: "download",
2065
2222
  icon: "download",
@@ -2210,6 +2367,9 @@ var MarkdownPlugin = class {
2210
2367
  },
2211
2368
  print: () => {
2212
2369
  window.print();
2370
+ },
2371
+ copy: () => {
2372
+ navigator.clipboard?.writeText(text);
2213
2373
  }
2214
2374
  };
2215
2375
  }