@files-preview-app/preview-file 1.1.4 → 1.1.6

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/angular.js CHANGED
@@ -389,8 +389,39 @@ function createElement(tag, attrs, ...children) {
389
389
  }
390
390
  return el;
391
391
  }
392
+ 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>`;
393
+ 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>`;
394
+ 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>`;
395
+ 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>`;
396
+ 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>`;
397
+ 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>`;
398
+ 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>`;
392
399
  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>`;
393
400
  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>`;
401
+ 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>`;
402
+ 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>`;
403
+ 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>`;
404
+ 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>`;
405
+ 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>`;
406
+ 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>`;
407
+ var ICON_MAP = {
408
+ "zoom-in": ICON_ZOOM_IN,
409
+ "zoom-out": ICON_ZOOM_OUT,
410
+ "fit-page": ICON_FIT_PAGE,
411
+ "fit-slide": ICON_FIT_PAGE,
412
+ "rotate-cw": ICON_ROTATE_CW,
413
+ "rotate-ccw": ICON_ROTATE_CCW,
414
+ "download": ICON_DOWNLOAD,
415
+ "print": ICON_PRINT,
416
+ "thumbnails": ICON_THUMBNAILS,
417
+ "fullscreen": ICON_FULLSCREEN,
418
+ "play": ICON_PLAY,
419
+ "pause": ICON_PAUSE,
420
+ "close": ICON_CLOSE,
421
+ "copy": ICON_COPY,
422
+ "prev": ICON_PAGE_PREV,
423
+ "next": ICON_PAGE_NEXT
424
+ };
394
425
  var ToolbarController = class {
395
426
  el;
396
427
  toolbarEl;
@@ -501,10 +532,11 @@ var ToolbarController = class {
501
532
  type: "button",
502
533
  "data-action-id": id
503
534
  });
504
- if (iconHtml.startsWith("<svg")) {
505
- btn.innerHTML = sanitizeSVG(iconHtml);
535
+ const svg = ICON_MAP[iconHtml] || iconHtml;
536
+ if (svg && svg.startsWith("<svg")) {
537
+ btn.innerHTML = sanitizeSVG(svg);
506
538
  } else {
507
- btn.textContent = iconHtml;
539
+ btn.textContent = svg || title;
508
540
  }
509
541
  btn.addEventListener("click", onClick);
510
542
  return btn;
@@ -585,6 +617,9 @@ var FilePreviewViewer = class {
585
617
  contentEl = null;
586
618
  currentBuffer = null;
587
619
  currentMetadata = null;
620
+ keyHandler = null;
621
+ currentContainer = null;
622
+ currentOptions = {};
588
623
  /**
589
624
  * Register a preview plugin.
590
625
  */
@@ -639,24 +674,47 @@ var FilePreviewViewer = class {
639
674
  emit: (event, payload) => this.eventEmitter.emit(event, payload)
640
675
  });
641
676
  this.activeInstance = instance;
677
+ this.hideLoading();
678
+ this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
642
679
  if (options.showToolbar !== false && this.toolbar) {
643
680
  const actions = matchedPlugin.getToolbarActions(instance);
681
+ const hasFullscreen = actions.some((a) => a.id === "fullscreen");
682
+ if (!hasFullscreen && this.wrapperEl) {
683
+ actions.push({
684
+ id: "fullscreen",
685
+ icon: "fullscreen",
686
+ label: "Toggle Fullscreen",
687
+ type: "button",
688
+ group: "view",
689
+ execute: () => {
690
+ if (!document.fullscreenElement) {
691
+ this.wrapperEl?.requestFullscreen?.();
692
+ } else {
693
+ document.exitFullscreen?.();
694
+ }
695
+ }
696
+ });
697
+ }
644
698
  this.toolbar.update(actions);
645
699
  this.toolbar.show();
646
700
  }
647
701
  if (instance.getThumbnails && this.thumbnailPanel) {
648
- const thumbnails = await Promise.resolve(instance.getThumbnails());
649
- if (thumbnails && thumbnails.length > 0) {
650
- this.thumbnailPanel.update(thumbnails, (index) => {
651
- instance.goToPage?.(index + 1);
652
- });
653
- if (options.showThumbnails) {
654
- this.thumbnailPanel.show();
702
+ Promise.resolve().then(async () => {
703
+ try {
704
+ const thumbnails = await Promise.resolve(instance.getThumbnails());
705
+ if (thumbnails && thumbnails.length > 0 && this.thumbnailPanel) {
706
+ this.thumbnailPanel.update(thumbnails, (index) => {
707
+ instance.goToPage?.(index + 1);
708
+ });
709
+ if (options.showThumbnails) {
710
+ this.thumbnailPanel.show();
711
+ }
712
+ }
713
+ } catch (e) {
714
+ console.warn("[FilePreview] Non-critical thumbnail load error:", e);
655
715
  }
656
- }
716
+ });
657
717
  }
658
- this.hideLoading();
659
- this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
660
718
  return instance;
661
719
  } catch (error) {
662
720
  this.hideLoading();
@@ -678,6 +736,10 @@ var FilePreviewViewer = class {
678
736
  * Destroy the viewer and clean up all resources.
679
737
  */
680
738
  destroy() {
739
+ if (this.keyHandler) {
740
+ window.removeEventListener("keydown", this.keyHandler);
741
+ this.keyHandler = null;
742
+ }
681
743
  this.abort();
682
744
  this.destroyInstance();
683
745
  this.toolbar?.destroy();
@@ -693,6 +755,7 @@ var FilePreviewViewer = class {
693
755
  this.thumbnailPanel = null;
694
756
  this.currentBuffer = null;
695
757
  this.currentMetadata = null;
758
+ this.currentContainer = null;
696
759
  }
697
760
  /**
698
761
  * Get the currently active preview instance.
@@ -722,6 +785,8 @@ var FilePreviewViewer = class {
722
785
  return null;
723
786
  }
724
787
  setupDOM(container, options) {
788
+ this.currentContainer = container;
789
+ this.currentOptions = options;
725
790
  if (this.wrapperEl?.parentNode === container) return;
726
791
  if (this.wrapperEl?.parentNode) {
727
792
  this.wrapperEl.parentNode.removeChild(this.wrapperEl);
@@ -730,6 +795,7 @@ var FilePreviewViewer = class {
730
795
  const toolbarPos = options.toolbarPosition ?? "top";
731
796
  this.wrapperEl = document.createElement("div");
732
797
  this.wrapperEl.className = `fp-viewer ${themeClass} ${options.className ?? ""}`.trim();
798
+ this.wrapperEl.tabIndex = 0;
733
799
  const toolbarEl = document.createElement("div");
734
800
  toolbarEl.className = "fp-toolbar-container";
735
801
  this.contentEl = document.createElement("div");
@@ -751,6 +817,59 @@ var FilePreviewViewer = class {
751
817
  container.appendChild(this.wrapperEl);
752
818
  this.toolbar = new ToolbarController(toolbarEl);
753
819
  this.thumbnailPanel = new ThumbnailPanel(thumbnailEl);
820
+ this.setupKeyboardShortcuts();
821
+ this.setupDragAndDrop(container, options);
822
+ }
823
+ setupKeyboardShortcuts() {
824
+ if (this.keyHandler) return;
825
+ this.keyHandler = (e) => {
826
+ if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement || e.target?.isContentEditable) {
827
+ return;
828
+ }
829
+ if (!this.activeInstance) return;
830
+ if (e.key === "ArrowRight" || e.key === "PageDown") {
831
+ const cur = this.activeInstance.getCurrentPage?.() ?? 1;
832
+ this.activeInstance.goToPage?.(cur + 1);
833
+ } else if (e.key === "ArrowLeft" || e.key === "PageUp") {
834
+ const cur = this.activeInstance.getCurrentPage?.() ?? 1;
835
+ this.activeInstance.goToPage?.(Math.max(1, cur - 1));
836
+ } else if (e.key === "+" || e.key === "=") {
837
+ this.activeInstance.zoomIn?.();
838
+ } else if (e.key === "-" || e.key === "_") {
839
+ this.activeInstance.zoomOut?.();
840
+ } else if (e.key === "0") {
841
+ this.activeInstance.fitToPage?.();
842
+ } else if (e.key === "r" || e.key === "R") {
843
+ this.activeInstance.rotateCW?.();
844
+ } else if (e.key === "f" || e.key === "F") {
845
+ if (!document.fullscreenElement) {
846
+ this.wrapperEl?.requestFullscreen?.();
847
+ } else {
848
+ document.exitFullscreen?.();
849
+ }
850
+ }
851
+ };
852
+ window.addEventListener("keydown", this.keyHandler);
853
+ }
854
+ setupDragAndDrop(container, options) {
855
+ if (!this.wrapperEl) return;
856
+ this.wrapperEl.addEventListener("dragover", (e) => {
857
+ e.preventDefault();
858
+ this.wrapperEl?.classList.add("fp-dragover");
859
+ });
860
+ this.wrapperEl.addEventListener("dragleave", (e) => {
861
+ if (e.relatedTarget === null || !this.wrapperEl?.contains(e.relatedTarget)) {
862
+ this.wrapperEl?.classList.remove("fp-dragover");
863
+ }
864
+ });
865
+ this.wrapperEl.addEventListener("drop", (e) => {
866
+ e.preventDefault();
867
+ this.wrapperEl?.classList.remove("fp-dragover");
868
+ const file = e.dataTransfer?.files?.[0];
869
+ if (file) {
870
+ this.preview(container, file, options);
871
+ }
872
+ });
754
873
  }
755
874
  showLoading() {
756
875
  if (!this.contentEl) return;
@@ -769,10 +888,27 @@ var FilePreviewViewer = class {
769
888
  this.contentEl.innerHTML = "";
770
889
  const errorEl = document.createElement("div");
771
890
  errorEl.className = "fp-error";
891
+ errorEl.style.display = "flex";
892
+ errorEl.style.flexDirection = "column";
893
+ errorEl.style.alignItems = "center";
894
+ errorEl.style.justifyContent = "center";
895
+ errorEl.style.padding = "32px";
896
+ errorEl.style.textAlign = "center";
897
+ const hasDownload = !!(this.currentBuffer && this.currentMetadata);
772
898
  errorEl.innerHTML = `
