@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/angular.cjs CHANGED
@@ -1137,6 +1137,10 @@ var FilePreviewViewer = class _FilePreviewViewer {
1137
1137
  * Preview a file in the given container element.
1138
1138
  */
1139
1139
  async preview(container, source, options = {}) {
1140
+ options = {
1141
+ ...options,
1142
+ fitMode: options.fitMode ?? "width"
1143
+ };
1140
1144
  this.currentOptions = options;
1141
1145
  this.abort();
1142
1146
  this.abortController = new AbortController();
@@ -1185,8 +1189,8 @@ var FilePreviewViewer = class _FilePreviewViewer {
1185
1189
  this.activeInstance = instance;
1186
1190
  instance.openInSeparateWindow = () => this.openInSeparateWindow();
1187
1191
  instance.toggleThumbnails = () => this.toggleThumbnails();
1188
- if (!instance.resetZoom && instance.fitToPage) {
1189
- instance.resetZoom = () => instance.fitToPage?.();
1192
+ if (!instance.resetZoom) {
1193
+ instance.resetZoom = () => instance.fitToWidth ? instance.fitToWidth() : instance.fitToPage?.();
1190
1194
  }
1191
1195
  this.hideLoading();
1192
1196
  this.eventEmitter.emit("loaded", { metadata, plugin: matchedPlugin.id });
@@ -1583,7 +1587,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1583
1587
  this.wrapperEl?.classList.toggle("fp-fullscreen-active");
1584
1588
  }
1585
1589
  setTimeout(() => {
1586
- this.activeInstance?.fitToPage?.();
1590
+ this.triggerAutoFit();
1587
1591
  }, 120);
1588
1592
  }
1589
1593
  /**
@@ -1710,18 +1714,29 @@ var FilePreviewViewer = class _FilePreviewViewer {
1710
1714
  this.thumbnailPanel = new ThumbnailPanel(thumbnailEl, (isOpen) => {
1711
1715
  this.toolbar?.setActionActive("thumbnails", isOpen);
1712
1716
  setTimeout(() => {
1713
- this.activeInstance?.fitToPage?.();
1717
+ this.triggerAutoFit();
1714
1718
  }, 260);
1715
1719
  });
1716
1720
  this.setupKeyboardShortcuts();
1717
1721
  this.setupDragAndDrop(container, options);
1718
1722
  this.setupResizeAndFullscreenListeners();
1719
1723
  }
1724
+ triggerAutoFit() {
1725
+ if (!this.activeInstance) return;
1726
+ const mode = this.currentOptions?.fitMode ?? "width";
1727
+ if (mode === "width" && this.activeInstance.fitToWidth) {
1728
+ this.activeInstance.fitToWidth();
1729
+ } else if (this.activeInstance.fitToPage) {
1730
+ this.activeInstance.fitToPage();
1731
+ } else if (this.activeInstance.resetZoom) {
1732
+ this.activeInstance.resetZoom();
1733
+ }
1734
+ }
1720
1735
  setupResizeAndFullscreenListeners() {
1721
1736
  if (this.fullscreenHandler) return;
1722
1737
  this.fullscreenHandler = () => {
1723
1738
  setTimeout(() => {
1724
- this.activeInstance?.fitToPage?.();
1739
+ this.triggerAutoFit();
1725
1740
  }, 100);
1726
1741
  };
1727
1742
  document.addEventListener("fullscreenchange", this.fullscreenHandler);
@@ -1729,7 +1744,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1729
1744
  if (typeof ResizeObserver !== "undefined" && this.contentEl) {
1730
1745
  this.resizeObserver = new ResizeObserver(() => {
1731
1746
  if (this.activeInstance && (!this.activeInstance.getZoom || Math.abs((this.activeInstance.getZoom?.() ?? 1) - 1) < 0.05)) {
1732
- this.activeInstance.fitToPage?.();
1747
+ this.triggerAutoFit();
1733
1748
  }
1734
1749
  });
1735
1750
  this.resizeObserver.observe(this.contentEl);
@@ -1757,7 +1772,7 @@ var FilePreviewViewer = class _FilePreviewViewer {
1757
1772
  } else if (e.key === "-" || e.key === "_") {
1758
1773
  this.activeInstance.zoomOut?.();
1759
1774
  } else if (e.key === "0") {
1760
- this.activeInstance.fitToPage?.();
1775
+ this.resetZoom();
1761
1776
  } else if (e.key === "r" || e.key === "R") {
1762
1777
  this.activeInstance.rotateCW?.();
1763
1778
  } else if (e.key === "f" || e.key === "F") {
@@ -2380,7 +2395,7 @@ var PdfPlugin = class {
2380
2395
  renderPage(currentPage);
2381
2396
  },
2382
2397
  resetZoom: () => {
2383
- fitMode = "page";
2398
+ fitMode = ctx?.options?.fitMode || "width";
2384
2399
  zoomScale = 1;
2385
2400
  rotation = 0;
2386
2401
  container.scrollTop = 0;
@@ -2564,6 +2579,16 @@ var MediaPlugin = class {
2564
2579
  instance.resetZoom?.();
2565
2580
  }
2566
2581
  },
2582
+ {
2583
+ id: "fit-width",
2584
+ icon: "fit-width",
2585
+ label: "Fit to Width",
2586
+ type: "button",
2587
+ group: "zoom",
2588
+ execute: () => {
2589
+ instance.fitToWidth?.();
2590
+ }
2591
+ },
2567
2592
  {
2568
2593
  id: "rotate-cw",
2569
2594
  icon: "rotate-cw",
@@ -2660,9 +2685,30 @@ var MediaPlugin = class {
2660
2685
  const isVideo = mimeType.startsWith("video/") || VIDEO_EXTS.includes(ctx.metadata.extension || "");
2661
2686
  const isAudio = mimeType.startsWith("audio/") || AUDIO_EXTS.includes(ctx.metadata.extension || "");
2662
2687
  let url = "";
2688
+ let naturalW = 800;
2689
+ let naturalH = 600;
2663
2690
  if (ctx.metadata.extension === ".svg" || mimeType === "image/svg+xml") {
2664
2691
  const decoder = new TextDecoder("utf-8");
2665
2692
  const svgText = decoder.decode(ctx.buffer);
2693
+ try {
2694
+ const vbMatch = svgText.match(/viewBox=["']\s*([0-9.-]+)\s+([0-9.-]+)\s+([0-9.-]+)\s+([0-9.-]+)\s*["']/i);
2695
+ if (vbMatch) {
2696
+ const vw = parseFloat(vbMatch[3]);
2697
+ const vh = parseFloat(vbMatch[4]);
2698
+ if (vw > 0 && vh > 0) {
2699
+ naturalW = vw;
2700
+ naturalH = vh;
2701
+ }
2702
+ } else {
2703
+ const wMatch = svgText.match(/width=["']([0-9.]+)(?:px)?["']/i);
2704
+ const hMatch = svgText.match(/height=["']([0-9.]+)(?:px)?["']/i);
2705
+ if (wMatch && hMatch) {
2706
+ naturalW = parseFloat(wMatch[1]) || naturalW;
2707
+ naturalH = parseFloat(hMatch[1]) || naturalH;
2708
+ }
2709
+ }
2710
+ } catch {
2711
+ }
2666
2712
  const blob = new Blob([svgText], { type: "image/svg+xml" });
2667
2713
  url = URL.createObjectURL(blob);
2668
2714
  } else {
@@ -2710,7 +2756,7 @@ var MediaPlugin = class {
2710
2756
  scrollWrapper.style.width = "max-content";
2711
2757
  scrollWrapper.style.height = "max-content";
2712
2758
  scrollWrapper.style.display = "flex";
2713
- scrollWrapper.style.justifyContent = "center";
2759
+ scrollWrapper.style.flexDirection = "column";
2714
2760
  scrollWrapper.style.alignItems = "center";
2715
2761
  scrollWrapper.style.padding = "16px";
2716
2762
  scrollWrapper.style.boxSizing = "border-box";
@@ -2721,40 +2767,54 @@ var MediaPlugin = class {
2721
2767
  sizer.style.display = "flex";
2722
2768
  sizer.style.justifyContent = "center";
2723
2769
  sizer.style.alignItems = "center";
2770
+ sizer.style.margin = "auto 0";
2724
2771
  sizer.appendChild(element);
2725
2772
  scrollWrapper.appendChild(sizer);
2726
2773
  ctx.container.style.overflow = "auto";
2727
2774
  ctx.container.style.padding = "0";
2728
2775
  ctx.container.innerHTML = "";
2729
2776
  ctx.container.appendChild(scrollWrapper);
2730
- let naturalW = 800;
2731
- let naturalH = 600;
2732
- let baseW = 800;
2733
- let baseH = 600;
2777
+ let baseW = naturalW;
2778
+ let baseH = naturalH;
2779
+ let fitMode = ctx?.options?.fitMode || "width";
2780
+ let isUserZoomed = false;
2734
2781
  const updateBaseDimensions = () => {
2735
2782
  if (!isImage) return;
2736
2783
  const img = element;
2737
- naturalW = img.naturalWidth || naturalW;
2738
- naturalH = img.naturalHeight || naturalH;
2739
- const availW = Math.max(100, ctx.container.clientWidth - 48);
2740
- const availH = Math.max(100, ctx.container.clientHeight - 48);
2741
- const fitRatio = Math.min(1, availW / naturalW, availH / naturalH);
2784
+ if (img.naturalWidth > 0 && img.naturalHeight > 0) {
2785
+ naturalW = img.naturalWidth;
2786
+ naturalH = img.naturalHeight;
2787
+ }
2788
+ const availW = Math.max(100, ctx.container.clientWidth - 32);
2789
+ const availH = Math.max(100, ctx.container.clientHeight - 32);
2790
+ let fitRatio;
2791
+ if (fitMode === "page") {
2792
+ fitRatio = Math.min(availW / naturalW, availH / naturalH);
2793
+ } else {
2794
+ fitRatio = availW / naturalW;
2795
+ }
2742
2796
  baseW = Math.max(1, Math.round(naturalW * fitRatio));
2743
2797
  baseH = Math.max(1, Math.round(naturalH * fitRatio));
2744
2798
  };
2745
2799
  const applyTransform = () => {
2746
2800
  if (isImage) {
2747
- updateBaseDimensions();
2801
+ if (!isUserZoomed) {
2802
+ updateBaseDimensions();
2803
+ }
2804
+ const availH = Math.max(100, ctx.container.clientHeight - 32);
2748
2805
  const isRotated90 = rotation % 180 !== 0;
2749
2806
  const boxW = Math.round((isRotated90 ? baseH : baseW) * currentZoom);
2750
2807
  const boxH = Math.round((isRotated90 ? baseW : baseH) * currentZoom);
2751
2808
  sizer.style.width = `${boxW}px`;
2752
2809
  sizer.style.height = `${boxH}px`;
2753
- element.style.width = `${baseW}px`;
2754
- element.style.height = `${baseH}px`;
2810
+ sizer.style.margin = boxH < availH ? "auto 0" : "0";
2811
+ const imgW = isRotated90 ? boxH : boxW;
2812
+ const imgH = isRotated90 ? boxW : boxH;
2813
+ element.style.width = `${imgW}px`;
2814
+ element.style.height = `${imgH}px`;
2755
2815
  element.style.maxWidth = "none";
2756
2816
  element.style.maxHeight = "none";
2757
- element.style.transform = `scale(${currentZoom}) rotate(${rotation}deg)`;
2817
+ element.style.transform = rotation ? `rotate(${rotation}deg)` : "none";
2758
2818
  element.style.transformOrigin = "center center";
2759
2819
  element.style.flexShrink = "0";
2760
2820
  } else {
@@ -2774,7 +2834,8 @@ var MediaPlugin = class {
2774
2834
  }
2775
2835
  }
2776
2836
  const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
2777
- if (currentZoom === 1) {
2837
+ if (!isUserZoomed && currentZoom === 1) {
2838
+ updateBaseDimensions();
2778
2839
  applyTransform();
2779
2840
  }
2780
2841
  }) : null;
@@ -2789,28 +2850,49 @@ var MediaPlugin = class {
2789
2850
  return {
2790
2851
  destroy: cleanup,
2791
2852
  zoomIn: isImage ? () => {
2792
- currentZoom += 0.1;
2853
+ isUserZoomed = true;
2854
+ currentZoom = Math.min(5, Math.round((currentZoom + 0.15) * 100) / 100);
2793
2855
  applyTransform();
2794
2856
  } : void 0,
2795
2857
  zoomOut: isImage ? () => {
2796
- currentZoom = Math.max(0.1, currentZoom - 0.1);
2858
+ isUserZoomed = true;
2859
+ currentZoom = Math.max(0.1, Math.round((currentZoom - 0.15) * 100) / 100);
2797
2860
  applyTransform();
2798
2861
  } : void 0,
2799
2862
  getZoom: isImage ? () => currentZoom : void 0,
2800
2863
  setZoom: isImage ? (level) => {
2864
+ isUserZoomed = true;
2801
2865
  currentZoom = level;
2802
2866
  applyTransform();
2803
2867
  } : void 0,
2804
2868
  fitToPage: isImage ? () => {
2869
+ isUserZoomed = false;
2870
+ fitMode = "page";
2805
2871
  currentZoom = 1;
2806
2872
  rotation = 0;
2873
+ ctx.container.scrollTop = 0;
2874
+ ctx.container.scrollLeft = 0;
2875
+ updateBaseDimensions();
2876
+ applyTransform();
2877
+ } : void 0,
2878
+ fitToWidth: isImage ? () => {
2879
+ isUserZoomed = false;
2880
+ fitMode = "width";
2881
+ currentZoom = 1;
2882
+ rotation = 0;
2883
+ ctx.container.scrollTop = 0;
2884
+ ctx.container.scrollLeft = 0;
2885
+ updateBaseDimensions();
2807
2886
  applyTransform();
2808
2887
  } : void 0,
2809
2888
  resetZoom: isImage ? () => {
2889
+ isUserZoomed = false;
2890
+ fitMode = ctx?.options?.fitMode || "width";
2810
2891
  currentZoom = 1;
2811
2892
  rotation = 0;
2812
2893
  ctx.container.scrollTop = 0;
2813
2894
  ctx.container.scrollLeft = 0;
2895
+ updateBaseDimensions();
2814
2896
  applyTransform();
2815
2897
  } : void 0,
2816
2898
  rotateCW: isImage || isVideo ? () => {
@@ -2937,6 +3019,14 @@ var DocxPlugin = class {
2937
3019
  group: "zoom",
2938
3020
  execute: () => instance.resetZoom?.()
2939
3021
  },
3022
+ {
3023
+ id: "fit-width",
3024
+ icon: "fit-width",
3025
+ label: "Fit to Width",
3026
+ type: "button",
3027
+ group: "zoom",
3028
+ execute: () => instance.fitToWidth?.()
3029
+ },
2940
3030
  {
2941
3031
  id: "rotate-cw",
2942
3032
  icon: "rotate-cw",
@@ -3196,13 +3286,19 @@ var DocxPlugin = class {
3196
3286
  }
3197
3287
  scale = 1;
3198
3288
  let rotation = 0;
3199
- let fitMode = "page";
3289
+ let fitMode = ctx?.options?.fitMode || "width";
3200
3290
  let isUserZoomed = false;
3201
- const calculateFitScale = (_mode = fitMode) => {
3291
+ const calculateFitScale = (mode = fitMode) => {
3202
3292
  const activeEl = pageElements[currentPage - 1] || wrapper.querySelector("section.docx") || wrapper;
3203
3293
  const elW = activeEl.offsetWidth || 816;
3294
+ const elH = activeEl.offsetHeight || 1056;
3204
3295
  const availW = Math.max(280, ctx.container.clientWidth - 32);
3296
+ const availH = Math.max(280, ctx.container.clientHeight - 32);
3205
3297
  const sW = availW / elW;
3298
+ const sH = availH / elH;
3299
+ if (mode === "page") {
3300
+ return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
3301
+ }
3206
3302
  return Math.max(0.35, Math.min(3, sW));
3207
3303
  };
3208
3304
  const applyTransform = () => {
@@ -3223,7 +3319,7 @@ var DocxPlugin = class {
3223
3319
  if (typeof ResizeObserver !== "undefined") {
3224
3320
  resizeObserver = new ResizeObserver(() => {
3225
3321
  if (!isUserZoomed) {
3226
- const newFit = calculateFitScale("page");
3322
+ const newFit = calculateFitScale(fitMode);
3227
3323
  if (Math.abs(scale - newFit) > 0.015) {
3228
3324
  scale = newFit;
3229
3325
  applyTransform();
@@ -3233,7 +3329,7 @@ var DocxPlugin = class {
3233
3329
  resizeObserver.observe(ctx.container);
3234
3330
  }
3235
3331
  setTimeout(() => {
3236
- scale = calculateFitScale("page");
3332
+ scale = calculateFitScale(fitMode);
3237
3333
  applyTransform();
3238
3334
  }, 40);
3239
3335
  const cleanup = () => {
@@ -3272,13 +3368,22 @@ var DocxPlugin = class {
3272
3368
  },
3273
3369
  fitToPage: () => {
3274
3370
  isUserZoomed = false;
3371
+ fitMode = "page";
3275
3372
  scale = calculateFitScale("page");
3276
3373
  rotation = 0;
3277
3374
  applyTransform();
3278
3375
  },
3376
+ fitToWidth: () => {
3377
+ isUserZoomed = false;
3378
+ fitMode = "width";
3379
+ scale = calculateFitScale("width");
3380
+ rotation = 0;
3381
+ applyTransform();
3382
+ },
3279
3383
  resetZoom: () => {
3280
3384
  isUserZoomed = false;
3281
- scale = calculateFitScale("page");
3385
+ fitMode = ctx?.options?.fitMode || "width";
3386
+ scale = calculateFitScale(fitMode);
3282
3387
  rotation = 0;
3283
3388
  ctx.container.scrollTop = 0;
3284
3389
  ctx.container.scrollLeft = 0;
@@ -3743,6 +3848,16 @@ var ExcelPlugin = class {
3743
3848
  instance.resetZoom?.();
3744
3849
  }
3745
3850
  },
3851
+ {
3852
+ id: "fit-width",
3853
+ icon: "fit-width",
3854
+ label: "Fit to Width",
3855
+ type: "button",
3856
+ group: "zoom",
3857
+ execute: () => {
3858
+ instance.fitToWidth?.();
3859
+ }
3860
+ },
3746
3861
  {
3747
3862
  id: "download",
3748
3863
  icon: "download",
@@ -3959,6 +4074,11 @@ var ExcelPlugin = class {
3959
4074
  scale = calculateFitScale();
3960
4075
  applyTransform();
3961
4076
  },
4077
+ fitToWidth: () => {
4078
+ isUserZoomed = false;
4079
+ scale = calculateFitScale();
4080
+ applyTransform();
4081
+ },
3962
4082
  resetZoom: () => {
3963
4083
  isUserZoomed = false;
3964
4084
  scale = calculateFitScale();
@@ -4106,6 +4226,16 @@ var CsvPlugin = class {
4106
4226
  instance.resetZoom?.();
4107
4227
  }
4108
4228
  },
4229
+ {
4230
+ id: "fit-width",
4231
+ icon: "fit-width",
4232
+ label: "Fit to Width",
4233
+ type: "button",
4234
+ group: "zoom",
4235
+ execute: () => {
4236
+ instance.fitToWidth?.();
4237
+ }
4238
+ },
4109
4239
  {
4110
4240
  id: "download",
4111
4241
  icon: "download",
@@ -4319,6 +4449,11 @@ var CsvPlugin = class {
4319
4449
  scale = calculateFitScale();
4320
4450
  applyScale();
4321
4451
  },
4452
+ fitToWidth: () => {
4453
+ isUserZoomed = false;
4454
+ scale = calculateFitScale();
4455
+ applyScale();
4456
+ },
4322
4457
  resetZoom: () => {
4323
4458
  isUserZoomed = false;
4324
4459
  scale = calculateFitScale();
@@ -4451,6 +4586,16 @@ var CodePlugin = class {
4451
4586
  instance.resetZoom?.();
4452
4587
  }
4453
4588
  },
4589
+ {
4590
+ id: "fit-width",
4591
+ icon: "fit-width",
4592
+ label: "Fit to Width",
4593
+ type: "button",
4594
+ group: "zoom",
4595
+ execute: () => {
4596
+ instance.fitToWidth?.();
4597
+ }
4598
+ },
4454
4599
  {
4455
4600
  id: "copy",
4456
4601
  icon: "copy",
@@ -4755,6 +4900,13 @@ var CodePlugin = class {
4755
4900
  zoomLevel = isTxt ? calculateTxtFit() : 1;
4756
4901
  updateTransform();
4757
4902
  },
4903
+ fitToWidth: () => {
4904
+ isUserZoomed = false;
4905
+ fontSize = 13;
4906
+ rotation = 0;
4907
+ zoomLevel = isTxt ? calculateTxtFit() : 1;
4908
+ updateTransform();
4909
+ },
4758
4910
  resetZoom: () => {
4759
4911
  isUserZoomed = false;
4760
4912
  fontSize = 13;
@@ -4839,6 +4991,14 @@ var ArchivePlugin = class {
4839
4991
  group: "zoom",
4840
4992
  execute: () => instance.resetZoom?.()
4841
4993
  },
4994
+ {
4995
+ id: "fit-width",
4996
+ icon: "fit-width",
4997
+ label: "Fit to Width",
4998
+ type: "button",
4999
+ group: "zoom",
5000
+ execute: () => instance.fitToWidth?.()
5001
+ },
4842
5002
  {
4843
5003
  id: "download",
4844
5004
  icon: "download",
@@ -5036,6 +5196,10 @@ var ArchivePlugin = class {
5036
5196
  scale = 1;
5037
5197
  applyTransform();
5038
5198
  },
5199
+ fitToWidth: () => {
5200
+ scale = 1;
5201
+ applyTransform();
5202
+ },
5039
5203
  resetZoom: () => {
5040
5204
  scale = 1;
5041
5205
  wrapper.scrollTop = 0;
@@ -5100,6 +5264,14 @@ var MarkdownPlugin = class {
5100
5264
  group: "zoom",
5101
5265
  execute: () => instance.resetZoom?.()
5102
5266
  },
5267
+ {
5268
+ id: "fit-width",
5269
+ icon: "fit-width",
5270
+ label: "Fit to Width",
5271
+ type: "button",
5272
+ group: "zoom",
5273
+ execute: () => instance.fitToWidth?.()
5274
+ },
5103
5275
  {
5104
5276
  id: "copy",
5105
5277
  icon: "copy",
@@ -5253,6 +5425,10 @@ var MarkdownPlugin = class {
5253
5425
  fontSize = 15;
5254
5426
  wrapper.style.fontSize = "15px";
5255
5427
  },
5428
+ fitToWidth: () => {
5429
+ fontSize = 15;
5430
+ wrapper.style.fontSize = "15px";
5431
+ },
5256
5432
  resetZoom: () => {
5257
5433
  fontSize = 15;
5258
5434
  wrapper.style.fontSize = "15px";
@@ -5360,6 +5536,14 @@ var PptxPlugin = class {
5360
5536
  group: "zoom",
5361
5537
  execute: () => instance.resetZoom?.()
5362
5538
  },
5539
+ {
5540
+ id: "fit-width",
5541
+ icon: "fit-width",
5542
+ label: "Fit to Width",
5543
+ type: "button",
5544
+ group: "zoom",
5545
+ execute: () => instance.fitToWidth?.()
5546
+ },
5363
5547
  {
5364
5548
  id: "rotate-cw",
5365
5549
  icon: "rotate-cw",
@@ -5443,12 +5627,17 @@ var PptxPlugin = class {
5443
5627
  ctx.container.innerHTML = "";
5444
5628
  ctx.container.style.overflow = "auto";
5445
5629
  ctx.container.appendChild(wrapper);
5446
- const calculateFitScale = () => {
5447
- const availW = Math.max(200, ctx.container.clientWidth - 48);
5448
- const availH = Math.max(200, ctx.container.clientHeight - 72);
5630
+ let fitMode = ctx?.options?.fitMode || "width";
5631
+ let isUserZoomed = false;
5632
+ const calculateFitScale = (mode = fitMode) => {
5633
+ const availW = Math.max(200, ctx.container.clientWidth - 32);
5634
+ const availH = Math.max(200, ctx.container.clientHeight - 32);
5449
5635
  const cW = canvas.offsetWidth || 1280;
5450
5636
  const cH = canvas.offsetHeight || 720;
5451
- return Math.min(1, Math.min(availW / cW, availH / cH));
5637
+ if (mode === "page") {
5638
+ return Math.min(2.5, Math.min(availW / cW, availH / cH));
5639
+ }
5640
+ return Math.min(2.5, availW / cW);
5452
5641
  };
5453
5642
  const applyTransform = () => {
5454
5643
  const cW = canvas.offsetWidth || 1280;
@@ -5467,14 +5656,24 @@ var PptxPlugin = class {
5467
5656
  try {
5468
5657
  await renderer.renderSlide(currentSlide - 1, canvas, 1280);
5469
5658
  ctx.emit("page-change", { page: currentSlide, totalPages: slideCount });
5470
- scale = calculateFitScale();
5659
+ if (!isUserZoomed) {
5660
+ scale = calculateFitScale(fitMode);
5661
+ }
5471
5662
  applyTransform();
5472
5663
  } catch (err) {
5473
5664
  console.error("[PptxPlugin] Failed to render slide:", err);
5474
5665
  }
5475
5666
  };
5476
5667
  await renderCurrentSlide();
5668
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
5669
+ if (!isUserZoomed) {
5670
+ scale = calculateFitScale(fitMode);
5671
+ applyTransform();
5672
+ }
5673
+ }) : null;
5674
+ ro?.observe(ctx.container);
5477
5675
  const cleanup = () => {
5676
+ ro?.disconnect();
5478
5677
  renderer.destroy();
5479
5678
  wrapper.remove();
5480
5679
  ctx.container.innerHTML = "";
@@ -5491,28 +5690,44 @@ var PptxPlugin = class {
5491
5690
  getPageCount: () => slideCount,
5492
5691
  getCurrentPage: () => currentSlide,
5493
5692
  zoomIn: () => {
5693
+ isUserZoomed = true;
5494
5694
  scale += 0.15;
5495
5695
  applyTransform();
5496
5696
  },
5497
5697
  zoomOut: () => {
5698
+ isUserZoomed = true;
5498
5699
  scale = Math.max(0.2, scale - 0.15);
5499
5700
  applyTransform();
5500
5701
  },
5501
5702
  getZoom: () => scale,
5502
5703
  setZoom: (level) => {
5704
+ isUserZoomed = true;
5503
5705
  scale = level;
5504
5706
  applyTransform();
5505
5707
  },
5506
5708
  fitToPage: () => {
5507
- scale = calculateFitScale();
5709
+ isUserZoomed = false;
5710
+ fitMode = "page";
5711
+ scale = calculateFitScale("page");
5712
+ rotation = 0;
5713
+ applyTransform();
5714
+ },
5715
+ fitToWidth: () => {
5716
+ isUserZoomed = false;
5717
+ fitMode = "width";
5718
+ scale = calculateFitScale("width");
5508
5719
  rotation = 0;
5509
5720
  applyTransform();
5510
5721
  },
5511
5722
  resetZoom: () => {
5512
- scale = calculateFitScale();
5723
+ isUserZoomed = false;
5724
+ fitMode = ctx?.options?.fitMode || "width";
5725
+ scale = calculateFitScale(fitMode);
5513
5726
  rotation = 0;
5514
5727
  wrapper.scrollTop = 0;
5515
5728
  wrapper.scrollLeft = 0;
5729
+ ctx.container.scrollTop = 0;
5730
+ ctx.container.scrollLeft = 0;
5516
5731
  applyTransform();
5517
5732
  },
5518
5733
  rotateCW: () => {
@@ -5957,6 +6172,14 @@ var RtfPlugin = class {
5957
6172
  group: "zoom",
5958
6173
  execute: () => instance.resetZoom?.()
5959
6174
  },
6175
+ {
6176
+ id: "fit-width",
6177
+ icon: "fit-width",
6178
+ label: "Fit to Width",
6179
+ type: "button",
6180
+ group: "zoom",
6181
+ execute: () => instance.fitToWidth?.()
6182
+ },
5960
6183
  {
5961
6184
  id: "rotate-cw",
5962
6185
  icon: "rotate-cw",
@@ -6445,6 +6668,13 @@ var RtfPlugin = class {
6445
6668
  applyTransform();
6446
6669
  },
6447
6670
  fitToPage: () => {
6671
+ isUserZoomed = false;
6672
+ fitMode = "page";
6673
+ scale = calculateFitScale("page");
6674
+ rotation = 0;
6675
+ applyTransform();
6676
+ },
6677
+ fitToWidth: () => {
6448
6678
  isUserZoomed = false;
6449
6679
  fitMode = "width";
6450
6680
  scale = calculateFitScale("width");
@@ -6453,8 +6683,8 @@ var RtfPlugin = class {
6453
6683
  },
6454
6684
  resetZoom: () => {
6455
6685
  isUserZoomed = false;
6456
- fitMode = "width";
6457
- scale = calculateFitScale("width");
6686
+ fitMode = ctx?.options?.fitMode || "width";
6687
+ scale = calculateFitScale(fitMode);
6458
6688
  rotation = 0;
6459
6689
  ctx.container.scrollTop = 0;
6460
6690
  ctx.container.scrollLeft = 0;
@@ -6535,6 +6765,14 @@ var HtmlPreviewPlugin = class {
6535
6765
  group: "zoom",
6536
6766
  execute: () => instance.resetZoom?.()
6537
6767
  },
6768
+ {
6769
+ id: "fit-width",
6770
+ icon: "fit-width",
6771
+ label: "Fit to Width",
6772
+ type: "button",
6773
+ group: "zoom",
6774
+ execute: () => instance.fitToWidth?.()
6775
+ },
6538
6776
  {
6539
6777
  id: "copy",
6540
6778
  icon: "copy",
@@ -6628,6 +6866,10 @@ var HtmlPreviewPlugin = class {
6628
6866
  scale = 1;
6629
6867
  applyTransform();
6630
6868
  },
6869
+ fitToWidth: () => {
6870
+ scale = 1;
6871
+ applyTransform();
6872
+ },
6631
6873
  resetZoom: () => {
6632
6874
  scale = 1;
6633
6875
  ctx.container.scrollTop = 0;
@@ -6742,6 +6984,14 @@ var OpenDocumentPlugin = class {
6742
6984
  group: "zoom",
6743
6985
  execute: () => instance.resetZoom?.()
6744
6986
  },
6987
+ {
6988
+ id: "fit-width",
6989
+ icon: "fit-width",
6990
+ label: "Fit to Width",
6991
+ type: "button",
6992
+ group: "zoom",
6993
+ execute: () => instance.fitToWidth?.()
6994
+ },
6745
6995
  {
6746
6996
  id: "rotate-cw",
6747
6997
  icon: "rotate-cw",
@@ -6858,7 +7108,10 @@ var OpenDocumentPlugin = class {
6858
7108
  const sW = availW / elW;
6859
7109
  const sH = availH / (isPresentation ? 540 : singlePageH);
6860
7110
  if (isPresentation) {
6861
- return Math.min(2.5, Math.min(sW, sH));
7111
+ if (mode === "page") {
7112
+ return Math.min(2.5, Math.min(sW, sH));
7113
+ }
7114
+ return Math.min(2.5, sW);
6862
7115
  }
6863
7116
  if (mode === "page") {
6864
7117
  return Math.max(0.35, Math.min(3, Math.min(sW, sH)));
@@ -7027,6 +7280,13 @@ var OpenDocumentPlugin = class {
7027
7280
  applyTransform();
7028
7281
  },
7029
7282
  fitToPage: () => {
7283
+ isUserZoomed = false;
7284
+ fitMode = "page";
7285
+ scale = calculateFitScale("page");
7286
+ rotation = 0;
7287
+ applyTransform();
7288
+ },
7289
+ fitToWidth: () => {
7030
7290
  isUserZoomed = false;
7031
7291
  fitMode = "width";
7032
7292
  scale = calculateFitScale("width");
@@ -7035,8 +7295,8 @@ var OpenDocumentPlugin = class {
7035
7295
  },
7036
7296
  resetZoom: () => {
7037
7297
  isUserZoomed = false;
7038
- fitMode = "width";
7039
- scale = calculateFitScale("width");
7298
+ fitMode = ctx?.options?.fitMode || "width";
7299
+ scale = calculateFitScale(fitMode);
7040
7300
  rotation = 0;
7041
7301
  container.scrollTop = 0;
7042
7302
  container.scrollLeft = 0;
@@ -7393,6 +7653,14 @@ var DocPlugin = class {
7393
7653
  group: "zoom",
7394
7654
  execute: () => instance.resetZoom?.()
7395
7655
  },
7656
+ {
7657
+ id: "fit-width",
7658
+ icon: "fit-width",
7659
+ label: "Fit to Width",
7660
+ type: "button",
7661
+ group: "zoom",
7662
+ execute: () => instance.fitToWidth?.()
7663
+ },
7396
7664
  {
7397
7665
  id: "rotate-cw",
7398
7666
  icon: "rotate-cw",
@@ -7614,6 +7882,13 @@ var DocPlugin = class {
7614
7882
  applyTransform();
7615
7883
  },
7616
7884
  fitToPage: () => {
7885
+ isUserZoomed = false;
7886
+ fitMode = "page";
7887
+ scale = calculateFitScale("page");
7888
+ rotation = 0;
7889
+ applyTransform();
7890
+ },
7891
+ fitToWidth: () => {
7617
7892
  isUserZoomed = false;
7618
7893
  fitMode = "width";
7619
7894
  scale = calculateFitScale("width");
@@ -7622,8 +7897,8 @@ var DocPlugin = class {
7622
7897
  },
7623
7898
  resetZoom: () => {
7624
7899
  isUserZoomed = false;
7625
- fitMode = "width";
7626
- scale = calculateFitScale("width");
7900
+ fitMode = ctx?.options?.fitMode || "width";
7901
+ scale = calculateFitScale(fitMode);
7627
7902
  rotation = 0;
7628
7903
  container.scrollTop = 0;
7629
7904
  container.scrollLeft = 0;
@@ -8119,6 +8394,14 @@ var PptPlugin = class {
8119
8394
  group: "zoom",
8120
8395
  execute: () => instance.resetZoom?.()
8121
8396
  },
8397
+ {
8398
+ id: "fit-width",
8399
+ icon: "fit-width",
8400
+ label: "Fit to Width",
8401
+ type: "button",
8402
+ group: "zoom",
8403
+ execute: () => instance.fitToWidth?.()
8404
+ },
8122
8405
  {
8123
8406
  id: "rotate-cw",
8124
8407
  icon: "rotate-cw",
@@ -8193,10 +8476,15 @@ var PptPlugin = class {
8193
8476
  let currentSlide = 1;
8194
8477
  let slides = [];
8195
8478
  const createdBlobUrls = [];
8196
- const calculateFitScale = () => {
8197
- const availW = Math.max(200, container.clientWidth - 48);
8198
- const availH = Math.max(200, container.clientHeight - 72);
8199
- return Math.min(1, Math.min(availW / 960, availH / 540));
8479
+ let fitMode = ctx?.options?.fitMode || "width";
8480
+ let isUserZoomed = false;
8481
+ const calculateFitScale = (mode = fitMode) => {
8482
+ const availW = Math.max(200, container.clientWidth - 32);
8483
+ const availH = Math.max(200, container.clientHeight - 32);
8484
+ if (mode === "page") {
8485
+ return Math.min(2.5, Math.min(availW / 960, availH / 540));
8486
+ }
8487
+ return Math.min(2.5, availW / 960);
8200
8488
  };
8201
8489
  const applyTransform = () => {
8202
8490
  const cW = 960;
@@ -8211,8 +8499,15 @@ var PptPlugin = class {
8211
8499
  slideCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
8212
8500
  slideCard.style.transformOrigin = "center center";
8213
8501
  };
8502
+ const ro = typeof ResizeObserver !== "undefined" ? new ResizeObserver(() => {
8503
+ if (!isUserZoomed) {
8504
+ scale = calculateFitScale(fitMode);
8505
+ applyTransform();
8506
+ }
8507
+ }) : null;
8508
+ ro?.observe(container);
8214
8509
  setTimeout(() => {
8215
- scale = calculateFitScale();
8510
+ scale = calculateFitScale(fitMode);
8216
8511
  applyTransform();
8217
8512
  }, 60);
8218
8513
  try {
@@ -8305,6 +8600,7 @@ var PptPlugin = class {
8305
8600
  };
8306
8601
  renderSlide(1);
8307
8602
  const cleanup = () => {
8603
+ ro?.disconnect();
8308
8604
  for (const u of createdBlobUrls) {
8309
8605
  URL.revokeObjectURL(u);
8310
8606
  }
@@ -8315,28 +8611,44 @@ var PptPlugin = class {
8315
8611
  return {
8316
8612
  destroy: cleanup,
8317
8613
  zoomIn: () => {
8614
+ isUserZoomed = true;
8318
8615
  scale += 0.15;
8319
8616
  applyTransform();
8320
8617
  },
8321
8618
  zoomOut: () => {
8619
+ isUserZoomed = true;
8322
8620
  scale = Math.max(0.2, scale - 0.15);
8323
8621
  applyTransform();
8324
8622
  },
8325
8623
  getZoom: () => scale,
8326
8624
  setZoom: (level) => {
8625
+ isUserZoomed = true;
8327
8626
  scale = level;
8328
8627
  applyTransform();
8329
8628
  },
8330
8629
  fitToPage: () => {
8331
- scale = calculateFitScale();
8630
+ isUserZoomed = false;
8631
+ fitMode = "page";
8632
+ scale = calculateFitScale("page");
8633
+ rotation = 0;
8634
+ applyTransform();
8635
+ },
8636
+ fitToWidth: () => {
8637
+ isUserZoomed = false;
8638
+ fitMode = "width";
8639
+ scale = calculateFitScale("width");
8332
8640
  rotation = 0;
8333
8641
  applyTransform();
8334
8642
  },
8335
8643
  resetZoom: () => {
8336
- scale = calculateFitScale();
8644
+ isUserZoomed = false;
8645
+ fitMode = ctx?.options?.fitMode || "width";
8646
+ scale = calculateFitScale(fitMode);
8337
8647
  rotation = 0;
8338
8648
  container.scrollTop = 0;
8339
8649
  container.scrollLeft = 0;
8650
+ ctx.container.scrollTop = 0;
8651
+ ctx.container.scrollLeft = 0;
8340
8652
  applyTransform();
8341
8653
  },
8342
8654
  rotateCW: () => {