@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/index.cjs CHANGED
@@ -609,8 +609,7 @@ var ICON_FULLSCREEN = `<svg width="24" height="24" viewBox="0 0 24 24" fill="non
609
609
  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>`;
610
610
  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>`;
611
611
  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>`;
612
- 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>`;
613
- 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>`;
612
+ 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>`;
614
613
  var ICON_MAP = {
615
614
  "zoom-in": ICON_ZOOM_IN,
616
615
  "zoom-out": ICON_ZOOM_OUT,
@@ -637,28 +636,177 @@ var ICON_MAP = {
637
636
  "forward-10": ICON_FAST_FORWARD,
638
637
  "rewind": ICON_REWIND,
639
638
  "replay-10": ICON_REWIND,
640
- "speed": ICON_SPEED,
641
639
  "open-window": ICON_EXTERNAL_WINDOW,
642
- "external-window": ICON_EXTERNAL_WINDOW
640
+ "external-window": ICON_EXTERNAL_WINDOW,
641
+ "openWindow": ICON_EXTERNAL_WINDOW,
642
+ "openSeparateWindow": ICON_EXTERNAL_WINDOW
643
643
  };
644
644
  var ToolbarController = class {
645
645
  el;
646
646
  toolbarEl;
647
647
  actions = [];
648
+ config = {};
648
649
  pageInputEl = null;
649
650
  pageLabelEl = null;
650
- constructor(container) {
651
+ prevPageBtn = null;
652
+ nextPageBtn = null;
653
+ constructor(container, config) {
651
654
  this.el = container;
655
+ if (config) this.config = { ...config };
652
656
  this.toolbarEl = createElement("div", { className: "fp-toolbar" });
653
657
  this.el.appendChild(this.toolbarEl);
654
658
  }
659
+ setConfig(config) {
660
+ this.config = { ...config };
661
+ this.render();
662
+ }
663
+ getConfig() {
664
+ return { ...this.config };
665
+ }
666
+ hideAction(actionId) {
667
+ this.config[actionId] = false;
668
+ if (actionId === "zoom-in" || actionId === "zoomIn") this.config.zoomIn = false;
669
+ if (actionId === "zoom-out" || actionId === "zoomOut") this.config.zoomOut = false;
670
+ if (actionId === "fit-page" || actionId === "fitPage" || actionId === "fitToPage" || actionId === "fit-width") {
671
+ this.config.fitPage = false;
672
+ this.config.fitToPage = false;
673
+ this.config.fitWidth = false;
674
+ }
675
+ if (actionId === "rotate-cw" || actionId === "rotate" || actionId === "rotateCW") this.config.rotate = false;
676
+ if (actionId === "fullscreen") this.config.fullscreen = false;
677
+ if (actionId === "download") this.config.download = false;
678
+ if (actionId === "print") this.config.print = false;
679
+ if (actionId === "open-window" || actionId === "openWindow" || actionId === "openSeparateWindow") {
680
+ this.config.openWindow = false;
681
+ this.config.openSeparateWindow = false;
682
+ }
683
+ if (actionId === "copy") this.config.copy = false;
684
+ if (actionId === "page-nav" || actionId === "pageNav" || actionId === "pagination") {
685
+ this.config.pageNav = false;
686
+ this.config.pagination = false;
687
+ }
688
+ this.render();
689
+ }
690
+ showAction(actionId) {
691
+ this.config[actionId] = true;
692
+ if (actionId === "zoom-in" || actionId === "zoomIn") this.config.zoomIn = true;
693
+ if (actionId === "zoom-out" || actionId === "zoomOut") this.config.zoomOut = true;
694
+ if (actionId === "fit-page" || actionId === "fitPage" || actionId === "fitToPage" || actionId === "fit-width") {
695
+ this.config.fitPage = true;
696
+ this.config.fitToPage = true;
697
+ this.config.fitWidth = true;
698
+ }
699
+ if (actionId === "rotate-cw" || actionId === "rotate" || actionId === "rotateCW") this.config.rotate = true;
700
+ if (actionId === "fullscreen") this.config.fullscreen = true;
701
+ if (actionId === "download") this.config.download = true;
702
+ if (actionId === "print") this.config.print = true;
703
+ if (actionId === "open-window" || actionId === "openWindow" || actionId === "openSeparateWindow") {
704
+ this.config.openWindow = true;
705
+ this.config.openSeparateWindow = true;
706
+ }
707
+ if (actionId === "copy") this.config.copy = true;
708
+ if (actionId === "page-nav" || actionId === "pageNav" || actionId === "pagination") {
709
+ this.config.pageNav = true;
710
+ this.config.pagination = true;
711
+ }
712
+ this.render();
713
+ }
714
+ normalizeActionId(actionId) {
715
+ if (actionId === "zoomIn") return "zoom-in";
716
+ if (actionId === "zoomOut") return "zoom-out";
717
+ if (actionId === "fitPage" || actionId === "fitToPage" || actionId === "fitWidth") return "fit-page";
718
+ if (actionId === "rotateCW") return "rotate-cw";
719
+ if (actionId === "rotateCCW") return "rotate-ccw";
720
+ if (actionId === "openWindow" || actionId === "openSeparateWindow") return "open-window";
721
+ if (actionId === "pageNav") return "page-nav";
722
+ return actionId;
723
+ }
724
+ enableAction(actionId) {
725
+ const norm = this.normalizeActionId(actionId);
726
+ const btn = this.toolbarEl.querySelector(`button[data-action-id="${actionId}"], button[data-action-id="${norm}"]`);
727
+ if (btn) {
728
+ btn.disabled = false;
729
+ btn.classList.remove("disabled");
730
+ }
731
+ }
732
+ disableAction(actionId) {
733
+ const norm = this.normalizeActionId(actionId);
734
+ const btn = this.toolbarEl.querySelector(`button[data-action-id="${actionId}"], button[data-action-id="${norm}"]`);
735
+ if (btn) {
736
+ btn.disabled = true;
737
+ btn.classList.add("disabled");
738
+ }
739
+ }
740
+ setActionActive(actionId, active) {
741
+ const norm = this.normalizeActionId(actionId);
742
+ const btn = this.toolbarEl.querySelector(`button[data-action-id="${actionId}"], button[data-action-id="${norm}"]`);
743
+ if (btn) {
744
+ if (active) {
745
+ btn.classList.add("active");
746
+ } else {
747
+ btn.classList.remove("active");
748
+ }
749
+ }
750
+ }
751
+ isActionEnabled(action) {
752
+ const c = this.config;
753
+ const id = action.id;
754
+ if (c[id] === false) return false;
755
+ if (id === "zoom-in" && (c.zoomIn === false || c["zoom-in"] === false)) return false;
756
+ if (id === "zoom-out" && (c.zoomOut === false || c["zoom-out"] === false)) return false;
757
+ 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)) {
758
+ return false;
759
+ }
760
+ if ((id === "rotate-cw" || id === "rotate-ccw" || id === "rotate") && (c.rotate === false || c.rotateCW === false || c.rotateCCW === false || c["rotate-cw"] === false)) {
761
+ return false;
762
+ }
763
+ if (id === "fullscreen" && c.fullscreen === false) return false;
764
+ if (id === "download" && c.download === false) return false;
765
+ if (id === "print" && c.print === false) return false;
766
+ if ((id === "open-window" || id === "external-window") && (c.openWindow === false || c.openSeparateWindow === false || c["open-window"] === false)) {
767
+ return false;
768
+ }
769
+ if (id === "copy" && c.copy === false) return false;
770
+ if (id === "thumbnails" && c.thumbnails === false) return false;
771
+ if (id === "page-nav" && (c.pageNav === false || c.pagination === false || c["page-nav"] === false)) {
772
+ return false;
773
+ }
774
+ if ((id === "prev" || id === "page-prev") && (c.prevPage === false || c.prev === false || c["prev"] === false || c.pageNav === false || c.pagination === false)) {
775
+ return false;
776
+ }
777
+ if ((id === "next" || id === "page-next") && (c.nextPage === false || c.next === false || c["next"] === false || c.pageNav === false || c.pagination === false)) {
778
+ return false;
779
+ }
780
+ if (id === "play" && c.play === false) return false;
781
+ if (id === "pause" && c.pause === false) return false;
782
+ if ((id === "fast-forward" || id === "forward-10") && (c.fastForward === false || c["fast-forward"] === false)) {
783
+ return false;
784
+ }
785
+ if ((id === "rewind" || id === "replay-10") && (c.rewind === false || c["rewind"] === false)) {
786
+ return false;
787
+ }
788
+ if (id === "speed" && c.speed === false) return false;
789
+ return true;
790
+ }
655
791
  setPage(page, max) {
656
792
  if (this.pageInputEl) {
657
793
  this.pageInputEl.value = page.toString();
794
+ if (max !== void 0) {
795
+ this.pageInputEl.max = max.toString();
796
+ }
658
797
  }
659
798
  if (max !== void 0 && this.pageLabelEl) {
660
799
  this.pageLabelEl.textContent = ` / ${max}`;
661
800
  }
801
+ const currentMax = max !== void 0 ? max : parseInt(this.pageInputEl?.max || "1", 10) || 1;
802
+ if (this.prevPageBtn) {
803
+ this.prevPageBtn.disabled = page <= 1;
804
+ this.prevPageBtn.style.opacity = page <= 1 ? "0.4" : "1";
805
+ }
806
+ if (this.nextPageBtn) {
807
+ this.nextPageBtn.disabled = page >= currentMax;
808
+ this.nextPageBtn.style.opacity = page >= currentMax ? "0.4" : "1";
809
+ }
662
810
  }
663
811
  update(actions) {
664
812
  this.actions = actions;
@@ -681,8 +829,9 @@ var ToolbarController = class {
681
829
  view: [],
682
830
  actions: []
683
831
  };
684
- const hasPageNav = this.actions.some((a) => a.type === "page-nav");
685
- const effectiveActions = hasPageNav ? this.actions.filter((a) => a.id !== "page-prev" && a.id !== "page-next" && a.id !== "prev" && a.id !== "next") : this.actions;
832
+ const enabledActions = this.actions.filter((a) => this.isActionEnabled(a));
833
+ const hasPageNav = enabledActions.some((a) => a.type === "page-nav");
834
+ const effectiveActions = hasPageNav ? enabledActions.filter((a) => a.id !== "page-prev" && a.id !== "page-next" && a.id !== "prev" && a.id !== "next") : enabledActions;
686
835
  for (const action of effectiveActions) {
687
836
  if (groups[action.group]) {
688
837
  groups[action.group].push(action);
@@ -742,6 +891,13 @@ var ToolbarController = class {
742
891
  action.execute("go", val);
743
892
  });
744
893
  const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${max}`);
894
+ const curVal = action.value ?? 1;
895
+ prevBtn.disabled = curVal <= 1;
896
+ prevBtn.style.opacity = curVal <= 1 ? "0.4" : "1";
897
+ nextBtn.disabled = curVal >= max;
898
+ nextBtn.style.opacity = curVal >= max ? "0.4" : "1";
899
+ this.prevPageBtn = prevBtn;
900
+ this.nextPageBtn = nextBtn;
745
901
  this.pageInputEl = input;
746
902
  this.pageLabelEl = label;
747
903
  groupEl.appendChild(prevBtn);
