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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/angular.js CHANGED
@@ -542,8 +542,7 @@ var ICON_FULLSCREEN = `<svg width="24" height="24" viewBox="0 0 24 24" fill="non
542
542
  var ICON_COPY = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path></svg>`;
543
543
  var ICON_FAST_FORWARD = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 19 22 12 13 5 13 19"></polygon><polygon points="2 19 11 12 2 5 2 19"></polygon></svg>`;
544
544
  var ICON_REWIND = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="11 19 2 12 11 5 11 19"></polygon><polygon points="22 19 13 12 22 5 22 19"></polygon></svg>`;
545
- var ICON_SPEED = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"></circle><polyline points="12 6 12 12 16 14"></polyline></svg>`;
546
- var ICON_EXTERNAL_WINDOW = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>`;
545
+ var ICON_EXTERNAL_WINDOW = `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="3" y="3" width="18" height="18" rx="2"></rect><line x1="3" y1="8" x2="21" y2="8"></line><circle cx="6" cy="5.5" r="0.75" fill="currentColor"></circle><circle cx="8.5" cy="5.5" r="0.75" fill="currentColor"></circle><polyline points="14 13 18 13 18 17"></polyline><line x1="11" y1="20" x2="18" y2="13"></line></svg>`;
547
546
  var ICON_MAP = {
548
547
  "zoom-in": ICON_ZOOM_IN,
549
548
  "zoom-out": ICON_ZOOM_OUT,
@@ -570,28 +569,177 @@ var ICON_MAP = {
570
569
  "forward-10": ICON_FAST_FORWARD,
571
570
  "rewind": ICON_REWIND,
572
571
  "replay-10": ICON_REWIND,
573
- "speed": ICON_SPEED,
574
572
  "open-window": ICON_EXTERNAL_WINDOW,
575
- "external-window": ICON_EXTERNAL_WINDOW
573
+ "external-window": ICON_EXTERNAL_WINDOW,
574
+ "openWindow": ICON_EXTERNAL_WINDOW,
575
+ "openSeparateWindow": ICON_EXTERNAL_WINDOW
576
576
  };
577
577
  var ToolbarController = class {
578
578
  el;
579
579
  toolbarEl;
580
580
  actions = [];
581
+ config = {};
581
582
  pageInputEl = null;
582
583
  pageLabelEl = null;
583
- constructor(container) {
584
+ prevPageBtn = null;
585
+ nextPageBtn = null;
586
+ constructor(container, config) {
584
587
  this.el = container;
588
+ if (config) this.config = { ...config };
585
589
  this.toolbarEl = createElement("div", { className: "fp-toolbar" });
586
590
  this.el.appendChild(this.toolbarEl);
587
591
  }
592
+ setConfig(config) {
593
+ this.config = { ...config };
594
+ this.render();
595
+ }
596
+ getConfig() {
597
+ return { ...this.config };
598
+ }
599
+ hideAction(actionId) {
600
+ this.config[actionId] = false;
601
+ if (actionId === "zoom-in" || actionId === "zoomIn") this.config.zoomIn = false;
602
+ if (actionId === "zoom-out" || actionId === "zoomOut") this.config.zoomOut = false;
603
+ if (actionId === "fit-page" || actionId === "fitPage" || actionId === "fitToPage" || actionId === "fit-width") {
604
+ this.config.fitPage = false;
605
+ this.config.fitToPage = false;
606
+ this.config.fitWidth = false;
607
+ }
608
+ if (actionId === "rotate-cw" || actionId === "rotate" || actionId === "rotateCW") this.config.rotate = false;
609
+ if (actionId === "fullscreen") this.config.fullscreen = false;
610
+ if (actionId === "download") this.config.download = false;
611
+ if (actionId === "print") this.config.print = false;
612
+ if (actionId === "open-window" || actionId === "openWindow" || actionId === "openSeparateWindow") {
613
+ this.config.openWindow = false;
614
+ this.config.openSeparateWindow = false;
615
+ }
616
+ if (actionId === "copy") this.config.copy = false;
617
+ if (actionId === "page-nav" || actionId === "pageNav" || actionId === "pagination") {
618
+ this.config.pageNav = false;
619
+ this.config.pagination = false;
620
+ }
621
+ this.render();
622
+ }
623
+ showAction(actionId) {
624
+ this.config[actionId] = true;
625
+ if (actionId === "zoom-in" || actionId === "zoomIn") this.config.zoomIn = true;
626
+ if (actionId === "zoom-out" || actionId === "zoomOut") this.config.zoomOut = true;
627
+ if (actionId === "fit-page" || actionId === "fitPage" || actionId === "fitToPage" || actionId === "fit-width") {
628
+ this.config.fitPage = true;
629
+ this.config.fitToPage = true;
630
+ this.config.fitWidth = true;
631
+ }
632
+ if (actionId === "rotate-cw" || actionId === "rotate" || actionId === "rotateCW") this.config.rotate = true;
633
+ if (actionId === "fullscreen") this.config.fullscreen = true;
634
+ if (actionId === "download") this.config.download = true;
635
+ if (actionId === "print") this.config.print = true;
636
+ if (actionId === "open-window" || actionId === "openWindow" || actionId === "openSeparateWindow") {
637
+ this.config.openWindow = true;
638
+ this.config.openSeparateWindow = true;
639
+ }
640
+ if (actionId === "copy") this.config.copy = true;
641
+ if (actionId === "page-nav" || actionId === "pageNav" || actionId === "pagination") {
642
+ this.config.pageNav = true;
643
+ this.config.pagination = true;
644
+ }
645
+ this.render();
646
+ }
647
+ normalizeActionId(actionId) {
648
+ if (actionId === "zoomIn") return "zoom-in";
649
+ if (actionId === "zoomOut") return "zoom-out";
650
+ if (actionId === "fitPage" || actionId === "fitToPage" || actionId === "fitWidth") return "fit-page";
651
+ if (actionId === "rotateCW") return "rotate-cw";
652
+ if (actionId === "rotateCCW") return "rotate-ccw";
653
+ if (actionId === "openWindow" || actionId === "openSeparateWindow") return "open-window";
654
+ if (actionId === "pageNav") return "page-nav";
655
+ return actionId;
656
+ }
657
+ enableAction(actionId) {
658
+ const norm = this.normalizeActionId(actionId);
659
+ const btn = this.toolbarEl.querySelector(`button[data-action-id="${actionId}"], button[data-action-id="${norm}"]`);
660
+ if (btn) {
661
+ btn.disabled = false;
662
+ btn.classList.remove("disabled");
663
+ }
664
+ }
665
+ disableAction(actionId) {
666
+ const norm = this.normalizeActionId(actionId);
667
+ const btn = this.toolbarEl.querySelector(`button[data-action-id="${actionId}"], button[data-action-id="${norm}"]`);
668
+ if (btn) {
669
+ btn.disabled = true;
670
+ btn.classList.add("disabled");
671
+ }
672
+ }
673
+ setActionActive(actionId, active) {
674
+ const norm = this.normalizeActionId(actionId);
675
+ const btn = this.toolbarEl.querySelector(`button[data-action-id="${actionId}"], button[data-action-id="${norm}"]`);
676
+ if (btn) {
677
+ if (active) {
678
+ btn.classList.add("active");
679
+ } else {
680
+ btn.classList.remove("active");
681
+ }
682
+ }
683
+ }
684
+ isActionEnabled(action) {
685
+ const c = this.config;
686
+ const id = action.id;
687
+ if (c[id] === false) return false;
688
+ if (id === "zoom-in" && (c.zoomIn === false || c["zoom-in"] === false)) return false;
689
+ if (id === "zoom-out" && (c.zoomOut === false || c["zoom-out"] === false)) return false;
690
+ if ((id === "fit-page" || id === "fit-width" || id === "fit-slide") && (c.fitPage === false || c.fitToPage === false || c.fitWidth === false || c["fit-page"] === false || c["fit-width"] === false)) {
691
+ return false;
692
+ }
693
+ if ((id === "rotate-cw" || id === "rotate-ccw" || id === "rotate") && (c.rotate === false || c.rotateCW === false || c.rotateCCW === false || c["rotate-cw"] === false)) {
694
+ return false;
695
+ }
696
+ if (id === "fullscreen" && c.fullscreen === false) return false;
697
+ if (id === "download" && c.download === false) return false;
698
+ if (id === "print" && c.print === false) return false;
699
+ if ((id === "open-window" || id === "external-window") && (c.openWindow === false || c.openSeparateWindow === false || c["open-window"] === false)) {
700
+ return false;
701
+ }
702
+ if (id === "copy" && c.copy === false) return false;
703
+ if (id === "thumbnails" && c.thumbnails === false) return false;
704
+ if (id === "page-nav" && (c.pageNav === false || c.pagination === false || c["page-nav"] === false)) {
705
+ return false;
706
+ }
707
+ if ((id === "prev" || id === "page-prev") && (c.prevPage === false || c.prev === false || c["prev"] === false || c.pageNav === false || c.pagination === false)) {
708
+ return false;
709
+ }
710
+ if ((id === "next" || id === "page-next") && (c.nextPage === false || c.next === false || c["next"] === false || c.pageNav === false || c.pagination === false)) {
711
+ return false;
712
+ }
713
+ if (id === "play" && c.play === false) return false;
714
+ if (id === "pause" && c.pause === false) return false;
715
+ if ((id === "fast-forward" || id === "forward-10") && (c.fastForward === false || c["fast-forward"] === false)) {
716
+ return false;
717
+ }
718
+ if ((id === "rewind" || id === "replay-10") && (c.rewind === false || c["rewind"] === false)) {
719
+ return false;
720
+ }
721
+ if (id === "speed" && c.speed === false) return false;
722
+ return true;
723
+ }
588
724
  setPage(page, max) {
589
725
  if (this.pageInputEl) {
590
726
  this.pageInputEl.value = page.toString();
727
+ if (max !== void 0) {
728
+ this.pageInputEl.max = max.toString();
729
+ }
591
730
  }
592
731
  if (max !== void 0 && this.pageLabelEl) {
593
732
  this.pageLabelEl.textContent = ` / ${max}`;
594
733
  }
734
+ const currentMax = max !== void 0 ? max : parseInt(this.pageInputEl?.max || "1", 10) || 1;
735
+ if (this.prevPageBtn) {
736
+ this.prevPageBtn.disabled = page <= 1;
737
+ this.prevPageBtn.style.opacity = page <= 1 ? "0.4" : "1";
738
+ }
739
+ if (this.nextPageBtn) {
740
+ this.nextPageBtn.disabled = page >= currentMax;
741
+ this.nextPageBtn.style.opacity = page >= currentMax ? "0.4" : "1";
742
+ }
595
743
  }
596
744
  update(actions) {
597
745
  this.actions = actions;
@@ -614,8 +762,9 @@ var ToolbarController = class {
614
762
  view: [],
615
763
  actions: []
616
764
  };
617
- const hasPageNav = this.actions.some((a) => a.type === "page-nav");
618
- const effectiveActions = hasPageNav ? this.actions.filter((a) => a.id !== "page-prev" && a.id !== "page-next" && a.id !== "prev" && a.id !== "next") : this.actions;
765
+ const enabledActions = this.actions.filter((a) => this.isActionEnabled(a));
766
+ const hasPageNav = enabledActions.some((a) => a.type === "page-nav");
767
+ const effectiveActions = hasPageNav ? enabledActions.filter((a) => a.id !== "page-prev" && a.id !== "page-next" && a.id !== "prev" && a.id !== "next") : enabledActions;
619
768
  for (const action of effectiveActions) {
620
769
  if (groups[action.group]) {
621
770
  groups[action.group].push(action);
@@ -675,6 +824,13 @@ var ToolbarController = class {
675
824
  action.execute("go", val);
676
825
  });
677
826
  const label = createElement("span", { className: "fp-toolbar-label" }, ` / ${max}`);
827
+ const curVal = action.value ?? 1;
828
+ prevBtn.disabled = curVal <= 1;
829
+ prevBtn.style.opacity = curVal <= 1 ? "0.4" : "1";
830
+ nextBtn.disabled = curVal >= max;
831
+ nextBtn.style.opacity = curVal >= max ? "0.4" : "1";
832
+ this.prevPageBtn = prevBtn;
833
+ this.nextPageBtn = nextBtn;
678
834
  this.pageInputEl = input;
679
835
  this.pageLabelEl = label;
680
836
  groupEl.appendChild(prevBtn);
@@ -730,25 +886,63 @@ var ToolbarController = class {
730
886
  var ThumbnailPanel = class {
731
887
  el;
732
888
  panelEl;
889
+ listEl;
890
+ headerEl;
733
891
  thumbnails = [];
734
892
  onSelect;
893
+ onToggleCallback;
735
894
  activeIndex = 0;
736
- constructor(container) {
895
+ observer;
896
+ renderedIndices = /* @__PURE__ */ new Set();
897
+ constructor(container, onToggle) {
737
898
  this.el = container;
899
+ this.onToggleCallback = onToggle;
738
900
  this.panelEl = createElement("div", { className: "fp-thumbnail-panel hidden" });
901
+ this.panelEl.style.width = "0px";
902
+ this.panelEl.style.minWidth = "0px";
903
+ this.panelEl.style.opacity = "0";
904
+ this.headerEl = createElement("div", { className: "fp-thumbnail-header" });
905
+ const title = createElement("span", { className: "fp-thumbnail-title" }, "Pages");
906
+ const closeBtn = createElement("button", {
907
+ className: "fp-thumbnail-close-btn",
908
+ title: "Close Thumbnails",
909
+ type: "button"
910
+ });
911
+ closeBtn.innerHTML = ICON_CLOSE;
912
+ closeBtn.addEventListener("click", (e) => {
913
+ e.stopPropagation();
914
+ this.hide();
915
+ });
916
+ this.headerEl.appendChild(title);
917
+ this.headerEl.appendChild(closeBtn);
918
+ this.listEl = createElement("div", { className: "fp-thumbnail-list" });
919
+ this.panelEl.appendChild(this.headerEl);
920
+ this.panelEl.appendChild(this.listEl);
739
921
  this.el.appendChild(this.panelEl);
740
922
  }
923
+ isOpen() {
924
+ return !this.panelEl.classList.contains("hidden") && this.panelEl.style.width !== "0px";
925
+ }
741
926
  update(thumbnails, onSelect) {
742
927
  this.thumbnails = thumbnails;
743
928
  this.onSelect = onSelect;
929
+ this.renderedIndices.clear();
930
+ const titleEl = this.headerEl.querySelector(".fp-thumbnail-title");
931
+ if (titleEl) {
932
+ titleEl.textContent = `Pages (${thumbnails.length})`;
933
+ }
744
934
  this.render();
935
+ if (this.isOpen()) {
936
+ this.renderAllVisible();
937
+ }
745
938
  }
746
939
  setActive(index) {
747
940
  this.activeIndex = index;
748
- const items = this.panelEl.querySelectorAll(".fp-thumbnail-item");
941
+ const items = this.listEl.querySelectorAll(".fp-thumbnail-item");
749
942
  items.forEach((item, i) => {
750
943
  if (i === index) {
751
944
  item.classList.add("active");
945
+ item.scrollIntoView({ block: "nearest", behavior: "smooth" });
752
946
  } else {
753
947
  item.classList.remove("active");
754
948
  }
@@ -756,37 +950,102 @@ var ThumbnailPanel = class {
756
950
  }
757
951
  show() {
758
952
  this.panelEl.classList.remove("hidden");
953
+ this.panelEl.style.width = "200px";
954
+ this.panelEl.style.minWidth = "200px";
955
+ this.panelEl.style.opacity = "1";
956
+ this.panelEl.style.display = "flex";
957
+ this.onToggleCallback?.(true);
958
+ this.renderAllVisible();
759
959
  }
760
960
  hide() {
761
961
  this.panelEl.classList.add("hidden");
962
+ this.panelEl.style.width = "0px";
963
+ this.panelEl.style.minWidth = "0px";
964
+ this.panelEl.style.opacity = "0";
965
+ this.onToggleCallback?.(false);
762
966
  }
763
967
  toggle() {
764
- this.panelEl.classList.toggle("hidden");
968
+ if (this.isOpen()) {
969
+ this.hide();
970
+ } else {
971
+ this.show();
972
+ }
765
973
  }
766
974
  destroy() {
975
+ if (this.observer) {
976
+ this.observer.disconnect();
977
+ this.observer = void 0;
978
+ }
979
+ this.renderedIndices.clear();
767
980
  this.el.innerHTML = "";
768
981
  }
769
- async render() {
770
- this.panelEl.innerHTML = "";
982
+ renderAllVisible() {
983
+ const items = this.listEl.querySelectorAll(".fp-thumbnail-item");
984
+ items.forEach((item) => {
985
+ const idx = parseInt(item.dataset.thumbIndex || "-1", 10);
986
+ if (idx >= 0 && !this.renderedIndices.has(idx)) {
987
+ this.renderThumbnailItem(idx, item);
988
+ }
989
+ });
990
+ }
991
+ renderThumbnailItem(idx, itemEl) {
992
+ if (this.renderedIndices.has(idx)) return;
993
+ this.renderedIndices.add(idx);
994
+ const canvas = itemEl.querySelector("canvas");
995
+ const thumb = this.thumbnails[idx];
996
+ if (canvas && thumb) {
997
+ Promise.resolve(thumb.render(canvas)).catch((err) => {
998
+ console.warn(`[FilePreview] Error rendering thumbnail ${idx}:`, err);
999
+ });
1000
+ }
1001
+ }
1002
+ render() {
1003
+ if (this.observer) {
1004
+ this.observer.disconnect();
1005
+ }
1006
+ this.listEl.innerHTML = "";
1007
+ const useObserver = typeof IntersectionObserver !== "undefined";
1008
+ if (useObserver) {
1009
+ this.observer = new IntersectionObserver(
1010
+ (entries) => {
1011
+ for (const entry of entries) {
1012
+ if (entry.isIntersecting) {
1013
+ const target = entry.target;
1014
+ const idx = parseInt(target.dataset.thumbIndex || "-1", 10);
1015
+ if (idx >= 0) {
1016
+ this.renderThumbnailItem(idx, target);
1017
+ }
1018
+ }
1019
+ }
1020
+ },
1021
+ {
1022
+ root: this.listEl,
1023
+ rootMargin: "120px 0px 120px 0px"
1024
+ }
1025
+ );
1026
+ }
771
1027
  for (let i = 0; i < this.thumbnails.length; i++) {
772
1028
  const thumb = this.thumbnails[i];
773
- const itemEl = createElement("div", { className: "fp-thumbnail-item" });
774
- if (i === this.activeIndex) {
775
- itemEl.classList.add("active");
776
- }
1029
+ const itemEl = createElement("div", {
1030
+ className: "fp-thumbnail-item" + (i === this.activeIndex ? " active" : "")
1031
+ });
1032
+ itemEl.dataset.thumbIndex = i.toString();
777
1033
  itemEl.addEventListener("click", () => {
778
1034
  this.setActive(i);
779
1035
  this.onSelect?.(i);
780
1036
  });
781
- const canvas = createElement("canvas", { width: "150", height: "200" });
782
- const label = createElement("span", { className: "fp-thumbnail-label" }, thumb.label);
783
- itemEl.appendChild(canvas);
1037
+ const canvasWrapper = createElement("div", { className: "fp-thumbnail-canvas-wrapper" });
1038
+ const canvas = createElement("canvas", { width: "130", height: "170" });
1039
+ canvasWrapper.appendChild(canvas);
1040
+ const label = createElement("span", { className: "fp-thumbnail-label" }, thumb.label || `Page ${i + 1}`);
1041
+ itemEl.appendChild(canvasWrapper);
784
1042
  itemEl.appendChild(label);
785
- this.panelEl.appendChild(itemEl);
786
- try {
787
- await thumb.render(canvas);
788
- } catch (err) {
789
- console.error(`[FilePreview] Error rendering thumbnail ${i}:`, err);
1043
+ this.listEl.appendChild(itemEl);
1044
+ if (this.observer) {
1045
+ this.observer.observe(itemEl);
1046
+ }
1047
+ if (i < 4) {
1048
+ this.renderThumbnailItem(i, itemEl);
790
1049
  }
791
1050
  }
792
1051
  }
@@ -807,6 +1066,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
807
1066
  currentOptions = {};
808
1067
  resizeObserver = null;
809
1068
  fullscreenHandler = null;
1069
+ titleBarEl = null;
810
1070
  /**
811
1071
  * Register a preview plugin.
812
1072
  */
@@ -843,6 +1103,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
843
1103
  }
844
1104
  this.currentBuffer = buffer.slice(0);
845
1105
  this.currentMetadata = metadata;
1106
+ this.updateTitleBar(options, metadata);
846
1107
  if (signal.aborted) throw new DOMException("Aborted", "AbortError");
847
1108
  const fileInfo = { metadata, buffer };
848
1109
  const matchedPlugin = await this.findPlugin(fileInfo);
@@ -866,15 +1127,21 @@ var FilePreviewViewer = class _FilePreviewViewer {
866
1127
  if (event === "page-change" && payload && typeof payload.page === "number") {
867
1128
  const total = payload.total ?? payload.totalPages;
868
1129
  this.toolbar?.setPage(payload.page, total);
1130
+ this.thumbnailPanel?.setActive(payload.page - 1);
869
1131
  }
870
1132
  this.eventEmitter.emit(event, payload);
871
- }
1133
+ },
1134
+ toggleThumbnails: () => this.toggleThumbnails()
872
1135
  });
873
1136
  this.activeInstance = instance;
874
1137
  instance.openInSeparateWindow = () => this.openInSeparateWindow();
1138
+ instance.toggleThumbnails = () => this.toggleThumbnails();
875
1139
  this.hideLoading();
876
1140
  this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
877
- if (options.showToolbar !== false && this.toolbar) {
1141
+ const isToolbarVisible = options.showToolbar !== false && options.toolbar !== false;
1142
+ if (isToolbarVisible && this.toolbar) {
1143
+ const toolbarConfig = this.extractToolbarConfig(options);
1144
+ this.toolbar.setConfig(toolbarConfig);
878
1145
  const actions = matchedPlugin.getToolbarActions(instance);
879
1146
  const hasFullscreen = actions.some((a) => a.id === "fullscreen");
880
1147
  if (!hasFullscreen && this.wrapperEl) {
@@ -924,6 +1191,12 @@ var FilePreviewViewer = class _FilePreviewViewer {
924
1191
  }
925
1192
  });
926
1193
  }
1194
+ const thumbAction = actions.find((a) => a.id === "thumbnails");
1195
+ if (thumbAction) {
1196
+ thumbAction.execute = () => {
1197
+ this.toggleThumbnails();
1198
+ };
1199
+ }
927
1200
  this.toolbar.update(actions);
928
1201
  this.toolbar.show();
929
1202
  }
@@ -1008,7 +1281,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1008
1281
  if (targetUrl) {
1009
1282
  const newWin2 = window.open(targetUrl, "_blank");
1010
1283
  if (!newWin2) {
1011
- alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
1284
+ console.warn("[FilePreviewViewer] Popup blocker prevented opening the preview in a separate window.");
1012
1285
  return null;
1013
1286
  }
1014
1287
  return newWin2;
@@ -1016,7 +1289,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1016
1289
  const title = (this.currentMetadata?.name || "Document Preview") + " - Full Preview";
1017
1290
  const newWin = window.open("", "_blank");
1018
1291
  if (!newWin) {
1019
- alert("Popup blocker prevented opening the preview in a separate window. Please allow popups for this site.");
1292
+ console.warn("[FilePreviewViewer] Popup blocker prevented opening the preview in a separate window.");
1020
1293
  return null;
1021
1294
  }
1022
1295
  newWin.document.title = title;
@@ -1093,6 +1366,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1093
1366
  this.currentBuffer = null;
1094
1367
  this.currentMetadata = null;
1095
1368
  this.currentContainer = null;
1369
+ this.titleBarEl = null;
1096
1370
  }
1097
1371
  /**
1098
1372
  * Get the currently active preview instance.
@@ -1100,7 +1374,218 @@ var FilePreviewViewer = class _FilePreviewViewer {
1100
1374
  getInstance() {
1101
1375
  return this.activeInstance;
1102
1376
  }
1377
+ // --- Toolbar Configuration & Feature Toggles ---
1378
+ /**
1379
+ * Configure toolbar features dynamically through options or methods.
1380
+ * e.g. viewer.setToolbarConfig({ zoomIn: false, print: false })
1381
+ */
1382
+ setToolbarConfig(config) {
1383
+ this.toolbar?.setConfig(config);
1384
+ }
1385
+ /**
1386
+ * Get current toolbar configuration.
1387
+ */
1388
+ getToolbarConfig() {
1389
+ return this.toolbar?.getConfig() ?? {};
1390
+ }
1391
+ /**
1392
+ * Hide a specific toolbar action by ID or alias (e.g. 'zoom-in', 'print', 'download').
1393
+ */
1394
+ hideToolbarAction(actionId) {
1395
+ this.toolbar?.hideAction(actionId);
1396
+ }
1397
+ /**
1398
+ * Show a specific toolbar action by ID or alias.
1399
+ */
1400
+ showToolbarAction(actionId) {
1401
+ this.toolbar?.showAction(actionId);
1402
+ }
1403
+ /**
1404
+ * Enable a specific toolbar action button.
1405
+ */
1406
+ enableToolbarAction(actionId) {
1407
+ this.toolbar?.enableAction(actionId);
1408
+ }
1409
+ /**
1410
+ * Disable a specific toolbar action button.
1411
+ */
1412
+ disableToolbarAction(actionId) {
1413
+ this.toolbar?.disableAction(actionId);
1414
+ }
1415
+ // --- Programmatic Toolbar Action Methods ---
1416
+ /**
1417
+ * Fit document to page so it fills the frame width with minimal margins.
1418
+ */
1419
+ fitToPage() {
1420
+ this.activeInstance?.fitToPage?.();
1421
+ }
1422
+ /**
1423
+ * Zoom in.
1424
+ */
1425
+ zoomIn() {
1426
+ this.activeInstance?.zoomIn?.();
1427
+ }
1428
+ /**
1429
+ * Zoom out.
1430
+ */
1431
+ zoomOut() {
1432
+ this.activeInstance?.zoomOut?.();
1433
+ }
1434
+ /**
1435
+ * Set specific zoom level.
1436
+ */
1437
+ setZoom(level) {
1438
+ this.activeInstance?.setZoom?.(level);
1439
+ }
1440
+ /**
1441
+ * Get current zoom level.
1442
+ */
1443
+ getZoom() {
1444
+ return this.activeInstance?.getZoom?.() ?? 1;
1445
+ }
1446
+ /**
1447
+ * Rotate 90 degrees clockwise.
1448
+ */
1449
+ rotateCW() {
1450
+ this.activeInstance?.rotateCW?.();
1451
+ }
1452
+ /**
1453
+ * Rotate 90 degrees counter-clockwise.
1454
+ */
1455
+ rotateCCW() {
1456
+ this.activeInstance?.rotateCCW?.();
1457
+ }
1458
+ /**
1459
+ * Navigate to a specific page number.
1460
+ */
1461
+ goToPage(page) {
1462
+ this.activeInstance?.goToPage?.(page);
1463
+ this.toolbar?.setPage(page);
1464
+ }
1465
+ /**
1466
+ * Navigate to next page.
1467
+ */
1468
+ nextPage() {
1469
+ const cur = this.getCurrentPage();
1470
+ const max = this.getPageCount();
1471
+ if (cur < max) {
1472
+ this.goToPage(cur + 1);
1473
+ }
1474
+ }
1475
+ /**
1476
+ * Navigate to previous page.
1477
+ */
1478
+ prevPage() {
1479
+ const cur = this.getCurrentPage();
1480
+ if (cur > 1) {
1481
+ this.goToPage(cur - 1);
1482
+ }
1483
+ }
1484
+ /**
1485
+ * Get total page count.
1486
+ */
1487
+ getPageCount() {
1488
+ return this.activeInstance?.getPageCount?.() ?? 1;
1489
+ }
1490
+ /**
1491
+ * Get current page number.
1492
+ */
1493
+ getCurrentPage() {
1494
+ return this.activeInstance?.getCurrentPage?.() ?? 1;
1495
+ }
1496
+ /**
1497
+ * Download the current document.
1498
+ */
1499
+ download() {
1500
+ this.activeInstance?.download?.();
1501
+ }
1502
+ /**
1503
+ * Print the current document.
1504
+ */
1505
+ print() {
1506
+ this.activeInstance?.print?.();
1507
+ }
1508
+ /**
1509
+ * Toggle fullscreen mode.
1510
+ */
1511
+ toggleFullscreen() {
1512
+ try {
1513
+ if (!document.fullscreenElement) {
1514
+ this.wrapperEl?.requestFullscreen?.();
1515
+ this.wrapperEl?.classList.add("fp-fullscreen-active");
1516
+ } else {
1517
+ document.exitFullscreen?.();
1518
+ this.wrapperEl?.classList.remove("fp-fullscreen-active");
1519
+ }
1520
+ } catch {
1521
+ this.wrapperEl?.classList.toggle("fp-fullscreen-active");
1522
+ }
1523
+ setTimeout(() => {
1524
+ this.activeInstance?.fitToPage?.();
1525
+ }, 120);
1526
+ }
1527
+ /**
1528
+ * Toggle thumbnails sidebar panel.
1529
+ */
1530
+ toggleThumbnails() {
1531
+ if (!this.thumbnailPanel) return;
1532
+ this.thumbnailPanel.toggle();
1533
+ const isOpen = this.thumbnailPanel.isOpen();
1534
+ this.toolbar?.setActionActive("thumbnails", isOpen);
1535
+ setTimeout(() => {
1536
+ this.activeInstance?.fitToPage?.();
1537
+ }, 260);
1538
+ }
1539
+ /**
1540
+ * Set whether to display the file name / title bar above the toolbar.
1541
+ */
1542
+ setShowFileName(show) {
1543
+ if (this.currentOptions) {
1544
+ this.currentOptions.showFileName = show;
1545
+ }
1546
+ this.updateTitleBar(this.currentOptions, this.currentMetadata);
1547
+ }
1548
+ /**
1549
+ * Set or override the displayed file name in the preview panel.
1550
+ */
1551
+ setFileName(name) {
1552
+ if (this.currentOptions) {
1553
+ this.currentOptions.fileName = name;
1554
+ }
1555
+ this.updateTitleBar(this.currentOptions, this.currentMetadata);
1556
+ }
1103
1557
  // --- Private methods ---
1558
+ updateTitleBar(options, metadata) {
1559
+ if (!this.titleBarEl) return;
1560
+ const show = options?.showFileName !== false;
1561
+ if (!show) {
1562
+ this.titleBarEl.style.display = "none";
1563
+ return;
1564
+ }
1565
+ const name = options?.fileName || metadata?.name || this.currentMetadata?.name || "";
1566
+ if (!name) {
1567
+ this.titleBarEl.style.display = "none";
1568
+ return;
1569
+ }
1570
+ this.titleBarEl.style.display = "flex";
1571
+ const ext = metadata?.extension || (name.includes(".") ? "." + name.split(".").pop() : "");
1572
+ const badge = ext ? ext.replace(".", "").toUpperCase() : "";
1573
+ this.titleBarEl.innerHTML = `
1574
+ <div class="fp-titlebar-info">
1575
+ <span class="fp-titlebar-icon">\u{1F4C4}</span>
1576
+ <span class="fp-titlebar-name" title="${name}">${name}</span>
1577
+ ${badge ? `<span class="fp-titlebar-badge">${badge}</span>` : ""}
1578
+ </div>
1579
+ `;
1580
+ }
1581
+ extractToolbarConfig(options) {
1582
+ if (!options) return {};
1583
+ const nested = typeof options.toolbar === "object" ? options.toolbar : {};
1584
+ return {
1585
+ ...options,
1586
+ ...nested
1587
+ };
1588
+ }
1104
1589
  abort() {
1105
1590
  if (this.abortController) {
1106
1591
  this.abortController.abort();
@@ -1143,17 +1628,29 @@ var FilePreviewViewer = class _FilePreviewViewer {
1143
1628
  bodyEl.className = "fp-body";
1144
1629
  bodyEl.appendChild(thumbnailEl);
1145
1630
  bodyEl.appendChild(this.contentEl);
1631
+ const titleBarEl = document.createElement("div");
1632
+ titleBarEl.className = "fp-titlebar";
1633
+ this.titleBarEl = titleBarEl;
1634
+ this.updateTitleBar(options, this.currentMetadata);
1146
1635
  if (toolbarPos === "top") {
1636
+ this.wrapperEl.appendChild(titleBarEl);
1147
1637
  this.wrapperEl.appendChild(toolbarEl);
1148
1638
  this.wrapperEl.appendChild(bodyEl);
1149
1639
  } else {
1640
+ this.wrapperEl.appendChild(titleBarEl);
1150
1641
  this.wrapperEl.appendChild(bodyEl);
1151
1642
  this.wrapperEl.appendChild(toolbarEl);
1152
1643
  }
1153
1644
  container.innerHTML = "";
1154
1645
  container.appendChild(this.wrapperEl);
1155
- this.toolbar = new ToolbarController(toolbarEl);
1156
- this.thumbnailPanel = new ThumbnailPanel(thumbnailEl);
1646
+ const initialToolbarConfig = this.extractToolbarConfig(options);
1647
+ this.toolbar = new ToolbarController(toolbarEl, initialToolbarConfig);
1648
+ this.thumbnailPanel = new ThumbnailPanel(thumbnailEl, (isOpen) => {
1649
+ this.toolbar?.setActionActive("thumbnails", isOpen);
1650
+ setTimeout(() => {
1651
+ this.activeInstance?.fitToPage?.();
1652
+ }, 260);
1653
+ });
1157
1654
  this.setupKeyboardShortcuts();
1158
1655
  this.setupDragAndDrop(container, options);
1159
1656
  this.setupResizeAndFullscreenListeners();
@@ -1543,6 +2040,14 @@ var PdfPlugin = class {
1543
2040
  group: "zoom",
1544
2041
  execute: () => instance.fitToPage?.()
1545
2042
  },
2043
+ {
2044
+ id: "fit-width",
2045
+ icon: "fit-width",
2046
+ label: "Fit to Width",
2047
+ type: "button",
2048
+ group: "zoom",
2049
+ execute: () => instance.fitToWidth?.()
2050
+ },
1546
2051
  {
1547
2052
  id: "rotate-cw",
1548
2053
  icon: "rotate-cw",
@@ -1551,6 +2056,14 @@ var PdfPlugin = class {
1551
2056
  group: "view",
1552
2057
  execute: () => instance.rotateCW?.()
1553
2058
  },
2059
+ {
2060
+ id: "rotate-ccw",
2061
+ icon: "rotate-ccw",
2062
+ label: "Rotate Counter-Clockwise",
2063
+ type: "button",
2064
+ group: "view",
2065
+ execute: () => instance.rotateCCW?.()
2066
+ },
1554
2067
  {
1555
2068
  id: "download",
1556
2069
  icon: "download",
@@ -1582,12 +2095,13 @@ var PdfPlugin = class {
1582
2095
  container.className = "fp-pdf-container";
1583
2096
  container.style.width = "100%";
1584
2097
  container.style.height = "100%";
1585
- container.style.overflow = "auto";
2098
+ container.style.overflowX = "hidden";
2099
+ container.style.overflowY = "auto";
1586
2100
  container.style.display = "flex";
1587
2101
  container.style.flexDirection = "column";
1588
2102
  container.style.alignItems = "center";
1589
2103
  container.style.justifyContent = "flex-start";
1590
- container.style.padding = "20px 16px";
2104
+ container.style.padding = "16px 8px";
1591
2105
  container.style.backgroundColor = "#0f172a";
1592
2106
  container.style.boxSizing = "border-box";
1593
2107
  container.style.position = "relative";
@@ -1598,30 +2112,12 @@ var PdfPlugin = class {
1598
2112
  pageCard.style.borderRadius = "4px";
1599
2113
  pageCard.style.overflow = "hidden";
1600
2114
  pageCard.style.lineHeight = "0";
1601
- pageCard.style.transition = "transform 0.15s ease";
1602
2115
  pageCard.style.position = "relative";
1603
2116
  pageCard.style.flexShrink = "0";
2117
+ pageCard.style.transition = "box-shadow 0.2s ease";
1604
2118
  let canvas = document.createElement("canvas");
1605
2119
  pageCard.appendChild(canvas);
1606
2120
  container.appendChild(pageCard);
1607
- const indicator = document.createElement("div");
1608
- indicator.className = "fp-pdf-page-indicator";
1609
- indicator.style.position = "sticky";
1610
- indicator.style.bottom = "16px";
1611
- indicator.style.marginTop = "16px";
1612
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
1613
- indicator.style.backdropFilter = "blur(8px)";
1614
- indicator.style.color = "#f8fafc";
1615
- indicator.style.fontSize = "12px";
1616
- indicator.style.fontWeight = "600";
1617
- indicator.style.padding = "5px 14px";
1618
- indicator.style.borderRadius = "20px";
1619
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
1620
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
1621
- indicator.style.zIndex = "10";
1622
- indicator.style.userSelect = "none";
1623
- indicator.style.pointerEvents = "none";
1624
- container.appendChild(indicator);
1625
2121
  ctx.container.appendChild(container);
1626
2122
  const standardFontsUrl = typeof window !== "undefined" && window.__PDF_STANDARD_FONTS_URL__ || "./standard_fonts/";
1627
2123
  const cmapsUrl = typeof window !== "undefined" && window.__PDF_CMAPS_URL__ || "./cmaps/";
@@ -1637,8 +2133,9 @@ var PdfPlugin = class {
1637
2133
  let currentPage = 1;
1638
2134
  let zoomScale = 1;
1639
2135
  let rotation = 0;
1640
- let fitMode = "page";
2136
+ let fitMode = ctx?.options?.fitMode || "width";
1641
2137
  let currentRenderTask = null;
2138
+ let textLayerDiv = null;
1642
2139
  const renderPage = async (pageNum) => {
1643
2140
  if (currentRenderTask) {
1644
2141
  try {
@@ -1650,30 +2147,37 @@ var PdfPlugin = class {
1650
2147
  const newCanvas = document.createElement("canvas");
1651
2148
  pageCard.replaceChild(newCanvas, canvas);
1652
2149
  canvas = newCanvas;
2150
+ if (textLayerDiv) {
2151
+ textLayerDiv.remove();
2152
+ textLayerDiv = null;
2153
+ }
1653
2154
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
1654
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
1655
2155
  ctx.emit("page-change", { page: currentPage, total: totalPages });
1656
2156
  const page = await pdfDoc.getPage(currentPage);
1657
2157
  const containerWidth = container.clientWidth || 900;
1658
2158
  const containerHeight = container.clientHeight || 700;
1659
2159
  const unscaledVp = page.getViewport({ scale: 1, rotation });
1660
- const availWidth = Math.max(320, containerWidth - 48);
1661
- const availHeight = Math.max(550, containerHeight - 88);
2160
+ const availWidth = Math.max(280, containerWidth - 36);
2161
+ const availHeight = Math.max(280, containerHeight - 32);
1662
2162
  const scaleW = availWidth / unscaledVp.width;
1663
2163
  const scaleH = availHeight / unscaledVp.height;
1664
2164
  let fitScale;
1665
2165
  if (fitMode === "page") {
1666
- fitScale = Math.max(0.4, Math.min(scaleW, scaleH));
2166
+ fitScale = Math.max(0.2, Math.min(4, Math.min(scaleW, scaleH)));
1667
2167
  } else {
1668
- fitScale = Math.max(0.65, Math.min(1.25, scaleW));
2168
+ fitScale = Math.max(0.2, Math.min(4, scaleW));
1669
2169
  }
1670
2170
  const effectiveScale = (fitScale > 0 ? fitScale : 1) * zoomScale;
1671
2171
  const pixelRatio = window.devicePixelRatio || 1;
1672
2172
  const viewport = page.getViewport({ scale: effectiveScale, rotation });
2173
+ const displayWidth = Math.floor(viewport.width);
2174
+ const displayHeight = Math.floor(viewport.height);
1673
2175
  canvas.width = Math.floor(viewport.width * pixelRatio);
1674
2176
  canvas.height = Math.floor(viewport.height * pixelRatio);
1675
- canvas.style.width = `${Math.floor(viewport.width)}px`;
1676
- canvas.style.height = `${Math.floor(viewport.height)}px`;
2177
+ canvas.style.width = `${displayWidth}px`;
2178
+ canvas.style.height = `${displayHeight}px`;
2179
+ pageCard.style.width = `${displayWidth}px`;
2180
+ pageCard.style.height = `${displayHeight}px`;
1677
2181
  const canvasCtx = canvas.getContext("2d");
1678
2182
  if (!canvasCtx) return;
1679
2183
  canvasCtx.setTransform(pixelRatio, 0, 0, pixelRatio, 0, 0);
@@ -1687,9 +2191,31 @@ var PdfPlugin = class {
1687
2191
  if (err?.name !== "RenderingCancelledException") {
1688
2192
  console.warn("[PdfPlugin] Page render warning:", err);
1689
2193
  }
2194
+ return;
1690
2195
  } finally {
1691
2196
  currentRenderTask = null;
1692
2197
  }
2198
+ try {
2199
+ textLayerDiv = document.createElement("div");
2200
+ textLayerDiv.className = "textLayer";
2201
+ textLayerDiv.style.width = `${displayWidth}px`;
2202
+ textLayerDiv.style.height = `${displayHeight}px`;
2203
+ textLayerDiv.style.position = "absolute";
2204
+ textLayerDiv.style.top = "0";
2205
+ textLayerDiv.style.left = "0";
2206
+ textLayerDiv.style.lineHeight = "1";
2207
+ pageCard.appendChild(textLayerDiv);
2208
+ if (pdfjsLib.TextLayer) {
2209
+ const textLayer = new pdfjsLib.TextLayer({
2210
+ textContentSource: page.streamTextContent(),
2211
+ container: textLayerDiv,
2212
+ viewport
2213
+ });
2214
+ await textLayer.render();
2215
+ }
2216
+ } catch (err) {
2217
+ console.debug("[PdfPlugin] TextLayer notice:", err);
2218
+ }
1693
2219
  };
1694
2220
  renderPage(1);
1695
2221
  let resizeTimer = null;
@@ -1699,11 +2225,48 @@ var PdfPlugin = class {
1699
2225
  if (Math.abs(zoomScale - 1) < 0.05) {
1700
2226
  renderPage(currentPage);
1701
2227
  }
1702
- }, 150);
2228
+ }, 120);
1703
2229
  });
1704
2230
  resizeObserver.observe(container);
2231
+ const onWheel = (e) => {
2232
+ if (e.ctrlKey || e.metaKey) {
2233
+ e.preventDefault();
2234
+ const delta = e.deltaY > 0 ? -0.15 : 0.15;
2235
+ zoomScale = Math.max(0.25, Math.min(4, Math.round((zoomScale + delta) * 100) / 100));
2236
+ renderPage(currentPage);
2237
+ }
2238
+ };
2239
+ container.addEventListener("wheel", onWheel, { passive: false });
2240
+ const onKeyDown = (e) => {
2241
+ const tag = e.target?.tagName?.toLowerCase();
2242
+ if (tag === "input" || tag === "textarea" || tag === "select") return;
2243
+ if (e.key === "ArrowRight" || e.key === "PageDown") {
2244
+ if (currentPage < totalPages) {
2245
+ e.preventDefault();
2246
+ container.scrollTop = 0;
2247
+ renderPage(currentPage + 1);
2248
+ }
2249
+ } else if (e.key === "ArrowLeft" || e.key === "PageUp") {
2250
+ if (currentPage > 1) {
2251
+ e.preventDefault();
2252
+ container.scrollTop = 0;
2253
+ renderPage(currentPage - 1);
2254
+ }
2255
+ } else if (e.key === "Home") {
2256
+ e.preventDefault();
2257
+ container.scrollTop = 0;
2258
+ renderPage(1);
2259
+ } else if (e.key === "End") {
2260
+ e.preventDefault();
2261
+ container.scrollTop = 0;
2262
+ renderPage(totalPages);
2263
+ }
2264
+ };
2265
+ window.addEventListener("keydown", onKeyDown);
1705
2266
  const cleanup = () => {
1706
2267
  resizeObserver.disconnect();
2268
+ window.removeEventListener("keydown", onKeyDown);
2269
+ container.removeEventListener("wheel", onWheel);
1707
2270
  if (resizeTimer) clearTimeout(resizeTimer);
1708
2271
  if (currentRenderTask) {
1709
2272
  try {
@@ -1722,22 +2285,26 @@ var PdfPlugin = class {
1722
2285
  const instance = {
1723
2286
  destroy: cleanup,
1724
2287
  zoomIn: () => {
1725
- zoomScale = Math.min(3.5, zoomScale + 0.2);
2288
+ zoomScale = Math.min(4, Math.round((zoomScale + 0.2) * 10) / 10);
1726
2289
  renderPage(currentPage);
1727
2290
  },
1728
2291
  zoomOut: () => {
1729
- zoomScale = Math.max(0.3, zoomScale - 0.2);
2292
+ zoomScale = Math.max(0.25, Math.round((zoomScale - 0.2) * 10) / 10);
1730
2293
  renderPage(currentPage);
1731
2294
  },
1732
2295
  getZoom: () => zoomScale,
1733
2296
  setZoom: (level) => {
1734
- zoomScale = Math.max(0.3, Math.min(3.5, level));
2297
+ zoomScale = Math.max(0.25, Math.min(4, level));
1735
2298
  renderPage(currentPage);
1736
2299
  },
1737
2300
  fitToPage: () => {
1738
- fitMode = fitMode === "page" ? "width" : "page";
2301
+ fitMode = "page";
2302
+ zoomScale = 1;
2303
+ renderPage(currentPage);
2304
+ },
2305
+ fitToWidth: () => {
2306
+ fitMode = "width";
1739
2307
  zoomScale = 1;
1740
- rotation = 0;
1741
2308
  renderPage(currentPage);
1742
2309
  },
1743
2310
  rotateCW: () => {
@@ -1755,6 +2322,9 @@ var PdfPlugin = class {
1755
2322
  container.scrollTop = 0;
1756
2323
  renderPage(page);
1757
2324
  },
2325
+ toggleThumbnails: () => {
2326
+ ctx?.toggleThumbnails?.();
2327
+ },
1758
2328
  download: () => {
1759
2329
  const blob = new Blob([ctx.buffer], { type: "application/pdf" });
1760
2330
  const url = URL.createObjectURL(blob);
@@ -1788,7 +2358,7 @@ var PdfPlugin = class {
1788
2358
  },
1789
2359
  getThumbnails: async () => {
1790
2360
  const thumbnails = [];
1791
- const count = Math.min(totalPages, 50);
2361
+ const count = totalPages;
1792
2362
  for (let i = 1; i <= count; i++) {
1793
2363
  thumbnails.push({
1794
2364
  index: i,
@@ -1796,9 +2366,10 @@ var PdfPlugin = class {
1796
2366
  render: async (thumbCanvas) => {
1797
2367
  try {
1798
2368
  const p = await pdfDoc.getPage(i);
1799
- const baseVp = p.getViewport({ scale: 1 });
1800
- const thumbScale = (thumbCanvas.width || 120) / baseVp.width;
1801
- const thumbVp = p.getViewport({ scale: thumbScale });
2369
+ const baseVp = p.getViewport({ scale: 1, rotation });
2370
+ const targetW = thumbCanvas.width || 130;
2371
+ const thumbScale = targetW / baseVp.width;
2372
+ const thumbVp = p.getViewport({ scale: thumbScale, rotation });
1802
2373
  thumbCanvas.height = Math.floor(thumbVp.height);
1803
2374
  const tCtx = thumbCanvas.getContext("2d");
1804
2375
  if (tCtx) {
@@ -2142,28 +2713,26 @@ var DocxPlugin = class {
2142
2713
  getToolbarActions(instance) {
2143
2714
  const totalPages = instance.getPageCount?.() ?? 1;
2144
2715
  const actions = [];
2145
- if (totalPages > 1) {
2146
- actions.push({
2147
- id: "page-nav",
2148
- icon: "",
2149
- label: "Page Navigation",
2150
- type: "page-nav",
2151
- group: "navigation",
2152
- value: instance.getCurrentPage?.() ?? 1,
2153
- max: totalPages,
2154
- execute: (action, page) => {
2155
- const cur = instance.getCurrentPage?.() ?? 1;
2156
- const max = instance.getPageCount?.() ?? 1;
2157
- if (action === "prev") {
2158
- if (cur > 1) instance.goToPage?.(cur - 1);
2159
- } else if (action === "next") {
2160
- if (cur < max) instance.goToPage?.(cur + 1);
2161
- } else if (typeof page === "number") {
2162
- instance.goToPage?.(page);
2163
- }
2716
+ actions.push({
2717
+ id: "page-nav",
2718
+ icon: "",
2719
+ label: "Page Navigation",
2720
+ type: "page-nav",
2721
+ group: "navigation",
2722
+ value: instance.getCurrentPage?.() ?? 1,
2723
+ max: totalPages,
2724
+ execute: (action, page) => {
2725
+ const cur = instance.getCurrentPage?.() ?? 1;
2726
+ const max = instance.getPageCount?.() ?? 1;
2727
+ if (action === "prev") {
2728
+ if (cur > 1) instance.goToPage?.(cur - 1);
2729
+ } else if (action === "next") {
2730
+ if (cur < max) instance.goToPage?.(cur + 1);
2731
+ } else if (typeof page === "number") {
2732
+ instance.goToPage?.(page);
2164
2733
  }
2165
- });
2166
- }
2734
+ }
2735
+ });
2167
2736
  actions.push(
2168
2737
  {
2169
2738
  id: "zoom-out",
@@ -2229,11 +2798,17 @@ var DocxPlugin = class {
2229
2798
  wrapper.className = "fp-docx-wrapper";
2230
2799
  wrapper.style.transformOrigin = "top center";
2231
2800
  wrapper.style.transition = "transform 0.2s ease";
2232
- wrapper.style.padding = "24px 16px";
2233
- wrapper.style.maxWidth = "950px";
2801
+ wrapper.style.padding = "0";
2802
+ wrapper.style.maxWidth = "none";
2803
+ wrapper.style.width = "816px";
2234
2804
  wrapper.style.margin = "0 auto";
2805
+ wrapper.style.display = "flex";
2806
+ wrapper.style.flexDirection = "column";
2807
+ wrapper.style.alignItems = "center";
2235
2808
  wrapper.style.boxSizing = "border-box";
2236
- ctx.container.style.overflow = "auto";
2809
+ ctx.container.style.overflowX = "hidden";
2810
+ ctx.container.style.overflowY = "auto";
2811
+ ctx.container.style.padding = "16px 8px";
2237
2812
  ctx.container.style.backgroundColor = "#f1f5f9";
2238
2813
  ctx.container.appendChild(wrapper);
2239
2814
  const styleOverride = document.createElement("style");
@@ -2407,39 +2982,13 @@ var DocxPlugin = class {
2407
2982
  const pageElements = sections.length > 0 ? sections : cards;
2408
2983
  const totalPages = Math.max(1, pageElements.length);
2409
2984
  let currentPage = 1;
2410
- let indicator = null;
2411
- if (totalPages > 1) {
2412
- indicator = document.createElement("div");
2413
- indicator.className = "fp-docx-page-indicator";
2414
- indicator.style.position = "sticky";
2415
- indicator.style.bottom = "16px";
2416
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
2417
- indicator.style.backdropFilter = "blur(8px)";
2418
- indicator.style.color = "#f8fafc";
2419
- indicator.style.fontSize = "12px";
2420
- indicator.style.fontWeight = "600";
2421
- indicator.style.padding = "5px 14px";
2422
- indicator.style.borderRadius = "20px";
2423
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
2424
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
2425
- indicator.style.zIndex = "10";
2426
- indicator.style.userSelect = "none";
2427
- indicator.style.pointerEvents = "none";
2428
- indicator.style.textAlign = "center";
2429
- indicator.style.width = "fit-content";
2430
- indicator.style.margin = "16px auto 0";
2431
- ctx.container.appendChild(indicator);
2432
- }
2433
2985
  const showPage = (pageNum) => {
2434
2986
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
2435
2987
  if (pageElements.length > 1) {
2436
2988
  pageElements.forEach((sec, idx) => {
2437
- sec.style.display = idx + 1 === currentPage ? "block" : "none";
2989
+ sec.style.display = idx + 1 === currentPage ? "" : "none";
2438
2990
  });
2439
2991
  }
2440
- if (indicator) {
2441
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
2442
- }
2443
2992
  ctx.container.scrollTop = 0;
2444
2993
  ctx.emit("page-change", { page: currentPage, total: totalPages });
2445
2994
  };
@@ -2448,31 +2997,46 @@ var DocxPlugin = class {
2448
2997
  }
2449
2998
  scale = 1;
2450
2999
  let rotation = 0;
2451
- let fitMode = "width";
2452
- const calculateFitScale = (mode = fitMode) => {
2453
- const activeEl = pageElements[currentPage - 1] || wrapper.firstElementChild || wrapper;
3000
+ let fitMode = "page";
3001
+ let isUserZoomed = false;
3002
+ const calculateFitScale = (_mode = fitMode) => {
3003
+ const activeEl = pageElements[currentPage - 1] || wrapper.querySelector("section.docx") || wrapper;
2454
3004
  const elW = activeEl.offsetWidth || 816;
2455
- const singlePageH = Math.min(activeEl.offsetHeight || 1056, Math.round(elW * 1.32));
2456
- const availW = Math.max(280, ctx.container.clientWidth - 48);
2457
- const availH = Math.max(280, ctx.container.clientHeight - 80);
3005
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
2458
3006
  const sW = availW / elW;
2459
- const sH = availH / singlePageH;
2460
- if (mode === "page") {
2461
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
2462
- }
2463
- return Math.max(0.65, Math.min(1.05, sW));
3007
+ return Math.max(0.35, Math.min(3, sW));
2464
3008
  };
2465
3009
  const applyTransform = () => {
2466
3010
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
2467
3011
  wrapper.style.transformOrigin = "top center";
3012
+ const activeEl = pageElements[currentPage - 1] || wrapper.querySelector("section.docx") || wrapper;
3013
+ const elH = activeEl.offsetHeight || 1056;
3014
+ if (scale > 1) {
3015
+ wrapper.style.marginBottom = `${Math.round(elH * scale - elH + 32)}px`;
3016
+ } else {
3017
+ wrapper.style.marginBottom = "32px";
3018
+ }
2468
3019
  };
3020
+ let resizeObserver = null;
3021
+ if (typeof ResizeObserver !== "undefined") {
3022
+ resizeObserver = new ResizeObserver(() => {
3023
+ if (!isUserZoomed) {
3024
+ const newFit = calculateFitScale("page");
3025
+ if (Math.abs(scale - newFit) > 0.015) {
3026
+ scale = newFit;
3027
+ applyTransform();
3028
+ }
3029
+ }
3030
+ });
3031
+ resizeObserver.observe(ctx.container);
3032
+ }
2469
3033
  setTimeout(() => {
2470
- fitMode = "width";
2471
- scale = calculateFitScale("width");
3034
+ scale = calculateFitScale("page");
2472
3035
  applyTransform();
2473
- }, 60);
3036
+ }, 40);
2474
3037
  const cleanup = () => {
2475
- indicator?.remove();
3038
+ resizeObserver?.disconnect();
3039
+ resizeObserver = null;
2476
3040
  for (const url of createdBlobUrls) {
2477
3041
  URL.revokeObjectURL(url);
2478
3042
  }
@@ -2489,21 +3053,24 @@ var DocxPlugin = class {
2489
3053
  showPage(page);
2490
3054
  },
2491
3055
  zoomIn: () => {
2492
- scale += 0.15;
3056
+ isUserZoomed = true;
3057
+ scale = Math.min(3.5, scale + 0.15);
2493
3058
  applyTransform();
2494
3059
  },
2495
3060
  zoomOut: () => {
3061
+ isUserZoomed = true;
2496
3062
  scale = Math.max(0.2, scale - 0.15);
2497
3063
  applyTransform();
2498
3064
  },
2499
3065
  getZoom: () => scale,
2500
3066
  setZoom: (level) => {
3067
+ isUserZoomed = true;
2501
3068
  scale = level;
2502
3069
  applyTransform();
2503
3070
  },
2504
3071
  fitToPage: () => {
2505
- fitMode = fitMode === "width" ? "page" : "width";
2506
- scale = calculateFitScale(fitMode);
3072
+ isUserZoomed = false;
3073
+ scale = calculateFitScale("page");
2507
3074
  rotation = 0;
2508
3075
  applyTransform();
2509
3076
  },
@@ -2893,7 +3460,7 @@ var ExcelPlugin = class {
2893
3460
  "application/vnd.ms-excel.template.macroEnabled.12",
2894
3461
  "application/vnd.oasis.opendocument.spreadsheet"
2895
3462
  ];
2896
- weight = 80;
3463
+ weight = 85;
2897
3464
  supports(file) {
2898
3465
  const ext = file.metadata.extension?.toLowerCase();
2899
3466
  const mime = file.metadata.mimeType?.toLowerCase();
@@ -2905,6 +3472,14 @@ var ExcelPlugin = class {
2905
3472
  getToolbarActions(instance) {
2906
3473
  const totalSheets = instance.getPageCount?.() ?? 1;
2907
3474
  const actions = [];
3475
+ actions.push({
3476
+ id: "thumbnails",
3477
+ icon: "thumbnails",
3478
+ label: "Sheet Thumbnails",
3479
+ type: "button",
3480
+ group: "navigation",
3481
+ execute: () => instance.toggleThumbnails?.()
3482
+ });
2908
3483
  if (totalSheets > 1) {
2909
3484
  actions.push({
2910
3485
  id: "page-nav",
@@ -2987,6 +3562,16 @@ var ExcelPlugin = class {
2987
3562
  execute: () => {
2988
3563
  instance.print?.();
2989
3564
  }
3565
+ },
3566
+ {
3567
+ id: "open-window",
3568
+ icon: "open-window",
3569
+ label: "Open in Separate Full Window",
3570
+ type: "button",
3571
+ group: "actions",
3572
+ execute: () => {
3573
+ instance.openInSeparateWindow?.();
3574
+ }
2990
3575
  }
2991
3576
  );
2992
3577
  return actions;
@@ -3152,6 +3737,63 @@ var ExcelPlugin = class {
3152
3737
  },
3153
3738
  getPageCount: () => sheetNames.length,
3154
3739
  getCurrentPage: () => currentSheetIndex,
3740
+ getThumbnails: () => {
3741
+ return sheetNames.map((name, idx) => ({
3742
+ index: idx,
3743
+ label: name,
3744
+ render: async (canvas) => {
3745
+ canvas.width = 140;
3746
+ canvas.height = 100;
3747
+ const c = canvas.getContext("2d");
3748
+ if (!c) return;
3749
+ c.fillStyle = "#ffffff";
3750
+ c.fillRect(0, 0, canvas.width, canvas.height);
3751
+ c.fillStyle = "#107c41";
3752
+ c.fillRect(0, 0, canvas.width, 16);
3753
+ c.fillStyle = "#ffffff";
3754
+ c.font = 'bold 8px -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif';
3755
+ c.fillText(`\u{1F4CA} ${name.slice(0, 16)}`, 6, 11);
3756
+ const startY = 17;
3757
+ const rowH = 13;
3758
+ const colW = 30;
3759
+ const colHeaders = ["A", "B", "C", "D"];
3760
+ c.fillStyle = "#f1f5f9";
3761
+ c.fillRect(0, startY, canvas.width, rowH);
3762
+ c.strokeStyle = "#cbd5e1";
3763
+ c.lineWidth = 0.8;
3764
+ c.strokeRect(0, startY, canvas.width, rowH);
3765
+ c.fillStyle = "#64748b";
3766
+ c.font = "bold 7px sans-serif";
3767
+ for (let ci = 0; ci < colHeaders.length; ci++) {
3768
+ c.fillText(colHeaders[ci], 14 + ci * colW, startY + 9);
3769
+ }
3770
+ const ws = wb?.Sheets[name];
3771
+ for (let ri = 1; ri <= 5; ri++) {
3772
+ const y = startY + ri * rowH;
3773
+ if (y > canvas.height - 2) break;
3774
+ c.fillStyle = "#f8fafc";
3775
+ c.fillRect(0, y, 12, rowH);
3776
+ c.fillStyle = "#94a3b8";
3777
+ c.font = "6px sans-serif";
3778
+ c.fillText(String(ri), 3, y + 9);
3779
+ c.strokeStyle = "#e2e8f0";
3780
+ c.strokeRect(0, y, canvas.width, rowH);
3781
+ c.fillStyle = "#334155";
3782
+ c.font = "6px sans-serif";
3783
+ for (let ci = 0; ci < colHeaders.length; ci++) {
3784
+ const cellRef = `${colHeaders[ci]}${ri}`;
3785
+ const cellVal = ws?.[cellRef]?.w || ws?.[cellRef]?.v;
3786
+ if (cellVal !== void 0) {
3787
+ c.fillText(String(cellVal).slice(0, 6), 14 + ci * colW, y + 9);
3788
+ }
3789
+ }
3790
+ }
3791
+ c.strokeStyle = "#cbd5e1";
3792
+ c.lineWidth = 1;
3793
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
3794
+ }
3795
+ }));
3796
+ },
3155
3797
  download: () => {
3156
3798
  const blob = new Blob([ctx.buffer], { type: this.mimeTypes[0] });
3157
3799
  const url = URL.createObjectURL(blob);
@@ -3184,7 +3826,39 @@ var CsvPlugin = class {
3184
3826
  return ext === ".csv" || ext === ".tsv" || mime === "text/csv" || mime === "text/tab-separated-values";
3185
3827
  }
3186
3828
  getToolbarActions(instance) {
3187
- return [
3829
+ const totalPages = instance.getPageCount?.() ?? 1;
3830
+ const actions = [];
3831
+ actions.push({
3832
+ id: "thumbnails",
3833
+ icon: "thumbnails",
3834
+ label: "Page Thumbnails",
3835
+ type: "button",
3836
+ group: "navigation",
3837
+ execute: () => instance.toggleThumbnails?.()
3838
+ });
3839
+ if (totalPages > 1) {
3840
+ actions.push({
3841
+ id: "page-nav",
3842
+ icon: "",
3843
+ label: "Page Navigation",
3844
+ type: "page-nav",
3845
+ group: "navigation",
3846
+ value: instance.getCurrentPage?.() ?? 1,
3847
+ max: totalPages,
3848
+ execute: (action, page) => {
3849
+ const cur = instance.getCurrentPage?.() ?? 1;
3850
+ const max = instance.getPageCount?.() ?? 1;
3851
+ if (action === "prev") {
3852
+ if (cur > 1) instance.goToPage?.(cur - 1);
3853
+ } else if (action === "next") {
3854
+ if (cur < max) instance.goToPage?.(cur + 1);
3855
+ } else if (typeof page === "number") {
3856
+ instance.goToPage?.(page);
3857
+ }
3858
+ }
3859
+ });
3860
+ }
3861
+ actions.push(
3188
3862
  {
3189
3863
  id: "zoom-out",
3190
3864
  icon: "zoom-out",
@@ -3205,6 +3879,16 @@ var CsvPlugin = class {
3205
3879
  instance.zoomIn?.();
3206
3880
  }
3207
3881
  },
3882
+ {
3883
+ id: "fit-page",
3884
+ icon: "fit-page",
3885
+ label: "Fit to View",
3886
+ type: "button",
3887
+ group: "zoom",
3888
+ execute: () => {
3889
+ instance.fitToPage?.();
3890
+ }
3891
+ },
3208
3892
  {
3209
3893
  id: "download",
3210
3894
  icon: "download",
@@ -3224,8 +3908,19 @@ var CsvPlugin = class {
3224
3908
  execute: () => {
3225
3909
  instance.print?.();
3226
3910
  }
3911
+ },
3912
+ {
3913
+ id: "open-window",
3914
+ icon: "open-window",
3915
+ label: "Open in Separate Full Window",
3916
+ type: "button",
3917
+ group: "actions",
3918
+ execute: () => {
3919
+ instance.openInSeparateWindow?.();
3920
+ }
3227
3921
  }
3228
- ];
3922
+ );
3923
+ return actions;
3229
3924
  }
3230
3925
  async render(ctx) {
3231
3926
  const decoder = new TextDecoder("utf-8");
@@ -3237,33 +3932,62 @@ var CsvPlugin = class {
3237
3932
  container.style.padding = "16px";
3238
3933
  container.style.boxSizing = "border-box";
3239
3934
  container.style.transformOrigin = "top left";
3935
+ const delimiter = ctx.metadata.extension === ".tsv" ? " " : ",";
3936
+ const allLines = text.split(/\r?\n/).filter((r) => r.trim().length > 0);
3937
+ const headerLine = allLines[0] || "";
3938
+ const headerCells = headerLine.split(delimiter);
3939
+ const dataLines = allLines.slice(1);
3940
+ const ROWS_PER_PAGE = 100;
3941
+ const totalPages = Math.max(1, Math.ceil(dataLines.length / ROWS_PER_PAGE));
3942
+ let currentPage = 1;
3240
3943
  const table = document.createElement("table");
3241
3944
  table.style.borderCollapse = "collapse";
3242
3945
  table.style.width = "100%";
3243
- table.style.fontFamily = "monospace";
3946
+ table.style.fontFamily = '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, monospace';
3244
3947
  table.style.fontSize = "13px";
3245
- const delimiter = ctx.metadata.extension === ".tsv" ? " " : ",";
3246
- const rows = text.split(/\r?\n/).slice(0, 500);
3247
- rows.forEach((row, rowIndex) => {
3248
- if (!row.trim()) return;
3249
- const tr = document.createElement("tr");
3250
- const cells = row.split(delimiter);
3251
- cells.forEach((cell) => {
3252
- const td = document.createElement(rowIndex === 0 ? "th" : "td");
3253
- td.textContent = cell.trim();
3254
- td.style.border = "1px solid #d0d7de";
3255
- td.style.padding = "6px 12px";
3256
- if (rowIndex === 0) {
3257
- td.style.backgroundColor = "#f6f8fa";
3258
- td.style.fontWeight = "bold";
3259
- }
3260
- tr.appendChild(td);
3948
+ const renderPage = (pageNum) => {
3949
+ currentPage = Math.max(1, Math.min(totalPages, pageNum));
3950
+ table.innerHTML = "";
3951
+ const headerTr = document.createElement("tr");
3952
+ headerCells.forEach((cell) => {
3953
+ const th = document.createElement("th");
3954
+ th.textContent = cell.trim();
3955
+ th.style.border = "1px solid #d0d7de";
3956
+ th.style.padding = "8px 12px";
3957
+ th.style.backgroundColor = "#f6f8fa";
3958
+ th.style.fontWeight = "600";
3959
+ th.style.whiteSpace = "nowrap";
3960
+ headerTr.appendChild(th);
3261
3961
  });
3262
- table.appendChild(tr);
3263
- });
3962
+ table.appendChild(headerTr);
3963
+ const start = (currentPage - 1) * ROWS_PER_PAGE;
3964
+ const end = Math.min(dataLines.length, start + ROWS_PER_PAGE);
3965
+ const pageRows = dataLines.slice(start, end);
3966
+ pageRows.forEach((row, idx) => {
3967
+ const tr = document.createElement("tr");
3968
+ if (idx % 2 === 1) tr.style.backgroundColor = "#f8fafc";
3969
+ const cells = row.split(delimiter);
3970
+ cells.forEach((cell) => {
3971
+ const td = document.createElement("td");
3972
+ td.textContent = cell.trim();
3973
+ td.style.border = "1px solid #d0d7de";
3974
+ td.style.padding = "6px 12px";
3975
+ td.style.whiteSpace = "nowrap";
3976
+ tr.appendChild(td);
3977
+ });
3978
+ table.appendChild(tr);
3979
+ });
3980
+ container.scrollTop = 0;
3981
+ ctx.emit("page-change", { page: currentPage, total: totalPages });
3982
+ };
3983
+ renderPage(1);
3264
3984
  container.appendChild(table);
3265
3985
  ctx.container.appendChild(container);
3266
3986
  let scale = 1;
3987
+ const applyScale = () => {
3988
+ container.style.transform = `scale(${scale})`;
3989
+ container.style.transformOrigin = "top left";
3990
+ };
3267
3991
  const cleanup = () => {
3268
3992
  container.remove();
3269
3993
  ctx.container.innerHTML = "";
@@ -3271,22 +3995,86 @@ var CsvPlugin = class {
3271
3995
  ctx.signal.addEventListener("abort", cleanup);
3272
3996
  return {
3273
3997
  destroy: cleanup,
3998
+ getPageCount: () => totalPages,
3999
+ getCurrentPage: () => currentPage,
4000
+ goToPage: (page) => renderPage(page),
4001
+ getThumbnails: () => {
4002
+ const thumbnails = [];
4003
+ for (let i = 0; i < totalPages; i++) {
4004
+ const pageIdx = i;
4005
+ const startRow = pageIdx * ROWS_PER_PAGE + 1;
4006
+ const endRow = Math.min(dataLines.length, (pageIdx + 1) * ROWS_PER_PAGE);
4007
+ const label = totalPages === 1 ? "Table" : `Page ${pageIdx + 1} (${startRow}-${endRow})`;
4008
+ thumbnails.push({
4009
+ index: pageIdx,
4010
+ label,
4011
+ render: async (canvas) => {
4012
+ canvas.width = 140;
4013
+ canvas.height = 100;
4014
+ const c = canvas.getContext("2d");
4015
+ if (!c) return;
4016
+ c.fillStyle = "#ffffff";
4017
+ c.fillRect(0, 0, canvas.width, canvas.height);
4018
+ c.fillStyle = "#2563eb";
4019
+ c.fillRect(0, 0, canvas.width, 16);
4020
+ c.fillStyle = "#ffffff";
4021
+ c.font = "bold 8px sans-serif";
4022
+ c.fillText(`\u{1F4C8} ${label.slice(0, 18)}`, 6, 11);
4023
+ const startY = 17;
4024
+ const rowH = 13;
4025
+ const colW = 28;
4026
+ const cols = Math.min(4, headerCells.length || 4);
4027
+ c.fillStyle = "#f1f5f9";
4028
+ c.fillRect(0, startY, canvas.width, rowH);
4029
+ c.strokeStyle = "#cbd5e1";
4030
+ c.lineWidth = 0.8;
4031
+ c.strokeRect(0, startY, canvas.width, rowH);
4032
+ c.fillStyle = "#64748b";
4033
+ c.font = "bold 7px sans-serif";
4034
+ for (let ci = 0; ci < cols; ci++) {
4035
+ const hName = headerCells[ci] || `Col ${ci + 1}`;
4036
+ c.fillText(hName.slice(0, 5), 8 + ci * colW, startY + 9);
4037
+ }
4038
+ const rowsToPreview = dataLines.slice(pageIdx * ROWS_PER_PAGE, pageIdx * ROWS_PER_PAGE + 5);
4039
+ for (let ri = 0; ri < rowsToPreview.length; ri++) {
4040
+ const y = startY + (ri + 1) * rowH;
4041
+ if (y > canvas.height - 2) break;
4042
+ c.strokeStyle = "#e2e8f0";
4043
+ c.strokeRect(0, y, canvas.width, rowH);
4044
+ const cVals = rowsToPreview[ri].split(delimiter);
4045
+ c.fillStyle = "#334155";
4046
+ c.font = "6px sans-serif";
4047
+ for (let ci = 0; ci < cols; ci++) {
4048
+ const val = cVals[ci] || "";
4049
+ c.fillText(val.trim().slice(0, 6), 8 + ci * colW, y + 9);
4050
+ }
4051
+ }
4052
+ c.strokeStyle = "#cbd5e1";
4053
+ c.lineWidth = 1;
4054
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
4055
+ }
4056
+ });
4057
+ }
4058
+ return thumbnails;
4059
+ },
3274
4060
  zoomIn: () => {
3275
4061
  scale += 0.1;
3276
- container.style.transform = `scale(${scale})`;
4062
+ applyScale();
3277
4063
  },
3278
4064
  zoomOut: () => {
3279
4065
  scale = Math.max(0.2, scale - 0.1);
3280
- container.style.transform = `scale(${scale})`;
4066
+ applyScale();
3281
4067
  },
3282
4068
  getZoom: () => scale,
3283
4069
  setZoom: (level) => {
3284
4070
  scale = level;
3285
- container.style.transform = `scale(${scale})`;
4071
+ applyScale();
3286
4072
  },
3287
4073
  fitToPage: () => {
3288
- scale = 1;
3289
- container.style.transform = `scale(1)`;
4074
+ const availW = Math.max(280, (ctx.container.clientWidth || window.innerWidth) - 48);
4075
+ const tW = table.offsetWidth || 600;
4076
+ scale = Math.max(0.35, Math.min(1, availW / tW));
4077
+ applyScale();
3290
4078
  },
3291
4079
  download: () => {
3292
4080
  const blob = new Blob([ctx.buffer], { type: "text/csv" });
@@ -3350,28 +4138,34 @@ var CodePlugin = class {
3350
4138
  getToolbarActions(instance) {
3351
4139
  const totalPages = instance.getPageCount?.() ?? 1;
3352
4140
  const actions = [];
3353
- if (totalPages > 1) {
3354
- actions.push({
3355
- id: "page-nav",
3356
- icon: "",
3357
- label: "Page Navigation",
3358
- type: "page-nav",
3359
- group: "navigation",
3360
- value: instance.getCurrentPage?.() ?? 1,
3361
- max: totalPages,
3362
- execute: (action, page) => {
3363
- const cur = instance.getCurrentPage?.() ?? 1;
3364
- const max = instance.getPageCount?.() ?? 1;
3365
- if (action === "prev") {
3366
- if (cur > 1) instance.goToPage?.(cur - 1);
3367
- } else if (action === "next") {
3368
- if (cur < max) instance.goToPage?.(cur + 1);
3369
- } else if (typeof page === "number") {
3370
- instance.goToPage?.(page);
3371
- }
4141
+ actions.push({
4142
+ id: "thumbnails",
4143
+ icon: "thumbnails",
4144
+ label: "Page Thumbnails",
4145
+ type: "button",
4146
+ group: "navigation",
4147
+ execute: () => instance.toggleThumbnails?.()
4148
+ });
4149
+ actions.push({
4150
+ id: "page-nav",
4151
+ icon: "",
4152
+ label: "Page Navigation",
4153
+ type: "page-nav",
4154
+ group: "navigation",
4155
+ value: instance.getCurrentPage?.() ?? 1,
4156
+ max: totalPages,
4157
+ execute: (action, page) => {
4158
+ const cur = instance.getCurrentPage?.() ?? 1;
4159
+ const max = instance.getPageCount?.() ?? 1;
4160
+ if (action === "prev") {
4161
+ if (cur > 1) instance.goToPage?.(cur - 1);
4162
+ } else if (action === "next") {
4163
+ if (cur < max) instance.goToPage?.(cur + 1);
4164
+ } else if (typeof page === "number") {
4165
+ instance.goToPage?.(page);
3372
4166
  }
3373
- });
3374
- }
4167
+ }
4168
+ });
3375
4169
  actions.push(
3376
4170
  {
3377
4171
  id: "zoom-out",
@@ -3515,16 +4309,19 @@ var CodePlugin = class {
3515
4309
  let fontSize = 13;
3516
4310
  let rotation = 0;
3517
4311
  let zoomLevel = 1;
4312
+ let isUserZoomed = false;
3518
4313
  const pre = document.createElement("pre");
3519
4314
  const code = document.createElement("code");
3520
4315
  if (isTxt) {
4316
+ container.style.overflowX = "hidden";
4317
+ container.style.overflowY = "auto";
3521
4318
  container.style.backgroundColor = "#f1f5f9";
3522
- container.style.padding = "32px";
4319
+ container.style.padding = "16px 8px";
3523
4320
  container.style.boxSizing = "border-box";
3524
4321
  container.style.display = "flex";
3525
4322
  container.style.flexDirection = "column";
3526
4323
  container.style.alignItems = "center";
3527
- pre.style.margin = "0";
4324
+ pre.style.margin = "0 auto";
3528
4325
  pre.style.fontFamily = "Consolas, 'Courier New', monospace";
3529
4326
  pre.style.fontSize = "13px";
3530
4327
  pre.style.color = "#1e293b";
@@ -3536,10 +4333,12 @@ var CodePlugin = class {
3536
4333
  pre.style.minHeight = "1056px";
3537
4334
  pre.style.padding = "72px 56px";
3538
4335
  pre.style.boxSizing = "border-box";
3539
- pre.style.boxShadow = "0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)";
4336
+ pre.style.boxShadow = "0 4px 24px rgba(0, 0, 0, 0.08)";
3540
4337
  pre.style.borderRadius = "4px";
3541
4338
  pre.style.transformOrigin = "top center";
4339
+ pre.style.transition = "transform 0.15s ease";
3542
4340
  } else {
4341
+ container.style.overflow = "auto";
3543
4342
  container.style.backgroundColor = "#1e1e1e";
3544
4343
  container.style.color = "#d4d4d4";
3545
4344
  container.style.padding = "16px";
@@ -3571,42 +4370,9 @@ var CodePlugin = class {
3571
4370
  renderCodePage(rawPages[0] || (isTxt ? "" : fullText));
3572
4371
  pre.appendChild(code);
3573
4372
  container.appendChild(pre);
3574
- let indicator = null;
3575
- if (totalPages > 1) {
3576
- indicator = document.createElement("div");
3577
- indicator.className = "fp-code-page-indicator";
3578
- indicator.style.position = "sticky";
3579
- indicator.style.bottom = "16px";
3580
- if (isTxt) {
3581
- indicator.style.backgroundColor = "rgba(255, 255, 255, 0.9)";
3582
- indicator.style.color = "#334155";
3583
- indicator.style.border = "1px solid #e2e8f0";
3584
- indicator.style.boxShadow = "0 1px 3px rgba(0,0,0,0.1)";
3585
- } else {
3586
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
3587
- indicator.style.color = "#f8fafc";
3588
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
3589
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
3590
- indicator.style.backdropFilter = "blur(8px)";
3591
- }
3592
- indicator.style.fontSize = "12px";
3593
- indicator.style.fontWeight = "600";
3594
- indicator.style.padding = "5px 14px";
3595
- indicator.style.borderRadius = "20px";
3596
- indicator.style.zIndex = "10";
3597
- indicator.style.userSelect = "none";
3598
- indicator.style.pointerEvents = "none";
3599
- indicator.style.textAlign = "center";
3600
- indicator.style.width = "fit-content";
3601
- indicator.style.margin = "16px auto 0";
3602
- container.appendChild(indicator);
3603
- }
3604
4373
  const showPage = (pageNum) => {
3605
4374
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
3606
4375
  renderCodePage(rawPages[currentPage - 1] || (isTxt ? "" : fullText));
3607
- if (indicator) {
3608
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
3609
- }
3610
4376
  container.scrollTop = 0;
3611
4377
  ctx.emit("page-change", { page: currentPage, total: totalPages });
3612
4378
  };
@@ -3614,36 +4380,90 @@ var CodePlugin = class {
3614
4380
  showPage(1);
3615
4381
  }
3616
4382
  ctx.container.appendChild(container);
3617
- const cleanup = () => {
3618
- indicator?.remove();
3619
- container.remove();
3620
- ctx.container.innerHTML = "";
4383
+ const calculateTxtFit = () => {
4384
+ const availW = Math.max(280, container.clientWidth - 32);
4385
+ return Math.max(0.4, Math.min(3, availW / 816));
3621
4386
  };
3622
- ctx.signal.addEventListener("abort", cleanup);
3623
4387
  const updateTransform = () => {
3624
4388
  if (isTxt) {
3625
4389
  pre.style.transform = `scale(${zoomLevel}) rotate(${rotation}deg)`;
4390
+ const scaledH = 1056 * zoomLevel;
4391
+ const extraH = Math.max(0, scaledH - 1056);
4392
+ pre.style.marginBottom = `${extraH + 32}px`;
3626
4393
  } else {
3627
4394
  pre.style.transform = `rotate(${rotation}deg)`;
3628
4395
  pre.style.fontSize = `${fontSize}px`;
3629
4396
  }
3630
4397
  };
4398
+ let resizeObserver = null;
4399
+ if (isTxt) {
4400
+ setTimeout(() => {
4401
+ zoomLevel = calculateTxtFit();
4402
+ updateTransform();
4403
+ }, 60);
4404
+ resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
4405
+ if (!isUserZoomed) {
4406
+ zoomLevel = calculateTxtFit();
4407
+ updateTransform();
4408
+ }
4409
+ }) : null;
4410
+ resizeObserver?.observe(container);
4411
+ }
4412
+ const cleanup = () => {
4413
+ resizeObserver?.disconnect();
4414
+ container.remove();
4415
+ ctx.container.innerHTML = "";
4416
+ };
4417
+ ctx.signal.addEventListener("abort", cleanup);
3631
4418
  return {
3632
4419
  destroy: cleanup,
3633
4420
  getPageCount: () => totalPages,
3634
4421
  getCurrentPage: () => currentPage,
3635
4422
  goToPage: (page) => showPage(page),
4423
+ getThumbnails: () => {
4424
+ return rawPages.map((pageText, idx) => ({
4425
+ index: idx,
4426
+ label: `Page ${idx + 1}`,
4427
+ render: async (canvas) => {
4428
+ canvas.width = 140;
4429
+ canvas.height = 180;
4430
+ const c = canvas.getContext("2d");
4431
+ if (!c) return;
4432
+ c.fillStyle = "#ffffff";
4433
+ c.fillRect(0, 0, canvas.width, canvas.height);
4434
+ c.strokeStyle = "#cbd5e1";
4435
+ c.lineWidth = 1;
4436
+ c.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
4437
+ c.fillStyle = "#64748b";
4438
+ const lines = (pageText || "").split("\n").slice(0, 24);
4439
+ const lineH = 6;
4440
+ const startY = 14;
4441
+ const startX = 12;
4442
+ const maxW = canvas.width - 24;
4443
+ c.font = "5px monospace";
4444
+ for (let li = 0; li < lines.length; li++) {
4445
+ const l = lines[li].trim();
4446
+ if (!l) continue;
4447
+ const y = startY + li * lineH;
4448
+ if (y > canvas.height - 12) break;
4449
+ c.fillText(l.slice(0, 30), startX, y, maxW);
4450
+ }
4451
+ }
4452
+ }));
4453
+ },
3636
4454
  zoomIn: () => {
4455
+ isUserZoomed = true;
3637
4456
  if (isTxt) {
3638
- zoomLevel = Math.min(3, zoomLevel + 0.1);
4457
+ zoomLevel = Math.min(3.5, zoomLevel + 0.15);
3639
4458
  } else {
3640
4459
  fontSize = Math.min(32, fontSize + 2);
3641
4460
  }
3642
4461
  updateTransform();
3643
4462
  },
3644
4463
  zoomOut: () => {
4464
+ isUserZoomed = true;
3645
4465
  if (isTxt) {
3646
- zoomLevel = Math.max(0.1, zoomLevel - 0.1);
4466
+ zoomLevel = Math.max(0.1, zoomLevel - 0.15);
3647
4467
  } else {
3648
4468
  fontSize = Math.max(8, fontSize - 2);
3649
4469
  }
@@ -3651,6 +4471,7 @@ var CodePlugin = class {
3651
4471
  },
3652
4472
  getZoom: () => isTxt ? zoomLevel : fontSize / 13,
3653
4473
  setZoom: (level) => {
4474
+ isUserZoomed = true;
3654
4475
  if (isTxt) {
3655
4476
  zoomLevel = level;
3656
4477
  } else {
@@ -3659,9 +4480,10 @@ var CodePlugin = class {
3659
4480
  updateTransform();
3660
4481
  },
3661
4482
  fitToPage: () => {
4483
+ isUserZoomed = false;
3662
4484
  fontSize = 13;
3663
4485
  rotation = 0;
3664
- zoomLevel = isTxt ? Math.max(0.65, Math.min(1.05, (container.clientWidth - 48) / 816)) : 1;
4486
+ zoomLevel = isTxt ? calculateTxtFit() : 1;
3665
4487
  updateTransform();
3666
4488
  },
3667
4489
  rotateCW: () => {
@@ -4542,28 +5364,26 @@ var RtfPlugin = class {
4542
5364
  getToolbarActions(instance) {
4543
5365
  const totalPages = instance.getPageCount?.() ?? 1;
4544
5366
  const actions = [];
4545
- if (totalPages > 1) {
4546
- actions.push({
4547
- id: "page-nav",
4548
- icon: "",
4549
- label: "Page Navigation",
4550
- type: "page-nav",
4551
- group: "navigation",
4552
- value: instance.getCurrentPage?.() ?? 1,
4553
- max: totalPages,
4554
- execute: (action, page) => {
4555
- const cur = instance.getCurrentPage?.() ?? 1;
4556
- const max = instance.getPageCount?.() ?? 1;
4557
- if (action === "prev") {
4558
- if (cur > 1) instance.goToPage?.(cur - 1);
4559
- } else if (action === "next") {
4560
- if (cur < max) instance.goToPage?.(cur + 1);
4561
- } else if (typeof page === "number") {
4562
- instance.goToPage?.(page);
4563
- }
5367
+ actions.push({
5368
+ id: "page-nav",
5369
+ icon: "",
5370
+ label: "Page Navigation",
5371
+ type: "page-nav",
5372
+ group: "navigation",
5373
+ value: instance.getCurrentPage?.() ?? 1,
5374
+ max: totalPages,
5375
+ execute: (action, page) => {
5376
+ const cur = instance.getCurrentPage?.() ?? 1;
5377
+ const max = instance.getPageCount?.() ?? 1;
5378
+ if (action === "prev") {
5379
+ if (cur > 1) instance.goToPage?.(cur - 1);
5380
+ } else if (action === "next") {
5381
+ if (cur < max) instance.goToPage?.(cur + 1);
5382
+ } else if (typeof page === "number") {
5383
+ instance.goToPage?.(page);
4564
5384
  }
4565
- });
4566
- }
5385
+ }
5386
+ });
4567
5387
  actions.push(
4568
5388
  {
4569
5389
  id: "zoom-out",
@@ -4635,43 +5455,57 @@ var RtfPlugin = class {
4635
5455
  async render(ctx) {
4636
5456
  const wrapper = document.createElement("div");
4637
5457
  wrapper.className = "fp-rtf-wrapper";
4638
- wrapper.style.padding = "32px";
4639
- wrapper.style.maxWidth = "850px";
5458
+ wrapper.style.padding = "0";
5459
+ wrapper.style.width = "816px";
4640
5460
  wrapper.style.margin = "0 auto";
4641
- wrapper.style.backgroundColor = "#fff";
4642
- wrapper.style.boxShadow = "0 2px 8px rgba(0,0,0,0.08)";
4643
- wrapper.style.borderRadius = "4px";
4644
- wrapper.style.minHeight = "100%";
5461
+ wrapper.style.boxSizing = "border-box";
5462
+ wrapper.style.display = "flex";
5463
+ wrapper.style.flexDirection = "column";
5464
+ wrapper.style.alignItems = "center";
5465
+ wrapper.style.backgroundColor = "transparent";
4645
5466
  wrapper.style.transformOrigin = "top center";
4646
- wrapper.style.transition = "transform 0.2s ease";
4647
- ctx.container.style.overflow = "auto";
4648
- ctx.container.style.padding = "24px";
5467
+ wrapper.style.transition = "transform 0.15s ease";
5468
+ ctx.container.style.overflowX = "hidden";
5469
+ ctx.container.style.overflowY = "auto";
5470
+ ctx.container.style.padding = "16px 8px";
4649
5471
  ctx.container.style.backgroundColor = "#f1f5f9";
4650
5472
  ctx.container.appendChild(wrapper);
4651
5473
  let scale = 1;
4652
5474
  let rotation = 0;
4653
5475
  let pageElements = [];
4654
- let fitMode = "width";
5476
+ let isUserZoomed = false;
5477
+ let fitMode = ctx?.options?.fitMode || "width";
4655
5478
  const calculateFitScale = (mode = fitMode) => {
4656
5479
  const activeEl = pageElements[currentPage - 1] || wrapper;
4657
- const elW = activeEl.offsetWidth || 850;
4658
- const singlePageH = Math.min(activeEl.offsetHeight || 1056, Math.round(elW * 1.32));
4659
- const availW = Math.max(280, ctx.container.clientWidth - 48);
4660
- const availH = Math.max(280, ctx.container.clientHeight - 80);
5480
+ const elW = 816;
5481
+ const singlePageH = activeEl?.offsetHeight || 1056;
5482
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
5483
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
4661
5484
  const sW = availW / elW;
4662
5485
  const sH = availH / singlePageH;
4663
5486
  if (mode === "page") {
4664
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
5487
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
4665
5488
  }
4666
- return Math.max(0.65, Math.min(1.05, sW));
5489
+ return Math.max(0.4, Math.min(3, sW));
4667
5490
  };
4668
5491
  const applyTransform = () => {
4669
5492
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
4670
5493
  wrapper.style.transformOrigin = "top center";
5494
+ const activeEl = pageElements[currentPage - 1];
5495
+ const baseH = activeEl?.offsetHeight || 1056;
5496
+ const scaledH = baseH * scale;
5497
+ const extraH = Math.max(0, scaledH - baseH);
5498
+ wrapper.style.marginBottom = `${extraH + 32}px`;
4671
5499
  };
5500
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
5501
+ if (!isUserZoomed) {
5502
+ scale = calculateFitScale(fitMode);
5503
+ applyTransform();
5504
+ }
5505
+ }) : null;
5506
+ resizeObserver?.observe(ctx.container);
4672
5507
  setTimeout(() => {
4673
- fitMode = "width";
4674
- scale = calculateFitScale("width");
5508
+ scale = calculateFitScale(fitMode);
4675
5509
  applyTransform();
4676
5510
  }, 60);
4677
5511
  try {
@@ -4681,20 +5515,34 @@ var RtfPlugin = class {
4681
5515
  const doc = new RTFJS.Document(ctx.buffer, {});
4682
5516
  const htmlElements = await doc.render();
4683
5517
  const contentNodes = [];
4684
- for (const item of htmlElements) {
4685
- if (item.children && item.children.length > 0 && !item.tagName.toLowerCase().startsWith("table")) {
4686
- contentNodes.push(...Array.from(item.children));
4687
- } else {
4688
- contentNodes.push(item);
5518
+ const extractBlocks = (nodes) => {
5519
+ for (const item of nodes) {
5520
+ if (!item || !(item instanceof HTMLElement)) continue;
5521
+ const tag = item.tagName.toLowerCase();
5522
+ const hasChildBlocks = Array.from(item.children).some((c) => {
5523
+ const ct = c.tagName.toLowerCase();
5524
+ return ["p", "div", "table", "h1", "h2", "h3", "h4", "h5", "h6", "section", "article", "ul", "ol"].includes(ct);
5525
+ });
5526
+ if (hasChildBlocks && !["table", "tr", "td", "th", "ul", "ol", "li"].includes(tag)) {
5527
+ extractBlocks(Array.from(item.children));
5528
+ } else {
5529
+ if (tag === "p" || tag === "div") {
5530
+ item.style.display = "block";
5531
+ item.style.marginBottom = "12px";
5532
+ item.style.lineHeight = "1.6";
5533
+ }
5534
+ contentNodes.push(item);
5535
+ }
4689
5536
  }
4690
- }
5537
+ };
5538
+ extractBlocks(htmlElements);
4691
5539
  wrapper.innerHTML = "";
4692
5540
  contentNodes.forEach((node) => wrapper.appendChild(node));
4693
5541
  const childHeights = contentNodes.map((c) => {
4694
5542
  const rectH = c.getBoundingClientRect ? c.getBoundingClientRect().height : 0;
4695
5543
  const offH = c.offsetHeight || 0;
4696
5544
  const textLen = c.textContent?.trim().length || 0;
4697
- const estH = Math.max(24, Math.ceil(textLen / 75) * 22 + 14);
5545
+ const estH = Math.max(28, Math.ceil(textLen / 75) * 24 + 16);
4698
5546
  return Math.max(rectH, offH, estH);
4699
5547
  });
4700
5548
  wrapper.innerHTML = "";
@@ -4711,6 +5559,7 @@ var RtfPlugin = class {
4711
5559
  card.style.marginBottom = "24px";
4712
5560
  card.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
4713
5561
  card.style.lineHeight = "1.6";
5562
+ card.style.color = "#1e293b";
4714
5563
  return card;
4715
5564
  };
4716
5565
  let curCard = createRtfCard();
@@ -4746,9 +5595,8 @@ var RtfPlugin = class {
4746
5595
  pageCard.style.padding = "72px 56px";
4747
5596
  pageCard.style.width = "816px";
4748
5597
  pageCard.style.minHeight = "1056px";
4749
- pageCard.style.maxWidth = "100%";
4750
5598
  pageCard.style.boxSizing = "border-box";
4751
- pageCard.style.fontFamily = "serif";
5599
+ pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
4752
5600
  pageCard.style.fontSize = "12pt";
4753
5601
  pageCard.style.lineHeight = "1.6";
4754
5602
  pageCard.style.color = "#1e293b";
@@ -4761,29 +5609,6 @@ var RtfPlugin = class {
4761
5609
  }
4762
5610
  const totalPages = Math.max(1, pageElements.length);
4763
5611
  let currentPage = 1;
4764
- let indicator = null;
4765
- if (totalPages > 1) {
4766
- indicator = document.createElement("div");
4767
- indicator.className = "fp-rtf-page-indicator";
4768
- indicator.style.position = "sticky";
4769
- indicator.style.bottom = "16px";
4770
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
4771
- indicator.style.backdropFilter = "blur(8px)";
4772
- indicator.style.color = "#f8fafc";
4773
- indicator.style.fontSize = "12px";
4774
- indicator.style.fontWeight = "600";
4775
- indicator.style.padding = "5px 14px";
4776
- indicator.style.borderRadius = "20px";
4777
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
4778
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
4779
- indicator.style.zIndex = "10";
4780
- indicator.style.userSelect = "none";
4781
- indicator.style.pointerEvents = "none";
4782
- indicator.style.textAlign = "center";
4783
- indicator.style.width = "fit-content";
4784
- indicator.style.margin = "16px auto 0";
4785
- ctx.container.appendChild(indicator);
4786
- }
4787
5612
  const showPage = (pageNum) => {
4788
5613
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
4789
5614
  if (totalPages > 1) {
@@ -4791,9 +5616,6 @@ var RtfPlugin = class {
4791
5616
  el.style.display = idx + 1 === currentPage ? "block" : "none";
4792
5617
  });
4793
5618
  }
4794
- if (indicator) {
4795
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
4796
- }
4797
5619
  ctx.container.scrollTop = 0;
4798
5620
  ctx.emit("page-change", { page: currentPage, total: totalPages });
4799
5621
  };
@@ -4801,7 +5623,7 @@ var RtfPlugin = class {
4801
5623
  showPage(1);
4802
5624
  }
4803
5625
  const cleanup = () => {
4804
- indicator?.remove();
5626
+ resizeObserver?.disconnect();
4805
5627
  wrapper.remove();
4806
5628
  ctx.container.innerHTML = "";
4807
5629
  };
@@ -4812,21 +5634,25 @@ var RtfPlugin = class {
4812
5634
  getCurrentPage: () => currentPage,
4813
5635
  goToPage: (page) => showPage(page),
4814
5636
  zoomIn: () => {
5637
+ isUserZoomed = true;
4815
5638
  scale += 0.15;
4816
5639
  applyTransform();
4817
5640
  },
4818
5641
  zoomOut: () => {
5642
+ isUserZoomed = true;
4819
5643
  scale = Math.max(0.2, scale - 0.15);
4820
5644
  applyTransform();
4821
5645
  },
4822
5646
  getZoom: () => scale,
4823
5647
  setZoom: (level) => {
5648
+ isUserZoomed = true;
4824
5649
  scale = level;
4825
5650
  applyTransform();
4826
5651
  },
4827
5652
  fitToPage: () => {
4828
- fitMode = fitMode === "width" ? "page" : "width";
4829
- scale = calculateFitScale(fitMode);
5653
+ isUserZoomed = false;
5654
+ fitMode = "width";
5655
+ scale = calculateFitScale("width");
4830
5656
  rotation = 0;
4831
5657
  applyTransform();
4832
5658
  },
@@ -5014,40 +5840,36 @@ var OpenDocumentPlugin = class {
5014
5840
  const isPresentation = instance.isPresentation;
5015
5841
  const totalPages = instance.getPageCount?.() ?? 1;
5016
5842
  const actions = [];
5017
- if (isPresentation || totalPages > 1) {
5018
- if (isPresentation) {
5019
- actions.push({
5020
- id: "thumbnails",
5021
- icon: "thumbnails",
5022
- label: "Slide Thumbnails",
5023
- type: "button",
5024
- group: "navigation",
5025
- execute: () => instance.toggleThumbnails?.()
5026
- });
5027
- }
5028
- actions.push({
5029
- id: "page-nav",
5030
- icon: "",
5031
- label: isPresentation ? "Slide Navigation" : "Page Navigation",
5032
- type: "page-nav",
5033
- group: "navigation",
5034
- value: instance.getCurrentPage?.() ?? 1,
5035
- max: totalPages,
5036
- execute: (action, page) => {
5037
- const cur = instance.getCurrentPage?.() ?? 1;
5038
- const max = instance.getPageCount?.() ?? 1;
5039
- if (action === "prev") {
5040
- if (cur > 1) instance.goToPage?.(cur - 1);
5041
- } else if (action === "next") {
5042
- if (cur < max) instance.goToPage?.(cur + 1);
5043
- } else if (typeof page === "number") {
5044
- instance.goToPage?.(page);
5045
- } else if (typeof action === "number") {
5046
- instance.goToPage?.(action);
5047
- }
5843
+ actions.push({
5844
+ id: "thumbnails",
5845
+ icon: "thumbnails",
5846
+ label: isPresentation ? "Slide Thumbnails" : "Page Thumbnails",
5847
+ type: "button",
5848
+ group: "navigation",
5849
+ execute: () => instance.toggleThumbnails?.()
5850
+ });
5851
+ actions.push({
5852
+ id: "page-nav",
5853
+ icon: "",
5854
+ label: isPresentation ? "Slide Navigation" : "Page Navigation",
5855
+ type: "page-nav",
5856
+ group: "navigation",
5857
+ value: instance.getCurrentPage?.() ?? 1,
5858
+ max: totalPages,
5859
+ execute: (action, page) => {
5860
+ const cur = instance.getCurrentPage?.() ?? 1;
5861
+ const max = instance.getPageCount?.() ?? 1;
5862
+ if (action === "prev") {
5863
+ if (cur > 1) instance.goToPage?.(cur - 1);
5864
+ } else if (action === "next") {
5865
+ if (cur < max) instance.goToPage?.(cur + 1);
5866
+ } else if (typeof page === "number") {
5867
+ instance.goToPage?.(page);
5868
+ } else if (typeof action === "number") {
5869
+ instance.goToPage?.(action);
5048
5870
  }
5049
- });
5050
- }
5871
+ }
5872
+ });
5051
5873
  actions.push(
5052
5874
  {
5053
5875
  id: "zoom-out",
@@ -5138,49 +5960,65 @@ var OpenDocumentPlugin = class {
5138
5960
  container.className = "fp-odf-container";
5139
5961
  container.style.width = "100%";
5140
5962
  container.style.height = "100%";
5141
- container.style.overflow = "auto";
5142
- container.style.padding = "24px";
5963
+ container.style.overflowX = "hidden";
5964
+ container.style.overflowY = "auto";
5965
+ container.style.padding = "16px 8px";
5143
5966
  container.style.backgroundColor = "#f1f5f9";
5144
5967
  const wrapper = document.createElement("div");
5145
5968
  wrapper.className = "fp-odf-wrapper";
5146
5969
  wrapper.style.margin = "0 auto";
5970
+ wrapper.style.width = isPresentation ? "960px" : "816px";
5971
+ wrapper.style.boxSizing = "border-box";
5147
5972
  wrapper.style.backgroundColor = "#ffffff";
5148
5973
  wrapper.style.boxShadow = "0 2px 10px rgba(0,0,0,0.08)";
5149
5974
  wrapper.style.borderRadius = "4px";
5150
5975
  wrapper.style.transformOrigin = "top center";
5151
- wrapper.style.transition = "transform 0.2s ease";
5976
+ wrapper.style.transition = "transform 0.15s ease";
5152
5977
  container.appendChild(wrapper);
5153
5978
  ctx.container.appendChild(container);
5154
5979
  let scale = 1;
5155
5980
  let rotation = 0;
5156
- let fitMode = "width";
5981
+ let isUserZoomed = false;
5982
+ let fitMode = ctx?.options?.fitMode || "width";
5983
+ let currentPage = 1;
5984
+ let totalPages = 1;
5985
+ let slides = [];
5157
5986
  const calculateFitScale = (mode = fitMode) => {
5158
- const elW = wrapper.offsetWidth || 850;
5159
- const singlePageH = Math.min(wrapper.offsetHeight || 1056, Math.round(elW * 1.32));
5160
- const availW = Math.max(280, ctx.container.clientWidth - 48);
5161
- const availH = Math.max(280, ctx.container.clientHeight - 80);
5987
+ const activeSlide = slides[currentPage - 1];
5988
+ const elW = isPresentation ? 960 : 816;
5989
+ const singlePageH = Math.min(activeSlide?.offsetHeight || wrapper.offsetHeight || 1056, Math.round(elW * 1.32));
5990
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
5991
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
5162
5992
  const sW = availW / elW;
5163
5993
  const sH = availH / (isPresentation ? 540 : singlePageH);
5164
5994
  if (isPresentation) {
5165
- return Math.min(1, Math.min(sW, sH));
5995
+ return Math.min(2.5, Math.min(sW, sH));
5166
5996
  }
5167
5997
  if (mode === "page") {
5168
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
5998
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
5169
5999
  }
5170
- return Math.max(0.65, Math.min(1.05, sW));
6000
+ return Math.max(0.4, Math.min(3, sW));
5171
6001
  };
5172
6002
  const applyTransform = () => {
5173
6003
  wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5174
6004
  wrapper.style.transformOrigin = "top center";
6005
+ const activeSlide = slides[currentPage - 1];
6006
+ const baseH = activeSlide?.offsetHeight || wrapper.offsetHeight || 1056;
6007
+ const scaledH = baseH * scale;
6008
+ const extraH = Math.max(0, scaledH - baseH);
6009
+ wrapper.style.marginBottom = `${extraH + 32}px`;
5175
6010
  };
6011
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
6012
+ if (!isUserZoomed) {
6013
+ scale = calculateFitScale(fitMode);
6014
+ applyTransform();
6015
+ }
6016
+ }) : null;
6017
+ resizeObserver?.observe(ctx.container);
5176
6018
  setTimeout(() => {
5177
- fitMode = "width";
5178
- scale = calculateFitScale("width");
6019
+ scale = calculateFitScale(fitMode);
5179
6020
  applyTransform();
5180
6021
  }, 60);
5181
- let currentPage = 1;
5182
- let totalPages = 1;
5183
- let slides = [];
5184
6022
  if (isPresentation) {
5185
6023
  wrapper.style.maxWidth = "960px";
5186
6024
  wrapper.style.aspectRatio = "16 / 9";
@@ -5281,24 +6119,9 @@ var OpenDocumentPlugin = class {
5281
6119
  wrapper.appendChild(page);
5282
6120
  slides.push(page);
5283
6121
  });
5284
- const pageIndicator = document.createElement("div");
5285
- pageIndicator.className = "fp-odt-page-indicator";
5286
- pageIndicator.style.position = "sticky";
5287
- pageIndicator.style.bottom = "16px";
5288
- pageIndicator.style.left = "50%";
5289
- pageIndicator.style.transform = "translateX(-50%)";
5290
- pageIndicator.style.backgroundColor = "rgba(0, 0, 0, 0.6)";
5291
- pageIndicator.style.color = "#fff";
5292
- pageIndicator.style.padding = "6px 12px";
5293
- pageIndicator.style.borderRadius = "16px";
5294
- pageIndicator.style.fontSize = "12px";
5295
- pageIndicator.style.zIndex = "100";
5296
- pageIndicator.style.display = "inline-block";
5297
- pageIndicator.style.width = "fit-content";
5298
- pageIndicator.textContent = `Page 1 of ${totalPages}`;
5299
- container.appendChild(pageIndicator);
5300
6122
  }
5301
6123
  const cleanup = () => {
6124
+ resizeObserver?.disconnect();
5302
6125
  imageUrls.forEach((url) => URL.revokeObjectURL(url));
5303
6126
  container.remove();
5304
6127
  ctx.container.innerHTML = "";
@@ -5310,10 +6133,6 @@ var OpenDocumentPlugin = class {
5310
6133
  slides.forEach((s, idx) => {
5311
6134
  s.style.display = idx === page - 1 ? "block" : "none";
5312
6135
  });
5313
- const indicator = container.querySelector(".fp-odt-page-indicator");
5314
- if (indicator) {
5315
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
5316
- }
5317
6136
  container.scrollTop = 0;
5318
6137
  ctx.emit("page-change", { page: currentPage, total: totalPages });
5319
6138
  };
@@ -5321,21 +6140,25 @@ var OpenDocumentPlugin = class {
5321
6140
  destroy: cleanup,
5322
6141
  isPresentation,
5323
6142
  zoomIn: () => {
6143
+ isUserZoomed = true;
5324
6144
  scale += 0.15;
5325
6145
  applyTransform();
5326
6146
  },
5327
6147
  zoomOut: () => {
6148
+ isUserZoomed = true;
5328
6149
  scale = Math.max(0.2, scale - 0.15);
5329
6150
  applyTransform();
5330
6151
  },
5331
6152
  getZoom: () => scale,
5332
6153
  setZoom: (level) => {
6154
+ isUserZoomed = true;
5333
6155
  scale = level;
5334
6156
  applyTransform();
5335
6157
  },
5336
6158
  fitToPage: () => {
5337
- fitMode = fitMode === "width" ? "page" : "width";
5338
- scale = calculateFitScale(fitMode);
6159
+ isUserZoomed = false;
6160
+ fitMode = "width";
6161
+ scale = calculateFitScale("width");
5339
6162
  rotation = 0;
5340
6163
  applyTransform();
5341
6164
  },
@@ -5351,23 +6174,45 @@ var OpenDocumentPlugin = class {
5351
6174
  getPageCount: () => totalPages,
5352
6175
  getCurrentPage: () => currentPage,
5353
6176
  getThumbnails: async () => {
5354
- if (!isPresentation) return [];
5355
- return slides.map((_, idx) => ({
5356
- index: idx,
5357
- label: `Slide ${idx + 1}`,
5358
- render: async (canvas) => {
5359
- const ctx2d = canvas.getContext("2d");
5360
- if (!ctx2d) return;
5361
- canvas.width = 160;
5362
- canvas.height = 90;
5363
- ctx2d.fillStyle = "#f8fafc";
5364
- ctx2d.fillRect(0, 0, 160, 90);
5365
- ctx2d.fillStyle = "#334155";
5366
- ctx2d.font = "bold 12px sans-serif";
5367
- ctx2d.textAlign = "center";
5368
- ctx2d.fillText(`Slide ${idx + 1}`, 80, 50);
5369
- }
5370
- }));
6177
+ const count = totalPages || slides.length || 1;
6178
+ const items = [];
6179
+ for (let idx = 0; idx < count; idx++) {
6180
+ const itemIdx = idx;
6181
+ items.push({
6182
+ index: itemIdx,
6183
+ label: isPresentation ? `Slide ${itemIdx + 1}` : `Page ${itemIdx + 1}`,
6184
+ render: async (canvas) => {
6185
+ const ctx2d = canvas.getContext("2d");
6186
+ if (!ctx2d) return;
6187
+ if (isPresentation) {
6188
+ canvas.width = 160;
6189
+ canvas.height = 90;
6190
+ ctx2d.fillStyle = "#f8fafc";
6191
+ ctx2d.fillRect(0, 0, 160, 90);
6192
+ ctx2d.strokeStyle = "#cbd5e1";
6193
+ ctx2d.lineWidth = 1;
6194
+ ctx2d.strokeRect(0.5, 0.5, 159, 89);
6195
+ ctx2d.fillStyle = "#334155";
6196
+ ctx2d.font = "bold 11px sans-serif";
6197
+ ctx2d.textAlign = "center";
6198
+ ctx2d.fillText(`Slide ${itemIdx + 1}`, 80, 50);
6199
+ } else {
6200
+ canvas.width = 140;
6201
+ canvas.height = 180;
6202
+ ctx2d.fillStyle = "#ffffff";
6203
+ ctx2d.fillRect(0, 0, 140, 180);
6204
+ ctx2d.strokeStyle = "#cbd5e1";
6205
+ ctx2d.lineWidth = 1;
6206
+ ctx2d.strokeRect(0.5, 0.5, 139, 179);
6207
+ ctx2d.fillStyle = "#64748b";
6208
+ ctx2d.font = "bold 10px sans-serif";
6209
+ ctx2d.textAlign = "center";
6210
+ ctx2d.fillText(`Page ${itemIdx + 1}`, 70, 90);
6211
+ }
6212
+ }
6213
+ });
6214
+ }
6215
+ return items;
5371
6216
  },
5372
6217
  download: () => {
5373
6218
  const mimeType = this.mimeTypes.find((m) => m.includes(ext.replace(".", ""))) || "application/octet-stream";
@@ -5472,23 +6317,24 @@ var OpenDocumentPlugin = class {
5472
6317
  case "h": {
5473
6318
  const level = Math.min(6, Math.max(1, Number(node.getAttribute("text:outline-level") || 1)));
5474
6319
  const style = styles.get(node.getAttribute("text:style-name") || "") || "";
5475
- html += `<h${level} style="${style}; font-family: -apple-system, BlinkMacSystemFont, sans-serif;">${node.textContent || ""}</h${level}>`;
6320
+ html += `<h${level} style="${style}; margin: 18px 0 8px; color: #1e3a8a; font-family: Calibri, 'Segoe UI', Arial, sans-serif;">${node.textContent || ""}</h${level}>`;
5476
6321
  break;
5477
6322
  }
5478
6323
  case "p": {
5479
6324
  const style = styles.get(node.getAttribute("text:style-name") || "") || "";
5480
6325
  const inner = this.renderSpans(node, styles, images);
5481
- html += `<p style="${style}; line-height: 1.6; margin: 8px 0; font-family: -apple-system, BlinkMacSystemFont, sans-serif;">${inner}</p>`;
6326
+ const isInsideLi = node.parentElement?.localName === "list-item" || node.parentElement?.tagName?.toLowerCase() === "list-item";
6327
+ html += `<p style="${style}; line-height: 1.5; margin: ${isInsideLi ? "0" : "8px 0 12px"}; font-family: Calibri, 'Segoe UI', Arial, sans-serif;">${inner}</p>`;
5482
6328
  break;
5483
6329
  }
5484
6330
  case "list": {
5485
- html += '<ul style="margin: 8px 0; padding-left: 24px;">';
6331
+ html += '<ul style="margin: 8px 0 12px; padding-left: 24px;">';
5486
6332
  Array.from(node.children).forEach((c) => walk(c));
5487
6333
  html += "</ul>";
5488
6334
  break;
5489
6335
  }
5490
6336
  case "list-item": {
5491
- html += "<li>";
6337
+ html += '<li style="margin: 4px 0; font-size: 11pt; line-height: 1.5; color: #1e293b;">';
5492
6338
  Array.from(node.children).forEach((c) => walk(c));
5493
6339
  html += "</li>";
5494
6340
  break;
@@ -5614,28 +6460,26 @@ var DocPlugin = class {
5614
6460
  getToolbarActions(instance) {
5615
6461
  const totalPages = instance.getPageCount?.() ?? 1;
5616
6462
  const actions = [];
5617
- if (totalPages > 1) {
5618
- actions.push({
5619
- id: "page-nav",
5620
- icon: "",
5621
- label: "Page Navigation",
5622
- type: "page-nav",
5623
- group: "navigation",
5624
- value: instance.getCurrentPage?.() ?? 1,
5625
- max: totalPages,
5626
- execute: (action, page) => {
5627
- const cur = instance.getCurrentPage?.() ?? 1;
5628
- const max = instance.getPageCount?.() ?? 1;
5629
- if (action === "prev") {
5630
- if (cur > 1) instance.goToPage?.(cur - 1);
5631
- } else if (action === "next") {
5632
- if (cur < max) instance.goToPage?.(cur + 1);
5633
- } else if (typeof page === "number") {
5634
- instance.goToPage?.(page);
5635
- }
6463
+ actions.push({
6464
+ id: "page-nav",
6465
+ icon: "",
6466
+ label: "Page Navigation",
6467
+ type: "page-nav",
6468
+ group: "navigation",
6469
+ value: instance.getCurrentPage?.() ?? 1,
6470
+ max: totalPages,
6471
+ execute: (action, page) => {
6472
+ const cur = instance.getCurrentPage?.() ?? 1;
6473
+ const max = instance.getPageCount?.() ?? 1;
6474
+ if (action === "prev") {
6475
+ if (cur > 1) instance.goToPage?.(cur - 1);
6476
+ } else if (action === "next") {
6477
+ if (cur < max) instance.goToPage?.(cur + 1);
6478
+ } else if (typeof page === "number") {
6479
+ instance.goToPage?.(page);
5636
6480
  }
5637
- });
5638
- }
6481
+ }
6482
+ });
5639
6483
  actions.push(
5640
6484
  {
5641
6485
  id: "zoom-out",
@@ -5709,8 +6553,9 @@ var DocPlugin = class {
5709
6553
  container.className = "fp-doc-container";
5710
6554
  container.style.width = "100%";
5711
6555
  container.style.height = "100%";
5712
- container.style.overflow = "auto";
5713
- container.style.padding = "32px 16px";
6556
+ container.style.overflowX = "hidden";
6557
+ container.style.overflowY = "auto";
6558
+ container.style.padding = "16px 8px";
5714
6559
  container.style.backgroundColor = "#f1f5f9";
5715
6560
  container.style.display = "flex";
5716
6561
  container.style.justifyContent = "center";
@@ -5755,16 +6600,15 @@ var DocPlugin = class {
5755
6600
  const pageCard = document.createElement("div");
5756
6601
  pageCard.className = "fp-doc-page-card";
5757
6602
  pageCard.style.width = "816px";
5758
- pageCard.style.height = "1056px";
6603
+ pageCard.style.minHeight = "1056px";
5759
6604
  pageCard.style.backgroundColor = "#ffffff";
5760
- pageCard.style.boxShadow = "0 2px 12px rgba(0,0,0,0.08)";
6605
+ pageCard.style.boxShadow = "0 4px 24px rgba(0,0,0,0.08)";
5761
6606
  pageCard.style.borderRadius = "4px";
5762
- pageCard.style.padding = "96px 72px";
6607
+ pageCard.style.padding = "72px 56px";
5763
6608
  pageCard.style.boxSizing = "border-box";
5764
- pageCard.style.overflow = "hidden";
5765
6609
  pageCard.style.display = i === 0 ? "block" : "none";
5766
6610
  pageCard.style.transformOrigin = "top center";
5767
- pageCard.style.transition = "transform 0.2s ease";
6611
+ pageCard.style.transition = "transform 0.15s ease";
5768
6612
  pageCard.style.fontFamily = 'Calibri, "Segoe UI", Arial, sans-serif';
5769
6613
  pageCard.style.color = "#1e293b";
5770
6614
  if (isFallback && i === 0) {
@@ -5781,34 +6625,15 @@ var DocPlugin = class {
5781
6625
  container.appendChild(pageCard);
5782
6626
  pageCards.push(pageCard);
5783
6627
  }
5784
- let indicator = null;
5785
- if (totalPages > 1) {
5786
- indicator = document.createElement("div");
5787
- indicator.className = "fp-doc-page-indicator";
5788
- indicator.style.position = "fixed";
5789
- indicator.style.bottom = "16px";
5790
- indicator.style.left = "50%";
5791
- indicator.style.transform = "translateX(-50%)";
5792
- indicator.style.backgroundColor = "rgba(15, 23, 42, 0.85)";
5793
- indicator.style.backdropFilter = "blur(8px)";
5794
- indicator.style.color = "#f8fafc";
5795
- indicator.style.fontSize = "12px";
5796
- indicator.style.fontWeight = "600";
5797
- indicator.style.padding = "5px 14px";
5798
- indicator.style.borderRadius = "20px";
5799
- indicator.style.border = "1px solid rgba(255, 255, 255, 0.15)";
5800
- indicator.style.boxShadow = "0 4px 12px rgba(0, 0, 0, 0.3)";
5801
- indicator.style.zIndex = "10";
5802
- indicator.style.userSelect = "none";
5803
- indicator.style.pointerEvents = "none";
5804
- indicator.style.textAlign = "center";
5805
- container.appendChild(indicator);
5806
- }
5807
6628
  let rotation = 0;
5808
6629
  const applyTransform = () => {
5809
6630
  const activeCard = pageCards[currentPage - 1];
5810
6631
  if (activeCard) {
5811
6632
  activeCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
6633
+ const baseH = activeCard.offsetHeight || 1056;
6634
+ const scaledH = baseH * scale;
6635
+ const extraH = Math.max(0, scaledH - baseH);
6636
+ activeCard.style.marginBottom = `${extraH + 32}px`;
5812
6637
  }
5813
6638
  };
5814
6639
  const showPage = (pageNum) => {
@@ -5821,35 +6646,39 @@ var DocPlugin = class {
5821
6646
  card.style.display = "none";
5822
6647
  }
5823
6648
  });
5824
- if (indicator) {
5825
- indicator.textContent = `Page ${currentPage} of ${totalPages}`;
5826
- }
5827
6649
  container.scrollTop = 0;
5828
6650
  ctx.emit("page-change", { page: currentPage, total: totalPages });
5829
6651
  };
5830
6652
  if (totalPages > 1) {
5831
6653
  showPage(1);
5832
6654
  }
5833
- let fitMode = "width";
6655
+ let fitMode = ctx?.options?.fitMode || "width";
6656
+ let isUserZoomed = false;
5834
6657
  const calculateFitScale = (mode = fitMode) => {
5835
6658
  const elW = 816;
5836
6659
  const elH = 1056;
5837
- const availW = Math.max(280, ctx.container.clientWidth - 48);
5838
- const availH = Math.max(280, ctx.container.clientHeight - 80);
6660
+ const availW = Math.max(280, ctx.container.clientWidth - 32);
6661
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
5839
6662
  const sW = availW / elW;
5840
6663
  const sH = availH / elH;
5841
6664
  if (mode === "page") {
5842
- return Math.max(0.55, Math.min(1.15, Math.min(sW, sH)));
6665
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
5843
6666
  }
5844
- return Math.max(0.65, Math.min(1.05, sW));
6667
+ return Math.max(0.4, Math.min(3, sW));
5845
6668
  };
6669
+ const resizeObserver = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
6670
+ if (!isUserZoomed) {
6671
+ scale = calculateFitScale(fitMode);
6672
+ applyTransform();
6673
+ }
6674
+ }) : null;
6675
+ resizeObserver?.observe(ctx.container);
5846
6676
  setTimeout(() => {
5847
- fitMode = "width";
5848
- scale = calculateFitScale("width");
6677
+ scale = calculateFitScale(fitMode);
5849
6678
  applyTransform();
5850
6679
  }, 60);
5851
6680
  const cleanup = () => {
5852
- indicator?.remove();
6681
+ resizeObserver?.disconnect();
5853
6682
  container.remove();
5854
6683
  ctx.container.innerHTML = "";
5855
6684
  };
@@ -5860,21 +6689,25 @@ var DocPlugin = class {
5860
6689
  getCurrentPage: () => currentPage,
5861
6690
  goToPage: (page) => showPage(page),
5862
6691
  zoomIn: () => {
6692
+ isUserZoomed = true;
5863
6693
  scale += 0.15;
5864
6694
  applyTransform();
5865
6695
  },
5866
6696
  zoomOut: () => {
6697
+ isUserZoomed = true;
5867
6698
  scale = Math.max(0.2, scale - 0.15);
5868
6699
  applyTransform();
5869
6700
  },
5870
6701
  getZoom: () => scale,
5871
6702
  setZoom: (level) => {
6703
+ isUserZoomed = true;
5872
6704
  scale = level;
5873
6705
  applyTransform();
5874
6706
  },
5875
6707
  fitToPage: () => {
5876
- fitMode = fitMode === "width" ? "page" : "width";
5877
- scale = calculateFitScale(fitMode);
6708
+ isUserZoomed = false;
6709
+ fitMode = "width";
6710
+ scale = calculateFitScale("width");
5878
6711
  rotation = 0;
5879
6712
  applyTransform();
5880
6713
  },
@@ -6251,25 +7084,31 @@ ${chartSvg}
6251
7084
  i++;
6252
7085
  continue;
6253
7086
  }
6254
- if (line.length < 60 && !line.endsWith(".") && (/^[A-Z0-9\s:_-]+$/.test(line) || line.startsWith("#"))) {
7087
+ if (/^\d{1,2}$/.test(line.trim())) {
7088
+ i++;
7089
+ continue;
7090
+ }
7091
+ const isShortLine = line.length < 60 && !line.endsWith(".") && !line.endsWith(",");
7092
+ 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));
7093
+ if (isTitleLike) {
6255
7094
  if (inList) {
6256
7095
  html += "</ul>";
6257
7096
  inList = false;
6258
7097
  }
6259
- html += `<h2 style="font-size: 16px; font-weight: 700; color: #1e3a8a; margin: 16px 0 8px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify6.sanitize(line, sanitizeOptions)}</h2>`;
7098
+ html += `<h2 style="font-size: 18px; font-weight: 700; color: #1e3a8a; margin: 18px 0 10px; border-bottom: 1px solid #e2e8f0; padding-bottom: 4px;">${DOMPurify6.sanitize(line, sanitizeOptions)}</h2>`;
6260
7099
  } else if (line.startsWith("\u2022") || line.startsWith("-") || line.startsWith("*")) {
6261
7100
  if (!inList) {
6262
- html += '<ul style="margin: 8px 0; padding-left: 24px;">';
7101
+ html += '<ul style="margin: 8px 0 12px; padding-left: 24px;">';
6263
7102
  inList = true;
6264
7103
  }
6265
7104
  const bulletText = line.replace(/^[•\-\*]\s*/, "");
6266
- html += `<li style="margin: 4px 0; line-height: 1.15; font-size: 12pt;">${DOMPurify6.sanitize(bulletText, sanitizeOptions)}</li>`;
7105
+ html += `<li style="margin: 4px 0; line-height: 1.5; font-size: 11pt; color: #1e293b;">${DOMPurify6.sanitize(bulletText, sanitizeOptions)}</li>`;
6267
7106
  } else {
6268
7107
  if (inList) {
6269
7108
  html += "</ul>";
6270
7109
  inList = false;
6271
7110
  }
6272
- html += `<p style="line-height: 1.15; margin: 0; font-size: 12pt; text-align: justify;">${DOMPurify6.sanitize(line, sanitizeOptions)}</p>`;
7111
+ html += `<p style="line-height: 1.5; margin: 0 0 12px 0; font-size: 11pt; color: #1e293b; text-align: justify;">${DOMPurify6.sanitize(line, sanitizeOptions)}</p>`;
6273
7112
  }
6274
7113
  i++;
6275
7114
  }