@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.js CHANGED
@@ -578,8 +578,7 @@ var ICON_FULLSCREEN = `<svg width="24" height="24" viewBox="0 0 24 24" fill="non
578
578
  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>`;
579
579
  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>`;
580
580
  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>`;
581
- 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>`;
582
- 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>`;
581
+ 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>`;
583
582
  var ICON_MAP = {
584
583
  "zoom-in": ICON_ZOOM_IN,
585
584
  "zoom-out": ICON_ZOOM_OUT,
@@ -606,28 +605,177 @@ var ICON_MAP = {
606
605
  "forward-10": ICON_FAST_FORWARD,
607
606
  "rewind": ICON_REWIND,
608
607
  "replay-10": ICON_REWIND,
609
- "speed": ICON_SPEED,
610
608
  "open-window": ICON_EXTERNAL_WINDOW,
611
- "external-window": ICON_EXTERNAL_WINDOW
609
+ "external-window": ICON_EXTERNAL_WINDOW,
610
+ "openWindow": ICON_EXTERNAL_WINDOW,
611
+ "openSeparateWindow": ICON_EXTERNAL_WINDOW
612
612
  };
613
613
  var ToolbarController = class {
614
614
  el;
615
615
  toolbarEl;
616
616
  actions = [];
617
+ config = {};
617
618
  pageInputEl = null;
618
619
  pageLabelEl = null;
619
- constructor(container) {
620
+ prevPageBtn = null;
621
+ nextPageBtn = null;
622
+ constructor(container, config) {
620
623
  this.el = container;
624
+ if (config) this.config = { ...config };
621
625
  this.toolbarEl = createElement("div", { className: "fp-toolbar" });
622
626
  this.el.appendChild(this.toolbarEl);
623
627
  }
628
+ setConfig(config) {
629
+ this.config = { ...config };
630
+ this.render();
631
+ }
632
+ getConfig() {
633
+ return { ...this.config };
634
+ }
635
+ hideAction(actionId) {
636
+ this.config[actionId] = false;
637
+ if (actionId === "zoom-in" || actionId === "zoomIn") this.config.zoomIn = false;
638
+ if (actionId === "zoom-out" || actionId === "zoomOut") this.config.zoomOut = false;
639
+ if (actionId === "fit-page" || actionId === "fitPage" || actionId === "fitToPage" || actionId === "fit-width") {
640
+ this.config.fitPage = false;
641
+ this.config.fitToPage = false;
642
+ this.config.fitWidth = false;
643
+ }
644
+ if (actionId === "rotate-cw" || actionId === "rotate" || actionId === "rotateCW") this.config.rotate = false;
645
+ if (actionId === "fullscreen") this.config.fullscreen = false;
646
+ if (actionId === "download") this.config.download = false;
647
+ if (actionId === "print") this.config.print = false;
648
+ if (actionId === "open-window" || actionId === "openWindow" || actionId === "openSeparateWindow") {
649
+ this.config.openWindow = false;
650
+ this.config.openSeparateWindow = false;
651
+ }
652
+ if (actionId === "copy") this.config.copy = false;
653
+ if (actionId === "page-nav" || actionId === "pageNav" || actionId === "pagination") {
654
+ this.config.pageNav = false;
655
+ this.config.pagination = false;
656
+ }
657
+ this.render();
658
+ }
659
+ showAction(actionId) {
660
+ this.config[actionId] = true;
661
+ if (actionId === "zoom-in" || actionId === "zoomIn") this.config.zoomIn = true;
662
+ if (actionId === "zoom-out" || actionId === "zoomOut") this.config.zoomOut = true;
663
+ if (actionId === "fit-page" || actionId === "fitPage" || actionId === "fitToPage" || actionId === "fit-width") {
664
+ this.config.fitPage = true;
665
+ this.config.fitToPage = true;
666
+ this.config.fitWidth = true;
667
+ }
668
+ if (actionId === "rotate-cw" || actionId === "rotate" || actionId === "rotateCW") this.config.rotate = true;
669
+ if (actionId === "fullscreen") this.config.fullscreen = true;
670
+ if (actionId === "download") this.config.download = true;
671
+ if (actionId === "print") this.config.print = true;
672
+ if (actionId === "open-window" || actionId === "openWindow" || actionId === "openSeparateWindow") {
673
+ this.config.openWindow = true;
674
+ this.config.openSeparateWindow = true;
675
+ }
676
+ if (actionId === "copy") this.config.copy = true;
677
+ if (actionId === "page-nav" || actionId === "pageNav" || actionId === "pagination") {
678
+ this.config.pageNav = true;
679
+ this.config.pagination = true;
680
+ }
681
+ this.render();
682
+ }
683
+ normalizeActionId(actionId) {
684
+ if (actionId === "zoomIn") return "zoom-in";
685
+ if (actionId === "zoomOut") return "zoom-out";
686
+ if (actionId === "fitPage" || actionId === "fitToPage" || actionId === "fitWidth") return "fit-page";
687
+ if (actionId === "rotateCW") return "rotate-cw";
688
+ if (actionId === "rotateCCW") return "rotate-ccw";
689
+ if (actionId === "openWindow" || actionId === "openSeparateWindow") return "open-window";
690
+ if (actionId === "pageNav") return "page-nav";
691
+ return actionId;
692
+ }
693
+ enableAction(actionId) {
694
+ const norm = this.normalizeActionId(actionId);
695
+ const btn = this.toolbarEl.querySelector(`button[data-action-id="${actionId}"], button[data-action-id="${norm}"]`);
696
+ if (btn) {
697
+ btn.disabled = false;
698
+ btn.classList.remove("disabled");
699
+ }
700
+ }
701
+ disableAction(actionId) {
702
+ const norm = this.normalizeActionId(actionId);
703
+ const btn = this.toolbarEl.querySelector(`button[data-action-id="${actionId}"], button[data-action-id="${norm}"]`);
704
+ if (btn) {
705
+ btn.disabled = true;
706
+ btn.classList.add("disabled");
707
+ }
708
+ }
709
+ setActionActive(actionId, active) {
710
+ const norm = this.normalizeActionId(actionId);
711
+ const btn = this.toolbarEl.querySelector(`button[data-action-id="${actionId}"], button[data-action-id="${norm}"]`);
712
+ if (btn) {
713
+ if (active) {
714
+ btn.classList.add("active");
715
+ } else {
716
+ btn.classList.remove("active");
717
+ }
718
+ }
719
+ }
720
+ isActionEnabled(action) {
721
+ const c = this.config;
722
+ const id = action.id;
723
+ if (c[id] === false) return false;
724
+ if (id === "zoom-in" && (c.zoomIn === false || c["zoom-in"] === false)) return false;
725
+ if (id === "zoom-out" && (c.zoomOut === false || c["zoom-out"] === false)) return false;
726
+ 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)) {
727
+ return false;
728
+ }
729
+ if ((id === "rotate-cw" || id === "rotate-ccw" || id === "rotate") && (c.rotate === false || c.rotateCW === false || c.rotateCCW === false || c["rotate-cw"] === false)) {
730
+ return false;
731
+ }
732
+ if (id === "fullscreen" && c.fullscreen === false) return false;
733
+ if (id === "download" && c.download === false) return false;
734
+ if (id === "print" && c.print === false) return false;
735
+ if ((id === "open-window" || id === "external-window") && (c.openWindow === false || c.openSeparateWindow === false || c["open-window"] === false)) {
736
+ return false;
737
+ }
738
+ if (id === "copy" && c.copy === false) return false;
739
+ if (id === "thumbnails" && c.thumbnails === false) return false;
740
+ if (id === "page-nav" && (c.pageNav === false || c.pagination === false || c["page-nav"] === false)) {
741
+ return false;
742
+ }
743
+ if ((id === "prev" || id === "page-prev") && (c.prevPage === false || c.prev === false || c["prev"] === false || c.pageNav === false || c.pagination === false)) {
744
+ return false;
745
+ }
746
+ if ((id === "next" || id === "page-next") && (c.nextPage === false || c.next === false || c["next"] === false || c.pageNav === false || c.pagination === false)) {
747
+ return false;
748
+ }
749
+ if (id === "play" && c.play === false) return false;
750
+ if (id === "pause" && c.pause === false) return false;
751
+ if ((id === "fast-forward" || id === "forward-10") && (c.fastForward === false || c["fast-forward"] === false)) {
752
+ return false;
753
+ }
754
+ if ((id === "rewind" || id === "replay-10") && (c.rewind === false || c["rewind"] === false)) {
755
+ return false;
756
+ }
757
+ if (id === "speed" && c.speed === false) return false;
758
+ return true;
759
+ }
624
760
  setPage(page, max) {
625
761
  if (this.pageInputEl) {
626
762
  this.pageInputEl.value = page.toString();
763
+ if (max !== void 0) {
764
+ this.pageInputEl.max = max.toString();
765
+ }
627
766
  }
628
767
  if (max !== void 0 && this.pageLabelEl) {
629
768
  this.pageLabelEl.textContent = ` / ${max}`;
630
769
  }
770
+ const currentMax = max !== void 0 ? max : parseInt(this.pageInputEl?.max || "1", 10) || 1;
771
+ if (this.prevPageBtn) {
772
+ this.prevPageBtn.disabled = page <= 1;
773
+ this.prevPageBtn.style.opacity = page <= 1 ? "0.4" : "1";
774
+ }
775
+ if (this.nextPageBtn) {
776
+ this.nextPageBtn.disabled = page >= currentMax;
777
+ this.nextPageBtn.style.opacity = page >= currentMax ? "0.4" : "1";
778
+ }
631
779
  }
632
780
  update(actions) {
633
781
  this.actions = actions;
@@ -650,8 +798,9 @@ var ToolbarController = class {
650
798
  view: [],
651
799
  actions: []
652
800
  };
653
- const hasPageNav = this.actions.some((a) => a.type === "page-nav");
654
- const effectiveActions = hasPageNav ? this.actions.filter((a) => a.id !== "page-prev" && a.id !== "page-next" && a.id !== "prev" && a.id !== "next") : this.actions;
801
+ const enabledActions = this.actions.filter((a) => this.isActionEnabled(a));
802
+ const hasPageNav = enabledActions.some((a) => a.type === "page-nav");
803
+ const effectiveActions = hasPageNav ? enabledActions.filter((a) => a.id !== "page-prev" && a.id !== "page-next" && a.id !== "prev" && a.id !== "next") : enabledActions;
655
804
  for (const action of effectiveActions) {
656
805
  if (groups[action.group]) {
657
806
  groups[action.group].push(action);
@@ -711,6 +860,13 @@ var ToolbarController = class {
711
860
  action.execute("go", val);
712
861
  });
713
862
  const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${max}`);
863
+ const curVal = action.value ?? 1;
864
+ prevBtn.disabled = curVal <= 1;
865
+ prevBtn.style.opacity = curVal <= 1 ? "0.4" : "1";
866
+ nextBtn.disabled = curVal >= max;
867
+ nextBtn.style.opacity = curVal >= max ? "0.4" : "1";
868
+ this.prevPageBtn = prevBtn;
869
+ this.nextPageBtn = nextBtn;
714
870
  this.pageInputEl = input;
715
871
  this.pageLabelEl = label;
716
872
  groupEl.appendChild(prevBtn);