773
- <div class="fp-error-icon">\u26A0\uFE0F</div>
774
- <div class="fp-error-message">${message}</div>
899
+ <div class="fp-error-icon" style="font-size:36px;margin-bottom:8px;">\u26A0\uFE0F</div>
900
+ <div class="fp-error-message" style="font-size:16px;font-weight:600;margin-bottom:6px;">Cannot preview file</div>
901
+ <div class="fp-error-sub" style="font-size:13px;color:#64748b;max-width:420px;line-height:1.5;margin-bottom:16px;">${message}</div>
902
+ ${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>' : ""}
775
903
  `;
904
+ if (hasDownload && this.currentBuffer) {
905
+ const btn = errorEl.querySelector(".fp-error-download-btn");
906
+ if (btn) {
907
+ btn.addEventListener("click", () => {
908
+ downloadFile(this.currentBuffer, this.currentMetadata?.name || "download", this.currentMetadata?.mimeType);
909
+ });
910
+ }
911
+ }
776
912
  this.contentEl.appendChild(errorEl);
777
913
  }
778
914
  };
@@ -1693,6 +1829,16 @@ var CodePlugin = class {
1693
1829
  instance.zoomIn?.();
1694
1830
  }
1695
1831
  },
1832
+ {
1833
+ id: "copy",
1834
+ icon: "copy",
1835
+ label: "Copy Content",
1836
+ type: "button",
1837
+ group: "actions",
1838
+ execute: () => {
1839
+ instance.copy?.();
1840
+ }
1841
+ },
1696
1842
  {
1697
1843
  id: "download",
1698
1844
  icon: "download",
@@ -1780,6 +1926,9 @@ var CodePlugin = class {
1780
1926
  },
1781
1927
  print: () => {
1782
1928
  window.print();
1929
+ },
1930
+ copy: () => {
1931
+ navigator.clipboard?.writeText(text);
1783
1932
  }
1784
1933
  };
1785
1934
  }
@@ -2030,6 +2179,14 @@ var MarkdownPlugin = class {
2030
2179
  group: "zoom",
2031
2180
  execute: () => instance.fitToPage?.()
2032
2181
  },
2182
+ {
2183
+ id: "copy",
2184
+ icon: "copy",
2185
+ label: "Copy Markdown",
2186
+ type: "button",
2187
+ group: "actions",
2188
+ execute: () => instance.copy?.()
2189
+ },
2033
2190
  {
2034
2191
  id: "download",
2035
2192
  icon: "download",
@@ -2180,6 +2337,9 @@ var MarkdownPlugin = class {
2180
2337
  },
2181
2338
  print: () => {
2182
2339
  window.print();
2340
+ },
2341
+ copy: () => {
2342
+ navigator.clipboard?.writeText(text);
2183
2343
  }
2184
2344
  };
2185
2345
  }