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