@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/index.cjs CHANGED
@@ -1171,6 +1171,10 @@ var FilePreviewViewer = class _FilePreviewViewer {
1171
1171
  * Preview a file in the given container element.
1172
1172
  */
1173
1173
  async preview(container, source, options = {}) {
1174
+ options = {
1175
+ ...options,
1176
+ fitMode: options.fitMode ?? "width"
1177
+ };
1174
1178
  this.currentOptions = options;
1175
1179
  this.abort();
1176
1180
  this.abortController = new AbortController();
@@ -1219,8 +1223,8 @@ var FilePreviewViewer = class _FilePreviewViewer {
1219
1223
  this.activeInstance = instance;
1220
1224
  instance.openInSeparateWindow = () => this.openInSeparateWindow();
1221
1225
  instance.toggleThumbnails = () => this.toggleThumbnails();
1222
- if (!instance.resetZoom && instance.fitToPage) {
1223
- instance.resetZoom = () => instance.fitToPage?.();
1226
+ if (!instance.resetZoom) {
1227
+ instance.resetZoom = () => instance.fitToWidth ? instance.fitToWidth() : instance.fitToPage?.();
1224
1228
  }
1225
1229
  this.hideLoading();
1226
1230
  this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
@@ -1617,7 +1621,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1617
1621
  this.wrapperEl?.classList.toggle("fp-fullscreen-active");
1618
1622
  }
1619
1623
  setTimeout(() => {
1620
- this.activeInstance?.fitToPage?.();
1624
+ this.triggerAutoFit();
1621
1625
  }, 120);
1622
1626
  }
1623
1627
  /**
@@ -1744,18 +1748,29 @@ var FilePreviewViewer = class _FilePreviewViewer {
1744
1748
  this.thumbnailPanel = new ThumbnailPanel(thumbnailEl, (isOpen) => {
1745
1749
  this.toolbar?.setActionActive("thumbnails", isOpen);
1746
1750
  setTimeout(() => {
1747
- this.activeInstance?.fitToPage?.();
1751
+ this.triggerAutoFit();
1748
1752
  }, 260);
1749
1753
  });
1750
1754
  this.setupKeyboardShortcuts();
1751
1755
  this.setupDragAndDrop(container, options);
1752
1756
  this.setupResizeAndFullscreenListeners();
1753
1757
  }
1758
+ triggerAutoFit() {
1759
+ if (!this.activeInstance) return;
1760
+ const mode = this.currentOptions?.fitMode ?? "width";
1761
+ if (mode === "width" && this.activeInstance.fitToWidth) {
1762
+ this.activeInstance.fitToWidth();
1763
+ } else if (this.activeInstance.fitToPage) {
1764
+ this.activeInstance.fitToPage();
1765
+ } else if (this.activeInstance.resetZoom) {
1766
+ this.activeInstance.resetZoom();
1767
+ }
1768
+ }
1754
1769
  setupResizeAndFullscreenListeners() {
1755
1770
  if (this.fullscreenHandler) return;
1756
1771
  this.fullscreenHandler = () => {
1757
1772
  setTimeout(() => {
1758
- this.activeInstance?.fitToPage?.();
1773
+ this.triggerAutoFit();
1759
1774
  }, 100);
1760
1775
  };
1761
1776
  document.addEventListener("fullscreenchange", this.fullscreenHandler);
@@ -1763,7 +1778,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1763
1778
  if (typeof ResizeObserver !== "undefined" && this.contentEl) {
1764
1779
  this.resizeObserver = new ResizeObserver(() => {
1765
1780
  if (this.activeInstance && (!this.activeInstance.getZoom || Math.abs((this.activeInstance.getZoom?.() ?? 1) - 1) < 0.05)) {
1766
- this.activeInstance.fitToPage?.();
1781
+ this.triggerAutoFit();
1767
1782
  }
1768
1783
  });
1769
1784
  this.resizeObserver.observe(this.contentEl);
@@ -1791,7 +1806,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1791
1806
  } else if (e.key === "-" || e.key === "_") {
1792
1807
  this.activeInstance.zoomOut?.();
1793
1808
  } else if (e.key === "0") {
1794
- this.activeInstance.fitToPage?.();
1809
+ this.resetZoom();
1795
1810
  } else if (e.key === "r" || e.key === "R") {
1796
1811
  this.activeInstance.rotateCW?.();
1797
1812
  } else if (e.key === "f" || e.key === "F") {
@@ -2414,7 +2429,7 @@ var PdfPlugin = class {
2414
2429
  renderPage(currentPage);
2415
2430
  },
2416
2431
  resetZoom: () => {
2417
- fitMode = "page";
2432
+ fitMode = ctx?.options?.fitMode || "width";
2418
2433
  zoomScale = 1;
2419
2434
  rotation = 0;
2420
2435
  container.scrollTop = 0;
@@ -2598,6 +2613,16 @@ var MediaPlugin = class {
2598
2613
  instance.resetZoom?.();
2599
2614
  }
2600
2615
  },
2616
+ {
2617
+ id: "fit-width",
2618
+ icon: "fit-width",
2619
+ label: "Fit to Width",
2620
+ type: "button",
2621
+ group: "zoom",
2622
+ execute: () => {
2623
+ instance.fitToWidth?.();
2624
+ }
2625
+ },
2601
2626
  {
2602
2627
  id: "rotate-cw",
2603
2628
  icon: "rotate-cw",
@@ -2694,9 +2719,30 @@ var MediaPlugin = class {
2694
2719
  const isVideo = mimeType.startsWith("video/") || VIDEO_EXTS.includes(ctx.metadata.extension || "");
2695
2720
  const isAudio = mimeType.startsWith("audio/") || AUDIO_EXTS.includes(ctx.metadata.extension || "");
2696
2721
  let url = "";
2722
+ let naturalW = 800;
2723
+ let naturalH = 600;
2697
2724
  if (ctx.metadata.extension === ".svg" || mimeType === "image/svg+xml") {
2698
2725
  const decoder = new TextDecoder("utf-8");
2699
2726
  const svgText = decoder.decode(ctx.buffer);
2727
+ try {
2728
+ const vbMatch = svgText.match(/viewBox=["']\s*([0-9.-]+)\s+([0-9.-]+)\s+([0-9.-]+)\s+([0-9.-]+)\s*["']/i);
2729
+ if (vbMatch) {
2730
+ const vw = parseFloat(vbMatch[3]);
2731
+ const vh = parseFloat(vbMatch[4]);
2732
+ if (vw > 0 && vh > 0) {
2733
+ naturalW = vw;
2734
+ naturalH = vh;
2735
+ }
2736
+ } else {
2737
+ const wMatch = svgText.match(/width=["']([0-9.]+)(?:px)?["']/i);
2738
+ const hMatch = svgText.match(/height=["']([0-9.]+)(?:px)?["']/i);
2739
+ if (wMatch && hMatch) {
2740
+ naturalW = parseFloat(wMatch[1]) || naturalW;
2741
+ naturalH = parseFloat(hMatch[1]) || naturalH;
2742
+ }
2743
+ }
2744
+ } catch {
2745
+ }
2700
2746
  const blob = new Blob([svgText], { type: "image/svg+xml" });
2701
2747
  url = URL.createObjectURL(blob);
2702
2748
  } else {
@@ -2744,7 +2790,7 @@ var MediaPlugin = class {
2744
2790
  scrollWrapper.style.width = "max-content";
2745
2791
  scrollWrapper.style.height = "max-content";
2746
2792
  scrollWrapper.style.display = "flex";
2747
- scrollWrapper.style.justifyContent = "center";
2793
+ scrollWrapper.style.flexDirection = "column";
2748
2794
  scrollWrapper.style.alignItems = "center";
2749
2795
  scrollWrapper.style.padding = "16px";
2750
2796
  scrollWrapper.style.boxSizing = "border-box";
@@ -2755,40 +2801,54 @@ var MediaPlugin = class {
2755
2801
  sizer.style.display = "flex";
2756
2802
  sizer.style.justifyContent = "center";
2757
2803
  sizer.style.alignItems = "center";
2804
+ sizer.style.margin = "auto 0";
2758
2805
  sizer.appendChild(element);
2759
2806
  scrollWrapper.appendChild(sizer);
2760
2807
  ctx.container.style.overflow = "auto";
2761
2808
  ctx.container.style.padding = "0";
2762
2809
  ctx.container.innerHTML = "";
2763
2810
  ctx.container.appendChild(scrollWrapper);
2764
- let naturalW = 800;
2765
- let naturalH = 600;
2766
- let baseW = 800;
2767
- let baseH = 600;
2811
+ let baseW = naturalW;
2812
+ let baseH = naturalH;
2813
+ let fitMode = ctx?.options?.fitMode || "width";
2814
+ let isUserZoomed = false;
2768
2815
  const updateBaseDimensions = () => {
2769
2816
  if (!isImage) return;
2770
2817
  const img = element;
2771
- naturalW = img.naturalWidth || naturalW;
2772
- naturalH = img.naturalHeight || naturalH;
2773
- const availW = Math.max(100, ctx.container.clientWidth - 48);
2774
- const availH = Math.max(100, ctx.container.clientHeight - 48);
2775
- const fitRatio = Math.min(1, availW / naturalW, availH / naturalH);
2818
+ if (img.naturalWidth > 0 && img.naturalHeight > 0) {
2819
+ naturalW = img.naturalWidth;
2820
+ naturalH = img.naturalHeight;
2821
+ }
2822
+ const availW = Math.max(100, ctx.container.clientWidth - 32);
2823
+ const availH = Math.max(100, ctx.container.clientHeight - 32);
2824
+ let fitRatio;
2825
+ if (fitMode === "page") {
2826
+ fitRatio = Math.min(availW / naturalW, availH / naturalH);
2827
+ } else {
2828
+ fitRatio = availW / naturalW;
2829
+ }
2776
2830
  baseW = Math.max(1, Math.round(naturalW * fitRatio));
2777
2831
  baseH = Math.max(1, Math.round(naturalH * fitRatio));
2778
2832
  };
2779
2833
  const applyTransform = () => {
2780
2834
  if (isImage) {
2781
- updateBaseDimensions();
2835
+ if (!isUserZoomed) {
2836
+ updateBaseDimensions();
2837
+ }
2838
+ const availH = Math.max(100, ctx.container.clientHeight - 32);
2782
2839
  const isRotated90 = rotation % 180 !== 0;
2783
2840
  const boxW = Math.round((isRotated90 ? baseH : baseW) * currentZoom);
2784
2841
  const boxH = Math.round((isRotated90 ? baseW : baseH) * currentZoom);
2785
2842
  sizer.style.width = `${boxW}px`;
2786
2843
  sizer.style.height = `${boxH}px`;
2787
- element.style.width = `${baseW}px`;
2788
- element.style.height = `${baseH}px`;
2844
+ sizer.style.margin = boxH < availH ? "auto 0" : "0";
2845
+ const imgW = isRotated90 ? boxH : boxW;
2846
+ const imgH = isRotated90 ? boxW : boxH;
2847
+ element.style.width = `${imgW}px`;
2848
+ element.style.height = `${imgH}px`;
2789
2849
  element.style.maxWidth = "none";
2790
2850
  element.style.maxHeight = "none";
2791
- element.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
2851
+ element.style.transform = rotation ? `rotate(${rotation}deg)` : "none";
2792
2852
  element.style.transformOrigin = "center center";
2793
2853
  element.style.flexShrink = "0";
2794
2854
  } else {
@@ -2808,7 +2868,8 @@ var MediaPlugin = class {
2808
2868
  }
2809
2869
  }
2810
2870
  const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
2811
- if (currentZoom === 1) {
2871
+ if (!isUserZoomed && currentZoom === 1) {
2872
+ updateBaseDimensions();
2812
2873
  applyTransform();
2813
2874
  }
2814
2875
  }) : null;
@@ -2823,28 +2884,49 @@ var MediaPlugin = class {
2823
2884
  return {
2824
2885
  destroy: cleanup,
2825
2886
  zoomIn: isImage ? () => {
2826
- currentZoom += 0.1;
2887
+ isUserZoomed = true;
2888
+ currentZoom = Math.min(5, Math.round((currentZoom + 0.15) * 100) / 100);
2827
2889
  applyTransform();
2828
2890
  } : void 0,
2829
2891
  zoomOut: isImage ? () => {
2830
- currentZoom = Math.max(0.1, currentZoom - 0.1);
2892
+ isUserZoomed = true;
2893
+ currentZoom = Math.max(0.1, Math.round((currentZoom - 0.15) * 100) / 100);
2831
2894
  applyTransform();
2832
2895
  } : void 0,
2833
2896
  getZoom: isImage ? () => currentZoom : void 0,
2834
2897
  setZoom: isImage ? (level) => {
2898
+ isUserZoomed = true;
2835
2899
  currentZoom = level;
2836
2900
  applyTransform();
2837
2901
  } : void 0,
2838
2902
  fitToPage: isImage ? () => {
2903
+ isUserZoomed = false;
2904
+ fitMode = "page";
2839
2905
  currentZoom = 1;
2840
2906
  rotation = 0;
2907
+ ctx.container.scrollTop = 0;
2908
+ ctx.container.scrollLeft = 0;
2909
+ updateBaseDimensions();
2910
+ applyTransform();
2911
+ } : void 0,
2912
+ fitToWidth: isImage ? () => {
2913
+ isUserZoomed = false;
2914
+ fitMode = "width";
2915
+ currentZoom = 1;
2916
+ rotation = 0;
2917
+ ctx.container.scrollTop = 0;
2918
+ ctx.container.scrollLeft = 0;
2919
+ updateBaseDimensions();
2841
2920
  applyTransform();
2842
2921
  } : void 0,
2843
2922
  resetZoom: isImage ? () => {
2923
+ isUserZoomed = false;
2924
+ fitMode = ctx?.options?.fitMode || "width";
2844
2925
  currentZoom = 1;
2845
2926
  rotation = 0;
2846
2927
  ctx.container.scrollTop = 0;
2847
2928
  ctx.container.scrollLeft = 0;
2929
+ updateBaseDimensions();
2848
2930
  applyTransform();
2849
2931
  } : void 0,
2850
2932
  rotateCW: isImage || isVideo ? () => {
@@ -2971,6 +3053,14 @@ var DocxPlugin = class {
2971
3053
  group: "zoom",
2972
3054
  execute: () => instance.resetZoom?.()
2973
3055
  },
3056
+ {
3057
+ id: "fit-width",
3058
+ icon: "fit-width",
3059
+ label: "Fit to Width",
3060
+ type: "button",
3061
+ group: "zoom",
3062
+ execute: () => instance.fitToWidth?.()
3063
+ },
2974
3064
  {
2975
3065
  id: "rotate-cw",
2976
3066
  icon: "rotate-cw",
@@ -3230,13 +3320,19 @@ var DocxPlugin = class {
3230
3320
  }
3231
3321
  scale = 1;
3232
3322
  let rotation = 0;
3233
- let fitMode = "page";
3323
+ let fitMode = ctx?.options?.fitMode || "width";
3234
3324
  let isUserZoomed = false;
3235
- const calculateFitScale = (_mode = fitMode) => {
3325
+ const calculateFitScale = (mode = fitMode) => {
3236
3326
  const activeEl = pageElements[currentPage - 1] || wrapper.querySelector("section.docx") || wrapper;
3237
3327
  const elW = activeEl.offsetWidth || 816;
3328
+ const elH = activeEl.offsetHeight || 1056;
3238
3329
  const availW = Math.max(280, ctx.container.clientWidth - 32);
3330
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
3239
3331
  const sW = availW / elW;
3332
+ const sH = availH / elH;
3333
+ if (mode === "page") {
3334
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
3335
+ }
3240
3336
  return Math.max(0.35, Math.min(3, sW));
3241
3337
  };
3242
3338
  const applyTransform = () => {
@@ -3257,7 +3353,7 @@ var DocxPlugin = class {
3257
3353
  if (typeof ResizeObserver !== "undefined") {
3258
3354
  resizeObserver = new ResizeObserver(() => {
3259
3355
  if (!isUserZoomed) {
3260
- const newFit = calculateFitScale("page");
3356
+ const newFit = calculateFitScale(fitMode);
3261
3357
  if (Math.abs(scale - newFit) > 0.015) {
3262
3358
  scale = newFit;
3263
3359
  applyTransform();
@@ -3267,7 +3363,7 @@ var DocxPlugin = class {
3267
3363
  resizeObserver.observe(ctx.container);
3268
3364
  }
3269
3365
  setTimeout(() => {
3270
- scale = calculateFitScale("page");
3366
+ scale = calculateFitScale(fitMode);
3271
3367
  applyTransform();
3272
3368
  }, 40);
3273
3369
  const cleanup = () => {
@@ -3306,13 +3402,22 @@ var DocxPlugin = class {
3306
3402
  },
3307
3403
  fitToPage: () => {
3308
3404
  isUserZoomed = false;
3405
+ fitMode = "page";
3309
3406
  scale = calculateFitScale("page");
3310
3407
  rotation = 0;
3311
3408
  applyTransform();
3312
3409
  },
3410
+ fitToWidth: () => {
3411
+ isUserZoomed = false;
3412
+ fitMode = "width";
3413
+ scale = calculateFitScale("width");
3414
+ rotation = 0;
3415
+ applyTransform();
3416
+ },
3313
3417
  resetZoom: () => {
3314
3418
  isUserZoomed = false;
3315
- scale = calculateFitScale("page");
3419
+ fitMode = ctx?.options?.fitMode || "width";
3420
+ scale = calculateFitScale(fitMode);
3316
3421
  rotation = 0;
3317
3422
  ctx.container.scrollTop = 0;
3318
3423
  ctx.container.scrollLeft = 0;
@@ -3777,6 +3882,16 @@ var ExcelPlugin = class {
3777
3882
  instance.resetZoom?.();
3778
3883
  }
3779
3884
  },
3885
+ {
3886
+ id: "fit-width",
3887
+ icon: "fit-width",
3888
+ label: "Fit to Width",
3889
+ type: "button",
3890
+ group: "zoom",
3891
+ execute: () => {
3892
+ instance.fitToWidth?.();
3893
+ }
3894
+ },
3780
3895
  {
3781
3896
  id: "download",
3782
3897
  icon: "download",
@@ -3993,6 +4108,11 @@ var ExcelPlugin = class {
3993
4108
  scale = calculateFitScale();
3994
4109
  applyTransform();
3995
4110
  },
4111
+ fitToWidth: () => {
4112
+ isUserZoomed = false;
4113
+ scale = calculateFitScale();
4114
+ applyTransform();
4115
+ },
3996
4116
  resetZoom: () => {
3997
4117
  isUserZoomed = false;
3998
4118
  scale = calculateFitScale();
@@ -4140,6 +4260,16 @@ var CsvPlugin = class {
4140
4260
  instance.resetZoom?.();
4141
4261
  }
4142
4262
  },
4263
+ {
4264
+ id: "fit-width",
4265
+ icon: "fit-width",
4266
+ label: "Fit to Width",
4267
+ type: "button",
4268
+ group: "zoom",
4269
+ execute: () => {
4270
+ instance.fitToWidth?.();
4271
+ }
4272
+ },
4143
4273
  {
4144
4274
  id: "download",
4145
4275
  icon: "download",
@@ -4353,6 +4483,11 @@ var CsvPlugin = class {
4353
4483
  scale = calculateFitScale();
4354
4484
  applyScale();
4355
4485
  },
4486
+ fitToWidth: () => {
4487
+ isUserZoomed = false;
4488
+ scale = calculateFitScale();
4489
+ applyScale();
4490
+ },
4356
4491
  resetZoom: () => {
4357
4492
  isUserZoomed = false;
4358
4493
  scale = calculateFitScale();
@@ -4485,6 +4620,16 @@ var CodePlugin = class {
4485
4620
  instance.resetZoom?.();
4486
4621
  }
4487
4622
  },
4623
+ {
4624
+ id: "fit-width",
4625
+ icon: "fit-width",
4626
+ label: "Fit to Width",
4627
+ type: "button",
4628
+ group: "zoom",
4629
+ execute: () => {
4630
+ instance.fitToWidth?.();
4631
+ }
4632
+ },
4488
4633
  {
4489
4634
  id: "copy",
4490
4635
  icon: "copy",
@@ -4789,6 +4934,13 @@ var CodePlugin = class {
4789
4934
  zoomLevel = isTxt ? calculateTxtFit() : 1;
4790
4935
  updateTransform();
4791
4936
  },
4937
+ fitToWidth: () => {
4938
+ isUserZoomed = false;
4939
+ fontSize = 13;
4940
+ rotation = 0;
4941
+ zoomLevel = isTxt ? calculateTxtFit() : 1;
4942
+ updateTransform();
4943
+ },
4792
4944
  resetZoom: () => {
4793
4945
  isUserZoomed = false;
4794
4946
  fontSize = 13;
@@ -4873,6 +5025,14 @@ var ArchivePlugin = class {
4873
5025
  group: "zoom",
4874
5026
  execute: () => instance.resetZoom?.()
4875
5027
  },
5028
+ {
5029
+ id: "fit-width",
5030
+ icon: "fit-width",
5031
+ label: "Fit to Width",
5032
+ type: "button",
5033
+ group: "zoom",
5034
+ execute: () => instance.fitToWidth?.()
5035
+ },
4876
5036
  {
4877
5037
  id: "download",
4878
5038
  icon: "download",
@@ -5070,6 +5230,10 @@ var ArchivePlugin = class {
5070
5230
  scale = 1;
5071
5231
  applyTransform();
5072
5232
  },
5233
+ fitToWidth: () => {
5234
+ scale = 1;
5235
+ applyTransform();
5236
+ },
5073
5237
  resetZoom: () => {
5074
5238
  scale = 1;
5075
5239
  wrapper.scrollTop = 0;
@@ -5134,6 +5298,14 @@ var MarkdownPlugin = class {
5134
5298
  group: "zoom",
5135
5299
  execute: () => instance.resetZoom?.()
5136
5300
  },
5301
+ {
5302
+ id: "fit-width",
5303
+ icon: "fit-width",
5304
+ label: "Fit to Width",
5305
+ type: "button",
5306
+ group: "zoom",
5307
+ execute: () => instance.fitToWidth?.()
5308
+ },
5137
5309
  {
5138
5310
  id: "copy",
5139
5311
  icon: "copy",
@@ -5287,6 +5459,10 @@ var MarkdownPlugin = class {
5287
5459
  fontSize = 15;
5288
5460
  wrapper.style.fontSize = "15px";
5289
5461
  },
5462
+ fitToWidth: () => {
5463
+ fontSize = 15;
5464
+ wrapper.style.fontSize = "15px";
5465
+ },
5290
5466
  resetZoom: () => {
5291
5467
  fontSize = 15;
5292
5468
  wrapper.style.fontSize = "15px";
@@ -5394,6 +5570,14 @@ var PptxPlugin = class {
5394
5570
  group: "zoom",
5395
5571
  execute: () => instance.resetZoom?.()
5396
5572
  },
5573
+ {
5574
+ id: "fit-width",
5575
+ icon: "fit-width",
5576
+ label: "Fit to Width",
5577
+ type: "button",
5578
+ group: "zoom",
5579
+ execute: () => instance.fitToWidth?.()
5580
+ },
5397
5581
  {
5398
5582
  id: "rotate-cw",
5399
5583
  icon: "rotate-cw",
@@ -5477,12 +5661,17 @@ var PptxPlugin = class {
5477
5661
  ctx.container.innerHTML = "";
5478
5662
  ctx.container.style.overflow = "auto";
5479
5663
  ctx.container.appendChild(wrapper);
5480
- const calculateFitScale = () => {
5481
- const availW = Math.max(200, ctx.container.clientWidth - 48);
5482
- const availH = Math.max(200, ctx.container.clientHeight - 72);
5664
+ let fitMode = ctx?.options?.fitMode || "width";
5665
+ let isUserZoomed = false;
5666
+ const calculateFitScale = (mode = fitMode) => {
5667
+ const availW = Math.max(200, ctx.container.clientWidth - 32);
5668
+ const availH = Math.max(200, ctx.container.clientHeight - 32);
5483
5669
  const cW = canvas.offsetWidth || 1280;
5484
5670
  const cH = canvas.offsetHeight || 720;
5485
- return Math.min(1, Math.min(availW / cW, availH / cH));
5671
+ if (mode === "page") {
5672
+ return Math.min(2.5, Math.min(availW / cW, availH / cH));
5673
+ }
5674
+ return Math.min(2.5, availW / cW);
5486
5675
  };
5487
5676
  const applyTransform = () => {
5488
5677
  const cW = canvas.offsetWidth || 1280;
@@ -5501,14 +5690,24 @@ var PptxPlugin = class {
5501
5690
  try {
5502
5691
  await renderer.renderSlide(currentSlide - 1, canvas, 1280);
5503
5692
  ctx.emit("page-change", { page: currentSlide, totalPages: slideCount });
5504
- scale = calculateFitScale();
5693
+ if (!isUserZoomed) {
5694
+ scale = calculateFitScale(fitMode);
5695
+ }
5505
5696
  applyTransform();
5506
5697
  } catch (err) {
5507
5698
  console.error("[PptxPlugin] Failed to render slide:", err);
5508
5699
  }
5509
5700
  };
5510
5701
  await renderCurrentSlide();
5702
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
5703
+ if (!isUserZoomed) {
5704
+ scale = calculateFitScale(fitMode);
5705
+ applyTransform();
5706
+ }
5707
+ }) : null;
5708
+ ro?.observe(ctx.container);
5511
5709
  const cleanup = () => {
5710
+ ro?.disconnect();
5512
5711
  renderer.destroy();
5513
5712
  wrapper.remove();
5514
5713
  ctx.container.innerHTML = "";
@@ -5525,28 +5724,44 @@ var PptxPlugin = class {
5525
5724
  getPageCount: () => slideCount,
5526
5725
  getCurrentPage: () => currentSlide,
5527
5726
  zoomIn: () => {
5727
+ isUserZoomed = true;
5528
5728
  scale += 0.15;
5529
5729
  applyTransform();
5530
5730
  },
5531
5731
  zoomOut: () => {
5732
+ isUserZoomed = true;
5532
5733
  scale = Math.max(0.2, scale - 0.15);
5533
5734
  applyTransform();
5534
5735
  },
5535
5736
  getZoom: () => scale,
5536
5737
  setZoom: (level) => {
5738
+ isUserZoomed = true;
5537
5739
  scale = level;
5538
5740
  applyTransform();
5539
5741
  },
5540
5742
  fitToPage: () => {
5541
- scale = calculateFitScale();
5743
+ isUserZoomed = false;
5744
+ fitMode = "page";
5745
+ scale = calculateFitScale("page");
5746
+ rotation = 0;
5747
+ applyTransform();
5748
+ },
5749
+ fitToWidth: () => {
5750
+ isUserZoomed = false;
5751
+ fitMode = "width";
5752
+ scale = calculateFitScale("width");
5542
5753
  rotation = 0;
5543
5754
  applyTransform();
5544
5755
  },
5545
5756
  resetZoom: () => {
5546
- scale = calculateFitScale();
5757
+ isUserZoomed = false;
5758
+ fitMode = ctx?.options?.fitMode || "width";
5759
+ scale = calculateFitScale(fitMode);
5547
5760
  rotation = 0;
5548
5761
  wrapper.scrollTop = 0;
5549
5762
  wrapper.scrollLeft = 0;
5763
+ ctx.container.scrollTop = 0;
5764
+ ctx.container.scrollLeft = 0;
5550
5765
  applyTransform();
5551
5766
  },
5552
5767
  rotateCW: () => {
@@ -5991,6 +6206,14 @@ var RtfPlugin = class {
5991
6206
  group: "zoom",
5992
6207
  execute: () => instance.resetZoom?.()
5993
6208
  },
6209
+ {
6210
+ id: "fit-width",
6211
+ icon: "fit-width",
6212
+ label: "Fit to Width",
6213
+ type: "button",
6214
+ group: "zoom",
6215
+ execute: () => instance.fitToWidth?.()
6216
+ },
5994
6217
  {
5995
6218
  id: "rotate-cw",
5996
6219
  icon: "rotate-cw",
@@ -6479,6 +6702,13 @@ var RtfPlugin = class {
6479
6702
  applyTransform();
6480
6703
  },
6481
6704
  fitToPage: () => {
6705
+ isUserZoomed = false;
6706
+ fitMode = "page";
6707
+ scale = calculateFitScale("page");
6708
+ rotation = 0;
6709
+ applyTransform();
6710
+ },
6711
+ fitToWidth: () => {
6482
6712
  isUserZoomed = false;
6483
6713
  fitMode = "width";
6484
6714
  scale = calculateFitScale("width");
@@ -6487,8 +6717,8 @@ var RtfPlugin = class {
6487
6717
  },
6488
6718
  resetZoom: () => {
6489
6719
  isUserZoomed = false;
6490
- fitMode = "width";
6491
- scale = calculateFitScale("width");
6720
+ fitMode = ctx?.options?.fitMode || "width";
6721
+ scale = calculateFitScale(fitMode);
6492
6722
  rotation = 0;
6493
6723
  ctx.container.scrollTop = 0;
6494
6724
  ctx.container.scrollLeft = 0;
@@ -6569,6 +6799,14 @@ var HtmlPreviewPlugin = class {
6569
6799
  group: "zoom",
6570
6800
  execute: () => instance.resetZoom?.()
6571
6801
  },
6802
+ {
6803
+ id: "fit-width",
6804
+ icon: "fit-width",
6805
+ label: "Fit to Width",
6806
+ type: "button",
6807
+ group: "zoom",
6808
+ execute: () => instance.fitToWidth?.()
6809
+ },
6572
6810
  {
6573
6811
  id: "copy",
6574
6812
  icon: "copy",
@@ -6662,6 +6900,10 @@ var HtmlPreviewPlugin = class {
6662
6900
  scale = 1;
6663
6901
  applyTransform();
6664
6902
  },
6903
+ fitToWidth: () => {
6904
+ scale = 1;
6905
+ applyTransform();
6906
+ },
6665
6907
  resetZoom: () => {
6666
6908
  scale = 1;
6667
6909
  ctx.container.scrollTop = 0;
@@ -6776,6 +7018,14 @@ var OpenDocumentPlugin = class {
6776
7018
  group: "zoom",
6777
7019
  execute: () => instance.resetZoom?.()
6778
7020
  },
7021
+ {
7022
+ id: "fit-width",
7023
+ icon: "fit-width",
7024
+ label: "Fit to Width",
7025
+ type: "button",
7026
+ group: "zoom",
7027
+ execute: () => instance.fitToWidth?.()
7028
+ },
6779
7029
  {
6780
7030
  id: "rotate-cw",
6781
7031
  icon: "rotate-cw",
@@ -6892,7 +7142,10 @@ var OpenDocumentPlugin = class {
6892
7142
  const sW = availW / elW;
6893
7143
  const sH = availH / (isPresentation ? 540 : singlePageH);
6894
7144
  if (isPresentation) {
6895
- return Math.min(2.5, Math.min(sW, sH));
7145
+ if (mode === "page") {
7146
+ return Math.min(2.5, Math.min(sW, sH));
7147
+ }
7148
+ return Math.min(2.5, sW);
6896
7149
  }
6897
7150
  if (mode === "page") {
6898
7151
  return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
@@ -7061,6 +7314,13 @@ var OpenDocumentPlugin = class {
7061
7314
  applyTransform();
7062
7315
  },
7063
7316
  fitToPage: () => {
7317
+ isUserZoomed = false;
7318
+ fitMode = "page";
7319
+ scale = calculateFitScale("page");
7320
+ rotation = 0;
7321
+ applyTransform();
7322
+ },
7323
+ fitToWidth: () => {
7064
7324
  isUserZoomed = false;
7065
7325
  fitMode = "width";
7066
7326
  scale = calculateFitScale("width");
@@ -7069,8 +7329,8 @@ var OpenDocumentPlugin = class {
7069
7329
  },
7070
7330
  resetZoom: () => {
7071
7331
  isUserZoomed = false;
7072
- fitMode = "width";
7073
- scale = calculateFitScale("width");
7332
+ fitMode = ctx?.options?.fitMode || "width";
7333
+ scale = calculateFitScale(fitMode);
7074
7334
  rotation = 0;
7075
7335
  container.scrollTop = 0;
7076
7336
  container.scrollLeft = 0;
@@ -7427,6 +7687,14 @@ var DocPlugin = class {
7427
7687
  group: "zoom",
7428
7688
  execute: () => instance.resetZoom?.()
7429
7689
  },
7690
+ {
7691
+ id: "fit-width",
7692
+ icon: "fit-width",
7693
+ label: "Fit to Width",
7694
+ type: "button",
7695
+ group: "zoom",
7696
+ execute: () => instance.fitToWidth?.()
7697
+ },
7430
7698
  {
7431
7699
  id: "rotate-cw",
7432
7700
  icon: "rotate-cw",
@@ -7648,6 +7916,13 @@ var DocPlugin = class {
7648
7916
  applyTransform();
7649
7917
  },
7650
7918
  fitToPage: () => {
7919
+ isUserZoomed = false;
7920
+ fitMode = "page";
7921
+ scale = calculateFitScale("page");
7922
+ rotation = 0;
7923
+ applyTransform();
7924
+ },
7925
+ fitToWidth: () => {
7651
7926
  isUserZoomed = false;
7652
7927
  fitMode = "width";
7653
7928
  scale = calculateFitScale("width");
@@ -7656,8 +7931,8 @@ var DocPlugin = class {
7656
7931
  },
7657
7932
  resetZoom: () => {
7658
7933
  isUserZoomed = false;
7659
- fitMode = "width";
7660
- scale = calculateFitScale("width");
7934
+ fitMode = ctx?.options?.fitMode || "width";
7935
+ scale = calculateFitScale(fitMode);
7661
7936
  rotation = 0;
7662
7937
  container.scrollTop = 0;
7663
7938
  container.scrollLeft = 0;
@@ -8153,6 +8428,14 @@ var PptPlugin = class {
8153
8428
  group: "zoom",
8154
8429
  execute: () => instance.resetZoom?.()
8155
8430
  },
8431
+ {
8432
+ id: "fit-width",
8433
+ icon: "fit-width",
8434
+ label: "Fit to Width",
8435
+ type: "button",
8436
+ group: "zoom",
8437
+ execute: () => instance.fitToWidth?.()
8438
+ },
8156
8439
  {
8157
8440
  id: "rotate-cw",
8158
8441
  icon: "rotate-cw",
@@ -8227,10 +8510,15 @@ var PptPlugin = class {
8227
8510
  let currentSlide = 1;
8228
8511
  let slides = [];
8229
8512
  const createdBlobUrls = [];
8230
- const calculateFitScale = () => {
8231
- const availW = Math.max(200, container.clientWidth - 48);
8232
- const availH = Math.max(200, container.clientHeight - 72);
8233
- return Math.min(1, Math.min(availW / 960, availH / 540));
8513
+ let fitMode = ctx?.options?.fitMode || "width";
8514
+ let isUserZoomed = false;
8515
+ const calculateFitScale = (mode = fitMode) => {
8516
+ const availW = Math.max(200, container.clientWidth - 32);
8517
+ const availH = Math.max(200, container.clientHeight - 32);
8518
+ if (mode === "page") {
8519
+ return Math.min(2.5, Math.min(availW / 960, availH / 540));
8520
+ }
8521
+ return Math.min(2.5, availW / 960);
8234
8522
  };
8235
8523
  const applyTransform = () => {
8236
8524
  const cW = 960;
@@ -8245,8 +8533,15 @@ var PptPlugin = class {
8245
8533
  slideCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
8246
8534
  slideCard.style.transformOrigin = "center center";
8247
8535
  };
8536
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
8537
+ if (!isUserZoomed) {
8538
+ scale = calculateFitScale(fitMode);
8539
+ applyTransform();
8540
+ }
8541
+ }) : null;
8542
+ ro?.observe(container);
8248
8543
  setTimeout(() => {
8249
- scale = calculateFitScale();
8544
+ scale = calculateFitScale(fitMode);
8250
8545
  applyTransform();
8251
8546
  }, 60);
8252
8547
  try {
@@ -8339,6 +8634,7 @@ var PptPlugin = class {
8339
8634
  };
8340
8635
  renderSlide(1);
8341
8636
  const cleanup = () => {
8637
+ ro?.disconnect();
8342
8638
  for (const u of createdBlobUrls) {
8343
8639
  URL.revokeObjectURL(u);
8344
8640
  }
@@ -8349,28 +8645,44 @@ var PptPlugin = class {
8349
8645
  return {
8350
8646
  destroy: cleanup,
8351
8647
  zoomIn: () => {
8648
+ isUserZoomed = true;
8352
8649
  scale += 0.15;
8353
8650
  applyTransform();
8354
8651
  },
8355
8652
  zoomOut: () => {
8653
+ isUserZoomed = true;
8356
8654
  scale = Math.max(0.2, scale - 0.15);
8357
8655
  applyTransform();
8358
8656
  },
8359
8657
  getZoom: () => scale,
8360
8658
  setZoom: (level) => {
8659
+ isUserZoomed = true;
8361
8660
  scale = level;
8362
8661
  applyTransform();
8363
8662
  },
8364
8663
  fitToPage: () => {
8365
- scale = calculateFitScale();
8664
+ isUserZoomed = false;
8665
+ fitMode = "page";
8666
+ scale = calculateFitScale("page");
8667
+ rotation = 0;
8668
+ applyTransform();
8669
+ },
8670
+ fitToWidth: () => {
8671
+ isUserZoomed = false;
8672
+ fitMode = "width";
8673
+ scale = calculateFitScale("width");
8366
8674
  rotation = 0;
8367
8675
  applyTransform();
8368
8676
  },
8369
8677
  resetZoom: () => {
8370
- scale = calculateFitScale();
8678
+ isUserZoomed = false;
8679
+ fitMode = ctx?.options?.fitMode || "width";
8680
+ scale = calculateFitScale(fitMode);
8371
8681
  rotation = 0;
8372
8682
  container.scrollTop = 0;
8373
8683
  container.scrollLeft = 0;
8684
+ ctx.container.scrollTop = 0;
8685
+ ctx.container.scrollLeft = 0;
8374
8686
  applyTransform();
8375
8687
  },
8376
8688
  rotateCW: () => {