@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.cjs CHANGED
@@ -575,8 +575,7 @@ var ICON_FULLSCREEN = `<svg width="24" height="24" viewBox="0 0 24 24" fill="non
575
575
  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>`;
576
576
  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>`;
577
577
  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>`;
578
- 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>`;
579
- 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>`;
578
+ 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>`;
580
579
  var ICON_MAP = {
581
580
  "zoom-in": ICON_ZOOM_IN,
582
581
  "zoom-out": ICON_ZOOM_OUT,
@@ -603,28 +602,177 @@ var ICON_MAP = {
603
602
  "forward-10": ICON_FAST_FORWARD,
604
603
  "rewind": ICON_REWIND,
605
604
  "replay-10": ICON_REWIND,
606
- "speed": ICON_SPEED,
607
605
  "open-window": ICON_EXTERNAL_WINDOW,
608
- "external-window": ICON_EXTERNAL_WINDOW
606
+ "external-window": ICON_EXTERNAL_WINDOW,
607
+ "openWindow": ICON_EXTERNAL_WINDOW,
608
+ "openSeparateWindow": ICON_EXTERNAL_WINDOW
609
609
  };
610
610
  var ToolbarController = class {
611
611
  el;
612
612
  toolbarEl;
613
613
  actions = [];
614
+ config = {};
614
615
  pageInputEl = null;
615
616
  pageLabelEl = null;
616
- constructor(container) {
617
+ prevPageBtn = null;
618
+ nextPageBtn = null;
619
+ constructor(container, config) {
617
620
  this.el = container;
621
+ if (config) this.config = { ...config };
618
622
  this.toolbarEl = createElement("div", { className: "fp-toolbar" });
619
623
  this.el.appendChild(this.toolbarEl);
620
624
  }
625
+ setConfig(config) {
626
+ this.config = { ...config };
627
+ this.render();
628
+ }
629
+ getConfig() {
630
+ return { ...this.config };
631
+ }
632
+ hideAction(actionId) {
633
+ this.config[actionId] = false;
634
+ if (actionId === "zoom-in" || actionId === "zoomIn") this.config.zoomIn = false;
635
+ if (actionId === "zoom-out" || actionId === "zoomOut") this.config.zoomOut = false;
636
+ if (actionId === "fit-page" || actionId === "fitPage" || actionId === "fitToPage" || actionId === "fit-width") {
637
+ this.config.fitPage = false;
638
+ this.config.fitToPage = false;
639
+ this.config.fitWidth = false;
640
+ }
641
+ if (actionId === "rotate-cw" || actionId === "rotate" || actionId === "rotateCW") this.config.rotate = false;
642
+ if (actionId === "fullscreen") this.config.fullscreen = false;
643
+ if (actionId === "download") this.config.download = false;
644
+ if (actionId === "print") this.config.print = false;
645
+ if (actionId === "open-window" || actionId === "openWindow" || actionId === "openSeparateWindow") {
646
+ this.config.openWindow = false;
647
+ this.config.openSeparateWindow = false;
648
+ }
649
+ if (actionId === "copy") this.config.copy = false;
650
+ if (actionId === "page-nav" || actionId === "pageNav" || actionId === "pagination") {
651
+ this.config.pageNav = false;
652
+ this.config.pagination = false;
653
+ }
654
+ this.render();
655
+ }
656
+ showAction(actionId) {
657
+ this.config[actionId] = true;
658
+ if (actionId === "zoom-in" || actionId === "zoomIn") this.config.zoomIn = true;
659
+ if (actionId === "zoom-out" || actionId === "zoomOut") this.config.zoomOut = true;
660
+ if (actionId === "fit-page" || actionId === "fitPage" || actionId === "fitToPage" || actionId === "fit-width") {
661
+ this.config.fitPage = true;
662
+ this.config.fitToPage = true;
663
+ this.config.fitWidth = true;
664
+ }
665
+ if (actionId === "rotate-cw" || actionId === "rotate" || actionId === "rotateCW") this.config.rotate = true;
666
+ if (actionId === "fullscreen") this.config.fullscreen = true;
667
+ if (actionId === "download") this.config.download = true;
668
+ if (actionId === "print") this.config.print = true;
669
+ if (actionId === "open-window" || actionId === "openWindow" || actionId === "openSeparateWindow") {
670
+ this.config.openWindow = true;
671
+ this.config.openSeparateWindow = true;
672
+ }
673
+ if (actionId === "copy") this.config.copy = true;
674
+ if (actionId === "page-nav" || actionId === "pageNav" || actionId === "pagination") {
675
+ this.config.pageNav = true;
676
+ this.config.pagination = true;
677
+ }
678
+ this.render();
679
+ }
680
+ normalizeActionId(actionId) {
681
+ if (actionId === "zoomIn") return "zoom-in";
682
+ if (actionId === "zoomOut") return "zoom-out";
683
+ if (actionId === "fitPage" || actionId === "fitToPage" || actionId === "fitWidth") return "fit-page";
684
+ if (actionId === "rotateCW") return "rotate-cw";
685
+ if (actionId === "rotateCCW") return "rotate-ccw";
686
+ if (actionId === "openWindow" || actionId === "openSeparateWindow") return "open-window";
687
+ if (actionId === "pageNav") return "page-nav";
688
+ return actionId;
689
+ }
690
+ enableAction(actionId) {
691
+ const norm = this.normalizeActionId(actionId);
692
+ const btn = this.toolbarEl.querySelector(`button[data-action-id="${actionId}"], button[data-action-id="${norm}"]`);
693
+ if (btn) {
694
+ btn.disabled = false;
695
+ btn.classList.remove("disabled");
696
+ }
697
+ }
698
+ disableAction(actionId) {
699
+ const norm = this.normalizeActionId(actionId);
700
+ const btn = this.toolbarEl.querySelector(`button[data-action-id="${actionId}"], button[data-action-id="${norm}"]`);
701
+ if (btn) {
702
+ btn.disabled = true;
703
+ btn.classList.add("disabled");
704
+ }
705
+ }
706
+ setActionActive(actionId, active) {
707
+ const norm = this.normalizeActionId(actionId);
708
+ const btn = this.toolbarEl.querySelector(`button[data-action-id="${actionId}"], button[data-action-id="${norm}"]`);
709
+ if (btn) {
710
+ if (active) {
711
+ btn.classList.add("active");
712
+ } else {
713
+ btn.classList.remove("active");
714
+ }
715
+ }
716
+ }
717
+ isActionEnabled(action) {
718
+ const c = this.config;
719
+ const id = action.id;
720
+ if (c[id] === false) return false;
721
+ if (id === "zoom-in" && (c.zoomIn === false || c["zoom-in"] === false)) return false;
722
+ if (id === "zoom-out" && (c.zoomOut === false || c["zoom-out"] === false)) return false;
723
+ 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)) {
724
+ return false;
725
+ }
726
+ if ((id === "rotate-cw" || id === "rotate-ccw" || id === "rotate") && (c.rotate === false || c.rotateCW === false || c.rotateCCW === false || c["rotate-cw"] === false)) {
727
+ return false;
728
+ }
729
+ if (id === "fullscreen" && c.fullscreen === false) return false;
730
+ if (id === "download" && c.download === false) return false;
731
+ if (id === "print" && c.print === false) return false;
732
+ if ((id === "open-window" || id === "external-window") && (c.openWindow === false || c.openSeparateWindow === false || c["open-window"] === false)) {
733
+ return false;
734
+ }
735
+ if (id === "copy" && c.copy === false) return false;
736
+ if (id === "thumbnails" && c.thumbnails === false) return false;
737
+ if (id === "page-nav" && (c.pageNav === false || c.pagination === false || c["page-nav"] === false)) {
738
+ return false;
739
+ }
740
+ if ((id === "prev" || id === "page-prev") && (c.prevPage === false || c.prev === false || c["prev"] === false || c.pageNav === false || c.pagination === false)) {
741
+ return false;
742
+ }
743
+ if ((id === "next" || id === "page-next") && (c.nextPage === false || c.next === false || c["next"] === false || c.pageNav === false || c.pagination === false)) {
744
+ return false;
745
+ }
746
+ if (id === "play" && c.play === false) return false;
747
+ if (id === "pause" && c.pause === false) return false;
748
+ if ((id === "fast-forward" || id === "forward-10") && (c.fastForward === false || c["fast-forward"] === false)) {
749
+ return false;
750
+ }
751
+ if ((id === "rewind" || id === "replay-10") && (c.rewind === false || c["rewind"] === false)) {
752
+ return false;
753
+ }
754
+ if (id === "speed" && c.speed === false) return false;
755
+ return true;
756
+ }
621
757
  setPage(page, max) {
622
758
  if (this.pageInputEl) {
623
759
  this.pageInputEl.value = page.toString();
760
+ if (max !== void 0) {
761
+ this.pageInputEl.max = max.toString();
762
+ }
624
763
  }
625
764
  if (max !== void 0 && this.pageLabelEl) {
626
765
  this.pageLabelEl.textContent = ` / ${max}`;
627
766
  }
767
+ const currentMax = max !== void 0 ? max : parseInt(this.pageInputEl?.max || "1", 10) || 1;
768
+ if (this.prevPageBtn) {
769
+ this.prevPageBtn.disabled = page <= 1;
770
+ this.prevPageBtn.style.opacity = page <= 1 ? "0.4" : "1";
771
+ }
772
+ if (this.nextPageBtn) {
773
+ this.nextPageBtn.disabled = page >= currentMax;
774
+ this.nextPageBtn.style.opacity = page >= currentMax ? "0.4" : "1";
775
+ }
628
776
  }
629
777
  update(actions) {
630
778
  this.actions = actions;
@@ -647,8 +795,9 @@ var ToolbarController = class {
647
795
  view: [],
648
796
  actions: []
649
797
  };
650
- const hasPageNav = this.actions.some((a) => a.type === "page-nav");
651
- const effectiveActions = hasPageNav ? this.actions.filter((a) => a.id !== "page-prev" && a.id !== "page-next" && a.id !== "prev" && a.id !== "next") : this.actions;
798
+ const enabledActions = this.actions.filter((a) => this.isActionEnabled(a));
799
+ const hasPageNav = enabledActions.some((a) => a.type === "page-nav");
800
+ const effectiveActions = hasPageNav ? enabledActions.filter((a) => a.id !== "page-prev" && a.id !== "page-next" && a.id !== "prev" && a.id !== "next") : enabledActions;
652
801
  for (const action of effectiveActions) {
653
802
  if (groups[action.group]) {
654
803
  groups[action.group].push(action);
@@ -708,6 +857,13 @@ var ToolbarController = class {
708
857
  action.execute("go", val);
709
858
  });
710
859
  const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${max}`);
860
+ const curVal = action.value ?? 1;
861
+ prevBtn.disabled = curVal <= 1;
862
+ prevBtn.style.opacity = curVal <= 1 ? "0.4" : "1";
863
+ nextBtn.disabled = curVal >= max;
864
+ nextBtn.style.opacity = curVal >= max ? "0.4" : "1";
865
+ this.prevPageBtn = prevBtn;
866
+ this.nextPageBtn = nextBtn;
711
867
  this.pageInputEl = input;
712
868
  this.pageLabelEl = label;
713
869
  groupEl.appendChild(prevBtn);
