@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/react.cjs CHANGED
@@ -1090,6 +1090,10 @@ var FilePreviewViewer = class _FilePreviewViewer {
1090
1090
  * Preview a file in the given container element.
1091
1091
  */
1092
1092
  async preview(container, source, options = {}) {
1093
+ options = {
1094
+ ...options,
1095
+ fitMode: options.fitMode ?? "width"
1096
+ };
1093
1097
  this.currentOptions = options;
1094
1098
  this.abort();
1095
1099
  this.abortController = new AbortController();
@@ -1138,8 +1142,8 @@ var FilePreviewViewer = class _FilePreviewViewer {
1138
1142
  this.activeInstance = instance;
1139
1143
  instance.openInSeparateWindow = () => this.openInSeparateWindow();
1140
1144
  instance.toggleThumbnails = () => this.toggleThumbnails();
1141
- if (!instance.resetZoom && instance.fitToPage) {
1142
- instance.resetZoom = () => instance.fitToPage?.();
1145
+ if (!instance.resetZoom) {
1146
+ instance.resetZoom = () => instance.fitToWidth ? instance.fitToWidth() : instance.fitToPage?.();
1143
1147
  }
1144
1148
  this.hideLoading();
1145
1149
  this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
@@ -1536,7 +1540,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1536
1540
  this.wrapperEl?.classList.toggle("fp-fullscreen-active");
1537
1541
  }
1538
1542
  setTimeout(() => {
1539
- this.activeInstance?.fitToPage?.();
1543
+ this.triggerAutoFit();
1540
1544
  }, 120);
1541
1545
  }
1542
1546
  /**
@@ -1663,18 +1667,29 @@ var FilePreviewViewer = class _FilePreviewViewer {
1663
1667
  this.thumbnailPanel = new ThumbnailPanel(thumbnailEl, (isOpen) => {
1664
1668
  this.toolbar?.setActionActive("thumbnails", isOpen);
1665
1669
  setTimeout(() => {
1666
- this.activeInstance?.fitToPage?.();
1670
+ this.triggerAutoFit();
1667
1671
  }, 260);
1668
1672
  });
1669
1673
  this.setupKeyboardShortcuts();
1670
1674
  this.setupDragAndDrop(container, options);
1671
1675
  this.setupResizeAndFullscreenListeners();
1672
1676
  }
1677
+ triggerAutoFit() {
1678
+ if (!this.activeInstance) return;
1679
+ const mode = this.currentOptions?.fitMode ?? "width";
1680
+ if (mode === "width" && this.activeInstance.fitToWidth) {
1681
+ this.activeInstance.fitToWidth();
1682
+ } else if (this.activeInstance.fitToPage) {
1683
+ this.activeInstance.fitToPage();
1684
+ } else if (this.activeInstance.resetZoom) {
1685
+ this.activeInstance.resetZoom();
1686
+ }
1687
+ }
1673
1688
  setupResizeAndFullscreenListeners() {
1674
1689
  if (this.fullscreenHandler) return;
1675
1690
  this.fullscreenHandler = () => {
1676
1691
  setTimeout(() => {
1677
- this.activeInstance?.fitToPage?.();
1692
+ this.triggerAutoFit();
1678
1693
  }, 100);
1679
1694
  };
1680
1695
  document.addEventListener("fullscreenchange", this.fullscreenHandler);
@@ -1682,7 +1697,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1682
1697
  if (typeof ResizeObserver !== "undefined" && this.contentEl) {
1683
1698
  this.resizeObserver = new ResizeObserver(() => {
1684
1699
  if (this.activeInstance && (!this.activeInstance.getZoom || Math.abs((this.activeInstance.getZoom?.() ?? 1) - 1) < 0.05)) {
1685
- this.activeInstance.fitToPage?.();
1700
+ this.triggerAutoFit();
1686
1701
  }
1687
1702
  });
1688
1703
  this.resizeObserver.observe(this.contentEl);
@@ -1710,7 +1725,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1710
1725
  } else if (e.key === "-" || e.key === "_") {
1711
1726
  this.activeInstance.zoomOut?.();
1712
1727
  } else if (e.key === "0") {
1713
- this.activeInstance.fitToPage?.();
1728
+ this.resetZoom();
1714
1729
  } else if (e.key === "r" || e.key === "R") {
1715
1730
  this.activeInstance.rotateCW?.();
1716
1731
  } else if (e.key === "f" || e.key === "F") {
@@ -2333,7 +2348,7 @@ var PdfPlugin = class {
2333
2348
  renderPage(currentPage);
2334
2349
  },
2335
2350
  resetZoom: () => {
2336
- fitMode = "page";
2351
+ fitMode = ctx?.options?.fitMode || "width";
2337
2352
  zoomScale = 1;
2338
2353
  rotation = 0;
2339
2354
  container.scrollTop = 0;
@@ -2517,6 +2532,16 @@ var MediaPlugin = class {
2517
2532
  instance.resetZoom?.();
2518
2533
  }
2519
2534
  },
2535
+ {
2536
+ id: "fit-width",
2537
+ icon: "fit-width",
2538
+ label: "Fit to Width",
2539
+ type: "button",
2540
+ group: "zoom",
2541
+ execute: () => {
2542
+ instance.fitToWidth?.();
2543
+ }
2544
+ },
2520
2545
  {
2521
2546
  id: "rotate-cw",
2522
2547
  icon: "rotate-cw",
@@ -2613,9 +2638,30 @@ var MediaPlugin = class {
2613
2638
  const isVideo = mimeType.startsWith("video/") || VIDEO_EXTS.includes(ctx.metadata.extension || "");
2614
2639
  const isAudio = mimeType.startsWith("audio/") || AUDIO_EXTS.includes(ctx.metadata.extension || "");
2615
2640
  let url = "";
2641
+ let naturalW = 800;
2642
+ let naturalH = 600;
2616
2643
  if (ctx.metadata.extension === ".svg" || mimeType === "image/svg+xml") {
2617
2644
  const decoder = new TextDecoder("utf-8");
2618
2645
  const svgText = decoder.decode(ctx.buffer);
2646
+ try {
2647
+ const vbMatch = svgText.match(/viewBox=["']\s*([0-9.-]+)\s+([0-9.-]+)\s+([0-9.-]+)\s+([0-9.-]+)\s*["']/i);
2648
+ if (vbMatch) {
2649
+ const vw = parseFloat(vbMatch[3]);
2650
+ const vh = parseFloat(vbMatch[4]);
2651
+ if (vw > 0 && vh > 0) {
2652
+ naturalW = vw;
2653
+ naturalH = vh;
2654
+ }
2655
+ } else {
2656
+ const wMatch = svgText.match(/width=["']([0-9.]+)(?:px)?["']/i);
2657
+ const hMatch = svgText.match(/height=["']([0-9.]+)(?:px)?["']/i);
2658
+ if (wMatch && hMatch) {
2659
+ naturalW = parseFloat(wMatch[1]) || naturalW;
2660
+ naturalH = parseFloat(hMatch[1]) || naturalH;
2661
+ }
2662
+ }
2663
+ } catch {
2664
+ }
2619
2665
  const blob = new Blob([svgText], { type: "image/svg+xml" });
2620
2666
  url = URL.createObjectURL(blob);
2621
2667
  } else {
@@ -2663,7 +2709,7 @@ var MediaPlugin = class {
2663
2709
  scrollWrapper.style.width = "max-content";
2664
2710
  scrollWrapper.style.height = "max-content";
2665
2711
  scrollWrapper.style.display = "flex";
2666
- scrollWrapper.style.justifyContent = "center";
2712
+ scrollWrapper.style.flexDirection = "column";
2667
2713
  scrollWrapper.style.alignItems = "center";
2668
2714
  scrollWrapper.style.padding = "16px";
2669
2715
  scrollWrapper.style.boxSizing = "border-box";
@@ -2674,40 +2720,54 @@ var MediaPlugin = class {
2674
2720
  sizer.style.display = "flex";
2675
2721
  sizer.style.justifyContent = "center";
2676
2722
  sizer.style.alignItems = "center";
2723
+ sizer.style.margin = "auto 0";
2677
2724
  sizer.appendChild(element);
2678
2725
  scrollWrapper.appendChild(sizer);
2679
2726
  ctx.container.style.overflow = "auto";
2680
2727
  ctx.container.style.padding = "0";
2681
2728
  ctx.container.innerHTML = "";
2682
2729
  ctx.container.appendChild(scrollWrapper);
2683
- let naturalW = 800;
2684
- let naturalH = 600;
2685
- let baseW = 800;
2686
- let baseH = 600;
2730
+ let baseW = naturalW;
2731
+ let baseH = naturalH;
2732
+ let fitMode = ctx?.options?.fitMode || "width";
2733
+ let isUserZoomed = false;
2687
2734
  const updateBaseDimensions = () => {
2688
2735
  if (!isImage) return;
2689
2736
  const img = element;
2690
- naturalW = img.naturalWidth || naturalW;
2691
- naturalH = img.naturalHeight || naturalH;
2692
- const availW = Math.max(100, ctx.container.clientWidth - 48);
2693
- const availH = Math.max(100, ctx.container.clientHeight - 48);
2694
- const fitRatio = Math.min(1, availW / naturalW, availH / naturalH);
2737
+ if (img.naturalWidth > 0 && img.naturalHeight > 0) {
2738
+ naturalW = img.naturalWidth;
2739
+ naturalH = img.naturalHeight;
2740
+ }
2741
+ const availW = Math.max(100, ctx.container.clientWidth - 32);
2742
+ const availH = Math.max(100, ctx.container.clientHeight - 32);
2743
+ let fitRatio;
2744
+ if (fitMode === "page") {
2745
+ fitRatio = Math.min(availW / naturalW, availH / naturalH);
2746
+ } else {
2747
+ fitRatio = availW / naturalW;
2748
+ }
2695
2749
  baseW = Math.max(1, Math.round(naturalW * fitRatio));
2696
2750
  baseH = Math.max(1, Math.round(naturalH * fitRatio));
2697
2751
  };
2698
2752
  const applyTransform = () => {
2699
2753
  if (isImage) {
2700
- updateBaseDimensions();
2754
+ if (!isUserZoomed) {
2755
+ updateBaseDimensions();
2756
+ }
2757
+ const availH = Math.max(100, ctx.container.clientHeight - 32);
2701
2758
  const isRotated90 = rotation % 180 !== 0;
2702
2759
  const boxW = Math.round((isRotated90 ? baseH : baseW) * currentZoom);
2703
2760
  const boxH = Math.round((isRotated90 ? baseW : baseH) * currentZoom);
2704
2761
  sizer.style.width = `${boxW}px`;
2705
2762
  sizer.style.height = `${boxH}px`;
2706
- element.style.width = `${baseW}px`;
2707
- element.style.height = `${baseH}px`;
2763
+ sizer.style.margin = boxH < availH ? "auto 0" : "0";
2764
+ const imgW = isRotated90 ? boxH : boxW;
2765
+ const imgH = isRotated90 ? boxW : boxH;
2766
+ element.style.width = `${imgW}px`;
2767
+ element.style.height = `${imgH}px`;
2708
2768
  element.style.maxWidth = "none";
2709
2769
  element.style.maxHeight = "none";
2710
- element.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
2770
+ element.style.transform = rotation ? `rotate(${rotation}deg)` : "none";
2711
2771
  element.style.transformOrigin = "center center";
2712
2772
  element.style.flexShrink = "0";
2713
2773
  } else {
@@ -2727,7 +2787,8 @@ var MediaPlugin = class {
2727
2787
  }
2728
2788
  }
2729
2789
  const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
2730
- if (currentZoom === 1) {
2790
+ if (!isUserZoomed && currentZoom === 1) {
2791
+ updateBaseDimensions();
2731
2792
  applyTransform();
2732
2793
  }
2733
2794
  }) : null;
@@ -2742,28 +2803,49 @@ var MediaPlugin = class {
2742
2803
  return {
2743
2804
  destroy: cleanup,
2744
2805
  zoomIn: isImage ? () => {
2745
- currentZoom += 0.1;
2806
+ isUserZoomed = true;
2807
+ currentZoom = Math.min(5, Math.round((currentZoom + 0.15) * 100) / 100);
2746
2808
  applyTransform();
2747
2809
  } : void 0,
2748
2810
  zoomOut: isImage ? () => {
2749
- currentZoom = Math.max(0.1, currentZoom - 0.1);
2811
+ isUserZoomed = true;
2812
+ currentZoom = Math.max(0.1, Math.round((currentZoom - 0.15) * 100) / 100);
2750
2813
  applyTransform();
2751
2814
  } : void 0,
2752
2815
  getZoom: isImage ? () => currentZoom : void 0,
2753
2816
  setZoom: isImage ? (level) => {
2817
+ isUserZoomed = true;
2754
2818
  currentZoom = level;
2755
2819
  applyTransform();
2756
2820
  } : void 0,
2757
2821
  fitToPage: isImage ? () => {
2822
+ isUserZoomed = false;
2823
+ fitMode = "page";
2758
2824
  currentZoom = 1;
2759
2825
  rotation = 0;
2826
+ ctx.container.scrollTop = 0;
2827
+ ctx.container.scrollLeft = 0;
2828
+ updateBaseDimensions();
2829
+ applyTransform();
2830
+ } : void 0,
2831
+ fitToWidth: isImage ? () => {
2832
+ isUserZoomed = false;
2833
+ fitMode = "width";
2834
+ currentZoom = 1;
2835
+ rotation = 0;
2836
+ ctx.container.scrollTop = 0;
2837
+ ctx.container.scrollLeft = 0;
2838
+ updateBaseDimensions();
2760
2839
  applyTransform();
2761
2840
  } : void 0,
2762
2841
  resetZoom: isImage ? () => {
2842
+ isUserZoomed = false;
2843
+ fitMode = ctx?.options?.fitMode || "width";
2763
2844
  currentZoom = 1;
2764
2845
  rotation = 0;
2765
2846
  ctx.container.scrollTop = 0;
2766
2847
  ctx.container.scrollLeft = 0;
2848
+ updateBaseDimensions();
2767
2849
  applyTransform();
2768
2850
  } : void 0,
2769
2851
  rotateCW: isImage || isVideo ? () => {
@@ -2890,6 +2972,14 @@ var DocxPlugin = class {
2890
2972
  group: "zoom",
2891
2973
  execute: () => instance.resetZoom?.()
2892
2974
  },
2975
+ {
2976
+ id: "fit-width",
2977
+ icon: "fit-width",
2978
+ label: "Fit to Width",
2979
+ type: "button",
2980
+ group: "zoom",
2981
+ execute: () => instance.fitToWidth?.()
2982
+ },
2893
2983
  {
2894
2984
  id: "rotate-cw",
2895
2985
  icon: "rotate-cw",
@@ -3149,13 +3239,19 @@ var DocxPlugin = class {
3149
3239
  }
3150
3240
  scale = 1;
3151
3241
  let rotation = 0;
3152
- let fitMode = "page";
3242
+ let fitMode = ctx?.options?.fitMode || "width";
3153
3243
  let isUserZoomed = false;
3154
- const calculateFitScale = (_mode = fitMode) => {
3244
+ const calculateFitScale = (mode = fitMode) => {
3155
3245
  const activeEl = pageElements[currentPage - 1] || wrapper.querySelector("section.docx") || wrapper;
3156
3246
  const elW = activeEl.offsetWidth || 816;
3247
+ const elH = activeEl.offsetHeight || 1056;
3157
3248
  const availW = Math.max(280, ctx.container.clientWidth - 32);
3249
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
3158
3250
  const sW = availW / elW;
3251
+ const sH = availH / elH;
3252
+ if (mode === "page") {
3253
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
3254
+ }
3159
3255
  return Math.max(0.35, Math.min(3, sW));
3160
3256
  };
3161
3257
  const applyTransform = () => {
@@ -3176,7 +3272,7 @@ var DocxPlugin = class {
3176
3272
  if (typeof ResizeObserver !== "undefined") {
3177
3273
  resizeObserver = new ResizeObserver(() => {
3178
3274
  if (!isUserZoomed) {
3179
- const newFit = calculateFitScale("page");
3275
+ const newFit = calculateFitScale(fitMode);
3180
3276
  if (Math.abs(scale - newFit) > 0.015) {
3181
3277
  scale = newFit;
3182
3278
  applyTransform();
@@ -3186,7 +3282,7 @@ var DocxPlugin = class {
3186
3282
  resizeObserver.observe(ctx.container);
3187
3283
  }
3188
3284
  setTimeout(() => {
3189
- scale = calculateFitScale("page");
3285
+ scale = calculateFitScale(fitMode);
3190
3286
  applyTransform();
3191
3287
  }, 40);
3192
3288
  const cleanup = () => {
@@ -3225,13 +3321,22 @@ var DocxPlugin = class {
3225
3321
  },
3226
3322
  fitToPage: () => {
3227
3323
  isUserZoomed = false;
3324
+ fitMode = "page";
3228
3325
  scale = calculateFitScale("page");
3229
3326
  rotation = 0;
3230
3327
  applyTransform();
3231
3328
  },
3329
+ fitToWidth: () => {
3330
+ isUserZoomed = false;
3331
+ fitMode = "width";
3332
+ scale = calculateFitScale("width");
3333
+ rotation = 0;
3334
+ applyTransform();
3335
+ },
3232
3336
  resetZoom: () => {
3233
3337
  isUserZoomed = false;
3234
- scale = calculateFitScale("page");
3338
+ fitMode = ctx?.options?.fitMode || "width";
3339
+ scale = calculateFitScale(fitMode);
3235
3340
  rotation = 0;
3236
3341
  ctx.container.scrollTop = 0;
3237
3342
  ctx.container.scrollLeft = 0;
@@ -3696,6 +3801,16 @@ var ExcelPlugin = class {
3696
3801
  instance.resetZoom?.();
3697
3802
  }
3698
3803
  },
3804
+ {
3805
+ id: "fit-width",
3806
+ icon: "fit-width",
3807
+ label: "Fit to Width",
3808
+ type: "button",
3809
+ group: "zoom",
3810
+ execute: () => {
3811
+ instance.fitToWidth?.();
3812
+ }
3813
+ },
3699
3814
  {
3700
3815
  id: "download",
3701
3816
  icon: "download",
@@ -3912,6 +4027,11 @@ var ExcelPlugin = class {
3912
4027
  scale = calculateFitScale();
3913
4028
  applyTransform();
3914
4029
  },
4030
+ fitToWidth: () => {
4031
+ isUserZoomed = false;
4032
+ scale = calculateFitScale();
4033
+ applyTransform();
4034
+ },
3915
4035
  resetZoom: () => {
3916
4036
  isUserZoomed = false;
3917
4037
  scale = calculateFitScale();
@@ -4059,6 +4179,16 @@ var CsvPlugin = class {
4059
4179
  instance.resetZoom?.();
4060
4180
  }
4061
4181
  },
4182
+ {
4183
+ id: "fit-width",
4184
+ icon: "fit-width",
4185
+ label: "Fit to Width",
4186
+ type: "button",
4187
+ group: "zoom",
4188
+ execute: () => {
4189
+ instance.fitToWidth?.();
4190
+ }
4191
+ },
4062
4192
  {
4063
4193
  id: "download",
4064
4194
  icon: "download",
@@ -4272,6 +4402,11 @@ var CsvPlugin = class {
4272
4402
  scale = calculateFitScale();
4273
4403
  applyScale();
4274
4404
  },
4405
+ fitToWidth: () => {
4406
+ isUserZoomed = false;
4407
+ scale = calculateFitScale();
4408
+ applyScale();
4409
+ },
4275
4410
  resetZoom: () => {
4276
4411
  isUserZoomed = false;
4277
4412
  scale = calculateFitScale();
@@ -4404,6 +4539,16 @@ var CodePlugin = class {
4404
4539
  instance.resetZoom?.();
4405
4540
  }
4406
4541
  },
4542
+ {
4543
+ id: "fit-width",
4544
+ icon: "fit-width",
4545
+ label: "Fit to Width",
4546
+ type: "button",
4547
+ group: "zoom",
4548
+ execute: () => {
4549
+ instance.fitToWidth?.();
4550
+ }
4551
+ },
4407
4552
  {
4408
4553
  id: "copy",
4409
4554
  icon: "copy",
@@ -4708,6 +4853,13 @@ var CodePlugin = class {
4708
4853
  zoomLevel = isTxt ? calculateTxtFit() : 1;
4709
4854
  updateTransform();
4710
4855
  },
4856
+ fitToWidth: () => {
4857
+ isUserZoomed = false;
4858
+ fontSize = 13;
4859
+ rotation = 0;
4860
+ zoomLevel = isTxt ? calculateTxtFit() : 1;
4861
+ updateTransform();
4862
+ },
4711
4863
  resetZoom: () => {
4712
4864
  isUserZoomed = false;
4713
4865
  fontSize = 13;
@@ -4792,6 +4944,14 @@ var ArchivePlugin = class {
4792
4944
  group: "zoom",
4793
4945
  execute: () => instance.resetZoom?.()
4794
4946
  },
4947
+ {
4948
+ id: "fit-width",
4949
+ icon: "fit-width",
4950
+ label: "Fit to Width",
4951
+ type: "button",
4952
+ group: "zoom",
4953
+ execute: () => instance.fitToWidth?.()
4954
+ },
4795
4955
  {
4796
4956
  id: "download",
4797
4957
  icon: "download",
@@ -4989,6 +5149,10 @@ var ArchivePlugin = class {
4989
5149
  scale = 1;
4990
5150
  applyTransform();
4991
5151
  },
5152
+ fitToWidth: () => {
5153
+ scale = 1;
5154
+ applyTransform();
5155
+ },
4992
5156
  resetZoom: () => {
4993
5157
  scale = 1;
4994
5158
  wrapper.scrollTop = 0;
@@ -5053,6 +5217,14 @@ var MarkdownPlugin = class {
5053
5217
  group: "zoom",
5054
5218
  execute: () => instance.resetZoom?.()
5055
5219
  },
5220
+ {
5221
+ id: "fit-width",
5222
+ icon: "fit-width",
5223
+ label: "Fit to Width",
5224
+ type: "button",
5225
+ group: "zoom",
5226
+ execute: () => instance.fitToWidth?.()
5227
+ },
5056
5228
  {
5057
5229
  id: "copy",
5058
5230
  icon: "copy",
@@ -5206,6 +5378,10 @@ var MarkdownPlugin = class {
5206
5378
  fontSize = 15;
5207
5379
  wrapper.style.fontSize = "15px";
5208
5380
  },
5381
+ fitToWidth: () => {
5382
+ fontSize = 15;
5383
+ wrapper.style.fontSize = "15px";
5384
+ },
5209
5385
  resetZoom: () => {
5210
5386
  fontSize = 15;
5211
5387
  wrapper.style.fontSize = "15px";
@@ -5313,6 +5489,14 @@ var PptxPlugin = class {
5313
5489
  group: "zoom",
5314
5490
  execute: () => instance.resetZoom?.()
5315
5491
  },
5492
+ {
5493
+ id: "fit-width",
5494
+ icon: "fit-width",
5495
+ label: "Fit to Width",
5496
+ type: "button",
5497
+ group: "zoom",
5498
+ execute: () => instance.fitToWidth?.()
5499
+ },
5316
5500
  {
5317
5501
  id: "rotate-cw",
5318
5502
  icon: "rotate-cw",
@@ -5396,12 +5580,17 @@ var PptxPlugin = class {
5396
5580
  ctx.container.innerHTML = "";
5397
5581
  ctx.container.style.overflow = "auto";
5398
5582
  ctx.container.appendChild(wrapper);
5399
- const calculateFitScale = () => {
5400
- const availW = Math.max(200, ctx.container.clientWidth - 48);
5401
- const availH = Math.max(200, ctx.container.clientHeight - 72);
5583
+ let fitMode = ctx?.options?.fitMode || "width";
5584
+ let isUserZoomed = false;
5585
+ const calculateFitScale = (mode = fitMode) => {
5586
+ const availW = Math.max(200, ctx.container.clientWidth - 32);
5587
+ const availH = Math.max(200, ctx.container.clientHeight - 32);
5402
5588
  const cW = canvas.offsetWidth || 1280;
5403
5589
  const cH = canvas.offsetHeight || 720;
5404
- return Math.min(1, Math.min(availW / cW, availH / cH));
5590
+ if (mode === "page") {
5591
+ return Math.min(2.5, Math.min(availW / cW, availH / cH));
5592
+ }
5593
+ return Math.min(2.5, availW / cW);
5405
5594
  };
5406
5595
  const applyTransform = () => {
5407
5596
  const cW = canvas.offsetWidth || 1280;
@@ -5420,14 +5609,24 @@ var PptxPlugin = class {
5420
5609
  try {
5421
5610
  await renderer.renderSlide(currentSlide - 1, canvas, 1280);
5422
5611
  ctx.emit("page-change", { page: currentSlide, totalPages: slideCount });
5423
- scale = calculateFitScale();
5612
+ if (!isUserZoomed) {
5613
+ scale = calculateFitScale(fitMode);
5614
+ }
5424
5615
  applyTransform();
5425
5616
  } catch (err) {
5426
5617
  console.error("[PptxPlugin] Failed to render slide:", err);
5427
5618
  }
5428
5619
  };
5429
5620
  await renderCurrentSlide();
5621
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
5622
+ if (!isUserZoomed) {
5623
+ scale = calculateFitScale(fitMode);
5624
+ applyTransform();
5625
+ }
5626
+ }) : null;
5627
+ ro?.observe(ctx.container);
5430
5628
  const cleanup = () => {
5629
+ ro?.disconnect();
5431
5630
  renderer.destroy();
5432
5631
  wrapper.remove();
5433
5632
  ctx.container.innerHTML = "";
@@ -5444,28 +5643,44 @@ var PptxPlugin = class {
5444
5643
  getPageCount: () => slideCount,
5445
5644
  getCurrentPage: () => currentSlide,
5446
5645
  zoomIn: () => {
5646
+ isUserZoomed = true;
5447
5647
  scale += 0.15;
5448
5648
  applyTransform();
5449
5649
  },
5450
5650
  zoomOut: () => {
5651
+ isUserZoomed = true;
5451
5652
  scale = Math.max(0.2, scale - 0.15);
5452
5653
  applyTransform();
5453
5654
  },
5454
5655
  getZoom: () => scale,
5455
5656
  setZoom: (level) => {
5657
+ isUserZoomed = true;
5456
5658
  scale = level;
5457
5659
  applyTransform();
5458
5660
  },
5459
5661
  fitToPage: () => {
5460
- scale = calculateFitScale();
5662
+ isUserZoomed = false;
5663
+ fitMode = "page";
5664
+ scale = calculateFitScale("page");
5665
+ rotation = 0;
5666
+ applyTransform();
5667
+ },
5668
+ fitToWidth: () => {
5669
+ isUserZoomed = false;
5670
+ fitMode = "width";
5671
+ scale = calculateFitScale("width");
5461
5672
  rotation = 0;
5462
5673
  applyTransform();
5463
5674
  },
5464
5675
  resetZoom: () => {
5465
- scale = calculateFitScale();
5676
+ isUserZoomed = false;
5677
+ fitMode = ctx?.options?.fitMode || "width";
5678
+ scale = calculateFitScale(fitMode);
5466
5679
  rotation = 0;
5467
5680
  wrapper.scrollTop = 0;
5468
5681
  wrapper.scrollLeft = 0;
5682
+ ctx.container.scrollTop = 0;
5683
+ ctx.container.scrollLeft = 0;
5469
5684
  applyTransform();
5470
5685
  },
5471
5686
  rotateCW: () => {
@@ -5910,6 +6125,14 @@ var RtfPlugin = class {
5910
6125
  group: "zoom",
5911
6126
  execute: () => instance.resetZoom?.()
5912
6127
  },
6128
+ {
6129
+ id: "fit-width",
6130
+ icon: "fit-width",
6131
+ label: "Fit to Width",
6132
+ type: "button",
6133
+ group: "zoom",
6134
+ execute: () => instance.fitToWidth?.()
6135
+ },
5913
6136
  {
5914
6137
  id: "rotate-cw",
5915
6138
  icon: "rotate-cw",
@@ -6398,6 +6621,13 @@ var RtfPlugin = class {
6398
6621
  applyTransform();
6399
6622
  },
6400
6623
  fitToPage: () => {
6624
+ isUserZoomed = false;
6625
+ fitMode = "page";
6626
+ scale = calculateFitScale("page");
6627
+ rotation = 0;
6628
+ applyTransform();
6629
+ },
6630
+ fitToWidth: () => {
6401
6631
  isUserZoomed = false;
6402
6632
  fitMode = "width";
6403
6633
  scale = calculateFitScale("width");
@@ -6406,8 +6636,8 @@ var RtfPlugin = class {
6406
6636
  },
6407
6637
  resetZoom: () => {
6408
6638
  isUserZoomed = false;
6409
- fitMode = "width";
6410
- scale = calculateFitScale("width");
6639
+ fitMode = ctx?.options?.fitMode || "width";
6640
+ scale = calculateFitScale(fitMode);
6411
6641
  rotation = 0;
6412
6642
  ctx.container.scrollTop = 0;
6413
6643
  ctx.container.scrollLeft = 0;
@@ -6488,6 +6718,14 @@ var HtmlPreviewPlugin = class {
6488
6718
  group: "zoom",
6489
6719
  execute: () => instance.resetZoom?.()
6490
6720
  },
6721
+ {
6722
+ id: "fit-width",
6723
+ icon: "fit-width",
6724
+ label: "Fit to Width",
6725
+ type: "button",
6726
+ group: "zoom",
6727
+ execute: () => instance.fitToWidth?.()
6728
+ },
6491
6729
  {
6492
6730
  id: "copy",
6493
6731
  icon: "copy",
@@ -6581,6 +6819,10 @@ var HtmlPreviewPlugin = class {
6581
6819
  scale = 1;
6582
6820
  applyTransform();
6583
6821
  },
6822
+ fitToWidth: () => {
6823
+ scale = 1;
6824
+ applyTransform();
6825
+ },
6584
6826
  resetZoom: () => {
6585
6827
  scale = 1;
6586
6828
  ctx.container.scrollTop = 0;
@@ -6695,6 +6937,14 @@ var OpenDocumentPlugin = class {
6695
6937
  group: "zoom",
6696
6938
  execute: () => instance.resetZoom?.()
6697
6939
  },
6940
+ {
6941
+ id: "fit-width",
6942
+ icon: "fit-width",
6943
+ label: "Fit to Width",
6944
+ type: "button",
6945
+ group: "zoom",
6946
+ execute: () => instance.fitToWidth?.()
6947
+ },
6698
6948
  {
6699
6949
  id: "rotate-cw",
6700
6950
  icon: "rotate-cw",
@@ -6811,7 +7061,10 @@ var OpenDocumentPlugin = class {
6811
7061
  const sW = availW / elW;
6812
7062
  const sH = availH / (isPresentation ? 540 : singlePageH);
6813
7063
  if (isPresentation) {
6814
- return Math.min(2.5, Math.min(sW, sH));
7064
+ if (mode === "page") {
7065
+ return Math.min(2.5, Math.min(sW, sH));
7066
+ }
7067
+ return Math.min(2.5, sW);
6815
7068
  }
6816
7069
  if (mode === "page") {
6817
7070
  return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
@@ -6980,6 +7233,13 @@ var OpenDocumentPlugin = class {
6980
7233
  applyTransform();
6981
7234
  },
6982
7235
  fitToPage: () => {
7236
+ isUserZoomed = false;
7237
+ fitMode = "page";
7238
+ scale = calculateFitScale("page");
7239
+ rotation = 0;
7240
+ applyTransform();
7241
+ },
7242
+ fitToWidth: () => {
6983
7243
  isUserZoomed = false;
6984
7244
  fitMode = "width";
6985
7245
  scale = calculateFitScale("width");
@@ -6988,8 +7248,8 @@ var OpenDocumentPlugin = class {
6988
7248
  },
6989
7249
  resetZoom: () => {
6990
7250
  isUserZoomed = false;
6991
- fitMode = "width";
6992
- scale = calculateFitScale("width");
7251
+ fitMode = ctx?.options?.fitMode || "width";
7252
+ scale = calculateFitScale(fitMode);
6993
7253
  rotation = 0;
6994
7254
  container.scrollTop = 0;
6995
7255
  container.scrollLeft = 0;
@@ -7346,6 +7606,14 @@ var DocPlugin = class {
7346
7606
  group: "zoom",
7347
7607
  execute: () => instance.resetZoom?.()
7348
7608
  },
7609
+ {
7610
+ id: "fit-width",
7611
+ icon: "fit-width",
7612
+ label: "Fit to Width",
7613
+ type: "button",
7614
+ group: "zoom",
7615
+ execute: () => instance.fitToWidth?.()
7616
+ },
7349
7617
  {
7350
7618
  id: "rotate-cw",
7351
7619
  icon: "rotate-cw",
@@ -7567,6 +7835,13 @@ var DocPlugin = class {
7567
7835
  applyTransform();
7568
7836
  },
7569
7837
  fitToPage: () => {
7838
+ isUserZoomed = false;
7839
+ fitMode = "page";
7840
+ scale = calculateFitScale("page");
7841
+ rotation = 0;
7842
+ applyTransform();
7843
+ },
7844
+ fitToWidth: () => {
7570
7845
  isUserZoomed = false;
7571
7846
  fitMode = "width";
7572
7847
  scale = calculateFitScale("width");
@@ -7575,8 +7850,8 @@ var DocPlugin = class {
7575
7850
  },
7576
7851
  resetZoom: () => {
7577
7852
  isUserZoomed = false;
7578
- fitMode = "width";
7579
- scale = calculateFitScale("width");
7853
+ fitMode = ctx?.options?.fitMode || "width";
7854
+ scale = calculateFitScale(fitMode);
7580
7855
  rotation = 0;
7581
7856
  container.scrollTop = 0;
7582
7857
  container.scrollLeft = 0;
@@ -8072,6 +8347,14 @@ var PptPlugin = class {
8072
8347
  group: "zoom",
8073
8348
  execute: () => instance.resetZoom?.()
8074
8349
  },
8350
+ {
8351
+ id: "fit-width",
8352
+ icon: "fit-width",
8353
+ label: "Fit to Width",
8354
+ type: "button",
8355
+ group: "zoom",
8356
+ execute: () => instance.fitToWidth?.()
8357
+ },
8075
8358
  {
8076
8359
  id: "rotate-cw",
8077
8360
  icon: "rotate-cw",
@@ -8146,10 +8429,15 @@ var PptPlugin = class {
8146
8429
  let currentSlide = 1;
8147
8430
  let slides = [];
8148
8431
  const createdBlobUrls = [];
8149
- const calculateFitScale = () => {
8150
- const availW = Math.max(200, container.clientWidth - 48);
8151
- const availH = Math.max(200, container.clientHeight - 72);
8152
- return Math.min(1, Math.min(availW / 960, availH / 540));
8432
+ let fitMode = ctx?.options?.fitMode || "width";
8433
+ let isUserZoomed = false;
8434
+ const calculateFitScale = (mode = fitMode) => {
8435
+ const availW = Math.max(200, container.clientWidth - 32);
8436
+ const availH = Math.max(200, container.clientHeight - 32);
8437
+ if (mode === "page") {
8438
+ return Math.min(2.5, Math.min(availW / 960, availH / 540));
8439
+ }
8440
+ return Math.min(2.5, availW / 960);
8153
8441
  };
8154
8442
  const applyTransform = () => {
8155
8443
  const cW = 960;
@@ -8164,8 +8452,15 @@ var PptPlugin = class {
8164
8452
  slideCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
8165
8453
  slideCard.style.transformOrigin = "center center";
8166
8454
  };
8455
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
8456
+ if (!isUserZoomed) {
8457
+ scale = calculateFitScale(fitMode);
8458
+ applyTransform();
8459
+ }
8460
+ }) : null;
8461
+ ro?.observe(container);
8167
8462
  setTimeout(() => {
8168
- scale = calculateFitScale();
8463
+ scale = calculateFitScale(fitMode);
8169
8464
  applyTransform();
8170
8465
  }, 60);
8171
8466
  try {
@@ -8258,6 +8553,7 @@ var PptPlugin = class {
8258
8553
  };
8259
8554
  renderSlide(1);
8260
8555
  const cleanup = () => {
8556
+ ro?.disconnect();
8261
8557
  for (const u of createdBlobUrls) {
8262
8558
  URL.revokeObjectURL(u);
8263
8559
  }
@@ -8268,28 +8564,44 @@ var PptPlugin = class {
8268
8564
  return {
8269
8565
  destroy: cleanup,
8270
8566
  zoomIn: () => {
8567
+ isUserZoomed = true;
8271
8568
  scale += 0.15;
8272
8569
  applyTransform();
8273
8570
  },
8274
8571
  zoomOut: () => {
8572
+ isUserZoomed = true;
8275
8573
  scale = Math.max(0.2, scale - 0.15);
8276
8574
  applyTransform();
8277
8575
  },
8278
8576
  getZoom: () => scale,
8279
8577
  setZoom: (level) => {
8578
+ isUserZoomed = true;
8280
8579
  scale = level;
8281
8580
  applyTransform();
8282
8581
  },
8283
8582
  fitToPage: () => {
8284
- scale = calculateFitScale();
8583
+ isUserZoomed = false;
8584
+ fitMode = "page";
8585
+ scale = calculateFitScale("page");
8586
+ rotation = 0;
8587
+ applyTransform();
8588
+ },
8589
+ fitToWidth: () => {
8590
+ isUserZoomed = false;
8591
+ fitMode = "width";
8592
+ scale = calculateFitScale("width");
8285
8593
  rotation = 0;
8286
8594
  applyTransform();
8287
8595
  },
8288
8596
  resetZoom: () => {
8289
- scale = calculateFitScale();
8597
+ isUserZoomed = false;
8598
+ fitMode = ctx?.options?.fitMode || "width";
8599
+ scale = calculateFitScale(fitMode);
8290
8600
  rotation = 0;
8291
8601
  container.scrollTop = 0;
8292
8602
  container.scrollLeft = 0;
8603
+ ctx.container.scrollTop = 0;
8604
+ ctx.container.scrollLeft = 0;
8293
8605
  applyTransform();
8294
8606
  },
8295
8607
  rotateCW: () => {