@@ -797,25 +953,63 @@ var ToolbarController = class {
797
953
  var ThumbnailPanel = class {
798
954
  el;
799
955
  panelEl;
956
+ listEl;
957
+ headerEl;
800
958
  thumbnails = [];
801
959
  onSelect;
960
+ onToggleCallback;
802
961
  activeIndex = 0;
803
- constructor(container) {
962
+ observer;
963
+ renderedIndices = /* @__PURE__ */ new Set();
964
+ constructor(container, onToggle) {
804
965
  this.el = container;
966
+ this.onToggleCallback = onToggle;
805
967
  this.panelEl = createElement("div", { className: "fp-thumbnail-panel hidden" });
968
+ this.panelEl.style.width = "0px";
969
+ this.panelEl.style.minWidth = "0px";
970
+ this.panelEl.style.opacity = "0";
971
+ this.headerEl = createElement("div", { className: "fp-thumbnail-header" });
972
+ const title = createElement("span", { className: "fp-thumbnail-title" }, "Pages");
973
+ const closeBtn = createElement("button", {
974
+ className: "fp-thumbnail-close-btn",
975
+ title: "Close Thumbnails",
976
+ type: "button"
977
+ });
978
+ closeBtn.innerHTML = ICON_CLOSE;
979
+ closeBtn.addEventListener("click", (e) => {
980
+ e.stopPropagation();
981
+ this.hide();
982
+ });
983
+ this.headerEl.appendChild(title);
984
+ this.headerEl.appendChild(closeBtn);
985
+ this.listEl = createElement("div", { className: "fp-thumbnail-list" });
986
+ this.panelEl.appendChild(this.headerEl);
987
+ this.panelEl.appendChild(this.listEl);
806
988
  this.el.appendChild(this.panelEl);
807
989
  }
990
+ isOpen() {
991
+ return !this.panelEl.classList.contains("hidden") && this.panelEl.style.width !== "0px";
992
+ }
808
993
  update(thumbnails, onSelect) {
809
994
  this.thumbnails = thumbnails;
810
995
  this.onSelect = onSelect;
996
+ this.renderedIndices.clear();
997
+ const titleEl = this.headerEl.querySelector(".fp-thumbnail-title");
998
+ if (titleEl) {
999
+ titleEl.textContent = `Pages (${thumbnails.length})`;
1000
+ }
811
1001
  this.render();
1002
+ if (this.isOpen()) {
1003
+ this.renderAllVisible();
1004
+ }
812
1005
  }
813
1006
  setActive(index) {
814
1007
  this.activeIndex = index;
815
- const items = this.panelEl.querySelectorAll(".fp-thumbnail-item");
1008
+ const items = this.listEl.querySelectorAll(".fp-thumbnail-item");
816
1009
  items.forEach((item, i) => {
817
1010
  if (i === index) {
818
1011
  item.classList.add("active");
1012
+ item.scrollIntoView({ block: "nearest", behavior: "smooth" });
819
1013
  } else {
820
1014
  item.classList.remove("active");
821
1015
  }
@@ -823,37 +1017,102 @@ var ThumbnailPanel = class {
823
1017
  }
824
1018
  show() {
825
1019
  this.panelEl.classList.remove("hidden");
1020
+ this.panelEl.style.width = "200px";
1021
+ this.panelEl.style.minWidth = "200px";
1022
+ this.panelEl.style.opacity = "1";
1023
+ this.panelEl.style.display = "flex";
1024
+ this.onToggleCallback?.(true);
1025
+ this.renderAllVisible();
826
1026
  }
827
1027
  hide() {
828
1028
  this.panelEl.classList.add("hidden");
1029
+ this.panelEl.style.width = "0px";
1030
+ this.panelEl.style.minWidth = "0px";
1031
+ this.panelEl.style.opacity = "0";
1032
+ this.onToggleCallback?.(false);
829
1033
  }
830
1034
  toggle() {
831
- this.panelEl.classList.toggle("hidden");
1035
+ if (this.isOpen()) {
1036
+ this.hide();
1037
+ } else {
1038
+ this.show();
1039
+ }
832
1040
  }
833
1041
  destroy() {
1042
+ if (this.observer) {
1043
+ this.observer.disconnect();
1044
+ this.observer = void 0;
1045
+ }
1046
+ this.renderedIndices.clear();
834
1047
  this.el.innerHTML = "";
835
1048
  }
836
- async render() {
837
- this.panelEl.innerHTML = "";
1049
+ renderAllVisible() {
1050
+ const items = this.listEl.querySelectorAll(".fp-thumbnail-item");
1051
+ items.forEach((item) => {
1052
+ const idx = parseInt(item.dataset.thumbIndex || "-1", 10);
1053
+ if (idx >= 0 && !this.renderedIndices.has(idx)) {
1054
+ this.renderThumbnailItem(idx, item);
1055
+ }
1056
+ });
1057
+ }
1058
+ renderThumbnailItem(idx, itemEl) {
1059
+ if (this.renderedIndices.has(idx)) return;
1060
+ this.renderedIndices.add(idx);
1061
+ const canvas = itemEl.querySelector("canvas");
1062
+ const thumb = this.thumbnails[idx];
1063
+ if (canvas && thumb) {
1064
+ Promise.resolve(thumb.render(canvas)).catch((err) => {
1065
+ console.warn(`[FilePreview] Error rendering thumbnail ${idx}:`, err);
1066
+ });
1067
+ }
1068
+ }
1069
+ render() {
1070
+ if (this.observer) {
1071
+ this.observer.disconnect();
1072
+ }
1073
+ this.listEl.innerHTML = "";
1074
+ const useObserver = typeof IntersectionObserver !== "undefined";
1075
+ if (useObserver) {
1076
+ this.observer = new IntersectionObserver(
1077
+ (entries) => {
1078
+ for (const entry of entries) {
1079
+ if (entry.isIntersecting) {
1080
+ const target = entry.target;
1081
+ const idx = parseInt(target.dataset.thumbIndex || "-1", 10);
1082
+ if (idx >= 0) {
1083
+ this.renderThumbnailItem(idx, target);
1084
+ }
1085
+ }
1086
+ }
1087
+ },
1088
+ {
1089
+ root: this.listEl,
1090
+ rootMargin: "120px 0px 120px 0px"
1091
+ }
1092
+ );
1093
+ }
838
1094
  for (let i = 0; i < this.thumbnails.length; i++) {
839
1095
  const thumb = this.thumbnails[i];
840
- const itemEl = createElement("div", { className: "fp-thumbnail-item" });
841
- if (i === this.activeIndex) {
842
- itemEl.classList.add("active");
843
- }
1096
+ const itemEl = createElement("div", {
1097
+ className: "fp-thumbnail-item" + (i === this.activeIndex ? " active" : "")
1098
+ });
1099
+ itemEl.dataset.thumbIndex = i.toString();
844
1100
  itemEl.addEventListener("click", () => {
845
1101
  this.setActive(i);
846
1102
  this.onSelect?.(i);
847
1103
  });
848
- const canvas = createElement("canvas", { width: "150", height: "200" });
849
- const label = createElement("span", { className: "fp-thumbnail-label" }, thumb.label);
850
- itemEl.appendChild(canvas);
1104
+ const canvasWrapper = createElement("div", { className: "fp-thumbnail-canvas-wrapper" });
1105
+ const canvas = createElement("canvas", { width: "130", height: "170" });
1106
+ canvasWrapper.appendChild(canvas);
1107
+ const label = createElement("span", { className: "fp-thumbnail-label" }, thumb.label || `Page ${i + 1}`);
1108
+ itemEl.appendChild(canvasWrapper);
851
1109
  itemEl.appendChild(label);
852
- this.panelEl.appendChild(itemEl);
853
- try {
854
- await thumb.render(canvas);
855
- } catch (err) {
856
- console.error(`[FilePreview] Error rendering thumbnail ${i}:`, err);
1110
+ this.listEl.appendChild(itemEl);
1111
+ if (this.observer) {
1112
+ this.observer.observe(itemEl);
1113
+ }
1114
+ if (i < 4) {
1115
+ this.renderThumbnailItem(i, itemEl);
857
1116
  }
858
1117
  }
859
1118
  }
@@ -874,6 +1133,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
874
1133
  currentOptions = {};
875
1134
  resizeObserver = null;
876
1135
  fullscreenHandler = null;
1136
+ titleBarEl = null;
877
1137
  /**
878
1138
  * Register a preview plugin.
879
1139
  */
@@ -910,6 +1170,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
910
1170
  }
911
1171
  this.currentBuffer = buffer.slice(0);
912
1172
  this.currentMetadata = metadata;
1173
+ this.updateTitleBar(options, metadata);
913
1174
  if (signal.aborted) throw new DOMException("Aborted", "AbortError");
914
1175
  const fileInfo = { metadata, buffer };
915
1176
  const matchedPlugin = await this.findPlugin(fileInfo);
@@ -933,15 +1194,21 @@ var FilePreviewViewer = class _FilePreviewViewer {
933
1194
  if (event === "page-change" && payload && typeof payload.page === "number") {
934
1195
  const total = payload.total ?? payload.totalPages;
935
1196
  this.toolbar?.setPage(payload.page, total);
1197
+ this.thumbnailPanel?.setActive(payload.page - 1);
936
1198
  }
937
1199
  this.eventEmitter.emit(event, payload);
938
- }
1200
+ },
1201
+ toggleThumbnails: () => this.toggleThumbnails()
939
1202
  });
940
1203
  this.activeInstance = instance;
941
1204
  instance.openInSeparateWindow = () => this.openInSeparateWindow();
1205
+ instance.toggleThumbnails = () => this.toggleThumbnails();
942
1206
  this.hideLoading();
943
1207
  this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
944
- if (options.showToolbar !== false && this.toolbar) {
1208
+ const isToolbarVisible = options.showToolbar !== false && options.toolbar !== false;
1209
+ if (isToolbarVisible && this.toolbar) {
1210
+ const toolbarConfig = this.extractToolbarConfig(options);
1211
+ this.toolbar.setConfig(toolbarConfig);
945
1212
  const actions = matchedPlugin.getToolbarActions(instance);
946
1213
  const hasFullscreen = actions.some((a) => a.id === "fullscreen");
947
1214
  if (!hasFullscreen && this.wrapperEl) {
@@ -991,6 +1258,12 @@ var FilePreviewViewer = class _FilePreviewViewer {
991
1258
  }
992
1259
  });
993
1260
  }
1261
+ const thumbAction = actions.find((a) => a.id === "thumbnails");
1262
+ if (thumbAction) {
1263
+ thumbAction.execute = () => {
1264
+ this.toggleThumbnails();
1265
+ };
1266
+ }
994
1267
  this.toolbar.update(actions);
995
1268
  this.toolbar.show();
996
1269
  }
@@ -1075,7 +1348,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1075
1348
  if (targetUrl) {
1076
1349
  const newWin2 = window.open(targetUrl, "_blank");
1077
1350
  if (!newWin2) {
1078
- alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
1351
+ console.warn("[FilePreviewViewer] Popup blocker prevented opening the preview in a separate window.");
1079
1352
  return null;
1080
1353
  }
1081
1354
  return newWin2;
@@ -1083,7 +1356,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1083
1356
  const title = (this.currentMetadata?.name || "Document Preview") + " - Full Preview";
1084
1357
  const newWin = window.open("", "_blank");
1085
1358
  if (!newWin) {
1086
- alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
1359
+ console.warn("[FilePreviewViewer] Popup blocker prevented opening the preview in a separate window.");
1087
1360
  return null;
1088
1361
  }
1089
1362
  newWin.document.title = title;
@@ -1160,6 +1433,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1160
1433
  this.currentBuffer = null;
1161
1434
  this.currentMetadata = null;
1162
1435
  this.currentContainer = null;
1436
+ this.titleBarEl = null;
1163
1437
  }
1164
1438
  /**
1165
1439
  * Get the currently active preview instance.
@@ -1167,7 +1441,218 @@ var FilePreviewViewer = class _FilePreviewViewer {
1167
1441
  getInstance() {
1168
1442
  return this.activeInstance;
1169
1443
  }
1444
+ // --- Toolbar Configuration & Feature Toggles ---
1445
+ /**
1446
+ * Configure toolbar features dynamically through options or methods.
1447
+ * e.g. viewer.setToolbarConfig({ zoomIn: false, print: false })
1448
+ */
1449
+ setToolbarConfig(config) {
1450
+ this.toolbar?.setConfig(config);
1451
+ }
1452
+ /**
1453
+ * Get current toolbar configuration.
1454
+ */
1455
+ getToolbarConfig() {
1456
+ return this.toolbar?.getConfig() ?? {};
1457
+ }
1458
+ /**
1459
+ * Hide a specific toolbar action by ID or alias (e.g. 'zoom-in', 'print', 'download').
1460
+ */
1461
+ hideToolbarAction(actionId) {
1462
+ this.toolbar?.hideAction(actionId);
1463
+ }
1464
+ /**
1465
+ * Show a specific toolbar action by ID or alias.
1466
+ */
1467
+ showToolbarAction(actionId) {
1468
+ this.toolbar?.showAction(actionId);
1469
+ }
1470
+ /**
1471
+ * Enable a specific toolbar action button.
1472
+ */
1473
+ enableToolbarAction(actionId) {
1474
+ this.toolbar?.enableAction(actionId);
1475
+ }
1476
+ /**
1477
+ * Disable a specific toolbar action button.
1478
+ */
1479
+ disableToolbarAction(actionId) {
1480
+ this.toolbar?.disableAction(actionId);
1481
+ }
1482
+ // --- Programmatic Toolbar Action Methods ---
1483
+ /**
1484
+ * Fit document to page so it fills the frame width with minimal margins.
1485
+ */
1486
+ fitToPage() {
1487
+ this.activeInstance?.fitToPage?.();
1488
+ }
1489
+ /**
1490
+ * Zoom in.
1491
+ */
1492
+ zoomIn() {
1493
+ this.activeInstance?.zoomIn?.();
1494
+ }
1495
+ /**
1496
+ * Zoom out.
1497
+ */
1498
+ zoomOut() {
1499
+ this.activeInstance?.zoomOut?.();
1500
+ }
1501
+ /**
1502
+ * Set specific zoom level.
1503
+ */
1504
+ setZoom(level) {
1505
+ this.activeInstance?.setZoom?.(level);
1506
+ }
1507
+ /**
1508
+ * Get current zoom level.
1509
+ */
1510
+ getZoom() {
1511
+ return this.activeInstance?.getZoom?.() ?? 1;
1512
+ }
1513
+ /**
1514
+ * Rotate 90 degrees clockwise.
1515
+ */
1516
+ rotateCW() {
1517
+ this.activeInstance?.rotateCW?.();
1518
+ }
1519
+ /**
1520
+ * Rotate 90 degrees counter-clockwise.
1521
+ */
1522
+ rotateCCW() {
1523
+ this.activeInstance?.rotateCCW?.();
1524
+ }
1525
+ /**
1526
+ * Navigate to a specific page number.
1527
+ */
1528
+ goToPage(page) {
1529
+ this.activeInstance?.goToPage?.(page);
1530
+ this.toolbar?.setPage(page);
1531
+ }
1532
+ /**
1533
+ * Navigate to next page.
1534
+ */
1535
+ nextPage() {
1536
+ const cur = this.getCurrentPage();
1537
+ const max = this.getPageCount();
1538
+ if (cur < max) {
1539
+ this.goToPage(cur + 1);
1540
+ }
1541
+ }
1542
+ /**
1543
+ * Navigate to previous page.
1544
+ */
1545
+ prevPage() {
1546
+ const cur = this.getCurrentPage();
1547
+ if (cur > 1) {
1548
+ this.goToPage(cur - 1);
1549
+ }
1550
+ }
1551
+ /**
1552
+ * Get total page count.
1553
+ */
1554
+ getPageCount() {
1555
+ return this.activeInstance?.getPageCount?.() ?? 1;
1556
+ }
1557
+ /**
1558
+ * Get current page number.
1559
+ */
1560
+ getCurrentPage() {
1561
+ return this.activeInstance?.getCurrentPage?.() ?? 1;
1562
+ }
1563
+ /**
1564
+ * Download the current document.
1565
+ */
1566
+ download() {
1567
+ this.activeInstance?.download?.();
1568
+ }
1569
+ /**
1570
+ * Print the current document.
1571
+ */
1572
+ print() {
1573
+ this.activeInstance?.print?.();
1574
+ }
1575
+ /**
1576
+ * Toggle fullscreen mode.
1577
+ */
1578
+ toggleFullscreen() {
1579
+ try {
1580
+ if (!document.fullscreenElement) {
1581
+ this.wrapperEl?.requestFullscreen?.();
1582
+ this.wrapperEl?.classList.add("fp-fullscreen-active");
1583
+ } else {
1584
+ document.exitFullscreen?.();
1585
+ this.wrapperEl?.classList.remove("fp-fullscreen-active");
1586
+ }
1587
+ } catch {
1588
+ this.wrapperEl?.classList.toggle("fp-fullscreen-active");
1589
+ }
1590
+ setTimeout(() => {
1591
+ this.activeInstance?.fitToPage?.();
1592
+ }, 120);
1593
+ }
1594
+ /**
1595
+ * Toggle thumbnails sidebar panel.
1596
+ */
1597
+ toggleThumbnails() {
1598
+ if (!this.thumbnailPanel) return;
1599
+ this.thumbnailPanel.toggle();
1600
+ const isOpen = this.thumbnailPanel.isOpen();
1601
+ this.toolbar?.setActionActive("thumbnails", isOpen);
1602
+ setTimeout(() => {
1603
+ this.activeInstance?.fitToPage?.();
1604
+ }, 260);
1605
+ }
1606
+ /**
1607
+ * Set whether to display the file name / title bar above the toolbar.
1608
+ */
1609
+ setShowFileName(show) {
1610
+ if (this.currentOptions) {
1611
+ this.currentOptions.showFileName = show;
1612
+ }
1613
+ this.updateTitleBar(this.currentOptions, this.currentMetadata);
1614
+ }
1615
+ /**
1616
+ * Set or override the displayed file name in the preview panel.
1617
+ */
1618
+ setFileName(name) {
1619
+ if (this.currentOptions) {
1620
+ this.currentOptions.fileName = name;
1621
+ }
1622
+ this.updateTitleBar(this.currentOptions, this.currentMetadata);
1623
+ }
1170
1624
  // --- Private methods ---
1625
+ updateTitleBar(options, metadata) {
1626
+ if (!this.titleBarEl) return;
1627
+ const show = options?.showFileName !== false;
1628
+ if (!show) {
1629
+ this.titleBarEl.style.display = "none";
1630
+ return;
1631
+ }
1632
+ const name = options?.fileName || metadata?.name || this.currentMetadata?.name || "";
1633
+ if (!name) {
1634
+ this.titleBarEl.style.display = "none";
1635
+ return;
1636
+ }
1637
+ this.titleBarEl.style.display = "flex";
1638
+ const ext = metadata?.extension || (name.includes(".") ? "." + name.split(".").pop() : "");
1639
+ const badge = ext ? ext.replace(".", "").toUpperCase() : "";
1640
+ this.titleBarEl.innerHTML = `
1641
+ <div class="fp-titlebar-info">
1642
+ <span class="fp-titlebar-icon">\u{1F4C4}</span>
1643
+ <span class="fp-titlebar-name" title="${name}">${name}</span>
1644
+ ${badge ? `<span class="fp-titlebar-badge">${badge}</span>` : ""}
1645
+ </div>
1646
+ `;
1647
+ }
1648
+ extractToolbarConfig(options) {
1649
+ if (!options) return {};
1650
+ const nested = typeof options.toolbar === "object" ? options.toolbar : {};
1651
+ return {
1652
+ ...options,
1653
+ ...nested
1654
+ };
1655
+ }
1171
1656
  abort() {
1172
1657
  if (this.abortController) {
1173
1658
  this.abortController.abort();
@@ -1210,17 +1695,29 @@ var FilePreviewViewer = class _FilePreviewViewer {
1210
1695
  bodyEl.className = "fp-body";
1211
1696
  bodyEl.appendChild(thumbnailEl);
1212
1697
  bodyEl.appendChild(this.contentEl);
1698
+ const titleBarEl = document.createElement("div");
1699
+ titleBarEl.className = "fp-titlebar";
1700
+ this.titleBarEl = titleBarEl;
1701
+ this.updateTitleBar(options, this.currentMetadata);
1213
1702
  if (toolbarPos === "top") {
1703
+ this.wrapperEl.appendChild(titleBarEl);
1214
1704
  this.wrapperEl.appendChild(toolbarEl);
1215
1705
  this.wrapperEl.appendChild(bodyEl);
1216
1706
  } else {
1707
+ this.wrapperEl.appendChild(titleBarEl);
1217
1708
  this.wrapperEl.appendChild(bodyEl);
1218
1709
  this.wrapperEl.appendChild(toolbarEl);
1219
1710
  }
1220
1711
  container.innerHTML = "";
1221
1712
  container.appendChild(this.wrapperEl);
1222
- this.toolbar = new ToolbarController(toolbarEl);
1223
- this.thumbnailPanel = new ThumbnailPanel(thumbnailEl);
1713
+ const initialToolbarConfig = this.extractToolbarConfig(options);
1714
+ this.toolbar = new ToolbarController(toolbarEl, initialToolbarConfig);
1715
+ this.thumbnailPanel = new ThumbnailPanel(thumbnailEl, (isOpen) => {
1716
+ this.toolbar?.setActionActive("thumbnails", isOpen);
1717
+ setTimeout(() => {
1718
+ this.activeInstance?.fitToPage?.();
1719
+ }, 260);
1720
+ });
1224
1721
  this.setupKeyboardShortcuts();
1225
1722
  this.setupDragAndDrop(container, options);
1226
1723
  this.setupResizeAndFullscreenListeners();
@@ -1610,6 +2107,14 @@ var PdfPlugin = class {
1610
2107
  group: "zoom",
1611
2108
  execute: () => instance.fitToPage?.()
1612
2109
  },
2110
+ {
2111
+ id: "fit-width",
2112
+ icon: "fit-width",
2113
+ label: "Fit to Width",
2114
+ type: "button",
2115
+ group: "zoom",
2116
+ execute: () => instance.fitToWidth?.()
2117
+ },
1613
2118
  {
1614
2119
  id: "rotate-cw",
1615
2120
  icon: "rotate-cw",
@@ -1618,6 +2123,14 @@ var PdfPlugin = class {
1618
2123
  group: "view",
1619
2124
  execute: () => instance.rotateCW?.()
1620
2125
  },
2126
+ {
2127
+ id: "rotate-ccw",
2128
+ icon: "rotate-ccw",
2129
+ label: "Rotate Counter-Clockwise",
2130
+ type: "button",
2131
+ group: "view",
2132
+ execute: () => instance.rotateCCW?.()
2133
+ },
1621
2134
  {
1622
2135
  id: "download",
1623
2136
  icon: "download",
@@ -1649,12 +2162,13 @@ var PdfPlugin = class {
1649
2162
  container.className = "fp-pdf-container";
1650
2163
  container.style.width = "100%";
1651
2164
  container.style.height = "100%";
1652
- container.style.overflow = "auto";
2165
+ container.style.overflowX = "hidden";
2166
+ container.style.overflowY = "auto";
1653
2167
  container.style.display = "flex";
1654
2168
  container.style.flexDirection = "column";
1655
2169
  container.style.alignItems = "center";
1656
2170
  container.style.justifyContent = "flex-start";
1657
- container.style.padding = "20px 16px";
2171
+ container.style.padding = "16px 8px";
1658
2172
  container.style.backgroundColor = "#0f172a";
1659
2173
  container.style.boxSizing = "border-box";
1660
2174
  container.style.position = "relative";
@@ -1665,30 +2179,12 @@ var PdfPlugin = class {
1665
2179
  pageCard.style.borderRadius = "4px";
1666
2180
  pageCard.style.overflow = "hidden";
1667
2181
  pageCard.style.lineHeight = "0";
1668
- pageCard.style.transition = "transform 0.15s ease";
1669
2182
  pageCard.style.position = "relative";
1670
2183
  pageCard.style.flexShrink = "0";
2184
+ pageCard.style.transition = "box-shadow 0.2s ease";
1671
2185
  let canvas = document.createElement("canvas");
1672
2186
  pageCard.appendChild(canvas);
1673
2187
  container.appendChild(pageCard);
1674
- const indicator = document.createElement("div");
1675
- indicator.className = "fp-pdf-page-indicator";
1676
- indicator.style.position = "sticky";
1677
- indicator.style.bottom = "16px";
1678
- indicator.style.marginTop = "16px";
1679
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
1680
- indicator.style.backdropFilter = "blur(8px)";
1681
- indicator.style.color = "#f8fafc";
1682
- indicator.style.fontSize = "12px";
1683
- indicator.style.fontWeight = "600";
1684
- indicator.style.padding = "5px 14px";
1685
- indicator.style.borderRadius = "20px";
1686
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
1687
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
1688
- indicator.style.zIndex = "10";
1689
- indicator.style.userSelect = "none";
1690
- indicator.style.pointerEvents = "none";
1691
- container.appendChild(indicator);
1692
2188
  ctx.container.appendChild(container);
1693
2189
  const standardFontsUrl = typeof window !== "undefined" && window.__PDF_STANDARD_FONTS_URL__ || "./standard_fonts/";
1694
2190
  const cmapsUrl = typeof window !== "undefined" && window.__PDF_CMAPS_URL__ || "./cmaps/";
@@ -1704,8 +2200,9 @@ var PdfPlugin = class {
1704
2200
  let currentPage = 1;
1705
2201
  let zoomScale = 1;
1706
2202
  let rotation = 0;
1707
- let fitMode = "page";
2203
+ let fitMode = ctx?.options?.fitMode || "width";
1708
2204
  let currentRenderTask = null;
2205
+ let textLayerDiv = null;
1709
2206
  const renderPage = async (pageNum) => {
1710
2207
  if (currentRenderTask) {
1711
2208
  try {
@@ -1717,30 +2214,37 @@ var PdfPlugin = class {
1717
2214
  const newCanvas = document.createElement("canvas");
1718
2215
  pageCard.replaceChild(newCanvas, canvas);
1719
2216
  canvas = newCanvas;
2217
+ if (textLayerDiv) {
2218
+ textLayerDiv.remove();
2219
+ textLayerDiv = null;
2220
+ }
1720
2221
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
1721
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
1722
2222
  ctx.emit("page-change", { page: currentPage, total: totalPages });
1723
2223
  const page = await pdfDoc.getPage(currentPage);
1724
2224
  const containerWidth = container.clientWidth || 900;
1725
2225
  const containerHeight = container.clientHeight || 700;
1726
2226
  const unscaledVp = page.getViewport({ scale: 1, rotation });
1727
- const availWidth = Math.max(320, containerWidth - 48);
1728
- const availHeight = Math.max(550, containerHeight - 88);
2227
+ const availWidth = Math.max(280, containerWidth - 36);
2228
+ const availHeight = Math.max(280, containerHeight - 32);
1729
2229
  const scaleW = availWidth / unscaledVp.width;
1730
2230
  const scaleH = availHeight / unscaledVp.height;
1731
2231
  let fitScale;
1732
2232
  if (fitMode === "page") {
1733
- fitScale = Math.max(0.4, Math.min(scaleW, scaleH));
2233
+ fitScale = Math.max(0.2, Math.min(4, Math.min(scaleW, scaleH)));
1734
2234
  } else {
1735
- fitScale = Math.max(0.65, Math.min(1.25, scaleW));
2235
+ fitScale = Math.max(0.2, Math.min(4, scaleW));
1736
2236
  }
1737
2237
  const effectiveScale = (fitScale > 0 ? fitScale : 1) * zoomScale;
1738
2238
  const pixelRatio = window.devicePixelRatio || 1;
1739
2239
  const viewport = page.getViewport({ scale: effectiveScale, rotation });
2240
+ const displayWidth = Math.floor(viewport.width);
2241
+ const displayHeight = Math.floor(viewport.height);
1740
2242
  canvas.width = Math.floor(viewport.width * pixelRatio);
1741
2243
  canvas.height = Math.floor(viewport.height * pixelRatio);
1742
- canvas.style.width = `${Math.floor(viewport.width)}px`;
1743
- canvas.style.height = `${Math.floor(viewport.height)}px`;
2244
+ canvas.style.width = `${displayWidth}px`;
2245
+ canvas.style.height = `${displayHeight}px`;
2246
+ pageCard.style.width = `${displayWidth}px`;
2247
+ pageCard.style.height = `${displayHeight}px`;
1744
2248
  const canvasCtx = canvas.getContext("2d");
1745
2249
  if (!canvasCtx) return;
1746
2250
  canvasCtx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
@@ -1754,9 +2258,31 @@ var PdfPlugin = class {
1754
2258
  if (err?.name !== "RenderingCancelledException") {
1755
2259
  console.warn("[PdfPlugin] Page render warning:", err);
1756
2260
  }
2261
+ return;
1757
2262
  } finally {
1758
2263
  currentRenderTask = null;
1759
2264
  }
2265
+ try {
2266
+ textLayerDiv = document.createElement("div");
2267
+ textLayerDiv.className = "textLayer";
2268
+ textLayerDiv.style.width = `${displayWidth}px`;
2269
+ textLayerDiv.style.height = `${displayHeight}px`;
2270
+ textLayerDiv.style.position = "absolute";
2271
+ textLayerDiv.style.top = "0";
2272
+ textLayerDiv.style.left = "0";
2273
+ textLayerDiv.style.lineHeight = "1";
2274
+ pageCard.appendChild(textLayerDiv);
2275
+ if (pdfjsLib__namespace.TextLayer) {
2276
+ const textLayer = new pdfjsLib__namespace.TextLayer({
2277
+ textContentSource: page.streamTextContent(),
2278
+ container: textLayerDiv,
2279
+ viewport
2280
+ });
2281
+ await textLayer.render();
2282
+ }
2283
+ } catch (err) {
2284
+ console.debug("[PdfPlugin] TextLayer notice:", err);
2285
+ }
1760
2286
  };
1761
2287
  renderPage(1);
1762
2288
  let resizeTimer = null;
@@ -1766,11 +2292,48 @@ var PdfPlugin = class {
1766
2292
  if (Math.abs(zoomScale - 1) < 0.05) {
1767
2293
  renderPage(currentPage);
1768
2294
  }
1769
- }, 150);
2295
+ }, 120);
1770
2296
  });
1771
2297
  resizeObserver.observe(container);
2298
+ const onWheel = (e) => {
2299
+ if (e.ctrlKey || e.metaKey) {
2300
+ e.preventDefault();
2301
+ const delta = e.deltaY > 0 ? -0.15 : 0.15;
2302
+ zoomScale = Math.max(0.25, Math.min(4, Math.round((zoomScale + delta) * 100) / 100));
2303
+ renderPage(currentPage);
2304
+ }
2305
+ };
2306
+ container.addEventListener("wheel", onWheel, { passive: false });
2307
+ const onKeyDown = (e) => {
2308
+ const tag = e.target?.tagName?.toLowerCase();
2309
+ if (tag === "input" || tag === "textarea" || tag === "select") return;
2310
+ if (e.key === "ArrowRight" || e.key === "PageDown") {
2311
+ if (currentPage < totalPages) {
2312
+ e.preventDefault();
2313
+ container.scrollTop = 0;
2314
+ renderPage(currentPage + 1);
2315
+ }
2316
+ } else if (e.key === "ArrowLeft" || e.key === "PageUp") {
2317
+ if (currentPage > 1) {
2318
+ e.preventDefault();
2319
+ container.scrollTop = 0;
2320
+ renderPage(currentPage - 1);
2321
+ }
2322
+ } else if (e.key === "Home") {
2323
+ e.preventDefault();
2324
+ container.scrollTop = 0;
2325
+ renderPage(1);
2326
+ } else if (e.key === "End") {
2327
+ e.preventDefault();
2328
+ container.scrollTop = 0;
2329
+ renderPage(totalPages);
2330
+ }
2331
+ };
2332
+ window.addEventListener("keydown", onKeyDown);
1772
2333
  const cleanup = () => {
1773
2334
  resizeObserver.disconnect();
2335
+ window.removeEventListener("keydown", onKeyDown);
2336
+ container.removeEventListener("wheel", onWheel);
1774
2337
  if (resizeTimer) clearTimeout(resizeTimer);
1775
2338
  if (currentRenderTask) {
1776
2339
  try {
@@ -1789,22 +2352,26 @@ var PdfPlugin = class {
1789
2352
  const instance = {
1790
2353
  destroy: cleanup,
1791
2354
  zoomIn: () => {
1792
- zoomScale = Math.min(3.5, zoomScale + 0.2);
2355
+ zoomScale = Math.min(4, Math.round((zoomScale + 0.2) * 10) / 10);
1793
2356
  renderPage(currentPage);
1794
2357
  },
1795
2358
  zoomOut: () => {
1796
- zoomScale = Math.max(0.3, zoomScale - 0.2);
2359
+ zoomScale = Math.max(0.25, Math.round((zoomScale - 0.2) * 10) / 10);
1797
2360
  renderPage(currentPage);
1798
2361
  },
1799
2362
  getZoom: () => zoomScale,
1800
2363
  setZoom: (level) => {
1801
- zoomScale = Math.max(0.3, Math.min(3.5, level));
2364
+ zoomScale = Math.max(0.25, Math.min(4, level));
1802
2365
  renderPage(currentPage);
1803
2366
  },
1804
2367
  fitToPage: () => {
1805
- fitMode = fitMode === "page" ? "width" : "page";
2368
+ fitMode = "page";
2369
+ zoomScale = 1;
2370
+ renderPage(currentPage);
2371
+ },
2372
+ fitToWidth: () => {
2373
+ fitMode = "width";
1806
2374
  zoomScale = 1;
1807
- rotation = 0;
1808
2375
  renderPage(currentPage);
1809
2376
  },
1810
2377
  rotateCW: () => {
@@ -1822,6 +2389,9 @@ var PdfPlugin = class {
1822
2389
  container.scrollTop = 0;
1823
2390
  renderPage(page);
1824
2391
  },
2392
+ toggleThumbnails: () => {
2393
+ ctx?.toggleThumbnails?.();
2394
+ },
1825
2395
  download: () => {
1826
2396
  const blob = new Blob([ctx.buffer], { type: "application/pdf" });
1827
2397
  const url = URL.createObjectURL(blob);
@@ -1855,7 +2425,7 @@ var PdfPlugin = class {
1855
2425
  },
1856
2426
  getThumbnails: async () => {
1857
2427
  const thumbnails = [];
1858
- const count = Math.min(totalPages, 50);
2428
+ const count = totalPages;
1859
2429
  for (let i = 1; i <= count; i++) {
1860
2430
  thumbnails.push({
1861
2431
  index: i,
@@ -1863,9 +2433,10 @@ var PdfPlugin = class {
1863
2433
  render: async (thumbCanvas) => {
1864
2434
  try {
1865
2435
  const p = await pdfDoc.getPage(i);
1866
- const baseVp = p.getViewport({ scale: 1 });
1867
- const thumbScale = (thumbCanvas.width || 120) / baseVp.width;
1868
- const thumbVp = p.getViewport({ scale: thumbScale });
2436
+ const baseVp = p.getViewport({ scale: 1, rotation });
2437
+ const targetW = thumbCanvas.width || 130;
2438
+ const thumbScale = targetW / baseVp.width;
2439
+ const thumbVp = p.getViewport({ scale: thumbScale, rotation });
1869
2440
  thumbCanvas.height = Math.floor(thumbVp.height);
1870
2441
  const tCtx = thumbCanvas.getContext("2d");
1871
2442
  if (tCtx) {
@@ -2209,28 +2780,26 @@ var DocxPlugin = class {
2209
2780
  getToolbarActions(instance) {
2210
2781
  const totalPages = instance.getPageCount?.() ?? 1;
2211
2782
  const actions = [];
2212
- if (totalPages > 1) {
2213
- actions.push({
2214
- id: "page-nav",
2215
- icon: "",
2216
- label: "Page Navigation",
2217
- type: "page-nav",
2218
- group: "navigation",
2219
- value: instance.getCurrentPage?.() ?? 1,
2220
- max: totalPages,
2221
- execute: (action, page) => {
2222
- const cur = instance.getCurrentPage?.() ?? 1;
2223
- const max = instance.getPageCount?.() ?? 1;
2224
- if (action === "prev") {
2225
- if (cur > 1) instance.goToPage?.(cur - 1);
2226
- } else if (action === "next") {
2227
- if (cur < max) instance.goToPage?.(cur + 1);
2228
- } else if (typeof page === "number") {
2229
- instance.goToPage?.(page);
2230
- }
2783
+ actions.push({
2784
+ id: "page-nav",
2785
+ icon: "",
2786
+ label: "Page Navigation",
2787
+ type: "page-nav",
2788
+ group: "navigation",
2789
+ value: instance.getCurrentPage?.() ?? 1,
2790
+ max: totalPages,
2791
+ execute: (action, page) => {
2792
+ const cur = instance.getCurrentPage?.() ?? 1;
2793
+ const max = instance.getPageCount?.() ?? 1;
2794
+ if (action === "prev") {
2795
+ if (cur > 1) instance.goToPage?.(cur - 1);
2796
+ } else if (action === "next") {
2797
+ if (cur < max) instance.goToPage?.(cur + 1);
2798
+ } else if (typeof page === "number") {
2799
+ instance.goToPage?.(page);
2231
2800
  }
2232
- });
2233
- }
2801
+ }
2802
+ });
2234
2803
  actions.push(
2235
2804
  {
2236
2805
  id: "zoom-out",
@@ -2296,11 +2865,17 @@ var DocxPlugin = class {
2296
2865
  wrapper.className = "fp-docx-wrapper";
2297
2866
  wrapper.style.transformOrigin = "top center";
2298
2867
  wrapper.style.transition = "transform 0.2s ease";
2299
- wrapper.style.padding = "24px 16px";
2300
- wrapper.style.maxWidth = "950px";
2868
+ wrapper.style.padding = "0";
2869
+ wrapper.style.maxWidth = "none";
2870
+ wrapper.style.width = "816px";
2301
2871
  wrapper.style.margin = "0 auto";
2872
+ wrapper.style.display = "flex";
2873
+ wrapper.style.flexDirection = "column";
2874
+ wrapper.style.alignItems = "center";
2302
2875
  wrapper.style.boxSizing = "border-box";
2303
- ctx.container.style.overflow = "auto";
2876
+ ctx.container.style.overflowX = "hidden";
2877
+ ctx.container.style.overflowY = "auto";
2878
+ ctx.container.style.padding = "16px 8px";
2304
2879
  ctx.container.style.backgroundColor = "#f1f5f9";
2305
2880
  ctx.container.appendChild(wrapper);
2306
2881
  const styleOverride = document.createElement("style");
@@ -2474,39 +3049,13 @@ var DocxPlugin = class {
2474
3049
  const pageElements = sections.length > 0 ? sections : cards;
2475
3050
  const totalPages = Math.max(1, pageElements.length);
2476
3051
  let currentPage = 1;
2477
- let indicator = null;
2478
- if (totalPages > 1) {
2479
- indicator = document.createElement("div");
2480
- indicator.className = "fp-docx-page-indicator";
2481
- indicator.style.position = "sticky";
2482
- indicator.style.bottom = "16px";
2483
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
2484
- indicator.style.backdropFilter = "blur(8px)";
2485
- indicator.style.color = "#f8fafc";
2486
- indicator.style.fontSize = "12px";
2487
- indicator.style.fontWeight = "600";
2488
- indicator.style.padding = "5px 14px";
2489
- indicator.style.borderRadius = "20px";
2490
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
2491
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
2492
- indicator.style.zIndex = "10";
2493
- indicator.style.userSelect = "none";
2494
- indicator.style.pointerEvents = "none";
2495
- indicator.style.textAlign = "center";
2496
- indicator.style.width = "fit-content";
2497
- indicator.style.margin = "16px auto 0";
2498
- ctx.container.appendChild(indicator);
2499
- }
2500
3052
  const showPage = (pageNum) => {
2501
3053
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
2502
3054
  if (pageElements.length > 1) {
2503
3055
  pageElements.forEach((sec, idx) => {
2504
- sec.style.display = idx + 1 === currentPage ? "block" : "none";
3056
+ sec.style.display = idx + 1 === currentPage ? "" : "none";
2505
3057
  });
2506
3058
  }
2507
- if (indicator) {
2508
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
2509
- }
2510
3059
  ctx.container.scrollTop = 0;
2511
3060
  ctx.emit("page-change", { page: currentPage, total: totalPages });
2512
3061
  };
@@ -2515,31 +3064,46 @@ var DocxPlugin = class {
2515
3064
  }
2516
3065
  scale = 1;
2517
3066
  let rotation = 0;
2518
- let fitMode = "width";
2519
- const calculateFitScale = (mode = fitMode) => {
2520
- const activeEl = pageElements[currentPage - 1] || wrapper.firstElementChild || wrapper;
3067
+ let fitMode = "page";
3068
+ let isUserZoomed = false;
3069
+ const calculateFitScale = (_mode = fitMode) => {
3070
+ const activeEl = pageElements[currentPage - 1] || wrapper.querySelector("section.docx") || wrapper;
2521
3071
  const elW = activeEl.offsetWidth || 816;
2522
- const singlePageH = Math.min(activeEl.offsetHeight || 1056, Math.round(elW * 1.32));
2523
- const availW = Math.max(280, ctx.container.clientWidth - 48);
2524
- const availH = Math.max(280, ctx.container.clientHeight - 80);
3072
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
2525
3073
  const sW = availW / elW;
2526
- const sH = availH / singlePageH;
2527
- if (mode === "page") {
2528
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
2529
- }
2530
- return Math.max(0.65, Math.min(1.05, sW));
3074
+ return Math.max(0.35, Math.min(3, sW));
2531
3075
  };
2532
3076
  const applyTransform = () => {
2533
3077
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
2534
3078
  wrapper.style.transformOrigin = "top center";
3079
+ const activeEl = pageElements[currentPage - 1] || wrapper.querySelector("section.docx") || wrapper;
3080
+ const elH = activeEl.offsetHeight || 1056;
3081
+ if (scale > 1) {
3082
+ wrapper.style.marginBottom = `${Math.round(elH * scale - elH + 32)}px`;
3083
+ } else {
3084
+ wrapper.style.marginBottom = "32px";
3085
+ }
2535
3086
  };
3087
+ let resizeObserver = null;
3088
+ if (typeof ResizeObserver !== "undefined") {
3089
+ resizeObserver = new ResizeObserver(() => {
3090
+ if (!isUserZoomed) {
3091
+ const newFit = calculateFitScale("page");
3092
+ if (Math.abs(scale - newFit) > 0.015) {
3093
+ scale = newFit;
3094
+ applyTransform();
3095
+ }
3096
+ }
3097
+ });
3098
+ resizeObserver.observe(ctx.container);
3099
+ }
2536
3100
  setTimeout(() => {
2537
- fitMode = "width";
2538
- scale = calculateFitScale("width");
3101
+ scale = calculateFitScale("page");
2539
3102
  applyTransform();
2540
- }, 60);
3103
+ }, 40);
2541
3104
  const cleanup = () => {
2542
- indicator?.remove();
3105
+ resizeObserver?.disconnect();
3106
+ resizeObserver = null;
2543
3107
  for (const url of createdBlobUrls) {
2544
3108
  URL.revokeObjectURL(url);
2545
3109
  }
@@ -2556,21 +3120,24 @@ var DocxPlugin = class {
2556
3120
  showPage(page);
2557
3121
  },
2558
3122
  zoomIn: () => {
2559
- scale += 0.15;
3123
+ isUserZoomed = true;
3124
+ scale = Math.min(3.5, scale + 0.15);
2560
3125
  applyTransform();
2561
3126
  },
2562
3127
  zoomOut: () => {
3128
+ isUserZoomed = true;
2563
3129
  scale = Math.max(0.2, scale - 0.15);
2564
3130
  applyTransform();
2565
3131
  },
2566
3132
  getZoom: () => scale,
2567
3133
  setZoom: (level) => {
3134
+ isUserZoomed = true;
2568
3135
  scale = level;
2569
3136
  applyTransform();
2570
3137
  },
2571
3138
  fitToPage: () => {
2572
- fitMode = fitMode === "width" ? "page" : "width";
2573
- scale = calculateFitScale(fitMode);
3139
+ isUserZoomed = false;
3140
+ scale = calculateFitScale("page");
2574
3141
  rotation = 0;
2575
3142
  applyTransform();
2576
3143
  },
@@ -2960,7 +3527,7 @@ var ExcelPlugin = class {
2960
3527
  "application/vnd.ms-excel.template.macroEnabled.12",
2961
3528
  "application/vnd.oasis.opendocument.spreadsheet"
2962
3529
  ];
2963
- weight = 80;
3530
+ weight = 85;
2964
3531
  supports(file) {
2965
3532
  const ext = file.metadata.extension?.toLowerCase();
2966
3533
  const mime = file.metadata.mimeType?.toLowerCase();
@@ -2972,6 +3539,14 @@ var ExcelPlugin = class {
2972
3539
  getToolbarActions(instance) {
2973
3540
  const totalSheets = instance.getPageCount?.() ?? 1;
2974
3541
  const actions = [];
3542
+ actions.push({
3543
+ id: "thumbnails",
3544
+ icon: "thumbnails",
3545
+ label: "Sheet Thumbnails",
3546
+ type: "button",
3547
+ group: "navigation",
3548
+ execute: () => instance.toggleThumbnails?.()
3549
+ });
2975
3550
  if (totalSheets > 1) {
2976
3551
  actions.push({
2977
3552
  id: "page-nav",
@@ -3054,6 +3629,16 @@ var ExcelPlugin = class {
3054
3629
  execute: () => {
3055
3630
  instance.print?.();
3056
3631
  }
3632
+ },
3633
+ {
3634
+ id: "open-window",
3635
+ icon: "open-window",
3636
+ label: "Open in Separate Full Window",
3637
+ type: "button",
3638
+ group: "actions",
3639
+ execute: () => {
3640
+ instance.openInSeparateWindow?.();
3641
+ }
3057
3642
  }
3058
3643
  );
3059
3644
  return actions;
@@ -3219,6 +3804,63 @@ var ExcelPlugin = class {
3219
3804
  },
3220
3805
  getPageCount: () => sheetNames.length,
3221
3806
  getCurrentPage: () => currentSheetIndex,
3807
+ getThumbnails: () => {
3808
+ return sheetNames.map((name, idx) => ({
3809
+ index: idx,
3810
+ label: name,
3811
+ render: async (canvas) => {
3812
+ canvas.width = 140;
3813
+ canvas.height = 100;
3814
+ const c = canvas.getContext("2d");
3815
+ if (!c) return;
3816
+ c.fillStyle = "#ffffff";
3817
+ c.fillRect(0, 0, canvas.width, canvas.height);
3818
+ c.fillStyle = "#107c41";
3819
+ c.fillRect(0, 0, canvas.width, 16);
3820
+ c.fillStyle = "#ffffff";
3821
+ c.font = 'bold 8px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
3822
+ c.fillText(`\u{1F4CA} ${name.slice(0, 16)}`, 6, 11);
3823
+ const startY = 17;
3824
+ const rowH = 13;
3825
+ const colW = 30;
3826
+ const colHeaders = ["A", "B", "C", "D"];
3827
+ c.fillStyle = "#f1f5f9";
3828
+ c.fillRect(0, startY, canvas.width, rowH);
3829
+ c.strokeStyle = "#cbd5e1";
3830
+ c.lineWidth = 0.8;
3831
+ c.strokeRect(0, startY, canvas.width, rowH);
3832
+ c.fillStyle = "#64748b";
3833
+ c.font = "bold 7px sans-serif";
3834
+ for (let ci = 0; ci < colHeaders.length; ci++) {
3835
+ c.fillText(colHeaders[ci], 14 + ci * colW, startY + 9);
3836
+ }
3837
+ const ws = wb?.Sheets[name];
3838
+ for (let ri = 1; ri <= 5; ri++) {
3839
+ const y = startY + ri * rowH;
3840
+ if (y > canvas.height - 2) break;
3841
+ c.fillStyle = "#f8fafc";
3842
+ c.fillRect(0, y, 12, rowH);
3843
+ c.fillStyle = "#94a3b8";
3844
+ c.font = "6px sans-serif";
3845
+ c.fillText(String(ri), 3, y + 9);
3846
+ c.strokeStyle = "#e2e8f0";
3847
+ c.strokeRect(0, y, canvas.width, rowH);
3848
+ c.fillStyle = "#334155";
3849
+ c.font = "6px sans-serif";
3850
+ for (let ci = 0; ci < colHeaders.length; ci++) {
3851
+ const cellRef = `${colHeaders[ci]}${ri}`;
3852
+ const cellVal = ws?.[cellRef]?.w || ws?.[cellRef]?.v;
3853
+ if (cellVal !== void 0) {
3854
+ c.fillText(String(cellVal).slice(0, 6), 14 + ci * colW, y + 9);
3855
+ }
3856
+ }
3857
+ }
3858
+ c.strokeStyle = "#cbd5e1";
3859
+ c.lineWidth = 1;
3860
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
3861
+ }
3862
+ }));
3863
+ },
3222
3864
  download: () => {
3223
3865
  const blob = new Blob([ctx.buffer], { type: this.mimeTypes[0] });
3224
3866
  const url = URL.createObjectURL(blob);
@@ -3251,7 +3893,39 @@ var CsvPlugin = class {
3251
3893
  return ext === ".csv" || ext === ".tsv" || mime === "text/csv" || mime === "text/tab-separated-values";
3252
3894
  }
3253
3895
  getToolbarActions(instance) {
3254
- return [
3896
+ const totalPages = instance.getPageCount?.() ?? 1;
3897
+ const actions = [];
3898
+ actions.push({
3899
+ id: "thumbnails",
3900
+ icon: "thumbnails",
3901
+ label: "Page Thumbnails",
3902
+ type: "button",
3903
+ group: "navigation",
3904
+ execute: () => instance.toggleThumbnails?.()
3905
+ });
3906
+ if (totalPages > 1) {
3907
+ actions.push({
3908
+ id: "page-nav",
3909
+ icon: "",
3910
+ label: "Page Navigation",
3911
+ type: "page-nav",
3912
+ group: "navigation",
3913
+ value: instance.getCurrentPage?.() ?? 1,
3914
+ max: totalPages,
3915
+ execute: (action, page) => {
3916
+ const cur = instance.getCurrentPage?.() ?? 1;
3917
+ const max = instance.getPageCount?.() ?? 1;
3918
+ if (action === "prev") {
3919
+ if (cur > 1) instance.goToPage?.(cur - 1);
3920
+ } else if (action === "next") {
3921
+ if (cur < max) instance.goToPage?.(cur + 1);
3922
+ } else if (typeof page === "number") {
3923
+ instance.goToPage?.(page);
3924
+ }
3925
+ }
3926
+ });
3927
+ }
3928
+ actions.push(
3255
3929
  {
3256
3930
  id: "zoom-out",
3257
3931
  icon: "zoom-out",
@@ -3272,6 +3946,16 @@ var CsvPlugin = class {
3272
3946
  instance.zoomIn?.();
3273
3947
  }
3274
3948
  },
3949
+ {
3950
+ id: "fit-page",
3951
+ icon: "fit-page",
3952
+ label: "Fit to View",
3953
+ type: "button",
3954
+ group: "zoom",
3955
+ execute: () => {
3956
+ instance.fitToPage?.();
3957
+ }
3958
+ },
3275
3959
  {
3276
3960
  id: "download",
3277
3961
  icon: "download",
@@ -3291,8 +3975,19 @@ var CsvPlugin = class {
3291
3975
  execute: () => {
3292
3976
  instance.print?.();
3293
3977
  }
3978
+ },
3979
+ {
3980
+ id: "open-window",
3981
+ icon: "open-window",
3982
+ label: "Open in Separate Full Window",
3983
+ type: "button",
3984
+ group: "actions",
3985
+ execute: () => {
3986
+ instance.openInSeparateWindow?.();
3987
+ }
3294
3988
  }
3295
- ];
3989
+ );
3990
+ return actions;
3296
3991
  }
3297
3992
  async render(ctx) {
3298
3993
  const decoder = new TextDecoder("utf-8");
@@ -3304,33 +3999,62 @@ var CsvPlugin = class {
3304
3999
  container.style.padding = "16px";
3305
4000
  container.style.boxSizing = "border-box";
3306
4001
  container.style.transformOrigin = "top left";
4002
+ const delimiter = ctx.metadata.extension === ".tsv" ? " " : ",";
4003
+ const allLines = text.split(/\r?\n/).filter((r) => r.trim().length > 0);
4004
+ const headerLine = allLines[0] || "";
4005
+ const headerCells = headerLine.split(delimiter);
4006
+ const dataLines = allLines.slice(1);
4007
+ const ROWS_PER_PAGE = 100;
4008
+ const totalPages = Math.max(1, Math.ceil(dataLines.length / ROWS_PER_PAGE));
4009
+ let currentPage = 1;
3307
4010
  const table = document.createElement("table");
3308
4011
  table.style.borderCollapse = "collapse";
3309
4012
  table.style.width = "100%";
3310
- table.style.fontFamily = "monospace";
4013
+ table.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace';
3311
4014
  table.style.fontSize = "13px";
3312
- const delimiter = ctx.metadata.extension === ".tsv" ? " " : ",";
3313
- const rows = text.split(/\r?\n/).slice(0, 500);
3314
- rows.forEach((row, rowIndex) => {
3315
- if (!row.trim()) return;
3316
- const tr = document.createElement("tr");
3317
- const cells = row.split(delimiter);
3318
- cells.forEach((cell) => {
3319
- const td = document.createElement(rowIndex === 0 ? "th" : "td");
3320
- td.textContent = cell.trim();
3321
- td.style.border = "1px solid #d0d7de";
3322
- td.style.padding = "6px 12px";
3323
- if (rowIndex === 0) {
3324
- td.style.backgroundColor = "#f6f8fa";
3325
- td.style.fontWeight = "bold";
3326
- }
3327
- tr.appendChild(td);
4015
+ const renderPage = (pageNum) => {
4016
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
4017
+ table.innerHTML = "";
4018
+ const headerTr = document.createElement("tr");
4019
+ headerCells.forEach((cell) => {
4020
+ const th = document.createElement("th");
4021
+ th.textContent = cell.trim();
4022
+ th.style.border = "1px solid #d0d7de";
4023
+ th.style.padding = "8px 12px";
4024
+ th.style.backgroundColor = "#f6f8fa";
4025
+ th.style.fontWeight = "600";
4026
+ th.style.whiteSpace = "nowrap";
4027
+ headerTr.appendChild(th);
3328
4028
  });
3329
- table.appendChild(tr);
3330
- });
4029
+ table.appendChild(headerTr);
4030
+ const start = (currentPage - 1) * ROWS_PER_PAGE;
4031
+ const end = Math.min(dataLines.length, start + ROWS_PER_PAGE);
4032
+ const pageRows = dataLines.slice(start, end);
4033
+ pageRows.forEach((row, idx) => {
4034
+ const tr = document.createElement("tr");
4035
+ if (idx % 2 === 1) tr.style.backgroundColor = "#f8fafc";
4036
+ const cells = row.split(delimiter);
4037
+ cells.forEach((cell) => {
4038
+ const td = document.createElement("td");
4039
+ td.textContent = cell.trim();
4040
+ td.style.border = "1px solid #d0d7de";
4041
+ td.style.padding = "6px 12px";
4042
+ td.style.whiteSpace = "nowrap";
4043
+ tr.appendChild(td);
4044
+ });
4045
+ table.appendChild(tr);
4046
+ });
4047
+ container.scrollTop = 0;
4048
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
4049
+ };
4050
+ renderPage(1);
3331
4051
  container.appendChild(table);
3332
4052
  ctx.container.appendChild(container);
3333
4053
  let scale = 1;
4054
+ const applyScale = () => {
4055
+ container.style.transform = `scale(${scale})`;
4056
+ container.style.transformOrigin = "top left";
4057
+ };
3334
4058
  const cleanup = () => {
3335
4059
  container.remove();
3336
4060
  ctx.container.innerHTML = "";
@@ -3338,22 +4062,86 @@ var CsvPlugin = class {
3338
4062
  ctx.signal.addEventListener("abort", cleanup);
3339
4063
  return {
3340
4064
  destroy: cleanup,
4065
+ getPageCount: () => totalPages,
4066
+ getCurrentPage: () => currentPage,
4067
+ goToPage: (page) => renderPage(page),
4068
+ getThumbnails: () => {
4069
+ const thumbnails = [];
4070
+ for (let i = 0; i < totalPages; i++) {
4071
+ const pageIdx = i;
4072
+ const startRow = pageIdx * ROWS_PER_PAGE + 1;
4073
+ const endRow = Math.min(dataLines.length, (pageIdx + 1) * ROWS_PER_PAGE);
4074
+ const label = totalPages === 1 ? "Table" : `Page ${pageIdx + 1} (${startRow}-${endRow})`;
4075
+ thumbnails.push({
4076
+ index: pageIdx,
4077
+ label,
4078
+ render: async (canvas) => {
4079
+ canvas.width = 140;
4080
+ canvas.height = 100;
4081
+ const c = canvas.getContext("2d");
4082
+ if (!c) return;
4083
+ c.fillStyle = "#ffffff";
4084
+ c.fillRect(0, 0, canvas.width, canvas.height);
4085
+ c.fillStyle = "#2563eb";
4086
+ c.fillRect(0, 0, canvas.width, 16);
4087
+ c.fillStyle = "#ffffff";
4088
+ c.font = "bold 8px sans-serif";
4089
+ c.fillText(`\u{1F4C8} ${label.slice(0, 18)}`, 6, 11);
4090
+ const startY = 17;
4091
+ const rowH = 13;
4092
+ const colW = 28;
4093
+ const cols = Math.min(4, headerCells.length || 4);
4094
+ c.fillStyle = "#f1f5f9";
4095
+ c.fillRect(0, startY, canvas.width, rowH);
4096
+ c.strokeStyle = "#cbd5e1";
4097
+ c.lineWidth = 0.8;
4098
+ c.strokeRect(0, startY, canvas.width, rowH);
4099
+ c.fillStyle = "#64748b";
4100
+ c.font = "bold 7px sans-serif";
4101
+ for (let ci = 0; ci < cols; ci++) {
4102
+ const hName = headerCells[ci] || `Col ${ci + 1}`;
4103
+ c.fillText(hName.slice(0, 5), 8 + ci * colW, startY + 9);
4104
+ }
4105
+ const rowsToPreview = dataLines.slice(pageIdx * ROWS_PER_PAGE, pageIdx * ROWS_PER_PAGE + 5);
4106
+ for (let ri = 0; ri < rowsToPreview.length; ri++) {
4107
+ const y = startY + (ri + 1) * rowH;
4108
+ if (y > canvas.height - 2) break;
4109
+ c.strokeStyle = "#e2e8f0";
4110
+ c.strokeRect(0, y, canvas.width, rowH);
4111
+ const cVals = rowsToPreview[ri].split(delimiter);
4112
+ c.fillStyle = "#334155";
4113
+ c.font = "6px sans-serif";
4114
+ for (let ci = 0; ci < cols; ci++) {
4115
+ const val = cVals[ci] || "";
4116
+ c.fillText(val.trim().slice(0, 6), 8 + ci * colW, y + 9);
4117
+ }
4118
+ }
4119
+ c.strokeStyle = "#cbd5e1";
4120
+ c.lineWidth = 1;
4121
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
4122
+ }
4123
+ });
4124
+ }
4125
+ return thumbnails;
4126
+ },
3341
4127
  zoomIn: () => {
3342
4128
  scale += 0.1;
3343
- container.style.transform = `scale(${scale})`;
4129
+ applyScale();
3344
4130
  },
3345
4131
  zoomOut: () => {
3346
4132
  scale = Math.max(0.2, scale - 0.1);
3347
- container.style.transform = `scale(${scale})`;
4133
+ applyScale();
3348
4134
  },
3349
4135
  getZoom: () => scale,
3350
4136
  setZoom: (level) => {
3351
4137
  scale = level;
3352
- container.style.transform = `scale(${scale})`;
4138
+ applyScale();
3353
4139
  },
3354
4140
  fitToPage: () => {
3355
- scale = 1;
3356
- container.style.transform = `scale(1)`;
4141
+ const availW = Math.max(280, (ctx.container.clientWidth || window.innerWidth) - 48);
4142
+ const tW = table.offsetWidth || 600;
4143
+ scale = Math.max(0.35, Math.min(1, availW / tW));
4144
+ applyScale();
3357
4145
  },
3358
4146
  download: () => {
3359
4147
  const blob = new Blob([ctx.buffer], { type: "text/csv" });
@@ -3417,28 +4205,34 @@ var CodePlugin = class {
3417
4205
  getToolbarActions(instance) {
3418
4206
  const totalPages = instance.getPageCount?.() ?? 1;
3419
4207
  const actions = [];
3420
- if (totalPages > 1) {
3421
- actions.push({
3422
- id: "page-nav",
3423
- icon: "",
3424
- label: "Page Navigation",
3425
- type: "page-nav",
3426
- group: "navigation",
3427
- value: instance.getCurrentPage?.() ?? 1,
3428
- max: totalPages,
3429
- execute: (action, page) => {
3430
- const cur = instance.getCurrentPage?.() ?? 1;
3431
- const max = instance.getPageCount?.() ?? 1;
3432
- if (action === "prev") {
3433
- if (cur > 1) instance.goToPage?.(cur - 1);
3434
- } else if (action === "next") {
3435
- if (cur < max) instance.goToPage?.(cur + 1);
3436
- } else if (typeof page === "number") {
3437
- instance.goToPage?.(page);
3438
- }
4208
+ actions.push({
4209
+ id: "thumbnails",
4210
+ icon: "thumbnails",
4211
+ label: "Page Thumbnails",
4212
+ type: "button",
4213
+ group: "navigation",
4214
+ execute: () => instance.toggleThumbnails?.()
4215
+ });
4216
+ actions.push({
4217
+ id: "page-nav",
4218
+ icon: "",
4219
+ label: "Page Navigation",
4220
+ type: "page-nav",
4221
+ group: "navigation",
4222
+ value: instance.getCurrentPage?.() ?? 1,
4223
+ max: totalPages,
4224
+ execute: (action, page) => {
4225
+ const cur = instance.getCurrentPage?.() ?? 1;
4226
+ const max = instance.getPageCount?.() ?? 1;
4227
+ if (action === "prev") {
4228
+ if (cur > 1) instance.goToPage?.(cur - 1);
4229
+ } else if (action === "next") {
4230
+ if (cur < max) instance.goToPage?.(cur + 1);
4231
+ } else if (typeof page === "number") {
4232
+ instance.goToPage?.(page);
3439
4233
  }
3440
- });
3441
- }
4234
+ }
4235
+ });
3442
4236
  actions.push(
3443
4237
  {
3444
4238
  id: "zoom-out",
@@ -3582,16 +4376,19 @@ var CodePlugin = class {
3582
4376
  let fontSize = 13;
3583
4377
  let rotation = 0;
3584
4378
  let zoomLevel = 1;
4379
+ let isUserZoomed = false;
3585
4380
  const pre = document.createElement("pre");
3586
4381
  const code = document.createElement("code");
3587
4382
  if (isTxt) {
4383
+ container.style.overflowX = "hidden";
4384
+ container.style.overflowY = "auto";
3588
4385
  container.style.backgroundColor = "#f1f5f9";
3589
- container.style.padding = "32px";
4386
+ container.style.padding = "16px 8px";
3590
4387
  container.style.boxSizing = "border-box";
3591
4388
  container.style.display = "flex";
3592
4389
  container.style.flexDirection = "column";
3593
4390
  container.style.alignItems = "center";
3594
- pre.style.margin = "0";
4391
+ pre.style.margin = "0 auto";
3595
4392
  pre.style.fontFamily = "Consolas, 'Courier New', monospace";
3596
4393
  pre.style.fontSize = "13px";
3597
4394
  pre.style.color = "#1e293b";
@@ -3603,10 +4400,12 @@ var CodePlugin = class {
3603
4400
  pre.style.minHeight = "1056px";
3604
4401
  pre.style.padding = "72px 56px";
3605
4402
  pre.style.boxSizing = "border-box";
3606
- pre.style.boxShadow = "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)";
4403
+ pre.style.boxShadow = "0 4px 24px rgba(0, 0, 0, 0.08)";
3607
4404
  pre.style.borderRadius = "4px";
3608
4405
  pre.style.transformOrigin = "top center";
4406
+ pre.style.transition = "transform 0.15s ease";
3609
4407
  } else {
4408
+ container.style.overflow = "auto";
3610
4409
  container.style.backgroundColor = "#1e1e1e";
3611
4410
  container.style.color = "#d4d4d4";
3612
4411
  container.style.padding = "16px";
@@ -3638,42 +4437,9 @@ var CodePlugin = class {
3638
4437
  renderCodePage(rawPages[0] || (isTxt ? "" : fullText));
3639
4438
  pre.appendChild(code);
3640
4439
  container.appendChild(pre);
3641
- let indicator = null;
3642
- if (totalPages > 1) {
3643
- indicator = document.createElement("div");
3644
- indicator.className = "fp-code-page-indicator";
3645
- indicator.style.position = "sticky";
3646
- indicator.style.bottom = "16px";
3647
- if (isTxt) {
3648
- indicator.style.backgroundColor = "rgba(255, 255, 255, 0.9)";
3649
- indicator.style.color = "#334155";
3650
- indicator.style.border = "1px solid #e2e8f0";
3651
- indicator.style.boxShadow = "0 1px 3px rgba(0,0,0,0.1)";
3652
- } else {
3653
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
3654
- indicator.style.color = "#f8fafc";
3655
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
3656
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
3657
- indicator.style.backdropFilter = "blur(8px)";
3658
- }
3659
- indicator.style.fontSize = "12px";
3660
- indicator.style.fontWeight = "600";
3661
- indicator.style.padding = "5px 14px";
3662
- indicator.style.borderRadius = "20px";
3663
- indicator.style.zIndex = "10";
3664
- indicator.style.userSelect = "none";
3665
- indicator.style.pointerEvents = "none";
3666
- indicator.style.textAlign = "center";
3667
- indicator.style.width = "fit-content";
3668
- indicator.style.margin = "16px auto 0";
3669
- container.appendChild(indicator);
3670
- }
3671
4440
  const showPage = (pageNum) => {
3672
4441
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
3673
4442
  renderCodePage(rawPages[currentPage - 1] || (isTxt ? "" : fullText));
3674
- if (indicator) {
3675
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
3676
- }
3677
4443
  container.scrollTop = 0;
3678
4444
  ctx.emit("page-change", { page: currentPage, total: totalPages });
3679
4445
  };
@@ -3681,36 +4447,90 @@ var CodePlugin = class {
3681
4447
  showPage(1);
3682
4448
  }
3683
4449
  ctx.container.appendChild(container);
3684
- const cleanup = () => {
3685
- indicator?.remove();
3686
- container.remove();
3687
- ctx.container.innerHTML = "";
4450
+ const calculateTxtFit = () => {
4451
+ const availW = Math.max(280, container.clientWidth - 32);
4452
+ return Math.max(0.4, Math.min(3, availW / 816));
3688
4453
  };
3689
- ctx.signal.addEventListener("abort", cleanup);
3690
4454
  const updateTransform = () => {
3691
4455
  if (isTxt) {
3692
4456
  pre.style.transform = `scale(${zoomLevel}) rotate(${rotation}deg)`;
4457
+ const scaledH = 1056 * zoomLevel;
4458
+ const extraH = Math.max(0, scaledH - 1056);
4459
+ pre.style.marginBottom = `${extraH + 32}px`;
3693
4460
  } else {
3694
4461
  pre.style.transform = `rotate(${rotation}deg)`;
3695
4462
  pre.style.fontSize = `${fontSize}px`;
3696
4463
  }
3697
4464
  };
4465
+ let resizeObserver = null;
4466
+ if (isTxt) {
4467
+ setTimeout(() => {
4468
+ zoomLevel = calculateTxtFit();
4469
+ updateTransform();
4470
+ }, 60);
4471
+ resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
4472
+ if (!isUserZoomed) {
4473
+ zoomLevel = calculateTxtFit();
4474
+ updateTransform();
4475
+ }
4476
+ }) : null;
4477
+ resizeObserver?.observe(container);
4478
+ }
4479
+ const cleanup = () => {
4480
+ resizeObserver?.disconnect();
4481
+ container.remove();
4482
+ ctx.container.innerHTML = "";
4483
+ };
4484
+ ctx.signal.addEventListener("abort", cleanup);
3698
4485
  return {
3699
4486
  destroy: cleanup,
3700
4487
  getPageCount: () => totalPages,
3701
4488
  getCurrentPage: () => currentPage,
3702
4489
  goToPage: (page) => showPage(page),
4490
+ getThumbnails: () => {
4491
+ return rawPages.map((pageText, idx) => ({
4492
+ index: idx,
4493
+ label: `Page ${idx + 1}`,
4494
+ render: async (canvas) => {
4495
+ canvas.width = 140;
4496
+ canvas.height = 180;
4497
+ const c = canvas.getContext("2d");
4498
+ if (!c) return;
4499
+ c.fillStyle = "#ffffff";
4500
+ c.fillRect(0, 0, canvas.width, canvas.height);
4501
+ c.strokeStyle = "#cbd5e1";
4502
+ c.lineWidth = 1;
4503
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
4504
+ c.fillStyle = "#64748b";
4505
+ const lines = (pageText || "").split("\n").slice(0, 24);
4506
+ const lineH = 6;
4507
+ const startY = 14;
4508
+ const startX = 12;
4509
+ const maxW = canvas.width - 24;
4510
+ c.font = "5px monospace";
4511
+ for (let li = 0; li < lines.length; li++) {
4512
+ const l = lines[li].trim();
4513
+ if (!l) continue;
4514
+ const y = startY + li * lineH;
4515
+ if (y > canvas.height - 12) break;
4516
+ c.fillText(l.slice(0, 30), startX, y, maxW);
4517
+ }
4518
+ }
4519
+ }));
4520
+ },
3703
4521
  zoomIn: () => {
4522
+ isUserZoomed = true;
3704
4523
  if (isTxt) {
3705
- zoomLevel = Math.min(3, zoomLevel + 0.1);
4524
+ zoomLevel = Math.min(3.5, zoomLevel + 0.15);
3706
4525
  } else {
3707
4526
  fontSize = Math.min(32, fontSize + 2);
3708
4527
  }
3709
4528
  updateTransform();
3710
4529
  },
3711
4530
  zoomOut: () => {
4531
+ isUserZoomed = true;
3712
4532
  if (isTxt) {
3713
- zoomLevel = Math.max(0.1, zoomLevel - 0.1);
4533
+ zoomLevel = Math.max(0.1, zoomLevel - 0.15);
3714
4534
  } else {
3715
4535
  fontSize = Math.max(8, fontSize - 2);
3716
4536
  }
@@ -3718,6 +4538,7 @@ var CodePlugin = class {
3718
4538
  },
3719
4539
  getZoom: () => isTxt ? zoomLevel : fontSize / 13,
3720
4540
  setZoom: (level) => {
4541
+ isUserZoomed = true;
3721
4542
  if (isTxt) {
3722
4543
  zoomLevel = level;
3723
4544
  } else {
@@ -3726,9 +4547,10 @@ var CodePlugin = class {
3726
4547
  updateTransform();
3727
4548
  },
3728
4549
  fitToPage: () => {
4550
+ isUserZoomed = false;
3729
4551
  fontSize = 13;
3730
4552
  rotation = 0;
3731
- zoomLevel = isTxt ? Math.max(0.65, Math.min(1.05, (container.clientWidth - 48) / 816)) : 1;
4553
+ zoomLevel = isTxt ? calculateTxtFit() : 1;
3732
4554
  updateTransform();
3733
4555
  },
3734
4556
  rotateCW: () => {
@@ -4609,28 +5431,26 @@ var RtfPlugin = class {
4609
5431
  getToolbarActions(instance) {
4610
5432
  const totalPages = instance.getPageCount?.() ?? 1;
4611
5433
  const actions = [];
4612
- if (totalPages > 1) {
4613
- actions.push({
4614
- id: "page-nav",
4615
- icon: "",
4616
- label: "Page Navigation",
4617
- type: "page-nav",
4618
- group: "navigation",
4619
- value: instance.getCurrentPage?.() ?? 1,
4620
- max: totalPages,
4621
- execute: (action, page) => {
4622
- const cur = instance.getCurrentPage?.() ?? 1;
4623
- const max = instance.getPageCount?.() ?? 1;
4624
- if (action === "prev") {
4625
- if (cur > 1) instance.goToPage?.(cur - 1);
4626
- } else if (action === "next") {
4627
- if (cur < max) instance.goToPage?.(cur + 1);
4628
- } else if (typeof page === "number") {
4629
- instance.goToPage?.(page);
4630
- }
5434
+ actions.push({
5435
+ id: "page-nav",
5436
+ icon: "",
5437
+ label: "Page Navigation",
5438
+ type: "page-nav",
5439
+ group: "navigation",
5440
+ value: instance.getCurrentPage?.() ?? 1,
5441
+ max: totalPages,
5442
+ execute: (action, page) => {
5443
+ const cur = instance.getCurrentPage?.() ?? 1;
5444
+ const max = instance.getPageCount?.() ?? 1;
5445
+ if (action === "prev") {
5446
+ if (cur > 1) instance.goToPage?.(cur - 1);
5447
+ } else if (action === "next") {
5448
+ if (cur < max) instance.goToPage?.(cur + 1);
5449
+ } else if (typeof page === "number") {
5450
+ instance.goToPage?.(page);
4631
5451
  }
4632
- });
4633
- }
5452
+ }
5453
+ });
4634
5454
  actions.push(
4635
5455
  {
4636
5456
  id: "zoom-out",
@@ -4702,43 +5522,57 @@ var RtfPlugin = class {
4702
5522
  async render(ctx) {
4703
5523
  const wrapper = document.createElement("div");
4704
5524
  wrapper.className = "fp-rtf-wrapper";
4705
- wrapper.style.padding = "32px";
4706
- wrapper.style.maxWidth = "850px";
5525
+ wrapper.style.padding = "0";
5526
+ wrapper.style.width = "816px";
4707
5527
  wrapper.style.margin = "0 auto";
4708
- wrapper.style.backgroundColor = "#fff";
4709
- wrapper.style.boxShadow = "0 2px 8px rgba(0,0,0,0.08)";
4710
- wrapper.style.borderRadius = "4px";
4711
- wrapper.style.minHeight = "100%";
5528
+ wrapper.style.boxSizing = "border-box";
5529
+ wrapper.style.display = "flex";
5530
+ wrapper.style.flexDirection = "column";
5531
+ wrapper.style.alignItems = "center";
5532
+ wrapper.style.backgroundColor = "transparent";
4712
5533
  wrapper.style.transformOrigin = "top center";
4713
- wrapper.style.transition = "transform 0.2s ease";
4714
- ctx.container.style.overflow = "auto";
4715
- ctx.container.style.padding = "24px";
5534
+ wrapper.style.transition = "transform 0.15s ease";
5535
+ ctx.container.style.overflowX = "hidden";
5536
+ ctx.container.style.overflowY = "auto";
5537
+ ctx.container.style.padding = "16px 8px";
4716
5538
  ctx.container.style.backgroundColor = "#f1f5f9";
4717
5539
  ctx.container.appendChild(wrapper);
4718
5540
  let scale = 1;
4719
5541
  let rotation = 0;
4720
5542
  let pageElements = [];
4721
- let fitMode = "width";
5543
+ let isUserZoomed = false;
5544
+ let fitMode = ctx?.options?.fitMode || "width";
4722
5545
  const calculateFitScale = (mode = fitMode) => {
4723
5546
  const activeEl = pageElements[currentPage - 1] || wrapper;
4724
- const elW = activeEl.offsetWidth || 850;
4725
- const singlePageH = Math.min(activeEl.offsetHeight || 1056, Math.round(elW * 1.32));
4726
- const availW = Math.max(280, ctx.container.clientWidth - 48);
4727
- const availH = Math.max(280, ctx.container.clientHeight - 80);
5547
+ const elW = 816;
5548
+ const singlePageH = activeEl?.offsetHeight || 1056;
5549
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
5550
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
4728
5551
  const sW = availW / elW;
4729
5552
  const sH = availH / singlePageH;
4730
5553
  if (mode === "page") {
4731
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
5554
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
4732
5555
  }
4733
- return Math.max(0.65, Math.min(1.05, sW));
5556
+ return Math.max(0.4, Math.min(3, sW));
4734
5557
  };
4735
5558
  const applyTransform = () => {
4736
5559
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
4737
5560
  wrapper.style.transformOrigin = "top center";
5561
+ const activeEl = pageElements[currentPage - 1];
5562
+ const baseH = activeEl?.offsetHeight || 1056;
5563
+ const scaledH = baseH * scale;
5564
+ const extraH = Math.max(0, scaledH - baseH);
5565
+ wrapper.style.marginBottom = `${extraH + 32}px`;
4738
5566
  };
5567
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
5568
+ if (!isUserZoomed) {
5569
+ scale = calculateFitScale(fitMode);
5570
+ applyTransform();
5571
+ }
5572
+ }) : null;
5573
+ resizeObserver?.observe(ctx.container);
4739
5574
  setTimeout(() => {
4740
- fitMode = "width";
4741
- scale = calculateFitScale("width");
5575
+ scale = calculateFitScale(fitMode);
4742
5576
  applyTransform();
4743
5577
  }, 60);
4744
5578
  try {
@@ -4748,20 +5582,34 @@ var RtfPlugin = class {
4748
5582
  const doc = new RTFJS__namespace.Document(ctx.buffer, {});
4749
5583
  const htmlElements = await doc.render();
4750
5584
  const contentNodes = [];
4751
- for (const item of htmlElements) {
4752
- if (item.children && item.children.length > 0 && !item.tagName.toLowerCase().startsWith("table")) {
4753
- contentNodes.push(...Array.from(item.children));
4754
- } else {
4755
- contentNodes.push(item);
5585
+ const extractBlocks = (nodes) => {
5586
+ for (const item of nodes) {
5587
+ if (!item || !(item instanceof HTMLElement)) continue;
5588
+ const tag = item.tagName.toLowerCase();
5589
+ const hasChildBlocks = Array.from(item.children).some((c) => {
5590
+ const ct = c.tagName.toLowerCase();
5591
+ return ["p", "div", "table", "h1", "h2", "h3", "h4", "h5", "h6", "section", "article", "ul", "ol"].includes(ct);
5592
+ });
5593
+ if (hasChildBlocks && !["table", "tr", "td", "th", "ul", "ol", "li"].includes(tag)) {
5594
+ extractBlocks(Array.from(item.children));
5595
+ } else {
5596
+ if (tag === "p" || tag === "div") {
5597
+ item.style.display = "block";
5598
+ item.style.marginBottom = "12px";
5599
+ item.style.lineHeight = "1.6";
5600
+ }
5601
+ contentNodes.push(item);
5602
+ }
4756
5603
  }
4757
- }
5604
+ };
5605
+ extractBlocks(htmlElements);
4758
5606
  wrapper.innerHTML = "";
4759
5607
  contentNodes.forEach((node) => wrapper.appendChild(node));
4760
5608
  const childHeights = contentNodes.map((c) => {
4761
5609
  const rectH = c.getBoundingClientRect ? c.getBoundingClientRect().height : 0;
4762
5610
  const offH = c.offsetHeight || 0;
4763
5611
  const textLen = c.textContent?.trim().length || 0;
4764
- const estH = Math.max(24, Math.ceil(textLen / 75) * 22 + 14);
5612
+ const estH = Math.max(28, Math.ceil(textLen / 75) * 24 + 16);
4765
5613
  return Math.max(rectH, offH, estH);
4766
5614
  });
4767
5615
  wrapper.innerHTML = "";
@@ -4778,6 +5626,7 @@ var RtfPlugin = class {
4778
5626
  card.style.marginBottom = "24px";
4779
5627
  card.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
4780
5628
  card.style.lineHeight = "1.6";
5629
+ card.style.color = "#1e293b";
4781
5630
  return card;
4782
5631
  };
4783
5632
  let curCard = createRtfCard();
@@ -4813,9 +5662,8 @@ var RtfPlugin = class {
4813
5662
  pageCard.style.padding = "72px 56px";
4814
5663
  pageCard.style.width = "816px";
4815
5664
  pageCard.style.minHeight = "1056px";
4816
- pageCard.style.maxWidth = "100%";
4817
5665
  pageCard.style.boxSizing = "border-box";
4818
- pageCard.style.fontFamily = "serif";
5666
+ pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
4819
5667
  pageCard.style.fontSize = "12pt";
4820
5668
  pageCard.style.lineHeight = "1.6";
4821
5669
  pageCard.style.color = "#1e293b";
@@ -4828,29 +5676,6 @@ var RtfPlugin = class {
4828
5676
  }
4829
5677
  const totalPages = Math.max(1, pageElements.length);
4830
5678
  let currentPage = 1;
4831
- let indicator = null;
4832
- if (totalPages > 1) {
4833
- indicator = document.createElement("div");
4834
- indicator.className = "fp-rtf-page-indicator";
4835
- indicator.style.position = "sticky";
4836
- indicator.style.bottom = "16px";
4837
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
4838
- indicator.style.backdropFilter = "blur(8px)";
4839
- indicator.style.color = "#f8fafc";
4840
- indicator.style.fontSize = "12px";
4841
- indicator.style.fontWeight = "600";
4842
- indicator.style.padding = "5px 14px";
4843
- indicator.style.borderRadius = "20px";
4844
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
4845
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
4846
- indicator.style.zIndex = "10";
4847
- indicator.style.userSelect = "none";
4848
- indicator.style.pointerEvents = "none";
4849
- indicator.style.textAlign = "center";
4850
- indicator.style.width = "fit-content";
4851
- indicator.style.margin = "16px auto 0";
4852
- ctx.container.appendChild(indicator);
4853
- }
4854
5679
  const showPage = (pageNum) => {
4855
5680
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
4856
5681
  if (totalPages > 1) {
@@ -4858,9 +5683,6 @@ var RtfPlugin = class {
4858
5683
  el.style.display = idx + 1 === currentPage ? "block" : "none";
4859
5684
  });
4860
5685
  }
4861
- if (indicator) {
4862
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4863
- }
4864
5686
  ctx.container.scrollTop = 0;
4865
5687
  ctx.emit("page-change", { page: currentPage, total: totalPages });
4866
5688
  };
@@ -4868,7 +5690,7 @@ var RtfPlugin = class {
4868
5690
  showPage(1);
4869
5691
  }
4870
5692
  const cleanup = () => {
4871
- indicator?.remove();
5693
+ resizeObserver?.disconnect();
4872
5694
  wrapper.remove();
4873
5695
  ctx.container.innerHTML = "";
4874
5696
  };
@@ -4879,21 +5701,25 @@ var RtfPlugin = class {
4879
5701
  getCurrentPage: () => currentPage,
4880
5702
  goToPage: (page) => showPage(page),
4881
5703
  zoomIn: () => {
5704
+ isUserZoomed = true;
4882
5705
  scale += 0.15;
4883
5706
  applyTransform();
4884
5707
  },
4885
5708
  zoomOut: () => {
5709
+ isUserZoomed = true;
4886
5710
  scale = Math.max(0.2, scale - 0.15);
4887
5711
  applyTransform();
4888
5712
  },
4889
5713
  getZoom: () => scale,
4890
5714
  setZoom: (level) => {
5715
+ isUserZoomed = true;
4891
5716
  scale = level;
4892
5717
  applyTransform();
4893
5718
  },
4894
5719
  fitToPage: () => {
4895
- fitMode = fitMode === "width" ? "page" : "width";
4896
- scale = calculateFitScale(fitMode);
5720
+ isUserZoomed = false;
5721
+ fitMode = "width";
5722
+ scale = calculateFitScale("width");
4897
5723
  rotation = 0;
4898
5724
  applyTransform();
4899
5725
  },
@@ -5081,40 +5907,36 @@ var OpenDocumentPlugin = class {
5081
5907
  const isPresentation = instance.isPresentation;
5082
5908
  const totalPages = instance.getPageCount?.() ?? 1;
5083
5909
  const actions = [];
5084
- if (isPresentation || totalPages > 1) {
5085
- if (isPresentation) {
5086
- actions.push({
5087
- id: "thumbnails",
5088
- icon: "thumbnails",
5089
- label: "Slide Thumbnails",
5090
- type: "button",
5091
- group: "navigation",
5092
- execute: () => instance.toggleThumbnails?.()
5093
- });
5094
- }
5095
- actions.push({
5096
- id: "page-nav",
5097
- icon: "",
5098
- label: isPresentation ? "Slide Navigation" : "Page Navigation",
5099
- type: "page-nav",
5100
- group: "navigation",
5101
- value: instance.getCurrentPage?.() ?? 1,
5102
- max: totalPages,
5103
- execute: (action, page) => {
5104
- const cur = instance.getCurrentPage?.() ?? 1;
5105
- const max = instance.getPageCount?.() ?? 1;
5106
- if (action === "prev") {
5107
- if (cur > 1) instance.goToPage?.(cur - 1);
5108
- } else if (action === "next") {
5109
- if (cur < max) instance.goToPage?.(cur + 1);
5110
- } else if (typeof page === "number") {
5111
- instance.goToPage?.(page);
5112
- } else if (typeof action === "number") {
5113
- instance.goToPage?.(action);
5114
- }
5910
+ actions.push({
5911
+ id: "thumbnails",
5912
+ icon: "thumbnails",
5913
+ label: isPresentation ? "Slide Thumbnails" : "Page Thumbnails",
5914
+ type: "button",
5915
+ group: "navigation",
5916
+ execute: () => instance.toggleThumbnails?.()
5917
+ });
5918
+ actions.push({
5919
+ id: "page-nav",
5920
+ icon: "",
5921
+ label: isPresentation ? "Slide Navigation" : "Page Navigation",
5922
+ type: "page-nav",
5923
+ group: "navigation",
5924
+ value: instance.getCurrentPage?.() ?? 1,
5925
+ max: totalPages,
5926
+ execute: (action, page) => {
5927
+ const cur = instance.getCurrentPage?.() ?? 1;
5928
+ const max = instance.getPageCount?.() ?? 1;
5929
+ if (action === "prev") {
5930
+ if (cur > 1) instance.goToPage?.(cur - 1);
5931
+ } else if (action === "next") {
5932
+ if (cur < max) instance.goToPage?.(cur + 1);
5933
+ } else if (typeof page === "number") {
5934
+ instance.goToPage?.(page);
5935
+ } else if (typeof action === "number") {
5936
+ instance.goToPage?.(action);
5115
5937
  }
5116
- });
5117
- }
5938
+ }
5939
+ });
5118
5940
  actions.push(
5119
5941
  {
5120
5942
  id: "zoom-out",
@@ -5205,49 +6027,65 @@ var OpenDocumentPlugin = class {
5205
6027
  container.className = "fp-odf-container";
5206
6028
  container.style.width = "100%";
5207
6029
  container.style.height = "100%";
5208
- container.style.overflow = "auto";
5209
- container.style.padding = "24px";
6030
+ container.style.overflowX = "hidden";
6031
+ container.style.overflowY = "auto";
6032
+ container.style.padding = "16px 8px";
5210
6033
  container.style.backgroundColor = "#f1f5f9";
5211
6034
  const wrapper = document.createElement("div");
5212
6035
  wrapper.className = "fp-odf-wrapper";
5213
6036
  wrapper.style.margin = "0 auto";
6037
+ wrapper.style.width = isPresentation ? "960px" : "816px";
6038
+ wrapper.style.boxSizing = "border-box";
5214
6039
  wrapper.style.backgroundColor = "#ffffff";
5215
6040
  wrapper.style.boxShadow = "0 2px 10px rgba(0,0,0,0.08)";
5216
6041
  wrapper.style.borderRadius = "4px";
5217
6042
  wrapper.style.transformOrigin = "top center";
5218
- wrapper.style.transition = "transform 0.2s ease";
6043
+ wrapper.style.transition = "transform 0.15s ease";
5219
6044
  container.appendChild(wrapper);
5220
6045
  ctx.container.appendChild(container);
5221
6046
  let scale = 1;
5222
6047
  let rotation = 0;
5223
- let fitMode = "width";
6048
+ let isUserZoomed = false;
6049
+ let fitMode = ctx?.options?.fitMode || "width";
6050
+ let currentPage = 1;
6051
+ let totalPages = 1;
6052
+ let slides = [];
5224
6053
  const calculateFitScale = (mode = fitMode) => {
5225
- const elW = wrapper.offsetWidth || 850;
5226
- const singlePageH = Math.min(wrapper.offsetHeight || 1056, Math.round(elW * 1.32));
5227
- const availW = Math.max(280, ctx.container.clientWidth - 48);
5228
- const availH = Math.max(280, ctx.container.clientHeight - 80);
6054
+ const activeSlide = slides[currentPage - 1];
6055
+ const elW = isPresentation ? 960 : 816;
6056
+ const singlePageH = Math.min(activeSlide?.offsetHeight || wrapper.offsetHeight || 1056, Math.round(elW * 1.32));
6057
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
6058
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
5229
6059
  const sW = availW / elW;
5230
6060
  const sH = availH / (isPresentation ? 540 : singlePageH);
5231
6061
  if (isPresentation) {
5232
- return Math.min(1, Math.min(sW, sH));
6062
+ return Math.min(2.5, Math.min(sW, sH));
5233
6063
  }
5234
6064
  if (mode === "page") {
5235
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
6065
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
5236
6066
  }
5237
- return Math.max(0.65, Math.min(1.05, sW));
6067
+ return Math.max(0.4, Math.min(3, sW));
5238
6068
  };
5239
6069
  const applyTransform = () => {
5240
6070
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5241
6071
  wrapper.style.transformOrigin = "top center";
6072
+ const activeSlide = slides[currentPage - 1];
6073
+ const baseH = activeSlide?.offsetHeight || wrapper.offsetHeight || 1056;
6074
+ const scaledH = baseH * scale;
6075
+ const extraH = Math.max(0, scaledH - baseH);
6076
+ wrapper.style.marginBottom = `${extraH + 32}px`;
5242
6077
  };
6078
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
6079
+ if (!isUserZoomed) {
6080
+ scale = calculateFitScale(fitMode);
6081
+ applyTransform();
6082
+ }
6083
+ }) : null;
6084
+ resizeObserver?.observe(ctx.container);
5243
6085
  setTimeout(() => {
5244
- fitMode = "width";
5245
- scale = calculateFitScale("width");
6086
+ scale = calculateFitScale(fitMode);
5246
6087
  applyTransform();
5247
6088
  }, 60);
5248
- let currentPage = 1;
5249
- let totalPages = 1;
5250
- let slides = [];
5251
6089
  if (isPresentation) {
5252
6090
  wrapper.style.maxWidth = "960px";
5253
6091
  wrapper.style.aspectRatio = "16 / 9";
@@ -5348,24 +6186,9 @@ var OpenDocumentPlugin = class {
5348
6186
  wrapper.appendChild(page);
5349
6187
  slides.push(page);
5350
6188
  });
5351
- const pageIndicator = document.createElement("div");
5352
- pageIndicator.className = "fp-odt-page-indicator";
5353
- pageIndicator.style.position = "sticky";
5354
- pageIndicator.style.bottom = "16px";
5355
- pageIndicator.style.left = "50%";
5356
- pageIndicator.style.transform = "translateX(-50%)";
5357
- pageIndicator.style.backgroundColor = "rgba(0, 0, 0, 0.6)";
5358
- pageIndicator.style.color = "#fff";
5359
- pageIndicator.style.padding = "6px 12px";
5360
- pageIndicator.style.borderRadius = "16px";
5361
- pageIndicator.style.fontSize = "12px";
5362
- pageIndicator.style.zIndex = "100";
5363
- pageIndicator.style.display = "inline-block";
5364
- pageIndicator.style.width = "fit-content";
5365
- pageIndicator.textContent = `Page 1 of ${totalPages}`;
5366
- container.appendChild(pageIndicator);
5367
6189
  }
5368
6190
  const cleanup = () => {
6191
+ resizeObserver?.disconnect();
5369
6192
  imageUrls.forEach((url) => URL.revokeObjectURL(url));
5370
6193
  container.remove();
5371
6194
  ctx.container.innerHTML = "";
@@ -5377,10 +6200,6 @@ var OpenDocumentPlugin = class {
5377
6200
  slides.forEach((s, idx) => {
5378
6201
  s.style.display = idx === page - 1 ? "block" : "none";
5379
6202
  });
5380
- const indicator = container.querySelector(".fp-odt-page-indicator");
5381
- if (indicator) {
5382
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
5383
- }
5384
6203
  container.scrollTop = 0;
5385
6204
  ctx.emit("page-change", { page: currentPage, total: totalPages });
5386
6205
  };
@@ -5388,21 +6207,25 @@ var OpenDocumentPlugin = class {
5388
6207
  destroy: cleanup,
5389
6208
  isPresentation,
5390
6209
  zoomIn: () => {
6210
+ isUserZoomed = true;
5391
6211
  scale += 0.15;
5392
6212
  applyTransform();
5393
6213
  },
5394
6214
  zoomOut: () => {
6215
+ isUserZoomed = true;
5395
6216
  scale = Math.max(0.2, scale - 0.15);
5396
6217
  applyTransform();
5397
6218
  },
5398
6219
  getZoom: () => scale,
5399
6220
  setZoom: (level) => {
6221
+ isUserZoomed = true;
5400
6222
  scale = level;
5401
6223
  applyTransform();
5402
6224
  },
5403
6225
  fitToPage: () => {
5404
- fitMode = fitMode === "width" ? "page" : "width";
5405
- scale = calculateFitScale(fitMode);
6226
+ isUserZoomed = false;
6227
+ fitMode = "width";
6228
+ scale = calculateFitScale("width");
5406
6229
  rotation = 0;
5407
6230
  applyTransform();
5408
6231
  },
@@ -5418,23 +6241,45 @@ var OpenDocumentPlugin = class {
5418
6241
  getPageCount: () => totalPages,
5419
6242
  getCurrentPage: () => currentPage,
5420
6243
  getThumbnails: async () => {
5421
- if (!isPresentation) return [];
5422
- return slides.map((_, idx) => ({
5423
- index: idx,
5424
- label: `Slide ${idx + 1}`,
5425
- render: async (canvas) => {
5426
- const ctx2d = canvas.getContext("2d");
5427
- if (!ctx2d) return;
5428
- canvas.width = 160;
5429
- canvas.height = 90;
5430
- ctx2d.fillStyle = "#f8fafc";
5431
- ctx2d.fillRect(0, 0, 160, 90);
5432
- ctx2d.fillStyle = "#334155";
5433
- ctx2d.font = "bold 12px sans-serif";
5434
- ctx2d.textAlign = "center";
5435
- ctx2d.fillText(`Slide ${idx + 1}`, 80, 50);
5436
- }
5437
- }));
6244
+ const count = totalPages || slides.length || 1;
6245
+ const items = [];
6246
+ for (let idx = 0; idx < count; idx++) {
6247
+ const itemIdx = idx;
6248
+ items.push({
6249
+ index: itemIdx,
6250
+ label: isPresentation ? `Slide ${itemIdx + 1}` : `Page ${itemIdx + 1}`,
6251
+ render: async (canvas) => {
6252
+ const ctx2d = canvas.getContext("2d");
6253
+ if (!ctx2d) return;
6254
+ if (isPresentation) {
6255
+ canvas.width = 160;
6256
+ canvas.height = 90;
6257
+ ctx2d.fillStyle = "#f8fafc";
6258
+ ctx2d.fillRect(0, 0, 160, 90);
6259
+ ctx2d.strokeStyle = "#cbd5e1";
6260
+ ctx2d.lineWidth = 1;
6261
+ ctx2d.strokeRect(0.5, 0.5, 159, 89);
6262
+ ctx2d.fillStyle = "#334155";
6263
+ ctx2d.font = "bold 11px sans-serif";
6264
+ ctx2d.textAlign = "center";
6265
+ ctx2d.fillText(`Slide ${itemIdx + 1}`, 80, 50);
6266
+ } else {
6267
+ canvas.width = 140;
6268
+ canvas.height = 180;
6269
+ ctx2d.fillStyle = "#ffffff";
6270
+ ctx2d.fillRect(0, 0, 140, 180);
6271
+ ctx2d.strokeStyle = "#cbd5e1";
6272
+ ctx2d.lineWidth = 1;
6273
+ ctx2d.strokeRect(0.5, 0.5, 139, 179);
6274
+ ctx2d.fillStyle = "#64748b";
6275
+ ctx2d.font = "bold 10px sans-serif";
6276
+ ctx2d.textAlign = "center";
6277
+ ctx2d.fillText(`Page ${itemIdx + 1}`, 70, 90);
6278
+ }
6279
+ }
6280
+ });
6281
+ }
6282
+ return items;
5438
6283
  },
5439
6284
  download: () => {
5440
6285
  const mimeType = this.mimeTypes.find((m) => m.includes(ext.replace(".", ""))) || "application/octet-stream";
@@ -5539,23 +6384,24 @@ var OpenDocumentPlugin = class {
5539
6384
  case "h": {
5540
6385
  const level = Math.min(6, Math.max(1, Number(node.getAttribute("text:outline-level") || 1)));
5541
6386
  const style = styles.get(node.getAttribute("text:style-name") || "") || "";
5542
- html += `<h${level} style="${style}; font-family: -apple-system, BlinkMacSystemFont, sans-serif;">${node.textContent || ""}</h${level}>`;
6387
+ html += `<h${level} style="${style}; margin: 18px 0 8px; color: #1e3a8a; font-family: Calibri, 'Segoe UI', Arial, sans-serif;">${node.textContent || ""}</h${level}>`;
5543
6388
  break;
5544
6389
  }
5545
6390
  case "p": {
5546
6391
  const style = styles.get(node.getAttribute("text:style-name") || "") || "";
5547
6392
  const inner = this.renderSpans(node, styles, images);
5548
- html += `<p style="${style}; line-height: 1.6; margin: 8px 0; font-family: -apple-system, BlinkMacSystemFont, sans-serif;">${inner}</p>`;
6393
+ const isInsideLi = node.parentElement?.localName === "list-item" || node.parentElement?.tagName?.toLowerCase() === "list-item";
6394
+ html += `<p style="${style}; line-height: 1.5; margin: ${isInsideLi ? "0" : "8px 0 12px"}; font-family: Calibri, 'Segoe UI', Arial, sans-serif;">${inner}</p>`;
5549
6395
  break;
5550
6396
  }
5551
6397
  case "list": {
5552
- html += '<ul style="margin: 8px 0; padding-left: 24px;">';
6398
+ html += '<ul style="margin: 8px 0 12px; padding-left: 24px;">';
5553
6399
  Array.from(node.children).forEach((c) => walk(c));
5554
6400
  html += "</ul>";
5555
6401
  break;
5556
6402
  }
5557
6403
  case "list-item": {
5558
- html += "<li>";
6404
+ html += '<li style="margin: 4px 0; font-size: 11pt; line-height: 1.5; color: #1e293b;">';
5559
6405
  Array.from(node.children).forEach((c) => walk(c));
5560
6406
  html += "</li>";
5561
6407
  break;
@@ -5681,28 +6527,26 @@ var DocPlugin = class {
5681
6527
  getToolbarActions(instance) {
5682
6528
  const totalPages = instance.getPageCount?.() ?? 1;
5683
6529
  const actions = [];
5684
- if (totalPages > 1) {
5685
- actions.push({
5686
- id: "page-nav",
5687
- icon: "",
5688
- label: "Page Navigation",
5689
- type: "page-nav",
5690
- group: "navigation",
5691
- value: instance.getCurrentPage?.() ?? 1,
5692
- max: totalPages,
5693
- execute: (action, page) => {
5694
- const cur = instance.getCurrentPage?.() ?? 1;
5695
- const max = instance.getPageCount?.() ?? 1;
5696
- if (action === "prev") {
5697
- if (cur > 1) instance.goToPage?.(cur - 1);
5698
- } else if (action === "next") {
5699
- if (cur < max) instance.goToPage?.(cur + 1);
5700
- } else if (typeof page === "number") {
5701
- instance.goToPage?.(page);
5702
- }
6530
+ actions.push({
6531
+ id: "page-nav",
6532
+ icon: "",
6533
+ label: "Page Navigation",
6534
+ type: "page-nav",
6535
+ group: "navigation",
6536
+ value: instance.getCurrentPage?.() ?? 1,
6537
+ max: totalPages,
6538
+ execute: (action, page) => {
6539
+ const cur = instance.getCurrentPage?.() ?? 1;
6540
+ const max = instance.getPageCount?.() ?? 1;
6541
+ if (action === "prev") {
6542
+ if (cur > 1) instance.goToPage?.(cur - 1);
6543
+ } else if (action === "next") {
6544
+ if (cur < max) instance.goToPage?.(cur + 1);
6545
+ } else if (typeof page === "number") {
6546
+ instance.goToPage?.(page);
5703
6547
  }
5704
- });
5705
- }
6548
+ }
6549
+ });
5706
6550
  actions.push(
5707
6551
  {
5708
6552
  id: "zoom-out",
@@ -5776,8 +6620,9 @@ var DocPlugin = class {
5776
6620
  container.className = "fp-doc-container";
5777
6621
  container.style.width = "100%";
5778
6622
  container.style.height = "100%";
5779
- container.style.overflow = "auto";
5780
- container.style.padding = "32px 16px";
6623
+ container.style.overflowX = "hidden";
6624
+ container.style.overflowY = "auto";
6625
+ container.style.padding = "16px 8px";
5781
6626
  container.style.backgroundColor = "#f1f5f9";
5782
6627
  container.style.display = "flex";
5783
6628
  container.style.justifyContent = "center";
@@ -5822,16 +6667,15 @@ var DocPlugin = class {
5822
6667
  const pageCard = document.createElement("div");
5823
6668
  pageCard.className = "fp-doc-page-card";
5824
6669
  pageCard.style.width = "816px";
5825
- pageCard.style.height = "1056px";
6670
+ pageCard.style.minHeight = "1056px";
5826
6671
  pageCard.style.backgroundColor = "#ffffff";
5827
- pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
6672
+ pageCard.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
5828
6673
  pageCard.style.borderRadius = "4px";
5829
- pageCard.style.padding = "96px 72px";
6674
+ pageCard.style.padding = "72px 56px";
5830
6675
  pageCard.style.boxSizing = "border-box";
5831
- pageCard.style.overflow = "hidden";
5832
6676
  pageCard.style.display = i === 0 ? "block" : "none";
5833
6677
  pageCard.style.transformOrigin = "top center";
5834
- pageCard.style.transition = "transform 0.2s ease";
6678
+ pageCard.style.transition = "transform 0.15s ease";
5835
6679
  pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
5836
6680
  pageCard.style.color = "#1e293b";
5837
6681
  if (isFallback && i === 0) {
@@ -5848,34 +6692,15 @@ var DocPlugin = class {
5848
6692
  container.appendChild(pageCard);
5849
6693
  pageCards.push(pageCard);
5850
6694
  }
5851
- let indicator = null;
5852
- if (totalPages > 1) {
5853
- indicator = document.createElement("div");
5854
- indicator.className = "fp-doc-page-indicator";
5855
- indicator.style.position = "fixed";
5856
- indicator.style.bottom = "16px";
5857
- indicator.style.left = "50%";
5858
- indicator.style.transform = "translateX(-50%)";
5859
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
5860
- indicator.style.backdropFilter = "blur(8px)";
5861
- indicator.style.color = "#f8fafc";
5862
- indicator.style.fontSize = "12px";
5863
- indicator.style.fontWeight = "600";
5864
- indicator.style.padding = "5px 14px";
5865
- indicator.style.borderRadius = "20px";
5866
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
5867
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
5868
- indicator.style.zIndex = "10";
5869
- indicator.style.userSelect = "none";
5870
- indicator.style.pointerEvents = "none";
5871
- indicator.style.textAlign = "center";
5872
- container.appendChild(indicator);
5873
- }
5874
6695
  let rotation = 0;
5875
6696
  const applyTransform = () => {
5876
6697
  const activeCard = pageCards[currentPage - 1];
5877
6698
  if (activeCard) {
5878
6699
  activeCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
6700
+ const baseH = activeCard.offsetHeight || 1056;
6701
+ const scaledH = baseH * scale;
6702
+ const extraH = Math.max(0, scaledH - baseH);
6703
+ activeCard.style.marginBottom = `${extraH + 32}px`;
5879
6704
  }
5880
6705
  };
5881
6706
  const showPage = (pageNum) => {
@@ -5888,35 +6713,39 @@ var DocPlugin = class {
5888
6713
  card.style.display = "none";
5889
6714
  }
5890
6715
  });
5891
- if (indicator) {
5892
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
5893
- }
5894
6716
  container.scrollTop = 0;
5895
6717
  ctx.emit("page-change", { page: currentPage, total: totalPages });
5896
6718
  };
5897
6719
  if (totalPages > 1) {
5898
6720
  showPage(1);
5899
6721
  }
5900
- let fitMode = "width";
6722
+ let fitMode = ctx?.options?.fitMode || "width";
6723
+ let isUserZoomed = false;
5901
6724
  const calculateFitScale = (mode = fitMode) => {
5902
6725
  const elW = 816;
5903
6726
  const elH = 1056;
5904
- const availW = Math.max(280, ctx.container.clientWidth - 48);
5905
- const availH = Math.max(280, ctx.container.clientHeight - 80);
6727
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
6728
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
5906
6729
  const sW = availW / elW;
5907
6730
  const sH = availH / elH;
5908
6731
  if (mode === "page") {
5909
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
6732
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
5910
6733
  }
5911
- return Math.max(0.65, Math.min(1.05, sW));
6734
+ return Math.max(0.4, Math.min(3, sW));
5912
6735
  };
6736
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
6737
+ if (!isUserZoomed) {
6738
+ scale = calculateFitScale(fitMode);
6739
+ applyTransform();
6740
+ }
6741
+ }) : null;
6742
+ resizeObserver?.observe(ctx.container);
5913
6743
  setTimeout(() => {
5914
- fitMode = "width";
5915
- scale = calculateFitScale("width");
6744
+ scale = calculateFitScale(fitMode);
5916
6745
  applyTransform();
5917
6746
  }, 60);
5918
6747
  const cleanup = () => {
5919
- indicator?.remove();
6748
+ resizeObserver?.disconnect();
5920
6749
  container.remove();
5921
6750
  ctx.container.innerHTML = "";
5922
6751
  };
@@ -5927,21 +6756,25 @@ var DocPlugin = class {
5927
6756
  getCurrentPage: () => currentPage,
5928
6757
  goToPage: (page) => showPage(page),
5929
6758
  zoomIn: () => {
6759
+ isUserZoomed = true;
5930
6760
  scale += 0.15;
5931
6761
  applyTransform();
5932
6762
  },
5933
6763
  zoomOut: () => {
6764
+ isUserZoomed = true;
5934
6765
  scale = Math.max(0.2, scale - 0.15);
5935
6766
  applyTransform();
5936
6767
  },
5937
6768
  getZoom: () => scale,
5938
6769
  setZoom: (level) => {
6770
+ isUserZoomed = true;
5939
6771
  scale = level;
5940
6772
  applyTransform();
5941
6773
  },
5942
6774
  fitToPage: () => {
5943
- fitMode = fitMode === "width" ? "page" : "width";
5944
- scale = calculateFitScale(fitMode);
6775
+ isUserZoomed = false;
6776
+ fitMode = "width";
6777
+ scale = calculateFitScale("width");
5945
6778
  rotation = 0;
5946
6779
  applyTransform();
5947
6780
  },
@@ -6318,25 +7151,31 @@ ${chartSvg}
6318
7151
  i++;
6319
7152
  continue;
6320
7153
  }
6321
- if (line.length < 60 && !line.endsWith(".") && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#"))) {
7154
+ if (/^\d{1,2}$/.test(line.trim())) {
7155
+ i++;
7156
+ continue;
7157
+ }
7158
+ const isShortLine = line.length < 60 && !line.endsWith(".") && !line.endsWith(",");
7159
+ 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));
7160
+ if (isTitleLike) {
6322
7161
  if (inList) {
6323
7162
  html += "</ul>";
6324
7163
  inList = false;
6325
7164
  }
6326
- 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>`;
7165
+ 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>`;
6327
7166
  } else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
6328
7167
  if (!inList) {
6329
- html += '<ul style="margin: 8px 0; padding-left: 24px;">';
7168
+ html += '<ul style="margin: 8px 0 12px; padding-left: 24px;">';
6330
7169
  inList = true;
6331
7170
  }
6332
7171
  const bulletText = line.replace(/^[•\-\*]\s*/, "");
6333
- html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6__default.default.sanitize(bulletText, sanitizeOptions)}</li>`;
7172
+ html += `<li style="margin: 4px 0; line-height: 1.5; font-size: 11pt; color: #1e293b;">${DOMPurify6__default.default.sanitize(bulletText, sanitizeOptions)}</li>`;
6334
7173
  } else {
6335
7174
  if (inList) {
6336
7175
  html += "</ul>";
6337
7176
  inList = false;
6338
7177
  }
6339
- html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6__default.default.sanitize(line, sanitizeOptions)}</p>`;
7178
+ 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>`;
6340
7179
  }
6341
7180
  i++;
6342
7181
  }