@@ -763,25 +919,63 @@ var ToolbarController = class {
763
919
  var ThumbnailPanel = class {
764
920
  el;
765
921
  panelEl;
922
+ listEl;
923
+ headerEl;
766
924
  thumbnails = [];
767
925
  onSelect;
926
+ onToggleCallback;
768
927
  activeIndex = 0;
769
- constructor(container) {
928
+ observer;
929
+ renderedIndices = /* @__PURE__ */ new Set();
930
+ constructor(container, onToggle) {
770
931
  this.el = container;
932
+ this.onToggleCallback = onToggle;
771
933
  this.panelEl = createElement("div", { className: "fp-thumbnail-panel hidden" });
934
+ this.panelEl.style.width = "0px";
935
+ this.panelEl.style.minWidth = "0px";
936
+ this.panelEl.style.opacity = "0";
937
+ this.headerEl = createElement("div", { className: "fp-thumbnail-header" });
938
+ const title = createElement("span", { className: "fp-thumbnail-title" }, "Pages");
939
+ const closeBtn = createElement("button", {
940
+ className: "fp-thumbnail-close-btn",
941
+ title: "Close Thumbnails",
942
+ type: "button"
943
+ });
944
+ closeBtn.innerHTML = ICON_CLOSE;
945
+ closeBtn.addEventListener("click", (e) => {
946
+ e.stopPropagation();
947
+ this.hide();
948
+ });
949
+ this.headerEl.appendChild(title);
950
+ this.headerEl.appendChild(closeBtn);
951
+ this.listEl = createElement("div", { className: "fp-thumbnail-list" });
952
+ this.panelEl.appendChild(this.headerEl);
953
+ this.panelEl.appendChild(this.listEl);
772
954
  this.el.appendChild(this.panelEl);
773
955
  }
956
+ isOpen() {
957
+ return !this.panelEl.classList.contains("hidden") && this.panelEl.style.width !== "0px";
958
+ }
774
959
  update(thumbnails, onSelect) {
775
960
  this.thumbnails = thumbnails;
776
961
  this.onSelect = onSelect;
962
+ this.renderedIndices.clear();
963
+ const titleEl = this.headerEl.querySelector(".fp-thumbnail-title");
964
+ if (titleEl) {
965
+ titleEl.textContent = `Pages (${thumbnails.length})`;
966
+ }
777
967
  this.render();
968
+ if (this.isOpen()) {
969
+ this.renderAllVisible();
970
+ }
778
971
  }
779
972
  setActive(index) {
780
973
  this.activeIndex = index;
781
- const items = this.panelEl.querySelectorAll(".fp-thumbnail-item");
974
+ const items = this.listEl.querySelectorAll(".fp-thumbnail-item");
782
975
  items.forEach((item, i) => {
783
976
  if (i === index) {
784
977
  item.classList.add("active");
978
+ item.scrollIntoView({ block: "nearest", behavior: "smooth" });
785
979
  } else {
786
980
  item.classList.remove("active");
787
981
  }
@@ -789,37 +983,102 @@ var ThumbnailPanel = class {
789
983
  }
790
984
  show() {
791
985
  this.panelEl.classList.remove("hidden");
986
+ this.panelEl.style.width = "200px";
987
+ this.panelEl.style.minWidth = "200px";
988
+ this.panelEl.style.opacity = "1";
989
+ this.panelEl.style.display = "flex";
990
+ this.onToggleCallback?.(true);
991
+ this.renderAllVisible();
792
992
  }
793
993
  hide() {
794
994
  this.panelEl.classList.add("hidden");
995
+ this.panelEl.style.width = "0px";
996
+ this.panelEl.style.minWidth = "0px";
997
+ this.panelEl.style.opacity = "0";
998
+ this.onToggleCallback?.(false);
795
999
  }
796
1000
  toggle() {
797
- this.panelEl.classList.toggle("hidden");
1001
+ if (this.isOpen()) {
1002
+ this.hide();
1003
+ } else {
1004
+ this.show();
1005
+ }
798
1006
  }
799
1007
  destroy() {
1008
+ if (this.observer) {
1009
+ this.observer.disconnect();
1010
+ this.observer = void 0;
1011
+ }
1012
+ this.renderedIndices.clear();
800
1013
  this.el.innerHTML = "";
801
1014
  }
802
- async render() {
803
- this.panelEl.innerHTML = "";
1015
+ renderAllVisible() {
1016
+ const items = this.listEl.querySelectorAll(".fp-thumbnail-item");
1017
+ items.forEach((item) => {
1018
+ const idx = parseInt(item.dataset.thumbIndex || "-1", 10);
1019
+ if (idx >= 0 && !this.renderedIndices.has(idx)) {
1020
+ this.renderThumbnailItem(idx, item);
1021
+ }
1022
+ });
1023
+ }
1024
+ renderThumbnailItem(idx, itemEl) {
1025
+ if (this.renderedIndices.has(idx)) return;
1026
+ this.renderedIndices.add(idx);
1027
+ const canvas = itemEl.querySelector("canvas");
1028
+ const thumb = this.thumbnails[idx];
1029
+ if (canvas && thumb) {
1030
+ Promise.resolve(thumb.render(canvas)).catch((err) => {
1031
+ console.warn(`[FilePreview] Error rendering thumbnail ${idx}:`, err);
1032
+ });
1033
+ }
1034
+ }
1035
+ render() {
1036
+ if (this.observer) {
1037
+ this.observer.disconnect();
1038
+ }
1039
+ this.listEl.innerHTML = "";
1040
+ const useObserver = typeof IntersectionObserver !== "undefined";
1041
+ if (useObserver) {
1042
+ this.observer = new IntersectionObserver(
1043
+ (entries) => {
1044
+ for (const entry of entries) {
1045
+ if (entry.isIntersecting) {
1046
+ const target = entry.target;
1047
+ const idx = parseInt(target.dataset.thumbIndex || "-1", 10);
1048
+ if (idx >= 0) {
1049
+ this.renderThumbnailItem(idx, target);
1050
+ }
1051
+ }
1052
+ }
1053
+ },
1054
+ {
1055
+ root: this.listEl,
1056
+ rootMargin: "120px 0px 120px 0px"
1057
+ }
1058
+ );
1059
+ }
804
1060
  for (let i = 0; i < this.thumbnails.length; i++) {
805
1061
  const thumb = this.thumbnails[i];
806
- const itemEl = createElement("div", { className: "fp-thumbnail-item" });
807
- if (i === this.activeIndex) {
808
- itemEl.classList.add("active");
809
- }
1062
+ const itemEl = createElement("div", {
1063
+ className: "fp-thumbnail-item" + (i === this.activeIndex ? " active" : "")
1064
+ });
1065
+ itemEl.dataset.thumbIndex = i.toString();
810
1066
  itemEl.addEventListener("click", () => {
811
1067
  this.setActive(i);
812
1068
  this.onSelect?.(i);
813
1069
  });
814
- const canvas = createElement("canvas", { width: "150", height: "200" });
815
- const label = createElement("span", { className: "fp-thumbnail-label" }, thumb.label);
816
- itemEl.appendChild(canvas);
1070
+ const canvasWrapper = createElement("div", { className: "fp-thumbnail-canvas-wrapper" });
1071
+ const canvas = createElement("canvas", { width: "130", height: "170" });
1072
+ canvasWrapper.appendChild(canvas);
1073
+ const label = createElement("span", { className: "fp-thumbnail-label" }, thumb.label || `Page ${i + 1}`);
1074
+ itemEl.appendChild(canvasWrapper);
817
1075
  itemEl.appendChild(label);
818
- this.panelEl.appendChild(itemEl);
819
- try {
820
- await thumb.render(canvas);
821
- } catch (err) {
822
- console.error(`[FilePreview] Error rendering thumbnail ${i}:`, err);
1076
+ this.listEl.appendChild(itemEl);
1077
+ if (this.observer) {
1078
+ this.observer.observe(itemEl);
1079
+ }
1080
+ if (i < 4) {
1081
+ this.renderThumbnailItem(i, itemEl);
823
1082
  }
824
1083
  }
825
1084
  }
@@ -840,6 +1099,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
840
1099
  currentOptions = {};
841
1100
  resizeObserver = null;
842
1101
  fullscreenHandler = null;
1102
+ titleBarEl = null;
843
1103
  /**
844
1104
  * Register a preview plugin.
845
1105
  */
@@ -876,6 +1136,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
876
1136
  }
877
1137
  this.currentBuffer = buffer.slice(0);
878
1138
  this.currentMetadata = metadata;
1139
+ this.updateTitleBar(options, metadata);
879
1140
  if (signal.aborted) throw new DOMException("Aborted", "AbortError");
880
1141
  const fileInfo = { metadata, buffer };
881
1142
  const matchedPlugin = await this.findPlugin(fileInfo);
@@ -899,15 +1160,21 @@ var FilePreviewViewer = class _FilePreviewViewer {
899
1160
  if (event === "page-change" && payload && typeof payload.page === "number") {
900
1161
  const total = payload.total ?? payload.totalPages;
901
1162
  this.toolbar?.setPage(payload.page, total);
1163
+ this.thumbnailPanel?.setActive(payload.page - 1);
902
1164
  }
903
1165
  this.eventEmitter.emit(event, payload);
904
- }
1166
+ },
1167
+ toggleThumbnails: () => this.toggleThumbnails()
905
1168
  });
906
1169
  this.activeInstance = instance;
907
1170
  instance.openInSeparateWindow = () => this.openInSeparateWindow();
1171
+ instance.toggleThumbnails = () => this.toggleThumbnails();
908
1172
  this.hideLoading();
909
1173
  this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
910
- if (options.showToolbar !== false && this.toolbar) {
1174
+ const isToolbarVisible = options.showToolbar !== false && options.toolbar !== false;
1175
+ if (isToolbarVisible && this.toolbar) {
1176
+ const toolbarConfig = this.extractToolbarConfig(options);
1177
+ this.toolbar.setConfig(toolbarConfig);
911
1178
  const actions = matchedPlugin.getToolbarActions(instance);
912
1179
  const hasFullscreen = actions.some((a) => a.id === "fullscreen");
913
1180
  if (!hasFullscreen && this.wrapperEl) {
@@ -957,6 +1224,12 @@ var FilePreviewViewer = class _FilePreviewViewer {
957
1224
  }
958
1225
  });
959
1226
  }
1227
+ const thumbAction = actions.find((a) => a.id === "thumbnails");
1228
+ if (thumbAction) {
1229
+ thumbAction.execute = () => {
1230
+ this.toggleThumbnails();
1231
+ };
1232
+ }
960
1233
  this.toolbar.update(actions);
961
1234
  this.toolbar.show();
962
1235
  }
@@ -1041,7 +1314,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1041
1314
  if (targetUrl) {
1042
1315
  const newWin2 = window.open(targetUrl, "_blank");
1043
1316
  if (!newWin2) {
1044
- alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
1317
+ console.warn("[FilePreviewViewer] Popup blocker prevented opening the preview in a separate window.");
1045
1318
  return null;
1046
1319
  }
1047
1320
  return newWin2;
@@ -1049,7 +1322,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1049
1322
  const title = (this.currentMetadata?.name || "Document Preview") + " - Full Preview";
1050
1323
  const newWin = window.open("", "_blank");
1051
1324
  if (!newWin) {
1052
- alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
1325
+ console.warn("[FilePreviewViewer] Popup blocker prevented opening the preview in a separate window.");
1053
1326
  return null;
1054
1327
  }
1055
1328
  newWin.document.title = title;
@@ -1126,6 +1399,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1126
1399
  this.currentBuffer = null;
1127
1400
  this.currentMetadata = null;
1128
1401
  this.currentContainer = null;
1402
+ this.titleBarEl = null;
1129
1403
  }
1130
1404
  /**
1131
1405
  * Get the currently active preview instance.
@@ -1133,7 +1407,218 @@ var FilePreviewViewer = class _FilePreviewViewer {
1133
1407
  getInstance() {
1134
1408
  return this.activeInstance;
1135
1409
  }
1410
+ // --- Toolbar Configuration & Feature Toggles ---
1411
+ /**
1412
+ * Configure toolbar features dynamically through options or methods.
1413
+ * e.g. viewer.setToolbarConfig({ zoomIn: false, print: false })
1414
+ */
1415
+ setToolbarConfig(config) {
1416
+ this.toolbar?.setConfig(config);
1417
+ }
1418
+ /**
1419
+ * Get current toolbar configuration.
1420
+ */
1421
+ getToolbarConfig() {
1422
+ return this.toolbar?.getConfig() ?? {};
1423
+ }
1424
+ /**
1425
+ * Hide a specific toolbar action by ID or alias (e.g. 'zoom-in', 'print', 'download').
1426
+ */
1427
+ hideToolbarAction(actionId) {
1428
+ this.toolbar?.hideAction(actionId);
1429
+ }
1430
+ /**
1431
+ * Show a specific toolbar action by ID or alias.
1432
+ */
1433
+ showToolbarAction(actionId) {
1434
+ this.toolbar?.showAction(actionId);
1435
+ }
1436
+ /**
1437
+ * Enable a specific toolbar action button.
1438
+ */
1439
+ enableToolbarAction(actionId) {
1440
+ this.toolbar?.enableAction(actionId);
1441
+ }
1442
+ /**
1443
+ * Disable a specific toolbar action button.
1444
+ */
1445
+ disableToolbarAction(actionId) {
1446
+ this.toolbar?.disableAction(actionId);
1447
+ }
1448
+ // --- Programmatic Toolbar Action Methods ---
1449
+ /**
1450
+ * Fit document to page so it fills the frame width with minimal margins.
1451
+ */
1452
+ fitToPage() {
1453
+ this.activeInstance?.fitToPage?.();
1454
+ }
1455
+ /**
1456
+ * Zoom in.
1457
+ */
1458
+ zoomIn() {
1459
+ this.activeInstance?.zoomIn?.();
1460
+ }
1461
+ /**
1462
+ * Zoom out.
1463
+ */
1464
+ zoomOut() {
1465
+ this.activeInstance?.zoomOut?.();
1466
+ }
1467
+ /**
1468
+ * Set specific zoom level.
1469
+ */
1470
+ setZoom(level) {
1471
+ this.activeInstance?.setZoom?.(level);
1472
+ }
1473
+ /**
1474
+ * Get current zoom level.
1475
+ */
1476
+ getZoom() {
1477
+ return this.activeInstance?.getZoom?.() ?? 1;
1478
+ }
1479
+ /**
1480
+ * Rotate 90 degrees clockwise.
1481
+ */
1482
+ rotateCW() {
1483
+ this.activeInstance?.rotateCW?.();
1484
+ }
1485
+ /**
1486
+ * Rotate 90 degrees counter-clockwise.
1487
+ */
1488
+ rotateCCW() {
1489
+ this.activeInstance?.rotateCCW?.();
1490
+ }
1491
+ /**
1492
+ * Navigate to a specific page number.
1493
+ */
1494
+ goToPage(page) {
1495
+ this.activeInstance?.goToPage?.(page);
1496
+ this.toolbar?.setPage(page);
1497
+ }
1498
+ /**
1499
+ * Navigate to next page.
1500
+ */
1501
+ nextPage() {
1502
+ const cur = this.getCurrentPage();
1503
+ const max = this.getPageCount();
1504
+ if (cur < max) {
1505
+ this.goToPage(cur + 1);
1506
+ }
1507
+ }
1508
+ /**
1509
+ * Navigate to previous page.
1510
+ */
1511
+ prevPage() {
1512
+ const cur = this.getCurrentPage();
1513
+ if (cur > 1) {
1514
+ this.goToPage(cur - 1);
1515
+ }
1516
+ }
1517
+ /**
1518
+ * Get total page count.
1519
+ */
1520
+ getPageCount() {
1521
+ return this.activeInstance?.getPageCount?.() ?? 1;
1522
+ }
1523
+ /**
1524
+ * Get current page number.
1525
+ */
1526
+ getCurrentPage() {
1527
+ return this.activeInstance?.getCurrentPage?.() ?? 1;
1528
+ }
1529
+ /**
1530
+ * Download the current document.
1531
+ */
1532
+ download() {
1533
+ this.activeInstance?.download?.();
1534
+ }
1535
+ /**
1536
+ * Print the current document.
1537
+ */
1538
+ print() {
1539
+ this.activeInstance?.print?.();
1540
+ }
1541
+ /**
1542
+ * Toggle fullscreen mode.
1543
+ */
1544
+ toggleFullscreen() {
1545
+ try {
1546
+ if (!document.fullscreenElement) {
1547
+ this.wrapperEl?.requestFullscreen?.();
1548
+ this.wrapperEl?.classList.add("fp-fullscreen-active");
1549
+ } else {
1550
+ document.exitFullscreen?.();
1551
+ this.wrapperEl?.classList.remove("fp-fullscreen-active");
1552
+ }
1553
+ } catch {
1554
+ this.wrapperEl?.classList.toggle("fp-fullscreen-active");
1555
+ }
1556
+ setTimeout(() => {
1557
+ this.activeInstance?.fitToPage?.();
1558
+ }, 120);
1559
+ }
1560
+ /**
1561
+ * Toggle thumbnails sidebar panel.
1562
+ */
1563
+ toggleThumbnails() {
1564
+ if (!this.thumbnailPanel) return;
1565
+ this.thumbnailPanel.toggle();
1566
+ const isOpen = this.thumbnailPanel.isOpen();
1567
+ this.toolbar?.setActionActive("thumbnails", isOpen);
1568
+ setTimeout(() => {
1569
+ this.activeInstance?.fitToPage?.();
1570
+ }, 260);
1571
+ }
1572
+ /**
1573
+ * Set whether to display the file name / title bar above the toolbar.
1574
+ */
1575
+ setShowFileName(show) {
1576
+ if (this.currentOptions) {
1577
+ this.currentOptions.showFileName = show;
1578
+ }
1579
+ this.updateTitleBar(this.currentOptions, this.currentMetadata);
1580
+ }
1581
+ /**
1582
+ * Set or override the displayed file name in the preview panel.
1583
+ */
1584
+ setFileName(name) {
1585
+ if (this.currentOptions) {
1586
+ this.currentOptions.fileName = name;
1587
+ }
1588
+ this.updateTitleBar(this.currentOptions, this.currentMetadata);
1589
+ }
1136
1590
  // --- Private methods ---
1591
+ updateTitleBar(options, metadata) {
1592
+ if (!this.titleBarEl) return;
1593
+ const show = options?.showFileName !== false;
1594
+ if (!show) {
1595
+ this.titleBarEl.style.display = "none";
1596
+ return;
1597
+ }
1598
+ const name = options?.fileName || metadata?.name || this.currentMetadata?.name || "";
1599
+ if (!name) {
1600
+ this.titleBarEl.style.display = "none";
1601
+ return;
1602
+ }
1603
+ this.titleBarEl.style.display = "flex";
1604
+ const ext = metadata?.extension || (name.includes(".") ? "." + name.split(".").pop() : "");
1605
+ const badge = ext ? ext.replace(".", "").toUpperCase() : "";
1606
+ this.titleBarEl.innerHTML = `
1607
+ <div class="fp-titlebar-info">
1608
+ <span class="fp-titlebar-icon">\u{1F4C4}</span>
1609
+ <span class="fp-titlebar-name" title="${name}">${name}</span>
1610
+ ${badge ? `<span class="fp-titlebar-badge">${badge}</span>` : ""}
1611
+ </div>
1612
+ `;
1613
+ }
1614
+ extractToolbarConfig(options) {
1615
+ if (!options) return {};
1616
+ const nested = typeof options.toolbar === "object" ? options.toolbar : {};
1617
+ return {
1618
+ ...options,
1619
+ ...nested
1620
+ };
1621
+ }
1137
1622
  abort() {
1138
1623
  if (this.abortController) {
1139
1624
  this.abortController.abort();
@@ -1176,17 +1661,29 @@ var FilePreviewViewer = class _FilePreviewViewer {
1176
1661
  bodyEl.className = "fp-body";
1177
1662
  bodyEl.appendChild(thumbnailEl);
1178
1663
  bodyEl.appendChild(this.contentEl);
1664
+ const titleBarEl = document.createElement("div");
1665
+ titleBarEl.className = "fp-titlebar";
1666
+ this.titleBarEl = titleBarEl;
1667
+ this.updateTitleBar(options, this.currentMetadata);
1179
1668
  if (toolbarPos === "top") {
1669
+ this.wrapperEl.appendChild(titleBarEl);
1180
1670
  this.wrapperEl.appendChild(toolbarEl);
1181
1671
  this.wrapperEl.appendChild(bodyEl);
1182
1672
  } else {
1673
+ this.wrapperEl.appendChild(titleBarEl);
1183
1674
  this.wrapperEl.appendChild(bodyEl);
1184
1675
  this.wrapperEl.appendChild(toolbarEl);
1185
1676
  }
1186
1677
  container.innerHTML = "";
1187
1678
  container.appendChild(this.wrapperEl);
1188
- this.toolbar = new ToolbarController(toolbarEl);
1189
- this.thumbnailPanel = new ThumbnailPanel(thumbnailEl);
1679
+ const initialToolbarConfig = this.extractToolbarConfig(options);
1680
+ this.toolbar = new ToolbarController(toolbarEl, initialToolbarConfig);
1681
+ this.thumbnailPanel = new ThumbnailPanel(thumbnailEl, (isOpen) => {
1682
+ this.toolbar?.setActionActive("thumbnails", isOpen);
1683
+ setTimeout(() => {
1684
+ this.activeInstance?.fitToPage?.();
1685
+ }, 260);
1686
+ });
1190
1687
  this.setupKeyboardShortcuts();
1191
1688
  this.setupDragAndDrop(container, options);
1192
1689
  this.setupResizeAndFullscreenListeners();
@@ -1576,6 +2073,14 @@ var PdfPlugin = class {
1576
2073
  group: "zoom",
1577
2074
  execute: () => instance.fitToPage?.()
1578
2075
  },
2076
+ {
2077
+ id: "fit-width",
2078
+ icon: "fit-width",
2079
+ label: "Fit to Width",
2080
+ type: "button",
2081
+ group: "zoom",
2082
+ execute: () => instance.fitToWidth?.()
2083
+ },
1579
2084
  {
1580
2085
  id: "rotate-cw",
1581
2086
  icon: "rotate-cw",
@@ -1584,6 +2089,14 @@ var PdfPlugin = class {
1584
2089
  group: "view",
1585
2090
  execute: () => instance.rotateCW?.()
1586
2091
  },
2092
+ {
2093
+ id: "rotate-ccw",
2094
+ icon: "rotate-ccw",
2095
+ label: "Rotate Counter-Clockwise",
2096
+ type: "button",
2097
+ group: "view",
2098
+ execute: () => instance.rotateCCW?.()
2099
+ },
1587
2100
  {
1588
2101
  id: "download",
1589
2102
  icon: "download",
@@ -1615,12 +2128,13 @@ var PdfPlugin = class {
1615
2128
  container.className = "fp-pdf-container";
1616
2129
  container.style.width = "100%";
1617
2130
  container.style.height = "100%";
1618
- container.style.overflow = "auto";
2131
+ container.style.overflowX = "hidden";
2132
+ container.style.overflowY = "auto";
1619
2133
  container.style.display = "flex";
1620
2134
  container.style.flexDirection = "column";
1621
2135
  container.style.alignItems = "center";
1622
2136
  container.style.justifyContent = "flex-start";
1623
- container.style.padding = "20px 16px";
2137
+ container.style.padding = "16px 8px";
1624
2138
  container.style.backgroundColor = "#0f172a";
1625
2139
  container.style.boxSizing = "border-box";
1626
2140
  container.style.position = "relative";
@@ -1631,30 +2145,12 @@ var PdfPlugin = class {
1631
2145
  pageCard.style.borderRadius = "4px";
1632
2146
  pageCard.style.overflow = "hidden";
1633
2147
  pageCard.style.lineHeight = "0";
1634
- pageCard.style.transition = "transform 0.15s ease";
1635
2148
  pageCard.style.position = "relative";
1636
2149
  pageCard.style.flexShrink = "0";
2150
+ pageCard.style.transition = "box-shadow 0.2s ease";
1637
2151
  let canvas = document.createElement("canvas");
1638
2152
  pageCard.appendChild(canvas);
1639
2153
  container.appendChild(pageCard);
1640
- const indicator = document.createElement("div");
1641
- indicator.className = "fp-pdf-page-indicator";
1642
- indicator.style.position = "sticky";
1643
- indicator.style.bottom = "16px";
1644
- indicator.style.marginTop = "16px";
1645
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
1646
- indicator.style.backdropFilter = "blur(8px)";
1647
- indicator.style.color = "#f8fafc";
1648
- indicator.style.fontSize = "12px";
1649
- indicator.style.fontWeight = "600";
1650
- indicator.style.padding = "5px 14px";
1651
- indicator.style.borderRadius = "20px";
1652
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
1653
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
1654
- indicator.style.zIndex = "10";
1655
- indicator.style.userSelect = "none";
1656
- indicator.style.pointerEvents = "none";
1657
- container.appendChild(indicator);
1658
2154
  ctx.container.appendChild(container);
1659
2155
  const standardFontsUrl = typeof window !== "undefined" && window.__PDF_STANDARD_FONTS_URL__ || "./standard_fonts/";
1660
2156
  const cmapsUrl = typeof window !== "undefined" && window.__PDF_CMAPS_URL__ || "./cmaps/";
@@ -1670,8 +2166,9 @@ var PdfPlugin = class {
1670
2166
  let currentPage = 1;
1671
2167
  let zoomScale = 1;
1672
2168
  let rotation = 0;
1673
- let fitMode = "page";
2169
+ let fitMode = ctx?.options?.fitMode || "width";
1674
2170
  let currentRenderTask = null;
2171
+ let textLayerDiv = null;
1675
2172
  const renderPage = async (pageNum) => {
1676
2173
  if (currentRenderTask) {
1677
2174
  try {
@@ -1683,30 +2180,37 @@ var PdfPlugin = class {
1683
2180
  const newCanvas = document.createElement("canvas");
1684
2181
  pageCard.replaceChild(newCanvas, canvas);
1685
2182
  canvas = newCanvas;
2183
+ if (textLayerDiv) {
2184
+ textLayerDiv.remove();
2185
+ textLayerDiv = null;
2186
+ }
1686
2187
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
1687
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
1688
2188
  ctx.emit("page-change", { page: currentPage, total: totalPages });
1689
2189
  const page = await pdfDoc.getPage(currentPage);
1690
2190
  const containerWidth = container.clientWidth || 900;
1691
2191
  const containerHeight = container.clientHeight || 700;
1692
2192
  const unscaledVp = page.getViewport({ scale: 1, rotation });
1693
- const availWidth = Math.max(320, containerWidth - 48);
1694
- const availHeight = Math.max(550, containerHeight - 88);
2193
+ const availWidth = Math.max(280, containerWidth - 36);
2194
+ const availHeight = Math.max(280, containerHeight - 32);
1695
2195
  const scaleW = availWidth / unscaledVp.width;
1696
2196
  const scaleH = availHeight / unscaledVp.height;
1697
2197
  let fitScale;
1698
2198
  if (fitMode === "page") {
1699
- fitScale = Math.max(0.4, Math.min(scaleW, scaleH));
2199
+ fitScale = Math.max(0.2, Math.min(4, Math.min(scaleW, scaleH)));
1700
2200
  } else {
1701
- fitScale = Math.max(0.65, Math.min(1.25, scaleW));
2201
+ fitScale = Math.max(0.2, Math.min(4, scaleW));
1702
2202
  }
1703
2203
  const effectiveScale = (fitScale > 0 ? fitScale : 1) * zoomScale;
1704
2204
  const pixelRatio = window.devicePixelRatio || 1;
1705
2205
  const viewport = page.getViewport({ scale: effectiveScale, rotation });
2206
+ const displayWidth = Math.floor(viewport.width);
2207
+ const displayHeight = Math.floor(viewport.height);
1706
2208
  canvas.width = Math.floor(viewport.width * pixelRatio);
1707
2209
  canvas.height = Math.floor(viewport.height * pixelRatio);
1708
- canvas.style.width = `${Math.floor(viewport.width)}px`;
1709
- canvas.style.height = `${Math.floor(viewport.height)}px`;
2210
+ canvas.style.width = `${displayWidth}px`;
2211
+ canvas.style.height = `${displayHeight}px`;
2212
+ pageCard.style.width = `${displayWidth}px`;
2213
+ pageCard.style.height = `${displayHeight}px`;
1710
2214
  const canvasCtx = canvas.getContext("2d");
1711
2215
  if (!canvasCtx) return;
1712
2216
  canvasCtx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
@@ -1720,9 +2224,31 @@ var PdfPlugin = class {
1720
2224
  if (err?.name !== "RenderingCancelledException") {
1721
2225
  console.warn("[PdfPlugin] Page render warning:", err);
1722
2226
  }
2227
+ return;
1723
2228
  } finally {
1724
2229
  currentRenderTask = null;
1725
2230
  }
2231
+ try {
2232
+ textLayerDiv = document.createElement("div");
2233
+ textLayerDiv.className = "textLayer";
2234
+ textLayerDiv.style.width = `${displayWidth}px`;
2235
+ textLayerDiv.style.height = `${displayHeight}px`;
2236
+ textLayerDiv.style.position = "absolute";
2237
+ textLayerDiv.style.top = "0";
2238
+ textLayerDiv.style.left = "0";
2239
+ textLayerDiv.style.lineHeight = "1";
2240
+ pageCard.appendChild(textLayerDiv);
2241
+ if (pdfjsLib__namespace.TextLayer) {
2242
+ const textLayer = new pdfjsLib__namespace.TextLayer({
2243
+ textContentSource: page.streamTextContent(),
2244
+ container: textLayerDiv,
2245
+ viewport
2246
+ });
2247
+ await textLayer.render();
2248
+ }
2249
+ } catch (err) {
2250
+ console.debug("[PdfPlugin] TextLayer notice:", err);
2251
+ }
1726
2252
  };
1727
2253
  renderPage(1);
1728
2254
  let resizeTimer = null;
@@ -1732,11 +2258,48 @@ var PdfPlugin = class {
1732
2258
  if (Math.abs(zoomScale - 1) < 0.05) {
1733
2259
  renderPage(currentPage);
1734
2260
  }
1735
- }, 150);
2261
+ }, 120);
1736
2262
  });
1737
2263
  resizeObserver.observe(container);
2264
+ const onWheel = (e) => {
2265
+ if (e.ctrlKey || e.metaKey) {
2266
+ e.preventDefault();
2267
+ const delta = e.deltaY > 0 ? -0.15 : 0.15;
2268
+ zoomScale = Math.max(0.25, Math.min(4, Math.round((zoomScale + delta) * 100) / 100));
2269
+ renderPage(currentPage);
2270
+ }
2271
+ };
2272
+ container.addEventListener("wheel", onWheel, { passive: false });
2273
+ const onKeyDown = (e) => {
2274
+ const tag = e.target?.tagName?.toLowerCase();
2275
+ if (tag === "input" || tag === "textarea" || tag === "select") return;
2276
+ if (e.key === "ArrowRight" || e.key === "PageDown") {
2277
+ if (currentPage < totalPages) {
2278
+ e.preventDefault();
2279
+ container.scrollTop = 0;
2280
+ renderPage(currentPage + 1);
2281
+ }
2282
+ } else if (e.key === "ArrowLeft" || e.key === "PageUp") {
2283
+ if (currentPage > 1) {
2284
+ e.preventDefault();
2285
+ container.scrollTop = 0;
2286
+ renderPage(currentPage - 1);
2287
+ }
2288
+ } else if (e.key === "Home") {
2289
+ e.preventDefault();
2290
+ container.scrollTop = 0;
2291
+ renderPage(1);
2292
+ } else if (e.key === "End") {
2293
+ e.preventDefault();
2294
+ container.scrollTop = 0;
2295
+ renderPage(totalPages);
2296
+ }
2297
+ };
2298
+ window.addEventListener("keydown", onKeyDown);
1738
2299
  const cleanup = () => {
1739
2300
  resizeObserver.disconnect();
2301
+ window.removeEventListener("keydown", onKeyDown);
2302
+ container.removeEventListener("wheel", onWheel);
1740
2303
  if (resizeTimer) clearTimeout(resizeTimer);
1741
2304
  if (currentRenderTask) {
1742
2305
  try {
@@ -1755,22 +2318,26 @@ var PdfPlugin = class {
1755
2318
  const instance = {
1756
2319
  destroy: cleanup,
1757
2320
  zoomIn: () => {
1758
- zoomScale = Math.min(3.5, zoomScale + 0.2);
2321
+ zoomScale = Math.min(4, Math.round((zoomScale + 0.2) * 10) / 10);
1759
2322
  renderPage(currentPage);
1760
2323
  },
1761
2324
  zoomOut: () => {
1762
- zoomScale = Math.max(0.3, zoomScale - 0.2);
2325
+ zoomScale = Math.max(0.25, Math.round((zoomScale - 0.2) * 10) / 10);
1763
2326
  renderPage(currentPage);
1764
2327
  },
1765
2328
  getZoom: () => zoomScale,
1766
2329
  setZoom: (level) => {
1767
- zoomScale = Math.max(0.3, Math.min(3.5, level));
2330
+ zoomScale = Math.max(0.25, Math.min(4, level));
1768
2331
  renderPage(currentPage);
1769
2332
  },
1770
2333
  fitToPage: () => {
1771
- fitMode = fitMode === "page" ? "width" : "page";
2334
+ fitMode = "page";
2335
+ zoomScale = 1;
2336
+ renderPage(currentPage);
2337
+ },
2338
+ fitToWidth: () => {
2339
+ fitMode = "width";
1772
2340
  zoomScale = 1;
1773
- rotation = 0;
1774
2341
  renderPage(currentPage);
1775
2342
  },
1776
2343
  rotateCW: () => {
@@ -1788,6 +2355,9 @@ var PdfPlugin = class {
1788
2355
  container.scrollTop = 0;
1789
2356
  renderPage(page);
1790
2357
  },
2358
+ toggleThumbnails: () => {
2359
+ ctx?.toggleThumbnails?.();
2360
+ },
1791
2361
  download: () => {
1792
2362
  const blob = new Blob([ctx.buffer], { type: "application/pdf" });
1793
2363
  const url = URL.createObjectURL(blob);
@@ -1821,7 +2391,7 @@ var PdfPlugin = class {
1821
2391
  },
1822
2392
  getThumbnails: async () => {
1823
2393
  const thumbnails = [];
1824
- const count = Math.min(totalPages, 50);
2394
+ const count = totalPages;
1825
2395
  for (let i = 1; i <= count; i++) {
1826
2396
  thumbnails.push({
1827
2397
  index: i,
@@ -1829,9 +2399,10 @@ var PdfPlugin = class {
1829
2399
  render: async (thumbCanvas) => {
1830
2400
  try {
1831
2401
  const p = await pdfDoc.getPage(i);
1832
- const baseVp = p.getViewport({ scale: 1 });
1833
- const thumbScale = (thumbCanvas.width || 120) / baseVp.width;
1834
- const thumbVp = p.getViewport({ scale: thumbScale });
2402
+ const baseVp = p.getViewport({ scale: 1, rotation });
2403
+ const targetW = thumbCanvas.width || 130;
2404
+ const thumbScale = targetW / baseVp.width;
2405
+ const thumbVp = p.getViewport({ scale: thumbScale, rotation });
1835
2406
  thumbCanvas.height = Math.floor(thumbVp.height);
1836
2407
  const tCtx = thumbCanvas.getContext("2d");
1837
2408
  if (tCtx) {
@@ -2175,28 +2746,26 @@ var DocxPlugin = class {
2175
2746
  getToolbarActions(instance) {
2176
2747
  const totalPages = instance.getPageCount?.() ?? 1;
2177
2748
  const actions = [];
2178
- if (totalPages > 1) {
2179
- actions.push({
2180
- id: "page-nav",
2181
- icon: "",
2182
- label: "Page Navigation",
2183
- type: "page-nav",
2184
- group: "navigation",
2185
- value: instance.getCurrentPage?.() ?? 1,
2186
- max: totalPages,
2187
- execute: (action, page) => {
2188
- const cur = instance.getCurrentPage?.() ?? 1;
2189
- const max = instance.getPageCount?.() ?? 1;
2190
- if (action === "prev") {
2191
- if (cur > 1) instance.goToPage?.(cur - 1);
2192
- } else if (action === "next") {
2193
- if (cur < max) instance.goToPage?.(cur + 1);
2194
- } else if (typeof page === "number") {
2195
- instance.goToPage?.(page);
2196
- }
2749
+ actions.push({
2750
+ id: "page-nav",
2751
+ icon: "",
2752
+ label: "Page Navigation",
2753
+ type: "page-nav",
2754
+ group: "navigation",
2755
+ value: instance.getCurrentPage?.() ?? 1,
2756
+ max: totalPages,
2757
+ execute: (action, page) => {
2758
+ const cur = instance.getCurrentPage?.() ?? 1;
2759
+ const max = instance.getPageCount?.() ?? 1;
2760
+ if (action === "prev") {
2761
+ if (cur > 1) instance.goToPage?.(cur - 1);
2762
+ } else if (action === "next") {
2763
+ if (cur < max) instance.goToPage?.(cur + 1);
2764
+ } else if (typeof page === "number") {
2765
+ instance.goToPage?.(page);
2197
2766
  }
2198
- });
2199
- }
2767
+ }
2768
+ });
2200
2769
  actions.push(
2201
2770
  {
2202
2771
  id: "zoom-out",
@@ -2262,11 +2831,17 @@ var DocxPlugin = class {
2262
2831
  wrapper.className = "fp-docx-wrapper";
2263
2832
  wrapper.style.transformOrigin = "top center";
2264
2833
  wrapper.style.transition = "transform 0.2s ease";
2265
- wrapper.style.padding = "24px 16px";
2266
- wrapper.style.maxWidth = "950px";
2834
+ wrapper.style.padding = "0";
2835
+ wrapper.style.maxWidth = "none";
2836
+ wrapper.style.width = "816px";
2267
2837
  wrapper.style.margin = "0 auto";
2838
+ wrapper.style.display = "flex";
2839
+ wrapper.style.flexDirection = "column";
2840
+ wrapper.style.alignItems = "center";
2268
2841
  wrapper.style.boxSizing = "border-box";
2269
- ctx.container.style.overflow = "auto";
2842
+ ctx.container.style.overflowX = "hidden";
2843
+ ctx.container.style.overflowY = "auto";
2844
+ ctx.container.style.padding = "16px 8px";
2270
2845
  ctx.container.style.backgroundColor = "#f1f5f9";
2271
2846
  ctx.container.appendChild(wrapper);
2272
2847
  const styleOverride = document.createElement("style");
@@ -2440,39 +3015,13 @@ var DocxPlugin = class {
2440
3015
  const pageElements = sections.length > 0 ? sections : cards;
2441
3016
  const totalPages = Math.max(1, pageElements.length);
2442
3017
  let currentPage = 1;
2443
- let indicator = null;
2444
- if (totalPages > 1) {
2445
- indicator = document.createElement("div");
2446
- indicator.className = "fp-docx-page-indicator";
2447
- indicator.style.position = "sticky";
2448
- indicator.style.bottom = "16px";
2449
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
2450
- indicator.style.backdropFilter = "blur(8px)";
2451
- indicator.style.color = "#f8fafc";
2452
- indicator.style.fontSize = "12px";
2453
- indicator.style.fontWeight = "600";
2454
- indicator.style.padding = "5px 14px";
2455
- indicator.style.borderRadius = "20px";
2456
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
2457
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
2458
- indicator.style.zIndex = "10";
2459
- indicator.style.userSelect = "none";
2460
- indicator.style.pointerEvents = "none";
2461
- indicator.style.textAlign = "center";
2462
- indicator.style.width = "fit-content";
2463
- indicator.style.margin = "16px auto 0";
2464
- ctx.container.appendChild(indicator);
2465
- }
2466
3018
  const showPage = (pageNum) => {
2467
3019
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
2468
3020
  if (pageElements.length > 1) {
2469
3021
  pageElements.forEach((sec, idx) => {
2470
- sec.style.display = idx + 1 === currentPage ? "block" : "none";
3022
+ sec.style.display = idx + 1 === currentPage ? "" : "none";
2471
3023
  });
2472
3024
  }
2473
- if (indicator) {
2474
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
2475
- }
2476
3025
  ctx.container.scrollTop = 0;
2477
3026
  ctx.emit("page-change", { page: currentPage, total: totalPages });
2478
3027
  };
@@ -2481,31 +3030,46 @@ var DocxPlugin = class {
2481
3030
  }
2482
3031
  scale = 1;
2483
3032
  let rotation = 0;
2484
- let fitMode = "width";
2485
- const calculateFitScale = (mode = fitMode) => {
2486
- const activeEl = pageElements[currentPage - 1] || wrapper.firstElementChild || wrapper;
3033
+ let fitMode = "page";
3034
+ let isUserZoomed = false;
3035
+ const calculateFitScale = (_mode = fitMode) => {
3036
+ const activeEl = pageElements[currentPage - 1] || wrapper.querySelector("section.docx") || wrapper;
2487
3037
  const elW = activeEl.offsetWidth || 816;
2488
- const singlePageH = Math.min(activeEl.offsetHeight || 1056, Math.round(elW * 1.32));
2489
- const availW = Math.max(280, ctx.container.clientWidth - 48);
2490
- const availH = Math.max(280, ctx.container.clientHeight - 80);
3038
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
2491
3039
  const sW = availW / elW;
2492
- const sH = availH / singlePageH;
2493
- if (mode === "page") {
2494
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
2495
- }
2496
- return Math.max(0.65, Math.min(1.05, sW));
3040
+ return Math.max(0.35, Math.min(3, sW));
2497
3041
  };
2498
3042
  const applyTransform = () => {
2499
3043
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
2500
3044
  wrapper.style.transformOrigin = "top center";
3045
+ const activeEl = pageElements[currentPage - 1] || wrapper.querySelector("section.docx") || wrapper;
3046
+ const elH = activeEl.offsetHeight || 1056;
3047
+ if (scale > 1) {
3048
+ wrapper.style.marginBottom = `${Math.round(elH * scale - elH + 32)}px`;
3049
+ } else {
3050
+ wrapper.style.marginBottom = "32px";
3051
+ }
2501
3052
  };
3053
+ let resizeObserver = null;
3054
+ if (typeof ResizeObserver !== "undefined") {
3055
+ resizeObserver = new ResizeObserver(() => {
3056
+ if (!isUserZoomed) {
3057
+ const newFit = calculateFitScale("page");
3058
+ if (Math.abs(scale - newFit) > 0.015) {
3059
+ scale = newFit;
3060
+ applyTransform();
3061
+ }
3062
+ }
3063
+ });
3064
+ resizeObserver.observe(ctx.container);
3065
+ }
2502
3066
  setTimeout(() => {
2503
- fitMode = "width";
2504
- scale = calculateFitScale("width");
3067
+ scale = calculateFitScale("page");
2505
3068
  applyTransform();
2506
- }, 60);
3069
+ }, 40);
2507
3070
  const cleanup = () => {
2508
- indicator?.remove();
3071
+ resizeObserver?.disconnect();
3072
+ resizeObserver = null;
2509
3073
  for (const url of createdBlobUrls) {
2510
3074
  URL.revokeObjectURL(url);
2511
3075
  }
@@ -2522,21 +3086,24 @@ var DocxPlugin = class {
2522
3086
  showPage(page);
2523
3087
  },
2524
3088
  zoomIn: () => {
2525
- scale += 0.15;
3089
+ isUserZoomed = true;
3090
+ scale = Math.min(3.5, scale + 0.15);
2526
3091
  applyTransform();
2527
3092
  },
2528
3093
  zoomOut: () => {
3094
+ isUserZoomed = true;
2529
3095
  scale = Math.max(0.2, scale - 0.15);
2530
3096
  applyTransform();
2531
3097
  },
2532
3098
  getZoom: () => scale,
2533
3099
  setZoom: (level) => {
3100
+ isUserZoomed = true;
2534
3101
  scale = level;
2535
3102
  applyTransform();
2536
3103
  },
2537
3104
  fitToPage: () => {
2538
- fitMode = fitMode === "width" ? "page" : "width";
2539
- scale = calculateFitScale(fitMode);
3105
+ isUserZoomed = false;
3106
+ scale = calculateFitScale("page");
2540
3107
  rotation = 0;
2541
3108
  applyTransform();
2542
3109
  },
@@ -2926,7 +3493,7 @@ var ExcelPlugin = class {
2926
3493
  "application/vnd.ms-excel.template.macroEnabled.12",
2927
3494
  "application/vnd.oasis.opendocument.spreadsheet"
2928
3495
  ];
2929
- weight = 80;
3496
+ weight = 85;
2930
3497
  supports(file) {
2931
3498
  const ext = file.metadata.extension?.toLowerCase();
2932
3499
  const mime = file.metadata.mimeType?.toLowerCase();
@@ -2938,6 +3505,14 @@ var ExcelPlugin = class {
2938
3505
  getToolbarActions(instance) {
2939
3506
  const totalSheets = instance.getPageCount?.() ?? 1;
2940
3507
  const actions = [];
3508
+ actions.push({
3509
+ id: "thumbnails",
3510
+ icon: "thumbnails",
3511
+ label: "Sheet Thumbnails",
3512
+ type: "button",
3513
+ group: "navigation",
3514
+ execute: () => instance.toggleThumbnails?.()
3515
+ });
2941
3516
  if (totalSheets > 1) {
2942
3517
  actions.push({
2943
3518
  id: "page-nav",
@@ -2981,26 +3556,6 @@ var ExcelPlugin = class {
2981
3556
  instance.zoomIn?.();
2982
3557
  }
2983
3558
  },
2984
- {
2985
- id: "fit-page",
2986
- icon: "fit-page",
2987
- label: "Fit to View",
2988
- type: "button",
2989
- group: "zoom",
2990
- execute: () => {
2991
- instance.fitToPage?.();
2992
- }
2993
- },
2994
- {
2995
- id: "rotate-cw",
2996
- icon: "rotate-cw",
2997
- label: "Rotate",
2998
- type: "button",
2999
- group: "view",
3000
- execute: () => {
3001
- instance.rotateCW?.();
3002
- }
3003
- },
3004
3559
  {
3005
3560
  id: "download",
3006
3561
  icon: "download",
@@ -3020,6 +3575,16 @@ var ExcelPlugin = class {
3020
3575
  execute: () => {
3021
3576
  instance.print?.();
3022
3577
  }
3578
+ },
3579
+ {
3580
+ id: "open-window",
3581
+ icon: "open-window",
3582
+ label: "Open in Separate Full Window",
3583
+ type: "button",
3584
+ group: "actions",
3585
+ execute: () => {
3586
+ instance.openInSeparateWindow?.();
3587
+ }
3023
3588
  }
3024
3589
  );
3025
3590
  return actions;
@@ -3051,12 +3616,11 @@ var ExcelPlugin = class {
3051
3616
  container.appendChild(tabsArea);
3052
3617
  ctx.container.appendChild(container);
3053
3618
  let scale = 1;
3054
- let rotation = 0;
3055
3619
  let currentSheetIndex = 1;
3056
3620
  let sheetNames = [];
3057
3621
  let wb = null;
3058
3622
  const applyTransform = () => {
3059
- contentArea.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
3623
+ contentArea.style.transform = `scale(${scale})`;
3060
3624
  contentArea.style.transformOrigin = "top left";
3061
3625
  };
3062
3626
  const calculateFitScale = () => {
@@ -3064,7 +3628,7 @@ var ExcelPlugin = class {
3064
3628
  if (!table) return 1;
3065
3629
  const availW = Math.max(280, container.clientWidth - 48);
3066
3630
  const tW = table.offsetWidth || 800;
3067
- return Math.max(0.65, Math.min(1, availW / tW));
3631
+ return Math.max(0.4, Math.min(1, availW / tW));
3068
3632
  };
3069
3633
  try {
3070
3634
  wb = XLSX__namespace.read(new Uint8Array(ctx.buffer), { type: "array", cellDates: true });
@@ -3119,7 +3683,16 @@ var ExcelPlugin = class {
3119
3683
  }
3120
3684
  });
3121
3685
  ctx.emit("page-change", { page: currentSheetIndex, total: sheetNames.length });
3686
+ requestAnimationFrame(() => {
3687
+ scale = calculateFitScale();
3688
+ applyTransform();
3689
+ });
3122
3690
  };
3691
+ const ro = new ResizeObserver(() => {
3692
+ scale = calculateFitScale();
3693
+ applyTransform();
3694
+ });
3695
+ ro.observe(container);
3123
3696
  if (sheetNames.length > 0) {
3124
3697
  sheetNames.forEach((name, idx) => {
3125
3698
  const btn = document.createElement("button");
@@ -3146,6 +3719,7 @@ var ExcelPlugin = class {
3146
3719
  `;
3147
3720
  }
3148
3721
  const cleanup = () => {
3722
+ ro.disconnect();
3149
3723
  container.remove();
3150
3724
  ctx.container.innerHTML = "";
3151
3725
  };
@@ -3167,16 +3741,11 @@ var ExcelPlugin = class {
3167
3741
  },
3168
3742
  fitToPage: () => {
3169
3743
  scale = calculateFitScale();
3170
- rotation = 0;
3171
3744
  applyTransform();
3172
3745
  },
3173
3746
  rotateCW: () => {
3174
- rotation = (rotation + 90) % 360;
3175
- applyTransform();
3176
3747
  },
3177
3748
  rotateCCW: () => {
3178
- rotation = (rotation - 90 + 360) % 360;
3179
- applyTransform();
3180
3749
  },
3181
3750
  goToPage: (page) => {
3182
3751
  if (page > 0 && page <= sheetNames.length) {
@@ -3185,6 +3754,63 @@ var ExcelPlugin = class {
3185
3754
  },
3186
3755
  getPageCount: () => sheetNames.length,
3187
3756
  getCurrentPage: () => currentSheetIndex,
3757
+ getThumbnails: () => {
3758
+ return sheetNames.map((name, idx) => ({
3759
+ index: idx,
3760
+ label: name,
3761
+ render: async (canvas) => {
3762
+ canvas.width = 140;
3763
+ canvas.height = 100;
3764
+ const c = canvas.getContext("2d");
3765
+ if (!c) return;
3766
+ c.fillStyle = "#ffffff";
3767
+ c.fillRect(0, 0, canvas.width, canvas.height);
3768
+ c.fillStyle = "#107c41";
3769
+ c.fillRect(0, 0, canvas.width, 16);
3770
+ c.fillStyle = "#ffffff";
3771
+ c.font = 'bold 8px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
3772
+ c.fillText(`\u{1F4CA} ${name.slice(0, 16)}`, 6, 11);
3773
+ const startY = 17;
3774
+ const rowH = 13;
3775
+ const colW = 30;
3776
+ const colHeaders = ["A", "B", "C", "D"];
3777
+ c.fillStyle = "#f1f5f9";
3778
+ c.fillRect(0, startY, canvas.width, rowH);
3779
+ c.strokeStyle = "#cbd5e1";
3780
+ c.lineWidth = 0.8;
3781
+ c.strokeRect(0, startY, canvas.width, rowH);
3782
+ c.fillStyle = "#64748b";
3783
+ c.font = "bold 7px sans-serif";
3784
+ for (let ci = 0; ci < colHeaders.length; ci++) {
3785
+ c.fillText(colHeaders[ci], 14 + ci * colW, startY + 9);
3786
+ }
3787
+ const ws = wb?.Sheets[name];
3788
+ for (let ri = 1; ri <= 5; ri++) {
3789
+ const y = startY + ri * rowH;
3790
+ if (y > canvas.height - 2) break;
3791
+ c.fillStyle = "#f8fafc";
3792
+ c.fillRect(0, y, 12, rowH);
3793
+ c.fillStyle = "#94a3b8";
3794
+ c.font = "6px sans-serif";
3795
+ c.fillText(String(ri), 3, y + 9);
3796
+ c.strokeStyle = "#e2e8f0";
3797
+ c.strokeRect(0, y, canvas.width, rowH);
3798
+ c.fillStyle = "#334155";
3799
+ c.font = "6px sans-serif";
3800
+ for (let ci = 0; ci < colHeaders.length; ci++) {
3801
+ const cellRef = `${colHeaders[ci]}${ri}`;
3802
+ const cellVal = ws?.[cellRef]?.w || ws?.[cellRef]?.v;
3803
+ if (cellVal !== void 0) {
3804
+ c.fillText(String(cellVal).slice(0, 6), 14 + ci * colW, y + 9);
3805
+ }
3806
+ }
3807
+ }
3808
+ c.strokeStyle = "#cbd5e1";
3809
+ c.lineWidth = 1;
3810
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
3811
+ }
3812
+ }));
3813
+ },
3188
3814
  download: () => {
3189
3815
  const blob = new Blob([ctx.buffer], { type: this.mimeTypes[0] });
3190
3816
  const url = URL.createObjectURL(blob);
@@ -3217,7 +3843,16 @@ var CsvPlugin = class {
3217
3843
  return ext === ".csv" || ext === ".tsv" || mime === "text/csv" || mime === "text/tab-separated-values";
3218
3844
  }
3219
3845
  getToolbarActions(instance) {
3220
- return [
3846
+ const actions = [];
3847
+ actions.push({
3848
+ id: "thumbnails",
3849
+ icon: "thumbnails",
3850
+ label: "Sheet Thumbnails",
3851
+ type: "button",
3852
+ group: "navigation",
3853
+ execute: () => instance.toggleThumbnails?.()
3854
+ });
3855
+ actions.push(
3221
3856
  {
3222
3857
  id: "zoom-out",
3223
3858
  icon: "zoom-out",
@@ -3257,8 +3892,19 @@ var CsvPlugin = class {
3257
3892
  execute: () => {
3258
3893
  instance.print?.();
3259
3894
  }
3895
+ },
3896
+ {
3897
+ id: "open-window",
3898
+ icon: "open-window",
3899
+ label: "Open in Separate Full Window",
3900
+ type: "button",
3901
+ group: "actions",
3902
+ execute: () => {
3903
+ instance.openInSeparateWindow?.();
3904
+ }
3260
3905
  }
3261
- ];
3906
+ );
3907
+ return actions;
3262
3908
  }
3263
3909
  async render(ctx) {
3264
3910
  const decoder = new TextDecoder("utf-8");
@@ -3269,57 +3915,156 @@ var CsvPlugin = class {
3269
3915
  container.style.overflow = "auto";
3270
3916
  container.style.padding = "16px";
3271
3917
  container.style.boxSizing = "border-box";
3272
- container.style.transformOrigin = "top left";
3918
+ const tableWrapper = document.createElement("div");
3919
+ tableWrapper.style.transformOrigin = "top left";
3920
+ const delimiter = ctx.metadata.extension === ".tsv" ? " " : ",";
3921
+ const allLines = text.split(/\r?\n/).filter((r) => r.trim().length > 0);
3922
+ const headerLine = allLines[0] || "";
3923
+ const headerCells = headerLine.split(delimiter);
3924
+ const dataLines = allLines.slice(1);
3273
3925
  const table = document.createElement("table");
3274
3926
  table.style.borderCollapse = "collapse";
3275
- table.style.width = "100%";
3276
- table.style.fontFamily = "monospace";
3927
+ table.style.minWidth = "100%";
3928
+ table.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace';
3277
3929
  table.style.fontSize = "13px";
3278
- const delimiter = ctx.metadata.extension === ".tsv" ? " " : ",";
3279
- const rows = text.split(/\r?\n/).slice(0, 500);
3280
- rows.forEach((row, rowIndex) => {
3281
- if (!row.trim()) return;
3930
+ table.style.background = "#ffffff";
3931
+ table.style.border = "1px solid #d0d7de";
3932
+ const headerTr = document.createElement("tr");
3933
+ headerCells.forEach((cell) => {
3934
+ const th = document.createElement("th");
3935
+ th.textContent = cell.trim();
3936
+ th.style.border = "1px solid #d0d7de";
3937
+ th.style.padding = "8px 12px";
3938
+ th.style.backgroundColor = "#f6f8fa";
3939
+ th.style.fontWeight = "600";
3940
+ th.style.whiteSpace = "nowrap";
3941
+ headerTr.appendChild(th);
3942
+ });
3943
+ table.appendChild(headerTr);
3944
+ dataLines.forEach((row, idx) => {
3282
3945
  const tr = document.createElement("tr");
3946
+ if (idx % 2 === 1) tr.style.backgroundColor = "#f8fafc";
3283
3947
  const cells = row.split(delimiter);
3284
3948
  cells.forEach((cell) => {
3285
- const td = document.createElement(rowIndex === 0 ? "th" : "td");
3949
+ const td = document.createElement("td");
3286
3950
  td.textContent = cell.trim();
3287
3951
  td.style.border = "1px solid #d0d7de";
3288
3952
  td.style.padding = "6px 12px";
3289
- if (rowIndex === 0) {
3290
- td.style.backgroundColor = "#f6f8fa";
3291
- td.style.fontWeight = "bold";
3292
- }
3953
+ td.style.whiteSpace = "nowrap";
3293
3954
  tr.appendChild(td);
3294
3955
  });
3295
3956
  table.appendChild(tr);
3296
3957
  });
3297
- container.appendChild(table);
3958
+ tableWrapper.appendChild(table);
3959
+ container.appendChild(tableWrapper);
3298
3960
  ctx.container.appendChild(container);
3299
3961
  let scale = 1;
3962
+ const calculateFitScale = () => {
3963
+ const availW = Math.max(280, container.clientWidth - 48);
3964
+ const tW = table.offsetWidth || 800;
3965
+ return Math.max(0.4, Math.min(1, availW / tW));
3966
+ };
3967
+ const applyScale = () => {
3968
+ tableWrapper.style.transform = `scale(${scale})`;
3969
+ };
3970
+ requestAnimationFrame(() => {
3971
+ scale = calculateFitScale();
3972
+ applyScale();
3973
+ });
3974
+ const ro = new ResizeObserver(() => {
3975
+ scale = calculateFitScale();
3976
+ applyScale();
3977
+ });
3978
+ ro.observe(container);
3300
3979
  const cleanup = () => {
3980
+ ro.disconnect();
3301
3981
  container.remove();
3302
3982
  ctx.container.innerHTML = "";
3303
3983
  };
3304
3984
  ctx.signal.addEventListener("abort", cleanup);
3305
3985
  return {
3306
3986
  destroy: cleanup,
3987
+ getPageCount: () => 1,
3988
+ getCurrentPage: () => 1,
3989
+ goToPage: () => {
3990
+ },
3991
+ getThumbnails: () => {
3992
+ const sheetName = ctx.metadata.name?.replace(/\.[^/.]+$/, "") || "Sheet1";
3993
+ return [{
3994
+ index: 0,
3995
+ label: sheetName,
3996
+ render: async (canvas) => {
3997
+ canvas.width = 140;
3998
+ canvas.height = 100;
3999
+ const c = canvas.getContext("2d");
4000
+ if (!c) return;
4001
+ c.fillStyle = "#ffffff";
4002
+ c.fillRect(0, 0, canvas.width, canvas.height);
4003
+ c.fillStyle = "#107c41";
4004
+ c.fillRect(0, 0, canvas.width, 16);
4005
+ c.fillStyle = "#ffffff";
4006
+ c.font = 'bold 8px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
4007
+ c.fillText(`\u{1F4CA} ${sheetName.slice(0, 16)}`, 6, 11);
4008
+ const startY = 17;
4009
+ const rowH = 13;
4010
+ const colW = 30;
4011
+ const cols = headerCells.slice(0, 4).map((h) => h.trim() || "Col");
4012
+ c.fillStyle = "#f1f5f9";
4013
+ c.fillRect(0, startY, canvas.width, rowH);
4014
+ c.strokeStyle = "#cbd5e1";
4015
+ c.lineWidth = 0.8;
4016
+ c.strokeRect(0, startY, canvas.width, rowH);
4017
+ c.fillStyle = "#64748b";
4018
+ c.font = "bold 7px sans-serif";
4019
+ for (let ci = 0; ci < cols.length; ci++) {
4020
+ c.fillText(cols[ci].slice(0, 5), 14 + ci * colW, startY + 9);
4021
+ }
4022
+ for (let ri = 0; ri < Math.min(5, dataLines.length); ri++) {
4023
+ const y = startY + (ri + 1) * rowH;
4024
+ if (y > canvas.height - 2) break;
4025
+ c.fillStyle = "#f8fafc";
4026
+ c.fillRect(0, y, 12, rowH);
4027
+ c.fillStyle = "#94a3b8";
4028
+ c.font = "6px sans-serif";
4029
+ c.fillText(String(ri + 1), 3, y + 9);
4030
+ c.strokeStyle = "#e2e8f0";
4031
+ c.strokeRect(0, y, canvas.width, rowH);
4032
+ const rowCells = dataLines[ri].split(delimiter);
4033
+ c.fillStyle = "#334155";
4034
+ c.font = "6px sans-serif";
4035
+ for (let ci = 0; ci < cols.length; ci++) {
4036
+ const val = (rowCells[ci] || "").trim();
4037
+ if (val) {
4038
+ c.fillText(val.slice(0, 6), 14 + ci * colW, y + 9);
4039
+ }
4040
+ }
4041
+ }
4042
+ c.strokeStyle = "#cbd5e1";
4043
+ c.lineWidth = 1;
4044
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
4045
+ }
4046
+ }];
4047
+ },
3307
4048
  zoomIn: () => {
3308
4049
  scale += 0.1;
3309
- container.style.transform = `scale(${scale})`;
4050
+ applyScale();
3310
4051
  },
3311
4052
  zoomOut: () => {
3312
4053
  scale = Math.max(0.2, scale - 0.1);
3313
- container.style.transform = `scale(${scale})`;
4054
+ applyScale();
3314
4055
  },
3315
4056
  getZoom: () => scale,
3316
4057
  setZoom: (level) => {
3317
4058
  scale = level;
3318
- container.style.transform = `scale(${scale})`;
4059
+ applyScale();
3319
4060
  },
3320
4061
  fitToPage: () => {
3321
- scale = 1;
3322
- container.style.transform = `scale(1)`;
4062
+ scale = calculateFitScale();
4063
+ applyScale();
4064
+ },
4065
+ rotateCW: () => {
4066
+ },
4067
+ rotateCCW: () => {
3323
4068
  },
3324
4069
  download: () => {
3325
4070
  const blob = new Blob([ctx.buffer], { type: "text/csv" });
@@ -3383,28 +4128,34 @@ var CodePlugin = class {
3383
4128
  getToolbarActions(instance) {
3384
4129
  const totalPages = instance.getPageCount?.() ?? 1;
3385
4130
  const actions = [];
3386
- if (totalPages > 1) {
3387
- actions.push({
3388
- id: "page-nav",
3389
- icon: "",
3390
- label: "Page Navigation",
3391
- type: "page-nav",
3392
- group: "navigation",
3393
- value: instance.getCurrentPage?.() ?? 1,
3394
- max: totalPages,
3395
- execute: (action, page) => {
3396
- const cur = instance.getCurrentPage?.() ?? 1;
3397
- const max = instance.getPageCount?.() ?? 1;
3398
- if (action === "prev") {
3399
- if (cur > 1) instance.goToPage?.(cur - 1);
3400
- } else if (action === "next") {
3401
- if (cur < max) instance.goToPage?.(cur + 1);
3402
- } else if (typeof page === "number") {
3403
- instance.goToPage?.(page);
3404
- }
4131
+ actions.push({
4132
+ id: "thumbnails",
4133
+ icon: "thumbnails",
4134
+ label: "Page Thumbnails",
4135
+ type: "button",
4136
+ group: "navigation",
4137
+ execute: () => instance.toggleThumbnails?.()
4138
+ });
4139
+ actions.push({
4140
+ id: "page-nav",
4141
+ icon: "",
4142
+ label: "Page Navigation",
4143
+ type: "page-nav",
4144
+ group: "navigation",
4145
+ value: instance.getCurrentPage?.() ?? 1,
4146
+ max: totalPages,
4147
+ execute: (action, page) => {
4148
+ const cur = instance.getCurrentPage?.() ?? 1;
4149
+ const max = instance.getPageCount?.() ?? 1;
4150
+ if (action === "prev") {
4151
+ if (cur > 1) instance.goToPage?.(cur - 1);
4152
+ } else if (action === "next") {
4153
+ if (cur < max) instance.goToPage?.(cur + 1);
4154
+ } else if (typeof page === "number") {
4155
+ instance.goToPage?.(page);
3405
4156
  }
3406
- });
3407
- }
4157
+ }
4158
+ });
3408
4159
  actions.push(
3409
4160
  {
3410
4161
  id: "zoom-out",
@@ -3426,26 +4177,6 @@ var CodePlugin = class {
3426
4177
  instance.zoomIn?.();
3427
4178
  }
3428
4179
  },
3429
- {
3430
- id: "fit-page",
3431
- icon: "fit-page",
3432
- label: "Fit to View",
3433
- type: "button",
3434
- group: "zoom",
3435
- execute: () => {
3436
- instance.fitToPage?.();
3437
- }
3438
- },
3439
- {
3440
- id: "rotate-cw",
3441
- icon: "rotate-cw",
3442
- label: "Rotate",
3443
- type: "button",
3444
- group: "view",
3445
- execute: () => {
3446
- instance.rotateCW?.();
3447
- }
3448
- },
3449
4180
  {
3450
4181
  id: "copy",
3451
4182
  icon: "copy",
@@ -3548,16 +4279,19 @@ var CodePlugin = class {
3548
4279
  let fontSize = 13;
3549
4280
  let rotation = 0;
3550
4281
  let zoomLevel = 1;
4282
+ let isUserZoomed = false;
3551
4283
  const pre = document.createElement("pre");
3552
4284
  const code = document.createElement("code");
3553
4285
  if (isTxt) {
4286
+ container.style.overflowX = "hidden";
4287
+ container.style.overflowY = "auto";
3554
4288
  container.style.backgroundColor = "#f1f5f9";
3555
- container.style.padding = "32px";
4289
+ container.style.padding = "16px 8px";
3556
4290
  container.style.boxSizing = "border-box";
3557
4291
  container.style.display = "flex";
3558
4292
  container.style.flexDirection = "column";
3559
4293
  container.style.alignItems = "center";
3560
- pre.style.margin = "0";
4294
+ pre.style.margin = "0 auto";
3561
4295
  pre.style.fontFamily = "Consolas, 'Courier New', monospace";
3562
4296
  pre.style.fontSize = "13px";
3563
4297
  pre.style.color = "#1e293b";
@@ -3569,10 +4303,12 @@ var CodePlugin = class {
3569
4303
  pre.style.minHeight = "1056px";
3570
4304
  pre.style.padding = "72px 56px";
3571
4305
  pre.style.boxSizing = "border-box";
3572
- pre.style.boxShadow = "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)";
4306
+ pre.style.boxShadow = "0 4px 24px rgba(0, 0, 0, 0.08)";
3573
4307
  pre.style.borderRadius = "4px";
3574
4308
  pre.style.transformOrigin = "top center";
4309
+ pre.style.transition = "transform 0.15s ease";
3575
4310
  } else {
4311
+ container.style.overflow = "auto";
3576
4312
  container.style.backgroundColor = "#1e1e1e";
3577
4313
  container.style.color = "#d4d4d4";
3578
4314
  container.style.padding = "16px";
@@ -3604,42 +4340,9 @@ var CodePlugin = class {
3604
4340
  renderCodePage(rawPages[0] || (isTxt ? "" : fullText));
3605
4341
  pre.appendChild(code);
3606
4342
  container.appendChild(pre);
3607
- let indicator = null;
3608
- if (totalPages > 1) {
3609
- indicator = document.createElement("div");
3610
- indicator.className = "fp-code-page-indicator";
3611
- indicator.style.position = "sticky";
3612
- indicator.style.bottom = "16px";
3613
- if (isTxt) {
3614
- indicator.style.backgroundColor = "rgba(255, 255, 255, 0.9)";
3615
- indicator.style.color = "#334155";
3616
- indicator.style.border = "1px solid #e2e8f0";
3617
- indicator.style.boxShadow = "0 1px 3px rgba(0,0,0,0.1)";
3618
- } else {
3619
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
3620
- indicator.style.color = "#f8fafc";
3621
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
3622
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
3623
- indicator.style.backdropFilter = "blur(8px)";
3624
- }
3625
- indicator.style.fontSize = "12px";
3626
- indicator.style.fontWeight = "600";
3627
- indicator.style.padding = "5px 14px";
3628
- indicator.style.borderRadius = "20px";
3629
- indicator.style.zIndex = "10";
3630
- indicator.style.userSelect = "none";
3631
- indicator.style.pointerEvents = "none";
3632
- indicator.style.textAlign = "center";
3633
- indicator.style.width = "fit-content";
3634
- indicator.style.margin = "16px auto 0";
3635
- container.appendChild(indicator);
3636
- }
3637
4343
  const showPage = (pageNum) => {
3638
4344
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
3639
4345
  renderCodePage(rawPages[currentPage - 1] || (isTxt ? "" : fullText));
3640
- if (indicator) {
3641
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
3642
- }
3643
4346
  container.scrollTop = 0;
3644
4347
  ctx.emit("page-change", { page: currentPage, total: totalPages });
3645
4348
  };
@@ -3647,36 +4350,90 @@ var CodePlugin = class {
3647
4350
  showPage(1);
3648
4351
  }
3649
4352
  ctx.container.appendChild(container);
3650
- const cleanup = () => {
3651
- indicator?.remove();
3652
- container.remove();
3653
- ctx.container.innerHTML = "";
4353
+ const calculateTxtFit = () => {
4354
+ const availW = Math.max(280, container.clientWidth - 32);
4355
+ return Math.max(0.4, Math.min(3, availW / 816));
3654
4356
  };
3655
- ctx.signal.addEventListener("abort", cleanup);
3656
4357
  const updateTransform = () => {
3657
4358
  if (isTxt) {
3658
4359
  pre.style.transform = `scale(${zoomLevel}) rotate(${rotation}deg)`;
4360
+ const scaledH = 1056 * zoomLevel;
4361
+ const extraH = Math.max(0, scaledH - 1056);
4362
+ pre.style.marginBottom = `${extraH + 32}px`;
3659
4363
  } else {
3660
4364
  pre.style.transform = `rotate(${rotation}deg)`;
3661
4365
  pre.style.fontSize = `${fontSize}px`;
3662
4366
  }
3663
4367
  };
4368
+ let resizeObserver = null;
4369
+ if (isTxt) {
4370
+ setTimeout(() => {
4371
+ zoomLevel = calculateTxtFit();
4372
+ updateTransform();
4373
+ }, 60);
4374
+ resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
4375
+ if (!isUserZoomed) {
4376
+ zoomLevel = calculateTxtFit();
4377
+ updateTransform();
4378
+ }
4379
+ }) : null;
4380
+ resizeObserver?.observe(container);
4381
+ }
4382
+ const cleanup = () => {
4383
+ resizeObserver?.disconnect();
4384
+ container.remove();
4385
+ ctx.container.innerHTML = "";
4386
+ };
4387
+ ctx.signal.addEventListener("abort", cleanup);
3664
4388
  return {
3665
4389
  destroy: cleanup,
3666
4390
  getPageCount: () => totalPages,
3667
4391
  getCurrentPage: () => currentPage,
3668
4392
  goToPage: (page) => showPage(page),
4393
+ getThumbnails: () => {
4394
+ return rawPages.map((pageText, idx) => ({
4395
+ index: idx,
4396
+ label: `Page ${idx + 1}`,
4397
+ render: async (canvas) => {
4398
+ canvas.width = 140;
4399
+ canvas.height = 180;
4400
+ const c = canvas.getContext("2d");
4401
+ if (!c) return;
4402
+ c.fillStyle = "#ffffff";
4403
+ c.fillRect(0, 0, canvas.width, canvas.height);
4404
+ c.strokeStyle = "#cbd5e1";
4405
+ c.lineWidth = 1;
4406
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
4407
+ c.fillStyle = "#64748b";
4408
+ const lines = (pageText || "").split("\n").slice(0, 24);
4409
+ const lineH = 6;
4410
+ const startY = 14;
4411
+ const startX = 12;
4412
+ const maxW = canvas.width - 24;
4413
+ c.font = "5px monospace";
4414
+ for (let li = 0; li < lines.length; li++) {
4415
+ const l = lines[li].trim();
4416
+ if (!l) continue;
4417
+ const y = startY + li * lineH;
4418
+ if (y > canvas.height - 12) break;
4419
+ c.fillText(l.slice(0, 30), startX, y, maxW);
4420
+ }
4421
+ }
4422
+ }));
4423
+ },
3669
4424
  zoomIn: () => {
4425
+ isUserZoomed = true;
3670
4426
  if (isTxt) {
3671
- zoomLevel = Math.min(3, zoomLevel + 0.1);
4427
+ zoomLevel = Math.min(3.5, zoomLevel + 0.15);
3672
4428
  } else {
3673
4429
  fontSize = Math.min(32, fontSize + 2);
3674
4430
  }
3675
4431
  updateTransform();
3676
4432
  },
3677
4433
  zoomOut: () => {
4434
+ isUserZoomed = true;
3678
4435
  if (isTxt) {
3679
- zoomLevel = Math.max(0.1, zoomLevel - 0.1);
4436
+ zoomLevel = Math.max(0.1, zoomLevel - 0.15);
3680
4437
  } else {
3681
4438
  fontSize = Math.max(8, fontSize - 2);
3682
4439
  }
@@ -3684,6 +4441,7 @@ var CodePlugin = class {
3684
4441
  },
3685
4442
  getZoom: () => isTxt ? zoomLevel : fontSize / 13,
3686
4443
  setZoom: (level) => {
4444
+ isUserZoomed = true;
3687
4445
  if (isTxt) {
3688
4446
  zoomLevel = level;
3689
4447
  } else {
@@ -3692,9 +4450,10 @@ var CodePlugin = class {
3692
4450
  updateTransform();
3693
4451
  },
3694
4452
  fitToPage: () => {
4453
+ isUserZoomed = false;
3695
4454
  fontSize = 13;
3696
4455
  rotation = 0;
3697
- zoomLevel = isTxt ? Math.max(0.65, Math.min(1.05, (container.clientWidth - 48) / 816)) : 1;
4456
+ zoomLevel = isTxt ? calculateTxtFit() : 1;
3698
4457
  updateTransform();
3699
4458
  },
3700
4459
  rotateCW: () => {
@@ -4561,6 +5320,148 @@ var ThreeDPlugin = class {
4561
5320
  function threeDPlugin() {
4562
5321
  return new ThreeDPlugin();
4563
5322
  }
5323
+ function cleanRtfText(raw) {
5324
+ return raw.replace(/\\bin\d+\s/g, "").replace(/{\\\*[^}]+}/g, "").replace(/\\u(-?\d+)\??/g, (_, code) => {
5325
+ let num = parseInt(code, 10);
5326
+ if (num < 0) num += 65536;
5327
+ return String.fromCharCode(num);
5328
+ }).replace(/\\'([0-9a-fA-F]{2})/g, (_, hex) => {
5329
+ return String.fromCharCode(parseInt(hex, 16));
5330
+ }).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();
5331
+ }
5332
+ function parseRtfTables(rtfText) {
5333
+ const rowSegments = rtfText.split(/\\row\b/);
5334
+ if (rowSegments.length <= 1) return [];
5335
+ const rawTables = [];
5336
+ let currentTable = null;
5337
+ let charOffset = 0;
5338
+ for (let i = 0; i < rowSegments.length - 1; i++) {
5339
+ const segment = rowSegments[i];
5340
+ const segmentStart = charOffset;
5341
+ const segmentEnd = charOffset + segment.length + 4;
5342
+ const cellParts = segment.split(/\\cell\b/);
5343
+ if (cellParts.length > 1) {
5344
+ const cellxMatches = [...segment.matchAll(/\\cellx(\d+)/g)].map((m) => parseInt(m[1], 10));
5345
+ let cellWidths = [];
5346
+ if (cellxMatches.length > 0) {
5347
+ let seq = [cellxMatches[0]];
5348
+ for (let k = 1; k < cellxMatches.length; k++) {
5349
+ if (cellxMatches[k] > cellxMatches[k - 1]) seq.push(cellxMatches[k]);
5350
+ else break;
5351
+ }
5352
+ let prev = 0;
5353
+ cellWidths = seq.map((x) => {
5354
+ const w = x - prev;
5355
+ prev = x;
5356
+ return w;
5357
+ });
5358
+ }
5359
+ const cells = [];
5360
+ for (let c = 0; c < cellParts.length - 1; c++) {
5361
+ let rawCell = cellParts[c];
5362
+ if (c === 0) {
5363
+ const lastTrowd = rawCell.lastIndexOf("\\trowd");
5364
+ if (lastTrowd !== -1) rawCell = rawCell.slice(lastTrowd);
5365
+ }
5366
+ cells.push(cleanRtfText(rawCell));
5367
+ }
5368
+ const isNewTable = !currentTable || segmentStart - currentTable.lastEnd > 2500;
5369
+ if (isNewTable) {
5370
+ const prevTextSlice = rtfText.slice(Math.max(0, segmentStart - 4e3), segmentStart);
5371
+ const cleanPrev = cleanRtfText(prevTextSlice);
5372
+ const anchorBefore = cleanPrev.slice(-80).trim();
5373
+ currentTable = {
5374
+ id: `RTF_TABLE_${rawTables.length}`,
5375
+ startPos: segmentStart,
5376
+ lastEnd: segmentEnd,
5377
+ rows: [],
5378
+ columnWidths: cellWidths,
5379
+ anchorBefore,
5380
+ anchorAfter: ""
5381
+ };
5382
+ rawTables.push(currentTable);
5383
+ }
5384
+ if (currentTable) {
5385
+ currentTable.rows.push({
5386
+ cells,
5387
+ widths: cellWidths.length === cells.length ? cellWidths : currentTable.columnWidths
5388
+ });
5389
+ currentTable.lastEnd = segmentEnd;
5390
+ }
5391
+ }
5392
+ charOffset += segment.length + 4;
5393
+ }
5394
+ rawTables.forEach((tbl) => {
5395
+ const afterSlice = rtfText.slice(tbl.lastEnd, Math.min(rtfText.length, tbl.lastEnd + 2e3));
5396
+ const cleanAfter = cleanRtfText(afterSlice);
5397
+ tbl.anchorAfter = cleanAfter.slice(0, 80).trim();
5398
+ });
5399
+ return rawTables.map((tbl) => {
5400
+ const totalW = (tbl.columnWidths || []).reduce((a, b) => a + b, 0);
5401
+ const colPercents = totalW > 0 ? tbl.columnWidths.map((w) => Math.round(w / totalW * 100)) : [];
5402
+ return {
5403
+ id: tbl.id,
5404
+ rows: tbl.rows.map((r) => r.cells),
5405
+ colPercents,
5406
+ anchorBefore: tbl.anchorBefore,
5407
+ anchorAfter: tbl.anchorAfter
5408
+ };
5409
+ });
5410
+ }
5411
+ function renderRtfTableElement(table) {
5412
+ const tableEl = document.createElement("table");
5413
+ tableEl.className = "fp-rtf-table";
5414
+ tableEl.style.width = "100%";
5415
+ tableEl.style.borderCollapse = "collapse";
5416
+ tableEl.style.margin = "16px 0 20px";
5417
+ tableEl.style.fontSize = "10pt";
5418
+ tableEl.style.lineHeight = "1.5";
5419
+ tableEl.style.border = "1px solid #cbd5e1";
5420
+ tableEl.style.borderRadius = "4px";
5421
+ tableEl.style.boxSizing = "border-box";
5422
+ tableEl.style.backgroundColor = "#ffffff";
5423
+ const thead = document.createElement("thead");
5424
+ const tbody = document.createElement("tbody");
5425
+ table.rows.forEach((rowCells, rIdx) => {
5426
+ const tr = document.createElement("tr");
5427
+ const isHeader = rIdx === 0;
5428
+ if (isHeader) {
5429
+ tr.style.backgroundColor = "#f8fafc";
5430
+ tr.style.borderBottom = "2px solid #cbd5e1";
5431
+ } else {
5432
+ tr.style.borderBottom = "1px solid #e2e8f0";
5433
+ if (rIdx % 2 === 0) {
5434
+ tr.style.backgroundColor = "#fcfdfe";
5435
+ }
5436
+ }
5437
+ rowCells.forEach((cellText, cIdx) => {
5438
+ const cellEl = document.createElement(isHeader ? "th" : "td");
5439
+ cellEl.style.padding = "8px 12px";
5440
+ cellEl.style.border = "1px solid #cbd5e1";
5441
+ cellEl.style.verticalAlign = "top";
5442
+ cellEl.style.color = isHeader ? "#0f172a" : "#1e293b";
5443
+ cellEl.style.fontWeight = isHeader ? "600" : "normal";
5444
+ if (table.colPercents && table.colPercents[cIdx]) {
5445
+ cellEl.style.width = `${table.colPercents[cIdx]}%`;
5446
+ }
5447
+ if (/^\d+$/.test(cellText.trim()) || cellText.trim() === "#") {
5448
+ cellEl.style.textAlign = "center";
5449
+ } else {
5450
+ cellEl.style.textAlign = "left";
5451
+ }
5452
+ cellEl.textContent = cellText;
5453
+ tr.appendChild(cellEl);
5454
+ });
5455
+ if (isHeader) {
5456
+ thead.appendChild(tr);
5457
+ } else {
5458
+ tbody.appendChild(tr);
5459
+ }
5460
+ });
5461
+ tableEl.appendChild(thead);
5462
+ tableEl.appendChild(tbody);
5463
+ return tableEl;
5464
+ }
4564
5465
  var RtfPlugin = class {
4565
5466
  id = "rtf";
4566
5467
  name = "Rich Text Format (RTF)";
@@ -4575,29 +5476,35 @@ var RtfPlugin = class {
4575
5476
  getToolbarActions(instance) {
4576
5477
  const totalPages = instance.getPageCount?.() ?? 1;
4577
5478
  const actions = [];
4578
- if (totalPages > 1) {
4579
- actions.push({
4580
- id: "page-nav",
4581
- icon: "",
4582
- label: "Page Navigation",
4583
- type: "page-nav",
4584
- group: "navigation",
4585
- value: instance.getCurrentPage?.() ?? 1,
4586
- max: totalPages,
4587
- execute: (action, page) => {
4588
- const cur = instance.getCurrentPage?.() ?? 1;
4589
- const max = instance.getPageCount?.() ?? 1;
4590
- if (action === "prev") {
4591
- if (cur > 1) instance.goToPage?.(cur - 1);
4592
- } else if (action === "next") {
4593
- if (cur < max) instance.goToPage?.(cur + 1);
4594
- } else if (typeof page === "number") {
4595
- instance.goToPage?.(page);
4596
- }
5479
+ actions.push({
5480
+ id: "page-nav",
5481
+ icon: "",
5482
+ label: "Page Navigation",
5483
+ type: "page-nav",
5484
+ group: "navigation",
5485
+ value: instance.getCurrentPage?.() ?? 1,
5486
+ max: totalPages,
5487
+ execute: (action, page) => {
5488
+ const cur = instance.getCurrentPage?.() ?? 1;
5489
+ const max = instance.getPageCount?.() ?? 1;
5490
+ if (action === "prev") {
5491
+ if (cur > 1) instance.goToPage?.(cur - 1);
5492
+ } else if (action === "next") {
5493
+ if (cur < max) instance.goToPage?.(cur + 1);
5494
+ } else if (typeof page === "number") {
5495
+ instance.goToPage?.(page);
4597
5496
  }
4598
- });
4599
- }
5497
+ }
5498
+ });
4600
5499
  actions.push(
5500
+ {
5501
+ id: "thumbnails",
5502
+ icon: "thumbnails",
5503
+ label: "Thumbnails",
5504
+ type: "button",
5505
+ group: "view",
5506
+ execute: () => instance.toggleThumbnails?.()
5507
+ },
4601
5508
  {
4602
5509
  id: "zoom-out",
4603
5510
  icon: "zoom-out",
@@ -4668,67 +5575,143 @@ var RtfPlugin = class {
4668
5575
  async render(ctx) {
4669
5576
  const wrapper = document.createElement("div");
4670
5577
  wrapper.className = "fp-rtf-wrapper";
4671
- wrapper.style.padding = "32px";
4672
- wrapper.style.maxWidth = "850px";
5578
+ wrapper.style.padding = "0";
5579
+ wrapper.style.width = "816px";
4673
5580
  wrapper.style.margin = "0 auto";
4674
- wrapper.style.backgroundColor = "#fff";
4675
- wrapper.style.boxShadow = "0 2px 8px rgba(0,0,0,0.08)";
4676
- wrapper.style.borderRadius = "4px";
4677
- wrapper.style.minHeight = "100%";
5581
+ wrapper.style.boxSizing = "border-box";
5582
+ wrapper.style.display = "flex";
5583
+ wrapper.style.flexDirection = "column";
5584
+ wrapper.style.alignItems = "center";
5585
+ wrapper.style.backgroundColor = "transparent";
4678
5586
  wrapper.style.transformOrigin = "top center";
4679
- wrapper.style.transition = "transform 0.2s ease";
4680
- ctx.container.style.overflow = "auto";
4681
- ctx.container.style.padding = "24px";
5587
+ wrapper.style.transition = "transform 0.15s ease";
5588
+ ctx.container.style.overflowX = "hidden";
5589
+ ctx.container.style.overflowY = "auto";
5590
+ ctx.container.style.padding = "16px 8px";
4682
5591
  ctx.container.style.backgroundColor = "#f1f5f9";
4683
5592
  ctx.container.appendChild(wrapper);
4684
5593
  let scale = 1;
4685
5594
  let rotation = 0;
4686
5595
  let pageElements = [];
4687
- let fitMode = "width";
5596
+ let isUserZoomed = false;
5597
+ let fitMode = ctx?.options?.fitMode || "width";
4688
5598
  const calculateFitScale = (mode = fitMode) => {
4689
5599
  const activeEl = pageElements[currentPage - 1] || wrapper;
4690
- const elW = activeEl.offsetWidth || 850;
4691
- const singlePageH = Math.min(activeEl.offsetHeight || 1056, Math.round(elW * 1.32));
4692
- const availW = Math.max(280, ctx.container.clientWidth - 48);
4693
- const availH = Math.max(280, ctx.container.clientHeight - 80);
5600
+ const elW = 816;
5601
+ const singlePageH = activeEl?.offsetHeight || 1056;
5602
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
5603
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
4694
5604
  const sW = availW / elW;
4695
5605
  const sH = availH / singlePageH;
4696
5606
  if (mode === "page") {
4697
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
5607
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
4698
5608
  }
4699
- return Math.max(0.65, Math.min(1.05, sW));
5609
+ return Math.max(0.4, Math.min(3, sW));
4700
5610
  };
4701
5611
  const applyTransform = () => {
4702
5612
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
4703
5613
  wrapper.style.transformOrigin = "top center";
5614
+ const activeEl = pageElements[currentPage - 1];
5615
+ const baseH = activeEl?.offsetHeight || 1056;
5616
+ const scaledH = baseH * scale;
5617
+ const extraH = Math.max(0, scaledH - baseH);
5618
+ wrapper.style.marginBottom = `${extraH + 32}px`;
4704
5619
  };
5620
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
5621
+ if (!isUserZoomed) {
5622
+ scale = calculateFitScale(fitMode);
5623
+ applyTransform();
5624
+ }
5625
+ }) : null;
5626
+ resizeObserver?.observe(ctx.container);
4705
5627
  setTimeout(() => {
4706
- fitMode = "width";
4707
- scale = calculateFitScale("width");
5628
+ scale = calculateFitScale(fitMode);
4708
5629
  applyTransform();
4709
5630
  }, 60);
4710
5631
  try {
4711
5632
  if (typeof RTFJS__namespace.loggingEnabled === "function") {
4712
5633
  RTFJS__namespace.loggingEnabled(false);
4713
5634
  }
5635
+ const rawText = new TextDecoder("latin1").decode(ctx.buffer);
5636
+ const tables = parseRtfTables(rawText);
4714
5637
  const doc = new RTFJS__namespace.Document(ctx.buffer, {});
4715
5638
  const htmlElements = await doc.render();
4716
5639
  const contentNodes = [];
4717
- for (const item of htmlElements) {
4718
- if (item.children && item.children.length > 0 && !item.tagName.toLowerCase().startsWith("table")) {
4719
- contentNodes.push(...Array.from(item.children));
4720
- } else {
4721
- contentNodes.push(item);
5640
+ const extractBlocks = (nodes) => {
5641
+ for (const item of nodes) {
5642
+ if (!item || !(item instanceof HTMLElement)) continue;
5643
+ const tag = item.tagName.toLowerCase();
5644
+ const hasChildBlocks = Array.from(item.children).some((c) => {
5645
+ const ct = c.tagName.toLowerCase();
5646
+ return ["p", "div", "table", "h1", "h2", "h3", "h4", "h5", "h6", "section", "article", "ul", "ol"].includes(ct);
5647
+ });
5648
+ if (hasChildBlocks && !["table", "tr", "td", "th", "ul", "ol", "li"].includes(tag)) {
5649
+ extractBlocks(Array.from(item.children));
5650
+ } else {
5651
+ const svgs = item.querySelectorAll("svg, img");
5652
+ svgs.forEach((s) => {
5653
+ const el = s;
5654
+ el.style.maxWidth = "100%";
5655
+ el.style.maxHeight = "360px";
5656
+ el.style.display = "block";
5657
+ el.style.margin = "12px auto";
5658
+ if (s.tagName.toLowerCase() === "svg") {
5659
+ s.removeAttribute("width");
5660
+ s.removeAttribute("height");
5661
+ el.style.width = "100%";
5662
+ el.style.maxHeight = "360px";
5663
+ }
5664
+ });
5665
+ if (tag === "p" || tag === "div") {
5666
+ item.style.display = "block";
5667
+ item.style.marginBottom = "12px";
5668
+ item.style.lineHeight = "1.6";
5669
+ }
5670
+ contentNodes.push(item);
5671
+ }
4722
5672
  }
4723
- }
5673
+ };
5674
+ extractBlocks(htmlElements);
5675
+ tables.forEach((table) => {
5676
+ const tableEl = renderRtfTableElement(table);
5677
+ let inserted = false;
5678
+ if (table.anchorBefore && table.anchorBefore.length > 10) {
5679
+ const keyword = table.anchorBefore.slice(-35).trim();
5680
+ for (let i = 0; i < contentNodes.length; i++) {
5681
+ if (contentNodes[i].textContent?.includes(keyword)) {
5682
+ contentNodes.splice(i + 1, 0, tableEl);
5683
+ inserted = true;
5684
+ break;
5685
+ }
5686
+ }
5687
+ }
5688
+ if (!inserted && table.anchorAfter && table.anchorAfter.length > 10) {
5689
+ const keyword = table.anchorAfter.slice(0, 35).trim();
5690
+ for (let i = 0; i < contentNodes.length; i++) {
5691
+ if (contentNodes[i].textContent?.includes(keyword)) {
5692
+ contentNodes.splice(i, 0, tableEl);
5693
+ inserted = true;
5694
+ break;
5695
+ }
5696
+ }
5697
+ }
5698
+ if (!inserted) {
5699
+ if (contentNodes.length > 2) {
5700
+ contentNodes.splice(2, 0, tableEl);
5701
+ } else {
5702
+ contentNodes.push(tableEl);
5703
+ }
5704
+ }
5705
+ });
4724
5706
  wrapper.innerHTML = "";
4725
5707
  contentNodes.forEach((node) => wrapper.appendChild(node));
4726
5708
  const childHeights = contentNodes.map((c) => {
4727
5709
  const rectH = c.getBoundingClientRect ? c.getBoundingClientRect().height : 0;
4728
5710
  const offH = c.offsetHeight || 0;
5711
+ const realH = Math.max(rectH, offH);
5712
+ if (realH > 0) return realH;
4729
5713
  const textLen = c.textContent?.trim().length || 0;
4730
- const estH = Math.max(24, Math.ceil(textLen / 75) * 22 + 14);
4731
- return Math.max(rectH, offH, estH);
5714
+ return Math.max(24, Math.ceil(textLen / 95) * 22 + 12);
4732
5715
  });
4733
5716
  wrapper.innerHTML = "";
4734
5717
  const createRtfCard = () => {
@@ -4737,35 +5720,49 @@ var RtfPlugin = class {
4737
5720
  card.style.backgroundColor = "#ffffff";
4738
5721
  card.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
4739
5722
  card.style.borderRadius = "4px";
4740
- card.style.padding = "72px 56px";
5723
+ card.style.padding = "64px 56px";
4741
5724
  card.style.width = "816px";
4742
5725
  card.style.minHeight = "1056px";
4743
5726
  card.style.boxSizing = "border-box";
4744
5727
  card.style.marginBottom = "24px";
4745
5728
  card.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
4746
5729
  card.style.lineHeight = "1.6";
5730
+ card.style.color = "#1e293b";
4747
5731
  return card;
4748
5732
  };
4749
5733
  let curCard = createRtfCard();
4750
5734
  wrapper.appendChild(curCard);
4751
5735
  pageElements = [curCard];
4752
5736
  let curH = 0;
4753
- const maxH = 912;
5737
+ const maxH = 928;
4754
5738
  for (let i = 0; i < contentNodes.length; i++) {
4755
5739
  const child = contentNodes[i];
4756
5740
  const chH = childHeights[i];
4757
- curCard.appendChild(child);
4758
- curH += chH;
4759
- if (curH >= maxH && i < contentNodes.length - 1) {
5741
+ const isChildEmpty = (child.textContent || "").trim().length === 0 && !child.querySelector("table, img, svg, canvas");
5742
+ if (curH === 0 && isChildEmpty) {
5743
+ continue;
5744
+ }
5745
+ if (curH + chH > maxH && curCard.childNodes.length > 0) {
4760
5746
  curCard = createRtfCard();
4761
5747
  wrapper.appendChild(curCard);
4762
5748
  pageElements.push(curCard);
4763
5749
  curH = 0;
4764
5750
  }
5751
+ curCard.appendChild(child);
5752
+ curH += chH;
5753
+ }
5754
+ pageElements = pageElements.filter((card) => {
5755
+ const hasText = (card.textContent || "").trim().length > 0;
5756
+ const hasMedia = card.querySelector("table, img, svg, canvas") !== null;
5757
+ return hasText || hasMedia;
5758
+ });
5759
+ if (pageElements.length === 0) {
5760
+ pageElements = [curCard];
4765
5761
  }
4766
5762
  } catch (err) {
4767
5763
  console.warn("[RtfPlugin] RTF render error, fallback text:", err);
4768
5764
  const text = new TextDecoder("latin1").decode(ctx.buffer);
5765
+ const tables = parseRtfTables(text);
4769
5766
  const rawPages = text.split(/\\page\b/).map((segment) => {
4770
5767
  return segment.replace(/\\par[d]?/g, "\n").replace(/\\[a-zA-Z0-9\-]+/g, "").replace(/[{}]/g, "").trim();
4771
5768
  }).filter((p) => p.length > 0);
@@ -4776,47 +5773,26 @@ var RtfPlugin = class {
4776
5773
  pageCard.style.backgroundColor = "#ffffff";
4777
5774
  pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
4778
5775
  pageCard.style.borderRadius = "4px";
4779
- pageCard.style.padding = "72px 56px";
5776
+ pageCard.style.padding = "64px 56px";
4780
5777
  pageCard.style.width = "816px";
4781
5778
  pageCard.style.minHeight = "1056px";
4782
- pageCard.style.maxWidth = "100%";
4783
5779
  pageCard.style.boxSizing = "border-box";
4784
- pageCard.style.fontFamily = "serif";
5780
+ pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
4785
5781
  pageCard.style.fontSize = "12pt";
4786
5782
  pageCard.style.lineHeight = "1.6";
4787
5783
  pageCard.style.color = "#1e293b";
4788
5784
  pageCard.style.whiteSpace = "pre-wrap";
4789
5785
  pageCard.style.display = i === 0 ? "block" : "none";
4790
5786
  pageCard.textContent = pages[i];
5787
+ if (i === 1 && tables.length > 0) {
5788
+ tables.forEach((tbl) => pageCard.appendChild(renderRtfTableElement(tbl)));
5789
+ }
4791
5790
  wrapper.appendChild(pageCard);
4792
5791
  pageElements.push(pageCard);
4793
5792
  }
4794
5793
  }
4795
5794
  const totalPages = Math.max(1, pageElements.length);
4796
5795
  let currentPage = 1;
4797
- let indicator = null;
4798
- if (totalPages > 1) {
4799
- indicator = document.createElement("div");
4800
- indicator.className = "fp-rtf-page-indicator";
4801
- indicator.style.position = "sticky";
4802
- indicator.style.bottom = "16px";
4803
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
4804
- indicator.style.backdropFilter = "blur(8px)";
4805
- indicator.style.color = "#f8fafc";
4806
- indicator.style.fontSize = "12px";
4807
- indicator.style.fontWeight = "600";
4808
- indicator.style.padding = "5px 14px";
4809
- indicator.style.borderRadius = "20px";
4810
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
4811
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
4812
- indicator.style.zIndex = "10";
4813
- indicator.style.userSelect = "none";
4814
- indicator.style.pointerEvents = "none";
4815
- indicator.style.textAlign = "center";
4816
- indicator.style.width = "fit-content";
4817
- indicator.style.margin = "16px auto 0";
4818
- ctx.container.appendChild(indicator);
4819
- }
4820
5796
  const showPage = (pageNum) => {
4821
5797
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
4822
5798
  if (totalPages > 1) {
@@ -4824,9 +5800,6 @@ var RtfPlugin = class {
4824
5800
  el.style.display = idx + 1 === currentPage ? "block" : "none";
4825
5801
  });
4826
5802
  }
4827
- if (indicator) {
4828
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4829
- }
4830
5803
  ctx.container.scrollTop = 0;
4831
5804
  ctx.emit("page-change", { page: currentPage, total: totalPages });
4832
5805
  };
@@ -4834,7 +5807,7 @@ var RtfPlugin = class {
4834
5807
  showPage(1);
4835
5808
  }
4836
5809
  const cleanup = () => {
4837
- indicator?.remove();
5810
+ resizeObserver?.disconnect();
4838
5811
  wrapper.remove();
4839
5812
  ctx.container.innerHTML = "";
4840
5813
  };
@@ -4844,22 +5817,52 @@ var RtfPlugin = class {
4844
5817
  getPageCount: () => totalPages,
4845
5818
  getCurrentPage: () => currentPage,
4846
5819
  goToPage: (page) => showPage(page),
5820
+ getThumbnails: async () => {
5821
+ const items = [];
5822
+ for (let idx = 0; idx < totalPages; idx++) {
5823
+ const pageIdx = idx;
5824
+ items.push({
5825
+ index: pageIdx,
5826
+ label: `Page ${pageIdx + 1}`,
5827
+ render: async (canvas) => {
5828
+ const ctx2d = canvas.getContext("2d");
5829
+ if (!ctx2d) return;
5830
+ canvas.width = 140;
5831
+ canvas.height = 180;
5832
+ ctx2d.fillStyle = "#ffffff";
5833
+ ctx2d.fillRect(0, 0, 140, 180);
5834
+ ctx2d.strokeStyle = "#cbd5e1";
5835
+ ctx2d.lineWidth = 1;
5836
+ ctx2d.strokeRect(0.5, 0.5, 139, 179);
5837
+ ctx2d.fillStyle = "#334155";
5838
+ ctx2d.font = 'bold 11px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
5839
+ ctx2d.textAlign = "center";
5840
+ ctx2d.fillText(`Page ${pageIdx + 1}`, 70, 95);
5841
+ }
5842
+ });
5843
+ }
5844
+ return items;
5845
+ },
4847
5846
  zoomIn: () => {
5847
+ isUserZoomed = true;
4848
5848
  scale += 0.15;
4849
5849
  applyTransform();
4850
5850
  },
4851
5851
  zoomOut: () => {
5852
+ isUserZoomed = true;
4852
5853
  scale = Math.max(0.2, scale - 0.15);
4853
5854
  applyTransform();
4854
5855
  },
4855
5856
  getZoom: () => scale,
4856
5857
  setZoom: (level) => {
5858
+ isUserZoomed = true;
4857
5859
  scale = level;
4858
5860
  applyTransform();
4859
5861
  },
4860
5862
  fitToPage: () => {
4861
- fitMode = fitMode === "width" ? "page" : "width";
4862
- scale = calculateFitScale(fitMode);
5863
+ isUserZoomed = false;
5864
+ fitMode = "width";
5865
+ scale = calculateFitScale("width");
4863
5866
  rotation = 0;
4864
5867
  applyTransform();
4865
5868
  },
@@ -5047,40 +6050,36 @@ var OpenDocumentPlugin = class {
5047
6050
  const isPresentation = instance.isPresentation;
5048
6051
  const totalPages = instance.getPageCount?.() ?? 1;
5049
6052
  const actions = [];
5050
- if (isPresentation || totalPages > 1) {
5051
- if (isPresentation) {
5052
- actions.push({
5053
- id: "thumbnails",
5054
- icon: "thumbnails",
5055
- label: "Slide Thumbnails",
5056
- type: "button",
5057
- group: "navigation",
5058
- execute: () => instance.toggleThumbnails?.()
5059
- });
5060
- }
5061
- actions.push({
5062
- id: "page-nav",
5063
- icon: "",
5064
- label: isPresentation ? "Slide Navigation" : "Page Navigation",
5065
- type: "page-nav",
5066
- group: "navigation",
5067
- value: instance.getCurrentPage?.() ?? 1,
5068
- max: totalPages,
5069
- execute: (action, page) => {
5070
- const cur = instance.getCurrentPage?.() ?? 1;
5071
- const max = instance.getPageCount?.() ?? 1;
5072
- if (action === "prev") {
5073
- if (cur > 1) instance.goToPage?.(cur - 1);
5074
- } else if (action === "next") {
5075
- if (cur < max) instance.goToPage?.(cur + 1);
5076
- } else if (typeof page === "number") {
5077
- instance.goToPage?.(page);
5078
- } else if (typeof action === "number") {
5079
- instance.goToPage?.(action);
5080
- }
6053
+ actions.push({
6054
+ id: "thumbnails",
6055
+ icon: "thumbnails",
6056
+ label: isPresentation ? "Slide Thumbnails" : "Page Thumbnails",
6057
+ type: "button",
6058
+ group: "navigation",
6059
+ execute: () => instance.toggleThumbnails?.()
6060
+ });
6061
+ actions.push({
6062
+ id: "page-nav",
6063
+ icon: "",
6064
+ label: isPresentation ? "Slide Navigation" : "Page Navigation",
6065
+ type: "page-nav",
6066
+ group: "navigation",
6067
+ value: instance.getCurrentPage?.() ?? 1,
6068
+ max: totalPages,
6069
+ execute: (action, page) => {
6070
+ const cur = instance.getCurrentPage?.() ?? 1;
6071
+ const max = instance.getPageCount?.() ?? 1;
6072
+ if (action === "prev") {
6073
+ if (cur > 1) instance.goToPage?.(cur - 1);
6074
+ } else if (action === "next") {
6075
+ if (cur < max) instance.goToPage?.(cur + 1);
6076
+ } else if (typeof page === "number") {
6077
+ instance.goToPage?.(page);
6078
+ } else if (typeof action === "number") {
6079
+ instance.goToPage?.(action);
5081
6080
  }
5082
- });
5083
- }
6081
+ }
6082
+ });
5084
6083
  actions.push(
5085
6084
  {
5086
6085
  id: "zoom-out",
@@ -5171,49 +6170,65 @@ var OpenDocumentPlugin = class {
5171
6170
  container.className = "fp-odf-container";
5172
6171
  container.style.width = "100%";
5173
6172
  container.style.height = "100%";
5174
- container.style.overflow = "auto";
5175
- container.style.padding = "24px";
6173
+ container.style.overflowX = "hidden";
6174
+ container.style.overflowY = "auto";
6175
+ container.style.padding = "16px 8px";
5176
6176
  container.style.backgroundColor = "#f1f5f9";
5177
6177
  const wrapper = document.createElement("div");
5178
6178
  wrapper.className = "fp-odf-wrapper";
5179
6179
  wrapper.style.margin = "0 auto";
6180
+ wrapper.style.width = isPresentation ? "960px" : "816px";
6181
+ wrapper.style.boxSizing = "border-box";
5180
6182
  wrapper.style.backgroundColor = "#ffffff";
5181
6183
  wrapper.style.boxShadow = "0 2px 10px rgba(0,0,0,0.08)";
5182
6184
  wrapper.style.borderRadius = "4px";
5183
6185
  wrapper.style.transformOrigin = "top center";
5184
- wrapper.style.transition = "transform 0.2s ease";
6186
+ wrapper.style.transition = "transform 0.15s ease";
5185
6187
  container.appendChild(wrapper);
5186
6188
  ctx.container.appendChild(container);
5187
6189
  let scale = 1;
5188
6190
  let rotation = 0;
5189
- let fitMode = "width";
6191
+ let isUserZoomed = false;
6192
+ let fitMode = ctx?.options?.fitMode || "width";
6193
+ let currentPage = 1;
6194
+ let totalPages = 1;
6195
+ let slides = [];
5190
6196
  const calculateFitScale = (mode = fitMode) => {
5191
- const elW = wrapper.offsetWidth || 850;
5192
- const singlePageH = Math.min(wrapper.offsetHeight || 1056, Math.round(elW * 1.32));
5193
- const availW = Math.max(280, ctx.container.clientWidth - 48);
5194
- const availH = Math.max(280, ctx.container.clientHeight - 80);
6197
+ const activeSlide = slides[currentPage - 1];
6198
+ const elW = isPresentation ? 960 : 816;
6199
+ const singlePageH = Math.min(activeSlide?.offsetHeight || wrapper.offsetHeight || 1056, Math.round(elW * 1.32));
6200
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
6201
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
5195
6202
  const sW = availW / elW;
5196
6203
  const sH = availH / (isPresentation ? 540 : singlePageH);
5197
6204
  if (isPresentation) {
5198
- return Math.min(1, Math.min(sW, sH));
6205
+ return Math.min(2.5, Math.min(sW, sH));
5199
6206
  }
5200
6207
  if (mode === "page") {
5201
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
6208
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
5202
6209
  }
5203
- return Math.max(0.65, Math.min(1.05, sW));
6210
+ return Math.max(0.4, Math.min(3, sW));
5204
6211
  };
5205
6212
  const applyTransform = () => {
5206
6213
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5207
6214
  wrapper.style.transformOrigin = "top center";
6215
+ const activeSlide = slides[currentPage - 1];
6216
+ const baseH = activeSlide?.offsetHeight || wrapper.offsetHeight || 1056;
6217
+ const scaledH = baseH * scale;
6218
+ const extraH = Math.max(0, scaledH - baseH);
6219
+ wrapper.style.marginBottom = `${extraH + 32}px`;
5208
6220
  };
6221
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
6222
+ if (!isUserZoomed) {
6223
+ scale = calculateFitScale(fitMode);
6224
+ applyTransform();
6225
+ }
6226
+ }) : null;
6227
+ resizeObserver?.observe(ctx.container);
5209
6228
  setTimeout(() => {
5210
- fitMode = "width";
5211
- scale = calculateFitScale("width");
6229
+ scale = calculateFitScale(fitMode);
5212
6230
  applyTransform();
5213
6231
  }, 60);
5214
- let currentPage = 1;
5215
- let totalPages = 1;
5216
- let slides = [];
5217
6232
  if (isPresentation) {
5218
6233
  wrapper.style.maxWidth = "960px";
5219
6234
  wrapper.style.aspectRatio = "16 / 9";
@@ -5314,24 +6329,9 @@ var OpenDocumentPlugin = class {
5314
6329
  wrapper.appendChild(page);
5315
6330
  slides.push(page);
5316
6331
  });
5317
- const pageIndicator = document.createElement("div");
5318
- pageIndicator.className = "fp-odt-page-indicator";
5319
- pageIndicator.style.position = "sticky";
5320
- pageIndicator.style.bottom = "16px";
5321
- pageIndicator.style.left = "50%";
5322
- pageIndicator.style.transform = "translateX(-50%)";
5323
- pageIndicator.style.backgroundColor = "rgba(0, 0, 0, 0.6)";
5324
- pageIndicator.style.color = "#fff";
5325
- pageIndicator.style.padding = "6px 12px";
5326
- pageIndicator.style.borderRadius = "16px";
5327
- pageIndicator.style.fontSize = "12px";
5328
- pageIndicator.style.zIndex = "100";
5329
- pageIndicator.style.display = "inline-block";
5330
- pageIndicator.style.width = "fit-content";
5331
- pageIndicator.textContent = `Page 1 of ${totalPages}`;
5332
- container.appendChild(pageIndicator);
5333
6332
  }
5334
6333
  const cleanup = () => {
6334
+ resizeObserver?.disconnect();
5335
6335
  imageUrls.forEach((url) => URL.revokeObjectURL(url));
5336
6336
  container.remove();
5337
6337
  ctx.container.innerHTML = "";
@@ -5343,10 +6343,6 @@ var OpenDocumentPlugin = class {
5343
6343
  slides.forEach((s, idx) => {
5344
6344
  s.style.display = idx === page - 1 ? "block" : "none";
5345
6345
  });
5346
- const indicator = container.querySelector(".fp-odt-page-indicator");
5347
- if (indicator) {
5348
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
5349
- }
5350
6346
  container.scrollTop = 0;
5351
6347
  ctx.emit("page-change", { page: currentPage, total: totalPages });
5352
6348
  };
@@ -5354,21 +6350,25 @@ var OpenDocumentPlugin = class {
5354
6350
  destroy: cleanup,
5355
6351
  isPresentation,
5356
6352
  zoomIn: () => {
6353
+ isUserZoomed = true;
5357
6354
  scale += 0.15;
5358
6355
  applyTransform();
5359
6356
  },
5360
6357
  zoomOut: () => {
6358
+ isUserZoomed = true;
5361
6359
  scale = Math.max(0.2, scale - 0.15);
5362
6360
  applyTransform();
5363
6361
  },
5364
6362
  getZoom: () => scale,
5365
6363
  setZoom: (level) => {
6364
+ isUserZoomed = true;
5366
6365
  scale = level;
5367
6366
  applyTransform();
5368
6367
  },
5369
6368
  fitToPage: () => {
5370
- fitMode = fitMode === "width" ? "page" : "width";
5371
- scale = calculateFitScale(fitMode);
6369
+ isUserZoomed = false;
6370
+ fitMode = "width";
6371
+ scale = calculateFitScale("width");
5372
6372
  rotation = 0;
5373
6373
  applyTransform();
5374
6374
  },
@@ -5384,23 +6384,45 @@ var OpenDocumentPlugin = class {
5384
6384
  getPageCount: () => totalPages,
5385
6385
  getCurrentPage: () => currentPage,
5386
6386
  getThumbnails: async () => {
5387
- if (!isPresentation) return [];
5388
- return slides.map((_, idx) => ({
5389
- index: idx,
5390
- label: `Slide ${idx + 1}`,
5391
- render: async (canvas) => {
5392
- const ctx2d = canvas.getContext("2d");
5393
- if (!ctx2d) return;
5394
- canvas.width = 160;
5395
- canvas.height = 90;
5396
- ctx2d.fillStyle = "#f8fafc";
5397
- ctx2d.fillRect(0, 0, 160, 90);
5398
- ctx2d.fillStyle = "#334155";
5399
- ctx2d.font = "bold 12px sans-serif";
5400
- ctx2d.textAlign = "center";
5401
- ctx2d.fillText(`Slide ${idx + 1}`, 80, 50);
5402
- }
5403
- }));
6387
+ const count = totalPages || slides.length || 1;
6388
+ const items = [];
6389
+ for (let idx = 0; idx < count; idx++) {
6390
+ const itemIdx = idx;
6391
+ items.push({
6392
+ index: itemIdx,
6393
+ label: isPresentation ? `Slide ${itemIdx + 1}` : `Page ${itemIdx + 1}`,
6394
+ render: async (canvas) => {
6395
+ const ctx2d = canvas.getContext("2d");
6396
+ if (!ctx2d) return;
6397
+ if (isPresentation) {
6398
+ canvas.width = 160;
6399
+ canvas.height = 90;
6400
+ ctx2d.fillStyle = "#f8fafc";
6401
+ ctx2d.fillRect(0, 0, 160, 90);
6402
+ ctx2d.strokeStyle = "#cbd5e1";
6403
+ ctx2d.lineWidth = 1;
6404
+ ctx2d.strokeRect(0.5, 0.5, 159, 89);
6405
+ ctx2d.fillStyle = "#334155";
6406
+ ctx2d.font = "bold 11px sans-serif";
6407
+ ctx2d.textAlign = "center";
6408
+ ctx2d.fillText(`Slide ${itemIdx + 1}`, 80, 50);
6409
+ } else {
6410
+ canvas.width = 140;
6411
+ canvas.height = 180;
6412
+ ctx2d.fillStyle = "#ffffff";
6413
+ ctx2d.fillRect(0, 0, 140, 180);
6414
+ ctx2d.strokeStyle = "#cbd5e1";
6415
+ ctx2d.lineWidth = 1;
6416
+ ctx2d.strokeRect(0.5, 0.5, 139, 179);
6417
+ ctx2d.fillStyle = "#64748b";
6418
+ ctx2d.font = "bold 10px sans-serif";
6419
+ ctx2d.textAlign = "center";
6420
+ ctx2d.fillText(`Page ${itemIdx + 1}`, 70, 90);
6421
+ }
6422
+ }
6423
+ });
6424
+ }
6425
+ return items;
5404
6426
  },
5405
6427
  download: () => {
5406
6428
  const mimeType = this.mimeTypes.find((m) => m.includes(ext.replace(".", ""))) || "application/octet-stream";
@@ -5505,23 +6527,24 @@ var OpenDocumentPlugin = class {
5505
6527
  case "h": {
5506
6528
  const level = Math.min(6, Math.max(1, Number(node.getAttribute("text:outline-level") || 1)));
5507
6529
  const style = styles.get(node.getAttribute("text:style-name") || "") || "";
5508
- html += `<h${level} style="${style}; font-family: -apple-system, BlinkMacSystemFont, sans-serif;">${node.textContent || ""}</h${level}>`;
6530
+ html += `<h${level} style="${style}; margin: 18px 0 8px; color: #1e3a8a; font-family: Calibri, 'Segoe UI', Arial, sans-serif;">${node.textContent || ""}</h${level}>`;
5509
6531
  break;
5510
6532
  }
5511
6533
  case "p": {
5512
6534
  const style = styles.get(node.getAttribute("text:style-name") || "") || "";
5513
6535
  const inner = this.renderSpans(node, styles, images);
5514
- html += `<p style="${style}; line-height: 1.6; margin: 8px 0; font-family: -apple-system, BlinkMacSystemFont, sans-serif;">${inner}</p>`;
6536
+ const isInsideLi = node.parentElement?.localName === "list-item" || node.parentElement?.tagName?.toLowerCase() === "list-item";
6537
+ html += `<p style="${style}; line-height: 1.5; margin: ${isInsideLi ? "0" : "8px 0 12px"}; font-family: Calibri, 'Segoe UI', Arial, sans-serif;">${inner}</p>`;
5515
6538
  break;
5516
6539
  }
5517
6540
  case "list": {
5518
- html += '<ul style="margin: 8px 0; padding-left: 24px;">';
6541
+ html += '<ul style="margin: 8px 0 12px; padding-left: 24px;">';
5519
6542
  Array.from(node.children).forEach((c) => walk(c));
5520
6543
  html += "</ul>";
5521
6544
  break;
5522
6545
  }
5523
6546
  case "list-item": {
5524
- html += "<li>";
6547
+ html += '<li style="margin: 4px 0; font-size: 11pt; line-height: 1.5; color: #1e293b;">';
5525
6548
  Array.from(node.children).forEach((c) => walk(c));
5526
6549
  html += "</li>";
5527
6550
  break;
@@ -5647,28 +6670,26 @@ var DocPlugin = class {
5647
6670
  getToolbarActions(instance) {
5648
6671
  const totalPages = instance.getPageCount?.() ?? 1;
5649
6672
  const actions = [];
5650
- if (totalPages > 1) {
5651
- actions.push({
5652
- id: "page-nav",
5653
- icon: "",
5654
- label: "Page Navigation",
5655
- type: "page-nav",
5656
- group: "navigation",
5657
- value: instance.getCurrentPage?.() ?? 1,
5658
- max: totalPages,
5659
- execute: (action, page) => {
5660
- const cur = instance.getCurrentPage?.() ?? 1;
5661
- const max = instance.getPageCount?.() ?? 1;
5662
- if (action === "prev") {
5663
- if (cur > 1) instance.goToPage?.(cur - 1);
5664
- } else if (action === "next") {
5665
- if (cur < max) instance.goToPage?.(cur + 1);
5666
- } else if (typeof page === "number") {
5667
- instance.goToPage?.(page);
5668
- }
6673
+ actions.push({
6674
+ id: "page-nav",
6675
+ icon: "",
6676
+ label: "Page Navigation",
6677
+ type: "page-nav",
6678
+ group: "navigation",
6679
+ value: instance.getCurrentPage?.() ?? 1,
6680
+ max: totalPages,
6681
+ execute: (action, page) => {
6682
+ const cur = instance.getCurrentPage?.() ?? 1;
6683
+ const max = instance.getPageCount?.() ?? 1;
6684
+ if (action === "prev") {
6685
+ if (cur > 1) instance.goToPage?.(cur - 1);
6686
+ } else if (action === "next") {
6687
+ if (cur < max) instance.goToPage?.(cur + 1);
6688
+ } else if (typeof page === "number") {
6689
+ instance.goToPage?.(page);
5669
6690
  }
5670
- });
5671
- }
6691
+ }
6692
+ });
5672
6693
  actions.push(
5673
6694
  {
5674
6695
  id: "zoom-out",
@@ -5742,8 +6763,9 @@ var DocPlugin = class {
5742
6763
  container.className = "fp-doc-container";
5743
6764
  container.style.width = "100%";
5744
6765
  container.style.height = "100%";
5745
- container.style.overflow = "auto";
5746
- container.style.padding = "32px 16px";
6766
+ container.style.overflowX = "hidden";
6767
+ container.style.overflowY = "auto";
6768
+ container.style.padding = "16px 8px";
5747
6769
  container.style.backgroundColor = "#f1f5f9";
5748
6770
  container.style.display = "flex";
5749
6771
  container.style.justifyContent = "center";
@@ -5788,16 +6810,15 @@ var DocPlugin = class {
5788
6810
  const pageCard = document.createElement("div");
5789
6811
  pageCard.className = "fp-doc-page-card";
5790
6812
  pageCard.style.width = "816px";
5791
- pageCard.style.height = "1056px";
6813
+ pageCard.style.minHeight = "1056px";
5792
6814
  pageCard.style.backgroundColor = "#ffffff";
5793
- pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
6815
+ pageCard.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
5794
6816
  pageCard.style.borderRadius = "4px";
5795
- pageCard.style.padding = "96px 72px";
6817
+ pageCard.style.padding = "72px 56px";
5796
6818
  pageCard.style.boxSizing = "border-box";
5797
- pageCard.style.overflow = "hidden";
5798
6819
  pageCard.style.display = i === 0 ? "block" : "none";
5799
6820
  pageCard.style.transformOrigin = "top center";
5800
- pageCard.style.transition = "transform 0.2s ease";
6821
+ pageCard.style.transition = "transform 0.15s ease";
5801
6822
  pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
5802
6823
  pageCard.style.color = "#1e293b";
5803
6824
  if (isFallback && i === 0) {
@@ -5814,34 +6835,15 @@ var DocPlugin = class {
5814
6835
  container.appendChild(pageCard);
5815
6836
  pageCards.push(pageCard);
5816
6837
  }
5817
- let indicator = null;
5818
- if (totalPages > 1) {
5819
- indicator = document.createElement("div");
5820
- indicator.className = "fp-doc-page-indicator";
5821
- indicator.style.position = "fixed";
5822
- indicator.style.bottom = "16px";
5823
- indicator.style.left = "50%";
5824
- indicator.style.transform = "translateX(-50%)";
5825
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
5826
- indicator.style.backdropFilter = "blur(8px)";
5827
- indicator.style.color = "#f8fafc";
5828
- indicator.style.fontSize = "12px";
5829
- indicator.style.fontWeight = "600";
5830
- indicator.style.padding = "5px 14px";
5831
- indicator.style.borderRadius = "20px";
5832
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
5833
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
5834
- indicator.style.zIndex = "10";
5835
- indicator.style.userSelect = "none";
5836
- indicator.style.pointerEvents = "none";
5837
- indicator.style.textAlign = "center";
5838
- container.appendChild(indicator);
5839
- }
5840
6838
  let rotation = 0;
5841
6839
  const applyTransform = () => {
5842
6840
  const activeCard = pageCards[currentPage - 1];
5843
6841
  if (activeCard) {
5844
6842
  activeCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
6843
+ const baseH = activeCard.offsetHeight || 1056;
6844
+ const scaledH = baseH * scale;
6845
+ const extraH = Math.max(0, scaledH - baseH);
6846
+ activeCard.style.marginBottom = `${extraH + 32}px`;
5845
6847
  }
5846
6848
  };
5847
6849
  const showPage = (pageNum) => {
@@ -5854,35 +6856,39 @@ var DocPlugin = class {
5854
6856
  card.style.display = "none";
5855
6857
  }
5856
6858
  });
5857
- if (indicator) {
5858
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
5859
- }
5860
6859
  container.scrollTop = 0;
5861
6860
  ctx.emit("page-change", { page: currentPage, total: totalPages });
5862
6861
  };
5863
6862
  if (totalPages > 1) {
5864
6863
  showPage(1);
5865
6864
  }
5866
- let fitMode = "width";
6865
+ let fitMode = ctx?.options?.fitMode || "width";
6866
+ let isUserZoomed = false;
5867
6867
  const calculateFitScale = (mode = fitMode) => {
5868
6868
  const elW = 816;
5869
6869
  const elH = 1056;
5870
- const availW = Math.max(280, ctx.container.clientWidth - 48);
5871
- const availH = Math.max(280, ctx.container.clientHeight - 80);
6870
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
6871
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
5872
6872
  const sW = availW / elW;
5873
6873
  const sH = availH / elH;
5874
6874
  if (mode === "page") {
5875
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
6875
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
5876
6876
  }
5877
- return Math.max(0.65, Math.min(1.05, sW));
6877
+ return Math.max(0.4, Math.min(3, sW));
5878
6878
  };
6879
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
6880
+ if (!isUserZoomed) {
6881
+ scale = calculateFitScale(fitMode);
6882
+ applyTransform();
6883
+ }
6884
+ }) : null;
6885
+ resizeObserver?.observe(ctx.container);
5879
6886
  setTimeout(() => {
5880
- fitMode = "width";
5881
- scale = calculateFitScale("width");
6887
+ scale = calculateFitScale(fitMode);
5882
6888
  applyTransform();
5883
6889
  }, 60);
5884
6890
  const cleanup = () => {
5885
- indicator?.remove();
6891
+ resizeObserver?.disconnect();
5886
6892
  container.remove();
5887
6893
  ctx.container.innerHTML = "";
5888
6894
  };
@@ -5893,21 +6899,25 @@ var DocPlugin = class {
5893
6899
  getCurrentPage: () => currentPage,
5894
6900
  goToPage: (page) => showPage(page),
5895
6901
  zoomIn: () => {
6902
+ isUserZoomed = true;
5896
6903
  scale += 0.15;
5897
6904
  applyTransform();
5898
6905
  },
5899
6906
  zoomOut: () => {
6907
+ isUserZoomed = true;
5900
6908
  scale = Math.max(0.2, scale - 0.15);
5901
6909
  applyTransform();
5902
6910
  },
5903
6911
  getZoom: () => scale,
5904
6912
  setZoom: (level) => {
6913
+ isUserZoomed = true;
5905
6914
  scale = level;
5906
6915
  applyTransform();
5907
6916
  },
5908
6917
  fitToPage: () => {
5909
- fitMode = fitMode === "width" ? "page" : "width";
5910
- scale = calculateFitScale(fitMode);
6918
+ isUserZoomed = false;
6919
+ fitMode = "width";
6920
+ scale = calculateFitScale("width");
5911
6921
  rotation = 0;
5912
6922
  applyTransform();
5913
6923
  },
@@ -6284,25 +7294,31 @@ ${chartSvg}
6284
7294
  i++;
6285
7295
  continue;
6286
7296
  }
6287
- if (line.length < 60 && !line.endsWith(".") && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#"))) {
7297
+ if (/^\d{1,2}$/.test(line.trim())) {
7298
+ i++;
7299
+ continue;
7300
+ }
7301
+ const isShortLine = line.length < 60 && !line.endsWith(".") && !line.endsWith(",");
7302
+ 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));
7303
+ if (isTitleLike) {
6288
7304
  if (inList) {
6289
7305
  html += "</ul>";
6290
7306
  inList = false;
6291
7307
  }
6292
- html += `<h2 style="font-size: 16px; font-weight: 700; color: #1e3a8a; margin: 16px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify6__default.default.sanitize(line, sanitizeOptions)}</h2>`;
7308
+ html += `<h2 style="font-size: 18px; font-weight: 700; color: #1e3a8a; margin: 18px 0 10px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify6__default.default.sanitize(line, sanitizeOptions)}</h2>`;
6293
7309
  } else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
6294
7310
  if (!inList) {
6295
- html += '<ul style="margin: 8px 0; padding-left: 24px;">';
7311
+ html += '<ul style="margin: 8px 0 12px; padding-left: 24px;">';
6296
7312
  inList = true;
6297
7313
  }
6298
7314
  const bulletText = line.replace(/^[•\-\*]\s*/, "");
6299
- html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6__default.default.sanitize(bulletText, sanitizeOptions)}</li>`;
7315
+ html += `<li style="margin: 4px 0; line-height: 1.5; font-size: 11pt; color: #1e293b;">${DOMPurify6__default.default.sanitize(bulletText, sanitizeOptions)}</li>`;
6300
7316
  } else {
6301
7317
  if (inList) {
6302
7318
  html += "</ul>";
6303
7319
  inList = false;
6304
7320
  }
6305
- html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6__default.default.sanitize(line, sanitizeOptions)}</p>`;
7321
+ html += `<p style="line-height: 1.5; margin: 0 0 12px 0; font-size: 11pt; color: #1e293b; text-align: justify;">${DOMPurify6__default.default.sanitize(line, sanitizeOptions)}</p>`;
6306
7322
  }
6307
7323
  i++;
6308
7324
  }
@@ -6856,14 +7872,14 @@ var FilePreviewViewer2 = class extends FilePreviewViewer {
6856
7872
  };
6857
7873
 
6858
7874
  // src/angular.ts
6859
- var _container_dec, _zoomChange_dec, _pageChange_dec, _error_dec, _loaded_dec, _loading_dec, _options_dec, _plugins_dec, _src_dec, _FilePreviewComponent_decorators, _init;
7875
+ 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;
6860
7876
  _FilePreviewComponent_decorators = [core.Component({
6861
7877
  selector: "fp-file-preview",
6862
7878
  standalone: true,
6863
7879
  imports: [common.CommonModule],
6864
7880
  template: `<div #container [style.width]="'100%'" [style.height]="'100%'"></div>`,
6865
7881
  changeDetection: core.ChangeDetectionStrategy.OnPush
6866
- })], _src_dec = [core.Input({ required: true })], _plugins_dec = [core.Input()], _options_dec = [core.Input()], _loading_dec = [core.Output()], _loaded_dec = [core.Output()], _error_dec = [core.Output()], _pageChange_dec = [core.Output()], _zoomChange_dec = [core.Output()], _container_dec = [core.ViewChild("container", { static: true })];
7882
+ })], _src_dec = [core.Input({ required: true })], _plugins_dec = [core.Input()], _options_dec = [core.Input()], _loading_dec = [core.Output()], _loaded_dec = [core.Output()], _error_dec = [core.Output()], _pageChange_dec = [core.Output()], _zoomChange_dec = [core.Output()], _rotate_dec = [core.Output()], _destroyed_dec = [core.Output()], _container_dec = [core.ViewChild("container", { static: true })];
6867
7883
  exports.FilePreviewComponent = class FilePreviewComponent {
6868
7884
  constructor(ngZone) {
6869
7885
  this.ngZone = ngZone;
@@ -6875,7 +7891,9 @@ exports.FilePreviewComponent = class FilePreviewComponent {
6875
7891
  __publicField(this, "error", __runInitializers(_init, 28, this, new core.EventEmitter())), __runInitializers(_init, 31, this);
6876
7892
  __publicField(this, "pageChange", __runInitializers(_init, 32, this, new core.EventEmitter())), __runInitializers(_init, 35, this);
6877
7893
  __publicField(this, "zoomChange", __runInitializers(_init, 36, this, new core.EventEmitter())), __runInitializers(_init, 39, this);
6878
- __publicField(this, "container", __runInitializers(_init, 40, this)), __runInitializers(_init, 43, this);
7894
+ __publicField(this, "rotate", __runInitializers(_init, 40, this, new core.EventEmitter())), __runInitializers(_init, 43, this);
7895
+ __publicField(this, "destroyed", __runInitializers(_init, 44, this, new core.EventEmitter())), __runInitializers(_init, 47, this);
7896
+ __publicField(this, "container", __runInitializers(_init, 48, this)), __runInitializers(_init, 51, this);
6879
7897
  __publicField(this, "viewer", null);
6880
7898
  __publicField(this, "instance", null);
6881
7899
  }
@@ -6900,6 +7918,12 @@ exports.FilePreviewComponent = class FilePreviewComponent {
6900
7918
  viewer.on("zoom-change", (data) => {
6901
7919
  this.ngZone.run(() => this.zoomChange.emit(data));
6902
7920
  });
7921
+ viewer.on("rotate", (data) => {
7922
+ this.ngZone.run(() => this.rotate.emit(data));
7923
+ });
7924
+ viewer.on("destroy", () => {
7925
+ this.ngZone.run(() => this.destroyed.emit());
7926
+ });
6903
7927
  this.render();
6904
7928
  }
6905
7929
  ngOnChanges(changes) {
@@ -6934,6 +7958,12 @@ exports.FilePreviewComponent = class FilePreviewComponent {
6934
7958
  getInstance() {
6935
7959
  return this.instance;
6936
7960
  }
7961
+ getViewer() {
7962
+ return this.viewer;
7963
+ }
7964
+ destroy() {
7965
+ this.viewer?.destroy();
7966
+ }
6937
7967
  };
6938
7968
  _init = __decoratorStart();
6939
7969
  __decorateElement(_init, 5, "src", _src_dec, exports.FilePreviewComponent);
@@ -6944,6 +7974,8 @@ __decorateElement(_init, 5, "loaded", _loaded_dec, exports.FilePreviewComponent)
6944
7974
  __decorateElement(_init, 5, "error", _error_dec, exports.FilePreviewComponent);
6945
7975
  __decorateElement(_init, 5, "pageChange", _pageChange_dec, exports.FilePreviewComponent);
6946
7976
  __decorateElement(_init, 5, "zoomChange", _zoomChange_dec, exports.FilePreviewComponent);
7977
+ __decorateElement(_init, 5, "rotate", _rotate_dec, exports.FilePreviewComponent);
7978
+ __decorateElement(_init, 5, "destroyed", _destroyed_dec, exports.FilePreviewComponent);
6947
7979
  __decorateElement(_init, 5, "container", _container_dec, exports.FilePreviewComponent);
6948
7980
  exports.FilePreviewComponent = __decorateElement(_init, 0, "FilePreviewComponent", _FilePreviewComponent_decorators, exports.FilePreviewComponent);
6949
7981
  __runInitializers(_init, 1, exports.FilePreviewComponent);