@@ -766,25 +922,63 @@ var ToolbarController = class {
766
922
  var ThumbnailPanel = class {
767
923
  el;
768
924
  panelEl;
925
+ listEl;
926
+ headerEl;
769
927
  thumbnails = [];
770
928
  onSelect;
929
+ onToggleCallback;
771
930
  activeIndex = 0;
772
- constructor(container) {
931
+ observer;
932
+ renderedIndices = /* @__PURE__ */ new Set();
933
+ constructor(container, onToggle) {
773
934
  this.el = container;
935
+ this.onToggleCallback = onToggle;
774
936
  this.panelEl = createElement("div", { className: "fp-thumbnail-panel hidden" });
937
+ this.panelEl.style.width = "0px";
938
+ this.panelEl.style.minWidth = "0px";
939
+ this.panelEl.style.opacity = "0";
940
+ this.headerEl = createElement("div", { className: "fp-thumbnail-header" });
941
+ const title = createElement("span", { className: "fp-thumbnail-title" }, "Pages");
942
+ const closeBtn = createElement("button", {
943
+ className: "fp-thumbnail-close-btn",
944
+ title: "Close Thumbnails",
945
+ type: "button"
946
+ });
947
+ closeBtn.innerHTML = ICON_CLOSE;
948
+ closeBtn.addEventListener("click", (e) => {
949
+ e.stopPropagation();
950
+ this.hide();
951
+ });
952
+ this.headerEl.appendChild(title);
953
+ this.headerEl.appendChild(closeBtn);
954
+ this.listEl = createElement("div", { className: "fp-thumbnail-list" });
955
+ this.panelEl.appendChild(this.headerEl);
956
+ this.panelEl.appendChild(this.listEl);
775
957
  this.el.appendChild(this.panelEl);
776
958
  }
959
+ isOpen() {
960
+ return !this.panelEl.classList.contains("hidden") && this.panelEl.style.width !== "0px";
961
+ }
777
962
  update(thumbnails, onSelect) {
778
963
  this.thumbnails = thumbnails;
779
964
  this.onSelect = onSelect;
965
+ this.renderedIndices.clear();
966
+ const titleEl = this.headerEl.querySelector(".fp-thumbnail-title");
967
+ if (titleEl) {
968
+ titleEl.textContent = `Pages (${thumbnails.length})`;
969
+ }
780
970
  this.render();
971
+ if (this.isOpen()) {
972
+ this.renderAllVisible();
973
+ }
781
974
  }
782
975
  setActive(index) {
783
976
  this.activeIndex = index;
784
- const items = this.panelEl.querySelectorAll(".fp-thumbnail-item");
977
+ const items = this.listEl.querySelectorAll(".fp-thumbnail-item");
785
978
  items.forEach((item, i) => {
786
979
  if (i === index) {
787
980
  item.classList.add("active");
981
+ item.scrollIntoView({ block: "nearest", behavior: "smooth" });
788
982
  } else {
789
983
  item.classList.remove("active");
790
984
  }
@@ -792,37 +986,102 @@ var ThumbnailPanel = class {
792
986
  }
793
987
  show() {
794
988
  this.panelEl.classList.remove("hidden");
989
+ this.panelEl.style.width = "200px";
990
+ this.panelEl.style.minWidth = "200px";
991
+ this.panelEl.style.opacity = "1";
992
+ this.panelEl.style.display = "flex";
993
+ this.onToggleCallback?.(true);
994
+ this.renderAllVisible();
795
995
  }
796
996
  hide() {
797
997
  this.panelEl.classList.add("hidden");
998
+ this.panelEl.style.width = "0px";
999
+ this.panelEl.style.minWidth = "0px";
1000
+ this.panelEl.style.opacity = "0";
1001
+ this.onToggleCallback?.(false);
798
1002
  }
799
1003
  toggle() {
800
- this.panelEl.classList.toggle("hidden");
1004
+ if (this.isOpen()) {
1005
+ this.hide();
1006
+ } else {
1007
+ this.show();
1008
+ }
801
1009
  }
802
1010
  destroy() {
1011
+ if (this.observer) {
1012
+ this.observer.disconnect();
1013
+ this.observer = void 0;
1014
+ }
1015
+ this.renderedIndices.clear();
803
1016
  this.el.innerHTML = "";
804
1017
  }
805
- async render() {
806
- this.panelEl.innerHTML = "";
1018
+ renderAllVisible() {
1019
+ const items = this.listEl.querySelectorAll(".fp-thumbnail-item");
1020
+ items.forEach((item) => {
1021
+ const idx = parseInt(item.dataset.thumbIndex || "-1", 10);
1022
+ if (idx >= 0 && !this.renderedIndices.has(idx)) {
1023
+ this.renderThumbnailItem(idx, item);
1024
+ }
1025
+ });
1026
+ }
1027
+ renderThumbnailItem(idx, itemEl) {
1028
+ if (this.renderedIndices.has(idx)) return;
1029
+ this.renderedIndices.add(idx);
1030
+ const canvas = itemEl.querySelector("canvas");
1031
+ const thumb = this.thumbnails[idx];
1032
+ if (canvas && thumb) {
1033
+ Promise.resolve(thumb.render(canvas)).catch((err) => {
1034
+ console.warn(`[FilePreview] Error rendering thumbnail ${idx}:`, err);
1035
+ });
1036
+ }
1037
+ }
1038
+ render() {
1039
+ if (this.observer) {
1040
+ this.observer.disconnect();
1041
+ }
1042
+ this.listEl.innerHTML = "";
1043
+ const useObserver = typeof IntersectionObserver !== "undefined";
1044
+ if (useObserver) {
1045
+ this.observer = new IntersectionObserver(
1046
+ (entries) => {
1047
+ for (const entry of entries) {
1048
+ if (entry.isIntersecting) {
1049
+ const target = entry.target;
1050
+ const idx = parseInt(target.dataset.thumbIndex || "-1", 10);
1051
+ if (idx >= 0) {
1052
+ this.renderThumbnailItem(idx, target);
1053
+ }
1054
+ }
1055
+ }
1056
+ },
1057
+ {
1058
+ root: this.listEl,
1059
+ rootMargin: "120px 0px 120px 0px"
1060
+ }
1061
+ );
1062
+ }
807
1063
  for (let i = 0; i < this.thumbnails.length; i++) {
808
1064
  const thumb = this.thumbnails[i];
809
- const itemEl = createElement("div", { className: "fp-thumbnail-item" });
810
- if (i === this.activeIndex) {
811
- itemEl.classList.add("active");
812
- }
1065
+ const itemEl = createElement("div", {
1066
+ className: "fp-thumbnail-item" + (i === this.activeIndex ? " active" : "")
1067
+ });
1068
+ itemEl.dataset.thumbIndex = i.toString();
813
1069
  itemEl.addEventListener("click", () => {
814
1070
  this.setActive(i);
815
1071
  this.onSelect?.(i);
816
1072
  });
817
- const canvas = createElement("canvas", { width: "150", height: "200" });
818
- const label = createElement("span", { className: "fp-thumbnail-label" }, thumb.label);
819
- itemEl.appendChild(canvas);
1073
+ const canvasWrapper = createElement("div", { className: "fp-thumbnail-canvas-wrapper" });
1074
+ const canvas = createElement("canvas", { width: "130", height: "170" });
1075
+ canvasWrapper.appendChild(canvas);
1076
+ const label = createElement("span", { className: "fp-thumbnail-label" }, thumb.label || `Page ${i + 1}`);
1077
+ itemEl.appendChild(canvasWrapper);
820
1078
  itemEl.appendChild(label);
821
- this.panelEl.appendChild(itemEl);
822
- try {
823
- await thumb.render(canvas);
824
- } catch (err) {
825
- console.error(`[FilePreview] Error rendering thumbnail ${i}:`, err);
1079
+ this.listEl.appendChild(itemEl);
1080
+ if (this.observer) {
1081
+ this.observer.observe(itemEl);
1082
+ }
1083
+ if (i < 4) {
1084
+ this.renderThumbnailItem(i, itemEl);
826
1085
  }
827
1086
  }
828
1087
  }
@@ -843,6 +1102,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
843
1102
  currentOptions = {};
844
1103
  resizeObserver = null;
845
1104
  fullscreenHandler = null;
1105
+ titleBarEl = null;
846
1106
  /**
847
1107
  * Register a preview plugin.
848
1108
  */
@@ -879,6 +1139,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
879
1139
  }
880
1140
  this.currentBuffer = buffer.slice(0);
881
1141
  this.currentMetadata = metadata;
1142
+ this.updateTitleBar(options, metadata);
882
1143
  if (signal.aborted) throw new DOMException("Aborted", "AbortError");
883
1144
  const fileInfo = { metadata, buffer };
884
1145
  const matchedPlugin = await this.findPlugin(fileInfo);
@@ -902,15 +1163,21 @@ var FilePreviewViewer = class _FilePreviewViewer {
902
1163
  if (event === "page-change" && payload && typeof payload.page === "number") {
903
1164
  const total = payload.total ?? payload.totalPages;
904
1165
  this.toolbar?.setPage(payload.page, total);
1166
+ this.thumbnailPanel?.setActive(payload.page - 1);
905
1167
  }
906
1168
  this.eventEmitter.emit(event, payload);
907
- }
1169
+ },
1170
+ toggleThumbnails: () => this.toggleThumbnails()
908
1171
  });
909
1172
  this.activeInstance = instance;
910
1173
  instance.openInSeparateWindow = () => this.openInSeparateWindow();
1174
+ instance.toggleThumbnails = () => this.toggleThumbnails();
911
1175
  this.hideLoading();
912
1176
  this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
913
- if (options.showToolbar !== false && this.toolbar) {
1177
+ const isToolbarVisible = options.showToolbar !== false && options.toolbar !== false;
1178
+ if (isToolbarVisible && this.toolbar) {
1179
+ const toolbarConfig = this.extractToolbarConfig(options);
1180
+ this.toolbar.setConfig(toolbarConfig);
914
1181
  const actions = matchedPlugin.getToolbarActions(instance);
915
1182
  const hasFullscreen = actions.some((a) => a.id === "fullscreen");
916
1183
  if (!hasFullscreen && this.wrapperEl) {
@@ -960,6 +1227,12 @@ var FilePreviewViewer = class _FilePreviewViewer {
960
1227
  }
961
1228
  });
962
1229
  }
1230
+ const thumbAction = actions.find((a) => a.id === "thumbnails");
1231
+ if (thumbAction) {
1232
+ thumbAction.execute = () => {
1233
+ this.toggleThumbnails();
1234
+ };
1235
+ }
963
1236
  this.toolbar.update(actions);
964
1237
  this.toolbar.show();
965
1238
  }
@@ -1044,7 +1317,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1044
1317
  if (targetUrl) {
1045
1318
  const newWin2 = window.open(targetUrl, "_blank");
1046
1319
  if (!newWin2) {
1047
- alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
1320
+ console.warn("[FilePreviewViewer] Popup blocker prevented opening the preview in a separate window.");
1048
1321
  return null;
1049
1322
  }
1050
1323
  return newWin2;
@@ -1052,7 +1325,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1052
1325
  const title = (this.currentMetadata?.name || "Document Preview") + " - Full Preview";
1053
1326
  const newWin = window.open("", "_blank");
1054
1327
  if (!newWin) {
1055
- alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
1328
+ console.warn("[FilePreviewViewer] Popup blocker prevented opening the preview in a separate window.");
1056
1329
  return null;
1057
1330
  }
1058
1331
  newWin.document.title = title;
@@ -1129,6 +1402,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1129
1402
  this.currentBuffer = null;
1130
1403
  this.currentMetadata = null;
1131
1404
  this.currentContainer = null;
1405
+ this.titleBarEl = null;
1132
1406
  }
1133
1407
  /**
1134
1408
  * Get the currently active preview instance.
@@ -1136,7 +1410,218 @@ var FilePreviewViewer = class _FilePreviewViewer {
1136
1410
  getInstance() {
1137
1411
  return this.activeInstance;
1138
1412
  }
1413
+ // --- Toolbar Configuration & Feature Toggles ---
1414
+ /**
1415
+ * Configure toolbar features dynamically through options or methods.
1416
+ * e.g. viewer.setToolbarConfig({ zoomIn: false, print: false })
1417
+ */
1418
+ setToolbarConfig(config) {
1419
+ this.toolbar?.setConfig(config);
1420
+ }
1421
+ /**
1422
+ * Get current toolbar configuration.
1423
+ */
1424
+ getToolbarConfig() {
1425
+ return this.toolbar?.getConfig() ?? {};
1426
+ }
1427
+ /**
1428
+ * Hide a specific toolbar action by ID or alias (e.g. 'zoom-in', 'print', 'download').
1429
+ */
1430
+ hideToolbarAction(actionId) {
1431
+ this.toolbar?.hideAction(actionId);
1432
+ }
1433
+ /**
1434
+ * Show a specific toolbar action by ID or alias.
1435
+ */
1436
+ showToolbarAction(actionId) {
1437
+ this.toolbar?.showAction(actionId);
1438
+ }
1439
+ /**
1440
+ * Enable a specific toolbar action button.
1441
+ */
1442
+ enableToolbarAction(actionId) {
1443
+ this.toolbar?.enableAction(actionId);
1444
+ }
1445
+ /**
1446
+ * Disable a specific toolbar action button.
1447
+ */
1448
+ disableToolbarAction(actionId) {
1449
+ this.toolbar?.disableAction(actionId);
1450
+ }
1451
+ // --- Programmatic Toolbar Action Methods ---
1452
+ /**
1453
+ * Fit document to page so it fills the frame width with minimal margins.
1454
+ */
1455
+ fitToPage() {
1456
+ this.activeInstance?.fitToPage?.();
1457
+ }
1458
+ /**
1459
+ * Zoom in.
1460
+ */
1461
+ zoomIn() {
1462
+ this.activeInstance?.zoomIn?.();
1463
+ }
1464
+ /**
1465
+ * Zoom out.
1466
+ */
1467
+ zoomOut() {
1468
+ this.activeInstance?.zoomOut?.();
1469
+ }
1470
+ /**
1471
+ * Set specific zoom level.
1472
+ */
1473
+ setZoom(level) {
1474
+ this.activeInstance?.setZoom?.(level);
1475
+ }
1476
+ /**
1477
+ * Get current zoom level.
1478
+ */
1479
+ getZoom() {
1480
+ return this.activeInstance?.getZoom?.() ?? 1;
1481
+ }
1482
+ /**
1483
+ * Rotate 90 degrees clockwise.
1484
+ */
1485
+ rotateCW() {
1486
+ this.activeInstance?.rotateCW?.();
1487
+ }
1488
+ /**
1489
+ * Rotate 90 degrees counter-clockwise.
1490
+ */
1491
+ rotateCCW() {
1492
+ this.activeInstance?.rotateCCW?.();
1493
+ }
1494
+ /**
1495
+ * Navigate to a specific page number.
1496
+ */
1497
+ goToPage(page) {
1498
+ this.activeInstance?.goToPage?.(page);
1499
+ this.toolbar?.setPage(page);
1500
+ }
1501
+ /**
1502
+ * Navigate to next page.
1503
+ */
1504
+ nextPage() {
1505
+ const cur = this.getCurrentPage();
1506
+ const max = this.getPageCount();
1507
+ if (cur < max) {
1508
+ this.goToPage(cur + 1);
1509
+ }
1510
+ }
1511
+ /**
1512
+ * Navigate to previous page.
1513
+ */
1514
+ prevPage() {
1515
+ const cur = this.getCurrentPage();
1516
+ if (cur > 1) {
1517
+ this.goToPage(cur - 1);
1518
+ }
1519
+ }
1520
+ /**
1521
+ * Get total page count.
1522
+ */
1523
+ getPageCount() {
1524
+ return this.activeInstance?.getPageCount?.() ?? 1;
1525
+ }
1526
+ /**
1527
+ * Get current page number.
1528
+ */
1529
+ getCurrentPage() {
1530
+ return this.activeInstance?.getCurrentPage?.() ?? 1;
1531
+ }
1532
+ /**
1533
+ * Download the current document.
1534
+ */
1535
+ download() {
1536
+ this.activeInstance?.download?.();
1537
+ }
1538
+ /**
1539
+ * Print the current document.
1540
+ */
1541
+ print() {
1542
+ this.activeInstance?.print?.();
1543
+ }
1544
+ /**
1545
+ * Toggle fullscreen mode.
1546
+ */
1547
+ toggleFullscreen() {
1548
+ try {
1549
+ if (!document.fullscreenElement) {
1550
+ this.wrapperEl?.requestFullscreen?.();
1551
+ this.wrapperEl?.classList.add("fp-fullscreen-active");
1552
+ } else {
1553
+ document.exitFullscreen?.();
1554
+ this.wrapperEl?.classList.remove("fp-fullscreen-active");
1555
+ }
1556
+ } catch {
1557
+ this.wrapperEl?.classList.toggle("fp-fullscreen-active");
1558
+ }
1559
+ setTimeout(() => {
1560
+ this.activeInstance?.fitToPage?.();
1561
+ }, 120);
1562
+ }
1563
+ /**
1564
+ * Toggle thumbnails sidebar panel.
1565
+ */
1566
+ toggleThumbnails() {
1567
+ if (!this.thumbnailPanel) return;
1568
+ this.thumbnailPanel.toggle();
1569
+ const isOpen = this.thumbnailPanel.isOpen();
1570
+ this.toolbar?.setActionActive("thumbnails", isOpen);
1571
+ setTimeout(() => {
1572
+ this.activeInstance?.fitToPage?.();
1573
+ }, 260);
1574
+ }
1575
+ /**
1576
+ * Set whether to display the file name / title bar above the toolbar.
1577
+ */
1578
+ setShowFileName(show) {
1579
+ if (this.currentOptions) {
1580
+ this.currentOptions.showFileName = show;
1581
+ }
1582
+ this.updateTitleBar(this.currentOptions, this.currentMetadata);
1583
+ }
1584
+ /**
1585
+ * Set or override the displayed file name in the preview panel.
1586
+ */
1587
+ setFileName(name) {
1588
+ if (this.currentOptions) {
1589
+ this.currentOptions.fileName = name;
1590
+ }
1591
+ this.updateTitleBar(this.currentOptions, this.currentMetadata);
1592
+ }
1139
1593
  // --- Private methods ---
1594
+ updateTitleBar(options, metadata) {
1595
+ if (!this.titleBarEl) return;
1596
+ const show = options?.showFileName !== false;
1597
+ if (!show) {
1598
+ this.titleBarEl.style.display = "none";
1599
+ return;
1600
+ }
1601
+ const name = options?.fileName || metadata?.name || this.currentMetadata?.name || "";
1602
+ if (!name) {
1603
+ this.titleBarEl.style.display = "none";
1604
+ return;
1605
+ }
1606
+ this.titleBarEl.style.display = "flex";
1607
+ const ext = metadata?.extension || (name.includes(".") ? "." + name.split(".").pop() : "");
1608
+ const badge = ext ? ext.replace(".", "").toUpperCase() : "";
1609
+ this.titleBarEl.innerHTML = `
1610
+ <div class="fp-titlebar-info">
1611
+ <span class="fp-titlebar-icon">\u{1F4C4}</span>
1612
+ <span class="fp-titlebar-name" title="${name}">${name}</span>
1613
+ ${badge ? `<span class="fp-titlebar-badge">${badge}</span>` : ""}
1614
+ </div>
1615
+ `;
1616
+ }
1617
+ extractToolbarConfig(options) {
1618
+ if (!options) return {};
1619
+ const nested = typeof options.toolbar === "object" ? options.toolbar : {};
1620
+ return {
1621
+ ...options,
1622
+ ...nested
1623
+ };
1624
+ }
1140
1625
  abort() {
1141
1626
  if (this.abortController) {
1142
1627
  this.abortController.abort();
@@ -1179,17 +1664,29 @@ var FilePreviewViewer = class _FilePreviewViewer {
1179
1664
  bodyEl.className = "fp-body";
1180
1665
  bodyEl.appendChild(thumbnailEl);
1181
1666
  bodyEl.appendChild(this.contentEl);
1667
+ const titleBarEl = document.createElement("div");
1668
+ titleBarEl.className = "fp-titlebar";
1669
+ this.titleBarEl = titleBarEl;
1670
+ this.updateTitleBar(options, this.currentMetadata);
1182
1671
  if (toolbarPos === "top") {
1672
+ this.wrapperEl.appendChild(titleBarEl);
1183
1673
  this.wrapperEl.appendChild(toolbarEl);
1184
1674
  this.wrapperEl.appendChild(bodyEl);
1185
1675
  } else {
1676
+ this.wrapperEl.appendChild(titleBarEl);
1186
1677
  this.wrapperEl.appendChild(bodyEl);
1187
1678
  this.wrapperEl.appendChild(toolbarEl);
1188
1679
  }
1189
1680
  container.innerHTML = "";
1190
1681
  container.appendChild(this.wrapperEl);
1191
- this.toolbar = new ToolbarController(toolbarEl);
1192
- this.thumbnailPanel = new ThumbnailPanel(thumbnailEl);
1682
+ const initialToolbarConfig = this.extractToolbarConfig(options);
1683
+ this.toolbar = new ToolbarController(toolbarEl, initialToolbarConfig);
1684
+ this.thumbnailPanel = new ThumbnailPanel(thumbnailEl, (isOpen) => {
1685
+ this.toolbar?.setActionActive("thumbnails", isOpen);
1686
+ setTimeout(() => {
1687
+ this.activeInstance?.fitToPage?.();
1688
+ }, 260);
1689
+ });
1193
1690
  this.setupKeyboardShortcuts();
1194
1691
  this.setupDragAndDrop(container, options);
1195
1692
  this.setupResizeAndFullscreenListeners();
@@ -1579,6 +2076,14 @@ var PdfPlugin = class {
1579
2076
  group: "zoom",
1580
2077
  execute: () => instance.fitToPage?.()
1581
2078
  },
2079
+ {
2080
+ id: "fit-width",
2081
+ icon: "fit-width",
2082
+ label: "Fit to Width",
2083
+ type: "button",
2084
+ group: "zoom",
2085
+ execute: () => instance.fitToWidth?.()
2086
+ },
1582
2087
  {
1583
2088
  id: "rotate-cw",
1584
2089
  icon: "rotate-cw",
@@ -1587,6 +2092,14 @@ var PdfPlugin = class {
1587
2092
  group: "view",
1588
2093
  execute: () => instance.rotateCW?.()
1589
2094
  },
2095
+ {
2096
+ id: "rotate-ccw",
2097
+ icon: "rotate-ccw",
2098
+ label: "Rotate Counter-Clockwise",
2099
+ type: "button",
2100
+ group: "view",
2101
+ execute: () => instance.rotateCCW?.()
2102
+ },
1590
2103
  {
1591
2104
  id: "download",
1592
2105
  icon: "download",
@@ -1618,12 +2131,13 @@ var PdfPlugin = class {
1618
2131
  container.className = "fp-pdf-container";
1619
2132
  container.style.width = "100%";
1620
2133
  container.style.height = "100%";
1621
- container.style.overflow = "auto";
2134
+ container.style.overflowX = "hidden";
2135
+ container.style.overflowY = "auto";
1622
2136
  container.style.display = "flex";
1623
2137
  container.style.flexDirection = "column";
1624
2138
  container.style.alignItems = "center";
1625
2139
  container.style.justifyContent = "flex-start";
1626
- container.style.padding = "20px 16px";
2140
+ container.style.padding = "16px 8px";
1627
2141
  container.style.backgroundColor = "#0f172a";
1628
2142
  container.style.boxSizing = "border-box";
1629
2143
  container.style.position = "relative";
@@ -1634,30 +2148,12 @@ var PdfPlugin = class {
1634
2148
  pageCard.style.borderRadius = "4px";
1635
2149
  pageCard.style.overflow = "hidden";
1636
2150
  pageCard.style.lineHeight = "0";
1637
- pageCard.style.transition = "transform 0.15s ease";
1638
2151
  pageCard.style.position = "relative";
1639
2152
  pageCard.style.flexShrink = "0";
2153
+ pageCard.style.transition = "box-shadow 0.2s ease";
1640
2154
  let canvas = document.createElement("canvas");
1641
2155
  pageCard.appendChild(canvas);
1642
2156
  container.appendChild(pageCard);
1643
- const indicator = document.createElement("div");
1644
- indicator.className = "fp-pdf-page-indicator";
1645
- indicator.style.position = "sticky";
1646
- indicator.style.bottom = "16px";
1647
- indicator.style.marginTop = "16px";
1648
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
1649
- indicator.style.backdropFilter = "blur(8px)";
1650
- indicator.style.color = "#f8fafc";
1651
- indicator.style.fontSize = "12px";
1652
- indicator.style.fontWeight = "600";
1653
- indicator.style.padding = "5px 14px";
1654
- indicator.style.borderRadius = "20px";
1655
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
1656
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
1657
- indicator.style.zIndex = "10";
1658
- indicator.style.userSelect = "none";
1659
- indicator.style.pointerEvents = "none";
1660
- container.appendChild(indicator);
1661
2157
  ctx.container.appendChild(container);
1662
2158
  const standardFontsUrl = typeof window !== "undefined" && window.__PDF_STANDARD_FONTS_URL__ || "./standard_fonts/";
1663
2159
  const cmapsUrl = typeof window !== "undefined" && window.__PDF_CMAPS_URL__ || "./cmaps/";
@@ -1673,8 +2169,9 @@ var PdfPlugin = class {
1673
2169
  let currentPage = 1;
1674
2170
  let zoomScale = 1;
1675
2171
  let rotation = 0;
1676
- let fitMode = "page";
2172
+ let fitMode = ctx?.options?.fitMode || "width";
1677
2173
  let currentRenderTask = null;
2174
+ let textLayerDiv = null;
1678
2175
  const renderPage = async (pageNum) => {
1679
2176
  if (currentRenderTask) {
1680
2177
  try {
@@ -1686,30 +2183,37 @@ var PdfPlugin = class {
1686
2183
  const newCanvas = document.createElement("canvas");
1687
2184
  pageCard.replaceChild(newCanvas, canvas);
1688
2185
  canvas = newCanvas;
2186
+ if (textLayerDiv) {
2187
+ textLayerDiv.remove();
2188
+ textLayerDiv = null;
2189
+ }
1689
2190
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
1690
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
1691
2191
  ctx.emit("page-change", { page: currentPage, total: totalPages });
1692
2192
  const page = await pdfDoc.getPage(currentPage);
1693
2193
  const containerWidth = container.clientWidth || 900;
1694
2194
  const containerHeight = container.clientHeight || 700;
1695
2195
  const unscaledVp = page.getViewport({ scale: 1, rotation });
1696
- const availWidth = Math.max(320, containerWidth - 48);
1697
- const availHeight = Math.max(550, containerHeight - 88);
2196
+ const availWidth = Math.max(280, containerWidth - 36);
2197
+ const availHeight = Math.max(280, containerHeight - 32);
1698
2198
  const scaleW = availWidth / unscaledVp.width;
1699
2199
  const scaleH = availHeight / unscaledVp.height;
1700
2200
  let fitScale;
1701
2201
  if (fitMode === "page") {
1702
- fitScale = Math.max(0.4, Math.min(scaleW, scaleH));
2202
+ fitScale = Math.max(0.2, Math.min(4, Math.min(scaleW, scaleH)));
1703
2203
  } else {
1704
- fitScale = Math.max(0.65, Math.min(1.25, scaleW));
2204
+ fitScale = Math.max(0.2, Math.min(4, scaleW));
1705
2205
  }
1706
2206
  const effectiveScale = (fitScale > 0 ? fitScale : 1) * zoomScale;
1707
2207
  const pixelRatio = window.devicePixelRatio || 1;
1708
2208
  const viewport = page.getViewport({ scale: effectiveScale, rotation });
2209
+ const displayWidth = Math.floor(viewport.width);
2210
+ const displayHeight = Math.floor(viewport.height);
1709
2211
  canvas.width = Math.floor(viewport.width * pixelRatio);
1710
2212
  canvas.height = Math.floor(viewport.height * pixelRatio);
1711
- canvas.style.width = `${Math.floor(viewport.width)}px`;
1712
- canvas.style.height = `${Math.floor(viewport.height)}px`;
2213
+ canvas.style.width = `${displayWidth}px`;
2214
+ canvas.style.height = `${displayHeight}px`;
2215
+ pageCard.style.width = `${displayWidth}px`;
2216
+ pageCard.style.height = `${displayHeight}px`;
1713
2217
  const canvasCtx = canvas.getContext("2d");
1714
2218
  if (!canvasCtx) return;
1715
2219
  canvasCtx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
@@ -1723,9 +2227,31 @@ var PdfPlugin = class {
1723
2227
  if (err?.name !== "RenderingCancelledException") {
1724
2228
  console.warn("[PdfPlugin] Page render warning:", err);
1725
2229
  }
2230
+ return;
1726
2231
  } finally {
1727
2232
  currentRenderTask = null;
1728
2233
  }
2234
+ try {
2235
+ textLayerDiv = document.createElement("div");
2236
+ textLayerDiv.className = "textLayer";
2237
+ textLayerDiv.style.width = `${displayWidth}px`;
2238
+ textLayerDiv.style.height = `${displayHeight}px`;
2239
+ textLayerDiv.style.position = "absolute";
2240
+ textLayerDiv.style.top = "0";
2241
+ textLayerDiv.style.left = "0";
2242
+ textLayerDiv.style.lineHeight = "1";
2243
+ pageCard.appendChild(textLayerDiv);
2244
+ if (pdfjsLib.TextLayer) {
2245
+ const textLayer = new pdfjsLib.TextLayer({
2246
+ textContentSource: page.streamTextContent(),
2247
+ container: textLayerDiv,
2248
+ viewport
2249
+ });
2250
+ await textLayer.render();
2251
+ }
2252
+ } catch (err) {
2253
+ console.debug("[PdfPlugin] TextLayer notice:", err);
2254
+ }
1729
2255
  };
1730
2256
  renderPage(1);
1731
2257
  let resizeTimer = null;
@@ -1735,11 +2261,48 @@ var PdfPlugin = class {
1735
2261
  if (Math.abs(zoomScale - 1) < 0.05) {
1736
2262
  renderPage(currentPage);
1737
2263
  }
1738
- }, 150);
2264
+ }, 120);
1739
2265
  });
1740
2266
  resizeObserver.observe(container);
2267
+ const onWheel = (e) => {
2268
+ if (e.ctrlKey || e.metaKey) {
2269
+ e.preventDefault();
2270
+ const delta = e.deltaY > 0 ? -0.15 : 0.15;
2271
+ zoomScale = Math.max(0.25, Math.min(4, Math.round((zoomScale + delta) * 100) / 100));
2272
+ renderPage(currentPage);
2273
+ }
2274
+ };
2275
+ container.addEventListener("wheel", onWheel, { passive: false });
2276
+ const onKeyDown = (e) => {
2277
+ const tag = e.target?.tagName?.toLowerCase();
2278
+ if (tag === "input" || tag === "textarea" || tag === "select") return;
2279
+ if (e.key === "ArrowRight" || e.key === "PageDown") {
2280
+ if (currentPage < totalPages) {
2281
+ e.preventDefault();
2282
+ container.scrollTop = 0;
2283
+ renderPage(currentPage + 1);
2284
+ }
2285
+ } else if (e.key === "ArrowLeft" || e.key === "PageUp") {
2286
+ if (currentPage > 1) {
2287
+ e.preventDefault();
2288
+ container.scrollTop = 0;
2289
+ renderPage(currentPage - 1);
2290
+ }
2291
+ } else if (e.key === "Home") {
2292
+ e.preventDefault();
2293
+ container.scrollTop = 0;
2294
+ renderPage(1);
2295
+ } else if (e.key === "End") {
2296
+ e.preventDefault();
2297
+ container.scrollTop = 0;
2298
+ renderPage(totalPages);
2299
+ }
2300
+ };
2301
+ window.addEventListener("keydown", onKeyDown);
1741
2302
  const cleanup = () => {
1742
2303
  resizeObserver.disconnect();
2304
+ window.removeEventListener("keydown", onKeyDown);
2305
+ container.removeEventListener("wheel", onWheel);
1743
2306
  if (resizeTimer) clearTimeout(resizeTimer);
1744
2307
  if (currentRenderTask) {
1745
2308
  try {
@@ -1758,22 +2321,26 @@ var PdfPlugin = class {
1758
2321
  const instance = {
1759
2322
  destroy: cleanup,
1760
2323
  zoomIn: () => {
1761
- zoomScale = Math.min(3.5, zoomScale + 0.2);
2324
+ zoomScale = Math.min(4, Math.round((zoomScale + 0.2) * 10) / 10);
1762
2325
  renderPage(currentPage);
1763
2326
  },
1764
2327
  zoomOut: () => {
1765
- zoomScale = Math.max(0.3, zoomScale - 0.2);
2328
+ zoomScale = Math.max(0.25, Math.round((zoomScale - 0.2) * 10) / 10);
1766
2329
  renderPage(currentPage);
1767
2330
  },
1768
2331
  getZoom: () => zoomScale,
1769
2332
  setZoom: (level) => {
1770
- zoomScale = Math.max(0.3, Math.min(3.5, level));
2333
+ zoomScale = Math.max(0.25, Math.min(4, level));
1771
2334
  renderPage(currentPage);
1772
2335
  },
1773
2336
  fitToPage: () => {
1774
- fitMode = fitMode === "page" ? "width" : "page";
2337
+ fitMode = "page";
2338
+ zoomScale = 1;
2339
+ renderPage(currentPage);
2340
+ },
2341
+ fitToWidth: () => {
2342
+ fitMode = "width";
1775
2343
  zoomScale = 1;
1776
- rotation = 0;
1777
2344
  renderPage(currentPage);
1778
2345
  },
1779
2346
  rotateCW: () => {
@@ -1791,6 +2358,9 @@ var PdfPlugin = class {
1791
2358
  container.scrollTop = 0;
1792
2359
  renderPage(page);
1793
2360
  },
2361
+ toggleThumbnails: () => {
2362
+ ctx?.toggleThumbnails?.();
2363
+ },
1794
2364
  download: () => {
1795
2365
  const blob = new Blob([ctx.buffer], { type: "application/pdf" });
1796
2366
  const url = URL.createObjectURL(blob);
@@ -1824,7 +2394,7 @@ var PdfPlugin = class {
1824
2394
  },
1825
2395
  getThumbnails: async () => {
1826
2396
  const thumbnails = [];
1827
- const count = Math.min(totalPages, 50);
2397
+ const count = totalPages;
1828
2398
  for (let i = 1; i <= count; i++) {
1829
2399
  thumbnails.push({
1830
2400
  index: i,
@@ -1832,9 +2402,10 @@ var PdfPlugin = class {
1832
2402
  render: async (thumbCanvas) => {
1833
2403
  try {
1834
2404
  const p = await pdfDoc.getPage(i);
1835
- const baseVp = p.getViewport({ scale: 1 });
1836
- const thumbScale = (thumbCanvas.width || 120) / baseVp.width;
1837
- const thumbVp = p.getViewport({ scale: thumbScale });
2405
+ const baseVp = p.getViewport({ scale: 1, rotation });
2406
+ const targetW = thumbCanvas.width || 130;
2407
+ const thumbScale = targetW / baseVp.width;
2408
+ const thumbVp = p.getViewport({ scale: thumbScale, rotation });
1838
2409
  thumbCanvas.height = Math.floor(thumbVp.height);
1839
2410
  const tCtx = thumbCanvas.getContext("2d");
1840
2411
  if (tCtx) {
@@ -2178,28 +2749,26 @@ var DocxPlugin = class {
2178
2749
  getToolbarActions(instance) {
2179
2750
  const totalPages = instance.getPageCount?.() ?? 1;
2180
2751
  const actions = [];
2181
- if (totalPages > 1) {
2182
- actions.push({
2183
- id: "page-nav",
2184
- icon: "",
2185
- label: "Page Navigation",
2186
- type: "page-nav",
2187
- group: "navigation",
2188
- value: instance.getCurrentPage?.() ?? 1,
2189
- max: totalPages,
2190
- execute: (action, page) => {
2191
- const cur = instance.getCurrentPage?.() ?? 1;
2192
- const max = instance.getPageCount?.() ?? 1;
2193
- if (action === "prev") {
2194
- if (cur > 1) instance.goToPage?.(cur - 1);
2195
- } else if (action === "next") {
2196
- if (cur < max) instance.goToPage?.(cur + 1);
2197
- } else if (typeof page === "number") {
2198
- instance.goToPage?.(page);
2199
- }
2752
+ actions.push({
2753
+ id: "page-nav",
2754
+ icon: "",
2755
+ label: "Page Navigation",
2756
+ type: "page-nav",
2757
+ group: "navigation",
2758
+ value: instance.getCurrentPage?.() ?? 1,
2759
+ max: totalPages,
2760
+ execute: (action, page) => {
2761
+ const cur = instance.getCurrentPage?.() ?? 1;
2762
+ const max = instance.getPageCount?.() ?? 1;
2763
+ if (action === "prev") {
2764
+ if (cur > 1) instance.goToPage?.(cur - 1);
2765
+ } else if (action === "next") {
2766
+ if (cur < max) instance.goToPage?.(cur + 1);
2767
+ } else if (typeof page === "number") {
2768
+ instance.goToPage?.(page);
2200
2769
  }
2201
- });
2202
- }
2770
+ }
2771
+ });
2203
2772
  actions.push(
2204
2773
  {
2205
2774
  id: "zoom-out",
@@ -2265,11 +2834,17 @@ var DocxPlugin = class {
2265
2834
  wrapper.className = "fp-docx-wrapper";
2266
2835
  wrapper.style.transformOrigin = "top center";
2267
2836
  wrapper.style.transition = "transform 0.2s ease";
2268
- wrapper.style.padding = "24px 16px";
2269
- wrapper.style.maxWidth = "950px";
2837
+ wrapper.style.padding = "0";
2838
+ wrapper.style.maxWidth = "none";
2839
+ wrapper.style.width = "816px";
2270
2840
  wrapper.style.margin = "0 auto";
2841
+ wrapper.style.display = "flex";
2842
+ wrapper.style.flexDirection = "column";
2843
+ wrapper.style.alignItems = "center";
2271
2844
  wrapper.style.boxSizing = "border-box";
2272
- ctx.container.style.overflow = "auto";
2845
+ ctx.container.style.overflowX = "hidden";
2846
+ ctx.container.style.overflowY = "auto";
2847
+ ctx.container.style.padding = "16px 8px";
2273
2848
  ctx.container.style.backgroundColor = "#f1f5f9";
2274
2849
  ctx.container.appendChild(wrapper);
2275
2850
  const styleOverride = document.createElement("style");
@@ -2443,39 +3018,13 @@ var DocxPlugin = class {
2443
3018
  const pageElements = sections.length > 0 ? sections : cards;
2444
3019
  const totalPages = Math.max(1, pageElements.length);
2445
3020
  let currentPage = 1;
2446
- let indicator = null;
2447
- if (totalPages > 1) {
2448
- indicator = document.createElement("div");
2449
- indicator.className = "fp-docx-page-indicator";
2450
- indicator.style.position = "sticky";
2451
- indicator.style.bottom = "16px";
2452
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
2453
- indicator.style.backdropFilter = "blur(8px)";
2454
- indicator.style.color = "#f8fafc";
2455
- indicator.style.fontSize = "12px";
2456
- indicator.style.fontWeight = "600";
2457
- indicator.style.padding = "5px 14px";
2458
- indicator.style.borderRadius = "20px";
2459
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
2460
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
2461
- indicator.style.zIndex = "10";
2462
- indicator.style.userSelect = "none";
2463
- indicator.style.pointerEvents = "none";
2464
- indicator.style.textAlign = "center";
2465
- indicator.style.width = "fit-content";
2466
- indicator.style.margin = "16px auto 0";
2467
- ctx.container.appendChild(indicator);
2468
- }
2469
3021
  const showPage = (pageNum) => {
2470
3022
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
2471
3023
  if (pageElements.length > 1) {
2472
3024
  pageElements.forEach((sec, idx) => {
2473
- sec.style.display = idx + 1 === currentPage ? "block" : "none";
3025
+ sec.style.display = idx + 1 === currentPage ? "" : "none";
2474
3026
  });
2475
3027
  }
2476
- if (indicator) {
2477
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
2478
- }
2479
3028
  ctx.container.scrollTop = 0;
2480
3029
  ctx.emit("page-change", { page: currentPage, total: totalPages });
2481
3030
  };
@@ -2484,31 +3033,46 @@ var DocxPlugin = class {
2484
3033
  }
2485
3034
  scale = 1;
2486
3035
  let rotation = 0;
2487
- let fitMode = "width";
2488
- const calculateFitScale = (mode = fitMode) => {
2489
- const activeEl = pageElements[currentPage - 1] || wrapper.firstElementChild || wrapper;
3036
+ let fitMode = "page";
3037
+ let isUserZoomed = false;
3038
+ const calculateFitScale = (_mode = fitMode) => {
3039
+ const activeEl = pageElements[currentPage - 1] || wrapper.querySelector("section.docx") || wrapper;
2490
3040
  const elW = activeEl.offsetWidth || 816;
2491
- const singlePageH = Math.min(activeEl.offsetHeight || 1056, Math.round(elW * 1.32));
2492
- const availW = Math.max(280, ctx.container.clientWidth - 48);
2493
- const availH = Math.max(280, ctx.container.clientHeight - 80);
3041
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
2494
3042
  const sW = availW / elW;
2495
- const sH = availH / singlePageH;
2496
- if (mode === "page") {
2497
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
2498
- }
2499
- return Math.max(0.65, Math.min(1.05, sW));
3043
+ return Math.max(0.35, Math.min(3, sW));
2500
3044
  };
2501
3045
  const applyTransform = () => {
2502
3046
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
2503
3047
  wrapper.style.transformOrigin = "top center";
3048
+ const activeEl = pageElements[currentPage - 1] || wrapper.querySelector("section.docx") || wrapper;
3049
+ const elH = activeEl.offsetHeight || 1056;
3050
+ if (scale > 1) {
3051
+ wrapper.style.marginBottom = `${Math.round(elH * scale - elH + 32)}px`;
3052
+ } else {
3053
+ wrapper.style.marginBottom = "32px";
3054
+ }
2504
3055
  };
3056
+ let resizeObserver = null;
3057
+ if (typeof ResizeObserver !== "undefined") {
3058
+ resizeObserver = new ResizeObserver(() => {
3059
+ if (!isUserZoomed) {
3060
+ const newFit = calculateFitScale("page");
3061
+ if (Math.abs(scale - newFit) > 0.015) {
3062
+ scale = newFit;
3063
+ applyTransform();
3064
+ }
3065
+ }
3066
+ });
3067
+ resizeObserver.observe(ctx.container);
3068
+ }
2505
3069
  setTimeout(() => {
2506
- fitMode = "width";
2507
- scale = calculateFitScale("width");
3070
+ scale = calculateFitScale("page");
2508
3071
  applyTransform();
2509
- }, 60);
3072
+ }, 40);
2510
3073
  const cleanup = () => {
2511
- indicator?.remove();
3074
+ resizeObserver?.disconnect();
3075
+ resizeObserver = null;
2512
3076
  for (const url of createdBlobUrls) {
2513
3077
  URL.revokeObjectURL(url);
2514
3078
  }
@@ -2525,21 +3089,24 @@ var DocxPlugin = class {
2525
3089
  showPage(page);
2526
3090
  },
2527
3091
  zoomIn: () => {
2528
- scale += 0.15;
3092
+ isUserZoomed = true;
3093
+ scale = Math.min(3.5, scale + 0.15);
2529
3094
  applyTransform();
2530
3095
  },
2531
3096
  zoomOut: () => {
3097
+ isUserZoomed = true;
2532
3098
  scale = Math.max(0.2, scale - 0.15);
2533
3099
  applyTransform();
2534
3100
  },
2535
3101
  getZoom: () => scale,
2536
3102
  setZoom: (level) => {
3103
+ isUserZoomed = true;
2537
3104
  scale = level;
2538
3105
  applyTransform();
2539
3106
  },
2540
3107
  fitToPage: () => {
2541
- fitMode = fitMode === "width" ? "page" : "width";
2542
- scale = calculateFitScale(fitMode);
3108
+ isUserZoomed = false;
3109
+ scale = calculateFitScale("page");
2543
3110
  rotation = 0;
2544
3111
  applyTransform();
2545
3112
  },
@@ -2929,7 +3496,7 @@ var ExcelPlugin = class {
2929
3496
  "application/vnd.ms-excel.template.macroEnabled.12",
2930
3497
  "application/vnd.oasis.opendocument.spreadsheet"
2931
3498
  ];
2932
- weight = 80;
3499
+ weight = 85;
2933
3500
  supports(file) {
2934
3501
  const ext = file.metadata.extension?.toLowerCase();
2935
3502
  const mime = file.metadata.mimeType?.toLowerCase();
@@ -2941,6 +3508,14 @@ var ExcelPlugin = class {
2941
3508
  getToolbarActions(instance) {
2942
3509
  const totalSheets = instance.getPageCount?.() ?? 1;
2943
3510
  const actions = [];
3511
+ actions.push({
3512
+ id: "thumbnails",
3513
+ icon: "thumbnails",
3514
+ label: "Sheet Thumbnails",
3515
+ type: "button",
3516
+ group: "navigation",
3517
+ execute: () => instance.toggleThumbnails?.()
3518
+ });
2944
3519
  if (totalSheets > 1) {
2945
3520
  actions.push({
2946
3521
  id: "page-nav",
@@ -3023,6 +3598,16 @@ var ExcelPlugin = class {
3023
3598
  execute: () => {
3024
3599
  instance.print?.();
3025
3600
  }
3601
+ },
3602
+ {
3603
+ id: "open-window",
3604
+ icon: "open-window",
3605
+ label: "Open in Separate Full Window",
3606
+ type: "button",
3607
+ group: "actions",
3608
+ execute: () => {
3609
+ instance.openInSeparateWindow?.();
3610
+ }
3026
3611
  }
3027
3612
  );
3028
3613
  return actions;
@@ -3188,6 +3773,63 @@ var ExcelPlugin = class {
3188
3773
  },
3189
3774
  getPageCount: () => sheetNames.length,
3190
3775
  getCurrentPage: () => currentSheetIndex,
3776
+ getThumbnails: () => {
3777
+ return sheetNames.map((name, idx) => ({
3778
+ index: idx,
3779
+ label: name,
3780
+ render: async (canvas) => {
3781
+ canvas.width = 140;
3782
+ canvas.height = 100;
3783
+ const c = canvas.getContext("2d");
3784
+ if (!c) return;
3785
+ c.fillStyle = "#ffffff";
3786
+ c.fillRect(0, 0, canvas.width, canvas.height);
3787
+ c.fillStyle = "#107c41";
3788
+ c.fillRect(0, 0, canvas.width, 16);
3789
+ c.fillStyle = "#ffffff";
3790
+ c.font = 'bold 8px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
3791
+ c.fillText(`\u{1F4CA} ${name.slice(0, 16)}`, 6, 11);
3792
+ const startY = 17;
3793
+ const rowH = 13;
3794
+ const colW = 30;
3795
+ const colHeaders = ["A", "B", "C", "D"];
3796
+ c.fillStyle = "#f1f5f9";
3797
+ c.fillRect(0, startY, canvas.width, rowH);
3798
+ c.strokeStyle = "#cbd5e1";
3799
+ c.lineWidth = 0.8;
3800
+ c.strokeRect(0, startY, canvas.width, rowH);
3801
+ c.fillStyle = "#64748b";
3802
+ c.font = "bold 7px sans-serif";
3803
+ for (let ci = 0; ci < colHeaders.length; ci++) {
3804
+ c.fillText(colHeaders[ci], 14 + ci * colW, startY + 9);
3805
+ }
3806
+ const ws = wb?.Sheets[name];
3807
+ for (let ri = 1; ri <= 5; ri++) {
3808
+ const y = startY + ri * rowH;
3809
+ if (y > canvas.height - 2) break;
3810
+ c.fillStyle = "#f8fafc";
3811
+ c.fillRect(0, y, 12, rowH);
3812
+ c.fillStyle = "#94a3b8";
3813
+ c.font = "6px sans-serif";
3814
+ c.fillText(String(ri), 3, y + 9);
3815
+ c.strokeStyle = "#e2e8f0";
3816
+ c.strokeRect(0, y, canvas.width, rowH);
3817
+ c.fillStyle = "#334155";
3818
+ c.font = "6px sans-serif";
3819
+ for (let ci = 0; ci < colHeaders.length; ci++) {
3820
+ const cellRef = `${colHeaders[ci]}${ri}`;
3821
+ const cellVal = ws?.[cellRef]?.w || ws?.[cellRef]?.v;
3822
+ if (cellVal !== void 0) {
3823
+ c.fillText(String(cellVal).slice(0, 6), 14 + ci * colW, y + 9);
3824
+ }
3825
+ }
3826
+ }
3827
+ c.strokeStyle = "#cbd5e1";
3828
+ c.lineWidth = 1;
3829
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
3830
+ }
3831
+ }));
3832
+ },
3191
3833
  download: () => {
3192
3834
  const blob = new Blob([ctx.buffer], { type: this.mimeTypes[0] });
3193
3835
  const url = URL.createObjectURL(blob);
@@ -3220,7 +3862,39 @@ var CsvPlugin = class {
3220
3862
  return ext === ".csv" || ext === ".tsv" || mime === "text/csv" || mime === "text/tab-separated-values";
3221
3863
  }
3222
3864
  getToolbarActions(instance) {
3223
- return [
3865
+ const totalPages = instance.getPageCount?.() ?? 1;
3866
+ const actions = [];
3867
+ actions.push({
3868
+ id: "thumbnails",
3869
+ icon: "thumbnails",
3870
+ label: "Page Thumbnails",
3871
+ type: "button",
3872
+ group: "navigation",
3873
+ execute: () => instance.toggleThumbnails?.()
3874
+ });
3875
+ if (totalPages > 1) {
3876
+ actions.push({
3877
+ id: "page-nav",
3878
+ icon: "",
3879
+ label: "Page Navigation",
3880
+ type: "page-nav",
3881
+ group: "navigation",
3882
+ value: instance.getCurrentPage?.() ?? 1,
3883
+ max: totalPages,
3884
+ execute: (action, page) => {
3885
+ const cur = instance.getCurrentPage?.() ?? 1;
3886
+ const max = instance.getPageCount?.() ?? 1;
3887
+ if (action === "prev") {
3888
+ if (cur > 1) instance.goToPage?.(cur - 1);
3889
+ } else if (action === "next") {
3890
+ if (cur < max) instance.goToPage?.(cur + 1);
3891
+ } else if (typeof page === "number") {
3892
+ instance.goToPage?.(page);
3893
+ }
3894
+ }
3895
+ });
3896
+ }
3897
+ actions.push(
3224
3898
  {
3225
3899
  id: "zoom-out",
3226
3900
  icon: "zoom-out",
@@ -3241,6 +3915,16 @@ var CsvPlugin = class {
3241
3915
  instance.zoomIn?.();
3242
3916
  }
3243
3917
  },
3918
+ {
3919
+ id: "fit-page",
3920
+ icon: "fit-page",
3921
+ label: "Fit to View",
3922
+ type: "button",
3923
+ group: "zoom",
3924
+ execute: () => {
3925
+ instance.fitToPage?.();
3926
+ }
3927
+ },
3244
3928
  {
3245
3929
  id: "download",
3246
3930
  icon: "download",
@@ -3260,8 +3944,19 @@ var CsvPlugin = class {
3260
3944
  execute: () => {
3261
3945
  instance.print?.();
3262
3946
  }
3947
+ },
3948
+ {
3949
+ id: "open-window",
3950
+ icon: "open-window",
3951
+ label: "Open in Separate Full Window",
3952
+ type: "button",
3953
+ group: "actions",
3954
+ execute: () => {
3955
+ instance.openInSeparateWindow?.();
3956
+ }
3263
3957
  }
3264
- ];
3958
+ );
3959
+ return actions;
3265
3960
  }
3266
3961
  async render(ctx) {
3267
3962
  const decoder = new TextDecoder("utf-8");
@@ -3273,33 +3968,62 @@ var CsvPlugin = class {
3273
3968
  container.style.padding = "16px";
3274
3969
  container.style.boxSizing = "border-box";
3275
3970
  container.style.transformOrigin = "top left";
3971
+ const delimiter = ctx.metadata.extension === ".tsv" ? " " : ",";
3972
+ const allLines = text.split(/\r?\n/).filter((r) => r.trim().length > 0);
3973
+ const headerLine = allLines[0] || "";
3974
+ const headerCells = headerLine.split(delimiter);
3975
+ const dataLines = allLines.slice(1);
3976
+ const ROWS_PER_PAGE = 100;
3977
+ const totalPages = Math.max(1, Math.ceil(dataLines.length / ROWS_PER_PAGE));
3978
+ let currentPage = 1;
3276
3979
  const table = document.createElement("table");
3277
3980
  table.style.borderCollapse = "collapse";
3278
3981
  table.style.width = "100%";
3279
- table.style.fontFamily = "monospace";
3982
+ table.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace';
3280
3983
  table.style.fontSize = "13px";
3281
- const delimiter = ctx.metadata.extension === ".tsv" ? " " : ",";
3282
- const rows = text.split(/\r?\n/).slice(0, 500);
3283
- rows.forEach((row, rowIndex) => {
3284
- if (!row.trim()) return;
3285
- const tr = document.createElement("tr");
3286
- const cells = row.split(delimiter);
3287
- cells.forEach((cell) => {
3288
- const td = document.createElement(rowIndex === 0 ? "th" : "td");
3289
- td.textContent = cell.trim();
3290
- td.style.border = "1px solid #d0d7de";
3291
- td.style.padding = "6px 12px";
3292
- if (rowIndex === 0) {
3293
- td.style.backgroundColor = "#f6f8fa";
3294
- td.style.fontWeight = "bold";
3295
- }
3296
- tr.appendChild(td);
3984
+ const renderPage = (pageNum) => {
3985
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
3986
+ table.innerHTML = "";
3987
+ const headerTr = document.createElement("tr");
3988
+ headerCells.forEach((cell) => {
3989
+ const th = document.createElement("th");
3990
+ th.textContent = cell.trim();
3991
+ th.style.border = "1px solid #d0d7de";
3992
+ th.style.padding = "8px 12px";
3993
+ th.style.backgroundColor = "#f6f8fa";
3994
+ th.style.fontWeight = "600";
3995
+ th.style.whiteSpace = "nowrap";
3996
+ headerTr.appendChild(th);
3297
3997
  });
3298
- table.appendChild(tr);
3299
- });
3998
+ table.appendChild(headerTr);
3999
+ const start = (currentPage - 1) * ROWS_PER_PAGE;
4000
+ const end = Math.min(dataLines.length, start + ROWS_PER_PAGE);
4001
+ const pageRows = dataLines.slice(start, end);
4002
+ pageRows.forEach((row, idx) => {
4003
+ const tr = document.createElement("tr");
4004
+ if (idx % 2 === 1) tr.style.backgroundColor = "#f8fafc";
4005
+ const cells = row.split(delimiter);
4006
+ cells.forEach((cell) => {
4007
+ const td = document.createElement("td");
4008
+ td.textContent = cell.trim();
4009
+ td.style.border = "1px solid #d0d7de";
4010
+ td.style.padding = "6px 12px";
4011
+ td.style.whiteSpace = "nowrap";
4012
+ tr.appendChild(td);
4013
+ });
4014
+ table.appendChild(tr);
4015
+ });
4016
+ container.scrollTop = 0;
4017
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
4018
+ };
4019
+ renderPage(1);
3300
4020
  container.appendChild(table);
3301
4021
  ctx.container.appendChild(container);
3302
4022
  let scale = 1;
4023
+ const applyScale = () => {
4024
+ container.style.transform = `scale(${scale})`;
4025
+ container.style.transformOrigin = "top left";
4026
+ };
3303
4027
  const cleanup = () => {
3304
4028
  container.remove();
3305
4029
  ctx.container.innerHTML = "";
@@ -3307,22 +4031,86 @@ var CsvPlugin = class {
3307
4031
  ctx.signal.addEventListener("abort", cleanup);
3308
4032
  return {
3309
4033
  destroy: cleanup,
4034
+ getPageCount: () => totalPages,
4035
+ getCurrentPage: () => currentPage,
4036
+ goToPage: (page) => renderPage(page),
4037
+ getThumbnails: () => {
4038
+ const thumbnails = [];
4039
+ for (let i = 0; i < totalPages; i++) {
4040
+ const pageIdx = i;
4041
+ const startRow = pageIdx * ROWS_PER_PAGE + 1;
4042
+ const endRow = Math.min(dataLines.length, (pageIdx + 1) * ROWS_PER_PAGE);
4043
+ const label = totalPages === 1 ? "Table" : `Page ${pageIdx + 1} (${startRow}-${endRow})`;
4044
+ thumbnails.push({
4045
+ index: pageIdx,
4046
+ label,
4047
+ render: async (canvas) => {
4048
+ canvas.width = 140;
4049
+ canvas.height = 100;
4050
+ const c = canvas.getContext("2d");
4051
+ if (!c) return;
4052
+ c.fillStyle = "#ffffff";
4053
+ c.fillRect(0, 0, canvas.width, canvas.height);
4054
+ c.fillStyle = "#2563eb";
4055
+ c.fillRect(0, 0, canvas.width, 16);
4056
+ c.fillStyle = "#ffffff";
4057
+ c.font = "bold 8px sans-serif";
4058
+ c.fillText(`\u{1F4C8} ${label.slice(0, 18)}`, 6, 11);
4059
+ const startY = 17;
4060
+ const rowH = 13;
4061
+ const colW = 28;
4062
+ const cols = Math.min(4, headerCells.length || 4);
4063
+ c.fillStyle = "#f1f5f9";
4064
+ c.fillRect(0, startY, canvas.width, rowH);
4065
+ c.strokeStyle = "#cbd5e1";
4066
+ c.lineWidth = 0.8;
4067
+ c.strokeRect(0, startY, canvas.width, rowH);
4068
+ c.fillStyle = "#64748b";
4069
+ c.font = "bold 7px sans-serif";
4070
+ for (let ci = 0; ci < cols; ci++) {
4071
+ const hName = headerCells[ci] || `Col ${ci + 1}`;
4072
+ c.fillText(hName.slice(0, 5), 8 + ci * colW, startY + 9);
4073
+ }
4074
+ const rowsToPreview = dataLines.slice(pageIdx * ROWS_PER_PAGE, pageIdx * ROWS_PER_PAGE + 5);
4075
+ for (let ri = 0; ri < rowsToPreview.length; ri++) {
4076
+ const y = startY + (ri + 1) * rowH;
4077
+ if (y > canvas.height - 2) break;
4078
+ c.strokeStyle = "#e2e8f0";
4079
+ c.strokeRect(0, y, canvas.width, rowH);
4080
+ const cVals = rowsToPreview[ri].split(delimiter);
4081
+ c.fillStyle = "#334155";
4082
+ c.font = "6px sans-serif";
4083
+ for (let ci = 0; ci < cols; ci++) {
4084
+ const val = cVals[ci] || "";
4085
+ c.fillText(val.trim().slice(0, 6), 8 + ci * colW, y + 9);
4086
+ }
4087
+ }
4088
+ c.strokeStyle = "#cbd5e1";
4089
+ c.lineWidth = 1;
4090
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
4091
+ }
4092
+ });
4093
+ }
4094
+ return thumbnails;
4095
+ },
3310
4096
  zoomIn: () => {
3311
4097
  scale += 0.1;
3312
- container.style.transform = `scale(${scale})`;
4098
+ applyScale();
3313
4099
  },
3314
4100
  zoomOut: () => {
3315
4101
  scale = Math.max(0.2, scale - 0.1);
3316
- container.style.transform = `scale(${scale})`;
4102
+ applyScale();
3317
4103
  },
3318
4104
  getZoom: () => scale,
3319
4105
  setZoom: (level) => {
3320
4106
  scale = level;
3321
- container.style.transform = `scale(${scale})`;
4107
+ applyScale();
3322
4108
  },
3323
4109
  fitToPage: () => {
3324
- scale = 1;
3325
- container.style.transform = `scale(1)`;
4110
+ const availW = Math.max(280, (ctx.container.clientWidth || window.innerWidth) - 48);
4111
+ const tW = table.offsetWidth || 600;
4112
+ scale = Math.max(0.35, Math.min(1, availW / tW));
4113
+ applyScale();
3326
4114
  },
3327
4115
  download: () => {
3328
4116
  const blob = new Blob([ctx.buffer], { type: "text/csv" });
@@ -3386,28 +4174,34 @@ var CodePlugin = class {
3386
4174
  getToolbarActions(instance) {
3387
4175
  const totalPages = instance.getPageCount?.() ?? 1;
3388
4176
  const actions = [];
3389
- if (totalPages > 1) {
3390
- actions.push({
3391
- id: "page-nav",
3392
- icon: "",
3393
- label: "Page Navigation",
3394
- type: "page-nav",
3395
- group: "navigation",
3396
- value: instance.getCurrentPage?.() ?? 1,
3397
- max: totalPages,
3398
- execute: (action, page) => {
3399
- const cur = instance.getCurrentPage?.() ?? 1;
3400
- const max = instance.getPageCount?.() ?? 1;
3401
- if (action === "prev") {
3402
- if (cur > 1) instance.goToPage?.(cur - 1);
3403
- } else if (action === "next") {
3404
- if (cur < max) instance.goToPage?.(cur + 1);
3405
- } else if (typeof page === "number") {
3406
- instance.goToPage?.(page);
3407
- }
4177
+ actions.push({
4178
+ id: "thumbnails",
4179
+ icon: "thumbnails",
4180
+ label: "Page Thumbnails",
4181
+ type: "button",
4182
+ group: "navigation",
4183
+ execute: () => instance.toggleThumbnails?.()
4184
+ });
4185
+ actions.push({
4186
+ id: "page-nav",
4187
+ icon: "",
4188
+ label: "Page Navigation",
4189
+ type: "page-nav",
4190
+ group: "navigation",
4191
+ value: instance.getCurrentPage?.() ?? 1,
4192
+ max: totalPages,
4193
+ execute: (action, page) => {
4194
+ const cur = instance.getCurrentPage?.() ?? 1;
4195
+ const max = instance.getPageCount?.() ?? 1;
4196
+ if (action === "prev") {
4197
+ if (cur > 1) instance.goToPage?.(cur - 1);
4198
+ } else if (action === "next") {
4199
+ if (cur < max) instance.goToPage?.(cur + 1);
4200
+ } else if (typeof page === "number") {
4201
+ instance.goToPage?.(page);
3408
4202
  }
3409
- });
3410
- }
4203
+ }
4204
+ });
3411
4205
  actions.push(
3412
4206
  {
3413
4207
  id: "zoom-out",
@@ -3551,16 +4345,19 @@ var CodePlugin = class {
3551
4345
  let fontSize = 13;
3552
4346
  let rotation = 0;
3553
4347
  let zoomLevel = 1;
4348
+ let isUserZoomed = false;
3554
4349
  const pre = document.createElement("pre");
3555
4350
  const code = document.createElement("code");
3556
4351
  if (isTxt) {
4352
+ container.style.overflowX = "hidden";
4353
+ container.style.overflowY = "auto";
3557
4354
  container.style.backgroundColor = "#f1f5f9";
3558
- container.style.padding = "32px";
4355
+ container.style.padding = "16px 8px";
3559
4356
  container.style.boxSizing = "border-box";
3560
4357
  container.style.display = "flex";
3561
4358
  container.style.flexDirection = "column";
3562
4359
  container.style.alignItems = "center";
3563
- pre.style.margin = "0";
4360
+ pre.style.margin = "0 auto";
3564
4361
  pre.style.fontFamily = "Consolas, 'Courier New', monospace";
3565
4362
  pre.style.fontSize = "13px";
3566
4363
  pre.style.color = "#1e293b";
@@ -3572,10 +4369,12 @@ var CodePlugin = class {
3572
4369
  pre.style.minHeight = "1056px";
3573
4370
  pre.style.padding = "72px 56px";
3574
4371
  pre.style.boxSizing = "border-box";
3575
- pre.style.boxShadow = "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)";
4372
+ pre.style.boxShadow = "0 4px 24px rgba(0, 0, 0, 0.08)";
3576
4373
  pre.style.borderRadius = "4px";
3577
4374
  pre.style.transformOrigin = "top center";
4375
+ pre.style.transition = "transform 0.15s ease";
3578
4376
  } else {
4377
+ container.style.overflow = "auto";
3579
4378
  container.style.backgroundColor = "#1e1e1e";
3580
4379
  container.style.color = "#d4d4d4";
3581
4380
  container.style.padding = "16px";
@@ -3607,42 +4406,9 @@ var CodePlugin = class {
3607
4406
  renderCodePage(rawPages[0] || (isTxt ? "" : fullText));
3608
4407
  pre.appendChild(code);
3609
4408
  container.appendChild(pre);
3610
- let indicator = null;
3611
- if (totalPages > 1) {
3612
- indicator = document.createElement("div");
3613
- indicator.className = "fp-code-page-indicator";
3614
- indicator.style.position = "sticky";
3615
- indicator.style.bottom = "16px";
3616
- if (isTxt) {
3617
- indicator.style.backgroundColor = "rgba(255, 255, 255, 0.9)";
3618
- indicator.style.color = "#334155";
3619
- indicator.style.border = "1px solid #e2e8f0";
3620
- indicator.style.boxShadow = "0 1px 3px rgba(0,0,0,0.1)";
3621
- } else {
3622
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
3623
- indicator.style.color = "#f8fafc";
3624
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
3625
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
3626
- indicator.style.backdropFilter = "blur(8px)";
3627
- }
3628
- indicator.style.fontSize = "12px";
3629
- indicator.style.fontWeight = "600";
3630
- indicator.style.padding = "5px 14px";
3631
- indicator.style.borderRadius = "20px";
3632
- indicator.style.zIndex = "10";
3633
- indicator.style.userSelect = "none";
3634
- indicator.style.pointerEvents = "none";
3635
- indicator.style.textAlign = "center";
3636
- indicator.style.width = "fit-content";
3637
- indicator.style.margin = "16px auto 0";
3638
- container.appendChild(indicator);
3639
- }
3640
4409
  const showPage = (pageNum) => {
3641
4410
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
3642
4411
  renderCodePage(rawPages[currentPage - 1] || (isTxt ? "" : fullText));
3643
- if (indicator) {
3644
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
3645
- }
3646
4412
  container.scrollTop = 0;
3647
4413
  ctx.emit("page-change", { page: currentPage, total: totalPages });
3648
4414
  };
@@ -3650,36 +4416,90 @@ var CodePlugin = class {
3650
4416
  showPage(1);
3651
4417
  }
3652
4418
  ctx.container.appendChild(container);
3653
- const cleanup = () => {
3654
- indicator?.remove();
3655
- container.remove();
3656
- ctx.container.innerHTML = "";
4419
+ const calculateTxtFit = () => {
4420
+ const availW = Math.max(280, container.clientWidth - 32);
4421
+ return Math.max(0.4, Math.min(3, availW / 816));
3657
4422
  };
3658
- ctx.signal.addEventListener("abort", cleanup);
3659
4423
  const updateTransform = () => {
3660
4424
  if (isTxt) {
3661
4425
  pre.style.transform = `scale(${zoomLevel}) rotate(${rotation}deg)`;
4426
+ const scaledH = 1056 * zoomLevel;
4427
+ const extraH = Math.max(0, scaledH - 1056);
4428
+ pre.style.marginBottom = `${extraH + 32}px`;
3662
4429
  } else {
3663
4430
  pre.style.transform = `rotate(${rotation}deg)`;
3664
4431
  pre.style.fontSize = `${fontSize}px`;
3665
4432
  }
3666
4433
  };
4434
+ let resizeObserver = null;
4435
+ if (isTxt) {
4436
+ setTimeout(() => {
4437
+ zoomLevel = calculateTxtFit();
4438
+ updateTransform();
4439
+ }, 60);
4440
+ resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
4441
+ if (!isUserZoomed) {
4442
+ zoomLevel = calculateTxtFit();
4443
+ updateTransform();
4444
+ }
4445
+ }) : null;
4446
+ resizeObserver?.observe(container);
4447
+ }
4448
+ const cleanup = () => {
4449
+ resizeObserver?.disconnect();
4450
+ container.remove();
4451
+ ctx.container.innerHTML = "";
4452
+ };
4453
+ ctx.signal.addEventListener("abort", cleanup);
3667
4454
  return {
3668
4455
  destroy: cleanup,
3669
4456
  getPageCount: () => totalPages,
3670
4457
  getCurrentPage: () => currentPage,
3671
4458
  goToPage: (page) => showPage(page),
4459
+ getThumbnails: () => {
4460
+ return rawPages.map((pageText, idx) => ({
4461
+ index: idx,
4462
+ label: `Page ${idx + 1}`,
4463
+ render: async (canvas) => {
4464
+ canvas.width = 140;
4465
+ canvas.height = 180;
4466
+ const c = canvas.getContext("2d");
4467
+ if (!c) return;
4468
+ c.fillStyle = "#ffffff";
4469
+ c.fillRect(0, 0, canvas.width, canvas.height);
4470
+ c.strokeStyle = "#cbd5e1";
4471
+ c.lineWidth = 1;
4472
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
4473
+ c.fillStyle = "#64748b";
4474
+ const lines = (pageText || "").split("\n").slice(0, 24);
4475
+ const lineH = 6;
4476
+ const startY = 14;
4477
+ const startX = 12;
4478
+ const maxW = canvas.width - 24;
4479
+ c.font = "5px monospace";
4480
+ for (let li = 0; li < lines.length; li++) {
4481
+ const l = lines[li].trim();
4482
+ if (!l) continue;
4483
+ const y = startY + li * lineH;
4484
+ if (y > canvas.height - 12) break;
4485
+ c.fillText(l.slice(0, 30), startX, y, maxW);
4486
+ }
4487
+ }
4488
+ }));
4489
+ },
3672
4490
  zoomIn: () => {
4491
+ isUserZoomed = true;
3673
4492
  if (isTxt) {
3674
- zoomLevel = Math.min(3, zoomLevel + 0.1);
4493
+ zoomLevel = Math.min(3.5, zoomLevel + 0.15);
3675
4494
  } else {
3676
4495
  fontSize = Math.min(32, fontSize + 2);
3677
4496
  }
3678
4497
  updateTransform();
3679
4498
  },
3680
4499
  zoomOut: () => {
4500
+ isUserZoomed = true;
3681
4501
  if (isTxt) {
3682
- zoomLevel = Math.max(0.1, zoomLevel - 0.1);
4502
+ zoomLevel = Math.max(0.1, zoomLevel - 0.15);
3683
4503
  } else {
3684
4504
  fontSize = Math.max(8, fontSize - 2);
3685
4505
  }
@@ -3687,6 +4507,7 @@ var CodePlugin = class {
3687
4507
  },
3688
4508
  getZoom: () => isTxt ? zoomLevel : fontSize / 13,
3689
4509
  setZoom: (level) => {
4510
+ isUserZoomed = true;
3690
4511
  if (isTxt) {
3691
4512
  zoomLevel = level;
3692
4513
  } else {
@@ -3695,9 +4516,10 @@ var CodePlugin = class {
3695
4516
  updateTransform();
3696
4517
  },
3697
4518
  fitToPage: () => {
4519
+ isUserZoomed = false;
3698
4520
  fontSize = 13;
3699
4521
  rotation = 0;
3700
- zoomLevel = isTxt ? Math.max(0.65, Math.min(1.05, (container.clientWidth - 48) / 816)) : 1;
4522
+ zoomLevel = isTxt ? calculateTxtFit() : 1;
3701
4523
  updateTransform();
3702
4524
  },
3703
4525
  rotateCW: () => {
@@ -4578,28 +5400,26 @@ var RtfPlugin = class {
4578
5400
  getToolbarActions(instance) {
4579
5401
  const totalPages = instance.getPageCount?.() ?? 1;
4580
5402
  const actions = [];
4581
- if (totalPages > 1) {
4582
- actions.push({
4583
- id: "page-nav",
4584
- icon: "",
4585
- label: "Page Navigation",
4586
- type: "page-nav",
4587
- group: "navigation",
4588
- value: instance.getCurrentPage?.() ?? 1,
4589
- max: totalPages,
4590
- execute: (action, page) => {
4591
- const cur = instance.getCurrentPage?.() ?? 1;
4592
- const max = instance.getPageCount?.() ?? 1;
4593
- if (action === "prev") {
4594
- if (cur > 1) instance.goToPage?.(cur - 1);
4595
- } else if (action === "next") {
4596
- if (cur < max) instance.goToPage?.(cur + 1);
4597
- } else if (typeof page === "number") {
4598
- instance.goToPage?.(page);
4599
- }
5403
+ actions.push({
5404
+ id: "page-nav",
5405
+ icon: "",
5406
+ label: "Page Navigation",
5407
+ type: "page-nav",
5408
+ group: "navigation",
5409
+ value: instance.getCurrentPage?.() ?? 1,
5410
+ max: totalPages,
5411
+ execute: (action, page) => {
5412
+ const cur = instance.getCurrentPage?.() ?? 1;
5413
+ const max = instance.getPageCount?.() ?? 1;
5414
+ if (action === "prev") {
5415
+ if (cur > 1) instance.goToPage?.(cur - 1);
5416
+ } else if (action === "next") {
5417
+ if (cur < max) instance.goToPage?.(cur + 1);
5418
+ } else if (typeof page === "number") {
5419
+ instance.goToPage?.(page);
4600
5420
  }
4601
- });
4602
- }
5421
+ }
5422
+ });
4603
5423
  actions.push(
4604
5424
  {
4605
5425
  id: "zoom-out",
@@ -4671,43 +5491,57 @@ var RtfPlugin = class {
4671
5491
  async render(ctx) {
4672
5492
  const wrapper = document.createElement("div");
4673
5493
  wrapper.className = "fp-rtf-wrapper";
4674
- wrapper.style.padding = "32px";
4675
- wrapper.style.maxWidth = "850px";
5494
+ wrapper.style.padding = "0";
5495
+ wrapper.style.width = "816px";
4676
5496
  wrapper.style.margin = "0 auto";
4677
- wrapper.style.backgroundColor = "#fff";
4678
- wrapper.style.boxShadow = "0 2px 8px rgba(0,0,0,0.08)";
4679
- wrapper.style.borderRadius = "4px";
4680
- wrapper.style.minHeight = "100%";
5497
+ wrapper.style.boxSizing = "border-box";
5498
+ wrapper.style.display = "flex";
5499
+ wrapper.style.flexDirection = "column";
5500
+ wrapper.style.alignItems = "center";
5501
+ wrapper.style.backgroundColor = "transparent";
4681
5502
  wrapper.style.transformOrigin = "top center";
4682
- wrapper.style.transition = "transform 0.2s ease";
4683
- ctx.container.style.overflow = "auto";
4684
- ctx.container.style.padding = "24px";
5503
+ wrapper.style.transition = "transform 0.15s ease";
5504
+ ctx.container.style.overflowX = "hidden";
5505
+ ctx.container.style.overflowY = "auto";
5506
+ ctx.container.style.padding = "16px 8px";
4685
5507
  ctx.container.style.backgroundColor = "#f1f5f9";
4686
5508
  ctx.container.appendChild(wrapper);
4687
5509
  let scale = 1;
4688
5510
  let rotation = 0;
4689
5511
  let pageElements = [];
4690
- let fitMode = "width";
5512
+ let isUserZoomed = false;
5513
+ let fitMode = ctx?.options?.fitMode || "width";
4691
5514
  const calculateFitScale = (mode = fitMode) => {
4692
5515
  const activeEl = pageElements[currentPage - 1] || wrapper;
4693
- const elW = activeEl.offsetWidth || 850;
4694
- const singlePageH = Math.min(activeEl.offsetHeight || 1056, Math.round(elW * 1.32));
4695
- const availW = Math.max(280, ctx.container.clientWidth - 48);
4696
- const availH = Math.max(280, ctx.container.clientHeight - 80);
5516
+ const elW = 816;
5517
+ const singlePageH = activeEl?.offsetHeight || 1056;
5518
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
5519
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
4697
5520
  const sW = availW / elW;
4698
5521
  const sH = availH / singlePageH;
4699
5522
  if (mode === "page") {
4700
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
5523
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
4701
5524
  }
4702
- return Math.max(0.65, Math.min(1.05, sW));
5525
+ return Math.max(0.4, Math.min(3, sW));
4703
5526
  };
4704
5527
  const applyTransform = () => {
4705
5528
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
4706
5529
  wrapper.style.transformOrigin = "top center";
5530
+ const activeEl = pageElements[currentPage - 1];
5531
+ const baseH = activeEl?.offsetHeight || 1056;
5532
+ const scaledH = baseH * scale;
5533
+ const extraH = Math.max(0, scaledH - baseH);
5534
+ wrapper.style.marginBottom = `${extraH + 32}px`;
4707
5535
  };
5536
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
5537
+ if (!isUserZoomed) {
5538
+ scale = calculateFitScale(fitMode);
5539
+ applyTransform();
5540
+ }
5541
+ }) : null;
5542
+ resizeObserver?.observe(ctx.container);
4708
5543
  setTimeout(() => {
4709
- fitMode = "width";
4710
- scale = calculateFitScale("width");
5544
+ scale = calculateFitScale(fitMode);
4711
5545
  applyTransform();
4712
5546
  }, 60);
4713
5547
  try {
@@ -4717,20 +5551,34 @@ var RtfPlugin = class {
4717
5551
  const doc = new RTFJS.Document(ctx.buffer, {});
4718
5552
  const htmlElements = await doc.render();
4719
5553
  const contentNodes = [];
4720
- for (const item of htmlElements) {
4721
- if (item.children && item.children.length > 0 && !item.tagName.toLowerCase().startsWith("table")) {
4722
- contentNodes.push(...Array.from(item.children));
4723
- } else {
4724
- contentNodes.push(item);
5554
+ const extractBlocks = (nodes) => {
5555
+ for (const item of nodes) {
5556
+ if (!item || !(item instanceof HTMLElement)) continue;
5557
+ const tag = item.tagName.toLowerCase();
5558
+ const hasChildBlocks = Array.from(item.children).some((c) => {
5559
+ const ct = c.tagName.toLowerCase();
5560
+ return ["p", "div", "table", "h1", "h2", "h3", "h4", "h5", "h6", "section", "article", "ul", "ol"].includes(ct);
5561
+ });
5562
+ if (hasChildBlocks && !["table", "tr", "td", "th", "ul", "ol", "li"].includes(tag)) {
5563
+ extractBlocks(Array.from(item.children));
5564
+ } else {
5565
+ if (tag === "p" || tag === "div") {
5566
+ item.style.display = "block";
5567
+ item.style.marginBottom = "12px";
5568
+ item.style.lineHeight = "1.6";
5569
+ }
5570
+ contentNodes.push(item);
5571
+ }
4725
5572
  }
4726
- }
5573
+ };
5574
+ extractBlocks(htmlElements);
4727
5575
  wrapper.innerHTML = "";
4728
5576
  contentNodes.forEach((node) => wrapper.appendChild(node));
4729
5577
  const childHeights = contentNodes.map((c) => {
4730
5578
  const rectH = c.getBoundingClientRect ? c.getBoundingClientRect().height : 0;
4731
5579
  const offH = c.offsetHeight || 0;
4732
5580
  const textLen = c.textContent?.trim().length || 0;
4733
- const estH = Math.max(24, Math.ceil(textLen / 75) * 22 + 14);
5581
+ const estH = Math.max(28, Math.ceil(textLen / 75) * 24 + 16);
4734
5582
  return Math.max(rectH, offH, estH);
4735
5583
  });
4736
5584
  wrapper.innerHTML = "";
@@ -4747,6 +5595,7 @@ var RtfPlugin = class {
4747
5595
  card.style.marginBottom = "24px";
4748
5596
  card.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
4749
5597
  card.style.lineHeight = "1.6";
5598
+ card.style.color = "#1e293b";
4750
5599
  return card;
4751
5600
  };
4752
5601
  let curCard = createRtfCard();
@@ -4782,9 +5631,8 @@ var RtfPlugin = class {
4782
5631
  pageCard.style.padding = "72px 56px";
4783
5632
  pageCard.style.width = "816px";
4784
5633
  pageCard.style.minHeight = "1056px";
4785
- pageCard.style.maxWidth = "100%";
4786
5634
  pageCard.style.boxSizing = "border-box";
4787
- pageCard.style.fontFamily = "serif";
5635
+ pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
4788
5636
  pageCard.style.fontSize = "12pt";
4789
5637
  pageCard.style.lineHeight = "1.6";
4790
5638
  pageCard.style.color = "#1e293b";
@@ -4797,29 +5645,6 @@ var RtfPlugin = class {
4797
5645
  }
4798
5646
  const totalPages = Math.max(1, pageElements.length);
4799
5647
  let currentPage = 1;
4800
- let indicator = null;
4801
- if (totalPages > 1) {
4802
- indicator = document.createElement("div");
4803
- indicator.className = "fp-rtf-page-indicator";
4804
- indicator.style.position = "sticky";
4805
- indicator.style.bottom = "16px";
4806
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
4807
- indicator.style.backdropFilter = "blur(8px)";
4808
- indicator.style.color = "#f8fafc";
4809
- indicator.style.fontSize = "12px";
4810
- indicator.style.fontWeight = "600";
4811
- indicator.style.padding = "5px 14px";
4812
- indicator.style.borderRadius = "20px";
4813
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
4814
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
4815
- indicator.style.zIndex = "10";
4816
- indicator.style.userSelect = "none";
4817
- indicator.style.pointerEvents = "none";
4818
- indicator.style.textAlign = "center";
4819
- indicator.style.width = "fit-content";
4820
- indicator.style.margin = "16px auto 0";
4821
- ctx.container.appendChild(indicator);
4822
- }
4823
5648
  const showPage = (pageNum) => {
4824
5649
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
4825
5650
  if (totalPages > 1) {
@@ -4827,9 +5652,6 @@ var RtfPlugin = class {
4827
5652
  el.style.display = idx + 1 === currentPage ? "block" : "none";
4828
5653
  });
4829
5654
  }
4830
- if (indicator) {
4831
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4832
- }
4833
5655
  ctx.container.scrollTop = 0;
4834
5656
  ctx.emit("page-change", { page: currentPage, total: totalPages });
4835
5657
  };
@@ -4837,7 +5659,7 @@ var RtfPlugin = class {
4837
5659
  showPage(1);
4838
5660
  }
4839
5661
  const cleanup = () => {
4840
- indicator?.remove();
5662
+ resizeObserver?.disconnect();
4841
5663
  wrapper.remove();
4842
5664
  ctx.container.innerHTML = "";
4843
5665
  };
@@ -4848,21 +5670,25 @@ var RtfPlugin = class {
4848
5670
  getCurrentPage: () => currentPage,
4849
5671
  goToPage: (page) => showPage(page),
4850
5672
  zoomIn: () => {
5673
+ isUserZoomed = true;
4851
5674
  scale += 0.15;
4852
5675
  applyTransform();
4853
5676
  },
4854
5677
  zoomOut: () => {
5678
+ isUserZoomed = true;
4855
5679
  scale = Math.max(0.2, scale - 0.15);
4856
5680
  applyTransform();
4857
5681
  },
4858
5682
  getZoom: () => scale,
4859
5683
  setZoom: (level) => {
5684
+ isUserZoomed = true;
4860
5685
  scale = level;
4861
5686
  applyTransform();
4862
5687
  },
4863
5688
  fitToPage: () => {
4864
- fitMode = fitMode === "width" ? "page" : "width";
4865
- scale = calculateFitScale(fitMode);
5689
+ isUserZoomed = false;
5690
+ fitMode = "width";
5691
+ scale = calculateFitScale("width");
4866
5692
  rotation = 0;
4867
5693
  applyTransform();
4868
5694
  },
@@ -5050,40 +5876,36 @@ var OpenDocumentPlugin = class {
5050
5876
  const isPresentation = instance.isPresentation;
5051
5877
  const totalPages = instance.getPageCount?.() ?? 1;
5052
5878
  const actions = [];
5053
- if (isPresentation || totalPages > 1) {
5054
- if (isPresentation) {
5055
- actions.push({
5056
- id: "thumbnails",
5057
- icon: "thumbnails",
5058
- label: "Slide Thumbnails",
5059
- type: "button",
5060
- group: "navigation",
5061
- execute: () => instance.toggleThumbnails?.()
5062
- });
5063
- }
5064
- actions.push({
5065
- id: "page-nav",
5066
- icon: "",
5067
- label: isPresentation ? "Slide Navigation" : "Page Navigation",
5068
- type: "page-nav",
5069
- group: "navigation",
5070
- value: instance.getCurrentPage?.() ?? 1,
5071
- max: totalPages,
5072
- execute: (action, page) => {
5073
- const cur = instance.getCurrentPage?.() ?? 1;
5074
- const max = instance.getPageCount?.() ?? 1;
5075
- if (action === "prev") {
5076
- if (cur > 1) instance.goToPage?.(cur - 1);
5077
- } else if (action === "next") {
5078
- if (cur < max) instance.goToPage?.(cur + 1);
5079
- } else if (typeof page === "number") {
5080
- instance.goToPage?.(page);
5081
- } else if (typeof action === "number") {
5082
- instance.goToPage?.(action);
5083
- }
5879
+ actions.push({
5880
+ id: "thumbnails",
5881
+ icon: "thumbnails",
5882
+ label: isPresentation ? "Slide Thumbnails" : "Page Thumbnails",
5883
+ type: "button",
5884
+ group: "navigation",
5885
+ execute: () => instance.toggleThumbnails?.()
5886
+ });
5887
+ actions.push({
5888
+ id: "page-nav",
5889
+ icon: "",
5890
+ label: isPresentation ? "Slide Navigation" : "Page Navigation",
5891
+ type: "page-nav",
5892
+ group: "navigation",
5893
+ value: instance.getCurrentPage?.() ?? 1,
5894
+ max: totalPages,
5895
+ execute: (action, page) => {
5896
+ const cur = instance.getCurrentPage?.() ?? 1;
5897
+ const max = instance.getPageCount?.() ?? 1;
5898
+ if (action === "prev") {
5899
+ if (cur > 1) instance.goToPage?.(cur - 1);
5900
+ } else if (action === "next") {
5901
+ if (cur < max) instance.goToPage?.(cur + 1);
5902
+ } else if (typeof page === "number") {
5903
+ instance.goToPage?.(page);
5904
+ } else if (typeof action === "number") {
5905
+ instance.goToPage?.(action);
5084
5906
  }
5085
- });
5086
- }
5907
+ }
5908
+ });
5087
5909
  actions.push(
5088
5910
  {
5089
5911
  id: "zoom-out",
@@ -5174,49 +5996,65 @@ var OpenDocumentPlugin = class {
5174
5996
  container.className = "fp-odf-container";
5175
5997
  container.style.width = "100%";
5176
5998
  container.style.height = "100%";
5177
- container.style.overflow = "auto";
5178
- container.style.padding = "24px";
5999
+ container.style.overflowX = "hidden";
6000
+ container.style.overflowY = "auto";
6001
+ container.style.padding = "16px 8px";
5179
6002
  container.style.backgroundColor = "#f1f5f9";
5180
6003
  const wrapper = document.createElement("div");
5181
6004
  wrapper.className = "fp-odf-wrapper";
5182
6005
  wrapper.style.margin = "0 auto";
6006
+ wrapper.style.width = isPresentation ? "960px" : "816px";
6007
+ wrapper.style.boxSizing = "border-box";
5183
6008
  wrapper.style.backgroundColor = "#ffffff";
5184
6009
  wrapper.style.boxShadow = "0 2px 10px rgba(0,0,0,0.08)";
5185
6010
  wrapper.style.borderRadius = "4px";
5186
6011
  wrapper.style.transformOrigin = "top center";
5187
- wrapper.style.transition = "transform 0.2s ease";
6012
+ wrapper.style.transition = "transform 0.15s ease";
5188
6013
  container.appendChild(wrapper);
5189
6014
  ctx.container.appendChild(container);
5190
6015
  let scale = 1;
5191
6016
  let rotation = 0;
5192
- let fitMode = "width";
6017
+ let isUserZoomed = false;
6018
+ let fitMode = ctx?.options?.fitMode || "width";
6019
+ let currentPage = 1;
6020
+ let totalPages = 1;
6021
+ let slides = [];
5193
6022
  const calculateFitScale = (mode = fitMode) => {
5194
- const elW = wrapper.offsetWidth || 850;
5195
- const singlePageH = Math.min(wrapper.offsetHeight || 1056, Math.round(elW * 1.32));
5196
- const availW = Math.max(280, ctx.container.clientWidth - 48);
5197
- const availH = Math.max(280, ctx.container.clientHeight - 80);
6023
+ const activeSlide = slides[currentPage - 1];
6024
+ const elW = isPresentation ? 960 : 816;
6025
+ const singlePageH = Math.min(activeSlide?.offsetHeight || wrapper.offsetHeight || 1056, Math.round(elW * 1.32));
6026
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
6027
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
5198
6028
  const sW = availW / elW;
5199
6029
  const sH = availH / (isPresentation ? 540 : singlePageH);
5200
6030
  if (isPresentation) {
5201
- return Math.min(1, Math.min(sW, sH));
6031
+ return Math.min(2.5, Math.min(sW, sH));
5202
6032
  }
5203
6033
  if (mode === "page") {
5204
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
6034
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
5205
6035
  }
5206
- return Math.max(0.65, Math.min(1.05, sW));
6036
+ return Math.max(0.4, Math.min(3, sW));
5207
6037
  };
5208
6038
  const applyTransform = () => {
5209
6039
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5210
6040
  wrapper.style.transformOrigin = "top center";
6041
+ const activeSlide = slides[currentPage - 1];
6042
+ const baseH = activeSlide?.offsetHeight || wrapper.offsetHeight || 1056;
6043
+ const scaledH = baseH * scale;
6044
+ const extraH = Math.max(0, scaledH - baseH);
6045
+ wrapper.style.marginBottom = `${extraH + 32}px`;
5211
6046
  };
6047
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
6048
+ if (!isUserZoomed) {
6049
+ scale = calculateFitScale(fitMode);
6050
+ applyTransform();
6051
+ }
6052
+ }) : null;
6053
+ resizeObserver?.observe(ctx.container);
5212
6054
  setTimeout(() => {
5213
- fitMode = "width";
5214
- scale = calculateFitScale("width");
6055
+ scale = calculateFitScale(fitMode);
5215
6056
  applyTransform();
5216
6057
  }, 60);
5217
- let currentPage = 1;
5218
- let totalPages = 1;
5219
- let slides = [];
5220
6058
  if (isPresentation) {
5221
6059
  wrapper.style.maxWidth = "960px";
5222
6060
  wrapper.style.aspectRatio = "16 / 9";
@@ -5317,24 +6155,9 @@ var OpenDocumentPlugin = class {
5317
6155
  wrapper.appendChild(page);
5318
6156
  slides.push(page);
5319
6157
  });
5320
- const pageIndicator = document.createElement("div");
5321
- pageIndicator.className = "fp-odt-page-indicator";
5322
- pageIndicator.style.position = "sticky";
5323
- pageIndicator.style.bottom = "16px";
5324
- pageIndicator.style.left = "50%";
5325
- pageIndicator.style.transform = "translateX(-50%)";
5326
- pageIndicator.style.backgroundColor = "rgba(0, 0, 0, 0.6)";
5327
- pageIndicator.style.color = "#fff";
5328
- pageIndicator.style.padding = "6px 12px";
5329
- pageIndicator.style.borderRadius = "16px";
5330
- pageIndicator.style.fontSize = "12px";
5331
- pageIndicator.style.zIndex = "100";
5332
- pageIndicator.style.display = "inline-block";
5333
- pageIndicator.style.width = "fit-content";
5334
- pageIndicator.textContent = `Page 1 of ${totalPages}`;
5335
- container.appendChild(pageIndicator);
5336
6158
  }
5337
6159
  const cleanup = () => {
6160
+ resizeObserver?.disconnect();
5338
6161
  imageUrls.forEach((url) => URL.revokeObjectURL(url));
5339
6162
  container.remove();
5340
6163
  ctx.container.innerHTML = "";
@@ -5346,10 +6169,6 @@ var OpenDocumentPlugin = class {
5346
6169
  slides.forEach((s, idx) => {
5347
6170
  s.style.display = idx === page - 1 ? "block" : "none";
5348
6171
  });
5349
- const indicator = container.querySelector(".fp-odt-page-indicator");
5350
- if (indicator) {
5351
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
5352
- }
5353
6172
  container.scrollTop = 0;
5354
6173
  ctx.emit("page-change", { page: currentPage, total: totalPages });
5355
6174
  };
@@ -5357,21 +6176,25 @@ var OpenDocumentPlugin = class {
5357
6176
  destroy: cleanup,
5358
6177
  isPresentation,
5359
6178
  zoomIn: () => {
6179
+ isUserZoomed = true;
5360
6180
  scale += 0.15;
5361
6181
  applyTransform();
5362
6182
  },
5363
6183
  zoomOut: () => {
6184
+ isUserZoomed = true;
5364
6185
  scale = Math.max(0.2, scale - 0.15);
5365
6186
  applyTransform();
5366
6187
  },
5367
6188
  getZoom: () => scale,
5368
6189
  setZoom: (level) => {
6190
+ isUserZoomed = true;
5369
6191
  scale = level;
5370
6192
  applyTransform();
5371
6193
  },
5372
6194
  fitToPage: () => {
5373
- fitMode = fitMode === "width" ? "page" : "width";
5374
- scale = calculateFitScale(fitMode);
6195
+ isUserZoomed = false;
6196
+ fitMode = "width";
6197
+ scale = calculateFitScale("width");
5375
6198
  rotation = 0;
5376
6199
  applyTransform();
5377
6200
  },
@@ -5387,23 +6210,45 @@ var OpenDocumentPlugin = class {
5387
6210
  getPageCount: () => totalPages,
5388
6211
  getCurrentPage: () => currentPage,
5389
6212
  getThumbnails: async () => {
5390
- if (!isPresentation) return [];
5391
- return slides.map((_, idx) => ({
5392
- index: idx,
5393
- label: `Slide ${idx + 1}`,
5394
- render: async (canvas) => {
5395
- const ctx2d = canvas.getContext("2d");
5396
- if (!ctx2d) return;
5397
- canvas.width = 160;
5398
- canvas.height = 90;
5399
- ctx2d.fillStyle = "#f8fafc";
5400
- ctx2d.fillRect(0, 0, 160, 90);
5401
- ctx2d.fillStyle = "#334155";
5402
- ctx2d.font = "bold 12px sans-serif";
5403
- ctx2d.textAlign = "center";
5404
- ctx2d.fillText(`Slide ${idx + 1}`, 80, 50);
5405
- }
5406
- }));
6213
+ const count = totalPages || slides.length || 1;
6214
+ const items = [];
6215
+ for (let idx = 0; idx < count; idx++) {
6216
+ const itemIdx = idx;
6217
+ items.push({
6218
+ index: itemIdx,
6219
+ label: isPresentation ? `Slide ${itemIdx + 1}` : `Page ${itemIdx + 1}`,
6220
+ render: async (canvas) => {
6221
+ const ctx2d = canvas.getContext("2d");
6222
+ if (!ctx2d) return;
6223
+ if (isPresentation) {
6224
+ canvas.width = 160;
6225
+ canvas.height = 90;
6226
+ ctx2d.fillStyle = "#f8fafc";
6227
+ ctx2d.fillRect(0, 0, 160, 90);
6228
+ ctx2d.strokeStyle = "#cbd5e1";
6229
+ ctx2d.lineWidth = 1;
6230
+ ctx2d.strokeRect(0.5, 0.5, 159, 89);
6231
+ ctx2d.fillStyle = "#334155";
6232
+ ctx2d.font = "bold 11px sans-serif";
6233
+ ctx2d.textAlign = "center";
6234
+ ctx2d.fillText(`Slide ${itemIdx + 1}`, 80, 50);
6235
+ } else {
6236
+ canvas.width = 140;
6237
+ canvas.height = 180;
6238
+ ctx2d.fillStyle = "#ffffff";
6239
+ ctx2d.fillRect(0, 0, 140, 180);
6240
+ ctx2d.strokeStyle = "#cbd5e1";
6241
+ ctx2d.lineWidth = 1;
6242
+ ctx2d.strokeRect(0.5, 0.5, 139, 179);
6243
+ ctx2d.fillStyle = "#64748b";
6244
+ ctx2d.font = "bold 10px sans-serif";
6245
+ ctx2d.textAlign = "center";
6246
+ ctx2d.fillText(`Page ${itemIdx + 1}`, 70, 90);
6247
+ }
6248
+ }
6249
+ });
6250
+ }
6251
+ return items;
5407
6252
  },
5408
6253
  download: () => {
5409
6254
  const mimeType = this.mimeTypes.find((m) => m.includes(ext.replace(".", ""))) || "application/octet-stream";
@@ -5508,23 +6353,24 @@ var OpenDocumentPlugin = class {
5508
6353
  case "h": {
5509
6354
  const level = Math.min(6, Math.max(1, Number(node.getAttribute("text:outline-level") || 1)));
5510
6355
  const style = styles.get(node.getAttribute("text:style-name") || "") || "";
5511
- html += `<h${level} style="${style}; font-family: -apple-system, BlinkMacSystemFont, sans-serif;">${node.textContent || ""}</h${level}>`;
6356
+ html += `<h${level} style="${style}; margin: 18px 0 8px; color: #1e3a8a; font-family: Calibri, 'Segoe UI', Arial, sans-serif;">${node.textContent || ""}</h${level}>`;
5512
6357
  break;
5513
6358
  }
5514
6359
  case "p": {
5515
6360
  const style = styles.get(node.getAttribute("text:style-name") || "") || "";
5516
6361
  const inner = this.renderSpans(node, styles, images);
5517
- html += `<p style="${style}; line-height: 1.6; margin: 8px 0; font-family: -apple-system, BlinkMacSystemFont, sans-serif;">${inner}</p>`;
6362
+ const isInsideLi = node.parentElement?.localName === "list-item" || node.parentElement?.tagName?.toLowerCase() === "list-item";
6363
+ html += `<p style="${style}; line-height: 1.5; margin: ${isInsideLi ? "0" : "8px 0 12px"}; font-family: Calibri, 'Segoe UI', Arial, sans-serif;">${inner}</p>`;
5518
6364
  break;
5519
6365
  }
5520
6366
  case "list": {
5521
- html += '<ul style="margin: 8px 0; padding-left: 24px;">';
6367
+ html += '<ul style="margin: 8px 0 12px; padding-left: 24px;">';
5522
6368
  Array.from(node.children).forEach((c) => walk(c));
5523
6369
  html += "</ul>";
5524
6370
  break;
5525
6371
  }
5526
6372
  case "list-item": {
5527
- html += "<li>";
6373
+ html += '<li style="margin: 4px 0; font-size: 11pt; line-height: 1.5; color: #1e293b;">';
5528
6374
  Array.from(node.children).forEach((c) => walk(c));
5529
6375
  html += "</li>";
5530
6376
  break;
@@ -5650,28 +6496,26 @@ var DocPlugin = class {
5650
6496
  getToolbarActions(instance) {
5651
6497
  const totalPages = instance.getPageCount?.() ?? 1;
5652
6498
  const actions = [];
5653
- if (totalPages > 1) {
5654
- actions.push({
5655
- id: "page-nav",
5656
- icon: "",
5657
- label: "Page Navigation",
5658
- type: "page-nav",
5659
- group: "navigation",
5660
- value: instance.getCurrentPage?.() ?? 1,
5661
- max: totalPages,
5662
- execute: (action, page) => {
5663
- const cur = instance.getCurrentPage?.() ?? 1;
5664
- const max = instance.getPageCount?.() ?? 1;
5665
- if (action === "prev") {
5666
- if (cur > 1) instance.goToPage?.(cur - 1);
5667
- } else if (action === "next") {
5668
- if (cur < max) instance.goToPage?.(cur + 1);
5669
- } else if (typeof page === "number") {
5670
- instance.goToPage?.(page);
5671
- }
6499
+ actions.push({
6500
+ id: "page-nav",
6501
+ icon: "",
6502
+ label: "Page Navigation",
6503
+ type: "page-nav",
6504
+ group: "navigation",
6505
+ value: instance.getCurrentPage?.() ?? 1,
6506
+ max: totalPages,
6507
+ execute: (action, page) => {
6508
+ const cur = instance.getCurrentPage?.() ?? 1;
6509
+ const max = instance.getPageCount?.() ?? 1;
6510
+ if (action === "prev") {
6511
+ if (cur > 1) instance.goToPage?.(cur - 1);
6512
+ } else if (action === "next") {
6513
+ if (cur < max) instance.goToPage?.(cur + 1);
6514
+ } else if (typeof page === "number") {
6515
+ instance.goToPage?.(page);
5672
6516
  }
5673
- });
5674
- }
6517
+ }
6518
+ });
5675
6519
  actions.push(
5676
6520
  {
5677
6521
  id: "zoom-out",
@@ -5745,8 +6589,9 @@ var DocPlugin = class {
5745
6589
  container.className = "fp-doc-container";
5746
6590
  container.style.width = "100%";
5747
6591
  container.style.height = "100%";
5748
- container.style.overflow = "auto";
5749
- container.style.padding = "32px 16px";
6592
+ container.style.overflowX = "hidden";
6593
+ container.style.overflowY = "auto";
6594
+ container.style.padding = "16px 8px";
5750
6595
  container.style.backgroundColor = "#f1f5f9";
5751
6596
  container.style.display = "flex";
5752
6597
  container.style.justifyContent = "center";
@@ -5791,16 +6636,15 @@ var DocPlugin = class {
5791
6636
  const pageCard = document.createElement("div");
5792
6637
  pageCard.className = "fp-doc-page-card";
5793
6638
  pageCard.style.width = "816px";
5794
- pageCard.style.height = "1056px";
6639
+ pageCard.style.minHeight = "1056px";
5795
6640
  pageCard.style.backgroundColor = "#ffffff";
5796
- pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
6641
+ pageCard.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
5797
6642
  pageCard.style.borderRadius = "4px";
5798
- pageCard.style.padding = "96px 72px";
6643
+ pageCard.style.padding = "72px 56px";
5799
6644
  pageCard.style.boxSizing = "border-box";
5800
- pageCard.style.overflow = "hidden";
5801
6645
  pageCard.style.display = i === 0 ? "block" : "none";
5802
6646
  pageCard.style.transformOrigin = "top center";
5803
- pageCard.style.transition = "transform 0.2s ease";
6647
+ pageCard.style.transition = "transform 0.15s ease";
5804
6648
  pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
5805
6649
  pageCard.style.color = "#1e293b";
5806
6650
  if (isFallback && i === 0) {
@@ -5817,34 +6661,15 @@ var DocPlugin = class {
5817
6661
  container.appendChild(pageCard);
5818
6662
  pageCards.push(pageCard);
5819
6663
  }
5820
- let indicator = null;
5821
- if (totalPages > 1) {
5822
- indicator = document.createElement("div");
5823
- indicator.className = "fp-doc-page-indicator";
5824
- indicator.style.position = "fixed";
5825
- indicator.style.bottom = "16px";
5826
- indicator.style.left = "50%";
5827
- indicator.style.transform = "translateX(-50%)";
5828
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
5829
- indicator.style.backdropFilter = "blur(8px)";
5830
- indicator.style.color = "#f8fafc";
5831
- indicator.style.fontSize = "12px";
5832
- indicator.style.fontWeight = "600";
5833
- indicator.style.padding = "5px 14px";
5834
- indicator.style.borderRadius = "20px";
5835
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
5836
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
5837
- indicator.style.zIndex = "10";
5838
- indicator.style.userSelect = "none";
5839
- indicator.style.pointerEvents = "none";
5840
- indicator.style.textAlign = "center";
5841
- container.appendChild(indicator);
5842
- }
5843
6664
  let rotation = 0;
5844
6665
  const applyTransform = () => {
5845
6666
  const activeCard = pageCards[currentPage - 1];
5846
6667
  if (activeCard) {
5847
6668
  activeCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
6669
+ const baseH = activeCard.offsetHeight || 1056;
6670
+ const scaledH = baseH * scale;
6671
+ const extraH = Math.max(0, scaledH - baseH);
6672
+ activeCard.style.marginBottom = `${extraH + 32}px`;
5848
6673
  }
5849
6674
  };
5850
6675
  const showPage = (pageNum) => {
@@ -5857,35 +6682,39 @@ var DocPlugin = class {
5857
6682
  card.style.display = "none";
5858
6683
  }
5859
6684
  });
5860
- if (indicator) {
5861
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
5862
- }
5863
6685
  container.scrollTop = 0;
5864
6686
  ctx.emit("page-change", { page: currentPage, total: totalPages });
5865
6687
  };
5866
6688
  if (totalPages > 1) {
5867
6689
  showPage(1);
5868
6690
  }
5869
- let fitMode = "width";
6691
+ let fitMode = ctx?.options?.fitMode || "width";
6692
+ let isUserZoomed = false;
5870
6693
  const calculateFitScale = (mode = fitMode) => {
5871
6694
  const elW = 816;
5872
6695
  const elH = 1056;
5873
- const availW = Math.max(280, ctx.container.clientWidth - 48);
5874
- const availH = Math.max(280, ctx.container.clientHeight - 80);
6696
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
6697
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
5875
6698
  const sW = availW / elW;
5876
6699
  const sH = availH / elH;
5877
6700
  if (mode === "page") {
5878
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
6701
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
5879
6702
  }
5880
- return Math.max(0.65, Math.min(1.05, sW));
6703
+ return Math.max(0.4, Math.min(3, sW));
5881
6704
  };
6705
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
6706
+ if (!isUserZoomed) {
6707
+ scale = calculateFitScale(fitMode);
6708
+ applyTransform();
6709
+ }
6710
+ }) : null;
6711
+ resizeObserver?.observe(ctx.container);
5882
6712
  setTimeout(() => {
5883
- fitMode = "width";
5884
- scale = calculateFitScale("width");
6713
+ scale = calculateFitScale(fitMode);
5885
6714
  applyTransform();
5886
6715
  }, 60);
5887
6716
  const cleanup = () => {
5888
- indicator?.remove();
6717
+ resizeObserver?.disconnect();
5889
6718
  container.remove();
5890
6719
  ctx.container.innerHTML = "";
5891
6720
  };
@@ -5896,21 +6725,25 @@ var DocPlugin = class {
5896
6725
  getCurrentPage: () => currentPage,
5897
6726
  goToPage: (page) => showPage(page),
5898
6727
  zoomIn: () => {
6728
+ isUserZoomed = true;
5899
6729
  scale += 0.15;
5900
6730
  applyTransform();
5901
6731
  },
5902
6732
  zoomOut: () => {
6733
+ isUserZoomed = true;
5903
6734
  scale = Math.max(0.2, scale - 0.15);
5904
6735
  applyTransform();
5905
6736
  },
5906
6737
  getZoom: () => scale,
5907
6738
  setZoom: (level) => {
6739
+ isUserZoomed = true;
5908
6740
  scale = level;
5909
6741
  applyTransform();
5910
6742
  },
5911
6743
  fitToPage: () => {
5912
- fitMode = fitMode === "width" ? "page" : "width";
5913
- scale = calculateFitScale(fitMode);
6744
+ isUserZoomed = false;
6745
+ fitMode = "width";
6746
+ scale = calculateFitScale("width");
5914
6747
  rotation = 0;
5915
6748
  applyTransform();
5916
6749
  },
@@ -6287,25 +7120,31 @@ ${chartSvg}
6287
7120
  i++;
6288
7121
  continue;
6289
7122
  }
6290
- if (line.length < 60 && !line.endsWith(".") && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#"))) {
7123
+ if (/^\d{1,2}$/.test(line.trim())) {
7124
+ i++;
7125
+ continue;
7126
+ }
7127
+ const isShortLine = line.length < 60 && !line.endsWith(".") && !line.endsWith(",");
7128
+ 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));
7129
+ if (isTitleLike) {
6291
7130
  if (inList) {
6292
7131
  html += "</ul>";
6293
7132
  inList = false;
6294
7133
  }
6295
- html += `<h2 style="font-size: 16px; font-weight: 700; color: #1e3a8a; margin: 16px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify6.sanitize(line, sanitizeOptions)}</h2>`;
7134
+ html += `<h2 style="font-size: 18px; font-weight: 700; color: #1e3a8a; margin: 18px 0 10px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify6.sanitize(line, sanitizeOptions)}</h2>`;
6296
7135
  } else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
6297
7136
  if (!inList) {
6298
- html += '<ul style="margin: 8px 0; padding-left: 24px;">';
7137
+ html += '<ul style="margin: 8px 0 12px; padding-left: 24px;">';
6299
7138
  inList = true;
6300
7139
  }
6301
7140
  const bulletText = line.replace(/^[•\-\*]\s*/, "");
6302
- html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6.sanitize(bulletText, sanitizeOptions)}</li>`;
7141
+ html += `<li style="margin: 4px 0; line-height: 1.5; font-size: 11pt; color: #1e293b;">${DOMPurify6.sanitize(bulletText, sanitizeOptions)}</li>`;
6303
7142
  } else {
6304
7143
  if (inList) {
6305
7144
  html += "</ul>";
6306
7145
  inList = false;
6307
7146
  }
6308
- html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6.sanitize(line, sanitizeOptions)}</p>`;
7147
+ html += `<p style="line-height: 1.5; margin: 0 0 12px 0; font-size: 11pt; color: #1e293b; text-align: justify;">${DOMPurify6.sanitize(line, sanitizeOptions)}</p>`;
6309
7148
  }
6310
7149
  i++;
6311
7150
  }