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

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",
@@ -3020,6 +3595,16 @@ var ExcelPlugin = class {
3020
3595
  execute: () => {
3021
3596
  instance.print?.();
3022
3597
  }
3598
+ },
3599
+ {
3600
+ id: "open-window",
3601
+ icon: "open-window",
3602
+ label: "Open in Separate Full Window",
3603
+ type: "button",
3604
+ group: "actions",
3605
+ execute: () => {
3606
+ instance.openInSeparateWindow?.();
3607
+ }
3023
3608
  }
3024
3609
  );
3025
3610
  return actions;
@@ -3185,6 +3770,63 @@ var ExcelPlugin = class {
3185
3770
  },
3186
3771
  getPageCount: () => sheetNames.length,
3187
3772
  getCurrentPage: () => currentSheetIndex,
3773
+ getThumbnails: () => {
3774
+ return sheetNames.map((name, idx) => ({
3775
+ index: idx,
3776
+ label: name,
3777
+ render: async (canvas) => {
3778
+ canvas.width = 140;
3779
+ canvas.height = 100;
3780
+ const c = canvas.getContext("2d");
3781
+ if (!c) return;
3782
+ c.fillStyle = "#ffffff";
3783
+ c.fillRect(0, 0, canvas.width, canvas.height);
3784
+ c.fillStyle = "#107c41";
3785
+ c.fillRect(0, 0, canvas.width, 16);
3786
+ c.fillStyle = "#ffffff";
3787
+ c.font = 'bold 8px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
3788
+ c.fillText(`\u{1F4CA} ${name.slice(0, 16)}`, 6, 11);
3789
+ const startY = 17;
3790
+ const rowH = 13;
3791
+ const colW = 30;
3792
+ const colHeaders = ["A", "B", "C", "D"];
3793
+ c.fillStyle = "#f1f5f9";
3794
+ c.fillRect(0, startY, canvas.width, rowH);
3795
+ c.strokeStyle = "#cbd5e1";
3796
+ c.lineWidth = 0.8;
3797
+ c.strokeRect(0, startY, canvas.width, rowH);
3798
+ c.fillStyle = "#64748b";
3799
+ c.font = "bold 7px sans-serif";
3800
+ for (let ci = 0; ci < colHeaders.length; ci++) {
3801
+ c.fillText(colHeaders[ci], 14 + ci * colW, startY + 9);
3802
+ }
3803
+ const ws = wb?.Sheets[name];
3804
+ for (let ri = 1; ri <= 5; ri++) {
3805
+ const y = startY + ri * rowH;
3806
+ if (y > canvas.height - 2) break;
3807
+ c.fillStyle = "#f8fafc";
3808
+ c.fillRect(0, y, 12, rowH);
3809
+ c.fillStyle = "#94a3b8";
3810
+ c.font = "6px sans-serif";
3811
+ c.fillText(String(ri), 3, y + 9);
3812
+ c.strokeStyle = "#e2e8f0";
3813
+ c.strokeRect(0, y, canvas.width, rowH);
3814
+ c.fillStyle = "#334155";
3815
+ c.font = "6px sans-serif";
3816
+ for (let ci = 0; ci < colHeaders.length; ci++) {
3817
+ const cellRef = `${colHeaders[ci]}${ri}`;
3818
+ const cellVal = ws?.[cellRef]?.w || ws?.[cellRef]?.v;
3819
+ if (cellVal !== void 0) {
3820
+ c.fillText(String(cellVal).slice(0, 6), 14 + ci * colW, y + 9);
3821
+ }
3822
+ }
3823
+ }
3824
+ c.strokeStyle = "#cbd5e1";
3825
+ c.lineWidth = 1;
3826
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
3827
+ }
3828
+ }));
3829
+ },
3188
3830
  download: () => {
3189
3831
  const blob = new Blob([ctx.buffer], { type: this.mimeTypes[0] });
3190
3832
  const url = URL.createObjectURL(blob);
@@ -3217,7 +3859,39 @@ var CsvPlugin = class {
3217
3859
  return ext === ".csv" || ext === ".tsv" || mime === "text/csv" || mime === "text/tab-separated-values";
3218
3860
  }
3219
3861
  getToolbarActions(instance) {
3220
- return [
3862
+ const totalPages = instance.getPageCount?.() ?? 1;
3863
+ const actions = [];
3864
+ actions.push({
3865
+ id: "thumbnails",
3866
+ icon: "thumbnails",
3867
+ label: "Page Thumbnails",
3868
+ type: "button",
3869
+ group: "navigation",
3870
+ execute: () => instance.toggleThumbnails?.()
3871
+ });
3872
+ if (totalPages > 1) {
3873
+ actions.push({
3874
+ id: "page-nav",
3875
+ icon: "",
3876
+ label: "Page Navigation",
3877
+ type: "page-nav",
3878
+ group: "navigation",
3879
+ value: instance.getCurrentPage?.() ?? 1,
3880
+ max: totalPages,
3881
+ execute: (action, page) => {
3882
+ const cur = instance.getCurrentPage?.() ?? 1;
3883
+ const max = instance.getPageCount?.() ?? 1;
3884
+ if (action === "prev") {
3885
+ if (cur > 1) instance.goToPage?.(cur - 1);
3886
+ } else if (action === "next") {
3887
+ if (cur < max) instance.goToPage?.(cur + 1);
3888
+ } else if (typeof page === "number") {
3889
+ instance.goToPage?.(page);
3890
+ }
3891
+ }
3892
+ });
3893
+ }
3894
+ actions.push(
3221
3895
  {
3222
3896
  id: "zoom-out",
3223
3897
  icon: "zoom-out",
@@ -3238,6 +3912,16 @@ var CsvPlugin = class {
3238
3912
  instance.zoomIn?.();
3239
3913
  }
3240
3914
  },
3915
+ {
3916
+ id: "fit-page",
3917
+ icon: "fit-page",
3918
+ label: "Fit to View",
3919
+ type: "button",
3920
+ group: "zoom",
3921
+ execute: () => {
3922
+ instance.fitToPage?.();
3923
+ }
3924
+ },
3241
3925
  {
3242
3926
  id: "download",
3243
3927
  icon: "download",
@@ -3257,8 +3941,19 @@ var CsvPlugin = class {
3257
3941
  execute: () => {
3258
3942
  instance.print?.();
3259
3943
  }
3944
+ },
3945
+ {
3946
+ id: "open-window",
3947
+ icon: "open-window",
3948
+ label: "Open in Separate Full Window",
3949
+ type: "button",
3950
+ group: "actions",
3951
+ execute: () => {
3952
+ instance.openInSeparateWindow?.();
3953
+ }
3260
3954
  }
3261
- ];
3955
+ );
3956
+ return actions;
3262
3957
  }
3263
3958
  async render(ctx) {
3264
3959
  const decoder = new TextDecoder("utf-8");
@@ -3270,33 +3965,62 @@ var CsvPlugin = class {
3270
3965
  container.style.padding = "16px";
3271
3966
  container.style.boxSizing = "border-box";
3272
3967
  container.style.transformOrigin = "top left";
3968
+ const delimiter = ctx.metadata.extension === ".tsv" ? " " : ",";
3969
+ const allLines = text.split(/\r?\n/).filter((r) => r.trim().length > 0);
3970
+ const headerLine = allLines[0] || "";
3971
+ const headerCells = headerLine.split(delimiter);
3972
+ const dataLines = allLines.slice(1);
3973
+ const ROWS_PER_PAGE = 100;
3974
+ const totalPages = Math.max(1, Math.ceil(dataLines.length / ROWS_PER_PAGE));
3975
+ let currentPage = 1;
3273
3976
  const table = document.createElement("table");
3274
3977
  table.style.borderCollapse = "collapse";
3275
3978
  table.style.width = "100%";
3276
- table.style.fontFamily = "monospace";
3979
+ table.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace';
3277
3980
  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;
3282
- const tr = document.createElement("tr");
3283
- const cells = row.split(delimiter);
3284
- cells.forEach((cell) => {
3285
- const td = document.createElement(rowIndex === 0 ? "th" : "td");
3286
- td.textContent = cell.trim();
3287
- td.style.border = "1px solid #d0d7de";
3288
- td.style.padding = "6px 12px";
3289
- if (rowIndex === 0) {
3290
- td.style.backgroundColor = "#f6f8fa";
3291
- td.style.fontWeight = "bold";
3292
- }
3293
- tr.appendChild(td);
3981
+ const renderPage = (pageNum) => {
3982
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
3983
+ table.innerHTML = "";
3984
+ const headerTr = document.createElement("tr");
3985
+ headerCells.forEach((cell) => {
3986
+ const th = document.createElement("th");
3987
+ th.textContent = cell.trim();
3988
+ th.style.border = "1px solid #d0d7de";
3989
+ th.style.padding = "8px 12px";
3990
+ th.style.backgroundColor = "#f6f8fa";
3991
+ th.style.fontWeight = "600";
3992
+ th.style.whiteSpace = "nowrap";
3993
+ headerTr.appendChild(th);
3294
3994
  });
3295
- table.appendChild(tr);
3296
- });
3995
+ table.appendChild(headerTr);
3996
+ const start = (currentPage - 1) * ROWS_PER_PAGE;
3997
+ const end = Math.min(dataLines.length, start + ROWS_PER_PAGE);
3998
+ const pageRows = dataLines.slice(start, end);
3999
+ pageRows.forEach((row, idx) => {
4000
+ const tr = document.createElement("tr");
4001
+ if (idx % 2 === 1) tr.style.backgroundColor = "#f8fafc";
4002
+ const cells = row.split(delimiter);
4003
+ cells.forEach((cell) => {
4004
+ const td = document.createElement("td");
4005
+ td.textContent = cell.trim();
4006
+ td.style.border = "1px solid #d0d7de";
4007
+ td.style.padding = "6px 12px";
4008
+ td.style.whiteSpace = "nowrap";
4009
+ tr.appendChild(td);
4010
+ });
4011
+ table.appendChild(tr);
4012
+ });
4013
+ container.scrollTop = 0;
4014
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
4015
+ };
4016
+ renderPage(1);
3297
4017
  container.appendChild(table);
3298
4018
  ctx.container.appendChild(container);
3299
4019
  let scale = 1;
4020
+ const applyScale = () => {
4021
+ container.style.transform = `scale(${scale})`;
4022
+ container.style.transformOrigin = "top left";
4023
+ };
3300
4024
  const cleanup = () => {
3301
4025
  container.remove();
3302
4026
  ctx.container.innerHTML = "";
@@ -3304,22 +4028,86 @@ var CsvPlugin = class {
3304
4028
  ctx.signal.addEventListener("abort", cleanup);
3305
4029
  return {
3306
4030
  destroy: cleanup,
4031
+ getPageCount: () => totalPages,
4032
+ getCurrentPage: () => currentPage,
4033
+ goToPage: (page) => renderPage(page),
4034
+ getThumbnails: () => {
4035
+ const thumbnails = [];
4036
+ for (let i = 0; i < totalPages; i++) {
4037
+ const pageIdx = i;
4038
+ const startRow = pageIdx * ROWS_PER_PAGE + 1;
4039
+ const endRow = Math.min(dataLines.length, (pageIdx + 1) * ROWS_PER_PAGE);
4040
+ const label = totalPages === 1 ? "Table" : `Page ${pageIdx + 1} (${startRow}-${endRow})`;
4041
+ thumbnails.push({
4042
+ index: pageIdx,
4043
+ label,
4044
+ render: async (canvas) => {
4045
+ canvas.width = 140;
4046
+ canvas.height = 100;
4047
+ const c = canvas.getContext("2d");
4048
+ if (!c) return;
4049
+ c.fillStyle = "#ffffff";
4050
+ c.fillRect(0, 0, canvas.width, canvas.height);
4051
+ c.fillStyle = "#2563eb";
4052
+ c.fillRect(0, 0, canvas.width, 16);
4053
+ c.fillStyle = "#ffffff";
4054
+ c.font = "bold 8px sans-serif";
4055
+ c.fillText(`\u{1F4C8} ${label.slice(0, 18)}`, 6, 11);
4056
+ const startY = 17;
4057
+ const rowH = 13;
4058
+ const colW = 28;
4059
+ const cols = Math.min(4, headerCells.length || 4);
4060
+ c.fillStyle = "#f1f5f9";
4061
+ c.fillRect(0, startY, canvas.width, rowH);
4062
+ c.strokeStyle = "#cbd5e1";
4063
+ c.lineWidth = 0.8;
4064
+ c.strokeRect(0, startY, canvas.width, rowH);
4065
+ c.fillStyle = "#64748b";
4066
+ c.font = "bold 7px sans-serif";
4067
+ for (let ci = 0; ci < cols; ci++) {
4068
+ const hName = headerCells[ci] || `Col ${ci + 1}`;
4069
+ c.fillText(hName.slice(0, 5), 8 + ci * colW, startY + 9);
4070
+ }
4071
+ const rowsToPreview = dataLines.slice(pageIdx * ROWS_PER_PAGE, pageIdx * ROWS_PER_PAGE + 5);
4072
+ for (let ri = 0; ri < rowsToPreview.length; ri++) {
4073
+ const y = startY + (ri + 1) * rowH;
4074
+ if (y > canvas.height - 2) break;
4075
+ c.strokeStyle = "#e2e8f0";
4076
+ c.strokeRect(0, y, canvas.width, rowH);
4077
+ const cVals = rowsToPreview[ri].split(delimiter);
4078
+ c.fillStyle = "#334155";
4079
+ c.font = "6px sans-serif";
4080
+ for (let ci = 0; ci < cols; ci++) {
4081
+ const val = cVals[ci] || "";
4082
+ c.fillText(val.trim().slice(0, 6), 8 + ci * colW, y + 9);
4083
+ }
4084
+ }
4085
+ c.strokeStyle = "#cbd5e1";
4086
+ c.lineWidth = 1;
4087
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
4088
+ }
4089
+ });
4090
+ }
4091
+ return thumbnails;
4092
+ },
3307
4093
  zoomIn: () => {
3308
4094
  scale += 0.1;
3309
- container.style.transform = `scale(${scale})`;
4095
+ applyScale();
3310
4096
  },
3311
4097
  zoomOut: () => {
3312
4098
  scale = Math.max(0.2, scale - 0.1);
3313
- container.style.transform = `scale(${scale})`;
4099
+ applyScale();
3314
4100
  },
3315
4101
  getZoom: () => scale,
3316
4102
  setZoom: (level) => {
3317
4103
  scale = level;
3318
- container.style.transform = `scale(${scale})`;
4104
+ applyScale();
3319
4105
  },
3320
4106
  fitToPage: () => {
3321
- scale = 1;
3322
- container.style.transform = `scale(1)`;
4107
+ const availW = Math.max(280, (ctx.container.clientWidth || window.innerWidth) - 48);
4108
+ const tW = table.offsetWidth || 600;
4109
+ scale = Math.max(0.35, Math.min(1, availW / tW));
4110
+ applyScale();
3323
4111
  },
3324
4112
  download: () => {
3325
4113
  const blob = new Blob([ctx.buffer], { type: "text/csv" });
@@ -3383,28 +4171,34 @@ var CodePlugin = class {
3383
4171
  getToolbarActions(instance) {
3384
4172
  const totalPages = instance.getPageCount?.() ?? 1;
3385
4173
  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
- }
4174
+ actions.push({
4175
+ id: "thumbnails",
4176
+ icon: "thumbnails",
4177
+ label: "Page Thumbnails",
4178
+ type: "button",
4179
+ group: "navigation",
4180
+ execute: () => instance.toggleThumbnails?.()
4181
+ });
4182
+ actions.push({
4183
+ id: "page-nav",
4184
+ icon: "",
4185
+ label: "Page Navigation",
4186
+ type: "page-nav",
4187
+ group: "navigation",
4188
+ value: instance.getCurrentPage?.() ?? 1,
4189
+ max: totalPages,
4190
+ execute: (action, page) => {
4191
+ const cur = instance.getCurrentPage?.() ?? 1;
4192
+ const max = instance.getPageCount?.() ?? 1;
4193
+ if (action === "prev") {
4194
+ if (cur > 1) instance.goToPage?.(cur - 1);
4195
+ } else if (action === "next") {
4196
+ if (cur < max) instance.goToPage?.(cur + 1);
4197
+ } else if (typeof page === "number") {
4198
+ instance.goToPage?.(page);
3405
4199
  }
3406
- });
3407
- }
4200
+ }
4201
+ });
3408
4202
  actions.push(
3409
4203
  {
3410
4204
  id: "zoom-out",
@@ -3548,16 +4342,19 @@ var CodePlugin = class {
3548
4342
  let fontSize = 13;
3549
4343
  let rotation = 0;
3550
4344
  let zoomLevel = 1;
4345
+ let isUserZoomed = false;
3551
4346
  const pre = document.createElement("pre");
3552
4347
  const code = document.createElement("code");
3553
4348
  if (isTxt) {
4349
+ container.style.overflowX = "hidden";
4350
+ container.style.overflowY = "auto";
3554
4351
  container.style.backgroundColor = "#f1f5f9";
3555
- container.style.padding = "32px";
4352
+ container.style.padding = "16px 8px";
3556
4353
  container.style.boxSizing = "border-box";
3557
4354
  container.style.display = "flex";
3558
4355
  container.style.flexDirection = "column";
3559
4356
  container.style.alignItems = "center";
3560
- pre.style.margin = "0";
4357
+ pre.style.margin = "0 auto";
3561
4358
  pre.style.fontFamily = "Consolas, 'Courier New', monospace";
3562
4359
  pre.style.fontSize = "13px";
3563
4360
  pre.style.color = "#1e293b";
@@ -3569,10 +4366,12 @@ var CodePlugin = class {
3569
4366
  pre.style.minHeight = "1056px";
3570
4367
  pre.style.padding = "72px 56px";
3571
4368
  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)";
4369
+ pre.style.boxShadow = "0 4px 24px rgba(0, 0, 0, 0.08)";
3573
4370
  pre.style.borderRadius = "4px";
3574
4371
  pre.style.transformOrigin = "top center";
4372
+ pre.style.transition = "transform 0.15s ease";
3575
4373
  } else {
4374
+ container.style.overflow = "auto";
3576
4375
  container.style.backgroundColor = "#1e1e1e";
3577
4376
  container.style.color = "#d4d4d4";
3578
4377
  container.style.padding = "16px";
@@ -3604,42 +4403,9 @@ var CodePlugin = class {
3604
4403
  renderCodePage(rawPages[0] || (isTxt ? "" : fullText));
3605
4404
  pre.appendChild(code);
3606
4405
  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
4406
  const showPage = (pageNum) => {
3638
4407
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
3639
4408
  renderCodePage(rawPages[currentPage - 1] || (isTxt ? "" : fullText));
3640
- if (indicator) {
3641
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
3642
- }
3643
4409
  container.scrollTop = 0;
3644
4410
  ctx.emit("page-change", { page: currentPage, total: totalPages });
3645
4411
  };
@@ -3647,36 +4413,90 @@ var CodePlugin = class {
3647
4413
  showPage(1);
3648
4414
  }
3649
4415
  ctx.container.appendChild(container);
3650
- const cleanup = () => {
3651
- indicator?.remove();
3652
- container.remove();
3653
- ctx.container.innerHTML = "";
4416
+ const calculateTxtFit = () => {
4417
+ const availW = Math.max(280, container.clientWidth - 32);
4418
+ return Math.max(0.4, Math.min(3, availW / 816));
3654
4419
  };
3655
- ctx.signal.addEventListener("abort", cleanup);
3656
4420
  const updateTransform = () => {
3657
4421
  if (isTxt) {
3658
4422
  pre.style.transform = `scale(${zoomLevel}) rotate(${rotation}deg)`;
4423
+ const scaledH = 1056 * zoomLevel;
4424
+ const extraH = Math.max(0, scaledH - 1056);
4425
+ pre.style.marginBottom = `${extraH + 32}px`;
3659
4426
  } else {
3660
4427
  pre.style.transform = `rotate(${rotation}deg)`;
3661
4428
  pre.style.fontSize = `${fontSize}px`;
3662
4429
  }
3663
4430
  };
4431
+ let resizeObserver = null;
4432
+ if (isTxt) {
4433
+ setTimeout(() => {
4434
+ zoomLevel = calculateTxtFit();
4435
+ updateTransform();
4436
+ }, 60);
4437
+ resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
4438
+ if (!isUserZoomed) {
4439
+ zoomLevel = calculateTxtFit();
4440
+ updateTransform();
4441
+ }
4442
+ }) : null;
4443
+ resizeObserver?.observe(container);
4444
+ }
4445
+ const cleanup = () => {
4446
+ resizeObserver?.disconnect();
4447
+ container.remove();
4448
+ ctx.container.innerHTML = "";
4449
+ };
4450
+ ctx.signal.addEventListener("abort", cleanup);
3664
4451
  return {
3665
4452
  destroy: cleanup,
3666
4453
  getPageCount: () => totalPages,
3667
4454
  getCurrentPage: () => currentPage,
3668
4455
  goToPage: (page) => showPage(page),
4456
+ getThumbnails: () => {
4457
+ return rawPages.map((pageText, idx) => ({
4458
+ index: idx,
4459
+ label: `Page ${idx + 1}`,
4460
+ render: async (canvas) => {
4461
+ canvas.width = 140;
4462
+ canvas.height = 180;
4463
+ const c = canvas.getContext("2d");
4464
+ if (!c) return;
4465
+ c.fillStyle = "#ffffff";
4466
+ c.fillRect(0, 0, canvas.width, canvas.height);
4467
+ c.strokeStyle = "#cbd5e1";
4468
+ c.lineWidth = 1;
4469
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
4470
+ c.fillStyle = "#64748b";
4471
+ const lines = (pageText || "").split("\n").slice(0, 24);
4472
+ const lineH = 6;
4473
+ const startY = 14;
4474
+ const startX = 12;
4475
+ const maxW = canvas.width - 24;
4476
+ c.font = "5px monospace";
4477
+ for (let li = 0; li < lines.length; li++) {
4478
+ const l = lines[li].trim();
4479
+ if (!l) continue;
4480
+ const y = startY + li * lineH;
4481
+ if (y > canvas.height - 12) break;
4482
+ c.fillText(l.slice(0, 30), startX, y, maxW);
4483
+ }
4484
+ }
4485
+ }));
4486
+ },
3669
4487
  zoomIn: () => {
4488
+ isUserZoomed = true;
3670
4489
  if (isTxt) {
3671
- zoomLevel = Math.min(3, zoomLevel + 0.1);
4490
+ zoomLevel = Math.min(3.5, zoomLevel + 0.15);
3672
4491
  } else {
3673
4492
  fontSize = Math.min(32, fontSize + 2);
3674
4493
  }
3675
4494
  updateTransform();
3676
4495
  },
3677
4496
  zoomOut: () => {
4497
+ isUserZoomed = true;
3678
4498
  if (isTxt) {
3679
- zoomLevel = Math.max(0.1, zoomLevel - 0.1);
4499
+ zoomLevel = Math.max(0.1, zoomLevel - 0.15);
3680
4500
  } else {
3681
4501
  fontSize = Math.max(8, fontSize - 2);
3682
4502
  }
@@ -3684,6 +4504,7 @@ var CodePlugin = class {
3684
4504
  },
3685
4505
  getZoom: () => isTxt ? zoomLevel : fontSize / 13,
3686
4506
  setZoom: (level) => {
4507
+ isUserZoomed = true;
3687
4508
  if (isTxt) {
3688
4509
  zoomLevel = level;
3689
4510
  } else {
@@ -3692,9 +4513,10 @@ var CodePlugin = class {
3692
4513
  updateTransform();
3693
4514
  },
3694
4515
  fitToPage: () => {
4516
+ isUserZoomed = false;
3695
4517
  fontSize = 13;
3696
4518
  rotation = 0;
3697
- zoomLevel = isTxt ? Math.max(0.65, Math.min(1.05, (container.clientWidth - 48) / 816)) : 1;
4519
+ zoomLevel = isTxt ? calculateTxtFit() : 1;
3698
4520
  updateTransform();
3699
4521
  },
3700
4522
  rotateCW: () => {
@@ -4575,28 +5397,26 @@ var RtfPlugin = class {
4575
5397
  getToolbarActions(instance) {
4576
5398
  const totalPages = instance.getPageCount?.() ?? 1;
4577
5399
  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
- }
5400
+ actions.push({
5401
+ id: "page-nav",
5402
+ icon: "",
5403
+ label: "Page Navigation",
5404
+ type: "page-nav",
5405
+ group: "navigation",
5406
+ value: instance.getCurrentPage?.() ?? 1,
5407
+ max: totalPages,
5408
+ execute: (action, page) => {
5409
+ const cur = instance.getCurrentPage?.() ?? 1;
5410
+ const max = instance.getPageCount?.() ?? 1;
5411
+ if (action === "prev") {
5412
+ if (cur > 1) instance.goToPage?.(cur - 1);
5413
+ } else if (action === "next") {
5414
+ if (cur < max) instance.goToPage?.(cur + 1);
5415
+ } else if (typeof page === "number") {
5416
+ instance.goToPage?.(page);
4597
5417
  }
4598
- });
4599
- }
5418
+ }
5419
+ });
4600
5420
  actions.push(
4601
5421
  {
4602
5422
  id: "zoom-out",
@@ -4668,43 +5488,57 @@ var RtfPlugin = class {
4668
5488
  async render(ctx) {
4669
5489
  const wrapper = document.createElement("div");
4670
5490
  wrapper.className = "fp-rtf-wrapper";
4671
- wrapper.style.padding = "32px";
4672
- wrapper.style.maxWidth = "850px";
5491
+ wrapper.style.padding = "0";
5492
+ wrapper.style.width = "816px";
4673
5493
  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%";
5494
+ wrapper.style.boxSizing = "border-box";
5495
+ wrapper.style.display = "flex";
5496
+ wrapper.style.flexDirection = "column";
5497
+ wrapper.style.alignItems = "center";
5498
+ wrapper.style.backgroundColor = "transparent";
4678
5499
  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";
5500
+ wrapper.style.transition = "transform 0.15s ease";
5501
+ ctx.container.style.overflowX = "hidden";
5502
+ ctx.container.style.overflowY = "auto";
5503
+ ctx.container.style.padding = "16px 8px";
4682
5504
  ctx.container.style.backgroundColor = "#f1f5f9";
4683
5505
  ctx.container.appendChild(wrapper);
4684
5506
  let scale = 1;
4685
5507
  let rotation = 0;
4686
5508
  let pageElements = [];
4687
- let fitMode = "width";
5509
+ let isUserZoomed = false;
5510
+ let fitMode = ctx?.options?.fitMode || "width";
4688
5511
  const calculateFitScale = (mode = fitMode) => {
4689
5512
  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);
5513
+ const elW = 816;
5514
+ const singlePageH = activeEl?.offsetHeight || 1056;
5515
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
5516
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
4694
5517
  const sW = availW / elW;
4695
5518
  const sH = availH / singlePageH;
4696
5519
  if (mode === "page") {
4697
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
5520
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
4698
5521
  }
4699
- return Math.max(0.65, Math.min(1.05, sW));
5522
+ return Math.max(0.4, Math.min(3, sW));
4700
5523
  };
4701
5524
  const applyTransform = () => {
4702
5525
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
4703
5526
  wrapper.style.transformOrigin = "top center";
5527
+ const activeEl = pageElements[currentPage - 1];
5528
+ const baseH = activeEl?.offsetHeight || 1056;
5529
+ const scaledH = baseH * scale;
5530
+ const extraH = Math.max(0, scaledH - baseH);
5531
+ wrapper.style.marginBottom = `${extraH + 32}px`;
4704
5532
  };
5533
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
5534
+ if (!isUserZoomed) {
5535
+ scale = calculateFitScale(fitMode);
5536
+ applyTransform();
5537
+ }
5538
+ }) : null;
5539
+ resizeObserver?.observe(ctx.container);
4705
5540
  setTimeout(() => {
4706
- fitMode = "width";
4707
- scale = calculateFitScale("width");
5541
+ scale = calculateFitScale(fitMode);
4708
5542
  applyTransform();
4709
5543
  }, 60);
4710
5544
  try {
@@ -4714,20 +5548,34 @@ var RtfPlugin = class {
4714
5548
  const doc = new RTFJS__namespace.Document(ctx.buffer, {});
4715
5549
  const htmlElements = await doc.render();
4716
5550
  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);
5551
+ const extractBlocks = (nodes) => {
5552
+ for (const item of nodes) {
5553
+ if (!item || !(item instanceof HTMLElement)) continue;
5554
+ const tag = item.tagName.toLowerCase();
5555
+ const hasChildBlocks = Array.from(item.children).some((c) => {
5556
+ const ct = c.tagName.toLowerCase();
5557
+ return ["p", "div", "table", "h1", "h2", "h3", "h4", "h5", "h6", "section", "article", "ul", "ol"].includes(ct);
5558
+ });
5559
+ if (hasChildBlocks && !["table", "tr", "td", "th", "ul", "ol", "li"].includes(tag)) {
5560
+ extractBlocks(Array.from(item.children));
5561
+ } else {
5562
+ if (tag === "p" || tag === "div") {
5563
+ item.style.display = "block";
5564
+ item.style.marginBottom = "12px";
5565
+ item.style.lineHeight = "1.6";
5566
+ }
5567
+ contentNodes.push(item);
5568
+ }
4722
5569
  }
4723
- }
5570
+ };
5571
+ extractBlocks(htmlElements);
4724
5572
  wrapper.innerHTML = "";
4725
5573
  contentNodes.forEach((node) => wrapper.appendChild(node));
4726
5574
  const childHeights = contentNodes.map((c) => {
4727
5575
  const rectH = c.getBoundingClientRect ? c.getBoundingClientRect().height : 0;
4728
5576
  const offH = c.offsetHeight || 0;
4729
5577
  const textLen = c.textContent?.trim().length || 0;
4730
- const estH = Math.max(24, Math.ceil(textLen / 75) * 22 + 14);
5578
+ const estH = Math.max(28, Math.ceil(textLen / 75) * 24 + 16);
4731
5579
  return Math.max(rectH, offH, estH);
4732
5580
  });
4733
5581
  wrapper.innerHTML = "";
@@ -4744,6 +5592,7 @@ var RtfPlugin = class {
4744
5592
  card.style.marginBottom = "24px";
4745
5593
  card.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
4746
5594
  card.style.lineHeight = "1.6";
5595
+ card.style.color = "#1e293b";
4747
5596
  return card;
4748
5597
  };
4749
5598
  let curCard = createRtfCard();
@@ -4779,9 +5628,8 @@ var RtfPlugin = class {
4779
5628
  pageCard.style.padding = "72px 56px";
4780
5629
  pageCard.style.width = "816px";
4781
5630
  pageCard.style.minHeight = "1056px";
4782
- pageCard.style.maxWidth = "100%";
4783
5631
  pageCard.style.boxSizing = "border-box";
4784
- pageCard.style.fontFamily = "serif";
5632
+ pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
4785
5633
  pageCard.style.fontSize = "12pt";
4786
5634
  pageCard.style.lineHeight = "1.6";
4787
5635
  pageCard.style.color = "#1e293b";
@@ -4794,29 +5642,6 @@ var RtfPlugin = class {
4794
5642
  }
4795
5643
  const totalPages = Math.max(1, pageElements.length);
4796
5644
  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
5645
  const showPage = (pageNum) => {
4821
5646
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
4822
5647
  if (totalPages > 1) {
@@ -4824,9 +5649,6 @@ var RtfPlugin = class {
4824
5649
  el.style.display = idx + 1 === currentPage ? "block" : "none";
4825
5650
  });
4826
5651
  }
4827
- if (indicator) {
4828
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4829
- }
4830
5652
  ctx.container.scrollTop = 0;
4831
5653
  ctx.emit("page-change", { page: currentPage, total: totalPages });
4832
5654
  };
@@ -4834,7 +5656,7 @@ var RtfPlugin = class {
4834
5656
  showPage(1);
4835
5657
  }
4836
5658
  const cleanup = () => {
4837
- indicator?.remove();
5659
+ resizeObserver?.disconnect();
4838
5660
  wrapper.remove();
4839
5661
  ctx.container.innerHTML = "";
4840
5662
  };
@@ -4845,21 +5667,25 @@ var RtfPlugin = class {
4845
5667
  getCurrentPage: () => currentPage,
4846
5668
  goToPage: (page) => showPage(page),
4847
5669
  zoomIn: () => {
5670
+ isUserZoomed = true;
4848
5671
  scale += 0.15;
4849
5672
  applyTransform();
4850
5673
  },
4851
5674
  zoomOut: () => {
5675
+ isUserZoomed = true;
4852
5676
  scale = Math.max(0.2, scale - 0.15);
4853
5677
  applyTransform();
4854
5678
  },
4855
5679
  getZoom: () => scale,
4856
5680
  setZoom: (level) => {
5681
+ isUserZoomed = true;
4857
5682
  scale = level;
4858
5683
  applyTransform();
4859
5684
  },
4860
5685
  fitToPage: () => {
4861
- fitMode = fitMode === "width" ? "page" : "width";
4862
- scale = calculateFitScale(fitMode);
5686
+ isUserZoomed = false;
5687
+ fitMode = "width";
5688
+ scale = calculateFitScale("width");
4863
5689
  rotation = 0;
4864
5690
  applyTransform();
4865
5691
  },
@@ -5047,40 +5873,36 @@ var OpenDocumentPlugin = class {
5047
5873
  const isPresentation = instance.isPresentation;
5048
5874
  const totalPages = instance.getPageCount?.() ?? 1;
5049
5875
  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
- }
5876
+ actions.push({
5877
+ id: "thumbnails",
5878
+ icon: "thumbnails",
5879
+ label: isPresentation ? "Slide Thumbnails" : "Page Thumbnails",
5880
+ type: "button",
5881
+ group: "navigation",
5882
+ execute: () => instance.toggleThumbnails?.()
5883
+ });
5884
+ actions.push({
5885
+ id: "page-nav",
5886
+ icon: "",
5887
+ label: isPresentation ? "Slide Navigation" : "Page Navigation",
5888
+ type: "page-nav",
5889
+ group: "navigation",
5890
+ value: instance.getCurrentPage?.() ?? 1,
5891
+ max: totalPages,
5892
+ execute: (action, page) => {
5893
+ const cur = instance.getCurrentPage?.() ?? 1;
5894
+ const max = instance.getPageCount?.() ?? 1;
5895
+ if (action === "prev") {
5896
+ if (cur > 1) instance.goToPage?.(cur - 1);
5897
+ } else if (action === "next") {
5898
+ if (cur < max) instance.goToPage?.(cur + 1);
5899
+ } else if (typeof page === "number") {
5900
+ instance.goToPage?.(page);
5901
+ } else if (typeof action === "number") {
5902
+ instance.goToPage?.(action);
5081
5903
  }
5082
- });
5083
- }
5904
+ }
5905
+ });
5084
5906
  actions.push(
5085
5907
  {
5086
5908
  id: "zoom-out",
@@ -5171,49 +5993,65 @@ var OpenDocumentPlugin = class {
5171
5993
  container.className = "fp-odf-container";
5172
5994
  container.style.width = "100%";
5173
5995
  container.style.height = "100%";
5174
- container.style.overflow = "auto";
5175
- container.style.padding = "24px";
5996
+ container.style.overflowX = "hidden";
5997
+ container.style.overflowY = "auto";
5998
+ container.style.padding = "16px 8px";
5176
5999
  container.style.backgroundColor = "#f1f5f9";
5177
6000
  const wrapper = document.createElement("div");
5178
6001
  wrapper.className = "fp-odf-wrapper";
5179
6002
  wrapper.style.margin = "0 auto";
6003
+ wrapper.style.width = isPresentation ? "960px" : "816px";
6004
+ wrapper.style.boxSizing = "border-box";
5180
6005
  wrapper.style.backgroundColor = "#ffffff";
5181
6006
  wrapper.style.boxShadow = "0 2px 10px rgba(0,0,0,0.08)";
5182
6007
  wrapper.style.borderRadius = "4px";
5183
6008
  wrapper.style.transformOrigin = "top center";
5184
- wrapper.style.transition = "transform 0.2s ease";
6009
+ wrapper.style.transition = "transform 0.15s ease";
5185
6010
  container.appendChild(wrapper);
5186
6011
  ctx.container.appendChild(container);
5187
6012
  let scale = 1;
5188
6013
  let rotation = 0;
5189
- let fitMode = "width";
6014
+ let isUserZoomed = false;
6015
+ let fitMode = ctx?.options?.fitMode || "width";
6016
+ let currentPage = 1;
6017
+ let totalPages = 1;
6018
+ let slides = [];
5190
6019
  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);
6020
+ const activeSlide = slides[currentPage - 1];
6021
+ const elW = isPresentation ? 960 : 816;
6022
+ const singlePageH = Math.min(activeSlide?.offsetHeight || wrapper.offsetHeight || 1056, Math.round(elW * 1.32));
6023
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
6024
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
5195
6025
  const sW = availW / elW;
5196
6026
  const sH = availH / (isPresentation ? 540 : singlePageH);
5197
6027
  if (isPresentation) {
5198
- return Math.min(1, Math.min(sW, sH));
6028
+ return Math.min(2.5, Math.min(sW, sH));
5199
6029
  }
5200
6030
  if (mode === "page") {
5201
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
6031
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
5202
6032
  }
5203
- return Math.max(0.65, Math.min(1.05, sW));
6033
+ return Math.max(0.4, Math.min(3, sW));
5204
6034
  };
5205
6035
  const applyTransform = () => {
5206
6036
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5207
6037
  wrapper.style.transformOrigin = "top center";
6038
+ const activeSlide = slides[currentPage - 1];
6039
+ const baseH = activeSlide?.offsetHeight || wrapper.offsetHeight || 1056;
6040
+ const scaledH = baseH * scale;
6041
+ const extraH = Math.max(0, scaledH - baseH);
6042
+ wrapper.style.marginBottom = `${extraH + 32}px`;
5208
6043
  };
6044
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
6045
+ if (!isUserZoomed) {
6046
+ scale = calculateFitScale(fitMode);
6047
+ applyTransform();
6048
+ }
6049
+ }) : null;
6050
+ resizeObserver?.observe(ctx.container);
5209
6051
  setTimeout(() => {
5210
- fitMode = "width";
5211
- scale = calculateFitScale("width");
6052
+ scale = calculateFitScale(fitMode);
5212
6053
  applyTransform();
5213
6054
  }, 60);
5214
- let currentPage = 1;
5215
- let totalPages = 1;
5216
- let slides = [];
5217
6055
  if (isPresentation) {
5218
6056
  wrapper.style.maxWidth = "960px";
5219
6057
  wrapper.style.aspectRatio = "16 / 9";
@@ -5314,24 +6152,9 @@ var OpenDocumentPlugin = class {
5314
6152
  wrapper.appendChild(page);
5315
6153
  slides.push(page);
5316
6154
  });
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
6155
  }
5334
6156
  const cleanup = () => {
6157
+ resizeObserver?.disconnect();
5335
6158
  imageUrls.forEach((url) => URL.revokeObjectURL(url));
5336
6159
  container.remove();
5337
6160
  ctx.container.innerHTML = "";
@@ -5343,10 +6166,6 @@ var OpenDocumentPlugin = class {
5343
6166
  slides.forEach((s, idx) => {
5344
6167
  s.style.display = idx === page - 1 ? "block" : "none";
5345
6168
  });
5346
- const indicator = container.querySelector(".fp-odt-page-indicator");
5347
- if (indicator) {
5348
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
5349
- }
5350
6169
  container.scrollTop = 0;
5351
6170
  ctx.emit("page-change", { page: currentPage, total: totalPages });
5352
6171
  };
@@ -5354,21 +6173,25 @@ var OpenDocumentPlugin = class {
5354
6173
  destroy: cleanup,
5355
6174
  isPresentation,
5356
6175
  zoomIn: () => {
6176
+ isUserZoomed = true;
5357
6177
  scale += 0.15;
5358
6178
  applyTransform();
5359
6179
  },
5360
6180
  zoomOut: () => {
6181
+ isUserZoomed = true;
5361
6182
  scale = Math.max(0.2, scale - 0.15);
5362
6183
  applyTransform();
5363
6184
  },
5364
6185
  getZoom: () => scale,
5365
6186
  setZoom: (level) => {
6187
+ isUserZoomed = true;
5366
6188
  scale = level;
5367
6189
  applyTransform();
5368
6190
  },
5369
6191
  fitToPage: () => {
5370
- fitMode = fitMode === "width" ? "page" : "width";
5371
- scale = calculateFitScale(fitMode);
6192
+ isUserZoomed = false;
6193
+ fitMode = "width";
6194
+ scale = calculateFitScale("width");
5372
6195
  rotation = 0;
5373
6196
  applyTransform();
5374
6197
  },
@@ -5384,23 +6207,45 @@ var OpenDocumentPlugin = class {
5384
6207
  getPageCount: () => totalPages,
5385
6208
  getCurrentPage: () => currentPage,
5386
6209
  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
- }));
6210
+ const count = totalPages || slides.length || 1;
6211
+ const items = [];
6212
+ for (let idx = 0; idx < count; idx++) {
6213
+ const itemIdx = idx;
6214
+ items.push({
6215
+ index: itemIdx,
6216
+ label: isPresentation ? `Slide ${itemIdx + 1}` : `Page ${itemIdx + 1}`,
6217
+ render: async (canvas) => {
6218
+ const ctx2d = canvas.getContext("2d");
6219
+ if (!ctx2d) return;
6220
+ if (isPresentation) {
6221
+ canvas.width = 160;
6222
+ canvas.height = 90;
6223
+ ctx2d.fillStyle = "#f8fafc";
6224
+ ctx2d.fillRect(0, 0, 160, 90);
6225
+ ctx2d.strokeStyle = "#cbd5e1";
6226
+ ctx2d.lineWidth = 1;
6227
+ ctx2d.strokeRect(0.5, 0.5, 159, 89);
6228
+ ctx2d.fillStyle = "#334155";
6229
+ ctx2d.font = "bold 11px sans-serif";
6230
+ ctx2d.textAlign = "center";
6231
+ ctx2d.fillText(`Slide ${itemIdx + 1}`, 80, 50);
6232
+ } else {
6233
+ canvas.width = 140;
6234
+ canvas.height = 180;
6235
+ ctx2d.fillStyle = "#ffffff";
6236
+ ctx2d.fillRect(0, 0, 140, 180);
6237
+ ctx2d.strokeStyle = "#cbd5e1";
6238
+ ctx2d.lineWidth = 1;
6239
+ ctx2d.strokeRect(0.5, 0.5, 139, 179);
6240
+ ctx2d.fillStyle = "#64748b";
6241
+ ctx2d.font = "bold 10px sans-serif";
6242
+ ctx2d.textAlign = "center";
6243
+ ctx2d.fillText(`Page ${itemIdx + 1}`, 70, 90);
6244
+ }
6245
+ }
6246
+ });
6247
+ }
6248
+ return items;
5404
6249
  },
5405
6250
  download: () => {
5406
6251
  const mimeType = this.mimeTypes.find((m) => m.includes(ext.replace(".", ""))) || "application/octet-stream";
@@ -5505,23 +6350,24 @@ var OpenDocumentPlugin = class {
5505
6350
  case "h": {
5506
6351
  const level = Math.min(6, Math.max(1, Number(node.getAttribute("text:outline-level") || 1)));
5507
6352
  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}>`;
6353
+ html += `<h${level} style="${style}; margin: 18px 0 8px; color: #1e3a8a; font-family: Calibri, 'Segoe UI', Arial, sans-serif;">${node.textContent || ""}</h${level}>`;
5509
6354
  break;
5510
6355
  }
5511
6356
  case "p": {
5512
6357
  const style = styles.get(node.getAttribute("text:style-name") || "") || "";
5513
6358
  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>`;
6359
+ const isInsideLi = node.parentElement?.localName === "list-item" || node.parentElement?.tagName?.toLowerCase() === "list-item";
6360
+ 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
6361
  break;
5516
6362
  }
5517
6363
  case "list": {
5518
- html += '<ul style="margin: 8px 0; padding-left: 24px;">';
6364
+ html += '<ul style="margin: 8px 0 12px; padding-left: 24px;">';
5519
6365
  Array.from(node.children).forEach((c) => walk(c));
5520
6366
  html += "</ul>";
5521
6367
  break;
5522
6368
  }
5523
6369
  case "list-item": {
5524
- html += "<li>";
6370
+ html += '<li style="margin: 4px 0; font-size: 11pt; line-height: 1.5; color: #1e293b;">';
5525
6371
  Array.from(node.children).forEach((c) => walk(c));
5526
6372
  html += "</li>";
5527
6373
  break;
@@ -5647,28 +6493,26 @@ var DocPlugin = class {
5647
6493
  getToolbarActions(instance) {
5648
6494
  const totalPages = instance.getPageCount?.() ?? 1;
5649
6495
  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
- }
6496
+ actions.push({
6497
+ id: "page-nav",
6498
+ icon: "",
6499
+ label: "Page Navigation",
6500
+ type: "page-nav",
6501
+ group: "navigation",
6502
+ value: instance.getCurrentPage?.() ?? 1,
6503
+ max: totalPages,
6504
+ execute: (action, page) => {
6505
+ const cur = instance.getCurrentPage?.() ?? 1;
6506
+ const max = instance.getPageCount?.() ?? 1;
6507
+ if (action === "prev") {
6508
+ if (cur > 1) instance.goToPage?.(cur - 1);
6509
+ } else if (action === "next") {
6510
+ if (cur < max) instance.goToPage?.(cur + 1);
6511
+ } else if (typeof page === "number") {
6512
+ instance.goToPage?.(page);
5669
6513
  }
5670
- });
5671
- }
6514
+ }
6515
+ });
5672
6516
  actions.push(
5673
6517
  {
5674
6518
  id: "zoom-out",
@@ -5742,8 +6586,9 @@ var DocPlugin = class {
5742
6586
  container.className = "fp-doc-container";
5743
6587
  container.style.width = "100%";
5744
6588
  container.style.height = "100%";
5745
- container.style.overflow = "auto";
5746
- container.style.padding = "32px 16px";
6589
+ container.style.overflowX = "hidden";
6590
+ container.style.overflowY = "auto";
6591
+ container.style.padding = "16px 8px";
5747
6592
  container.style.backgroundColor = "#f1f5f9";
5748
6593
  container.style.display = "flex";
5749
6594
  container.style.justifyContent = "center";
@@ -5788,16 +6633,15 @@ var DocPlugin = class {
5788
6633
  const pageCard = document.createElement("div");
5789
6634
  pageCard.className = "fp-doc-page-card";
5790
6635
  pageCard.style.width = "816px";
5791
- pageCard.style.height = "1056px";
6636
+ pageCard.style.minHeight = "1056px";
5792
6637
  pageCard.style.backgroundColor = "#ffffff";
5793
- pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
6638
+ pageCard.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
5794
6639
  pageCard.style.borderRadius = "4px";
5795
- pageCard.style.padding = "96px 72px";
6640
+ pageCard.style.padding = "72px 56px";
5796
6641
  pageCard.style.boxSizing = "border-box";
5797
- pageCard.style.overflow = "hidden";
5798
6642
  pageCard.style.display = i === 0 ? "block" : "none";
5799
6643
  pageCard.style.transformOrigin = "top center";
5800
- pageCard.style.transition = "transform 0.2s ease";
6644
+ pageCard.style.transition = "transform 0.15s ease";
5801
6645
  pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
5802
6646
  pageCard.style.color = "#1e293b";
5803
6647
  if (isFallback && i === 0) {
@@ -5814,34 +6658,15 @@ var DocPlugin = class {
5814
6658
  container.appendChild(pageCard);
5815
6659
  pageCards.push(pageCard);
5816
6660
  }
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
6661
  let rotation = 0;
5841
6662
  const applyTransform = () => {
5842
6663
  const activeCard = pageCards[currentPage - 1];
5843
6664
  if (activeCard) {
5844
6665
  activeCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
6666
+ const baseH = activeCard.offsetHeight || 1056;
6667
+ const scaledH = baseH * scale;
6668
+ const extraH = Math.max(0, scaledH - baseH);
6669
+ activeCard.style.marginBottom = `${extraH + 32}px`;
5845
6670
  }
5846
6671
  };
5847
6672
  const showPage = (pageNum) => {
@@ -5854,35 +6679,39 @@ var DocPlugin = class {
5854
6679
  card.style.display = "none";
5855
6680
  }
5856
6681
  });
5857
- if (indicator) {
5858
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
5859
- }
5860
6682
  container.scrollTop = 0;
5861
6683
  ctx.emit("page-change", { page: currentPage, total: totalPages });
5862
6684
  };
5863
6685
  if (totalPages > 1) {
5864
6686
  showPage(1);
5865
6687
  }
5866
- let fitMode = "width";
6688
+ let fitMode = ctx?.options?.fitMode || "width";
6689
+ let isUserZoomed = false;
5867
6690
  const calculateFitScale = (mode = fitMode) => {
5868
6691
  const elW = 816;
5869
6692
  const elH = 1056;
5870
- const availW = Math.max(280, ctx.container.clientWidth - 48);
5871
- const availH = Math.max(280, ctx.container.clientHeight - 80);
6693
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
6694
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
5872
6695
  const sW = availW / elW;
5873
6696
  const sH = availH / elH;
5874
6697
  if (mode === "page") {
5875
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
6698
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
5876
6699
  }
5877
- return Math.max(0.65, Math.min(1.05, sW));
6700
+ return Math.max(0.4, Math.min(3, sW));
5878
6701
  };
6702
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
6703
+ if (!isUserZoomed) {
6704
+ scale = calculateFitScale(fitMode);
6705
+ applyTransform();
6706
+ }
6707
+ }) : null;
6708
+ resizeObserver?.observe(ctx.container);
5879
6709
  setTimeout(() => {
5880
- fitMode = "width";
5881
- scale = calculateFitScale("width");
6710
+ scale = calculateFitScale(fitMode);
5882
6711
  applyTransform();
5883
6712
  }, 60);
5884
6713
  const cleanup = () => {
5885
- indicator?.remove();
6714
+ resizeObserver?.disconnect();
5886
6715
  container.remove();
5887
6716
  ctx.container.innerHTML = "";
5888
6717
  };
@@ -5893,21 +6722,25 @@ var DocPlugin = class {
5893
6722
  getCurrentPage: () => currentPage,
5894
6723
  goToPage: (page) => showPage(page),
5895
6724
  zoomIn: () => {
6725
+ isUserZoomed = true;
5896
6726
  scale += 0.15;
5897
6727
  applyTransform();
5898
6728
  },
5899
6729
  zoomOut: () => {
6730
+ isUserZoomed = true;
5900
6731
  scale = Math.max(0.2, scale - 0.15);
5901
6732
  applyTransform();
5902
6733
  },
5903
6734
  getZoom: () => scale,
5904
6735
  setZoom: (level) => {
6736
+ isUserZoomed = true;
5905
6737
  scale = level;
5906
6738
  applyTransform();
5907
6739
  },
5908
6740
  fitToPage: () => {
5909
- fitMode = fitMode === "width" ? "page" : "width";
5910
- scale = calculateFitScale(fitMode);
6741
+ isUserZoomed = false;
6742
+ fitMode = "width";
6743
+ scale = calculateFitScale("width");
5911
6744
  rotation = 0;
5912
6745
  applyTransform();
5913
6746
  },
@@ -6284,25 +7117,31 @@ ${chartSvg}
6284
7117
  i++;
6285
7118
  continue;
6286
7119
  }
6287
- if (line.length < 60 && !line.endsWith(".") && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#"))) {
7120
+ if (/^\d{1,2}$/.test(line.trim())) {
7121
+ i++;
7122
+ continue;
7123
+ }
7124
+ const isShortLine = line.length < 60 && !line.endsWith(".") && !line.endsWith(",");
7125
+ 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));
7126
+ if (isTitleLike) {
6288
7127
  if (inList) {
6289
7128
  html += "</ul>";
6290
7129
  inList = false;
6291
7130
  }
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>`;
7131
+ 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
7132
  } else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
6294
7133
  if (!inList) {
6295
- html += '<ul style="margin: 8px 0; padding-left: 24px;">';
7134
+ html += '<ul style="margin: 8px 0 12px; padding-left: 24px;">';
6296
7135
  inList = true;
6297
7136
  }
6298
7137
  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>`;
7138
+ html += `<li style="margin: 4px 0; line-height: 1.5; font-size: 11pt; color: #1e293b;">${DOMPurify6__default.default.sanitize(bulletText, sanitizeOptions)}</li>`;
6300
7139
  } else {
6301
7140
  if (inList) {
6302
7141
  html += "</ul>";
6303
7142
  inList = false;
6304
7143
  }
6305
- html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6__default.default.sanitize(line, sanitizeOptions)}</p>`;
7144
+ 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
7145
  }
6307
7146
  i++;
6308
7147
  }