@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.js CHANGED
@@ -1056,6 +1056,10 @@ var FilePreviewViewer = class _FilePreviewViewer {
1056
1056
  * Preview a file in the given container element.
1057
1057
  */
1058
1058
  async preview(container, source, options = {}) {
1059
+ options = {
1060
+ ...options,
1061
+ fitMode: options.fitMode ?? "width"
1062
+ };
1059
1063
  this.currentOptions = options;
1060
1064
  this.abort();
1061
1065
  this.abortController = new AbortController();
@@ -1104,8 +1108,8 @@ var FilePreviewViewer = class _FilePreviewViewer {
1104
1108
  this.activeInstance = instance;
1105
1109
  instance.openInSeparateWindow = () => this.openInSeparateWindow();
1106
1110
  instance.toggleThumbnails = () => this.toggleThumbnails();
1107
- if (!instance.resetZoom && instance.fitToPage) {
1108
- instance.resetZoom = () => instance.fitToPage?.();
1111
+ if (!instance.resetZoom) {
1112
+ instance.resetZoom = () => instance.fitToWidth ? instance.fitToWidth() : instance.fitToPage?.();
1109
1113
  }
1110
1114
  this.hideLoading();
1111
1115
  this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
@@ -1502,7 +1506,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1502
1506
  this.wrapperEl?.classList.toggle("fp-fullscreen-active");
1503
1507
  }
1504
1508
  setTimeout(() => {
1505
- this.activeInstance?.fitToPage?.();
1509
+ this.triggerAutoFit();
1506
1510
  }, 120);
1507
1511
  }
1508
1512
  /**
@@ -1629,18 +1633,29 @@ var FilePreviewViewer = class _FilePreviewViewer {
1629
1633
  this.thumbnailPanel = new ThumbnailPanel(thumbnailEl, (isOpen) => {
1630
1634
  this.toolbar?.setActionActive("thumbnails", isOpen);
1631
1635
  setTimeout(() => {
1632
- this.activeInstance?.fitToPage?.();
1636
+ this.triggerAutoFit();
1633
1637
  }, 260);
1634
1638
  });
1635
1639
  this.setupKeyboardShortcuts();
1636
1640
  this.setupDragAndDrop(container, options);
1637
1641
  this.setupResizeAndFullscreenListeners();
1638
1642
  }
1643
+ triggerAutoFit() {
1644
+ if (!this.activeInstance) return;
1645
+ const mode = this.currentOptions?.fitMode ?? "width";
1646
+ if (mode === "width" && this.activeInstance.fitToWidth) {
1647
+ this.activeInstance.fitToWidth();
1648
+ } else if (this.activeInstance.fitToPage) {
1649
+ this.activeInstance.fitToPage();
1650
+ } else if (this.activeInstance.resetZoom) {
1651
+ this.activeInstance.resetZoom();
1652
+ }
1653
+ }
1639
1654
  setupResizeAndFullscreenListeners() {
1640
1655
  if (this.fullscreenHandler) return;
1641
1656
  this.fullscreenHandler = () => {
1642
1657
  setTimeout(() => {
1643
- this.activeInstance?.fitToPage?.();
1658
+ this.triggerAutoFit();
1644
1659
  }, 100);
1645
1660
  };
1646
1661
  document.addEventListener("fullscreenchange", this.fullscreenHandler);
@@ -1648,7 +1663,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1648
1663
  if (typeof ResizeObserver !== "undefined" && this.contentEl) {
1649
1664
  this.resizeObserver = new ResizeObserver(() => {
1650
1665
  if (this.activeInstance && (!this.activeInstance.getZoom || Math.abs((this.activeInstance.getZoom?.() ?? 1) - 1) < 0.05)) {
1651
- this.activeInstance.fitToPage?.();
1666
+ this.triggerAutoFit();
1652
1667
  }
1653
1668
  });
1654
1669
  this.resizeObserver.observe(this.contentEl);
@@ -1676,7 +1691,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1676
1691
  } else if (e.key === "-" || e.key === "_") {
1677
1692
  this.activeInstance.zoomOut?.();
1678
1693
  } else if (e.key === "0") {
1679
- this.activeInstance.fitToPage?.();
1694
+ this.resetZoom();
1680
1695
  } else if (e.key === "r" || e.key === "R") {
1681
1696
  this.activeInstance.rotateCW?.();
1682
1697
  } else if (e.key === "f" || e.key === "F") {
@@ -2299,7 +2314,7 @@ var PdfPlugin = class {
2299
2314
  renderPage(currentPage);
2300
2315
  },
2301
2316
  resetZoom: () => {
2302
- fitMode = "page";
2317
+ fitMode = ctx?.options?.fitMode || "width";
2303
2318
  zoomScale = 1;
2304
2319
  rotation = 0;
2305
2320
  container.scrollTop = 0;
@@ -2483,6 +2498,16 @@ var MediaPlugin = class {
2483
2498
  instance.resetZoom?.();
2484
2499
  }
2485
2500
  },
2501
+ {
2502
+ id: "fit-width",
2503
+ icon: "fit-width",
2504
+ label: "Fit to Width",
2505
+ type: "button",
2506
+ group: "zoom",
2507
+ execute: () => {
2508
+ instance.fitToWidth?.();
2509
+ }
2510
+ },
2486
2511
  {
2487
2512
  id: "rotate-cw",
2488
2513
  icon: "rotate-cw",
@@ -2579,9 +2604,30 @@ var MediaPlugin = class {
2579
2604
  const isVideo = mimeType.startsWith("video/") || VIDEO_EXTS.includes(ctx.metadata.extension || "");
2580
2605
  const isAudio = mimeType.startsWith("audio/") || AUDIO_EXTS.includes(ctx.metadata.extension || "");
2581
2606
  let url = "";
2607
+ let naturalW = 800;
2608
+ let naturalH = 600;
2582
2609
  if (ctx.metadata.extension === ".svg" || mimeType === "image/svg+xml") {
2583
2610
  const decoder = new TextDecoder("utf-8");
2584
2611
  const svgText = decoder.decode(ctx.buffer);
2612
+ try {
2613
+ const vbMatch = svgText.match(/viewBox=["']\s*([0-9.-]+)\s+([0-9.-]+)\s+([0-9.-]+)\s+([0-9.-]+)\s*["']/i);
2614
+ if (vbMatch) {
2615
+ const vw = parseFloat(vbMatch[3]);
2616
+ const vh = parseFloat(vbMatch[4]);
2617
+ if (vw > 0 && vh > 0) {
2618
+ naturalW = vw;
2619
+ naturalH = vh;
2620
+ }
2621
+ } else {
2622
+ const wMatch = svgText.match(/width=["']([0-9.]+)(?:px)?["']/i);
2623
+ const hMatch = svgText.match(/height=["']([0-9.]+)(?:px)?["']/i);
2624
+ if (wMatch && hMatch) {
2625
+ naturalW = parseFloat(wMatch[1]) || naturalW;
2626
+ naturalH = parseFloat(hMatch[1]) || naturalH;
2627
+ }
2628
+ }
2629
+ } catch {
2630
+ }
2585
2631
  const blob = new Blob([svgText], { type: "image/svg+xml" });
2586
2632
  url = URL.createObjectURL(blob);
2587
2633
  } else {
@@ -2629,7 +2675,7 @@ var MediaPlugin = class {
2629
2675
  scrollWrapper.style.width = "max-content";
2630
2676
  scrollWrapper.style.height = "max-content";
2631
2677
  scrollWrapper.style.display = "flex";
2632
- scrollWrapper.style.justifyContent = "center";
2678
+ scrollWrapper.style.flexDirection = "column";
2633
2679
  scrollWrapper.style.alignItems = "center";
2634
2680
  scrollWrapper.style.padding = "16px";
2635
2681
  scrollWrapper.style.boxSizing = "border-box";
@@ -2640,40 +2686,54 @@ var MediaPlugin = class {
2640
2686
  sizer.style.display = "flex";
2641
2687
  sizer.style.justifyContent = "center";
2642
2688
  sizer.style.alignItems = "center";
2689
+ sizer.style.margin = "auto 0";
2643
2690
  sizer.appendChild(element);
2644
2691
  scrollWrapper.appendChild(sizer);
2645
2692
  ctx.container.style.overflow = "auto";
2646
2693
  ctx.container.style.padding = "0";
2647
2694
  ctx.container.innerHTML = "";
2648
2695
  ctx.container.appendChild(scrollWrapper);
2649
- let naturalW = 800;
2650
- let naturalH = 600;
2651
- let baseW = 800;
2652
- let baseH = 600;
2696
+ let baseW = naturalW;
2697
+ let baseH = naturalH;
2698
+ let fitMode = ctx?.options?.fitMode || "width";
2699
+ let isUserZoomed = false;
2653
2700
  const updateBaseDimensions = () => {
2654
2701
  if (!isImage) return;
2655
2702
  const img = element;
2656
- naturalW = img.naturalWidth || naturalW;
2657
- naturalH = img.naturalHeight || naturalH;
2658
- const availW = Math.max(100, ctx.container.clientWidth - 48);
2659
- const availH = Math.max(100, ctx.container.clientHeight - 48);
2660
- const fitRatio = Math.min(1, availW / naturalW, availH / naturalH);
2703
+ if (img.naturalWidth > 0 && img.naturalHeight > 0) {
2704
+ naturalW = img.naturalWidth;
2705
+ naturalH = img.naturalHeight;
2706
+ }
2707
+ const availW = Math.max(100, ctx.container.clientWidth - 32);
2708
+ const availH = Math.max(100, ctx.container.clientHeight - 32);
2709
+ let fitRatio;
2710
+ if (fitMode === "page") {
2711
+ fitRatio = Math.min(availW / naturalW, availH / naturalH);
2712
+ } else {
2713
+ fitRatio = availW / naturalW;
2714
+ }
2661
2715
  baseW = Math.max(1, Math.round(naturalW * fitRatio));
2662
2716
  baseH = Math.max(1, Math.round(naturalH * fitRatio));
2663
2717
  };
2664
2718
  const applyTransform = () => {
2665
2719
  if (isImage) {
2666
- updateBaseDimensions();
2720
+ if (!isUserZoomed) {
2721
+ updateBaseDimensions();
2722
+ }
2723
+ const availH = Math.max(100, ctx.container.clientHeight - 32);
2667
2724
  const isRotated90 = rotation % 180 !== 0;
2668
2725
  const boxW = Math.round((isRotated90 ? baseH : baseW) * currentZoom);
2669
2726
  const boxH = Math.round((isRotated90 ? baseW : baseH) * currentZoom);
2670
2727
  sizer.style.width = `${boxW}px`;
2671
2728
  sizer.style.height = `${boxH}px`;
2672
- element.style.width = `${baseW}px`;
2673
- element.style.height = `${baseH}px`;
2729
+ sizer.style.margin = boxH < availH ? "auto 0" : "0";
2730
+ const imgW = isRotated90 ? boxH : boxW;
2731
+ const imgH = isRotated90 ? boxW : boxH;
2732
+ element.style.width = `${imgW}px`;
2733
+ element.style.height = `${imgH}px`;
2674
2734
  element.style.maxWidth = "none";
2675
2735
  element.style.maxHeight = "none";
2676
- element.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
2736
+ element.style.transform = rotation ? `rotate(${rotation}deg)` : "none";
2677
2737
  element.style.transformOrigin = "center center";
2678
2738
  element.style.flexShrink = "0";
2679
2739
  } else {
@@ -2693,7 +2753,8 @@ var MediaPlugin = class {
2693
2753
  }
2694
2754
  }
2695
2755
  const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
2696
- if (currentZoom === 1) {
2756
+ if (!isUserZoomed && currentZoom === 1) {
2757
+ updateBaseDimensions();
2697
2758
  applyTransform();
2698
2759
  }
2699
2760
  }) : null;
@@ -2708,28 +2769,49 @@ var MediaPlugin = class {
2708
2769
  return {
2709
2770
  destroy: cleanup,
2710
2771
  zoomIn: isImage ? () => {
2711
- currentZoom += 0.1;
2772
+ isUserZoomed = true;
2773
+ currentZoom = Math.min(5, Math.round((currentZoom + 0.15) * 100) / 100);
2712
2774
  applyTransform();
2713
2775
  } : void 0,
2714
2776
  zoomOut: isImage ? () => {
2715
- currentZoom = Math.max(0.1, currentZoom - 0.1);
2777
+ isUserZoomed = true;
2778
+ currentZoom = Math.max(0.1, Math.round((currentZoom - 0.15) * 100) / 100);
2716
2779
  applyTransform();
2717
2780
  } : void 0,
2718
2781
  getZoom: isImage ? () => currentZoom : void 0,
2719
2782
  setZoom: isImage ? (level) => {
2783
+ isUserZoomed = true;
2720
2784
  currentZoom = level;
2721
2785
  applyTransform();
2722
2786
  } : void 0,
2723
2787
  fitToPage: isImage ? () => {
2788
+ isUserZoomed = false;
2789
+ fitMode = "page";
2724
2790
  currentZoom = 1;
2725
2791
  rotation = 0;
2792
+ ctx.container.scrollTop = 0;
2793
+ ctx.container.scrollLeft = 0;
2794
+ updateBaseDimensions();
2795
+ applyTransform();
2796
+ } : void 0,
2797
+ fitToWidth: isImage ? () => {
2798
+ isUserZoomed = false;
2799
+ fitMode = "width";
2800
+ currentZoom = 1;
2801
+ rotation = 0;
2802
+ ctx.container.scrollTop = 0;
2803
+ ctx.container.scrollLeft = 0;
2804
+ updateBaseDimensions();
2726
2805
  applyTransform();
2727
2806
  } : void 0,
2728
2807
  resetZoom: isImage ? () => {
2808
+ isUserZoomed = false;
2809
+ fitMode = ctx?.options?.fitMode || "width";
2729
2810
  currentZoom = 1;
2730
2811
  rotation = 0;
2731
2812
  ctx.container.scrollTop = 0;
2732
2813
  ctx.container.scrollLeft = 0;
2814
+ updateBaseDimensions();
2733
2815
  applyTransform();
2734
2816
  } : void 0,
2735
2817
  rotateCW: isImage || isVideo ? () => {
@@ -2856,6 +2938,14 @@ var DocxPlugin = class {
2856
2938
  group: "zoom",
2857
2939
  execute: () => instance.resetZoom?.()
2858
2940
  },
2941
+ {
2942
+ id: "fit-width",
2943
+ icon: "fit-width",
2944
+ label: "Fit to Width",
2945
+ type: "button",
2946
+ group: "zoom",
2947
+ execute: () => instance.fitToWidth?.()
2948
+ },
2859
2949
  {
2860
2950
  id: "rotate-cw",
2861
2951
  icon: "rotate-cw",
@@ -3115,13 +3205,19 @@ var DocxPlugin = class {
3115
3205
  }
3116
3206
  scale = 1;
3117
3207
  let rotation = 0;
3118
- let fitMode = "page";
3208
+ let fitMode = ctx?.options?.fitMode || "width";
3119
3209
  let isUserZoomed = false;
3120
- const calculateFitScale = (_mode = fitMode) => {
3210
+ const calculateFitScale = (mode = fitMode) => {
3121
3211
  const activeEl = pageElements[currentPage - 1] || wrapper.querySelector("section.docx") || wrapper;
3122
3212
  const elW = activeEl.offsetWidth || 816;
3213
+ const elH = activeEl.offsetHeight || 1056;
3123
3214
  const availW = Math.max(280, ctx.container.clientWidth - 32);
3215
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
3124
3216
  const sW = availW / elW;
3217
+ const sH = availH / elH;
3218
+ if (mode === "page") {
3219
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
3220
+ }
3125
3221
  return Math.max(0.35, Math.min(3, sW));
3126
3222
  };
3127
3223
  const applyTransform = () => {
@@ -3142,7 +3238,7 @@ var DocxPlugin = class {
3142
3238
  if (typeof ResizeObserver !== "undefined") {
3143
3239
  resizeObserver = new ResizeObserver(() => {
3144
3240
  if (!isUserZoomed) {
3145
- const newFit = calculateFitScale("page");
3241
+ const newFit = calculateFitScale(fitMode);
3146
3242
  if (Math.abs(scale - newFit) > 0.015) {
3147
3243
  scale = newFit;
3148
3244
  applyTransform();
@@ -3152,7 +3248,7 @@ var DocxPlugin = class {
3152
3248
  resizeObserver.observe(ctx.container);
3153
3249
  }
3154
3250
  setTimeout(() => {
3155
- scale = calculateFitScale("page");
3251
+ scale = calculateFitScale(fitMode);
3156
3252
  applyTransform();
3157
3253
  }, 40);
3158
3254
  const cleanup = () => {
@@ -3191,13 +3287,22 @@ var DocxPlugin = class {
3191
3287
  },
3192
3288
  fitToPage: () => {
3193
3289
  isUserZoomed = false;
3290
+ fitMode = "page";
3194
3291
  scale = calculateFitScale("page");
3195
3292
  rotation = 0;
3196
3293
  applyTransform();
3197
3294
  },
3295
+ fitToWidth: () => {
3296
+ isUserZoomed = false;
3297
+ fitMode = "width";
3298
+ scale = calculateFitScale("width");
3299
+ rotation = 0;
3300
+ applyTransform();
3301
+ },
3198
3302
  resetZoom: () => {
3199
3303
  isUserZoomed = false;
3200
- scale = calculateFitScale("page");
3304
+ fitMode = ctx?.options?.fitMode || "width";
3305
+ scale = calculateFitScale(fitMode);
3201
3306
  rotation = 0;
3202
3307
  ctx.container.scrollTop = 0;
3203
3308
  ctx.container.scrollLeft = 0;
@@ -3662,6 +3767,16 @@ var ExcelPlugin = class {
3662
3767
  instance.resetZoom?.();
3663
3768
  }
3664
3769
  },
3770
+ {
3771
+ id: "fit-width",
3772
+ icon: "fit-width",
3773
+ label: "Fit to Width",
3774
+ type: "button",
3775
+ group: "zoom",
3776
+ execute: () => {
3777
+ instance.fitToWidth?.();
3778
+ }
3779
+ },
3665
3780
  {
3666
3781
  id: "download",
3667
3782
  icon: "download",
@@ -3878,6 +3993,11 @@ var ExcelPlugin = class {
3878
3993
  scale = calculateFitScale();
3879
3994
  applyTransform();
3880
3995
  },
3996
+ fitToWidth: () => {
3997
+ isUserZoomed = false;
3998
+ scale = calculateFitScale();
3999
+ applyTransform();
4000
+ },
3881
4001
  resetZoom: () => {
3882
4002
  isUserZoomed = false;
3883
4003
  scale = calculateFitScale();
@@ -4025,6 +4145,16 @@ var CsvPlugin = class {
4025
4145
  instance.resetZoom?.();
4026
4146
  }
4027
4147
  },
4148
+ {
4149
+ id: "fit-width",
4150
+ icon: "fit-width",
4151
+ label: "Fit to Width",
4152
+ type: "button",
4153
+ group: "zoom",
4154
+ execute: () => {
4155
+ instance.fitToWidth?.();
4156
+ }
4157
+ },
4028
4158
  {
4029
4159
  id: "download",
4030
4160
  icon: "download",
@@ -4238,6 +4368,11 @@ var CsvPlugin = class {
4238
4368
  scale = calculateFitScale();
4239
4369
  applyScale();
4240
4370
  },
4371
+ fitToWidth: () => {
4372
+ isUserZoomed = false;
4373
+ scale = calculateFitScale();
4374
+ applyScale();
4375
+ },
4241
4376
  resetZoom: () => {
4242
4377
  isUserZoomed = false;
4243
4378
  scale = calculateFitScale();
@@ -4370,6 +4505,16 @@ var CodePlugin = class {
4370
4505
  instance.resetZoom?.();
4371
4506
  }
4372
4507
  },
4508
+ {
4509
+ id: "fit-width",
4510
+ icon: "fit-width",
4511
+ label: "Fit to Width",
4512
+ type: "button",
4513
+ group: "zoom",
4514
+ execute: () => {
4515
+ instance.fitToWidth?.();
4516
+ }
4517
+ },
4373
4518
  {
4374
4519
  id: "copy",
4375
4520
  icon: "copy",
@@ -4674,6 +4819,13 @@ var CodePlugin = class {
4674
4819
  zoomLevel = isTxt ? calculateTxtFit() : 1;
4675
4820
  updateTransform();
4676
4821
  },
4822
+ fitToWidth: () => {
4823
+ isUserZoomed = false;
4824
+ fontSize = 13;
4825
+ rotation = 0;
4826
+ zoomLevel = isTxt ? calculateTxtFit() : 1;
4827
+ updateTransform();
4828
+ },
4677
4829
  resetZoom: () => {
4678
4830
  isUserZoomed = false;
4679
4831
  fontSize = 13;
@@ -4758,6 +4910,14 @@ var ArchivePlugin = class {
4758
4910
  group: "zoom",
4759
4911
  execute: () => instance.resetZoom?.()
4760
4912
  },
4913
+ {
4914
+ id: "fit-width",
4915
+ icon: "fit-width",
4916
+ label: "Fit to Width",
4917
+ type: "button",
4918
+ group: "zoom",
4919
+ execute: () => instance.fitToWidth?.()
4920
+ },
4761
4921
  {
4762
4922
  id: "download",
4763
4923
  icon: "download",
@@ -4955,6 +5115,10 @@ var ArchivePlugin = class {
4955
5115
  scale = 1;
4956
5116
  applyTransform();
4957
5117
  },
5118
+ fitToWidth: () => {
5119
+ scale = 1;
5120
+ applyTransform();
5121
+ },
4958
5122
  resetZoom: () => {
4959
5123
  scale = 1;
4960
5124
  wrapper.scrollTop = 0;
@@ -5019,6 +5183,14 @@ var MarkdownPlugin = class {
5019
5183
  group: "zoom",
5020
5184
  execute: () => instance.resetZoom?.()
5021
5185
  },
5186
+ {
5187
+ id: "fit-width",
5188
+ icon: "fit-width",
5189
+ label: "Fit to Width",
5190
+ type: "button",
5191
+ group: "zoom",
5192
+ execute: () => instance.fitToWidth?.()
5193
+ },
5022
5194
  {
5023
5195
  id: "copy",
5024
5196
  icon: "copy",
@@ -5172,6 +5344,10 @@ var MarkdownPlugin = class {
5172
5344
  fontSize = 15;
5173
5345
  wrapper.style.fontSize = "15px";
5174
5346
  },
5347
+ fitToWidth: () => {
5348
+ fontSize = 15;
5349
+ wrapper.style.fontSize = "15px";
5350
+ },
5175
5351
  resetZoom: () => {
5176
5352
  fontSize = 15;
5177
5353
  wrapper.style.fontSize = "15px";
@@ -5279,6 +5455,14 @@ var PptxPlugin = class {
5279
5455
  group: "zoom",
5280
5456
  execute: () => instance.resetZoom?.()
5281
5457
  },
5458
+ {
5459
+ id: "fit-width",
5460
+ icon: "fit-width",
5461
+ label: "Fit to Width",
5462
+ type: "button",
5463
+ group: "zoom",
5464
+ execute: () => instance.fitToWidth?.()
5465
+ },
5282
5466
  {
5283
5467
  id: "rotate-cw",
5284
5468
  icon: "rotate-cw",
@@ -5362,12 +5546,17 @@ var PptxPlugin = class {
5362
5546
  ctx.container.innerHTML = "";
5363
5547
  ctx.container.style.overflow = "auto";
5364
5548
  ctx.container.appendChild(wrapper);
5365
- const calculateFitScale = () => {
5366
- const availW = Math.max(200, ctx.container.clientWidth - 48);
5367
- const availH = Math.max(200, ctx.container.clientHeight - 72);
5549
+ let fitMode = ctx?.options?.fitMode || "width";
5550
+ let isUserZoomed = false;
5551
+ const calculateFitScale = (mode = fitMode) => {
5552
+ const availW = Math.max(200, ctx.container.clientWidth - 32);
5553
+ const availH = Math.max(200, ctx.container.clientHeight - 32);
5368
5554
  const cW = canvas.offsetWidth || 1280;
5369
5555
  const cH = canvas.offsetHeight || 720;
5370
- return Math.min(1, Math.min(availW / cW, availH / cH));
5556
+ if (mode === "page") {
5557
+ return Math.min(2.5, Math.min(availW / cW, availH / cH));
5558
+ }
5559
+ return Math.min(2.5, availW / cW);
5371
5560
  };
5372
5561
  const applyTransform = () => {
5373
5562
  const cW = canvas.offsetWidth || 1280;
@@ -5386,14 +5575,24 @@ var PptxPlugin = class {
5386
5575
  try {
5387
5576
  await renderer.renderSlide(currentSlide - 1, canvas, 1280);
5388
5577
  ctx.emit("page-change", { page: currentSlide, totalPages: slideCount });
5389
- scale = calculateFitScale();
5578
+ if (!isUserZoomed) {
5579
+ scale = calculateFitScale(fitMode);
5580
+ }
5390
5581
  applyTransform();
5391
5582
  } catch (err) {
5392
5583
  console.error("[PptxPlugin] Failed to render slide:", err);
5393
5584
  }
5394
5585
  };
5395
5586
  await renderCurrentSlide();
5587
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
5588
+ if (!isUserZoomed) {
5589
+ scale = calculateFitScale(fitMode);
5590
+ applyTransform();
5591
+ }
5592
+ }) : null;
5593
+ ro?.observe(ctx.container);
5396
5594
  const cleanup = () => {
5595
+ ro?.disconnect();
5397
5596
  renderer.destroy();
5398
5597
  wrapper.remove();
5399
5598
  ctx.container.innerHTML = "";
@@ -5410,28 +5609,44 @@ var PptxPlugin = class {
5410
5609
  getPageCount: () => slideCount,
5411
5610
  getCurrentPage: () => currentSlide,
5412
5611
  zoomIn: () => {
5612
+ isUserZoomed = true;
5413
5613
  scale += 0.15;
5414
5614
  applyTransform();
5415
5615
  },
5416
5616
  zoomOut: () => {
5617
+ isUserZoomed = true;
5417
5618
  scale = Math.max(0.2, scale - 0.15);
5418
5619
  applyTransform();
5419
5620
  },
5420
5621
  getZoom: () => scale,
5421
5622
  setZoom: (level) => {
5623
+ isUserZoomed = true;
5422
5624
  scale = level;
5423
5625
  applyTransform();
5424
5626
  },
5425
5627
  fitToPage: () => {
5426
- scale = calculateFitScale();
5628
+ isUserZoomed = false;
5629
+ fitMode = "page";
5630
+ scale = calculateFitScale("page");
5631
+ rotation = 0;
5632
+ applyTransform();
5633
+ },
5634
+ fitToWidth: () => {
5635
+ isUserZoomed = false;
5636
+ fitMode = "width";
5637
+ scale = calculateFitScale("width");
5427
5638
  rotation = 0;
5428
5639
  applyTransform();
5429
5640
  },
5430
5641
  resetZoom: () => {
5431
- scale = calculateFitScale();
5642
+ isUserZoomed = false;
5643
+ fitMode = ctx?.options?.fitMode || "width";
5644
+ scale = calculateFitScale(fitMode);
5432
5645
  rotation = 0;
5433
5646
  wrapper.scrollTop = 0;
5434
5647
  wrapper.scrollLeft = 0;
5648
+ ctx.container.scrollTop = 0;
5649
+ ctx.container.scrollLeft = 0;
5435
5650
  applyTransform();
5436
5651
  },
5437
5652
  rotateCW: () => {
@@ -5876,6 +6091,14 @@ var RtfPlugin = class {
5876
6091
  group: "zoom",
5877
6092
  execute: () => instance.resetZoom?.()
5878
6093
  },
6094
+ {
6095
+ id: "fit-width",
6096
+ icon: "fit-width",
6097
+ label: "Fit to Width",
6098
+ type: "button",
6099
+ group: "zoom",
6100
+ execute: () => instance.fitToWidth?.()
6101
+ },
5879
6102
  {
5880
6103
  id: "rotate-cw",
5881
6104
  icon: "rotate-cw",
@@ -6364,6 +6587,13 @@ var RtfPlugin = class {
6364
6587
  applyTransform();
6365
6588
  },
6366
6589
  fitToPage: () => {
6590
+ isUserZoomed = false;
6591
+ fitMode = "page";
6592
+ scale = calculateFitScale("page");
6593
+ rotation = 0;
6594
+ applyTransform();
6595
+ },
6596
+ fitToWidth: () => {
6367
6597
  isUserZoomed = false;
6368
6598
  fitMode = "width";
6369
6599
  scale = calculateFitScale("width");
@@ -6372,8 +6602,8 @@ var RtfPlugin = class {
6372
6602
  },
6373
6603
  resetZoom: () => {
6374
6604
  isUserZoomed = false;
6375
- fitMode = "width";
6376
- scale = calculateFitScale("width");
6605
+ fitMode = ctx?.options?.fitMode || "width";
6606
+ scale = calculateFitScale(fitMode);
6377
6607
  rotation = 0;
6378
6608
  ctx.container.scrollTop = 0;
6379
6609
  ctx.container.scrollLeft = 0;
@@ -6454,6 +6684,14 @@ var HtmlPreviewPlugin = class {
6454
6684
  group: "zoom",
6455
6685
  execute: () => instance.resetZoom?.()
6456
6686
  },
6687
+ {
6688
+ id: "fit-width",
6689
+ icon: "fit-width",
6690
+ label: "Fit to Width",
6691
+ type: "button",
6692
+ group: "zoom",
6693
+ execute: () => instance.fitToWidth?.()
6694
+ },
6457
6695
  {
6458
6696
  id: "copy",
6459
6697
  icon: "copy",
@@ -6547,6 +6785,10 @@ var HtmlPreviewPlugin = class {
6547
6785
  scale = 1;
6548
6786
  applyTransform();
6549
6787
  },
6788
+ fitToWidth: () => {
6789
+ scale = 1;
6790
+ applyTransform();
6791
+ },
6550
6792
  resetZoom: () => {
6551
6793
  scale = 1;
6552
6794
  ctx.container.scrollTop = 0;
@@ -6661,6 +6903,14 @@ var OpenDocumentPlugin = class {
6661
6903
  group: "zoom",
6662
6904
  execute: () => instance.resetZoom?.()
6663
6905
  },
6906
+ {
6907
+ id: "fit-width",
6908
+ icon: "fit-width",
6909
+ label: "Fit to Width",
6910
+ type: "button",
6911
+ group: "zoom",
6912
+ execute: () => instance.fitToWidth?.()
6913
+ },
6664
6914
  {
6665
6915
  id: "rotate-cw",
6666
6916
  icon: "rotate-cw",
@@ -6777,7 +7027,10 @@ var OpenDocumentPlugin = class {
6777
7027
  const sW = availW / elW;
6778
7028
  const sH = availH / (isPresentation ? 540 : singlePageH);
6779
7029
  if (isPresentation) {
6780
- return Math.min(2.5, Math.min(sW, sH));
7030
+ if (mode === "page") {
7031
+ return Math.min(2.5, Math.min(sW, sH));
7032
+ }
7033
+ return Math.min(2.5, sW);
6781
7034
  }
6782
7035
  if (mode === "page") {
6783
7036
  return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
@@ -6946,6 +7199,13 @@ var OpenDocumentPlugin = class {
6946
7199
  applyTransform();
6947
7200
  },
6948
7201
  fitToPage: () => {
7202
+ isUserZoomed = false;
7203
+ fitMode = "page";
7204
+ scale = calculateFitScale("page");
7205
+ rotation = 0;
7206
+ applyTransform();
7207
+ },
7208
+ fitToWidth: () => {
6949
7209
  isUserZoomed = false;
6950
7210
  fitMode = "width";
6951
7211
  scale = calculateFitScale("width");
@@ -6954,8 +7214,8 @@ var OpenDocumentPlugin = class {
6954
7214
  },
6955
7215
  resetZoom: () => {
6956
7216
  isUserZoomed = false;
6957
- fitMode = "width";
6958
- scale = calculateFitScale("width");
7217
+ fitMode = ctx?.options?.fitMode || "width";
7218
+ scale = calculateFitScale(fitMode);
6959
7219
  rotation = 0;
6960
7220
  container.scrollTop = 0;
6961
7221
  container.scrollLeft = 0;
@@ -7312,6 +7572,14 @@ var DocPlugin = class {
7312
7572
  group: "zoom",
7313
7573
  execute: () => instance.resetZoom?.()
7314
7574
  },
7575
+ {
7576
+ id: "fit-width",
7577
+ icon: "fit-width",
7578
+ label: "Fit to Width",
7579
+ type: "button",
7580
+ group: "zoom",
7581
+ execute: () => instance.fitToWidth?.()
7582
+ },
7315
7583
  {
7316
7584
  id: "rotate-cw",
7317
7585
  icon: "rotate-cw",
@@ -7533,6 +7801,13 @@ var DocPlugin = class {
7533
7801
  applyTransform();
7534
7802
  },
7535
7803
  fitToPage: () => {
7804
+ isUserZoomed = false;
7805
+ fitMode = "page";
7806
+ scale = calculateFitScale("page");
7807
+ rotation = 0;
7808
+ applyTransform();
7809
+ },
7810
+ fitToWidth: () => {
7536
7811
  isUserZoomed = false;
7537
7812
  fitMode = "width";
7538
7813
  scale = calculateFitScale("width");
@@ -7541,8 +7816,8 @@ var DocPlugin = class {
7541
7816
  },
7542
7817
  resetZoom: () => {
7543
7818
  isUserZoomed = false;
7544
- fitMode = "width";
7545
- scale = calculateFitScale("width");
7819
+ fitMode = ctx?.options?.fitMode || "width";
7820
+ scale = calculateFitScale(fitMode);
7546
7821
  rotation = 0;
7547
7822
  container.scrollTop = 0;
7548
7823
  container.scrollLeft = 0;
@@ -8038,6 +8313,14 @@ var PptPlugin = class {
8038
8313
  group: "zoom",
8039
8314
  execute: () => instance.resetZoom?.()
8040
8315
  },
8316
+ {
8317
+ id: "fit-width",
8318
+ icon: "fit-width",
8319
+ label: "Fit to Width",
8320
+ type: "button",
8321
+ group: "zoom",
8322
+ execute: () => instance.fitToWidth?.()
8323
+ },
8041
8324
  {
8042
8325
  id: "rotate-cw",
8043
8326
  icon: "rotate-cw",
@@ -8112,10 +8395,15 @@ var PptPlugin = class {
8112
8395
  let currentSlide = 1;
8113
8396
  let slides = [];
8114
8397
  const createdBlobUrls = [];
8115
- const calculateFitScale = () => {
8116
- const availW = Math.max(200, container.clientWidth - 48);
8117
- const availH = Math.max(200, container.clientHeight - 72);
8118
- return Math.min(1, Math.min(availW / 960, availH / 540));
8398
+ let fitMode = ctx?.options?.fitMode || "width";
8399
+ let isUserZoomed = false;
8400
+ const calculateFitScale = (mode = fitMode) => {
8401
+ const availW = Math.max(200, container.clientWidth - 32);
8402
+ const availH = Math.max(200, container.clientHeight - 32);
8403
+ if (mode === "page") {
8404
+ return Math.min(2.5, Math.min(availW / 960, availH / 540));
8405
+ }
8406
+ return Math.min(2.5, availW / 960);
8119
8407
  };
8120
8408
  const applyTransform = () => {
8121
8409
  const cW = 960;
@@ -8130,8 +8418,15 @@ var PptPlugin = class {
8130
8418
  slideCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
8131
8419
  slideCard.style.transformOrigin = "center center";
8132
8420
  };
8421
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
8422
+ if (!isUserZoomed) {
8423
+ scale = calculateFitScale(fitMode);
8424
+ applyTransform();
8425
+ }
8426
+ }) : null;
8427
+ ro?.observe(container);
8133
8428
  setTimeout(() => {
8134
- scale = calculateFitScale();
8429
+ scale = calculateFitScale(fitMode);
8135
8430
  applyTransform();
8136
8431
  }, 60);
8137
8432
  try {
@@ -8224,6 +8519,7 @@ var PptPlugin = class {
8224
8519
  };
8225
8520
  renderSlide(1);
8226
8521
  const cleanup = () => {
8522
+ ro?.disconnect();
8227
8523
  for (const u of createdBlobUrls) {
8228
8524
  URL.revokeObjectURL(u);
8229
8525
  }
@@ -8234,28 +8530,44 @@ var PptPlugin = class {
8234
8530
  return {
8235
8531
  destroy: cleanup,
8236
8532
  zoomIn: () => {
8533
+ isUserZoomed = true;
8237
8534
  scale += 0.15;
8238
8535
  applyTransform();
8239
8536
  },
8240
8537
  zoomOut: () => {
8538
+ isUserZoomed = true;
8241
8539
  scale = Math.max(0.2, scale - 0.15);
8242
8540
  applyTransform();
8243
8541
  },
8244
8542
  getZoom: () => scale,
8245
8543
  setZoom: (level) => {
8544
+ isUserZoomed = true;
8246
8545
  scale = level;
8247
8546
  applyTransform();
8248
8547
  },
8249
8548
  fitToPage: () => {
8250
- scale = calculateFitScale();
8549
+ isUserZoomed = false;
8550
+ fitMode = "page";
8551
+ scale = calculateFitScale("page");
8552
+ rotation = 0;
8553
+ applyTransform();
8554
+ },
8555
+ fitToWidth: () => {
8556
+ isUserZoomed = false;
8557
+ fitMode = "width";
8558
+ scale = calculateFitScale("width");
8251
8559
  rotation = 0;
8252
8560
  applyTransform();
8253
8561
  },
8254
8562
  resetZoom: () => {
8255
- scale = calculateFitScale();
8563
+ isUserZoomed = false;
8564
+ fitMode = ctx?.options?.fitMode || "width";
8565
+ scale = calculateFitScale(fitMode);
8256
8566
  rotation = 0;
8257
8567
  container.scrollTop = 0;
8258
8568
  container.scrollLeft = 0;
8569
+ ctx.container.scrollTop = 0;
8570
+ ctx.container.scrollLeft = 0;
8259
8571
  applyTransform();
8260
8572
  },
8261
8573
  rotateCW: () => {