@files-preview-app/preview-file 1.3.1 → 1.3.3

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
@@ -542,8 +542,7 @@ var ICON_FULLSCREEN = `<svg width="24" height="24" viewBox="0 0 24 24" fill="non
542
542
  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>`;
543
543
  var ICON_FAST_FORWARD = `<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="13 19 22 12 13 5 13 19"></polygon><polygon points="2 19 11 12 2 5 2 19"></polygon></svg>`;
544
544
  var ICON_REWIND = `<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="11 19 2 12 11 5 11 19"></polygon><polygon points="22 19 13 12 22 5 22 19"></polygon></svg>`;
545
- var ICON_SPEED = `<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="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>`;
546
- var ICON_EXTERNAL_WINDOW = `<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="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>`;
545
+ var ICON_EXTERNAL_WINDOW = `<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="18" height="18" rx="2"></rect><line x1="3" y1="8" x2="21" y2="8"></line><circle cx="6" cy="5.5" r="0.75" fill="currentColor"></circle><circle cx="8.5" cy="5.5" r="0.75" fill="currentColor"></circle><polyline points="14 13 18 13 18 17"></polyline><line x1="11" y1="20" x2="18" y2="13"></line></svg>`;
547
546
  var ICON_MAP = {
548
547
  "zoom-in": ICON_ZOOM_IN,
549
548
  "zoom-out": ICON_ZOOM_OUT,
@@ -570,28 +569,177 @@ var ICON_MAP = {
570
569
  "forward-10": ICON_FAST_FORWARD,
571
570
  "rewind": ICON_REWIND,
572
571
  "replay-10": ICON_REWIND,
573
- "speed": ICON_SPEED,
574
572
  "open-window": ICON_EXTERNAL_WINDOW,
575
- "external-window": ICON_EXTERNAL_WINDOW
573
+ "external-window": ICON_EXTERNAL_WINDOW,
574
+ "openWindow": ICON_EXTERNAL_WINDOW,
575
+ "openSeparateWindow": ICON_EXTERNAL_WINDOW
576
576
  };
577
577
  var ToolbarController = class {
578
578
  el;
579
579
  toolbarEl;
580
580
  actions = [];
581
+ config = {};
581
582
  pageInputEl = null;
582
583
  pageLabelEl = null;
583
- constructor(container) {
584
+ prevPageBtn = null;
585
+ nextPageBtn = null;
586
+ constructor(container, config) {
584
587
  this.el = container;
588
+ if (config) this.config = { ...config };
585
589
  this.toolbarEl = createElement("div", { className: "fp-toolbar" });
586
590
  this.el.appendChild(this.toolbarEl);
587
591
  }
592
+ setConfig(config) {
593
+ this.config = { ...config };
594
+ this.render();
595
+ }
596
+ getConfig() {
597
+ return { ...this.config };
598
+ }
599
+ hideAction(actionId) {
600
+ this.config[actionId] = false;
601
+ if (actionId === "zoom-in" || actionId === "zoomIn") this.config.zoomIn = false;
602
+ if (actionId === "zoom-out" || actionId === "zoomOut") this.config.zoomOut = false;
603
+ if (actionId === "fit-page" || actionId === "fitPage" || actionId === "fitToPage" || actionId === "fit-width") {
604
+ this.config.fitPage = false;
605
+ this.config.fitToPage = false;
606
+ this.config.fitWidth = false;
607
+ }
608
+ if (actionId === "rotate-cw" || actionId === "rotate" || actionId === "rotateCW") this.config.rotate = false;
609
+ if (actionId === "fullscreen") this.config.fullscreen = false;
610
+ if (actionId === "download") this.config.download = false;
611
+ if (actionId === "print") this.config.print = false;
612
+ if (actionId === "open-window" || actionId === "openWindow" || actionId === "openSeparateWindow") {
613
+ this.config.openWindow = false;
614
+ this.config.openSeparateWindow = false;
615
+ }
616
+ if (actionId === "copy") this.config.copy = false;
617
+ if (actionId === "page-nav" || actionId === "pageNav" || actionId === "pagination") {
618
+ this.config.pageNav = false;
619
+ this.config.pagination = false;
620
+ }
621
+ this.render();
622
+ }
623
+ showAction(actionId) {
624
+ this.config[actionId] = true;
625
+ if (actionId === "zoom-in" || actionId === "zoomIn") this.config.zoomIn = true;
626
+ if (actionId === "zoom-out" || actionId === "zoomOut") this.config.zoomOut = true;
627
+ if (actionId === "fit-page" || actionId === "fitPage" || actionId === "fitToPage" || actionId === "fit-width") {
628
+ this.config.fitPage = true;
629
+ this.config.fitToPage = true;
630
+ this.config.fitWidth = true;
631
+ }
632
+ if (actionId === "rotate-cw" || actionId === "rotate" || actionId === "rotateCW") this.config.rotate = true;
633
+ if (actionId === "fullscreen") this.config.fullscreen = true;
634
+ if (actionId === "download") this.config.download = true;
635
+ if (actionId === "print") this.config.print = true;
636
+ if (actionId === "open-window" || actionId === "openWindow" || actionId === "openSeparateWindow") {
637
+ this.config.openWindow = true;
638
+ this.config.openSeparateWindow = true;
639
+ }
640
+ if (actionId === "copy") this.config.copy = true;
641
+ if (actionId === "page-nav" || actionId === "pageNav" || actionId === "pagination") {
642
+ this.config.pageNav = true;
643
+ this.config.pagination = true;
644
+ }
645
+ this.render();
646
+ }
647
+ normalizeActionId(actionId) {
648
+ if (actionId === "zoomIn") return "zoom-in";
649
+ if (actionId === "zoomOut") return "zoom-out";
650
+ if (actionId === "fitPage" || actionId === "fitToPage" || actionId === "fitWidth") return "fit-page";
651
+ if (actionId === "rotateCW") return "rotate-cw";
652
+ if (actionId === "rotateCCW") return "rotate-ccw";
653
+ if (actionId === "openWindow" || actionId === "openSeparateWindow") return "open-window";
654
+ if (actionId === "pageNav") return "page-nav";
655
+ return actionId;
656
+ }
657
+ enableAction(actionId) {
658
+ const norm = this.normalizeActionId(actionId);
659
+ const btn = this.toolbarEl.querySelector(`button[data-action-id="${actionId}"], button[data-action-id="${norm}"]`);
660
+ if (btn) {
661
+ btn.disabled = false;
662
+ btn.classList.remove("disabled");
663
+ }
664
+ }
665
+ disableAction(actionId) {
666
+ const norm = this.normalizeActionId(actionId);
667
+ const btn = this.toolbarEl.querySelector(`button[data-action-id="${actionId}"], button[data-action-id="${norm}"]`);
668
+ if (btn) {
669
+ btn.disabled = true;
670
+ btn.classList.add("disabled");
671
+ }
672
+ }
673
+ setActionActive(actionId, active) {
674
+ const norm = this.normalizeActionId(actionId);
675
+ const btn = this.toolbarEl.querySelector(`button[data-action-id="${actionId}"], button[data-action-id="${norm}"]`);
676
+ if (btn) {
677
+ if (active) {
678
+ btn.classList.add("active");
679
+ } else {
680
+ btn.classList.remove("active");
681
+ }
682
+ }
683
+ }
684
+ isActionEnabled(action) {
685
+ const c = this.config;
686
+ const id = action.id;
687
+ if (c[id] === false) return false;
688
+ if (id === "zoom-in" && (c.zoomIn === false || c["zoom-in"] === false)) return false;
689
+ if (id === "zoom-out" && (c.zoomOut === false || c["zoom-out"] === false)) return false;
690
+ if ((id === "fit-page" || id === "fit-width" || id === "fit-slide") && (c.fitPage === false || c.fitToPage === false || c.fitWidth === false || c["fit-page"] === false || c["fit-width"] === false)) {
691
+ return false;
692
+ }
693
+ if ((id === "rotate-cw" || id === "rotate-ccw" || id === "rotate") && (c.rotate === false || c.rotateCW === false || c.rotateCCW === false || c["rotate-cw"] === false)) {
694
+ return false;
695
+ }
696
+ if (id === "fullscreen" && c.fullscreen === false) return false;
697
+ if (id === "download" && c.download === false) return false;
698
+ if (id === "print" && c.print === false) return false;
699
+ if ((id === "open-window" || id === "external-window") && (c.openWindow === false || c.openSeparateWindow === false || c["open-window"] === false)) {
700
+ return false;
701
+ }
702
+ if (id === "copy" && c.copy === false) return false;
703
+ if (id === "thumbnails" && c.thumbnails === false) return false;
704
+ if (id === "page-nav" && (c.pageNav === false || c.pagination === false || c["page-nav"] === false)) {
705
+ return false;
706
+ }
707
+ if ((id === "prev" || id === "page-prev") && (c.prevPage === false || c.prev === false || c["prev"] === false || c.pageNav === false || c.pagination === false)) {
708
+ return false;
709
+ }
710
+ if ((id === "next" || id === "page-next") && (c.nextPage === false || c.next === false || c["next"] === false || c.pageNav === false || c.pagination === false)) {
711
+ return false;
712
+ }
713
+ if (id === "play" && c.play === false) return false;
714
+ if (id === "pause" && c.pause === false) return false;
715
+ if ((id === "fast-forward" || id === "forward-10") && (c.fastForward === false || c["fast-forward"] === false)) {
716
+ return false;
717
+ }
718
+ if ((id === "rewind" || id === "replay-10") && (c.rewind === false || c["rewind"] === false)) {
719
+ return false;
720
+ }
721
+ if (id === "speed" && c.speed === false) return false;
722
+ return true;
723
+ }
588
724
  setPage(page, max) {
589
725
  if (this.pageInputEl) {
590
726
  this.pageInputEl.value = page.toString();
727
+ if (max !== void 0) {
728
+ this.pageInputEl.max = max.toString();
729
+ }
591
730
  }
592
731
  if (max !== void 0 && this.pageLabelEl) {
593
732
  this.pageLabelEl.textContent = ` / ${max}`;
594
733
  }
734
+ const currentMax = max !== void 0 ? max : parseInt(this.pageInputEl?.max || "1", 10) || 1;
735
+ if (this.prevPageBtn) {
736
+ this.prevPageBtn.disabled = page <= 1;
737
+ this.prevPageBtn.style.opacity = page <= 1 ? "0.4" : "1";
738
+ }
739
+ if (this.nextPageBtn) {
740
+ this.nextPageBtn.disabled = page >= currentMax;
741
+ this.nextPageBtn.style.opacity = page >= currentMax ? "0.4" : "1";
742
+ }
595
743
  }
596
744
  update(actions) {
597
745
  this.actions = actions;
@@ -614,8 +762,9 @@ var ToolbarController = class {
614
762
  view: [],
615
763
  actions: []
616
764
  };
617
- const hasPageNav = this.actions.some((a) => a.type === "page-nav");
618
- const effectiveActions = hasPageNav ? this.actions.filter((a) => a.id !== "page-prev" && a.id !== "page-next" && a.id !== "prev" && a.id !== "next") : this.actions;
765
+ const enabledActions = this.actions.filter((a) => this.isActionEnabled(a));
766
+ const hasPageNav = enabledActions.some((a) => a.type === "page-nav");
767
+ const effectiveActions = hasPageNav ? enabledActions.filter((a) => a.id !== "page-prev" && a.id !== "page-next" && a.id !== "prev" && a.id !== "next") : enabledActions;
619
768
  for (const action of effectiveActions) {
620
769
  if (groups[action.group]) {
621
770
  groups[action.group].push(action);
@@ -675,6 +824,13 @@ var ToolbarController = class {
675
824
  action.execute("go", val);
676
825
  });
677
826
  const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${max}`);
827
+ const curVal = action.value ?? 1;
828
+ prevBtn.disabled = curVal <= 1;
829
+ prevBtn.style.opacity = curVal <= 1 ? "0.4" : "1";
830
+ nextBtn.disabled = curVal >= max;
831
+ nextBtn.style.opacity = curVal >= max ? "0.4" : "1";
832
+ this.prevPageBtn = prevBtn;
833
+ this.nextPageBtn = nextBtn;
678
834
  this.pageInputEl = input;
679
835
  this.pageLabelEl = label;
680
836
  groupEl.appendChild(prevBtn);
@@ -730,25 +886,63 @@ var ToolbarController = class {
730
886
  var ThumbnailPanel = class {
731
887
  el;
732
888
  panelEl;
889
+ listEl;
890
+ headerEl;
733
891
  thumbnails = [];
734
892
  onSelect;
893
+ onToggleCallback;
735
894
  activeIndex = 0;
736
- constructor(container) {
895
+ observer;
896
+ renderedIndices = /* @__PURE__ */ new Set();
897
+ constructor(container, onToggle) {
737
898
  this.el = container;
899
+ this.onToggleCallback = onToggle;
738
900
  this.panelEl = createElement("div", { className: "fp-thumbnail-panel hidden" });
901
+ this.panelEl.style.width = "0px";
902
+ this.panelEl.style.minWidth = "0px";
903
+ this.panelEl.style.opacity = "0";
904
+ this.headerEl = createElement("div", { className: "fp-thumbnail-header" });
905
+ const title = createElement("span", { className: "fp-thumbnail-title" }, "Pages");
906
+ const closeBtn = createElement("button", {
907
+ className: "fp-thumbnail-close-btn",
908
+ title: "Close Thumbnails",
909
+ type: "button"
910
+ });
911
+ closeBtn.innerHTML = ICON_CLOSE;
912
+ closeBtn.addEventListener("click", (e) => {
913
+ e.stopPropagation();
914
+ this.hide();
915
+ });
916
+ this.headerEl.appendChild(title);
917
+ this.headerEl.appendChild(closeBtn);
918
+ this.listEl = createElement("div", { className: "fp-thumbnail-list" });
919
+ this.panelEl.appendChild(this.headerEl);
920
+ this.panelEl.appendChild(this.listEl);
739
921
  this.el.appendChild(this.panelEl);
740
922
  }
923
+ isOpen() {
924
+ return !this.panelEl.classList.contains("hidden") && this.panelEl.style.width !== "0px";
925
+ }
741
926
  update(thumbnails, onSelect) {
742
927
  this.thumbnails = thumbnails;
743
928
  this.onSelect = onSelect;
929
+ this.renderedIndices.clear();
930
+ const titleEl = this.headerEl.querySelector(".fp-thumbnail-title");
931
+ if (titleEl) {
932
+ titleEl.textContent = `Pages (${thumbnails.length})`;
933
+ }
744
934
  this.render();
935
+ if (this.isOpen()) {
936
+ this.renderAllVisible();
937
+ }
745
938
  }
746
939
  setActive(index) {
747
940
  this.activeIndex = index;
748
- const items = this.panelEl.querySelectorAll(".fp-thumbnail-item");
941
+ const items = this.listEl.querySelectorAll(".fp-thumbnail-item");
749
942
  items.forEach((item, i) => {
750
943
  if (i === index) {
751
944
  item.classList.add("active");
945
+ item.scrollIntoView({ block: "nearest", behavior: "smooth" });
752
946
  } else {
753
947
  item.classList.remove("active");
754
948
  }
@@ -756,37 +950,102 @@ var ThumbnailPanel = class {
756
950
  }
757
951
  show() {
758
952
  this.panelEl.classList.remove("hidden");
953
+ this.panelEl.style.width = "200px";
954
+ this.panelEl.style.minWidth = "200px";
955
+ this.panelEl.style.opacity = "1";
956
+ this.panelEl.style.display = "flex";
957
+ this.onToggleCallback?.(true);
958
+ this.renderAllVisible();
759
959
  }
760
960
  hide() {
761
961
  this.panelEl.classList.add("hidden");
962
+ this.panelEl.style.width = "0px";
963
+ this.panelEl.style.minWidth = "0px";
964
+ this.panelEl.style.opacity = "0";
965
+ this.onToggleCallback?.(false);
762
966
  }
763
967
  toggle() {
764
- this.panelEl.classList.toggle("hidden");
968
+ if (this.isOpen()) {
969
+ this.hide();
970
+ } else {
971
+ this.show();
972
+ }
765
973
  }
766
974
  destroy() {
975
+ if (this.observer) {
976
+ this.observer.disconnect();
977
+ this.observer = void 0;
978
+ }
979
+ this.renderedIndices.clear();
767
980
  this.el.innerHTML = "";
768
981
  }
769
- async render() {
770
- this.panelEl.innerHTML = "";
982
+ renderAllVisible() {
983
+ const items = this.listEl.querySelectorAll(".fp-thumbnail-item");
984
+ items.forEach((item) => {
985
+ const idx = parseInt(item.dataset.thumbIndex || "-1", 10);
986
+ if (idx >= 0 && !this.renderedIndices.has(idx)) {
987
+ this.renderThumbnailItem(idx, item);
988
+ }
989
+ });
990
+ }
991
+ renderThumbnailItem(idx, itemEl) {
992
+ if (this.renderedIndices.has(idx)) return;
993
+ this.renderedIndices.add(idx);
994
+ const canvas = itemEl.querySelector("canvas");
995
+ const thumb = this.thumbnails[idx];
996
+ if (canvas && thumb) {
997
+ Promise.resolve(thumb.render(canvas)).catch((err) => {
998
+ console.warn(`[FilePreview] Error rendering thumbnail ${idx}:`, err);
999
+ });
1000
+ }
1001
+ }
1002
+ render() {
1003
+ if (this.observer) {
1004
+ this.observer.disconnect();
1005
+ }
1006
+ this.listEl.innerHTML = "";
1007
+ const useObserver = typeof IntersectionObserver !== "undefined";
1008
+ if (useObserver) {
1009
+ this.observer = new IntersectionObserver(
1010
+ (entries) => {
1011
+ for (const entry of entries) {
1012
+ if (entry.isIntersecting) {
1013
+ const target = entry.target;
1014
+ const idx = parseInt(target.dataset.thumbIndex || "-1", 10);
1015
+ if (idx >= 0) {
1016
+ this.renderThumbnailItem(idx, target);
1017
+ }
1018
+ }
1019
+ }
1020
+ },
1021
+ {
1022
+ root: this.listEl,
1023
+ rootMargin: "120px 0px 120px 0px"
1024
+ }
1025
+ );
1026
+ }
771
1027
  for (let i = 0; i < this.thumbnails.length; i++) {
772
1028
  const thumb = this.thumbnails[i];
773
- const itemEl = createElement("div", { className: "fp-thumbnail-item" });
774
- if (i === this.activeIndex) {
775
- itemEl.classList.add("active");
776
- }
1029
+ const itemEl = createElement("div", {
1030
+ className: "fp-thumbnail-item" + (i === this.activeIndex ? " active" : "")
1031
+ });
1032
+ itemEl.dataset.thumbIndex = i.toString();
777
1033
  itemEl.addEventListener("click", () => {
778
1034
  this.setActive(i);
779
1035
  this.onSelect?.(i);
780
1036
  });
781
- const canvas = createElement("canvas", { width: "150", height: "200" });
782
- const label = createElement("span", { className: "fp-thumbnail-label" }, thumb.label);
783
- itemEl.appendChild(canvas);
1037
+ const canvasWrapper = createElement("div", { className: "fp-thumbnail-canvas-wrapper" });
1038
+ const canvas = createElement("canvas", { width: "130", height: "170" });
1039
+ canvasWrapper.appendChild(canvas);
1040
+ const label = createElement("span", { className: "fp-thumbnail-label" }, thumb.label || `Page ${i + 1}`);
1041
+ itemEl.appendChild(canvasWrapper);
784
1042
  itemEl.appendChild(label);
785
- this.panelEl.appendChild(itemEl);
786
- try {
787
- await thumb.render(canvas);
788
- } catch (err) {
789
- console.error(`[FilePreview] Error rendering thumbnail ${i}:`, err);
1043
+ this.listEl.appendChild(itemEl);
1044
+ if (this.observer) {
1045
+ this.observer.observe(itemEl);
1046
+ }
1047
+ if (i < 4) {
1048
+ this.renderThumbnailItem(i, itemEl);
790
1049
  }
791
1050
  }
792
1051
  }
@@ -807,6 +1066,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
807
1066
  currentOptions = {};
808
1067
  resizeObserver = null;
809
1068
  fullscreenHandler = null;
1069
+ titleBarEl = null;
810
1070
  /**
811
1071
  * Register a preview plugin.
812
1072
  */
@@ -843,6 +1103,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
843
1103
  }
844
1104
  this.currentBuffer = buffer.slice(0);
845
1105
  this.currentMetadata = metadata;
1106
+ this.updateTitleBar(options, metadata);
846
1107
  if (signal.aborted) throw new DOMException("Aborted", "AbortError");
847
1108
  const fileInfo = { metadata, buffer };
848
1109
  const matchedPlugin = await this.findPlugin(fileInfo);
@@ -866,15 +1127,21 @@ var FilePreviewViewer = class _FilePreviewViewer {
866
1127
  if (event === "page-change" && payload && typeof payload.page === "number") {
867
1128
  const total = payload.total ?? payload.totalPages;
868
1129
  this.toolbar?.setPage(payload.page, total);
1130
+ this.thumbnailPanel?.setActive(payload.page - 1);
869
1131
  }
870
1132
  this.eventEmitter.emit(event, payload);
871
- }
1133
+ },
1134
+ toggleThumbnails: () => this.toggleThumbnails()
872
1135
  });
873
1136
  this.activeInstance = instance;
874
1137
  instance.openInSeparateWindow = () => this.openInSeparateWindow();
1138
+ instance.toggleThumbnails = () => this.toggleThumbnails();
875
1139
  this.hideLoading();
876
1140
  this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
877
- if (options.showToolbar !== false && this.toolbar) {
1141
+ const isToolbarVisible = options.showToolbar !== false && options.toolbar !== false;
1142
+ if (isToolbarVisible && this.toolbar) {
1143
+ const toolbarConfig = this.extractToolbarConfig(options);
1144
+ this.toolbar.setConfig(toolbarConfig);
878
1145
  const actions = matchedPlugin.getToolbarActions(instance);
879
1146
  const hasFullscreen = actions.some((a) => a.id === "fullscreen");
880
1147
  if (!hasFullscreen && this.wrapperEl) {
@@ -924,6 +1191,12 @@ var FilePreviewViewer = class _FilePreviewViewer {
924
1191
  }
925
1192
  });
926
1193
  }
1194
+ const thumbAction = actions.find((a) => a.id === "thumbnails");
1195
+ if (thumbAction) {
1196
+ thumbAction.execute = () => {
1197
+ this.toggleThumbnails();
1198
+ };
1199
+ }
927
1200
  this.toolbar.update(actions);
928
1201
  this.toolbar.show();
929
1202
  }
@@ -1008,7 +1281,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1008
1281
  if (targetUrl) {
1009
1282
  const newWin2 = window.open(targetUrl, "_blank");
1010
1283
  if (!newWin2) {
1011
- alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
1284
+ console.warn("[FilePreviewViewer] Popup blocker prevented opening the preview in a separate window.");
1012
1285
  return null;
1013
1286
  }
1014
1287
  return newWin2;
@@ -1016,7 +1289,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1016
1289
  const title = (this.currentMetadata?.name || "Document Preview") + " - Full Preview";
1017
1290
  const newWin = window.open("", "_blank");
1018
1291
  if (!newWin) {
1019
- alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
1292
+ console.warn("[FilePreviewViewer] Popup blocker prevented opening the preview in a separate window.");
1020
1293
  return null;
1021
1294
  }
1022
1295
  newWin.document.title = title;
@@ -1093,6 +1366,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1093
1366
  this.currentBuffer = null;
1094
1367
  this.currentMetadata = null;
1095
1368
  this.currentContainer = null;
1369
+ this.titleBarEl = null;
1096
1370
  }
1097
1371
  /**
1098
1372
  * Get the currently active preview instance.
@@ -1100,7 +1374,218 @@ var FilePreviewViewer = class _FilePreviewViewer {
1100
1374
  getInstance() {
1101
1375
  return this.activeInstance;
1102
1376
  }
1377
+ // --- Toolbar Configuration & Feature Toggles ---
1378
+ /**
1379
+ * Configure toolbar features dynamically through options or methods.
1380
+ * e.g. viewer.setToolbarConfig({ zoomIn: false, print: false })
1381
+ */
1382
+ setToolbarConfig(config) {
1383
+ this.toolbar?.setConfig(config);
1384
+ }
1385
+ /**
1386
+ * Get current toolbar configuration.
1387
+ */
1388
+ getToolbarConfig() {
1389
+ return this.toolbar?.getConfig() ?? {};
1390
+ }
1391
+ /**
1392
+ * Hide a specific toolbar action by ID or alias (e.g. 'zoom-in', 'print', 'download').
1393
+ */
1394
+ hideToolbarAction(actionId) {
1395
+ this.toolbar?.hideAction(actionId);
1396
+ }
1397
+ /**
1398
+ * Show a specific toolbar action by ID or alias.
1399
+ */
1400
+ showToolbarAction(actionId) {
1401
+ this.toolbar?.showAction(actionId);
1402
+ }
1403
+ /**
1404
+ * Enable a specific toolbar action button.
1405
+ */
1406
+ enableToolbarAction(actionId) {
1407
+ this.toolbar?.enableAction(actionId);
1408
+ }
1409
+ /**
1410
+ * Disable a specific toolbar action button.
1411
+ */
1412
+ disableToolbarAction(actionId) {
1413
+ this.toolbar?.disableAction(actionId);
1414
+ }
1415
+ // --- Programmatic Toolbar Action Methods ---
1416
+ /**
1417
+ * Fit document to page so it fills the frame width with minimal margins.
1418
+ */
1419
+ fitToPage() {
1420
+ this.activeInstance?.fitToPage?.();
1421
+ }
1422
+ /**
1423
+ * Zoom in.
1424
+ */
1425
+ zoomIn() {
1426
+ this.activeInstance?.zoomIn?.();
1427
+ }
1428
+ /**
1429
+ * Zoom out.
1430
+ */
1431
+ zoomOut() {
1432
+ this.activeInstance?.zoomOut?.();
1433
+ }
1434
+ /**
1435
+ * Set specific zoom level.
1436
+ */
1437
+ setZoom(level) {
1438
+ this.activeInstance?.setZoom?.(level);
1439
+ }
1440
+ /**
1441
+ * Get current zoom level.
1442
+ */
1443
+ getZoom() {
1444
+ return this.activeInstance?.getZoom?.() ?? 1;
1445
+ }
1446
+ /**
1447
+ * Rotate 90 degrees clockwise.
1448
+ */
1449
+ rotateCW() {
1450
+ this.activeInstance?.rotateCW?.();
1451
+ }
1452
+ /**
1453
+ * Rotate 90 degrees counter-clockwise.
1454
+ */
1455
+ rotateCCW() {
1456
+ this.activeInstance?.rotateCCW?.();
1457
+ }
1458
+ /**
1459
+ * Navigate to a specific page number.
1460
+ */
1461
+ goToPage(page) {
1462
+ this.activeInstance?.goToPage?.(page);
1463
+ this.toolbar?.setPage(page);
1464
+ }
1465
+ /**
1466
+ * Navigate to next page.
1467
+ */
1468
+ nextPage() {
1469
+ const cur = this.getCurrentPage();
1470
+ const max = this.getPageCount();
1471
+ if (cur < max) {
1472
+ this.goToPage(cur + 1);
1473
+ }
1474
+ }
1475
+ /**
1476
+ * Navigate to previous page.
1477
+ */
1478
+ prevPage() {
1479
+ const cur = this.getCurrentPage();
1480
+ if (cur > 1) {
1481
+ this.goToPage(cur - 1);
1482
+ }
1483
+ }
1484
+ /**
1485
+ * Get total page count.
1486
+ */
1487
+ getPageCount() {
1488
+ return this.activeInstance?.getPageCount?.() ?? 1;
1489
+ }
1490
+ /**
1491
+ * Get current page number.
1492
+ */
1493
+ getCurrentPage() {
1494
+ return this.activeInstance?.getCurrentPage?.() ?? 1;
1495
+ }
1496
+ /**
1497
+ * Download the current document.
1498
+ */
1499
+ download() {
1500
+ this.activeInstance?.download?.();
1501
+ }
1502
+ /**
1503
+ * Print the current document.
1504
+ */
1505
+ print() {
1506
+ this.activeInstance?.print?.();
1507
+ }
1508
+ /**
1509
+ * Toggle fullscreen mode.
1510
+ */
1511
+ toggleFullscreen() {
1512
+ try {
1513
+ if (!document.fullscreenElement) {
1514
+ this.wrapperEl?.requestFullscreen?.();
1515
+ this.wrapperEl?.classList.add("fp-fullscreen-active");
1516
+ } else {
1517
+ document.exitFullscreen?.();
1518
+ this.wrapperEl?.classList.remove("fp-fullscreen-active");
1519
+ }
1520
+ } catch {
1521
+ this.wrapperEl?.classList.toggle("fp-fullscreen-active");
1522
+ }
1523
+ setTimeout(() => {
1524
+ this.activeInstance?.fitToPage?.();
1525
+ }, 120);
1526
+ }
1527
+ /**
1528
+ * Toggle thumbnails sidebar panel.
1529
+ */
1530
+ toggleThumbnails() {
1531
+ if (!this.thumbnailPanel) return;
1532
+ this.thumbnailPanel.toggle();
1533
+ const isOpen = this.thumbnailPanel.isOpen();
1534
+ this.toolbar?.setActionActive("thumbnails", isOpen);
1535
+ setTimeout(() => {
1536
+ this.activeInstance?.fitToPage?.();
1537
+ }, 260);
1538
+ }
1539
+ /**
1540
+ * Set whether to display the file name / title bar above the toolbar.
1541
+ */
1542
+ setShowFileName(show) {
1543
+ if (this.currentOptions) {
1544
+ this.currentOptions.showFileName = show;
1545
+ }
1546
+ this.updateTitleBar(this.currentOptions, this.currentMetadata);
1547
+ }
1548
+ /**
1549
+ * Set or override the displayed file name in the preview panel.
1550
+ */
1551
+ setFileName(name) {
1552
+ if (this.currentOptions) {
1553
+ this.currentOptions.fileName = name;
1554
+ }
1555
+ this.updateTitleBar(this.currentOptions, this.currentMetadata);
1556
+ }
1103
1557
  // --- Private methods ---
1558
+ updateTitleBar(options, metadata) {
1559
+ if (!this.titleBarEl) return;
1560
+ const show = options?.showFileName !== false;
1561
+ if (!show) {
1562
+ this.titleBarEl.style.display = "none";
1563
+ return;
1564
+ }
1565
+ const name = options?.fileName || metadata?.name || this.currentMetadata?.name || "";
1566
+ if (!name) {
1567
+ this.titleBarEl.style.display = "none";
1568
+ return;
1569
+ }
1570
+ this.titleBarEl.style.display = "flex";
1571
+ const ext = metadata?.extension || (name.includes(".") ? "." + name.split(".").pop() : "");
1572
+ const badge = ext ? ext.replace(".", "").toUpperCase() : "";
1573
+ this.titleBarEl.innerHTML = `
1574
+ <div class="fp-titlebar-info">
1575
+ <span class="fp-titlebar-icon">\u{1F4C4}</span>
1576
+ <span class="fp-titlebar-name" title="${name}">${name}</span>
1577
+ ${badge ? `<span class="fp-titlebar-badge">${badge}</span>` : ""}
1578
+ </div>
1579
+ `;
1580
+ }
1581
+ extractToolbarConfig(options) {
1582
+ if (!options) return {};
1583
+ const nested = typeof options.toolbar === "object" ? options.toolbar : {};
1584
+ return {
1585
+ ...options,
1586
+ ...nested
1587
+ };
1588
+ }
1104
1589
  abort() {
1105
1590
  if (this.abortController) {
1106
1591
  this.abortController.abort();
@@ -1143,17 +1628,29 @@ var FilePreviewViewer = class _FilePreviewViewer {
1143
1628
  bodyEl.className = "fp-body";
1144
1629
  bodyEl.appendChild(thumbnailEl);
1145
1630
  bodyEl.appendChild(this.contentEl);
1631
+ const titleBarEl = document.createElement("div");
1632
+ titleBarEl.className = "fp-titlebar";
1633
+ this.titleBarEl = titleBarEl;
1634
+ this.updateTitleBar(options, this.currentMetadata);
1146
1635
  if (toolbarPos === "top") {
1636
+ this.wrapperEl.appendChild(titleBarEl);
1147
1637
  this.wrapperEl.appendChild(toolbarEl);
1148
1638
  this.wrapperEl.appendChild(bodyEl);
1149
1639
  } else {
1640
+ this.wrapperEl.appendChild(titleBarEl);
1150
1641
  this.wrapperEl.appendChild(bodyEl);
1151
1642
  this.wrapperEl.appendChild(toolbarEl);
1152
1643
  }
1153
1644
  container.innerHTML = "";
1154
1645
  container.appendChild(this.wrapperEl);
1155
- this.toolbar = new ToolbarController(toolbarEl);
1156
- this.thumbnailPanel = new ThumbnailPanel(thumbnailEl);
1646
+ const initialToolbarConfig = this.extractToolbarConfig(options);
1647
+ this.toolbar = new ToolbarController(toolbarEl, initialToolbarConfig);
1648
+ this.thumbnailPanel = new ThumbnailPanel(thumbnailEl, (isOpen) => {
1649
+ this.toolbar?.setActionActive("thumbnails", isOpen);
1650
+ setTimeout(() => {
1651
+ this.activeInstance?.fitToPage?.();
1652
+ }, 260);
1653
+ });
1157
1654
  this.setupKeyboardShortcuts();
1158
1655
  this.setupDragAndDrop(container, options);
1159
1656
  this.setupResizeAndFullscreenListeners();
@@ -1543,6 +2040,14 @@ var PdfPlugin = class {
1543
2040
  group: "zoom",
1544
2041
  execute: () => instance.fitToPage?.()
1545
2042
  },
2043
+ {
2044
+ id: "fit-width",
2045
+ icon: "fit-width",
2046
+ label: "Fit to Width",
2047
+ type: "button",
2048
+ group: "zoom",
2049
+ execute: () => instance.fitToWidth?.()
2050
+ },
1546
2051
  {
1547
2052
  id: "rotate-cw",
1548
2053
  icon: "rotate-cw",
@@ -1551,6 +2056,14 @@ var PdfPlugin = class {
1551
2056
  group: "view",
1552
2057
  execute: () => instance.rotateCW?.()
1553
2058
  },
2059
+ {
2060
+ id: "rotate-ccw",
2061
+ icon: "rotate-ccw",
2062
+ label: "Rotate Counter-Clockwise",
2063
+ type: "button",
2064
+ group: "view",
2065
+ execute: () => instance.rotateCCW?.()
2066
+ },
1554
2067
  {
1555
2068
  id: "download",
1556
2069
  icon: "download",
@@ -1582,12 +2095,13 @@ var PdfPlugin = class {
1582
2095
  container.className = "fp-pdf-container";
1583
2096
  container.style.width = "100%";
1584
2097
  container.style.height = "100%";
1585
- container.style.overflow = "auto";
2098
+ container.style.overflowX = "hidden";
2099
+ container.style.overflowY = "auto";
1586
2100
  container.style.display = "flex";
1587
2101
  container.style.flexDirection = "column";
1588
2102
  container.style.alignItems = "center";
1589
2103
  container.style.justifyContent = "flex-start";
1590
- container.style.padding = "20px 16px";
2104
+ container.style.padding = "16px 8px";
1591
2105
  container.style.backgroundColor = "#0f172a";
1592
2106
  container.style.boxSizing = "border-box";
1593
2107
  container.style.position = "relative";
@@ -1598,30 +2112,12 @@ var PdfPlugin = class {
1598
2112
  pageCard.style.borderRadius = "4px";
1599
2113
  pageCard.style.overflow = "hidden";
1600
2114
  pageCard.style.lineHeight = "0";
1601
- pageCard.style.transition = "transform 0.15s ease";
1602
2115
  pageCard.style.position = "relative";
1603
2116
  pageCard.style.flexShrink = "0";
2117
+ pageCard.style.transition = "box-shadow 0.2s ease";
1604
2118
  let canvas = document.createElement("canvas");
1605
2119
  pageCard.appendChild(canvas);
1606
2120
  container.appendChild(pageCard);
1607
- const indicator = document.createElement("div");
1608
- indicator.className = "fp-pdf-page-indicator";
1609
- indicator.style.position = "sticky";
1610
- indicator.style.bottom = "16px";
1611
- indicator.style.marginTop = "16px";
1612
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
1613
- indicator.style.backdropFilter = "blur(8px)";
1614
- indicator.style.color = "#f8fafc";
1615
- indicator.style.fontSize = "12px";
1616
- indicator.style.fontWeight = "600";
1617
- indicator.style.padding = "5px 14px";
1618
- indicator.style.borderRadius = "20px";
1619
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
1620
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
1621
- indicator.style.zIndex = "10";
1622
- indicator.style.userSelect = "none";
1623
- indicator.style.pointerEvents = "none";
1624
- container.appendChild(indicator);
1625
2121
  ctx.container.appendChild(container);
1626
2122
  const standardFontsUrl = typeof window !== "undefined" && window.__PDF_STANDARD_FONTS_URL__ || "./standard_fonts/";
1627
2123
  const cmapsUrl = typeof window !== "undefined" && window.__PDF_CMAPS_URL__ || "./cmaps/";
@@ -1637,8 +2133,9 @@ var PdfPlugin = class {
1637
2133
  let currentPage = 1;
1638
2134
  let zoomScale = 1;
1639
2135
  let rotation = 0;
1640
- let fitMode = "page";
2136
+ let fitMode = ctx?.options?.fitMode || "width";
1641
2137
  let currentRenderTask = null;
2138
+ let textLayerDiv = null;
1642
2139
  const renderPage = async (pageNum) => {
1643
2140
  if (currentRenderTask) {
1644
2141
  try {
@@ -1650,30 +2147,37 @@ var PdfPlugin = class {
1650
2147
  const newCanvas = document.createElement("canvas");
1651
2148
  pageCard.replaceChild(newCanvas, canvas);
1652
2149
  canvas = newCanvas;
2150
+ if (textLayerDiv) {
2151
+ textLayerDiv.remove();
2152
+ textLayerDiv = null;
2153
+ }
1653
2154
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
1654
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
1655
2155
  ctx.emit("page-change", { page: currentPage, total: totalPages });
1656
2156
  const page = await pdfDoc.getPage(currentPage);
1657
2157
  const containerWidth = container.clientWidth || 900;
1658
2158
  const containerHeight = container.clientHeight || 700;
1659
2159
  const unscaledVp = page.getViewport({ scale: 1, rotation });
1660
- const availWidth = Math.max(320, containerWidth - 48);
1661
- const availHeight = Math.max(550, containerHeight - 88);
2160
+ const availWidth = Math.max(280, containerWidth - 36);
2161
+ const availHeight = Math.max(280, containerHeight - 32);
1662
2162
  const scaleW = availWidth / unscaledVp.width;
1663
2163
  const scaleH = availHeight / unscaledVp.height;
1664
2164
  let fitScale;
1665
2165
  if (fitMode === "page") {
1666
- fitScale = Math.max(0.4, Math.min(scaleW, scaleH));
2166
+ fitScale = Math.max(0.2, Math.min(4, Math.min(scaleW, scaleH)));
1667
2167
  } else {
1668
- fitScale = Math.max(0.65, Math.min(1.25, scaleW));
2168
+ fitScale = Math.max(0.2, Math.min(4, scaleW));
1669
2169
  }
1670
2170
  const effectiveScale = (fitScale > 0 ? fitScale : 1) * zoomScale;
1671
2171
  const pixelRatio = window.devicePixelRatio || 1;
1672
2172
  const viewport = page.getViewport({ scale: effectiveScale, rotation });
2173
+ const displayWidth = Math.floor(viewport.width);
2174
+ const displayHeight = Math.floor(viewport.height);
1673
2175
  canvas.width = Math.floor(viewport.width * pixelRatio);
1674
2176
  canvas.height = Math.floor(viewport.height * pixelRatio);
1675
- canvas.style.width = `${Math.floor(viewport.width)}px`;
1676
- canvas.style.height = `${Math.floor(viewport.height)}px`;
2177
+ canvas.style.width = `${displayWidth}px`;
2178
+ canvas.style.height = `${displayHeight}px`;
2179
+ pageCard.style.width = `${displayWidth}px`;
2180
+ pageCard.style.height = `${displayHeight}px`;
1677
2181
  const canvasCtx = canvas.getContext("2d");
1678
2182
  if (!canvasCtx) return;
1679
2183
  canvasCtx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
@@ -1687,9 +2191,31 @@ var PdfPlugin = class {
1687
2191
  if (err?.name !== "RenderingCancelledException") {
1688
2192
  console.warn("[PdfPlugin] Page render warning:", err);
1689
2193
  }
2194
+ return;
1690
2195
  } finally {
1691
2196
  currentRenderTask = null;
1692
2197
  }
2198
+ try {
2199
+ textLayerDiv = document.createElement("div");
2200
+ textLayerDiv.className = "textLayer";
2201
+ textLayerDiv.style.width = `${displayWidth}px`;
2202
+ textLayerDiv.style.height = `${displayHeight}px`;
2203
+ textLayerDiv.style.position = "absolute";
2204
+ textLayerDiv.style.top = "0";
2205
+ textLayerDiv.style.left = "0";
2206
+ textLayerDiv.style.lineHeight = "1";
2207
+ pageCard.appendChild(textLayerDiv);
2208
+ if (pdfjsLib.TextLayer) {
2209
+ const textLayer = new pdfjsLib.TextLayer({
2210
+ textContentSource: page.streamTextContent(),
2211
+ container: textLayerDiv,
2212
+ viewport
2213
+ });
2214
+ await textLayer.render();
2215
+ }
2216
+ } catch (err) {
2217
+ console.debug("[PdfPlugin] TextLayer notice:", err);
2218
+ }
1693
2219
  };
1694
2220
  renderPage(1);
1695
2221
  let resizeTimer = null;
@@ -1699,11 +2225,48 @@ var PdfPlugin = class {
1699
2225
  if (Math.abs(zoomScale - 1) < 0.05) {
1700
2226
  renderPage(currentPage);
1701
2227
  }
1702
- }, 150);
2228
+ }, 120);
1703
2229
  });
1704
2230
  resizeObserver.observe(container);
2231
+ const onWheel = (e) => {
2232
+ if (e.ctrlKey || e.metaKey) {
2233
+ e.preventDefault();
2234
+ const delta = e.deltaY > 0 ? -0.15 : 0.15;
2235
+ zoomScale = Math.max(0.25, Math.min(4, Math.round((zoomScale + delta) * 100) / 100));
2236
+ renderPage(currentPage);
2237
+ }
2238
+ };
2239
+ container.addEventListener("wheel", onWheel, { passive: false });
2240
+ const onKeyDown = (e) => {
2241
+ const tag = e.target?.tagName?.toLowerCase();
2242
+ if (tag === "input" || tag === "textarea" || tag === "select") return;
2243
+ if (e.key === "ArrowRight" || e.key === "PageDown") {
2244
+ if (currentPage < totalPages) {
2245
+ e.preventDefault();
2246
+ container.scrollTop = 0;
2247
+ renderPage(currentPage + 1);
2248
+ }
2249
+ } else if (e.key === "ArrowLeft" || e.key === "PageUp") {
2250
+ if (currentPage > 1) {
2251
+ e.preventDefault();
2252
+ container.scrollTop = 0;
2253
+ renderPage(currentPage - 1);
2254
+ }
2255
+ } else if (e.key === "Home") {
2256
+ e.preventDefault();
2257
+ container.scrollTop = 0;
2258
+ renderPage(1);
2259
+ } else if (e.key === "End") {
2260
+ e.preventDefault();
2261
+ container.scrollTop = 0;
2262
+ renderPage(totalPages);
2263
+ }
2264
+ };
2265
+ window.addEventListener("keydown", onKeyDown);
1705
2266
  const cleanup = () => {
1706
2267
  resizeObserver.disconnect();
2268
+ window.removeEventListener("keydown", onKeyDown);
2269
+ container.removeEventListener("wheel", onWheel);
1707
2270
  if (resizeTimer) clearTimeout(resizeTimer);
1708
2271
  if (currentRenderTask) {
1709
2272
  try {
@@ -1722,22 +2285,26 @@ var PdfPlugin = class {
1722
2285
  const instance = {
1723
2286
  destroy: cleanup,
1724
2287
  zoomIn: () => {
1725
- zoomScale = Math.min(3.5, zoomScale + 0.2);
2288
+ zoomScale = Math.min(4, Math.round((zoomScale + 0.2) * 10) / 10);
1726
2289
  renderPage(currentPage);
1727
2290
  },
1728
2291
  zoomOut: () => {
1729
- zoomScale = Math.max(0.3, zoomScale - 0.2);
2292
+ zoomScale = Math.max(0.25, Math.round((zoomScale - 0.2) * 10) / 10);
1730
2293
  renderPage(currentPage);
1731
2294
  },
1732
2295
  getZoom: () => zoomScale,
1733
2296
  setZoom: (level) => {
1734
- zoomScale = Math.max(0.3, Math.min(3.5, level));
2297
+ zoomScale = Math.max(0.25, Math.min(4, level));
1735
2298
  renderPage(currentPage);
1736
2299
  },
1737
2300
  fitToPage: () => {
1738
- fitMode = fitMode === "page" ? "width" : "page";
2301
+ fitMode = "page";
2302
+ zoomScale = 1;
2303
+ renderPage(currentPage);
2304
+ },
2305
+ fitToWidth: () => {
2306
+ fitMode = "width";
1739
2307
  zoomScale = 1;
1740
- rotation = 0;
1741
2308
  renderPage(currentPage);
1742
2309
  },
1743
2310
  rotateCW: () => {
@@ -1755,6 +2322,9 @@ var PdfPlugin = class {
1755
2322
  container.scrollTop = 0;
1756
2323
  renderPage(page);
1757
2324
  },
2325
+ toggleThumbnails: () => {
2326
+ ctx?.toggleThumbnails?.();
2327
+ },
1758
2328
  download: () => {
1759
2329
  const blob = new Blob([ctx.buffer], { type: "application/pdf" });
1760
2330
  const url = URL.createObjectURL(blob);
@@ -1788,7 +2358,7 @@ var PdfPlugin = class {
1788
2358
  },
1789
2359
  getThumbnails: async () => {
1790
2360
  const thumbnails = [];
1791
- const count = Math.min(totalPages, 50);
2361
+ const count = totalPages;
1792
2362
  for (let i = 1; i <= count; i++) {
1793
2363
  thumbnails.push({
1794
2364
  index: i,
@@ -1796,9 +2366,10 @@ var PdfPlugin = class {
1796
2366
  render: async (thumbCanvas) => {
1797
2367
  try {
1798
2368
  const p = await pdfDoc.getPage(i);
1799
- const baseVp = p.getViewport({ scale: 1 });
1800
- const thumbScale = (thumbCanvas.width || 120) / baseVp.width;
1801
- const thumbVp = p.getViewport({ scale: thumbScale });
2369
+ const baseVp = p.getViewport({ scale: 1, rotation });
2370
+ const targetW = thumbCanvas.width || 130;
2371
+ const thumbScale = targetW / baseVp.width;
2372
+ const thumbVp = p.getViewport({ scale: thumbScale, rotation });
1802
2373
  thumbCanvas.height = Math.floor(thumbVp.height);
1803
2374
  const tCtx = thumbCanvas.getContext("2d");
1804
2375
  if (tCtx) {
@@ -2142,28 +2713,26 @@ var DocxPlugin = class {
2142
2713
  getToolbarActions(instance) {
2143
2714
  const totalPages = instance.getPageCount?.() ?? 1;
2144
2715
  const actions = [];
2145
- if (totalPages > 1) {
2146
- actions.push({
2147
- id: "page-nav",
2148
- icon: "",
2149
- label: "Page Navigation",
2150
- type: "page-nav",
2151
- group: "navigation",
2152
- value: instance.getCurrentPage?.() ?? 1,
2153
- max: totalPages,
2154
- execute: (action, page) => {
2155
- const cur = instance.getCurrentPage?.() ?? 1;
2156
- const max = instance.getPageCount?.() ?? 1;
2157
- if (action === "prev") {
2158
- if (cur > 1) instance.goToPage?.(cur - 1);
2159
- } else if (action === "next") {
2160
- if (cur < max) instance.goToPage?.(cur + 1);
2161
- } else if (typeof page === "number") {
2162
- instance.goToPage?.(page);
2163
- }
2716
+ actions.push({
2717
+ id: "page-nav",
2718
+ icon: "",
2719
+ label: "Page Navigation",
2720
+ type: "page-nav",
2721
+ group: "navigation",
2722
+ value: instance.getCurrentPage?.() ?? 1,
2723
+ max: totalPages,
2724
+ execute: (action, page) => {
2725
+ const cur = instance.getCurrentPage?.() ?? 1;
2726
+ const max = instance.getPageCount?.() ?? 1;
2727
+ if (action === "prev") {
2728
+ if (cur > 1) instance.goToPage?.(cur - 1);
2729
+ } else if (action === "next") {
2730
+ if (cur < max) instance.goToPage?.(cur + 1);
2731
+ } else if (typeof page === "number") {
2732
+ instance.goToPage?.(page);
2164
2733
  }
2165
- });
2166
- }
2734
+ }
2735
+ });
2167
2736
  actions.push(
2168
2737
  {
2169
2738
  id: "zoom-out",
@@ -2229,11 +2798,17 @@ var DocxPlugin = class {
2229
2798
  wrapper.className = "fp-docx-wrapper";
2230
2799
  wrapper.style.transformOrigin = "top center";
2231
2800
  wrapper.style.transition = "transform 0.2s ease";
2232
- wrapper.style.padding = "24px 16px";
2233
- wrapper.style.maxWidth = "950px";
2801
+ wrapper.style.padding = "0";
2802
+ wrapper.style.maxWidth = "none";
2803
+ wrapper.style.width = "816px";
2234
2804
  wrapper.style.margin = "0 auto";
2805
+ wrapper.style.display = "flex";
2806
+ wrapper.style.flexDirection = "column";
2807
+ wrapper.style.alignItems = "center";
2235
2808
  wrapper.style.boxSizing = "border-box";
2236
- ctx.container.style.overflow = "auto";
2809
+ ctx.container.style.overflowX = "hidden";
2810
+ ctx.container.style.overflowY = "auto";
2811
+ ctx.container.style.padding = "16px 8px";
2237
2812
  ctx.container.style.backgroundColor = "#f1f5f9";
2238
2813
  ctx.container.appendChild(wrapper);
2239
2814
  const styleOverride = document.createElement("style");
@@ -2407,39 +2982,13 @@ var DocxPlugin = class {
2407
2982
  const pageElements = sections.length > 0 ? sections : cards;
2408
2983
  const totalPages = Math.max(1, pageElements.length);
2409
2984
  let currentPage = 1;
2410
- let indicator = null;
2411
- if (totalPages > 1) {
2412
- indicator = document.createElement("div");
2413
- indicator.className = "fp-docx-page-indicator";
2414
- indicator.style.position = "sticky";
2415
- indicator.style.bottom = "16px";
2416
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
2417
- indicator.style.backdropFilter = "blur(8px)";
2418
- indicator.style.color = "#f8fafc";
2419
- indicator.style.fontSize = "12px";
2420
- indicator.style.fontWeight = "600";
2421
- indicator.style.padding = "5px 14px";
2422
- indicator.style.borderRadius = "20px";
2423
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
2424
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
2425
- indicator.style.zIndex = "10";
2426
- indicator.style.userSelect = "none";
2427
- indicator.style.pointerEvents = "none";
2428
- indicator.style.textAlign = "center";
2429
- indicator.style.width = "fit-content";
2430
- indicator.style.margin = "16px auto 0";
2431
- ctx.container.appendChild(indicator);
2432
- }
2433
2985
  const showPage = (pageNum) => {
2434
2986
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
2435
2987
  if (pageElements.length > 1) {
2436
2988
  pageElements.forEach((sec, idx) => {
2437
- sec.style.display = idx + 1 === currentPage ? "block" : "none";
2989
+ sec.style.display = idx + 1 === currentPage ? "" : "none";
2438
2990
  });
2439
2991
  }
2440
- if (indicator) {
2441
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
2442
- }
2443
2992
  ctx.container.scrollTop = 0;
2444
2993
  ctx.emit("page-change", { page: currentPage, total: totalPages });
2445
2994
  };
@@ -2448,31 +2997,46 @@ var DocxPlugin = class {
2448
2997
  }
2449
2998
  scale = 1;
2450
2999
  let rotation = 0;
2451
- let fitMode = "width";
2452
- const calculateFitScale = (mode = fitMode) => {
2453
- const activeEl = pageElements[currentPage - 1] || wrapper.firstElementChild || wrapper;
3000
+ let fitMode = "page";
3001
+ let isUserZoomed = false;
3002
+ const calculateFitScale = (_mode = fitMode) => {
3003
+ const activeEl = pageElements[currentPage - 1] || wrapper.querySelector("section.docx") || wrapper;
2454
3004
  const elW = activeEl.offsetWidth || 816;
2455
- const singlePageH = Math.min(activeEl.offsetHeight || 1056, Math.round(elW * 1.32));
2456
- const availW = Math.max(280, ctx.container.clientWidth - 48);
2457
- const availH = Math.max(280, ctx.container.clientHeight - 80);
3005
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
2458
3006
  const sW = availW / elW;
2459
- const sH = availH / singlePageH;
2460
- if (mode === "page") {
2461
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
2462
- }
2463
- return Math.max(0.65, Math.min(1.05, sW));
3007
+ return Math.max(0.35, Math.min(3, sW));
2464
3008
  };
2465
3009
  const applyTransform = () => {
2466
3010
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
2467
3011
  wrapper.style.transformOrigin = "top center";
3012
+ const activeEl = pageElements[currentPage - 1] || wrapper.querySelector("section.docx") || wrapper;
3013
+ const elH = activeEl.offsetHeight || 1056;
3014
+ if (scale > 1) {
3015
+ wrapper.style.marginBottom = `${Math.round(elH * scale - elH + 32)}px`;
3016
+ } else {
3017
+ wrapper.style.marginBottom = "32px";
3018
+ }
2468
3019
  };
3020
+ let resizeObserver = null;
3021
+ if (typeof ResizeObserver !== "undefined") {
3022
+ resizeObserver = new ResizeObserver(() => {
3023
+ if (!isUserZoomed) {
3024
+ const newFit = calculateFitScale("page");
3025
+ if (Math.abs(scale - newFit) > 0.015) {
3026
+ scale = newFit;
3027
+ applyTransform();
3028
+ }
3029
+ }
3030
+ });
3031
+ resizeObserver.observe(ctx.container);
3032
+ }
2469
3033
  setTimeout(() => {
2470
- fitMode = "width";
2471
- scale = calculateFitScale("width");
3034
+ scale = calculateFitScale("page");
2472
3035
  applyTransform();
2473
- }, 60);
3036
+ }, 40);
2474
3037
  const cleanup = () => {
2475
- indicator?.remove();
3038
+ resizeObserver?.disconnect();
3039
+ resizeObserver = null;
2476
3040
  for (const url of createdBlobUrls) {
2477
3041
  URL.revokeObjectURL(url);
2478
3042
  }
@@ -2489,21 +3053,24 @@ var DocxPlugin = class {
2489
3053
  showPage(page);
2490
3054
  },
2491
3055
  zoomIn: () => {
2492
- scale += 0.15;
3056
+ isUserZoomed = true;
3057
+ scale = Math.min(3.5, scale + 0.15);
2493
3058
  applyTransform();
2494
3059
  },
2495
3060
  zoomOut: () => {
3061
+ isUserZoomed = true;
2496
3062
  scale = Math.max(0.2, scale - 0.15);
2497
3063
  applyTransform();
2498
3064
  },
2499
3065
  getZoom: () => scale,
2500
3066
  setZoom: (level) => {
3067
+ isUserZoomed = true;
2501
3068
  scale = level;
2502
3069
  applyTransform();
2503
3070
  },
2504
3071
  fitToPage: () => {
2505
- fitMode = fitMode === "width" ? "page" : "width";
2506
- scale = calculateFitScale(fitMode);
3072
+ isUserZoomed = false;
3073
+ scale = calculateFitScale("page");
2507
3074
  rotation = 0;
2508
3075
  applyTransform();
2509
3076
  },
@@ -2893,7 +3460,7 @@ var ExcelPlugin = class {
2893
3460
  "application/vnd.ms-excel.template.macroEnabled.12",
2894
3461
  "application/vnd.oasis.opendocument.spreadsheet"
2895
3462
  ];
2896
- weight = 80;
3463
+ weight = 85;
2897
3464
  supports(file) {
2898
3465
  const ext = file.metadata.extension?.toLowerCase();
2899
3466
  const mime = file.metadata.mimeType?.toLowerCase();
@@ -2905,6 +3472,14 @@ var ExcelPlugin = class {
2905
3472
  getToolbarActions(instance) {
2906
3473
  const totalSheets = instance.getPageCount?.() ?? 1;
2907
3474
  const actions = [];
3475
+ actions.push({
3476
+ id: "thumbnails",
3477
+ icon: "thumbnails",
3478
+ label: "Sheet Thumbnails",
3479
+ type: "button",
3480
+ group: "navigation",
3481
+ execute: () => instance.toggleThumbnails?.()
3482
+ });
2908
3483
  if (totalSheets > 1) {
2909
3484
  actions.push({
2910
3485
  id: "page-nav",
@@ -2948,26 +3523,6 @@ var ExcelPlugin = class {
2948
3523
  instance.zoomIn?.();
2949
3524
  }
2950
3525
  },
2951
- {
2952
- id: "fit-page",
2953
- icon: "fit-page",
2954
- label: "Fit to View",
2955
- type: "button",
2956
- group: "zoom",
2957
- execute: () => {
2958
- instance.fitToPage?.();
2959
- }
2960
- },
2961
- {
2962
- id: "rotate-cw",
2963
- icon: "rotate-cw",
2964
- label: "Rotate",
2965
- type: "button",
2966
- group: "view",
2967
- execute: () => {
2968
- instance.rotateCW?.();
2969
- }
2970
- },
2971
3526
  {
2972
3527
  id: "download",
2973
3528
  icon: "download",
@@ -2987,6 +3542,16 @@ var ExcelPlugin = class {
2987
3542
  execute: () => {
2988
3543
  instance.print?.();
2989
3544
  }
3545
+ },
3546
+ {
3547
+ id: "open-window",
3548
+ icon: "open-window",
3549
+ label: "Open in Separate Full Window",
3550
+ type: "button",
3551
+ group: "actions",
3552
+ execute: () => {
3553
+ instance.openInSeparateWindow?.();
3554
+ }
2990
3555
  }
2991
3556
  );
2992
3557
  return actions;
@@ -3018,12 +3583,11 @@ var ExcelPlugin = class {
3018
3583
  container.appendChild(tabsArea);
3019
3584
  ctx.container.appendChild(container);
3020
3585
  let scale = 1;
3021
- let rotation = 0;
3022
3586
  let currentSheetIndex = 1;
3023
3587
  let sheetNames = [];
3024
3588
  let wb = null;
3025
3589
  const applyTransform = () => {
3026
- contentArea.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
3590
+ contentArea.style.transform = `scale(${scale})`;
3027
3591
  contentArea.style.transformOrigin = "top left";
3028
3592
  };
3029
3593
  const calculateFitScale = () => {
@@ -3031,7 +3595,7 @@ var ExcelPlugin = class {
3031
3595
  if (!table) return 1;
3032
3596
  const availW = Math.max(280, container.clientWidth - 48);
3033
3597
  const tW = table.offsetWidth || 800;
3034
- return Math.max(0.65, Math.min(1, availW / tW));
3598
+ return Math.max(0.4, Math.min(1, availW / tW));
3035
3599
  };
3036
3600
  try {
3037
3601
  wb = XLSX.read(new Uint8Array(ctx.buffer), { type: "array", cellDates: true });
@@ -3086,7 +3650,16 @@ var ExcelPlugin = class {
3086
3650
  }
3087
3651
  });
3088
3652
  ctx.emit("page-change", { page: currentSheetIndex, total: sheetNames.length });
3653
+ requestAnimationFrame(() => {
3654
+ scale = calculateFitScale();
3655
+ applyTransform();
3656
+ });
3089
3657
  };
3658
+ const ro = new ResizeObserver(() => {
3659
+ scale = calculateFitScale();
3660
+ applyTransform();
3661
+ });
3662
+ ro.observe(container);
3090
3663
  if (sheetNames.length > 0) {
3091
3664
  sheetNames.forEach((name, idx) => {
3092
3665
  const btn = document.createElement("button");
@@ -3113,6 +3686,7 @@ var ExcelPlugin = class {
3113
3686
  `;
3114
3687
  }
3115
3688
  const cleanup = () => {
3689
+ ro.disconnect();
3116
3690
  container.remove();
3117
3691
  ctx.container.innerHTML = "";
3118
3692
  };
@@ -3134,16 +3708,11 @@ var ExcelPlugin = class {
3134
3708
  },
3135
3709
  fitToPage: () => {
3136
3710
  scale = calculateFitScale();
3137
- rotation = 0;
3138
3711
  applyTransform();
3139
3712
  },
3140
3713
  rotateCW: () => {
3141
- rotation = (rotation + 90) % 360;
3142
- applyTransform();
3143
3714
  },
3144
3715
  rotateCCW: () => {
3145
- rotation = (rotation - 90 + 360) % 360;
3146
- applyTransform();
3147
3716
  },
3148
3717
  goToPage: (page) => {
3149
3718
  if (page > 0 && page <= sheetNames.length) {
@@ -3152,6 +3721,63 @@ var ExcelPlugin = class {
3152
3721
  },
3153
3722
  getPageCount: () => sheetNames.length,
3154
3723
  getCurrentPage: () => currentSheetIndex,
3724
+ getThumbnails: () => {
3725
+ return sheetNames.map((name, idx) => ({
3726
+ index: idx,
3727
+ label: name,
3728
+ render: async (canvas) => {
3729
+ canvas.width = 140;
3730
+ canvas.height = 100;
3731
+ const c = canvas.getContext("2d");
3732
+ if (!c) return;
3733
+ c.fillStyle = "#ffffff";
3734
+ c.fillRect(0, 0, canvas.width, canvas.height);
3735
+ c.fillStyle = "#107c41";
3736
+ c.fillRect(0, 0, canvas.width, 16);
3737
+ c.fillStyle = "#ffffff";
3738
+ c.font = 'bold 8px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
3739
+ c.fillText(`\u{1F4CA} ${name.slice(0, 16)}`, 6, 11);
3740
+ const startY = 17;
3741
+ const rowH = 13;
3742
+ const colW = 30;
3743
+ const colHeaders = ["A", "B", "C", "D"];
3744
+ c.fillStyle = "#f1f5f9";
3745
+ c.fillRect(0, startY, canvas.width, rowH);
3746
+ c.strokeStyle = "#cbd5e1";
3747
+ c.lineWidth = 0.8;
3748
+ c.strokeRect(0, startY, canvas.width, rowH);
3749
+ c.fillStyle = "#64748b";
3750
+ c.font = "bold 7px sans-serif";
3751
+ for (let ci = 0; ci < colHeaders.length; ci++) {
3752
+ c.fillText(colHeaders[ci], 14 + ci * colW, startY + 9);
3753
+ }
3754
+ const ws = wb?.Sheets[name];
3755
+ for (let ri = 1; ri <= 5; ri++) {
3756
+ const y = startY + ri * rowH;
3757
+ if (y > canvas.height - 2) break;
3758
+ c.fillStyle = "#f8fafc";
3759
+ c.fillRect(0, y, 12, rowH);
3760
+ c.fillStyle = "#94a3b8";
3761
+ c.font = "6px sans-serif";
3762
+ c.fillText(String(ri), 3, y + 9);
3763
+ c.strokeStyle = "#e2e8f0";
3764
+ c.strokeRect(0, y, canvas.width, rowH);
3765
+ c.fillStyle = "#334155";
3766
+ c.font = "6px sans-serif";
3767
+ for (let ci = 0; ci < colHeaders.length; ci++) {
3768
+ const cellRef = `${colHeaders[ci]}${ri}`;
3769
+ const cellVal = ws?.[cellRef]?.w || ws?.[cellRef]?.v;
3770
+ if (cellVal !== void 0) {
3771
+ c.fillText(String(cellVal).slice(0, 6), 14 + ci * colW, y + 9);
3772
+ }
3773
+ }
3774
+ }
3775
+ c.strokeStyle = "#cbd5e1";
3776
+ c.lineWidth = 1;
3777
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
3778
+ }
3779
+ }));
3780
+ },
3155
3781
  download: () => {
3156
3782
  const blob = new Blob([ctx.buffer], { type: this.mimeTypes[0] });
3157
3783
  const url = URL.createObjectURL(blob);
@@ -3184,7 +3810,16 @@ var CsvPlugin = class {
3184
3810
  return ext === ".csv" || ext === ".tsv" || mime === "text/csv" || mime === "text/tab-separated-values";
3185
3811
  }
3186
3812
  getToolbarActions(instance) {
3187
- return [
3813
+ const actions = [];
3814
+ actions.push({
3815
+ id: "thumbnails",
3816
+ icon: "thumbnails",
3817
+ label: "Sheet Thumbnails",
3818
+ type: "button",
3819
+ group: "navigation",
3820
+ execute: () => instance.toggleThumbnails?.()
3821
+ });
3822
+ actions.push(
3188
3823
  {
3189
3824
  id: "zoom-out",
3190
3825
  icon: "zoom-out",
@@ -3224,8 +3859,19 @@ var CsvPlugin = class {
3224
3859
  execute: () => {
3225
3860
  instance.print?.();
3226
3861
  }
3862
+ },
3863
+ {
3864
+ id: "open-window",
3865
+ icon: "open-window",
3866
+ label: "Open in Separate Full Window",
3867
+ type: "button",
3868
+ group: "actions",
3869
+ execute: () => {
3870
+ instance.openInSeparateWindow?.();
3871
+ }
3227
3872
  }
3228
- ];
3873
+ );
3874
+ return actions;
3229
3875
  }
3230
3876
  async render(ctx) {
3231
3877
  const decoder = new TextDecoder("utf-8");
@@ -3236,57 +3882,156 @@ var CsvPlugin = class {
3236
3882
  container.style.overflow = "auto";
3237
3883
  container.style.padding = "16px";
3238
3884
  container.style.boxSizing = "border-box";
3239
- container.style.transformOrigin = "top left";
3885
+ const tableWrapper = document.createElement("div");
3886
+ tableWrapper.style.transformOrigin = "top left";
3887
+ const delimiter = ctx.metadata.extension === ".tsv" ? " " : ",";
3888
+ const allLines = text.split(/\r?\n/).filter((r) => r.trim().length > 0);
3889
+ const headerLine = allLines[0] || "";
3890
+ const headerCells = headerLine.split(delimiter);
3891
+ const dataLines = allLines.slice(1);
3240
3892
  const table = document.createElement("table");
3241
3893
  table.style.borderCollapse = "collapse";
3242
- table.style.width = "100%";
3243
- table.style.fontFamily = "monospace";
3894
+ table.style.minWidth = "100%";
3895
+ table.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace';
3244
3896
  table.style.fontSize = "13px";
3245
- const delimiter = ctx.metadata.extension === ".tsv" ? " " : ",";
3246
- const rows = text.split(/\r?\n/).slice(0, 500);
3247
- rows.forEach((row, rowIndex) => {
3248
- if (!row.trim()) return;
3897
+ table.style.background = "#ffffff";
3898
+ table.style.border = "1px solid #d0d7de";
3899
+ const headerTr = document.createElement("tr");
3900
+ headerCells.forEach((cell) => {
3901
+ const th = document.createElement("th");
3902
+ th.textContent = cell.trim();
3903
+ th.style.border = "1px solid #d0d7de";
3904
+ th.style.padding = "8px 12px";
3905
+ th.style.backgroundColor = "#f6f8fa";
3906
+ th.style.fontWeight = "600";
3907
+ th.style.whiteSpace = "nowrap";
3908
+ headerTr.appendChild(th);
3909
+ });
3910
+ table.appendChild(headerTr);
3911
+ dataLines.forEach((row, idx) => {
3249
3912
  const tr = document.createElement("tr");
3913
+ if (idx % 2 === 1) tr.style.backgroundColor = "#f8fafc";
3250
3914
  const cells = row.split(delimiter);
3251
3915
  cells.forEach((cell) => {
3252
- const td = document.createElement(rowIndex === 0 ? "th" : "td");
3916
+ const td = document.createElement("td");
3253
3917
  td.textContent = cell.trim();
3254
3918
  td.style.border = "1px solid #d0d7de";
3255
3919
  td.style.padding = "6px 12px";
3256
- if (rowIndex === 0) {
3257
- td.style.backgroundColor = "#f6f8fa";
3258
- td.style.fontWeight = "bold";
3259
- }
3920
+ td.style.whiteSpace = "nowrap";
3260
3921
  tr.appendChild(td);
3261
3922
  });
3262
3923
  table.appendChild(tr);
3263
3924
  });
3264
- container.appendChild(table);
3925
+ tableWrapper.appendChild(table);
3926
+ container.appendChild(tableWrapper);
3265
3927
  ctx.container.appendChild(container);
3266
3928
  let scale = 1;
3929
+ const calculateFitScale = () => {
3930
+ const availW = Math.max(280, container.clientWidth - 48);
3931
+ const tW = table.offsetWidth || 800;
3932
+ return Math.max(0.4, Math.min(1, availW / tW));
3933
+ };
3934
+ const applyScale = () => {
3935
+ tableWrapper.style.transform = `scale(${scale})`;
3936
+ };
3937
+ requestAnimationFrame(() => {
3938
+ scale = calculateFitScale();
3939
+ applyScale();
3940
+ });
3941
+ const ro = new ResizeObserver(() => {
3942
+ scale = calculateFitScale();
3943
+ applyScale();
3944
+ });
3945
+ ro.observe(container);
3267
3946
  const cleanup = () => {
3947
+ ro.disconnect();
3268
3948
  container.remove();
3269
3949
  ctx.container.innerHTML = "";
3270
3950
  };
3271
3951
  ctx.signal.addEventListener("abort", cleanup);
3272
3952
  return {
3273
3953
  destroy: cleanup,
3954
+ getPageCount: () => 1,
3955
+ getCurrentPage: () => 1,
3956
+ goToPage: () => {
3957
+ },
3958
+ getThumbnails: () => {
3959
+ const sheetName = ctx.metadata.name?.replace(/\.[^/.]+$/, "") || "Sheet1";
3960
+ return [{
3961
+ index: 0,
3962
+ label: sheetName,
3963
+ render: async (canvas) => {
3964
+ canvas.width = 140;
3965
+ canvas.height = 100;
3966
+ const c = canvas.getContext("2d");
3967
+ if (!c) return;
3968
+ c.fillStyle = "#ffffff";
3969
+ c.fillRect(0, 0, canvas.width, canvas.height);
3970
+ c.fillStyle = "#107c41";
3971
+ c.fillRect(0, 0, canvas.width, 16);
3972
+ c.fillStyle = "#ffffff";
3973
+ c.font = 'bold 8px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
3974
+ c.fillText(`\u{1F4CA} ${sheetName.slice(0, 16)}`, 6, 11);
3975
+ const startY = 17;
3976
+ const rowH = 13;
3977
+ const colW = 30;
3978
+ const cols = headerCells.slice(0, 4).map((h) => h.trim() || "Col");
3979
+ c.fillStyle = "#f1f5f9";
3980
+ c.fillRect(0, startY, canvas.width, rowH);
3981
+ c.strokeStyle = "#cbd5e1";
3982
+ c.lineWidth = 0.8;
3983
+ c.strokeRect(0, startY, canvas.width, rowH);
3984
+ c.fillStyle = "#64748b";
3985
+ c.font = "bold 7px sans-serif";
3986
+ for (let ci = 0; ci < cols.length; ci++) {
3987
+ c.fillText(cols[ci].slice(0, 5), 14 + ci * colW, startY + 9);
3988
+ }
3989
+ for (let ri = 0; ri < Math.min(5, dataLines.length); ri++) {
3990
+ const y = startY + (ri + 1) * rowH;
3991
+ if (y > canvas.height - 2) break;
3992
+ c.fillStyle = "#f8fafc";
3993
+ c.fillRect(0, y, 12, rowH);
3994
+ c.fillStyle = "#94a3b8";
3995
+ c.font = "6px sans-serif";
3996
+ c.fillText(String(ri + 1), 3, y + 9);
3997
+ c.strokeStyle = "#e2e8f0";
3998
+ c.strokeRect(0, y, canvas.width, rowH);
3999
+ const rowCells = dataLines[ri].split(delimiter);
4000
+ c.fillStyle = "#334155";
4001
+ c.font = "6px sans-serif";
4002
+ for (let ci = 0; ci < cols.length; ci++) {
4003
+ const val = (rowCells[ci] || "").trim();
4004
+ if (val) {
4005
+ c.fillText(val.slice(0, 6), 14 + ci * colW, y + 9);
4006
+ }
4007
+ }
4008
+ }
4009
+ c.strokeStyle = "#cbd5e1";
4010
+ c.lineWidth = 1;
4011
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
4012
+ }
4013
+ }];
4014
+ },
3274
4015
  zoomIn: () => {
3275
4016
  scale += 0.1;
3276
- container.style.transform = `scale(${scale})`;
4017
+ applyScale();
3277
4018
  },
3278
4019
  zoomOut: () => {
3279
4020
  scale = Math.max(0.2, scale - 0.1);
3280
- container.style.transform = `scale(${scale})`;
4021
+ applyScale();
3281
4022
  },
3282
4023
  getZoom: () => scale,
3283
4024
  setZoom: (level) => {
3284
4025
  scale = level;
3285
- container.style.transform = `scale(${scale})`;
4026
+ applyScale();
3286
4027
  },
3287
4028
  fitToPage: () => {
3288
- scale = 1;
3289
- container.style.transform = `scale(1)`;
4029
+ scale = calculateFitScale();
4030
+ applyScale();
4031
+ },
4032
+ rotateCW: () => {
4033
+ },
4034
+ rotateCCW: () => {
3290
4035
  },
3291
4036
  download: () => {
3292
4037
  const blob = new Blob([ctx.buffer], { type: "text/csv" });
@@ -3350,28 +4095,34 @@ var CodePlugin = class {
3350
4095
  getToolbarActions(instance) {
3351
4096
  const totalPages = instance.getPageCount?.() ?? 1;
3352
4097
  const actions = [];
3353
- if (totalPages > 1) {
3354
- actions.push({
3355
- id: "page-nav",
3356
- icon: "",
3357
- label: "Page Navigation",
3358
- type: "page-nav",
3359
- group: "navigation",
3360
- value: instance.getCurrentPage?.() ?? 1,
3361
- max: totalPages,
3362
- execute: (action, page) => {
3363
- const cur = instance.getCurrentPage?.() ?? 1;
3364
- const max = instance.getPageCount?.() ?? 1;
3365
- if (action === "prev") {
3366
- if (cur > 1) instance.goToPage?.(cur - 1);
3367
- } else if (action === "next") {
3368
- if (cur < max) instance.goToPage?.(cur + 1);
3369
- } else if (typeof page === "number") {
3370
- instance.goToPage?.(page);
3371
- }
4098
+ actions.push({
4099
+ id: "thumbnails",
4100
+ icon: "thumbnails",
4101
+ label: "Page Thumbnails",
4102
+ type: "button",
4103
+ group: "navigation",
4104
+ execute: () => instance.toggleThumbnails?.()
4105
+ });
4106
+ actions.push({
4107
+ id: "page-nav",
4108
+ icon: "",
4109
+ label: "Page Navigation",
4110
+ type: "page-nav",
4111
+ group: "navigation",
4112
+ value: instance.getCurrentPage?.() ?? 1,
4113
+ max: totalPages,
4114
+ execute: (action, page) => {
4115
+ const cur = instance.getCurrentPage?.() ?? 1;
4116
+ const max = instance.getPageCount?.() ?? 1;
4117
+ if (action === "prev") {
4118
+ if (cur > 1) instance.goToPage?.(cur - 1);
4119
+ } else if (action === "next") {
4120
+ if (cur < max) instance.goToPage?.(cur + 1);
4121
+ } else if (typeof page === "number") {
4122
+ instance.goToPage?.(page);
3372
4123
  }
3373
- });
3374
- }
4124
+ }
4125
+ });
3375
4126
  actions.push(
3376
4127
  {
3377
4128
  id: "zoom-out",
@@ -3393,26 +4144,6 @@ var CodePlugin = class {
3393
4144
  instance.zoomIn?.();
3394
4145
  }
3395
4146
  },
3396
- {
3397
- id: "fit-page",
3398
- icon: "fit-page",
3399
- label: "Fit to View",
3400
- type: "button",
3401
- group: "zoom",
3402
- execute: () => {
3403
- instance.fitToPage?.();
3404
- }
3405
- },
3406
- {
3407
- id: "rotate-cw",
3408
- icon: "rotate-cw",
3409
- label: "Rotate",
3410
- type: "button",
3411
- group: "view",
3412
- execute: () => {
3413
- instance.rotateCW?.();
3414
- }
3415
- },
3416
4147
  {
3417
4148
  id: "copy",
3418
4149
  icon: "copy",
@@ -3515,16 +4246,19 @@ var CodePlugin = class {
3515
4246
  let fontSize = 13;
3516
4247
  let rotation = 0;
3517
4248
  let zoomLevel = 1;
4249
+ let isUserZoomed = false;
3518
4250
  const pre = document.createElement("pre");
3519
4251
  const code = document.createElement("code");
3520
4252
  if (isTxt) {
4253
+ container.style.overflowX = "hidden";
4254
+ container.style.overflowY = "auto";
3521
4255
  container.style.backgroundColor = "#f1f5f9";
3522
- container.style.padding = "32px";
4256
+ container.style.padding = "16px 8px";
3523
4257
  container.style.boxSizing = "border-box";
3524
4258
  container.style.display = "flex";
3525
4259
  container.style.flexDirection = "column";
3526
4260
  container.style.alignItems = "center";
3527
- pre.style.margin = "0";
4261
+ pre.style.margin = "0 auto";
3528
4262
  pre.style.fontFamily = "Consolas, 'Courier New', monospace";
3529
4263
  pre.style.fontSize = "13px";
3530
4264
  pre.style.color = "#1e293b";
@@ -3536,10 +4270,12 @@ var CodePlugin = class {
3536
4270
  pre.style.minHeight = "1056px";
3537
4271
  pre.style.padding = "72px 56px";
3538
4272
  pre.style.boxSizing = "border-box";
3539
- pre.style.boxShadow = "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)";
4273
+ pre.style.boxShadow = "0 4px 24px rgba(0, 0, 0, 0.08)";
3540
4274
  pre.style.borderRadius = "4px";
3541
4275
  pre.style.transformOrigin = "top center";
4276
+ pre.style.transition = "transform 0.15s ease";
3542
4277
  } else {
4278
+ container.style.overflow = "auto";
3543
4279
  container.style.backgroundColor = "#1e1e1e";
3544
4280
  container.style.color = "#d4d4d4";
3545
4281
  container.style.padding = "16px";
@@ -3571,42 +4307,9 @@ var CodePlugin = class {
3571
4307
  renderCodePage(rawPages[0] || (isTxt ? "" : fullText));
3572
4308
  pre.appendChild(code);
3573
4309
  container.appendChild(pre);
3574
- let indicator = null;
3575
- if (totalPages > 1) {
3576
- indicator = document.createElement("div");
3577
- indicator.className = "fp-code-page-indicator";
3578
- indicator.style.position = "sticky";
3579
- indicator.style.bottom = "16px";
3580
- if (isTxt) {
3581
- indicator.style.backgroundColor = "rgba(255, 255, 255, 0.9)";
3582
- indicator.style.color = "#334155";
3583
- indicator.style.border = "1px solid #e2e8f0";
3584
- indicator.style.boxShadow = "0 1px 3px rgba(0,0,0,0.1)";
3585
- } else {
3586
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
3587
- indicator.style.color = "#f8fafc";
3588
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
3589
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
3590
- indicator.style.backdropFilter = "blur(8px)";
3591
- }
3592
- indicator.style.fontSize = "12px";
3593
- indicator.style.fontWeight = "600";
3594
- indicator.style.padding = "5px 14px";
3595
- indicator.style.borderRadius = "20px";
3596
- indicator.style.zIndex = "10";
3597
- indicator.style.userSelect = "none";
3598
- indicator.style.pointerEvents = "none";
3599
- indicator.style.textAlign = "center";
3600
- indicator.style.width = "fit-content";
3601
- indicator.style.margin = "16px auto 0";
3602
- container.appendChild(indicator);
3603
- }
3604
4310
  const showPage = (pageNum) => {
3605
4311
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
3606
4312
  renderCodePage(rawPages[currentPage - 1] || (isTxt ? "" : fullText));
3607
- if (indicator) {
3608
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
3609
- }
3610
4313
  container.scrollTop = 0;
3611
4314
  ctx.emit("page-change", { page: currentPage, total: totalPages });
3612
4315
  };
@@ -3614,36 +4317,90 @@ var CodePlugin = class {
3614
4317
  showPage(1);
3615
4318
  }
3616
4319
  ctx.container.appendChild(container);
3617
- const cleanup = () => {
3618
- indicator?.remove();
3619
- container.remove();
3620
- ctx.container.innerHTML = "";
4320
+ const calculateTxtFit = () => {
4321
+ const availW = Math.max(280, container.clientWidth - 32);
4322
+ return Math.max(0.4, Math.min(3, availW / 816));
3621
4323
  };
3622
- ctx.signal.addEventListener("abort", cleanup);
3623
4324
  const updateTransform = () => {
3624
4325
  if (isTxt) {
3625
4326
  pre.style.transform = `scale(${zoomLevel}) rotate(${rotation}deg)`;
4327
+ const scaledH = 1056 * zoomLevel;
4328
+ const extraH = Math.max(0, scaledH - 1056);
4329
+ pre.style.marginBottom = `${extraH + 32}px`;
3626
4330
  } else {
3627
4331
  pre.style.transform = `rotate(${rotation}deg)`;
3628
4332
  pre.style.fontSize = `${fontSize}px`;
3629
4333
  }
3630
4334
  };
4335
+ let resizeObserver = null;
4336
+ if (isTxt) {
4337
+ setTimeout(() => {
4338
+ zoomLevel = calculateTxtFit();
4339
+ updateTransform();
4340
+ }, 60);
4341
+ resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
4342
+ if (!isUserZoomed) {
4343
+ zoomLevel = calculateTxtFit();
4344
+ updateTransform();
4345
+ }
4346
+ }) : null;
4347
+ resizeObserver?.observe(container);
4348
+ }
4349
+ const cleanup = () => {
4350
+ resizeObserver?.disconnect();
4351
+ container.remove();
4352
+ ctx.container.innerHTML = "";
4353
+ };
4354
+ ctx.signal.addEventListener("abort", cleanup);
3631
4355
  return {
3632
4356
  destroy: cleanup,
3633
4357
  getPageCount: () => totalPages,
3634
4358
  getCurrentPage: () => currentPage,
3635
4359
  goToPage: (page) => showPage(page),
4360
+ getThumbnails: () => {
4361
+ return rawPages.map((pageText, idx) => ({
4362
+ index: idx,
4363
+ label: `Page ${idx + 1}`,
4364
+ render: async (canvas) => {
4365
+ canvas.width = 140;
4366
+ canvas.height = 180;
4367
+ const c = canvas.getContext("2d");
4368
+ if (!c) return;
4369
+ c.fillStyle = "#ffffff";
4370
+ c.fillRect(0, 0, canvas.width, canvas.height);
4371
+ c.strokeStyle = "#cbd5e1";
4372
+ c.lineWidth = 1;
4373
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
4374
+ c.fillStyle = "#64748b";
4375
+ const lines = (pageText || "").split("\n").slice(0, 24);
4376
+ const lineH = 6;
4377
+ const startY = 14;
4378
+ const startX = 12;
4379
+ const maxW = canvas.width - 24;
4380
+ c.font = "5px monospace";
4381
+ for (let li = 0; li < lines.length; li++) {
4382
+ const l = lines[li].trim();
4383
+ if (!l) continue;
4384
+ const y = startY + li * lineH;
4385
+ if (y > canvas.height - 12) break;
4386
+ c.fillText(l.slice(0, 30), startX, y, maxW);
4387
+ }
4388
+ }
4389
+ }));
4390
+ },
3636
4391
  zoomIn: () => {
4392
+ isUserZoomed = true;
3637
4393
  if (isTxt) {
3638
- zoomLevel = Math.min(3, zoomLevel + 0.1);
4394
+ zoomLevel = Math.min(3.5, zoomLevel + 0.15);
3639
4395
  } else {
3640
4396
  fontSize = Math.min(32, fontSize + 2);
3641
4397
  }
3642
4398
  updateTransform();
3643
4399
  },
3644
4400
  zoomOut: () => {
4401
+ isUserZoomed = true;
3645
4402
  if (isTxt) {
3646
- zoomLevel = Math.max(0.1, zoomLevel - 0.1);
4403
+ zoomLevel = Math.max(0.1, zoomLevel - 0.15);
3647
4404
  } else {
3648
4405
  fontSize = Math.max(8, fontSize - 2);
3649
4406
  }
@@ -3651,6 +4408,7 @@ var CodePlugin = class {
3651
4408
  },
3652
4409
  getZoom: () => isTxt ? zoomLevel : fontSize / 13,
3653
4410
  setZoom: (level) => {
4411
+ isUserZoomed = true;
3654
4412
  if (isTxt) {
3655
4413
  zoomLevel = level;
3656
4414
  } else {
@@ -3659,9 +4417,10 @@ var CodePlugin = class {
3659
4417
  updateTransform();
3660
4418
  },
3661
4419
  fitToPage: () => {
4420
+ isUserZoomed = false;
3662
4421
  fontSize = 13;
3663
4422
  rotation = 0;
3664
- zoomLevel = isTxt ? Math.max(0.65, Math.min(1.05, (container.clientWidth - 48) / 816)) : 1;
4423
+ zoomLevel = isTxt ? calculateTxtFit() : 1;
3665
4424
  updateTransform();
3666
4425
  },
3667
4426
  rotateCW: () => {
@@ -4528,6 +5287,148 @@ var ThreeDPlugin = class {
4528
5287
  function threeDPlugin() {
4529
5288
  return new ThreeDPlugin();
4530
5289
  }
5290
+ function cleanRtfText(raw) {
5291
+ return raw.replace(/\\bin\d+\s/g, "").replace(/{\\\*[^}]+}/g, "").replace(/\\u(-?\d+)\??/g, (_, code) => {
5292
+ let num = parseInt(code, 10);
5293
+ if (num < 0) num += 65536;
5294
+ return String.fromCharCode(num);
5295
+ }).replace(/\\'([0-9a-fA-F]{2})/g, (_, hex) => {
5296
+ return String.fromCharCode(parseInt(hex, 16));
5297
+ }).replace(/\\tab\b/g, " ").replace(/\\emdash\b/g, "\u2014").replace(/\\endash\b/g, "\u2013").replace(/\\bullet\b/g, "\u2022").replace(/\\lquote\b/g, "\u2018").replace(/\\rquote\b/g, "\u2019").replace(/\\ldblquote\b/g, "\u201C").replace(/\\rdblquote\b/g, "\u201D").replace(/\\\w+(?:-?\d+)?\s?/g, " ").replace(/[{}\r\n]/g, " ").replace(/\s+/g, " ").trim();
5298
+ }
5299
+ function parseRtfTables(rtfText) {
5300
+ const rowSegments = rtfText.split(/\\row\b/);
5301
+ if (rowSegments.length <= 1) return [];
5302
+ const rawTables = [];
5303
+ let currentTable = null;
5304
+ let charOffset = 0;
5305
+ for (let i = 0; i < rowSegments.length - 1; i++) {
5306
+ const segment = rowSegments[i];
5307
+ const segmentStart = charOffset;
5308
+ const segmentEnd = charOffset + segment.length + 4;
5309
+ const cellParts = segment.split(/\\cell\b/);
5310
+ if (cellParts.length > 1) {
5311
+ const cellxMatches = [...segment.matchAll(/\\cellx(\d+)/g)].map((m) => parseInt(m[1], 10));
5312
+ let cellWidths = [];
5313
+ if (cellxMatches.length > 0) {
5314
+ let seq = [cellxMatches[0]];
5315
+ for (let k = 1; k < cellxMatches.length; k++) {
5316
+ if (cellxMatches[k] > cellxMatches[k - 1]) seq.push(cellxMatches[k]);
5317
+ else break;
5318
+ }
5319
+ let prev = 0;
5320
+ cellWidths = seq.map((x) => {
5321
+ const w = x - prev;
5322
+ prev = x;
5323
+ return w;
5324
+ });
5325
+ }
5326
+ const cells = [];
5327
+ for (let c = 0; c < cellParts.length - 1; c++) {
5328
+ let rawCell = cellParts[c];
5329
+ if (c === 0) {
5330
+ const lastTrowd = rawCell.lastIndexOf("\\trowd");
5331
+ if (lastTrowd !== -1) rawCell = rawCell.slice(lastTrowd);
5332
+ }
5333
+ cells.push(cleanRtfText(rawCell));
5334
+ }
5335
+ const isNewTable = !currentTable || segmentStart - currentTable.lastEnd > 2500;
5336
+ if (isNewTable) {
5337
+ const prevTextSlice = rtfText.slice(Math.max(0, segmentStart - 4e3), segmentStart);
5338
+ const cleanPrev = cleanRtfText(prevTextSlice);
5339
+ const anchorBefore = cleanPrev.slice(-80).trim();
5340
+ currentTable = {
5341
+ id: `RTF_TABLE_${rawTables.length}`,
5342
+ startPos: segmentStart,
5343
+ lastEnd: segmentEnd,
5344
+ rows: [],
5345
+ columnWidths: cellWidths,
5346
+ anchorBefore,
5347
+ anchorAfter: ""
5348
+ };
5349
+ rawTables.push(currentTable);
5350
+ }
5351
+ if (currentTable) {
5352
+ currentTable.rows.push({
5353
+ cells,
5354
+ widths: cellWidths.length === cells.length ? cellWidths : currentTable.columnWidths
5355
+ });
5356
+ currentTable.lastEnd = segmentEnd;
5357
+ }
5358
+ }
5359
+ charOffset += segment.length + 4;
5360
+ }
5361
+ rawTables.forEach((tbl) => {
5362
+ const afterSlice = rtfText.slice(tbl.lastEnd, Math.min(rtfText.length, tbl.lastEnd + 2e3));
5363
+ const cleanAfter = cleanRtfText(afterSlice);
5364
+ tbl.anchorAfter = cleanAfter.slice(0, 80).trim();
5365
+ });
5366
+ return rawTables.map((tbl) => {
5367
+ const totalW = (tbl.columnWidths || []).reduce((a, b) => a + b, 0);
5368
+ const colPercents = totalW > 0 ? tbl.columnWidths.map((w) => Math.round(w / totalW * 100)) : [];
5369
+ return {
5370
+ id: tbl.id,
5371
+ rows: tbl.rows.map((r) => r.cells),
5372
+ colPercents,
5373
+ anchorBefore: tbl.anchorBefore,
5374
+ anchorAfter: tbl.anchorAfter
5375
+ };
5376
+ });
5377
+ }
5378
+ function renderRtfTableElement(table) {
5379
+ const tableEl = document.createElement("table");
5380
+ tableEl.className = "fp-rtf-table";
5381
+ tableEl.style.width = "100%";
5382
+ tableEl.style.borderCollapse = "collapse";
5383
+ tableEl.style.margin = "16px 0 20px";
5384
+ tableEl.style.fontSize = "10pt";
5385
+ tableEl.style.lineHeight = "1.5";
5386
+ tableEl.style.border = "1px solid #cbd5e1";
5387
+ tableEl.style.borderRadius = "4px";
5388
+ tableEl.style.boxSizing = "border-box";
5389
+ tableEl.style.backgroundColor = "#ffffff";
5390
+ const thead = document.createElement("thead");
5391
+ const tbody = document.createElement("tbody");
5392
+ table.rows.forEach((rowCells, rIdx) => {
5393
+ const tr = document.createElement("tr");
5394
+ const isHeader = rIdx === 0;
5395
+ if (isHeader) {
5396
+ tr.style.backgroundColor = "#f8fafc";
5397
+ tr.style.borderBottom = "2px solid #cbd5e1";
5398
+ } else {
5399
+ tr.style.borderBottom = "1px solid #e2e8f0";
5400
+ if (rIdx % 2 === 0) {
5401
+ tr.style.backgroundColor = "#fcfdfe";
5402
+ }
5403
+ }
5404
+ rowCells.forEach((cellText, cIdx) => {
5405
+ const cellEl = document.createElement(isHeader ? "th" : "td");
5406
+ cellEl.style.padding = "8px 12px";
5407
+ cellEl.style.border = "1px solid #cbd5e1";
5408
+ cellEl.style.verticalAlign = "top";
5409
+ cellEl.style.color = isHeader ? "#0f172a" : "#1e293b";
5410
+ cellEl.style.fontWeight = isHeader ? "600" : "normal";
5411
+ if (table.colPercents && table.colPercents[cIdx]) {
5412
+ cellEl.style.width = `${table.colPercents[cIdx]}%`;
5413
+ }
5414
+ if (/^\d+$/.test(cellText.trim()) || cellText.trim() === "#") {
5415
+ cellEl.style.textAlign = "center";
5416
+ } else {
5417
+ cellEl.style.textAlign = "left";
5418
+ }
5419
+ cellEl.textContent = cellText;
5420
+ tr.appendChild(cellEl);
5421
+ });
5422
+ if (isHeader) {
5423
+ thead.appendChild(tr);
5424
+ } else {
5425
+ tbody.appendChild(tr);
5426
+ }
5427
+ });
5428
+ tableEl.appendChild(thead);
5429
+ tableEl.appendChild(tbody);
5430
+ return tableEl;
5431
+ }
4531
5432
  var RtfPlugin = class {
4532
5433
  id = "rtf";
4533
5434
  name = "Rich Text Format (RTF)";
@@ -4542,29 +5443,35 @@ var RtfPlugin = class {
4542
5443
  getToolbarActions(instance) {
4543
5444
  const totalPages = instance.getPageCount?.() ?? 1;
4544
5445
  const actions = [];
4545
- if (totalPages > 1) {
4546
- actions.push({
4547
- id: "page-nav",
4548
- icon: "",
4549
- label: "Page Navigation",
4550
- type: "page-nav",
4551
- group: "navigation",
4552
- value: instance.getCurrentPage?.() ?? 1,
4553
- max: totalPages,
4554
- execute: (action, page) => {
4555
- const cur = instance.getCurrentPage?.() ?? 1;
4556
- const max = instance.getPageCount?.() ?? 1;
4557
- if (action === "prev") {
4558
- if (cur > 1) instance.goToPage?.(cur - 1);
4559
- } else if (action === "next") {
4560
- if (cur < max) instance.goToPage?.(cur + 1);
4561
- } else if (typeof page === "number") {
4562
- instance.goToPage?.(page);
4563
- }
5446
+ actions.push({
5447
+ id: "page-nav",
5448
+ icon: "",
5449
+ label: "Page Navigation",
5450
+ type: "page-nav",
5451
+ group: "navigation",
5452
+ value: instance.getCurrentPage?.() ?? 1,
5453
+ max: totalPages,
5454
+ execute: (action, page) => {
5455
+ const cur = instance.getCurrentPage?.() ?? 1;
5456
+ const max = instance.getPageCount?.() ?? 1;
5457
+ if (action === "prev") {
5458
+ if (cur > 1) instance.goToPage?.(cur - 1);
5459
+ } else if (action === "next") {
5460
+ if (cur < max) instance.goToPage?.(cur + 1);
5461
+ } else if (typeof page === "number") {
5462
+ instance.goToPage?.(page);
4564
5463
  }
4565
- });
4566
- }
5464
+ }
5465
+ });
4567
5466
  actions.push(
5467
+ {
5468
+ id: "thumbnails",
5469
+ icon: "thumbnails",
5470
+ label: "Thumbnails",
5471
+ type: "button",
5472
+ group: "view",
5473
+ execute: () => instance.toggleThumbnails?.()
5474
+ },
4568
5475
  {
4569
5476
  id: "zoom-out",
4570
5477
  icon: "zoom-out",
@@ -4635,67 +5542,143 @@ var RtfPlugin = class {
4635
5542
  async render(ctx) {
4636
5543
  const wrapper = document.createElement("div");
4637
5544
  wrapper.className = "fp-rtf-wrapper";
4638
- wrapper.style.padding = "32px";
4639
- wrapper.style.maxWidth = "850px";
5545
+ wrapper.style.padding = "0";
5546
+ wrapper.style.width = "816px";
4640
5547
  wrapper.style.margin = "0 auto";
4641
- wrapper.style.backgroundColor = "#fff";
4642
- wrapper.style.boxShadow = "0 2px 8px rgba(0,0,0,0.08)";
4643
- wrapper.style.borderRadius = "4px";
4644
- wrapper.style.minHeight = "100%";
5548
+ wrapper.style.boxSizing = "border-box";
5549
+ wrapper.style.display = "flex";
5550
+ wrapper.style.flexDirection = "column";
5551
+ wrapper.style.alignItems = "center";
5552
+ wrapper.style.backgroundColor = "transparent";
4645
5553
  wrapper.style.transformOrigin = "top center";
4646
- wrapper.style.transition = "transform 0.2s ease";
4647
- ctx.container.style.overflow = "auto";
4648
- ctx.container.style.padding = "24px";
5554
+ wrapper.style.transition = "transform 0.15s ease";
5555
+ ctx.container.style.overflowX = "hidden";
5556
+ ctx.container.style.overflowY = "auto";
5557
+ ctx.container.style.padding = "16px 8px";
4649
5558
  ctx.container.style.backgroundColor = "#f1f5f9";
4650
5559
  ctx.container.appendChild(wrapper);
4651
5560
  let scale = 1;
4652
5561
  let rotation = 0;
4653
5562
  let pageElements = [];
4654
- let fitMode = "width";
5563
+ let isUserZoomed = false;
5564
+ let fitMode = ctx?.options?.fitMode || "width";
4655
5565
  const calculateFitScale = (mode = fitMode) => {
4656
5566
  const activeEl = pageElements[currentPage - 1] || wrapper;
4657
- const elW = activeEl.offsetWidth || 850;
4658
- const singlePageH = Math.min(activeEl.offsetHeight || 1056, Math.round(elW * 1.32));
4659
- const availW = Math.max(280, ctx.container.clientWidth - 48);
4660
- const availH = Math.max(280, ctx.container.clientHeight - 80);
5567
+ const elW = 816;
5568
+ const singlePageH = activeEl?.offsetHeight || 1056;
5569
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
5570
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
4661
5571
  const sW = availW / elW;
4662
5572
  const sH = availH / singlePageH;
4663
5573
  if (mode === "page") {
4664
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
5574
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
4665
5575
  }
4666
- return Math.max(0.65, Math.min(1.05, sW));
5576
+ return Math.max(0.4, Math.min(3, sW));
4667
5577
  };
4668
5578
  const applyTransform = () => {
4669
5579
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
4670
5580
  wrapper.style.transformOrigin = "top center";
5581
+ const activeEl = pageElements[currentPage - 1];
5582
+ const baseH = activeEl?.offsetHeight || 1056;
5583
+ const scaledH = baseH * scale;
5584
+ const extraH = Math.max(0, scaledH - baseH);
5585
+ wrapper.style.marginBottom = `${extraH + 32}px`;
4671
5586
  };
5587
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
5588
+ if (!isUserZoomed) {
5589
+ scale = calculateFitScale(fitMode);
5590
+ applyTransform();
5591
+ }
5592
+ }) : null;
5593
+ resizeObserver?.observe(ctx.container);
4672
5594
  setTimeout(() => {
4673
- fitMode = "width";
4674
- scale = calculateFitScale("width");
5595
+ scale = calculateFitScale(fitMode);
4675
5596
  applyTransform();
4676
5597
  }, 60);
4677
5598
  try {
4678
5599
  if (typeof RTFJS.loggingEnabled === "function") {
4679
5600
  RTFJS.loggingEnabled(false);
4680
5601
  }
5602
+ const rawText = new TextDecoder("latin1").decode(ctx.buffer);
5603
+ const tables = parseRtfTables(rawText);
4681
5604
  const doc = new RTFJS.Document(ctx.buffer, {});
4682
5605
  const htmlElements = await doc.render();
4683
5606
  const contentNodes = [];
4684
- for (const item of htmlElements) {
4685
- if (item.children && item.children.length > 0 && !item.tagName.toLowerCase().startsWith("table")) {
4686
- contentNodes.push(...Array.from(item.children));
4687
- } else {
4688
- contentNodes.push(item);
5607
+ const extractBlocks = (nodes) => {
5608
+ for (const item of nodes) {
5609
+ if (!item || !(item instanceof HTMLElement)) continue;
5610
+ const tag = item.tagName.toLowerCase();
5611
+ const hasChildBlocks = Array.from(item.children).some((c) => {
5612
+ const ct = c.tagName.toLowerCase();
5613
+ return ["p", "div", "table", "h1", "h2", "h3", "h4", "h5", "h6", "section", "article", "ul", "ol"].includes(ct);
5614
+ });
5615
+ if (hasChildBlocks && !["table", "tr", "td", "th", "ul", "ol", "li"].includes(tag)) {
5616
+ extractBlocks(Array.from(item.children));
5617
+ } else {
5618
+ const svgs = item.querySelectorAll("svg, img");
5619
+ svgs.forEach((s) => {
5620
+ const el = s;
5621
+ el.style.maxWidth = "100%";
5622
+ el.style.maxHeight = "360px";
5623
+ el.style.display = "block";
5624
+ el.style.margin = "12px auto";
5625
+ if (s.tagName.toLowerCase() === "svg") {
5626
+ s.removeAttribute("width");
5627
+ s.removeAttribute("height");
5628
+ el.style.width = "100%";
5629
+ el.style.maxHeight = "360px";
5630
+ }
5631
+ });
5632
+ if (tag === "p" || tag === "div") {
5633
+ item.style.display = "block";
5634
+ item.style.marginBottom = "12px";
5635
+ item.style.lineHeight = "1.6";
5636
+ }
5637
+ contentNodes.push(item);
5638
+ }
4689
5639
  }
4690
- }
5640
+ };
5641
+ extractBlocks(htmlElements);
5642
+ tables.forEach((table) => {
5643
+ const tableEl = renderRtfTableElement(table);
5644
+ let inserted = false;
5645
+ if (table.anchorBefore && table.anchorBefore.length > 10) {
5646
+ const keyword = table.anchorBefore.slice(-35).trim();
5647
+ for (let i = 0; i < contentNodes.length; i++) {
5648
+ if (contentNodes[i].textContent?.includes(keyword)) {
5649
+ contentNodes.splice(i + 1, 0, tableEl);
5650
+ inserted = true;
5651
+ break;
5652
+ }
5653
+ }
5654
+ }
5655
+ if (!inserted && table.anchorAfter && table.anchorAfter.length > 10) {
5656
+ const keyword = table.anchorAfter.slice(0, 35).trim();
5657
+ for (let i = 0; i < contentNodes.length; i++) {
5658
+ if (contentNodes[i].textContent?.includes(keyword)) {
5659
+ contentNodes.splice(i, 0, tableEl);
5660
+ inserted = true;
5661
+ break;
5662
+ }
5663
+ }
5664
+ }
5665
+ if (!inserted) {
5666
+ if (contentNodes.length > 2) {
5667
+ contentNodes.splice(2, 0, tableEl);
5668
+ } else {
5669
+ contentNodes.push(tableEl);
5670
+ }
5671
+ }
5672
+ });
4691
5673
  wrapper.innerHTML = "";
4692
5674
  contentNodes.forEach((node) => wrapper.appendChild(node));
4693
5675
  const childHeights = contentNodes.map((c) => {
4694
5676
  const rectH = c.getBoundingClientRect ? c.getBoundingClientRect().height : 0;
4695
5677
  const offH = c.offsetHeight || 0;
5678
+ const realH = Math.max(rectH, offH);
5679
+ if (realH > 0) return realH;
4696
5680
  const textLen = c.textContent?.trim().length || 0;
4697
- const estH = Math.max(24, Math.ceil(textLen / 75) * 22 + 14);
4698
- return Math.max(rectH, offH, estH);
5681
+ return Math.max(24, Math.ceil(textLen / 95) * 22 + 12);
4699
5682
  });
4700
5683
  wrapper.innerHTML = "";
4701
5684
  const createRtfCard = () => {
@@ -4704,35 +5687,49 @@ var RtfPlugin = class {
4704
5687
  card.style.backgroundColor = "#ffffff";
4705
5688
  card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
4706
5689
  card.style.borderRadius = "4px";
4707
- card.style.padding = "72px 56px";
5690
+ card.style.padding = "64px 56px";
4708
5691
  card.style.width = "816px";
4709
5692
  card.style.minHeight = "1056px";
4710
5693
  card.style.boxSizing = "border-box";
4711
5694
  card.style.marginBottom = "24px";
4712
5695
  card.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
4713
5696
  card.style.lineHeight = "1.6";
5697
+ card.style.color = "#1e293b";
4714
5698
  return card;
4715
5699
  };
4716
5700
  let curCard = createRtfCard();
4717
5701
  wrapper.appendChild(curCard);
4718
5702
  pageElements = [curCard];
4719
5703
  let curH = 0;
4720
- const maxH = 912;
5704
+ const maxH = 928;
4721
5705
  for (let i = 0; i < contentNodes.length; i++) {
4722
5706
  const child = contentNodes[i];
4723
5707
  const chH = childHeights[i];
4724
- curCard.appendChild(child);
4725
- curH += chH;
4726
- if (curH >= maxH && i < contentNodes.length - 1) {
5708
+ const isChildEmpty = (child.textContent || "").trim().length === 0 && !child.querySelector("table, img, svg, canvas");
5709
+ if (curH === 0 && isChildEmpty) {
5710
+ continue;
5711
+ }
5712
+ if (curH + chH > maxH && curCard.childNodes.length > 0) {
4727
5713
  curCard = createRtfCard();
4728
5714
  wrapper.appendChild(curCard);
4729
5715
  pageElements.push(curCard);
4730
5716
  curH = 0;
4731
5717
  }
5718
+ curCard.appendChild(child);
5719
+ curH += chH;
5720
+ }
5721
+ pageElements = pageElements.filter((card) => {
5722
+ const hasText = (card.textContent || "").trim().length > 0;
5723
+ const hasMedia = card.querySelector("table, img, svg, canvas") !== null;
5724
+ return hasText || hasMedia;
5725
+ });
5726
+ if (pageElements.length === 0) {
5727
+ pageElements = [curCard];
4732
5728
  }
4733
5729
  } catch (err) {
4734
5730
  console.warn("[RtfPlugin] RTF render error, fallback text:", err);
4735
5731
  const text = new TextDecoder("latin1").decode(ctx.buffer);
5732
+ const tables = parseRtfTables(text);
4736
5733
  const rawPages = text.split(/\\page\b/).map((segment) => {
4737
5734
  return segment.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim();
4738
5735
  }).filter((p) => p.length > 0);
@@ -4743,47 +5740,26 @@ var RtfPlugin = class {
4743
5740
  pageCard.style.backgroundColor = "#ffffff";
4744
5741
  pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4745
5742
  pageCard.style.borderRadius = "4px";
4746
- pageCard.style.padding = "72px 56px";
5743
+ pageCard.style.padding = "64px 56px";
4747
5744
  pageCard.style.width = "816px";
4748
5745
  pageCard.style.minHeight = "1056px";
4749
- pageCard.style.maxWidth = "100%";
4750
5746
  pageCard.style.boxSizing = "border-box";
4751
- pageCard.style.fontFamily = "serif";
5747
+ pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
4752
5748
  pageCard.style.fontSize = "12pt";
4753
5749
  pageCard.style.lineHeight = "1.6";
4754
5750
  pageCard.style.color = "#1e293b";
4755
5751
  pageCard.style.whiteSpace = "pre-wrap";
4756
5752
  pageCard.style.display = i === 0 ? "block" : "none";
4757
5753
  pageCard.textContent = pages[i];
5754
+ if (i === 1 && tables.length > 0) {
5755
+ tables.forEach((tbl) => pageCard.appendChild(renderRtfTableElement(tbl)));
5756
+ }
4758
5757
  wrapper.appendChild(pageCard);
4759
5758
  pageElements.push(pageCard);
4760
5759
  }
4761
5760
  }
4762
5761
  const totalPages = Math.max(1, pageElements.length);
4763
5762
  let currentPage = 1;
4764
- let indicator = null;
4765
- if (totalPages > 1) {
4766
- indicator = document.createElement("div");
4767
- indicator.className = "fp-rtf-page-indicator";
4768
- indicator.style.position = "sticky";
4769
- indicator.style.bottom = "16px";
4770
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
4771
- indicator.style.backdropFilter = "blur(8px)";
4772
- indicator.style.color = "#f8fafc";
4773
- indicator.style.fontSize = "12px";
4774
- indicator.style.fontWeight = "600";
4775
- indicator.style.padding = "5px 14px";
4776
- indicator.style.borderRadius = "20px";
4777
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
4778
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
4779
- indicator.style.zIndex = "10";
4780
- indicator.style.userSelect = "none";
4781
- indicator.style.pointerEvents = "none";
4782
- indicator.style.textAlign = "center";
4783
- indicator.style.width = "fit-content";
4784
- indicator.style.margin = "16px auto 0";
4785
- ctx.container.appendChild(indicator);
4786
- }
4787
5763
  const showPage = (pageNum) => {
4788
5764
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
4789
5765
  if (totalPages > 1) {
@@ -4791,9 +5767,6 @@ var RtfPlugin = class {
4791
5767
  el.style.display = idx + 1 === currentPage ? "block" : "none";
4792
5768
  });
4793
5769
  }
4794
- if (indicator) {
4795
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4796
- }
4797
5770
  ctx.container.scrollTop = 0;
4798
5771
  ctx.emit("page-change", { page: currentPage, total: totalPages });
4799
5772
  };
@@ -4801,7 +5774,7 @@ var RtfPlugin = class {
4801
5774
  showPage(1);
4802
5775
  }
4803
5776
  const cleanup = () => {
4804
- indicator?.remove();
5777
+ resizeObserver?.disconnect();
4805
5778
  wrapper.remove();
4806
5779
  ctx.container.innerHTML = "";
4807
5780
  };
@@ -4811,22 +5784,52 @@ var RtfPlugin = class {
4811
5784
  getPageCount: () => totalPages,
4812
5785
  getCurrentPage: () => currentPage,
4813
5786
  goToPage: (page) => showPage(page),
5787
+ getThumbnails: async () => {
5788
+ const items = [];
5789
+ for (let idx = 0; idx < totalPages; idx++) {
5790
+ const pageIdx = idx;
5791
+ items.push({
5792
+ index: pageIdx,
5793
+ label: `Page ${pageIdx + 1}`,
5794
+ render: async (canvas) => {
5795
+ const ctx2d = canvas.getContext("2d");
5796
+ if (!ctx2d) return;
5797
+ canvas.width = 140;
5798
+ canvas.height = 180;
5799
+ ctx2d.fillStyle = "#ffffff";
5800
+ ctx2d.fillRect(0, 0, 140, 180);
5801
+ ctx2d.strokeStyle = "#cbd5e1";
5802
+ ctx2d.lineWidth = 1;
5803
+ ctx2d.strokeRect(0.5, 0.5, 139, 179);
5804
+ ctx2d.fillStyle = "#334155";
5805
+ ctx2d.font = 'bold 11px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
5806
+ ctx2d.textAlign = "center";
5807
+ ctx2d.fillText(`Page ${pageIdx + 1}`, 70, 95);
5808
+ }
5809
+ });
5810
+ }
5811
+ return items;
5812
+ },
4814
5813
  zoomIn: () => {
5814
+ isUserZoomed = true;
4815
5815
  scale += 0.15;
4816
5816
  applyTransform();
4817
5817
  },
4818
5818
  zoomOut: () => {
5819
+ isUserZoomed = true;
4819
5820
  scale = Math.max(0.2, scale - 0.15);
4820
5821
  applyTransform();
4821
5822
  },
4822
5823
  getZoom: () => scale,
4823
5824
  setZoom: (level) => {
5825
+ isUserZoomed = true;
4824
5826
  scale = level;
4825
5827
  applyTransform();
4826
5828
  },
4827
5829
  fitToPage: () => {
4828
- fitMode = fitMode === "width" ? "page" : "width";
4829
- scale = calculateFitScale(fitMode);
5830
+ isUserZoomed = false;
5831
+ fitMode = "width";
5832
+ scale = calculateFitScale("width");
4830
5833
  rotation = 0;
4831
5834
  applyTransform();
4832
5835
  },
@@ -5014,40 +6017,36 @@ var OpenDocumentPlugin = class {
5014
6017
  const isPresentation = instance.isPresentation;
5015
6018
  const totalPages = instance.getPageCount?.() ?? 1;
5016
6019
  const actions = [];
5017
- if (isPresentation || totalPages > 1) {
5018
- if (isPresentation) {
5019
- actions.push({
5020
- id: "thumbnails",
5021
- icon: "thumbnails",
5022
- label: "Slide Thumbnails",
5023
- type: "button",
5024
- group: "navigation",
5025
- execute: () => instance.toggleThumbnails?.()
5026
- });
5027
- }
5028
- actions.push({
5029
- id: "page-nav",
5030
- icon: "",
5031
- label: isPresentation ? "Slide Navigation" : "Page Navigation",
5032
- type: "page-nav",
5033
- group: "navigation",
5034
- value: instance.getCurrentPage?.() ?? 1,
5035
- max: totalPages,
5036
- execute: (action, page) => {
5037
- const cur = instance.getCurrentPage?.() ?? 1;
5038
- const max = instance.getPageCount?.() ?? 1;
5039
- if (action === "prev") {
5040
- if (cur > 1) instance.goToPage?.(cur - 1);
5041
- } else if (action === "next") {
5042
- if (cur < max) instance.goToPage?.(cur + 1);
5043
- } else if (typeof page === "number") {
5044
- instance.goToPage?.(page);
5045
- } else if (typeof action === "number") {
5046
- instance.goToPage?.(action);
5047
- }
6020
+ actions.push({
6021
+ id: "thumbnails",
6022
+ icon: "thumbnails",
6023
+ label: isPresentation ? "Slide Thumbnails" : "Page Thumbnails",
6024
+ type: "button",
6025
+ group: "navigation",
6026
+ execute: () => instance.toggleThumbnails?.()
6027
+ });
6028
+ actions.push({
6029
+ id: "page-nav",
6030
+ icon: "",
6031
+ label: isPresentation ? "Slide Navigation" : "Page Navigation",
6032
+ type: "page-nav",
6033
+ group: "navigation",
6034
+ value: instance.getCurrentPage?.() ?? 1,
6035
+ max: totalPages,
6036
+ execute: (action, page) => {
6037
+ const cur = instance.getCurrentPage?.() ?? 1;
6038
+ const max = instance.getPageCount?.() ?? 1;
6039
+ if (action === "prev") {
6040
+ if (cur > 1) instance.goToPage?.(cur - 1);
6041
+ } else if (action === "next") {
6042
+ if (cur < max) instance.goToPage?.(cur + 1);
6043
+ } else if (typeof page === "number") {
6044
+ instance.goToPage?.(page);
6045
+ } else if (typeof action === "number") {
6046
+ instance.goToPage?.(action);
5048
6047
  }
5049
- });
5050
- }
6048
+ }
6049
+ });
5051
6050
  actions.push(
5052
6051
  {
5053
6052
  id: "zoom-out",
@@ -5138,49 +6137,65 @@ var OpenDocumentPlugin = class {
5138
6137
  container.className = "fp-odf-container";
5139
6138
  container.style.width = "100%";
5140
6139
  container.style.height = "100%";
5141
- container.style.overflow = "auto";
5142
- container.style.padding = "24px";
6140
+ container.style.overflowX = "hidden";
6141
+ container.style.overflowY = "auto";
6142
+ container.style.padding = "16px 8px";
5143
6143
  container.style.backgroundColor = "#f1f5f9";
5144
6144
  const wrapper = document.createElement("div");
5145
6145
  wrapper.className = "fp-odf-wrapper";
5146
6146
  wrapper.style.margin = "0 auto";
6147
+ wrapper.style.width = isPresentation ? "960px" : "816px";
6148
+ wrapper.style.boxSizing = "border-box";
5147
6149
  wrapper.style.backgroundColor = "#ffffff";
5148
6150
  wrapper.style.boxShadow = "0 2px 10px rgba(0,0,0,0.08)";
5149
6151
  wrapper.style.borderRadius = "4px";
5150
6152
  wrapper.style.transformOrigin = "top center";
5151
- wrapper.style.transition = "transform 0.2s ease";
6153
+ wrapper.style.transition = "transform 0.15s ease";
5152
6154
  container.appendChild(wrapper);
5153
6155
  ctx.container.appendChild(container);
5154
6156
  let scale = 1;
5155
6157
  let rotation = 0;
5156
- let fitMode = "width";
6158
+ let isUserZoomed = false;
6159
+ let fitMode = ctx?.options?.fitMode || "width";
6160
+ let currentPage = 1;
6161
+ let totalPages = 1;
6162
+ let slides = [];
5157
6163
  const calculateFitScale = (mode = fitMode) => {
5158
- const elW = wrapper.offsetWidth || 850;
5159
- const singlePageH = Math.min(wrapper.offsetHeight || 1056, Math.round(elW * 1.32));
5160
- const availW = Math.max(280, ctx.container.clientWidth - 48);
5161
- const availH = Math.max(280, ctx.container.clientHeight - 80);
6164
+ const activeSlide = slides[currentPage - 1];
6165
+ const elW = isPresentation ? 960 : 816;
6166
+ const singlePageH = Math.min(activeSlide?.offsetHeight || wrapper.offsetHeight || 1056, Math.round(elW * 1.32));
6167
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
6168
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
5162
6169
  const sW = availW / elW;
5163
6170
  const sH = availH / (isPresentation ? 540 : singlePageH);
5164
6171
  if (isPresentation) {
5165
- return Math.min(1, Math.min(sW, sH));
6172
+ return Math.min(2.5, Math.min(sW, sH));
5166
6173
  }
5167
6174
  if (mode === "page") {
5168
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
6175
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
5169
6176
  }
5170
- return Math.max(0.65, Math.min(1.05, sW));
6177
+ return Math.max(0.4, Math.min(3, sW));
5171
6178
  };
5172
6179
  const applyTransform = () => {
5173
6180
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5174
6181
  wrapper.style.transformOrigin = "top center";
6182
+ const activeSlide = slides[currentPage - 1];
6183
+ const baseH = activeSlide?.offsetHeight || wrapper.offsetHeight || 1056;
6184
+ const scaledH = baseH * scale;
6185
+ const extraH = Math.max(0, scaledH - baseH);
6186
+ wrapper.style.marginBottom = `${extraH + 32}px`;
5175
6187
  };
6188
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
6189
+ if (!isUserZoomed) {
6190
+ scale = calculateFitScale(fitMode);
6191
+ applyTransform();
6192
+ }
6193
+ }) : null;
6194
+ resizeObserver?.observe(ctx.container);
5176
6195
  setTimeout(() => {
5177
- fitMode = "width";
5178
- scale = calculateFitScale("width");
6196
+ scale = calculateFitScale(fitMode);
5179
6197
  applyTransform();
5180
6198
  }, 60);
5181
- let currentPage = 1;
5182
- let totalPages = 1;
5183
- let slides = [];
5184
6199
  if (isPresentation) {
5185
6200
  wrapper.style.maxWidth = "960px";
5186
6201
  wrapper.style.aspectRatio = "16 / 9";
@@ -5281,24 +6296,9 @@ var OpenDocumentPlugin = class {
5281
6296
  wrapper.appendChild(page);
5282
6297
  slides.push(page);
5283
6298
  });
5284
- const pageIndicator = document.createElement("div");
5285
- pageIndicator.className = "fp-odt-page-indicator";
5286
- pageIndicator.style.position = "sticky";
5287
- pageIndicator.style.bottom = "16px";
5288
- pageIndicator.style.left = "50%";
5289
- pageIndicator.style.transform = "translateX(-50%)";
5290
- pageIndicator.style.backgroundColor = "rgba(0, 0, 0, 0.6)";
5291
- pageIndicator.style.color = "#fff";
5292
- pageIndicator.style.padding = "6px 12px";
5293
- pageIndicator.style.borderRadius = "16px";
5294
- pageIndicator.style.fontSize = "12px";
5295
- pageIndicator.style.zIndex = "100";
5296
- pageIndicator.style.display = "inline-block";
5297
- pageIndicator.style.width = "fit-content";
5298
- pageIndicator.textContent = `Page 1 of ${totalPages}`;
5299
- container.appendChild(pageIndicator);
5300
6299
  }
5301
6300
  const cleanup = () => {
6301
+ resizeObserver?.disconnect();
5302
6302
  imageUrls.forEach((url) => URL.revokeObjectURL(url));
5303
6303
  container.remove();
5304
6304
  ctx.container.innerHTML = "";
@@ -5310,10 +6310,6 @@ var OpenDocumentPlugin = class {
5310
6310
  slides.forEach((s, idx) => {
5311
6311
  s.style.display = idx === page - 1 ? "block" : "none";
5312
6312
  });
5313
- const indicator = container.querySelector(".fp-odt-page-indicator");
5314
- if (indicator) {
5315
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
5316
- }
5317
6313
  container.scrollTop = 0;
5318
6314
  ctx.emit("page-change", { page: currentPage, total: totalPages });
5319
6315
  };
@@ -5321,21 +6317,25 @@ var OpenDocumentPlugin = class {
5321
6317
  destroy: cleanup,
5322
6318
  isPresentation,
5323
6319
  zoomIn: () => {
6320
+ isUserZoomed = true;
5324
6321
  scale += 0.15;
5325
6322
  applyTransform();
5326
6323
  },
5327
6324
  zoomOut: () => {
6325
+ isUserZoomed = true;
5328
6326
  scale = Math.max(0.2, scale - 0.15);
5329
6327
  applyTransform();
5330
6328
  },
5331
6329
  getZoom: () => scale,
5332
6330
  setZoom: (level) => {
6331
+ isUserZoomed = true;
5333
6332
  scale = level;
5334
6333
  applyTransform();
5335
6334
  },
5336
6335
  fitToPage: () => {
5337
- fitMode = fitMode === "width" ? "page" : "width";
5338
- scale = calculateFitScale(fitMode);
6336
+ isUserZoomed = false;
6337
+ fitMode = "width";
6338
+ scale = calculateFitScale("width");
5339
6339
  rotation = 0;
5340
6340
  applyTransform();
5341
6341
  },
@@ -5351,23 +6351,45 @@ var OpenDocumentPlugin = class {
5351
6351
  getPageCount: () => totalPages,
5352
6352
  getCurrentPage: () => currentPage,
5353
6353
  getThumbnails: async () => {
5354
- if (!isPresentation) return [];
5355
- return slides.map((_, idx) => ({
5356
- index: idx,
5357
- label: `Slide ${idx + 1}`,
5358
- render: async (canvas) => {
5359
- const ctx2d = canvas.getContext("2d");
5360
- if (!ctx2d) return;
5361
- canvas.width = 160;
5362
- canvas.height = 90;
5363
- ctx2d.fillStyle = "#f8fafc";
5364
- ctx2d.fillRect(0, 0, 160, 90);
5365
- ctx2d.fillStyle = "#334155";
5366
- ctx2d.font = "bold 12px sans-serif";
5367
- ctx2d.textAlign = "center";
5368
- ctx2d.fillText(`Slide ${idx + 1}`, 80, 50);
5369
- }
5370
- }));
6354
+ const count = totalPages || slides.length || 1;
6355
+ const items = [];
6356
+ for (let idx = 0; idx < count; idx++) {
6357
+ const itemIdx = idx;
6358
+ items.push({
6359
+ index: itemIdx,
6360
+ label: isPresentation ? `Slide ${itemIdx + 1}` : `Page ${itemIdx + 1}`,
6361
+ render: async (canvas) => {
6362
+ const ctx2d = canvas.getContext("2d");
6363
+ if (!ctx2d) return;
6364
+ if (isPresentation) {
6365
+ canvas.width = 160;
6366
+ canvas.height = 90;
6367
+ ctx2d.fillStyle = "#f8fafc";
6368
+ ctx2d.fillRect(0, 0, 160, 90);
6369
+ ctx2d.strokeStyle = "#cbd5e1";
6370
+ ctx2d.lineWidth = 1;
6371
+ ctx2d.strokeRect(0.5, 0.5, 159, 89);
6372
+ ctx2d.fillStyle = "#334155";
6373
+ ctx2d.font = "bold 11px sans-serif";
6374
+ ctx2d.textAlign = "center";
6375
+ ctx2d.fillText(`Slide ${itemIdx + 1}`, 80, 50);
6376
+ } else {
6377
+ canvas.width = 140;
6378
+ canvas.height = 180;
6379
+ ctx2d.fillStyle = "#ffffff";
6380
+ ctx2d.fillRect(0, 0, 140, 180);
6381
+ ctx2d.strokeStyle = "#cbd5e1";
6382
+ ctx2d.lineWidth = 1;
6383
+ ctx2d.strokeRect(0.5, 0.5, 139, 179);
6384
+ ctx2d.fillStyle = "#64748b";
6385
+ ctx2d.font = "bold 10px sans-serif";
6386
+ ctx2d.textAlign = "center";
6387
+ ctx2d.fillText(`Page ${itemIdx + 1}`, 70, 90);
6388
+ }
6389
+ }
6390
+ });
6391
+ }
6392
+ return items;
5371
6393
  },
5372
6394
  download: () => {
5373
6395
  const mimeType = this.mimeTypes.find((m) => m.includes(ext.replace(".", ""))) || "application/octet-stream";
@@ -5472,23 +6494,24 @@ var OpenDocumentPlugin = class {
5472
6494
  case "h": {
5473
6495
  const level = Math.min(6, Math.max(1, Number(node.getAttribute("text:outline-level") || 1)));
5474
6496
  const style = styles.get(node.getAttribute("text:style-name") || "") || "";
5475
- html += `<h${level} style="${style}; font-family: -apple-system, BlinkMacSystemFont, sans-serif;">${node.textContent || ""}</h${level}>`;
6497
+ html += `<h${level} style="${style}; margin: 18px 0 8px; color: #1e3a8a; font-family: Calibri, 'Segoe UI', Arial, sans-serif;">${node.textContent || ""}</h${level}>`;
5476
6498
  break;
5477
6499
  }
5478
6500
  case "p": {
5479
6501
  const style = styles.get(node.getAttribute("text:style-name") || "") || "";
5480
6502
  const inner = this.renderSpans(node, styles, images);
5481
- html += `<p style="${style}; line-height: 1.6; margin: 8px 0; font-family: -apple-system, BlinkMacSystemFont, sans-serif;">${inner}</p>`;
6503
+ const isInsideLi = node.parentElement?.localName === "list-item" || node.parentElement?.tagName?.toLowerCase() === "list-item";
6504
+ html += `<p style="${style}; line-height: 1.5; margin: ${isInsideLi ? "0" : "8px 0 12px"}; font-family: Calibri, 'Segoe UI', Arial, sans-serif;">${inner}</p>`;
5482
6505
  break;
5483
6506
  }
5484
6507
  case "list": {
5485
- html += '<ul style="margin: 8px 0; padding-left: 24px;">';
6508
+ html += '<ul style="margin: 8px 0 12px; padding-left: 24px;">';
5486
6509
  Array.from(node.children).forEach((c) => walk(c));
5487
6510
  html += "</ul>";
5488
6511
  break;
5489
6512
  }
5490
6513
  case "list-item": {
5491
- html += "<li>";
6514
+ html += '<li style="margin: 4px 0; font-size: 11pt; line-height: 1.5; color: #1e293b;">';
5492
6515
  Array.from(node.children).forEach((c) => walk(c));
5493
6516
  html += "</li>";
5494
6517
  break;
@@ -5614,28 +6637,26 @@ var DocPlugin = class {
5614
6637
  getToolbarActions(instance) {
5615
6638
  const totalPages = instance.getPageCount?.() ?? 1;
5616
6639
  const actions = [];
5617
- if (totalPages > 1) {
5618
- actions.push({
5619
- id: "page-nav",
5620
- icon: "",
5621
- label: "Page Navigation",
5622
- type: "page-nav",
5623
- group: "navigation",
5624
- value: instance.getCurrentPage?.() ?? 1,
5625
- max: totalPages,
5626
- execute: (action, page) => {
5627
- const cur = instance.getCurrentPage?.() ?? 1;
5628
- const max = instance.getPageCount?.() ?? 1;
5629
- if (action === "prev") {
5630
- if (cur > 1) instance.goToPage?.(cur - 1);
5631
- } else if (action === "next") {
5632
- if (cur < max) instance.goToPage?.(cur + 1);
5633
- } else if (typeof page === "number") {
5634
- instance.goToPage?.(page);
5635
- }
6640
+ actions.push({
6641
+ id: "page-nav",
6642
+ icon: "",
6643
+ label: "Page Navigation",
6644
+ type: "page-nav",
6645
+ group: "navigation",
6646
+ value: instance.getCurrentPage?.() ?? 1,
6647
+ max: totalPages,
6648
+ execute: (action, page) => {
6649
+ const cur = instance.getCurrentPage?.() ?? 1;
6650
+ const max = instance.getPageCount?.() ?? 1;
6651
+ if (action === "prev") {
6652
+ if (cur > 1) instance.goToPage?.(cur - 1);
6653
+ } else if (action === "next") {
6654
+ if (cur < max) instance.goToPage?.(cur + 1);
6655
+ } else if (typeof page === "number") {
6656
+ instance.goToPage?.(page);
5636
6657
  }
5637
- });
5638
- }
6658
+ }
6659
+ });
5639
6660
  actions.push(
5640
6661
  {
5641
6662
  id: "zoom-out",
@@ -5709,8 +6730,9 @@ var DocPlugin = class {
5709
6730
  container.className = "fp-doc-container";
5710
6731
  container.style.width = "100%";
5711
6732
  container.style.height = "100%";
5712
- container.style.overflow = "auto";
5713
- container.style.padding = "32px 16px";
6733
+ container.style.overflowX = "hidden";
6734
+ container.style.overflowY = "auto";
6735
+ container.style.padding = "16px 8px";
5714
6736
  container.style.backgroundColor = "#f1f5f9";
5715
6737
  container.style.display = "flex";
5716
6738
  container.style.justifyContent = "center";
@@ -5755,16 +6777,15 @@ var DocPlugin = class {
5755
6777
  const pageCard = document.createElement("div");
5756
6778
  pageCard.className = "fp-doc-page-card";
5757
6779
  pageCard.style.width = "816px";
5758
- pageCard.style.height = "1056px";
6780
+ pageCard.style.minHeight = "1056px";
5759
6781
  pageCard.style.backgroundColor = "#ffffff";
5760
- pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
6782
+ pageCard.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
5761
6783
  pageCard.style.borderRadius = "4px";
5762
- pageCard.style.padding = "96px 72px";
6784
+ pageCard.style.padding = "72px 56px";
5763
6785
  pageCard.style.boxSizing = "border-box";
5764
- pageCard.style.overflow = "hidden";
5765
6786
  pageCard.style.display = i === 0 ? "block" : "none";
5766
6787
  pageCard.style.transformOrigin = "top center";
5767
- pageCard.style.transition = "transform 0.2s ease";
6788
+ pageCard.style.transition = "transform 0.15s ease";
5768
6789
  pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
5769
6790
  pageCard.style.color = "#1e293b";
5770
6791
  if (isFallback && i === 0) {
@@ -5781,34 +6802,15 @@ var DocPlugin = class {
5781
6802
  container.appendChild(pageCard);
5782
6803
  pageCards.push(pageCard);
5783
6804
  }
5784
- let indicator = null;
5785
- if (totalPages > 1) {
5786
- indicator = document.createElement("div");
5787
- indicator.className = "fp-doc-page-indicator";
5788
- indicator.style.position = "fixed";
5789
- indicator.style.bottom = "16px";
5790
- indicator.style.left = "50%";
5791
- indicator.style.transform = "translateX(-50%)";
5792
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
5793
- indicator.style.backdropFilter = "blur(8px)";
5794
- indicator.style.color = "#f8fafc";
5795
- indicator.style.fontSize = "12px";
5796
- indicator.style.fontWeight = "600";
5797
- indicator.style.padding = "5px 14px";
5798
- indicator.style.borderRadius = "20px";
5799
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
5800
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
5801
- indicator.style.zIndex = "10";
5802
- indicator.style.userSelect = "none";
5803
- indicator.style.pointerEvents = "none";
5804
- indicator.style.textAlign = "center";
5805
- container.appendChild(indicator);
5806
- }
5807
6805
  let rotation = 0;
5808
6806
  const applyTransform = () => {
5809
6807
  const activeCard = pageCards[currentPage - 1];
5810
6808
  if (activeCard) {
5811
6809
  activeCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
6810
+ const baseH = activeCard.offsetHeight || 1056;
6811
+ const scaledH = baseH * scale;
6812
+ const extraH = Math.max(0, scaledH - baseH);
6813
+ activeCard.style.marginBottom = `${extraH + 32}px`;
5812
6814
  }
5813
6815
  };
5814
6816
  const showPage = (pageNum) => {
@@ -5821,35 +6823,39 @@ var DocPlugin = class {
5821
6823
  card.style.display = "none";
5822
6824
  }
5823
6825
  });
5824
- if (indicator) {
5825
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
5826
- }
5827
6826
  container.scrollTop = 0;
5828
6827
  ctx.emit("page-change", { page: currentPage, total: totalPages });
5829
6828
  };
5830
6829
  if (totalPages > 1) {
5831
6830
  showPage(1);
5832
6831
  }
5833
- let fitMode = "width";
6832
+ let fitMode = ctx?.options?.fitMode || "width";
6833
+ let isUserZoomed = false;
5834
6834
  const calculateFitScale = (mode = fitMode) => {
5835
6835
  const elW = 816;
5836
6836
  const elH = 1056;
5837
- const availW = Math.max(280, ctx.container.clientWidth - 48);
5838
- const availH = Math.max(280, ctx.container.clientHeight - 80);
6837
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
6838
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
5839
6839
  const sW = availW / elW;
5840
6840
  const sH = availH / elH;
5841
6841
  if (mode === "page") {
5842
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
6842
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
5843
6843
  }
5844
- return Math.max(0.65, Math.min(1.05, sW));
6844
+ return Math.max(0.4, Math.min(3, sW));
5845
6845
  };
6846
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
6847
+ if (!isUserZoomed) {
6848
+ scale = calculateFitScale(fitMode);
6849
+ applyTransform();
6850
+ }
6851
+ }) : null;
6852
+ resizeObserver?.observe(ctx.container);
5846
6853
  setTimeout(() => {
5847
- fitMode = "width";
5848
- scale = calculateFitScale("width");
6854
+ scale = calculateFitScale(fitMode);
5849
6855
  applyTransform();
5850
6856
  }, 60);
5851
6857
  const cleanup = () => {
5852
- indicator?.remove();
6858
+ resizeObserver?.disconnect();
5853
6859
  container.remove();
5854
6860
  ctx.container.innerHTML = "";
5855
6861
  };
@@ -5860,21 +6866,25 @@ var DocPlugin = class {
5860
6866
  getCurrentPage: () => currentPage,
5861
6867
  goToPage: (page) => showPage(page),
5862
6868
  zoomIn: () => {
6869
+ isUserZoomed = true;
5863
6870
  scale += 0.15;
5864
6871
  applyTransform();
5865
6872
  },
5866
6873
  zoomOut: () => {
6874
+ isUserZoomed = true;
5867
6875
  scale = Math.max(0.2, scale - 0.15);
5868
6876
  applyTransform();
5869
6877
  },
5870
6878
  getZoom: () => scale,
5871
6879
  setZoom: (level) => {
6880
+ isUserZoomed = true;
5872
6881
  scale = level;
5873
6882
  applyTransform();
5874
6883
  },
5875
6884
  fitToPage: () => {
5876
- fitMode = fitMode === "width" ? "page" : "width";
5877
- scale = calculateFitScale(fitMode);
6885
+ isUserZoomed = false;
6886
+ fitMode = "width";
6887
+ scale = calculateFitScale("width");
5878
6888
  rotation = 0;
5879
6889
  applyTransform();
5880
6890
  },
@@ -6251,25 +7261,31 @@ ${chartSvg}
6251
7261
  i++;
6252
7262
  continue;
6253
7263
  }
6254
- if (line.length < 60 && !line.endsWith(".") && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#"))) {
7264
+ if (/^\d{1,2}$/.test(line.trim())) {
7265
+ i++;
7266
+ continue;
7267
+ }
7268
+ const isShortLine = line.length < 60 && !line.endsWith(".") && !line.endsWith(",");
7269
+ const isTitleLike = isShortLine && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#") || /^(?:Chapter|Section|Part|\d+\.)\b/i.test(line) || /^[A-Z][a-zA-Z0-9\s#:-]{2,45}$/.test(line));
7270
+ if (isTitleLike) {
6255
7271
  if (inList) {
6256
7272
  html += "</ul>";
6257
7273
  inList = false;
6258
7274
  }
6259
- html += `<h2 style="font-size: 16px; font-weight: 700; color: #1e3a8a; margin: 16px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify6.sanitize(line, sanitizeOptions)}</h2>`;
7275
+ html += `<h2 style="font-size: 18px; font-weight: 700; color: #1e3a8a; margin: 18px 0 10px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify6.sanitize(line, sanitizeOptions)}</h2>`;
6260
7276
  } else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
6261
7277
  if (!inList) {
6262
- html += '<ul style="margin: 8px 0; padding-left: 24px;">';
7278
+ html += '<ul style="margin: 8px 0 12px; padding-left: 24px;">';
6263
7279
  inList = true;
6264
7280
  }
6265
7281
  const bulletText = line.replace(/^[•\-\*]\s*/, "");
6266
- html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6.sanitize(bulletText, sanitizeOptions)}</li>`;
7282
+ html += `<li style="margin: 4px 0; line-height: 1.5; font-size: 11pt; color: #1e293b;">${DOMPurify6.sanitize(bulletText, sanitizeOptions)}</li>`;
6267
7283
  } else {
6268
7284
  if (inList) {
6269
7285
  html += "</ul>";
6270
7286
  inList = false;
6271
7287
  }
6272
- html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6.sanitize(line, sanitizeOptions)}</p>`;
7288
+ html += `<p style="line-height: 1.5; margin: 0 0 12px 0; font-size: 11pt; color: #1e293b; text-align: justify;">${DOMPurify6.sanitize(line, sanitizeOptions)}</p>`;
6273
7289
  }
6274
7290
  i++;
6275
7291
  }
@@ -6823,14 +7839,14 @@ var FilePreviewViewer2 = class extends FilePreviewViewer {
6823
7839
  };
6824
7840
 
6825
7841
  // src/angular.ts
6826
- var _container_dec, _zoomChange_dec, _pageChange_dec, _error_dec, _loaded_dec, _loading_dec, _options_dec, _plugins_dec, _src_dec, _FilePreviewComponent_decorators, _init;
7842
+ var _container_dec, _destroyed_dec, _rotate_dec, _zoomChange_dec, _pageChange_dec, _error_dec, _loaded_dec, _loading_dec, _options_dec, _plugins_dec, _src_dec, _FilePreviewComponent_decorators, _init;
6827
7843
  _FilePreviewComponent_decorators = [Component({
6828
7844
  selector: "fp-file-preview",
6829
7845
  standalone: true,
6830
7846
  imports: [CommonModule],
6831
7847
  template: `<div #container [style.width]="'100%'" [style.height]="'100%'"></div>`,
6832
7848
  changeDetection: ChangeDetectionStrategy.OnPush
6833
- })], _src_dec = [Input({ required: true })], _plugins_dec = [Input()], _options_dec = [Input()], _loading_dec = [Output()], _loaded_dec = [Output()], _error_dec = [Output()], _pageChange_dec = [Output()], _zoomChange_dec = [Output()], _container_dec = [ViewChild("container", { static: true })];
7849
+ })], _src_dec = [Input({ required: true })], _plugins_dec = [Input()], _options_dec = [Input()], _loading_dec = [Output()], _loaded_dec = [Output()], _error_dec = [Output()], _pageChange_dec = [Output()], _zoomChange_dec = [Output()], _rotate_dec = [Output()], _destroyed_dec = [Output()], _container_dec = [ViewChild("container", { static: true })];
6834
7850
  var FilePreviewComponent = class {
6835
7851
  constructor(ngZone) {
6836
7852
  this.ngZone = ngZone;
@@ -6842,7 +7858,9 @@ var FilePreviewComponent = class {
6842
7858
  __publicField(this, "error", __runInitializers(_init, 28, this, new EventEmitter$1())), __runInitializers(_init, 31, this);
6843
7859
  __publicField(this, "pageChange", __runInitializers(_init, 32, this, new EventEmitter$1())), __runInitializers(_init, 35, this);
6844
7860
  __publicField(this, "zoomChange", __runInitializers(_init, 36, this, new EventEmitter$1())), __runInitializers(_init, 39, this);
6845
- __publicField(this, "container", __runInitializers(_init, 40, this)), __runInitializers(_init, 43, this);
7861
+ __publicField(this, "rotate", __runInitializers(_init, 40, this, new EventEmitter$1())), __runInitializers(_init, 43, this);
7862
+ __publicField(this, "destroyed", __runInitializers(_init, 44, this, new EventEmitter$1())), __runInitializers(_init, 47, this);
7863
+ __publicField(this, "container", __runInitializers(_init, 48, this)), __runInitializers(_init, 51, this);
6846
7864
  __publicField(this, "viewer", null);
6847
7865
  __publicField(this, "instance", null);
6848
7866
  }
@@ -6867,6 +7885,12 @@ var FilePreviewComponent = class {
6867
7885
  viewer.on("zoom-change", (data) => {
6868
7886
  this.ngZone.run(() => this.zoomChange.emit(data));
6869
7887
  });
7888
+ viewer.on("rotate", (data) => {
7889
+ this.ngZone.run(() => this.rotate.emit(data));
7890
+ });
7891
+ viewer.on("destroy", () => {
7892
+ this.ngZone.run(() => this.destroyed.emit());
7893
+ });
6870
7894
  this.render();
6871
7895
  }
6872
7896
  ngOnChanges(changes) {
@@ -6901,6 +7925,12 @@ var FilePreviewComponent = class {
6901
7925
  getInstance() {
6902
7926
  return this.instance;
6903
7927
  }
7928
+ getViewer() {
7929
+ return this.viewer;
7930
+ }
7931
+ destroy() {
7932
+ this.viewer?.destroy();
7933
+ }
6904
7934
  };
6905
7935
  _init = __decoratorStart();
6906
7936
  __decorateElement(_init, 5, "src", _src_dec, FilePreviewComponent);
@@ -6911,6 +7941,8 @@ __decorateElement(_init, 5, "loaded", _loaded_dec, FilePreviewComponent);
6911
7941
  __decorateElement(_init, 5, "error", _error_dec, FilePreviewComponent);
6912
7942
  __decorateElement(_init, 5, "pageChange", _pageChange_dec, FilePreviewComponent);
6913
7943
  __decorateElement(_init, 5, "zoomChange", _zoomChange_dec, FilePreviewComponent);
7944
+ __decorateElement(_init, 5, "rotate", _rotate_dec, FilePreviewComponent);
7945
+ __decorateElement(_init, 5, "destroyed", _destroyed_dec, FilePreviewComponent);
6914
7946
  __decorateElement(_init, 5, "container", _container_dec, FilePreviewComponent);
6915
7947
  FilePreviewComponent = __decorateElement(_init, 0, "FilePreviewComponent", _FilePreviewComponent_decorators, FilePreviewComponent);
6916
7948
  __runInitializers(_init, 1, FilePreviewComponent);