@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.js CHANGED
@@ -1140,6 +1140,10 @@ var FilePreviewViewer = class _FilePreviewViewer {
1140
1140
  * Preview a file in the given container element.
1141
1141
  */
1142
1142
  async preview(container, source, options = {}) {
1143
+ options = {
1144
+ ...options,
1145
+ fitMode: options.fitMode ?? "width"
1146
+ };
1143
1147
  this.currentOptions = options;
1144
1148
  this.abort();
1145
1149
  this.abortController = new AbortController();
@@ -1188,8 +1192,8 @@ var FilePreviewViewer = class _FilePreviewViewer {
1188
1192
  this.activeInstance = instance;
1189
1193
  instance.openInSeparateWindow = () => this.openInSeparateWindow();
1190
1194
  instance.toggleThumbnails = () => this.toggleThumbnails();
1191
- if (!instance.resetZoom && instance.fitToPage) {
1192
- instance.resetZoom = () => instance.fitToPage?.();
1195
+ if (!instance.resetZoom) {
1196
+ instance.resetZoom = () => instance.fitToWidth ? instance.fitToWidth() : instance.fitToPage?.();
1193
1197
  }
1194
1198
  this.hideLoading();
1195
1199
  this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
@@ -1586,7 +1590,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1586
1590
  this.wrapperEl?.classList.toggle("fp-fullscreen-active");
1587
1591
  }
1588
1592
  setTimeout(() => {
1589
- this.activeInstance?.fitToPage?.();
1593
+ this.triggerAutoFit();
1590
1594
  }, 120);
1591
1595
  }
1592
1596
  /**
@@ -1713,18 +1717,29 @@ var FilePreviewViewer = class _FilePreviewViewer {
1713
1717
  this.thumbnailPanel = new ThumbnailPanel(thumbnailEl, (isOpen) => {
1714
1718
  this.toolbar?.setActionActive("thumbnails", isOpen);
1715
1719
  setTimeout(() => {
1716
- this.activeInstance?.fitToPage?.();
1720
+ this.triggerAutoFit();
1717
1721
  }, 260);
1718
1722
  });
1719
1723
  this.setupKeyboardShortcuts();
1720
1724
  this.setupDragAndDrop(container, options);
1721
1725
  this.setupResizeAndFullscreenListeners();
1722
1726
  }
1727
+ triggerAutoFit() {
1728
+ if (!this.activeInstance) return;
1729
+ const mode = this.currentOptions?.fitMode ?? "width";
1730
+ if (mode === "width" && this.activeInstance.fitToWidth) {
1731
+ this.activeInstance.fitToWidth();
1732
+ } else if (this.activeInstance.fitToPage) {
1733
+ this.activeInstance.fitToPage();
1734
+ } else if (this.activeInstance.resetZoom) {
1735
+ this.activeInstance.resetZoom();
1736
+ }
1737
+ }
1723
1738
  setupResizeAndFullscreenListeners() {
1724
1739
  if (this.fullscreenHandler) return;
1725
1740
  this.fullscreenHandler = () => {
1726
1741
  setTimeout(() => {
1727
- this.activeInstance?.fitToPage?.();
1742
+ this.triggerAutoFit();
1728
1743
  }, 100);
1729
1744
  };
1730
1745
  document.addEventListener("fullscreenchange", this.fullscreenHandler);
@@ -1732,7 +1747,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1732
1747
  if (typeof ResizeObserver !== "undefined" && this.contentEl) {
1733
1748
  this.resizeObserver = new ResizeObserver(() => {
1734
1749
  if (this.activeInstance && (!this.activeInstance.getZoom || Math.abs((this.activeInstance.getZoom?.() ?? 1) - 1) < 0.05)) {
1735
- this.activeInstance.fitToPage?.();
1750
+ this.triggerAutoFit();
1736
1751
  }
1737
1752
  });
1738
1753
  this.resizeObserver.observe(this.contentEl);
@@ -1760,7 +1775,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1760
1775
  } else if (e.key === "-" || e.key === "_") {
1761
1776
  this.activeInstance.zoomOut?.();
1762
1777
  } else if (e.key === "0") {
1763
- this.activeInstance.fitToPage?.();
1778
+ this.resetZoom();
1764
1779
  } else if (e.key === "r" || e.key === "R") {
1765
1780
  this.activeInstance.rotateCW?.();
1766
1781
  } else if (e.key === "f" || e.key === "F") {
@@ -2383,7 +2398,7 @@ var PdfPlugin = class {
2383
2398
  renderPage(currentPage);
2384
2399
  },
2385
2400
  resetZoom: () => {
2386
- fitMode = "page";
2401
+ fitMode = ctx?.options?.fitMode || "width";
2387
2402
  zoomScale = 1;
2388
2403
  rotation = 0;
2389
2404
  container.scrollTop = 0;
@@ -2567,6 +2582,16 @@ var MediaPlugin = class {
2567
2582
  instance.resetZoom?.();
2568
2583
  }
2569
2584
  },
2585
+ {
2586
+ id: "fit-width",
2587
+ icon: "fit-width",
2588
+ label: "Fit to Width",
2589
+ type: "button",
2590
+ group: "zoom",
2591
+ execute: () => {
2592
+ instance.fitToWidth?.();
2593
+ }
2594
+ },
2570
2595
  {
2571
2596
  id: "rotate-cw",
2572
2597
  icon: "rotate-cw",
@@ -2663,9 +2688,30 @@ var MediaPlugin = class {
2663
2688
  const isVideo = mimeType.startsWith("video/") || VIDEO_EXTS.includes(ctx.metadata.extension || "");
2664
2689
  const isAudio = mimeType.startsWith("audio/") || AUDIO_EXTS.includes(ctx.metadata.extension || "");
2665
2690
  let url = "";
2691
+ let naturalW = 800;
2692
+ let naturalH = 600;
2666
2693
  if (ctx.metadata.extension === ".svg" || mimeType === "image/svg+xml") {
2667
2694
  const decoder = new TextDecoder("utf-8");
2668
2695
  const svgText = decoder.decode(ctx.buffer);
2696
+ try {
2697
+ const vbMatch = svgText.match(/viewBox=["']\s*([0-9.-]+)\s+([0-9.-]+)\s+([0-9.-]+)\s+([0-9.-]+)\s*["']/i);
2698
+ if (vbMatch) {
2699
+ const vw = parseFloat(vbMatch[3]);
2700
+ const vh = parseFloat(vbMatch[4]);
2701
+ if (vw > 0 && vh > 0) {
2702
+ naturalW = vw;
2703
+ naturalH = vh;
2704
+ }
2705
+ } else {
2706
+ const wMatch = svgText.match(/width=["']([0-9.]+)(?:px)?["']/i);
2707
+ const hMatch = svgText.match(/height=["']([0-9.]+)(?:px)?["']/i);
2708
+ if (wMatch && hMatch) {
2709
+ naturalW = parseFloat(wMatch[1]) || naturalW;
2710
+ naturalH = parseFloat(hMatch[1]) || naturalH;
2711
+ }
2712
+ }
2713
+ } catch {
2714
+ }
2669
2715
  const blob = new Blob([svgText], { type: "image/svg+xml" });
2670
2716
  url = URL.createObjectURL(blob);
2671
2717
  } else {
@@ -2713,7 +2759,7 @@ var MediaPlugin = class {
2713
2759
  scrollWrapper.style.width = "max-content";
2714
2760
  scrollWrapper.style.height = "max-content";
2715
2761
  scrollWrapper.style.display = "flex";
2716
- scrollWrapper.style.justifyContent = "center";
2762
+ scrollWrapper.style.flexDirection = "column";
2717
2763
  scrollWrapper.style.alignItems = "center";
2718
2764
  scrollWrapper.style.padding = "16px";
2719
2765
  scrollWrapper.style.boxSizing = "border-box";
@@ -2724,40 +2770,54 @@ var MediaPlugin = class {
2724
2770
  sizer.style.display = "flex";
2725
2771
  sizer.style.justifyContent = "center";
2726
2772
  sizer.style.alignItems = "center";
2773
+ sizer.style.margin = "auto 0";
2727
2774
  sizer.appendChild(element);
2728
2775
  scrollWrapper.appendChild(sizer);
2729
2776
  ctx.container.style.overflow = "auto";
2730
2777
  ctx.container.style.padding = "0";
2731
2778
  ctx.container.innerHTML = "";
2732
2779
  ctx.container.appendChild(scrollWrapper);
2733
- let naturalW = 800;
2734
- let naturalH = 600;
2735
- let baseW = 800;
2736
- let baseH = 600;
2780
+ let baseW = naturalW;
2781
+ let baseH = naturalH;
2782
+ let fitMode = ctx?.options?.fitMode || "width";
2783
+ let isUserZoomed = false;
2737
2784
  const updateBaseDimensions = () => {
2738
2785
  if (!isImage) return;
2739
2786
  const img = element;
2740
- naturalW = img.naturalWidth || naturalW;
2741
- naturalH = img.naturalHeight || naturalH;
2742
- const availW = Math.max(100, ctx.container.clientWidth - 48);
2743
- const availH = Math.max(100, ctx.container.clientHeight - 48);
2744
- const fitRatio = Math.min(1, availW / naturalW, availH / naturalH);
2787
+ if (img.naturalWidth > 0 && img.naturalHeight > 0) {
2788
+ naturalW = img.naturalWidth;
2789
+ naturalH = img.naturalHeight;
2790
+ }
2791
+ const availW = Math.max(100, ctx.container.clientWidth - 32);
2792
+ const availH = Math.max(100, ctx.container.clientHeight - 32);
2793
+ let fitRatio;
2794
+ if (fitMode === "page") {
2795
+ fitRatio = Math.min(availW / naturalW, availH / naturalH);
2796
+ } else {
2797
+ fitRatio = availW / naturalW;
2798
+ }
2745
2799
  baseW = Math.max(1, Math.round(naturalW * fitRatio));
2746
2800
  baseH = Math.max(1, Math.round(naturalH * fitRatio));
2747
2801
  };
2748
2802
  const applyTransform = () => {
2749
2803
  if (isImage) {
2750
- updateBaseDimensions();
2804
+ if (!isUserZoomed) {
2805
+ updateBaseDimensions();
2806
+ }
2807
+ const availH = Math.max(100, ctx.container.clientHeight - 32);
2751
2808
  const isRotated90 = rotation % 180 !== 0;
2752
2809
  const boxW = Math.round((isRotated90 ? baseH : baseW) * currentZoom);
2753
2810
  const boxH = Math.round((isRotated90 ? baseW : baseH) * currentZoom);
2754
2811
  sizer.style.width = `${boxW}px`;
2755
2812
  sizer.style.height = `${boxH}px`;
2756
- element.style.width = `${baseW}px`;
2757
- element.style.height = `${baseH}px`;
2813
+ sizer.style.margin = boxH < availH ? "auto 0" : "0";
2814
+ const imgW = isRotated90 ? boxH : boxW;
2815
+ const imgH = isRotated90 ? boxW : boxH;
2816
+ element.style.width = `${imgW}px`;
2817
+ element.style.height = `${imgH}px`;
2758
2818
  element.style.maxWidth = "none";
2759
2819
  element.style.maxHeight = "none";
2760
- element.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
2820
+ element.style.transform = rotation ? `rotate(${rotation}deg)` : "none";
2761
2821
  element.style.transformOrigin = "center center";
2762
2822
  element.style.flexShrink = "0";
2763
2823
  } else {
@@ -2777,7 +2837,8 @@ var MediaPlugin = class {
2777
2837
  }
2778
2838
  }
2779
2839
  const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
2780
- if (currentZoom === 1) {
2840
+ if (!isUserZoomed && currentZoom === 1) {
2841
+ updateBaseDimensions();
2781
2842
  applyTransform();
2782
2843
  }
2783
2844
  }) : null;
@@ -2792,28 +2853,49 @@ var MediaPlugin = class {
2792
2853
  return {
2793
2854
  destroy: cleanup,
2794
2855
  zoomIn: isImage ? () => {
2795
- currentZoom += 0.1;
2856
+ isUserZoomed = true;
2857
+ currentZoom = Math.min(5, Math.round((currentZoom + 0.15) * 100) / 100);
2796
2858
  applyTransform();
2797
2859
  } : void 0,
2798
2860
  zoomOut: isImage ? () => {
2799
- currentZoom = Math.max(0.1, currentZoom - 0.1);
2861
+ isUserZoomed = true;
2862
+ currentZoom = Math.max(0.1, Math.round((currentZoom - 0.15) * 100) / 100);
2800
2863
  applyTransform();
2801
2864
  } : void 0,
2802
2865
  getZoom: isImage ? () => currentZoom : void 0,
2803
2866
  setZoom: isImage ? (level) => {
2867
+ isUserZoomed = true;
2804
2868
  currentZoom = level;
2805
2869
  applyTransform();
2806
2870
  } : void 0,
2807
2871
  fitToPage: isImage ? () => {
2872
+ isUserZoomed = false;
2873
+ fitMode = "page";
2808
2874
  currentZoom = 1;
2809
2875
  rotation = 0;
2876
+ ctx.container.scrollTop = 0;
2877
+ ctx.container.scrollLeft = 0;
2878
+ updateBaseDimensions();
2879
+ applyTransform();
2880
+ } : void 0,
2881
+ fitToWidth: isImage ? () => {
2882
+ isUserZoomed = false;
2883
+ fitMode = "width";
2884
+ currentZoom = 1;
2885
+ rotation = 0;
2886
+ ctx.container.scrollTop = 0;
2887
+ ctx.container.scrollLeft = 0;
2888
+ updateBaseDimensions();
2810
2889
  applyTransform();
2811
2890
  } : void 0,
2812
2891
  resetZoom: isImage ? () => {
2892
+ isUserZoomed = false;
2893
+ fitMode = ctx?.options?.fitMode || "width";
2813
2894
  currentZoom = 1;
2814
2895
  rotation = 0;
2815
2896
  ctx.container.scrollTop = 0;
2816
2897
  ctx.container.scrollLeft = 0;
2898
+ updateBaseDimensions();
2817
2899
  applyTransform();
2818
2900
  } : void 0,
2819
2901
  rotateCW: isImage || isVideo ? () => {
@@ -2940,6 +3022,14 @@ var DocxPlugin = class {
2940
3022
  group: "zoom",
2941
3023
  execute: () => instance.resetZoom?.()
2942
3024
  },
3025
+ {
3026
+ id: "fit-width",
3027
+ icon: "fit-width",
3028
+ label: "Fit to Width",
3029
+ type: "button",
3030
+ group: "zoom",
3031
+ execute: () => instance.fitToWidth?.()
3032
+ },
2943
3033
  {
2944
3034
  id: "rotate-cw",
2945
3035
  icon: "rotate-cw",
@@ -3199,13 +3289,19 @@ var DocxPlugin = class {
3199
3289
  }
3200
3290
  scale = 1;
3201
3291
  let rotation = 0;
3202
- let fitMode = "page";
3292
+ let fitMode = ctx?.options?.fitMode || "width";
3203
3293
  let isUserZoomed = false;
3204
- const calculateFitScale = (_mode = fitMode) => {
3294
+ const calculateFitScale = (mode = fitMode) => {
3205
3295
  const activeEl = pageElements[currentPage - 1] || wrapper.querySelector("section.docx") || wrapper;
3206
3296
  const elW = activeEl.offsetWidth || 816;
3297
+ const elH = activeEl.offsetHeight || 1056;
3207
3298
  const availW = Math.max(280, ctx.container.clientWidth - 32);
3299
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
3208
3300
  const sW = availW / elW;
3301
+ const sH = availH / elH;
3302
+ if (mode === "page") {
3303
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
3304
+ }
3209
3305
  return Math.max(0.35, Math.min(3, sW));
3210
3306
  };
3211
3307
  const applyTransform = () => {
@@ -3226,7 +3322,7 @@ var DocxPlugin = class {
3226
3322
  if (typeof ResizeObserver !== "undefined") {
3227
3323
  resizeObserver = new ResizeObserver(() => {
3228
3324
  if (!isUserZoomed) {
3229
- const newFit = calculateFitScale("page");
3325
+ const newFit = calculateFitScale(fitMode);
3230
3326
  if (Math.abs(scale - newFit) > 0.015) {
3231
3327
  scale = newFit;
3232
3328
  applyTransform();
@@ -3236,7 +3332,7 @@ var DocxPlugin = class {
3236
3332
  resizeObserver.observe(ctx.container);
3237
3333
  }
3238
3334
  setTimeout(() => {
3239
- scale = calculateFitScale("page");
3335
+ scale = calculateFitScale(fitMode);
3240
3336
  applyTransform();
3241
3337
  }, 40);
3242
3338
  const cleanup = () => {
@@ -3275,13 +3371,22 @@ var DocxPlugin = class {
3275
3371
  },
3276
3372
  fitToPage: () => {
3277
3373
  isUserZoomed = false;
3374
+ fitMode = "page";
3278
3375
  scale = calculateFitScale("page");
3279
3376
  rotation = 0;
3280
3377
  applyTransform();
3281
3378
  },
3379
+ fitToWidth: () => {
3380
+ isUserZoomed = false;
3381
+ fitMode = "width";
3382
+ scale = calculateFitScale("width");
3383
+ rotation = 0;
3384
+ applyTransform();
3385
+ },
3282
3386
  resetZoom: () => {
3283
3387
  isUserZoomed = false;
3284
- scale = calculateFitScale("page");
3388
+ fitMode = ctx?.options?.fitMode || "width";
3389
+ scale = calculateFitScale(fitMode);
3285
3390
  rotation = 0;
3286
3391
  ctx.container.scrollTop = 0;
3287
3392
  ctx.container.scrollLeft = 0;
@@ -3746,6 +3851,16 @@ var ExcelPlugin = class {
3746
3851
  instance.resetZoom?.();
3747
3852
  }
3748
3853
  },
3854
+ {
3855
+ id: "fit-width",
3856
+ icon: "fit-width",
3857
+ label: "Fit to Width",
3858
+ type: "button",
3859
+ group: "zoom",
3860
+ execute: () => {
3861
+ instance.fitToWidth?.();
3862
+ }
3863
+ },
3749
3864
  {
3750
3865
  id: "download",
3751
3866
  icon: "download",
@@ -3962,6 +4077,11 @@ var ExcelPlugin = class {
3962
4077
  scale = calculateFitScale();
3963
4078
  applyTransform();
3964
4079
  },
4080
+ fitToWidth: () => {
4081
+ isUserZoomed = false;
4082
+ scale = calculateFitScale();
4083
+ applyTransform();
4084
+ },
3965
4085
  resetZoom: () => {
3966
4086
  isUserZoomed = false;
3967
4087
  scale = calculateFitScale();
@@ -4109,6 +4229,16 @@ var CsvPlugin = class {
4109
4229
  instance.resetZoom?.();
4110
4230
  }
4111
4231
  },
4232
+ {
4233
+ id: "fit-width",
4234
+ icon: "fit-width",
4235
+ label: "Fit to Width",
4236
+ type: "button",
4237
+ group: "zoom",
4238
+ execute: () => {
4239
+ instance.fitToWidth?.();
4240
+ }
4241
+ },
4112
4242
  {
4113
4243
  id: "download",
4114
4244
  icon: "download",
@@ -4322,6 +4452,11 @@ var CsvPlugin = class {
4322
4452
  scale = calculateFitScale();
4323
4453
  applyScale();
4324
4454
  },
4455
+ fitToWidth: () => {
4456
+ isUserZoomed = false;
4457
+ scale = calculateFitScale();
4458
+ applyScale();
4459
+ },
4325
4460
  resetZoom: () => {
4326
4461
  isUserZoomed = false;
4327
4462
  scale = calculateFitScale();
@@ -4454,6 +4589,16 @@ var CodePlugin = class {
4454
4589
  instance.resetZoom?.();
4455
4590
  }
4456
4591
  },
4592
+ {
4593
+ id: "fit-width",
4594
+ icon: "fit-width",
4595
+ label: "Fit to Width",
4596
+ type: "button",
4597
+ group: "zoom",
4598
+ execute: () => {
4599
+ instance.fitToWidth?.();
4600
+ }
4601
+ },
4457
4602
  {
4458
4603
  id: "copy",
4459
4604
  icon: "copy",
@@ -4758,6 +4903,13 @@ var CodePlugin = class {
4758
4903
  zoomLevel = isTxt ? calculateTxtFit() : 1;
4759
4904
  updateTransform();
4760
4905
  },
4906
+ fitToWidth: () => {
4907
+ isUserZoomed = false;
4908
+ fontSize = 13;
4909
+ rotation = 0;
4910
+ zoomLevel = isTxt ? calculateTxtFit() : 1;
4911
+ updateTransform();
4912
+ },
4761
4913
  resetZoom: () => {
4762
4914
  isUserZoomed = false;
4763
4915
  fontSize = 13;
@@ -4842,6 +4994,14 @@ var ArchivePlugin = class {
4842
4994
  group: "zoom",
4843
4995
  execute: () => instance.resetZoom?.()
4844
4996
  },
4997
+ {
4998
+ id: "fit-width",
4999
+ icon: "fit-width",
5000
+ label: "Fit to Width",
5001
+ type: "button",
5002
+ group: "zoom",
5003
+ execute: () => instance.fitToWidth?.()
5004
+ },
4845
5005
  {
4846
5006
  id: "download",
4847
5007
  icon: "download",
@@ -5039,6 +5199,10 @@ var ArchivePlugin = class {
5039
5199
  scale = 1;
5040
5200
  applyTransform();
5041
5201
  },
5202
+ fitToWidth: () => {
5203
+ scale = 1;
5204
+ applyTransform();
5205
+ },
5042
5206
  resetZoom: () => {
5043
5207
  scale = 1;
5044
5208
  wrapper.scrollTop = 0;
@@ -5103,6 +5267,14 @@ var MarkdownPlugin = class {
5103
5267
  group: "zoom",
5104
5268
  execute: () => instance.resetZoom?.()
5105
5269
  },
5270
+ {
5271
+ id: "fit-width",
5272
+ icon: "fit-width",
5273
+ label: "Fit to Width",
5274
+ type: "button",
5275
+ group: "zoom",
5276
+ execute: () => instance.fitToWidth?.()
5277
+ },
5106
5278
  {
5107
5279
  id: "copy",
5108
5280
  icon: "copy",
@@ -5256,6 +5428,10 @@ var MarkdownPlugin = class {
5256
5428
  fontSize = 15;
5257
5429
  wrapper.style.fontSize = "15px";
5258
5430
  },
5431
+ fitToWidth: () => {
5432
+ fontSize = 15;
5433
+ wrapper.style.fontSize = "15px";
5434
+ },
5259
5435
  resetZoom: () => {
5260
5436
  fontSize = 15;
5261
5437
  wrapper.style.fontSize = "15px";
@@ -5363,6 +5539,14 @@ var PptxPlugin = class {
5363
5539
  group: "zoom",
5364
5540
  execute: () => instance.resetZoom?.()
5365
5541
  },
5542
+ {
5543
+ id: "fit-width",
5544
+ icon: "fit-width",
5545
+ label: "Fit to Width",
5546
+ type: "button",
5547
+ group: "zoom",
5548
+ execute: () => instance.fitToWidth?.()
5549
+ },
5366
5550
  {
5367
5551
  id: "rotate-cw",
5368
5552
  icon: "rotate-cw",
@@ -5446,12 +5630,17 @@ var PptxPlugin = class {
5446
5630
  ctx.container.innerHTML = "";
5447
5631
  ctx.container.style.overflow = "auto";
5448
5632
  ctx.container.appendChild(wrapper);
5449
- const calculateFitScale = () => {
5450
- const availW = Math.max(200, ctx.container.clientWidth - 48);
5451
- const availH = Math.max(200, ctx.container.clientHeight - 72);
5633
+ let fitMode = ctx?.options?.fitMode || "width";
5634
+ let isUserZoomed = false;
5635
+ const calculateFitScale = (mode = fitMode) => {
5636
+ const availW = Math.max(200, ctx.container.clientWidth - 32);
5637
+ const availH = Math.max(200, ctx.container.clientHeight - 32);
5452
5638
  const cW = canvas.offsetWidth || 1280;
5453
5639
  const cH = canvas.offsetHeight || 720;
5454
- return Math.min(1, Math.min(availW / cW, availH / cH));
5640
+ if (mode === "page") {
5641
+ return Math.min(2.5, Math.min(availW / cW, availH / cH));
5642
+ }
5643
+ return Math.min(2.5, availW / cW);
5455
5644
  };
5456
5645
  const applyTransform = () => {
5457
5646
  const cW = canvas.offsetWidth || 1280;
@@ -5470,14 +5659,24 @@ var PptxPlugin = class {
5470
5659
  try {
5471
5660
  await renderer.renderSlide(currentSlide - 1, canvas, 1280);
5472
5661
  ctx.emit("page-change", { page: currentSlide, totalPages: slideCount });
5473
- scale = calculateFitScale();
5662
+ if (!isUserZoomed) {
5663
+ scale = calculateFitScale(fitMode);
5664
+ }
5474
5665
  applyTransform();
5475
5666
  } catch (err) {
5476
5667
  console.error("[PptxPlugin] Failed to render slide:", err);
5477
5668
  }
5478
5669
  };
5479
5670
  await renderCurrentSlide();
5671
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
5672
+ if (!isUserZoomed) {
5673
+ scale = calculateFitScale(fitMode);
5674
+ applyTransform();
5675
+ }
5676
+ }) : null;
5677
+ ro?.observe(ctx.container);
5480
5678
  const cleanup = () => {
5679
+ ro?.disconnect();
5481
5680
  renderer.destroy();
5482
5681
  wrapper.remove();
5483
5682
  ctx.container.innerHTML = "";
@@ -5494,28 +5693,44 @@ var PptxPlugin = class {
5494
5693
  getPageCount: () => slideCount,
5495
5694
  getCurrentPage: () => currentSlide,
5496
5695
  zoomIn: () => {
5696
+ isUserZoomed = true;
5497
5697
  scale += 0.15;
5498
5698
  applyTransform();
5499
5699
  },
5500
5700
  zoomOut: () => {
5701
+ isUserZoomed = true;
5501
5702
  scale = Math.max(0.2, scale - 0.15);
5502
5703
  applyTransform();
5503
5704
  },
5504
5705
  getZoom: () => scale,
5505
5706
  setZoom: (level) => {
5707
+ isUserZoomed = true;
5506
5708
  scale = level;
5507
5709
  applyTransform();
5508
5710
  },
5509
5711
  fitToPage: () => {
5510
- scale = calculateFitScale();
5712
+ isUserZoomed = false;
5713
+ fitMode = "page";
5714
+ scale = calculateFitScale("page");
5715
+ rotation = 0;
5716
+ applyTransform();
5717
+ },
5718
+ fitToWidth: () => {
5719
+ isUserZoomed = false;
5720
+ fitMode = "width";
5721
+ scale = calculateFitScale("width");
5511
5722
  rotation = 0;
5512
5723
  applyTransform();
5513
5724
  },
5514
5725
  resetZoom: () => {
5515
- scale = calculateFitScale();
5726
+ isUserZoomed = false;
5727
+ fitMode = ctx?.options?.fitMode || "width";
5728
+ scale = calculateFitScale(fitMode);
5516
5729
  rotation = 0;
5517
5730
  wrapper.scrollTop = 0;
5518
5731
  wrapper.scrollLeft = 0;
5732
+ ctx.container.scrollTop = 0;
5733
+ ctx.container.scrollLeft = 0;
5519
5734
  applyTransform();
5520
5735
  },
5521
5736
  rotateCW: () => {
@@ -5960,6 +6175,14 @@ var RtfPlugin = class {
5960
6175
  group: "zoom",
5961
6176
  execute: () => instance.resetZoom?.()
5962
6177
  },
6178
+ {
6179
+ id: "fit-width",
6180
+ icon: "fit-width",
6181
+ label: "Fit to Width",
6182
+ type: "button",
6183
+ group: "zoom",
6184
+ execute: () => instance.fitToWidth?.()
6185
+ },
5963
6186
  {
5964
6187
  id: "rotate-cw",
5965
6188
  icon: "rotate-cw",
@@ -6448,6 +6671,13 @@ var RtfPlugin = class {
6448
6671
  applyTransform();
6449
6672
  },
6450
6673
  fitToPage: () => {
6674
+ isUserZoomed = false;
6675
+ fitMode = "page";
6676
+ scale = calculateFitScale("page");
6677
+ rotation = 0;
6678
+ applyTransform();
6679
+ },
6680
+ fitToWidth: () => {
6451
6681
  isUserZoomed = false;
6452
6682
  fitMode = "width";
6453
6683
  scale = calculateFitScale("width");
@@ -6456,8 +6686,8 @@ var RtfPlugin = class {
6456
6686
  },
6457
6687
  resetZoom: () => {
6458
6688
  isUserZoomed = false;
6459
- fitMode = "width";
6460
- scale = calculateFitScale("width");
6689
+ fitMode = ctx?.options?.fitMode || "width";
6690
+ scale = calculateFitScale(fitMode);
6461
6691
  rotation = 0;
6462
6692
  ctx.container.scrollTop = 0;
6463
6693
  ctx.container.scrollLeft = 0;
@@ -6538,6 +6768,14 @@ var HtmlPreviewPlugin = class {
6538
6768
  group: "zoom",
6539
6769
  execute: () => instance.resetZoom?.()
6540
6770
  },
6771
+ {
6772
+ id: "fit-width",
6773
+ icon: "fit-width",
6774
+ label: "Fit to Width",
6775
+ type: "button",
6776
+ group: "zoom",
6777
+ execute: () => instance.fitToWidth?.()
6778
+ },
6541
6779
  {
6542
6780
  id: "copy",
6543
6781
  icon: "copy",
@@ -6631,6 +6869,10 @@ var HtmlPreviewPlugin = class {
6631
6869
  scale = 1;
6632
6870
  applyTransform();
6633
6871
  },
6872
+ fitToWidth: () => {
6873
+ scale = 1;
6874
+ applyTransform();
6875
+ },
6634
6876
  resetZoom: () => {
6635
6877
  scale = 1;
6636
6878
  ctx.container.scrollTop = 0;
@@ -6745,6 +6987,14 @@ var OpenDocumentPlugin = class {
6745
6987
  group: "zoom",
6746
6988
  execute: () => instance.resetZoom?.()
6747
6989
  },
6990
+ {
6991
+ id: "fit-width",
6992
+ icon: "fit-width",
6993
+ label: "Fit to Width",
6994
+ type: "button",
6995
+ group: "zoom",
6996
+ execute: () => instance.fitToWidth?.()
6997
+ },
6748
6998
  {
6749
6999
  id: "rotate-cw",
6750
7000
  icon: "rotate-cw",
@@ -6861,7 +7111,10 @@ var OpenDocumentPlugin = class {
6861
7111
  const sW = availW / elW;
6862
7112
  const sH = availH / (isPresentation ? 540 : singlePageH);
6863
7113
  if (isPresentation) {
6864
- return Math.min(2.5, Math.min(sW, sH));
7114
+ if (mode === "page") {
7115
+ return Math.min(2.5, Math.min(sW, sH));
7116
+ }
7117
+ return Math.min(2.5, sW);
6865
7118
  }
6866
7119
  if (mode === "page") {
6867
7120
  return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
@@ -7030,6 +7283,13 @@ var OpenDocumentPlugin = class {
7030
7283
  applyTransform();
7031
7284
  },
7032
7285
  fitToPage: () => {
7286
+ isUserZoomed = false;
7287
+ fitMode = "page";
7288
+ scale = calculateFitScale("page");
7289
+ rotation = 0;
7290
+ applyTransform();
7291
+ },
7292
+ fitToWidth: () => {
7033
7293
  isUserZoomed = false;
7034
7294
  fitMode = "width";
7035
7295
  scale = calculateFitScale("width");
@@ -7038,8 +7298,8 @@ var OpenDocumentPlugin = class {
7038
7298
  },
7039
7299
  resetZoom: () => {
7040
7300
  isUserZoomed = false;
7041
- fitMode = "width";
7042
- scale = calculateFitScale("width");
7301
+ fitMode = ctx?.options?.fitMode || "width";
7302
+ scale = calculateFitScale(fitMode);
7043
7303
  rotation = 0;
7044
7304
  container.scrollTop = 0;
7045
7305
  container.scrollLeft = 0;
@@ -7396,6 +7656,14 @@ var DocPlugin = class {
7396
7656
  group: "zoom",
7397
7657
  execute: () => instance.resetZoom?.()
7398
7658
  },
7659
+ {
7660
+ id: "fit-width",
7661
+ icon: "fit-width",
7662
+ label: "Fit to Width",
7663
+ type: "button",
7664
+ group: "zoom",
7665
+ execute: () => instance.fitToWidth?.()
7666
+ },
7399
7667
  {
7400
7668
  id: "rotate-cw",
7401
7669
  icon: "rotate-cw",
@@ -7617,6 +7885,13 @@ var DocPlugin = class {
7617
7885
  applyTransform();
7618
7886
  },
7619
7887
  fitToPage: () => {
7888
+ isUserZoomed = false;
7889
+ fitMode = "page";
7890
+ scale = calculateFitScale("page");
7891
+ rotation = 0;
7892
+ applyTransform();
7893
+ },
7894
+ fitToWidth: () => {
7620
7895
  isUserZoomed = false;
7621
7896
  fitMode = "width";
7622
7897
  scale = calculateFitScale("width");
@@ -7625,8 +7900,8 @@ var DocPlugin = class {
7625
7900
  },
7626
7901
  resetZoom: () => {
7627
7902
  isUserZoomed = false;
7628
- fitMode = "width";
7629
- scale = calculateFitScale("width");
7903
+ fitMode = ctx?.options?.fitMode || "width";
7904
+ scale = calculateFitScale(fitMode);
7630
7905
  rotation = 0;
7631
7906
  container.scrollTop = 0;
7632
7907
  container.scrollLeft = 0;
@@ -8122,6 +8397,14 @@ var PptPlugin = class {
8122
8397
  group: "zoom",
8123
8398
  execute: () => instance.resetZoom?.()
8124
8399
  },
8400
+ {
8401
+ id: "fit-width",
8402
+ icon: "fit-width",
8403
+ label: "Fit to Width",
8404
+ type: "button",
8405
+ group: "zoom",
8406
+ execute: () => instance.fitToWidth?.()
8407
+ },
8125
8408
  {
8126
8409
  id: "rotate-cw",
8127
8410
  icon: "rotate-cw",
@@ -8196,10 +8479,15 @@ var PptPlugin = class {
8196
8479
  let currentSlide = 1;
8197
8480
  let slides = [];
8198
8481
  const createdBlobUrls = [];
8199
- const calculateFitScale = () => {
8200
- const availW = Math.max(200, container.clientWidth - 48);
8201
- const availH = Math.max(200, container.clientHeight - 72);
8202
- return Math.min(1, Math.min(availW / 960, availH / 540));
8482
+ let fitMode = ctx?.options?.fitMode || "width";
8483
+ let isUserZoomed = false;
8484
+ const calculateFitScale = (mode = fitMode) => {
8485
+ const availW = Math.max(200, container.clientWidth - 32);
8486
+ const availH = Math.max(200, container.clientHeight - 32);
8487
+ if (mode === "page") {
8488
+ return Math.min(2.5, Math.min(availW / 960, availH / 540));
8489
+ }
8490
+ return Math.min(2.5, availW / 960);
8203
8491
  };
8204
8492
  const applyTransform = () => {
8205
8493
  const cW = 960;
@@ -8214,8 +8502,15 @@ var PptPlugin = class {
8214
8502
  slideCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
8215
8503
  slideCard.style.transformOrigin = "center center";
8216
8504
  };
8505
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
8506
+ if (!isUserZoomed) {
8507
+ scale = calculateFitScale(fitMode);
8508
+ applyTransform();
8509
+ }
8510
+ }) : null;
8511
+ ro?.observe(container);
8217
8512
  setTimeout(() => {
8218
- scale = calculateFitScale();
8513
+ scale = calculateFitScale(fitMode);
8219
8514
  applyTransform();
8220
8515
  }, 60);
8221
8516
  try {
@@ -8308,6 +8603,7 @@ var PptPlugin = class {
8308
8603
  };
8309
8604
  renderSlide(1);
8310
8605
  const cleanup = () => {
8606
+ ro?.disconnect();
8311
8607
  for (const u of createdBlobUrls) {
8312
8608
  URL.revokeObjectURL(u);
8313
8609
  }
@@ -8318,28 +8614,44 @@ var PptPlugin = class {
8318
8614
  return {
8319
8615
  destroy: cleanup,
8320
8616
  zoomIn: () => {
8617
+ isUserZoomed = true;
8321
8618
  scale += 0.15;
8322
8619
  applyTransform();
8323
8620
  },
8324
8621
  zoomOut: () => {
8622
+ isUserZoomed = true;
8325
8623
  scale = Math.max(0.2, scale - 0.15);
8326
8624
  applyTransform();
8327
8625
  },
8328
8626
  getZoom: () => scale,
8329
8627
  setZoom: (level) => {
8628
+ isUserZoomed = true;
8330
8629
  scale = level;
8331
8630
  applyTransform();
8332
8631
  },
8333
8632
  fitToPage: () => {
8334
- scale = calculateFitScale();
8633
+ isUserZoomed = false;
8634
+ fitMode = "page";
8635
+ scale = calculateFitScale("page");
8636
+ rotation = 0;
8637
+ applyTransform();
8638
+ },
8639
+ fitToWidth: () => {
8640
+ isUserZoomed = false;
8641
+ fitMode = "width";
8642
+ scale = calculateFitScale("width");
8335
8643
  rotation = 0;
8336
8644
  applyTransform();
8337
8645
  },
8338
8646
  resetZoom: () => {
8339
- scale = calculateFitScale();
8647
+ isUserZoomed = false;
8648
+ fitMode = ctx?.options?.fitMode || "width";
8649
+ scale = calculateFitScale(fitMode);
8340
8650
  rotation = 0;
8341
8651
  container.scrollTop = 0;
8342
8652
  container.scrollLeft = 0;
8653
+ ctx.container.scrollTop = 0;
8654
+ ctx.container.scrollLeft = 0;
8343
8655
  applyTransform();
8344
8656
  },
8345
8657
  rotateCW: () => {