@files-preview-app/preview-file 1.3.7 → 1.3.8

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
@@ -1104,6 +1104,10 @@ var FilePreviewViewer = class _FilePreviewViewer {
1104
1104
  * Preview a file in the given container element.
1105
1105
  */
1106
1106
  async preview(container, source, options = {}) {
1107
+ options = {
1108
+ ...options,
1109
+ fitMode: options.fitMode ?? "width"
1110
+ };
1107
1111
  this.currentOptions = options;
1108
1112
  this.abort();
1109
1113
  this.abortController = new AbortController();
@@ -1152,8 +1156,8 @@ var FilePreviewViewer = class _FilePreviewViewer {
1152
1156
  this.activeInstance = instance;
1153
1157
  instance.openInSeparateWindow = () => this.openInSeparateWindow();
1154
1158
  instance.toggleThumbnails = () => this.toggleThumbnails();
1155
- if (!instance.resetZoom && instance.fitToPage) {
1156
- instance.resetZoom = () => instance.fitToPage?.();
1159
+ if (!instance.resetZoom) {
1160
+ instance.resetZoom = () => instance.fitToWidth ? instance.fitToWidth() : instance.fitToPage?.();
1157
1161
  }
1158
1162
  this.hideLoading();
1159
1163
  this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
@@ -1550,7 +1554,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1550
1554
  this.wrapperEl?.classList.toggle("fp-fullscreen-active");
1551
1555
  }
1552
1556
  setTimeout(() => {
1553
- this.activeInstance?.fitToPage?.();
1557
+ this.triggerAutoFit();
1554
1558
  }, 120);
1555
1559
  }
1556
1560
  /**
@@ -1677,18 +1681,29 @@ var FilePreviewViewer = class _FilePreviewViewer {
1677
1681
  this.thumbnailPanel = new ThumbnailPanel(thumbnailEl, (isOpen) => {
1678
1682
  this.toolbar?.setActionActive("thumbnails", isOpen);
1679
1683
  setTimeout(() => {
1680
- this.activeInstance?.fitToPage?.();
1684
+ this.triggerAutoFit();
1681
1685
  }, 260);
1682
1686
  });
1683
1687
  this.setupKeyboardShortcuts();
1684
1688
  this.setupDragAndDrop(container, options);
1685
1689
  this.setupResizeAndFullscreenListeners();
1686
1690
  }
1691
+ triggerAutoFit() {
1692
+ if (!this.activeInstance) return;
1693
+ const mode = this.currentOptions?.fitMode ?? "width";
1694
+ if (mode === "width" && this.activeInstance.fitToWidth) {
1695
+ this.activeInstance.fitToWidth();
1696
+ } else if (this.activeInstance.fitToPage) {
1697
+ this.activeInstance.fitToPage();
1698
+ } else if (this.activeInstance.resetZoom) {
1699
+ this.activeInstance.resetZoom();
1700
+ }
1701
+ }
1687
1702
  setupResizeAndFullscreenListeners() {
1688
1703
  if (this.fullscreenHandler) return;
1689
1704
  this.fullscreenHandler = () => {
1690
1705
  setTimeout(() => {
1691
- this.activeInstance?.fitToPage?.();
1706
+ this.triggerAutoFit();
1692
1707
  }, 100);
1693
1708
  };
1694
1709
  document.addEventListener("fullscreenchange", this.fullscreenHandler);
@@ -1696,7 +1711,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1696
1711
  if (typeof ResizeObserver !== "undefined" && this.contentEl) {
1697
1712
  this.resizeObserver = new ResizeObserver(() => {
1698
1713
  if (this.activeInstance && (!this.activeInstance.getZoom || Math.abs((this.activeInstance.getZoom?.() ?? 1) - 1) < 0.05)) {
1699
- this.activeInstance.fitToPage?.();
1714
+ this.triggerAutoFit();
1700
1715
  }
1701
1716
  });
1702
1717
  this.resizeObserver.observe(this.contentEl);
@@ -1724,7 +1739,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1724
1739
  } else if (e.key === "-" || e.key === "_") {
1725
1740
  this.activeInstance.zoomOut?.();
1726
1741
  } else if (e.key === "0") {
1727
- this.activeInstance.fitToPage?.();
1742
+ this.resetZoom();
1728
1743
  } else if (e.key === "r" || e.key === "R") {
1729
1744
  this.activeInstance.rotateCW?.();
1730
1745
  } else if (e.key === "f" || e.key === "F") {
@@ -2347,7 +2362,7 @@ var PdfPlugin = class {
2347
2362
  renderPage(currentPage);
2348
2363
  },
2349
2364
  resetZoom: () => {
2350
- fitMode = "page";
2365
+ fitMode = ctx?.options?.fitMode || "width";
2351
2366
  zoomScale = 1;
2352
2367
  rotation = 0;
2353
2368
  container.scrollTop = 0;
@@ -2531,6 +2546,16 @@ var MediaPlugin = class {
2531
2546
  instance.resetZoom?.();
2532
2547
  }
2533
2548
  },
2549
+ {
2550
+ id: "fit-width",
2551
+ icon: "fit-width",
2552
+ label: "Fit to Width",
2553
+ type: "button",
2554
+ group: "zoom",
2555
+ execute: () => {
2556
+ instance.fitToWidth?.();
2557
+ }
2558
+ },
2534
2559
  {
2535
2560
  id: "rotate-cw",
2536
2561
  icon: "rotate-cw",
@@ -2627,9 +2652,30 @@ var MediaPlugin = class {
2627
2652
  const isVideo = mimeType.startsWith("video/") || VIDEO_EXTS.includes(ctx.metadata.extension || "");
2628
2653
  const isAudio = mimeType.startsWith("audio/") || AUDIO_EXTS.includes(ctx.metadata.extension || "");
2629
2654
  let url = "";
2655
+ let naturalW = 800;
2656
+ let naturalH = 600;
2630
2657
  if (ctx.metadata.extension === ".svg" || mimeType === "image/svg+xml") {
2631
2658
  const decoder = new TextDecoder("utf-8");
2632
2659
  const svgText = decoder.decode(ctx.buffer);
2660
+ try {
2661
+ const vbMatch = svgText.match(/viewBox=["']\s*([0-9.-]+)\s+([0-9.-]+)\s+([0-9.-]+)\s+([0-9.-]+)\s*["']/i);
2662
+ if (vbMatch) {
2663
+ const vw = parseFloat(vbMatch[3]);
2664
+ const vh = parseFloat(vbMatch[4]);
2665
+ if (vw > 0 && vh > 0) {
2666
+ naturalW = vw;
2667
+ naturalH = vh;
2668
+ }
2669
+ } else {
2670
+ const wMatch = svgText.match(/width=["']([0-9.]+)(?:px)?["']/i);
2671
+ const hMatch = svgText.match(/height=["']([0-9.]+)(?:px)?["']/i);
2672
+ if (wMatch && hMatch) {
2673
+ naturalW = parseFloat(wMatch[1]) || naturalW;
2674
+ naturalH = parseFloat(hMatch[1]) || naturalH;
2675
+ }
2676
+ }
2677
+ } catch {
2678
+ }
2633
2679
  const blob = new Blob([svgText], { type: "image/svg+xml" });
2634
2680
  url = URL.createObjectURL(blob);
2635
2681
  } else {
@@ -2677,7 +2723,7 @@ var MediaPlugin = class {
2677
2723
  scrollWrapper.style.width = "max-content";
2678
2724
  scrollWrapper.style.height = "max-content";
2679
2725
  scrollWrapper.style.display = "flex";
2680
- scrollWrapper.style.justifyContent = "center";
2726
+ scrollWrapper.style.flexDirection = "column";
2681
2727
  scrollWrapper.style.alignItems = "center";
2682
2728
  scrollWrapper.style.padding = "16px";
2683
2729
  scrollWrapper.style.boxSizing = "border-box";
@@ -2688,40 +2734,54 @@ var MediaPlugin = class {
2688
2734
  sizer.style.display = "flex";
2689
2735
  sizer.style.justifyContent = "center";
2690
2736
  sizer.style.alignItems = "center";
2737
+ sizer.style.margin = "auto 0";
2691
2738
  sizer.appendChild(element);
2692
2739
  scrollWrapper.appendChild(sizer);
2693
2740
  ctx.container.style.overflow = "auto";
2694
2741
  ctx.container.style.padding = "0";
2695
2742
  ctx.container.innerHTML = "";
2696
2743
  ctx.container.appendChild(scrollWrapper);
2697
- let naturalW = 800;
2698
- let naturalH = 600;
2699
- let baseW = 800;
2700
- let baseH = 600;
2744
+ let baseW = naturalW;
2745
+ let baseH = naturalH;
2746
+ let fitMode = ctx?.options?.fitMode || "width";
2747
+ let isUserZoomed = false;
2701
2748
  const updateBaseDimensions = () => {
2702
2749
  if (!isImage) return;
2703
2750
  const img = element;
2704
- naturalW = img.naturalWidth || naturalW;
2705
- naturalH = img.naturalHeight || naturalH;
2706
- const availW = Math.max(100, ctx.container.clientWidth - 48);
2707
- const availH = Math.max(100, ctx.container.clientHeight - 48);
2708
- const fitRatio = Math.min(1, availW / naturalW, availH / naturalH);
2751
+ if (img.naturalWidth > 0 && img.naturalHeight > 0) {
2752
+ naturalW = img.naturalWidth;
2753
+ naturalH = img.naturalHeight;
2754
+ }
2755
+ const availW = Math.max(100, ctx.container.clientWidth - 32);
2756
+ const availH = Math.max(100, ctx.container.clientHeight - 32);
2757
+ let fitRatio;
2758
+ if (fitMode === "page") {
2759
+ fitRatio = Math.min(availW / naturalW, availH / naturalH);
2760
+ } else {
2761
+ fitRatio = availW / naturalW;
2762
+ }
2709
2763
  baseW = Math.max(1, Math.round(naturalW * fitRatio));
2710
2764
  baseH = Math.max(1, Math.round(naturalH * fitRatio));
2711
2765
  };
2712
2766
  const applyTransform = () => {
2713
2767
  if (isImage) {
2714
- updateBaseDimensions();
2768
+ if (!isUserZoomed) {
2769
+ updateBaseDimensions();
2770
+ }
2771
+ const availH = Math.max(100, ctx.container.clientHeight - 32);
2715
2772
  const isRotated90 = rotation % 180 !== 0;
2716
2773
  const boxW = Math.round((isRotated90 ? baseH : baseW) * currentZoom);
2717
2774
  const boxH = Math.round((isRotated90 ? baseW : baseH) * currentZoom);
2718
2775
  sizer.style.width = `${boxW}px`;
2719
2776
  sizer.style.height = `${boxH}px`;
2720
- element.style.width = `${baseW}px`;
2721
- element.style.height = `${baseH}px`;
2777
+ sizer.style.margin = boxH < availH ? "auto 0" : "0";
2778
+ const imgW = isRotated90 ? boxH : boxW;
2779
+ const imgH = isRotated90 ? boxW : boxH;
2780
+ element.style.width = `${imgW}px`;
2781
+ element.style.height = `${imgH}px`;
2722
2782
  element.style.maxWidth = "none";
2723
2783
  element.style.maxHeight = "none";
2724
- element.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
2784
+ element.style.transform = rotation ? `rotate(${rotation}deg)` : "none";
2725
2785
  element.style.transformOrigin = "center center";
2726
2786
  element.style.flexShrink = "0";
2727
2787
  } else {
@@ -2741,7 +2801,8 @@ var MediaPlugin = class {
2741
2801
  }
2742
2802
  }
2743
2803
  const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
2744
- if (currentZoom === 1) {
2804
+ if (!isUserZoomed && currentZoom === 1) {
2805
+ updateBaseDimensions();
2745
2806
  applyTransform();
2746
2807
  }
2747
2808
  }) : null;
@@ -2756,28 +2817,49 @@ var MediaPlugin = class {
2756
2817
  return {
2757
2818
  destroy: cleanup,
2758
2819
  zoomIn: isImage ? () => {
2759
- currentZoom += 0.1;
2820
+ isUserZoomed = true;
2821
+ currentZoom = Math.min(5, Math.round((currentZoom + 0.15) * 100) / 100);
2760
2822
  applyTransform();
2761
2823
  } : void 0,
2762
2824
  zoomOut: isImage ? () => {
2763
- currentZoom = Math.max(0.1, currentZoom - 0.1);
2825
+ isUserZoomed = true;
2826
+ currentZoom = Math.max(0.1, Math.round((currentZoom - 0.15) * 100) / 100);
2764
2827
  applyTransform();
2765
2828
  } : void 0,
2766
2829
  getZoom: isImage ? () => currentZoom : void 0,
2767
2830
  setZoom: isImage ? (level) => {
2831
+ isUserZoomed = true;
2768
2832
  currentZoom = level;
2769
2833
  applyTransform();
2770
2834
  } : void 0,
2771
2835
  fitToPage: isImage ? () => {
2836
+ isUserZoomed = false;
2837
+ fitMode = "page";
2772
2838
  currentZoom = 1;
2773
2839
  rotation = 0;
2840
+ ctx.container.scrollTop = 0;
2841
+ ctx.container.scrollLeft = 0;
2842
+ updateBaseDimensions();
2843
+ applyTransform();
2844
+ } : void 0,
2845
+ fitToWidth: isImage ? () => {
2846
+ isUserZoomed = false;
2847
+ fitMode = "width";
2848
+ currentZoom = 1;
2849
+ rotation = 0;
2850
+ ctx.container.scrollTop = 0;
2851
+ ctx.container.scrollLeft = 0;
2852
+ updateBaseDimensions();
2774
2853
  applyTransform();
2775
2854
  } : void 0,
2776
2855
  resetZoom: isImage ? () => {
2856
+ isUserZoomed = false;
2857
+ fitMode = ctx?.options?.fitMode || "width";
2777
2858
  currentZoom = 1;
2778
2859
  rotation = 0;
2779
2860
  ctx.container.scrollTop = 0;
2780
2861
  ctx.container.scrollLeft = 0;
2862
+ updateBaseDimensions();
2781
2863
  applyTransform();
2782
2864
  } : void 0,
2783
2865
  rotateCW: isImage || isVideo ? () => {
@@ -2904,6 +2986,14 @@ var DocxPlugin = class {
2904
2986
  group: "zoom",
2905
2987
  execute: () => instance.resetZoom?.()
2906
2988
  },
2989
+ {
2990
+ id: "fit-width",
2991
+ icon: "fit-width",
2992
+ label: "Fit to Width",
2993
+ type: "button",
2994
+ group: "zoom",
2995
+ execute: () => instance.fitToWidth?.()
2996
+ },
2907
2997
  {
2908
2998
  id: "rotate-cw",
2909
2999
  icon: "rotate-cw",
@@ -3163,13 +3253,19 @@ var DocxPlugin = class {
3163
3253
  }
3164
3254
  scale = 1;
3165
3255
  let rotation = 0;
3166
- let fitMode = "page";
3256
+ let fitMode = ctx?.options?.fitMode || "width";
3167
3257
  let isUserZoomed = false;
3168
- const calculateFitScale = (_mode = fitMode) => {
3258
+ const calculateFitScale = (mode = fitMode) => {
3169
3259
  const activeEl = pageElements[currentPage - 1] || wrapper.querySelector("section.docx") || wrapper;
3170
3260
  const elW = activeEl.offsetWidth || 816;
3261
+ const elH = activeEl.offsetHeight || 1056;
3171
3262
  const availW = Math.max(280, ctx.container.clientWidth - 32);
3263
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
3172
3264
  const sW = availW / elW;
3265
+ const sH = availH / elH;
3266
+ if (mode === "page") {
3267
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
3268
+ }
3173
3269
  return Math.max(0.35, Math.min(3, sW));
3174
3270
  };
3175
3271
  const applyTransform = () => {
@@ -3190,7 +3286,7 @@ var DocxPlugin = class {
3190
3286
  if (typeof ResizeObserver !== "undefined") {
3191
3287
  resizeObserver = new ResizeObserver(() => {
3192
3288
  if (!isUserZoomed) {
3193
- const newFit = calculateFitScale("page");
3289
+ const newFit = calculateFitScale(fitMode);
3194
3290
  if (Math.abs(scale - newFit) > 0.015) {
3195
3291
  scale = newFit;
3196
3292
  applyTransform();
@@ -3200,7 +3296,7 @@ var DocxPlugin = class {
3200
3296
  resizeObserver.observe(ctx.container);
3201
3297
  }
3202
3298
  setTimeout(() => {
3203
- scale = calculateFitScale("page");
3299
+ scale = calculateFitScale(fitMode);
3204
3300
  applyTransform();
3205
3301
  }, 40);
3206
3302
  const cleanup = () => {
@@ -3239,13 +3335,22 @@ var DocxPlugin = class {
3239
3335
  },
3240
3336
  fitToPage: () => {
3241
3337
  isUserZoomed = false;
3338
+ fitMode = "page";
3242
3339
  scale = calculateFitScale("page");
3243
3340
  rotation = 0;
3244
3341
  applyTransform();
3245
3342
  },
3343
+ fitToWidth: () => {
3344
+ isUserZoomed = false;
3345
+ fitMode = "width";
3346
+ scale = calculateFitScale("width");
3347
+ rotation = 0;
3348
+ applyTransform();
3349
+ },
3246
3350
  resetZoom: () => {
3247
3351
  isUserZoomed = false;
3248
- scale = calculateFitScale("page");
3352
+ fitMode = ctx?.options?.fitMode || "width";
3353
+ scale = calculateFitScale(fitMode);
3249
3354
  rotation = 0;
3250
3355
  ctx.container.scrollTop = 0;
3251
3356
  ctx.container.scrollLeft = 0;
@@ -3710,6 +3815,16 @@ var ExcelPlugin = class {
3710
3815
  instance.resetZoom?.();
3711
3816
  }
3712
3817
  },
3818
+ {
3819
+ id: "fit-width",
3820
+ icon: "fit-width",
3821
+ label: "Fit to Width",
3822
+ type: "button",
3823
+ group: "zoom",
3824
+ execute: () => {
3825
+ instance.fitToWidth?.();
3826
+ }
3827
+ },
3713
3828
  {
3714
3829
  id: "download",
3715
3830
  icon: "download",
@@ -3926,6 +4041,11 @@ var ExcelPlugin = class {
3926
4041
  scale = calculateFitScale();
3927
4042
  applyTransform();
3928
4043
  },
4044
+ fitToWidth: () => {
4045
+ isUserZoomed = false;
4046
+ scale = calculateFitScale();
4047
+ applyTransform();
4048
+ },
3929
4049
  resetZoom: () => {
3930
4050
  isUserZoomed = false;
3931
4051
  scale = calculateFitScale();
@@ -4073,6 +4193,16 @@ var CsvPlugin = class {
4073
4193
  instance.resetZoom?.();
4074
4194
  }
4075
4195
  },
4196
+ {
4197
+ id: "fit-width",
4198
+ icon: "fit-width",
4199
+ label: "Fit to Width",
4200
+ type: "button",
4201
+ group: "zoom",
4202
+ execute: () => {
4203
+ instance.fitToWidth?.();
4204
+ }
4205
+ },
4076
4206
  {
4077
4207
  id: "download",
4078
4208
  icon: "download",
@@ -4286,6 +4416,11 @@ var CsvPlugin = class {
4286
4416
  scale = calculateFitScale();
4287
4417
  applyScale();
4288
4418
  },
4419
+ fitToWidth: () => {
4420
+ isUserZoomed = false;
4421
+ scale = calculateFitScale();
4422
+ applyScale();
4423
+ },
4289
4424
  resetZoom: () => {
4290
4425
  isUserZoomed = false;
4291
4426
  scale = calculateFitScale();
@@ -4418,6 +4553,16 @@ var CodePlugin = class {
4418
4553
  instance.resetZoom?.();
4419
4554
  }
4420
4555
  },
4556
+ {
4557
+ id: "fit-width",
4558
+ icon: "fit-width",
4559
+ label: "Fit to Width",
4560
+ type: "button",
4561
+ group: "zoom",
4562
+ execute: () => {
4563
+ instance.fitToWidth?.();
4564
+ }
4565
+ },
4421
4566
  {
4422
4567
  id: "copy",
4423
4568
  icon: "copy",
@@ -4722,6 +4867,13 @@ var CodePlugin = class {
4722
4867
  zoomLevel = isTxt ? calculateTxtFit() : 1;
4723
4868
  updateTransform();
4724
4869
  },
4870
+ fitToWidth: () => {
4871
+ isUserZoomed = false;
4872
+ fontSize = 13;
4873
+ rotation = 0;
4874
+ zoomLevel = isTxt ? calculateTxtFit() : 1;
4875
+ updateTransform();
4876
+ },
4725
4877
  resetZoom: () => {
4726
4878
  isUserZoomed = false;
4727
4879
  fontSize = 13;
@@ -4806,6 +4958,14 @@ var ArchivePlugin = class {
4806
4958
  group: "zoom",
4807
4959
  execute: () => instance.resetZoom?.()
4808
4960
  },
4961
+ {
4962
+ id: "fit-width",
4963
+ icon: "fit-width",
4964
+ label: "Fit to Width",
4965
+ type: "button",
4966
+ group: "zoom",
4967
+ execute: () => instance.fitToWidth?.()
4968
+ },
4809
4969
  {
4810
4970
  id: "download",
4811
4971
  icon: "download",
@@ -5003,6 +5163,10 @@ var ArchivePlugin = class {
5003
5163
  scale = 1;
5004
5164
  applyTransform();
5005
5165
  },
5166
+ fitToWidth: () => {
5167
+ scale = 1;
5168
+ applyTransform();
5169
+ },
5006
5170
  resetZoom: () => {
5007
5171
  scale = 1;
5008
5172
  wrapper.scrollTop = 0;
@@ -5067,6 +5231,14 @@ var MarkdownPlugin = class {
5067
5231
  group: "zoom",
5068
5232
  execute: () => instance.resetZoom?.()
5069
5233
  },
5234
+ {
5235
+ id: "fit-width",
5236
+ icon: "fit-width",
5237
+ label: "Fit to Width",
5238
+ type: "button",
5239
+ group: "zoom",
5240
+ execute: () => instance.fitToWidth?.()
5241
+ },
5070
5242
  {
5071
5243
  id: "copy",
5072
5244
  icon: "copy",
@@ -5220,6 +5392,10 @@ var MarkdownPlugin = class {
5220
5392
  fontSize = 15;
5221
5393
  wrapper.style.fontSize = "15px";
5222
5394
  },
5395
+ fitToWidth: () => {
5396
+ fontSize = 15;
5397
+ wrapper.style.fontSize = "15px";
5398
+ },
5223
5399
  resetZoom: () => {
5224
5400
  fontSize = 15;
5225
5401
  wrapper.style.fontSize = "15px";
@@ -5327,6 +5503,14 @@ var PptxPlugin = class {
5327
5503
  group: "zoom",
5328
5504
  execute: () => instance.resetZoom?.()
5329
5505
  },
5506
+ {
5507
+ id: "fit-width",
5508
+ icon: "fit-width",
5509
+ label: "Fit to Width",
5510
+ type: "button",
5511
+ group: "zoom",
5512
+ execute: () => instance.fitToWidth?.()
5513
+ },
5330
5514
  {
5331
5515
  id: "rotate-cw",
5332
5516
  icon: "rotate-cw",
@@ -5410,12 +5594,17 @@ var PptxPlugin = class {
5410
5594
  ctx.container.innerHTML = "";
5411
5595
  ctx.container.style.overflow = "auto";
5412
5596
  ctx.container.appendChild(wrapper);
5413
- const calculateFitScale = () => {
5414
- const availW = Math.max(200, ctx.container.clientWidth - 48);
5415
- const availH = Math.max(200, ctx.container.clientHeight - 72);
5597
+ let fitMode = ctx?.options?.fitMode || "width";
5598
+ let isUserZoomed = false;
5599
+ const calculateFitScale = (mode = fitMode) => {
5600
+ const availW = Math.max(200, ctx.container.clientWidth - 32);
5601
+ const availH = Math.max(200, ctx.container.clientHeight - 32);
5416
5602
  const cW = canvas.offsetWidth || 1280;
5417
5603
  const cH = canvas.offsetHeight || 720;
5418
- return Math.min(1, Math.min(availW / cW, availH / cH));
5604
+ if (mode === "page") {
5605
+ return Math.min(2.5, Math.min(availW / cW, availH / cH));
5606
+ }
5607
+ return Math.min(2.5, availW / cW);
5419
5608
  };
5420
5609
  const applyTransform = () => {
5421
5610
  const cW = canvas.offsetWidth || 1280;
@@ -5434,14 +5623,24 @@ var PptxPlugin = class {
5434
5623
  try {
5435
5624
  await renderer.renderSlide(currentSlide - 1, canvas, 1280);
5436
5625
  ctx.emit("page-change", { page: currentSlide, totalPages: slideCount });
5437
- scale = calculateFitScale();
5626
+ if (!isUserZoomed) {
5627
+ scale = calculateFitScale(fitMode);
5628
+ }
5438
5629
  applyTransform();
5439
5630
  } catch (err) {
5440
5631
  console.error("[PptxPlugin] Failed to render slide:", err);
5441
5632
  }
5442
5633
  };
5443
5634
  await renderCurrentSlide();
5635
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
5636
+ if (!isUserZoomed) {
5637
+ scale = calculateFitScale(fitMode);
5638
+ applyTransform();
5639
+ }
5640
+ }) : null;
5641
+ ro?.observe(ctx.container);
5444
5642
  const cleanup = () => {
5643
+ ro?.disconnect();
5445
5644
  renderer.destroy();
5446
5645
  wrapper.remove();
5447
5646
  ctx.container.innerHTML = "";
@@ -5458,28 +5657,44 @@ var PptxPlugin = class {
5458
5657
  getPageCount: () => slideCount,
5459
5658
  getCurrentPage: () => currentSlide,
5460
5659
  zoomIn: () => {
5660
+ isUserZoomed = true;
5461
5661
  scale += 0.15;
5462
5662
  applyTransform();
5463
5663
  },
5464
5664
  zoomOut: () => {
5665
+ isUserZoomed = true;
5465
5666
  scale = Math.max(0.2, scale - 0.15);
5466
5667
  applyTransform();
5467
5668
  },
5468
5669
  getZoom: () => scale,
5469
5670
  setZoom: (level) => {
5671
+ isUserZoomed = true;
5470
5672
  scale = level;
5471
5673
  applyTransform();
5472
5674
  },
5473
5675
  fitToPage: () => {
5474
- scale = calculateFitScale();
5676
+ isUserZoomed = false;
5677
+ fitMode = "page";
5678
+ scale = calculateFitScale("page");
5679
+ rotation = 0;
5680
+ applyTransform();
5681
+ },
5682
+ fitToWidth: () => {
5683
+ isUserZoomed = false;
5684
+ fitMode = "width";
5685
+ scale = calculateFitScale("width");
5475
5686
  rotation = 0;
5476
5687
  applyTransform();
5477
5688
  },
5478
5689
  resetZoom: () => {
5479
- scale = calculateFitScale();
5690
+ isUserZoomed = false;
5691
+ fitMode = ctx?.options?.fitMode || "width";
5692
+ scale = calculateFitScale(fitMode);
5480
5693
  rotation = 0;
5481
5694
  wrapper.scrollTop = 0;
5482
5695
  wrapper.scrollLeft = 0;
5696
+ ctx.container.scrollTop = 0;
5697
+ ctx.container.scrollLeft = 0;
5483
5698
  applyTransform();
5484
5699
  },
5485
5700
  rotateCW: () => {
@@ -5924,6 +6139,14 @@ var RtfPlugin = class {
5924
6139
  group: "zoom",
5925
6140
  execute: () => instance.resetZoom?.()
5926
6141
  },
6142
+ {
6143
+ id: "fit-width",
6144
+ icon: "fit-width",
6145
+ label: "Fit to Width",
6146
+ type: "button",
6147
+ group: "zoom",
6148
+ execute: () => instance.fitToWidth?.()
6149
+ },
5927
6150
  {
5928
6151
  id: "rotate-cw",
5929
6152
  icon: "rotate-cw",
@@ -6412,6 +6635,13 @@ var RtfPlugin = class {
6412
6635
  applyTransform();
6413
6636
  },
6414
6637
  fitToPage: () => {
6638
+ isUserZoomed = false;
6639
+ fitMode = "page";
6640
+ scale = calculateFitScale("page");
6641
+ rotation = 0;
6642
+ applyTransform();
6643
+ },
6644
+ fitToWidth: () => {
6415
6645
  isUserZoomed = false;
6416
6646
  fitMode = "width";
6417
6647
  scale = calculateFitScale("width");
@@ -6420,8 +6650,8 @@ var RtfPlugin = class {
6420
6650
  },
6421
6651
  resetZoom: () => {
6422
6652
  isUserZoomed = false;
6423
- fitMode = "width";
6424
- scale = calculateFitScale("width");
6653
+ fitMode = ctx?.options?.fitMode || "width";
6654
+ scale = calculateFitScale(fitMode);
6425
6655
  rotation = 0;
6426
6656
  ctx.container.scrollTop = 0;
6427
6657
  ctx.container.scrollLeft = 0;
@@ -6502,6 +6732,14 @@ var HtmlPreviewPlugin = class {
6502
6732
  group: "zoom",
6503
6733
  execute: () => instance.resetZoom?.()
6504
6734
  },
6735
+ {
6736
+ id: "fit-width",
6737
+ icon: "fit-width",
6738
+ label: "Fit to Width",
6739
+ type: "button",
6740
+ group: "zoom",
6741
+ execute: () => instance.fitToWidth?.()
6742
+ },
6505
6743
  {
6506
6744
  id: "copy",
6507
6745
  icon: "copy",
@@ -6595,6 +6833,10 @@ var HtmlPreviewPlugin = class {
6595
6833
  scale = 1;
6596
6834
  applyTransform();
6597
6835
  },
6836
+ fitToWidth: () => {
6837
+ scale = 1;
6838
+ applyTransform();
6839
+ },
6598
6840
  resetZoom: () => {
6599
6841
  scale = 1;
6600
6842
  ctx.container.scrollTop = 0;
@@ -6709,6 +6951,14 @@ var OpenDocumentPlugin = class {
6709
6951
  group: "zoom",
6710
6952
  execute: () => instance.resetZoom?.()
6711
6953
  },
6954
+ {
6955
+ id: "fit-width",
6956
+ icon: "fit-width",
6957
+ label: "Fit to Width",
6958
+ type: "button",
6959
+ group: "zoom",
6960
+ execute: () => instance.fitToWidth?.()
6961
+ },
6712
6962
  {
6713
6963
  id: "rotate-cw",
6714
6964
  icon: "rotate-cw",
@@ -6825,7 +7075,10 @@ var OpenDocumentPlugin = class {
6825
7075
  const sW = availW / elW;
6826
7076
  const sH = availH / (isPresentation ? 540 : singlePageH);
6827
7077
  if (isPresentation) {
6828
- return Math.min(2.5, Math.min(sW, sH));
7078
+ if (mode === "page") {
7079
+ return Math.min(2.5, Math.min(sW, sH));
7080
+ }
7081
+ return Math.min(2.5, sW);
6829
7082
  }
6830
7083
  if (mode === "page") {
6831
7084
  return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
@@ -6994,6 +7247,13 @@ var OpenDocumentPlugin = class {
6994
7247
  applyTransform();
6995
7248
  },
6996
7249
  fitToPage: () => {
7250
+ isUserZoomed = false;
7251
+ fitMode = "page";
7252
+ scale = calculateFitScale("page");
7253
+ rotation = 0;
7254
+ applyTransform();
7255
+ },
7256
+ fitToWidth: () => {
6997
7257
  isUserZoomed = false;
6998
7258
  fitMode = "width";
6999
7259
  scale = calculateFitScale("width");
@@ -7002,8 +7262,8 @@ var OpenDocumentPlugin = class {
7002
7262
  },
7003
7263
  resetZoom: () => {
7004
7264
  isUserZoomed = false;
7005
- fitMode = "width";
7006
- scale = calculateFitScale("width");
7265
+ fitMode = ctx?.options?.fitMode || "width";
7266
+ scale = calculateFitScale(fitMode);
7007
7267
  rotation = 0;
7008
7268
  container.scrollTop = 0;
7009
7269
  container.scrollLeft = 0;
@@ -7360,6 +7620,14 @@ var DocPlugin = class {
7360
7620
  group: "zoom",
7361
7621
  execute: () => instance.resetZoom?.()
7362
7622
  },
7623
+ {
7624
+ id: "fit-width",
7625
+ icon: "fit-width",
7626
+ label: "Fit to Width",
7627
+ type: "button",
7628
+ group: "zoom",
7629
+ execute: () => instance.fitToWidth?.()
7630
+ },
7363
7631
  {
7364
7632
  id: "rotate-cw",
7365
7633
  icon: "rotate-cw",
@@ -7581,6 +7849,13 @@ var DocPlugin = class {
7581
7849
  applyTransform();
7582
7850
  },
7583
7851
  fitToPage: () => {
7852
+ isUserZoomed = false;
7853
+ fitMode = "page";
7854
+ scale = calculateFitScale("page");
7855
+ rotation = 0;
7856
+ applyTransform();
7857
+ },
7858
+ fitToWidth: () => {
7584
7859
  isUserZoomed = false;
7585
7860
  fitMode = "width";
7586
7861
  scale = calculateFitScale("width");
@@ -7589,8 +7864,8 @@ var DocPlugin = class {
7589
7864
  },
7590
7865
  resetZoom: () => {
7591
7866
  isUserZoomed = false;
7592
- fitMode = "width";
7593
- scale = calculateFitScale("width");
7867
+ fitMode = ctx?.options?.fitMode || "width";
7868
+ scale = calculateFitScale(fitMode);
7594
7869
  rotation = 0;
7595
7870
  container.scrollTop = 0;
7596
7871
  container.scrollLeft = 0;
@@ -8086,6 +8361,14 @@ var PptPlugin = class {
8086
8361
  group: "zoom",
8087
8362
  execute: () => instance.resetZoom?.()
8088
8363
  },
8364
+ {
8365
+ id: "fit-width",
8366
+ icon: "fit-width",
8367
+ label: "Fit to Width",
8368
+ type: "button",
8369
+ group: "zoom",
8370
+ execute: () => instance.fitToWidth?.()
8371
+ },
8089
8372
  {
8090
8373
  id: "rotate-cw",
8091
8374
  icon: "rotate-cw",
@@ -8160,10 +8443,15 @@ var PptPlugin = class {
8160
8443
  let currentSlide = 1;
8161
8444
  let slides = [];
8162
8445
  const createdBlobUrls = [];
8163
- const calculateFitScale = () => {
8164
- const availW = Math.max(200, container.clientWidth - 48);
8165
- const availH = Math.max(200, container.clientHeight - 72);
8166
- return Math.min(1, Math.min(availW / 960, availH / 540));
8446
+ let fitMode = ctx?.options?.fitMode || "width";
8447
+ let isUserZoomed = false;
8448
+ const calculateFitScale = (mode = fitMode) => {
8449
+ const availW = Math.max(200, container.clientWidth - 32);
8450
+ const availH = Math.max(200, container.clientHeight - 32);
8451
+ if (mode === "page") {
8452
+ return Math.min(2.5, Math.min(availW / 960, availH / 540));
8453
+ }
8454
+ return Math.min(2.5, availW / 960);
8167
8455
  };
8168
8456
  const applyTransform = () => {
8169
8457
  const cW = 960;
@@ -8178,8 +8466,15 @@ var PptPlugin = class {
8178
8466
  slideCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
8179
8467
  slideCard.style.transformOrigin = "center center";
8180
8468
  };
8469
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
8470
+ if (!isUserZoomed) {
8471
+ scale = calculateFitScale(fitMode);
8472
+ applyTransform();
8473
+ }
8474
+ }) : null;
8475
+ ro?.observe(container);
8181
8476
  setTimeout(() => {
8182
- scale = calculateFitScale();
8477
+ scale = calculateFitScale(fitMode);
8183
8478
  applyTransform();
8184
8479
  }, 60);
8185
8480
  try {
@@ -8272,6 +8567,7 @@ var PptPlugin = class {
8272
8567
  };
8273
8568
  renderSlide(1);
8274
8569
  const cleanup = () => {
8570
+ ro?.disconnect();
8275
8571
  for (const u of createdBlobUrls) {
8276
8572
  URL.revokeObjectURL(u);
8277
8573
  }
@@ -8282,28 +8578,44 @@ var PptPlugin = class {
8282
8578
  return {
8283
8579
  destroy: cleanup,
8284
8580
  zoomIn: () => {
8581
+ isUserZoomed = true;
8285
8582
  scale += 0.15;
8286
8583
  applyTransform();
8287
8584
  },
8288
8585
  zoomOut: () => {
8586
+ isUserZoomed = true;
8289
8587
  scale = Math.max(0.2, scale - 0.15);
8290
8588
  applyTransform();
8291
8589
  },
8292
8590
  getZoom: () => scale,
8293
8591
  setZoom: (level) => {
8592
+ isUserZoomed = true;
8294
8593
  scale = level;
8295
8594
  applyTransform();
8296
8595
  },
8297
8596
  fitToPage: () => {
8298
- scale = calculateFitScale();
8597
+ isUserZoomed = false;
8598
+ fitMode = "page";
8599
+ scale = calculateFitScale("page");
8600
+ rotation = 0;
8601
+ applyTransform();
8602
+ },
8603
+ fitToWidth: () => {
8604
+ isUserZoomed = false;
8605
+ fitMode = "width";
8606
+ scale = calculateFitScale("width");
8299
8607
  rotation = 0;
8300
8608
  applyTransform();
8301
8609
  },
8302
8610
  resetZoom: () => {
8303
- scale = calculateFitScale();
8611
+ isUserZoomed = false;
8612
+ fitMode = ctx?.options?.fitMode || "width";
8613
+ scale = calculateFitScale(fitMode);
8304
8614
  rotation = 0;
8305
8615
  container.scrollTop = 0;
8306
8616
  container.scrollLeft = 0;
8617
+ ctx.container.scrollTop = 0;
8618
+ ctx.container.scrollLeft = 0;
8307
8619
  applyTransform();
8308
8620
  },
8309
8621
  rotateCW: () => {