@files-preview-app/preview-file 1.1.4 → 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.
package/dist/vue.js CHANGED
@@ -341,8 +341,39 @@ function createElement(tag, attrs, ...children) {
341
341
  }
342
342
  return el;
343
343
  }
344
+ 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>`;
345
+ 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>`;
346
+ 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>`;
347
+ 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>`;
348
+ 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>`;
349
+ 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>`;
350
+ 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>`;
344
351
  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>`;
345
352
  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>`;
353
+ 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>`;
354
+ 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>`;
355
+ 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>`;
356
+ 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>`;
357
+ 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>`;
358
+ 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>`;
359
+ var ICON_MAP = {
360
+ "zoom-in": ICON_ZOOM_IN,
361
+ "zoom-out": ICON_ZOOM_OUT,
362
+ "fit-page": ICON_FIT_PAGE,
363
+ "fit-slide": ICON_FIT_PAGE,
364
+ "rotate-cw": ICON_ROTATE_CW,
365
+ "rotate-ccw": ICON_ROTATE_CCW,
366
+ "download": ICON_DOWNLOAD,
367
+ "print": ICON_PRINT,
368
+ "thumbnails": ICON_THUMBNAILS,
369
+ "fullscreen": ICON_FULLSCREEN,
370
+ "play": ICON_PLAY,
371
+ "pause": ICON_PAUSE,
372
+ "close": ICON_CLOSE,
373
+ "copy": ICON_COPY,
374
+ "prev": ICON_PAGE_PREV,
375
+ "next": ICON_PAGE_NEXT
376
+ };
346
377
  var ToolbarController = class {
347
378
  el;
348
379
  toolbarEl;
@@ -453,10 +484,11 @@ var ToolbarController = class {
453
484
  type: "button",
454
485
  "data-action-id": id
455
486
  });
456
- if (iconHtml.startsWith("<svg")) {
457
- btn.innerHTML = sanitizeSVG(iconHtml);
487
+ const svg = ICON_MAP[iconHtml] || iconHtml;
488
+ if (svg && svg.startsWith("<svg")) {
489
+ btn.innerHTML = sanitizeSVG(svg);
458
490
  } else {
459
- btn.textContent = iconHtml;
491
+ btn.textContent = svg || title;
460
492
  }
461
493
  btn.addEventListener("click", onClick);
462
494
  return btn;
@@ -537,6 +569,9 @@ var FilePreviewViewer = class {
537
569
  contentEl = null;
538
570
  currentBuffer = null;
539
571
  currentMetadata = null;
572
+ keyHandler = null;
573
+ currentContainer = null;
574
+ currentOptions = {};
540
575
  /**
541
576
  * Register a preview plugin.
542
577
  */
@@ -591,24 +626,47 @@ var FilePreviewViewer = class {
591
626
  emit: (event, payload) => this.eventEmitter.emit(event, payload)
592
627
  });
593
628
  this.activeInstance = instance;
629
+ this.hideLoading();
630
+ this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
594
631
  if (options.showToolbar !== false && this.toolbar) {
595
632
  const actions = matchedPlugin.getToolbarActions(instance);
633
+ const hasFullscreen = actions.some((a) => a.id === "fullscreen");
634
+ if (!hasFullscreen && this.wrapperEl) {
635
+ actions.push({
636
+ id: "fullscreen",
637
+ icon: "fullscreen",
638
+ label: "Toggle Fullscreen",
639
+ type: "button",
640
+ group: "view",
641
+ execute: () => {
642
+ if (!document.fullscreenElement) {
643
+ this.wrapperEl?.requestFullscreen?.();
644
+ } else {
645
+ document.exitFullscreen?.();
646
+ }
647
+ }
648
+ });
649
+ }
596
650
  this.toolbar.update(actions);
597
651
  this.toolbar.show();
598
652
  }
599
653
  if (instance.getThumbnails && this.thumbnailPanel) {
600
- const thumbnails = await Promise.resolve(instance.getThumbnails());
601
- if (thumbnails && thumbnails.length > 0) {
602
- this.thumbnailPanel.update(thumbnails, (index) => {
603
- instance.goToPage?.(index + 1);
604
- });
605
- if (options.showThumbnails) {
606
- this.thumbnailPanel.show();
654
+ Promise.resolve().then(async () => {
655
+ try {
656
+ const thumbnails = await Promise.resolve(instance.getThumbnails());
657
+ if (thumbnails && thumbnails.length > 0 && this.thumbnailPanel) {
658
+ this.thumbnailPanel.update(thumbnails, (index) => {
659
+ instance.goToPage?.(index + 1);
660
+ });
661
+ if (options.showThumbnails) {
662
+ this.thumbnailPanel.show();
663
+ }
664
+ }
665
+ } catch (e) {
666
+ console.warn("[FilePreview] Non-critical thumbnail load error:", e);
607
667
  }
608
- }
668
+ });
609
669
  }
610
- this.hideLoading();
611
- this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
612
670
  return instance;
613
671
  } catch (error) {
614
672
  this.hideLoading();
@@ -630,6 +688,10 @@ var FilePreviewViewer = class {
630
688
  * Destroy the viewer and clean up all resources.
631
689
  */
632
690
  destroy() {
691
+ if (this.keyHandler) {
692
+ window.removeEventListener("keydown", this.keyHandler);
693
+ this.keyHandler = null;
694
+ }
633
695
  this.abort();
634
696
  this.destroyInstance();
635
697
  this.toolbar?.destroy();
@@ -645,6 +707,7 @@ var FilePreviewViewer = class {
645
707
  this.thumbnailPanel = null;
646
708
  this.currentBuffer = null;
647
709
  this.currentMetadata = null;
710
+ this.currentContainer = null;
648
711
  }
649
712
  /**
650
713
  * Get the currently active preview instance.
@@ -674,6 +737,8 @@ var FilePreviewViewer = class {
674
737
  return null;
675
738
  }
676
739
  setupDOM(container, options) {
740
+ this.currentContainer = container;
741
+ this.currentOptions = options;
677
742
  if (this.wrapperEl?.parentNode === container) return;
678
743
  if (this.wrapperEl?.parentNode) {
679
744
  this.wrapperEl.parentNode.removeChild(this.wrapperEl);
@@ -682,6 +747,7 @@ var FilePreviewViewer = class {
682
747
  const toolbarPos = options.toolbarPosition ?? "top";
683
748
  this.wrapperEl = document.createElement("div");
684
749
  this.wrapperEl.className = `fp-viewer ${themeClass} ${options.className ?? ""}`.trim();
750
+ this.wrapperEl.tabIndex = 0;
685
751
  const toolbarEl = document.createElement("div");
686
752
  toolbarEl.className = "fp-toolbar-container";
687
753
  this.contentEl = document.createElement("div");
@@ -703,6 +769,59 @@ var FilePreviewViewer = class {
703
769
  container.appendChild(this.wrapperEl);
704
770
  this.toolbar = new ToolbarController(toolbarEl);
705
771
  this.thumbnailPanel = new ThumbnailPanel(thumbnailEl);
772
+ this.setupKeyboardShortcuts();
773
+ this.setupDragAndDrop(container, options);
774
+ }
775
+ setupKeyboardShortcuts() {
776
+ if (this.keyHandler) return;
777
+ this.keyHandler = (e) => {
778
+ if (e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement || e.target?.isContentEditable) {
779
+ return;
780
+ }
781
+ if (!this.activeInstance) return;
782
+ if (e.key === "ArrowRight" || e.key === "PageDown") {
783
+ const cur = this.activeInstance.getCurrentPage?.() ?? 1;
784
+ this.activeInstance.goToPage?.(cur + 1);
785
+ } else if (e.key === "ArrowLeft" || e.key === "PageUp") {
786
+ const cur = this.activeInstance.getCurrentPage?.() ?? 1;
787
+ this.activeInstance.goToPage?.(Math.max(1, cur - 1));
788
+ } else if (e.key === "+" || e.key === "=") {
789
+ this.activeInstance.zoomIn?.();
790
+ } else if (e.key === "-" || e.key === "_") {
791
+ this.activeInstance.zoomOut?.();
792
+ } else if (e.key === "0") {
793
+ this.activeInstance.fitToPage?.();
794
+ } else if (e.key === "r" || e.key === "R") {
795
+ this.activeInstance.rotateCW?.();
796
+ } else if (e.key === "f" || e.key === "F") {
797
+ if (!document.fullscreenElement) {
798
+ this.wrapperEl?.requestFullscreen?.();
799
+ } else {
800
+ document.exitFullscreen?.();
801
+ }
802
+ }
803
+ };
804
+ window.addEventListener("keydown", this.keyHandler);
805
+ }
806
+ setupDragAndDrop(container, options) {
807
+ if (!this.wrapperEl) return;
808
+ this.wrapperEl.addEventListener("dragover", (e) => {
809
+ e.preventDefault();
810
+ this.wrapperEl?.classList.add("fp-dragover");
811
+ });
812
+ this.wrapperEl.addEventListener("dragleave", (e) => {
813
+ if (e.relatedTarget === null || !this.wrapperEl?.contains(e.relatedTarget)) {
814
+ this.wrapperEl?.classList.remove("fp-dragover");
815
+ }
816
+ });
817
+ this.wrapperEl.addEventListener("drop", (e) => {
818
+ e.preventDefault();
819
+ this.wrapperEl?.classList.remove("fp-dragover");
820
+ const file = e.dataTransfer?.files?.[0];
821
+ if (file) {
822
+ this.preview(container, file, options);
823
+ }
824
+ });
706
825
  }
707
826
  showLoading() {
708
827
  if (!this.contentEl) return;
@@ -721,10 +840,27 @@ var FilePreviewViewer = class {
721
840
  this.contentEl.innerHTML = "";
722
841
  const errorEl = document.createElement("div");
723
842
  errorEl.className = "fp-error";
843
+ errorEl.style.display = "flex";
844
+ errorEl.style.flexDirection = "column";
845
+ errorEl.style.alignItems = "center";
846
+ errorEl.style.justifyContent = "center";
847
+ errorEl.style.padding = "32px";
848
+ errorEl.style.textAlign = "center";
849
+ const hasDownload = !!(this.currentBuffer && this.currentMetadata);
724
850
  errorEl.innerHTML = `
725
- <div class="fp-error-icon">\u26A0\uFE0F</div>
726
- <div class="fp-error-message">${message}</div>
851
+ <div class="fp-error-icon" style="font-size:36px;margin-bottom:8px;">\u26A0\uFE0F</div>
852
+ <div class="fp-error-message" style="font-size:16px;font-weight:600;margin-bottom:6px;">Cannot preview file</div>
853
+ <div class="fp-error-sub" style="font-size:13px;color:#64748b;max-width:420px;line-height:1.5;margin-bottom:16px;">${message}</div>
854
+ ${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>' : ""}
727
855
  `;
856
+ if (hasDownload && this.currentBuffer) {
857
+ const btn = errorEl.querySelector(".fp-error-download-btn");
858
+ if (btn) {
859
+ btn.addEventListener("click", () => {
860
+ downloadFile(this.currentBuffer, this.currentMetadata?.name || "download", this.currentMetadata?.mimeType);
861
+ });
862
+ }
863
+ }
728
864
  this.contentEl.appendChild(errorEl);
729
865
  }
730
866
  };
@@ -1645,6 +1781,16 @@ var CodePlugin = class {
1645
1781
  instance.zoomIn?.();
1646
1782
  }
1647
1783
  },
1784
+ {
1785
+ id: "copy",
1786
+ icon: "copy",
1787
+ label: "Copy Content",
1788
+ type: "button",
1789
+ group: "actions",
1790
+ execute: () => {
1791
+ instance.copy?.();
1792
+ }
1793
+ },
1648
1794
  {
1649
1795
  id: "download",
1650
1796
  icon: "download",
@@ -1732,6 +1878,9 @@ var CodePlugin = class {
1732
1878
  },
1733
1879
  print: () => {
1734
1880
  window.print();
1881
+ },
1882
+ copy: () => {
1883
+ navigator.clipboard?.writeText(text);
1735
1884
  }
1736
1885
  };
1737
1886
  }
@@ -1982,6 +2131,14 @@ var MarkdownPlugin = class {
1982
2131
  group: "zoom",
1983
2132
  execute: () => instance.fitToPage?.()
1984
2133
  },
2134
+ {
2135
+ id: "copy",
2136
+ icon: "copy",
2137
+ label: "Copy Markdown",
2138
+ type: "button",
2139
+ group: "actions",
2140
+ execute: () => instance.copy?.()
2141
+ },
1985
2142
  {
1986
2143
  id: "download",
1987
2144
  icon: "download",
@@ -2132,6 +2289,9 @@ var MarkdownPlugin = class {
2132
2289
  },
2133
2290
  print: () => {
2134
2291
  window.print();
2292
+ },
2293
+ copy: () => {
2294
+ navigator.clipboard?.writeText(text);
2135
2295
  }
2136
2296
  };
2137
2297
  }