@files-preview-app/preview-file 1.2.5 → 1.2.6

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
@@ -748,6 +748,8 @@ var FilePreviewViewer = class {
748
748
  keyHandler = null;
749
749
  currentContainer = null;
750
750
  currentOptions = {};
751
+ resizeObserver = null;
752
+ fullscreenHandler = null;
751
753
  /**
752
754
  * Register a preview plugin.
753
755
  */
@@ -820,12 +822,31 @@ var FilePreviewViewer = class {
820
822
  label: "Toggle Fullscreen",
821
823
  type: "button",
822
824
  group: "view",
823
- execute: () => {
824
- if (!document.fullscreenElement) {
825
- this.wrapperEl?.requestFullscreen?.();
826
- } else {
827
- document.exitFullscreen?.();
825
+ execute: async () => {
826
+ try {
827
+ const isNativeFs = !!document.fullscreenElement;
828
+ const isCssFs = this.wrapperEl?.classList.contains("fp-fullscreen-active");
829
+ if (!isNativeFs && !isCssFs) {
830
+ if (this.wrapperEl?.requestFullscreen) {
831
+ await this.wrapperEl.requestFullscreen().catch(() => {
832
+ this.wrapperEl?.classList.add("fp-fullscreen-active");
833
+ });
834
+ } else {
835
+ this.wrapperEl?.classList.add("fp-fullscreen-active");
836
+ }
837
+ } else {
838
+ if (document.fullscreenElement) {
839
+ await document.exitFullscreen().catch(() => {
840
+ });
841
+ }
842
+ this.wrapperEl?.classList.remove("fp-fullscreen-active");
843
+ }
844
+ } catch {
845
+ this.wrapperEl?.classList.toggle("fp-fullscreen-active");
828
846
  }
847
+ setTimeout(() => {
848
+ this.activeInstance?.fitToPage?.();
849
+ }, 120);
829
850
  }
830
851
  });
831
852
  }
@@ -875,6 +896,15 @@ var FilePreviewViewer = class {
875
896
  window.removeEventListener("keydown", this.keyHandler);
876
897
  this.keyHandler = null;
877
898
  }
899
+ if (this.resizeObserver) {
900
+ this.resizeObserver.disconnect();
901
+ this.resizeObserver = null;
902
+ }
903
+ if (this.fullscreenHandler) {
904
+ document.removeEventListener("fullscreenchange", this.fullscreenHandler);
905
+ document.removeEventListener("webkitfullscreenchange", this.fullscreenHandler);
906
+ this.fullscreenHandler = null;
907
+ }
878
908
  this.abort();
879
909
  this.destroyInstance();
880
910
  this.toolbar?.destroy();
@@ -954,6 +984,25 @@ var FilePreviewViewer = class {
954
984
  this.thumbnailPanel = new ThumbnailPanel(thumbnailEl);
955
985
  this.setupKeyboardShortcuts();
956
986
  this.setupDragAndDrop(container, options);
987
+ this.setupResizeAndFullscreenListeners();
988
+ }
989
+ setupResizeAndFullscreenListeners() {
990
+ if (this.fullscreenHandler) return;
991
+ this.fullscreenHandler = () => {
992
+ setTimeout(() => {
993
+ this.activeInstance?.fitToPage?.();
994
+ }, 100);
995
+ };
996
+ document.addEventListener("fullscreenchange", this.fullscreenHandler);
997
+ document.addEventListener("webkitfullscreenchange", this.fullscreenHandler);
998
+ if (typeof ResizeObserver !== "undefined" && this.contentEl) {
999
+ this.resizeObserver = new ResizeObserver(() => {
1000
+ if (this.activeInstance && (!this.activeInstance.getZoom || Math.abs((this.activeInstance.getZoom?.() ?? 1) - 1) < 0.05)) {
1001
+ this.activeInstance.fitToPage?.();
1002
+ }
1003
+ });
1004
+ this.resizeObserver.observe(this.contentEl);
1005
+ }
957
1006
  }
958
1007
  setupKeyboardShortcuts() {
959
1008
  if (this.keyHandler) return;
@@ -1345,7 +1394,8 @@ var PdfPlugin = class {
1345
1394
  container.style.display = "flex";
1346
1395
  container.style.flexDirection = "column";
1347
1396
  container.style.alignItems = "center";
1348
- container.style.padding = "24px 16px";
1397
+ container.style.justifyContent = "flex-start";
1398
+ container.style.padding = "20px 16px";
1349
1399
  container.style.backgroundColor = "#0f172a";
1350
1400
  container.style.boxSizing = "border-box";
1351
1401
  container.style.position = "relative";
@@ -1358,7 +1408,8 @@ var PdfPlugin = class {
1358
1408
  pageCard.style.lineHeight = "0";
1359
1409
  pageCard.style.transition = "transform 0.15s ease";
1360
1410
  pageCard.style.position = "relative";
1361
- const canvas = document.createElement("canvas");
1411
+ pageCard.style.flexShrink = "0";
1412
+ let canvas = document.createElement("canvas");
1362
1413
  pageCard.appendChild(canvas);
1363
1414
  container.appendChild(pageCard);
1364
1415
  const indicator = document.createElement("div");
@@ -1400,14 +1451,22 @@ var PdfPlugin = class {
1400
1451
  }
1401
1452
  currentRenderTask = null;
1402
1453
  }
1454
+ const newCanvas = document.createElement("canvas");
1455
+ pageCard.replaceChild(newCanvas, canvas);
1456
+ canvas = newCanvas;
1403
1457
  currentPage = Math.max(1, Math.min(totalPages, pageNum));
1404
1458
  indicator.textContent = `Page ${currentPage} of ${totalPages}`;
1405
1459
  ctx.emit("page-change", { page: currentPage, total: totalPages });
1406
1460
  const page = await pdfDoc.getPage(currentPage);
1407
1461
  const containerWidth = container.clientWidth || 900;
1462
+ const containerHeight = container.clientHeight || 700;
1408
1463
  const unscaledVp = page.getViewport({ scale: 1, rotation });
1409
- const baseScale = Math.min((containerWidth - 64) / unscaledVp.width, 1.6);
1410
- const effectiveScale = (baseScale > 0 ? baseScale : 1) * zoomScale;
1464
+ const availWidth = Math.max(100, containerWidth - 48);
1465
+ const availHeight = Math.max(100, containerHeight - 88);
1466
+ const scaleW = availWidth / unscaledVp.width;
1467
+ const scaleH = availHeight / unscaledVp.height;
1468
+ const fitScale = Math.min(scaleW, scaleH);
1469
+ const effectiveScale = (fitScale > 0 ? fitScale : 1) * zoomScale;
1411
1470
  const pixelRatio = window.devicePixelRatio || 1;
1412
1471
  const viewport = page.getViewport({ scale: effectiveScale, rotation });
1413
1472
  canvas.width = Math.floor(viewport.width * pixelRatio);
@@ -1464,6 +1523,7 @@ var PdfPlugin = class {
1464
1523
  },
1465
1524
  fitToPage: () => {
1466
1525
  zoomScale = 1;
1526
+ rotation = 0;
1467
1527
  renderPage(currentPage);
1468
1528
  },
1469
1529
  rotateCW: () => {
@@ -1914,6 +1974,14 @@ var DocxPlugin = class {
1914
1974
  group: "zoom",
1915
1975
  execute: () => instance.fitToPage?.()
1916
1976
  },
1977
+ {
1978
+ id: "rotate-cw",
1979
+ icon: "rotate-cw",
1980
+ label: "Rotate",
1981
+ type: "button",
1982
+ group: "view",
1983
+ execute: () => instance.rotateCW?.()
1984
+ },
1917
1985
  {
1918
1986
  id: "download",
1919
1987
  icon: "download",
@@ -2050,6 +2118,26 @@ var DocxPlugin = class {
2050
2118
  if (totalPages > 1) {
2051
2119
  showPage(1);
2052
2120
  }
2121
+ scale = 1;
2122
+ let rotation = 0;
2123
+ const calculateFitScale = () => {
2124
+ const activeEl = pageElements[currentPage - 1] || wrapper.firstElementChild || wrapper;
2125
+ const elW = activeEl.offsetWidth || 816;
2126
+ const elH = activeEl.offsetHeight || 1056;
2127
+ const availW = Math.max(200, ctx.container.clientWidth - 48);
2128
+ const availH = Math.max(200, ctx.container.clientHeight - 80);
2129
+ const sW = availW / elW;
2130
+ const sH = availH / elH;
2131
+ return Math.min(1.1, Math.min(sW, sH));
2132
+ };
2133
+ const applyTransform = () => {
2134
+ wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
2135
+ wrapper.style.transformOrigin = "top center";
2136
+ };
2137
+ setTimeout(() => {
2138
+ scale = calculateFitScale();
2139
+ applyTransform();
2140
+ }, 60);
2053
2141
  const cleanup = () => {
2054
2142
  indicator?.remove();
2055
2143
  for (const url of createdBlobUrls) {
@@ -2068,21 +2156,30 @@ var DocxPlugin = class {
2068
2156
  showPage(page);
2069
2157
  },
2070
2158
  zoomIn: () => {
2071
- scale += 0.1;
2072
- wrapper.style.transform = `scale(${scale})`;
2159
+ scale += 0.15;
2160
+ applyTransform();
2073
2161
  },
2074
2162
  zoomOut: () => {
2075
- scale = Math.max(0.2, scale - 0.1);
2076
- wrapper.style.transform = `scale(${scale})`;
2163
+ scale = Math.max(0.2, scale - 0.15);
2164
+ applyTransform();
2077
2165
  },
2078
2166
  getZoom: () => scale,
2079
2167
  setZoom: (level) => {
2080
2168
  scale = level;
2081
- wrapper.style.transform = `scale(${scale})`;
2169
+ applyTransform();
2082
2170
  },
2083
2171
  fitToPage: () => {
2084
- scale = 1;
2085
- wrapper.style.transform = "scale(1)";
2172
+ scale = calculateFitScale();
2173
+ rotation = 0;
2174
+ applyTransform();
2175
+ },
2176
+ rotateCW: () => {
2177
+ rotation = (rotation + 90) % 360;
2178
+ applyTransform();
2179
+ },
2180
+ rotateCCW: () => {
2181
+ rotation = (rotation - 90 + 360) % 360;
2182
+ applyTransform();
2086
2183
  },
2087
2184
  download: () => {
2088
2185
  const blob = new Blob([ctx.buffer], { type: this.mimeTypes[0] });
@@ -2376,6 +2473,26 @@ var ExcelPlugin = class {
2376
2473
  instance.zoomIn?.();
2377
2474
  }
2378
2475
  },
2476
+ {
2477
+ id: "fit-page",
2478
+ icon: "fit-page",
2479
+ label: "Fit to View",
2480
+ type: "button",
2481
+ group: "zoom",
2482
+ execute: () => {
2483
+ instance.fitToPage?.();
2484
+ }
2485
+ },
2486
+ {
2487
+ id: "rotate-cw",
2488
+ icon: "rotate-cw",
2489
+ label: "Rotate",
2490
+ type: "button",
2491
+ group: "view",
2492
+ execute: () => {
2493
+ instance.rotateCW?.();
2494
+ }
2495
+ },
2379
2496
  {
2380
2497
  id: "download",
2381
2498
  icon: "download",
@@ -2426,9 +2543,23 @@ var ExcelPlugin = class {
2426
2543
  container.appendChild(tabsArea);
2427
2544
  ctx.container.appendChild(container);
2428
2545
  let scale = 1;
2546
+ let rotation = 0;
2429
2547
  let currentSheetIndex = 1;
2430
2548
  let sheetNames = [];
2431
2549
  let wb = null;
2550
+ const applyTransform = () => {
2551
+ contentArea.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
2552
+ contentArea.style.transformOrigin = "top left";
2553
+ };
2554
+ const calculateFitScale = () => {
2555
+ const table = contentArea.querySelector("table");
2556
+ if (!table) return 1;
2557
+ const availW = Math.max(200, container.clientWidth - 48);
2558
+ const availH = Math.max(200, container.clientHeight - 80);
2559
+ const tW = table.offsetWidth || 800;
2560
+ const tH = table.offsetHeight || 600;
2561
+ return Math.min(1, Math.min(availW / tW, availH / tH));
2562
+ };
2432
2563
  try {
2433
2564
  wb = XLSX.read(new Uint8Array(ctx.buffer), { type: "array", cellDates: true });
2434
2565
  sheetNames = wb.SheetNames || [];
@@ -2516,17 +2647,30 @@ var ExcelPlugin = class {
2516
2647
  return {
2517
2648
  destroy: cleanup,
2518
2649
  zoomIn: () => {
2519
- scale += 0.1;
2520
- contentArea.style.transform = `scale(${scale})`;
2650
+ scale += 0.15;
2651
+ applyTransform();
2521
2652
  },
2522
2653
  zoomOut: () => {
2523
- scale = Math.max(0.2, scale - 0.1);
2524
- contentArea.style.transform = `scale(${scale})`;
2654
+ scale = Math.max(0.2, scale - 0.15);
2655
+ applyTransform();
2525
2656
  },
2526
2657
  getZoom: () => scale,
2527
2658
  setZoom: (level) => {
2528
2659
  scale = level;
2529
- contentArea.style.transform = `scale(${scale})`;
2660
+ applyTransform();
2661
+ },
2662
+ fitToPage: () => {
2663
+ scale = calculateFitScale();
2664
+ rotation = 0;
2665
+ applyTransform();
2666
+ },
2667
+ rotateCW: () => {
2668
+ rotation = (rotation + 90) % 360;
2669
+ applyTransform();
2670
+ },
2671
+ rotateCCW: () => {
2672
+ rotation = (rotation - 90 + 360) % 360;
2673
+ applyTransform();
2530
2674
  },
2531
2675
  goToPage: (page) => {
2532
2676
  if (page > 0 && page <= sheetNames.length) {
@@ -2776,6 +2920,26 @@ var CodePlugin = class {
2776
2920
  instance.zoomIn?.();
2777
2921
  }
2778
2922
  },
2923
+ {
2924
+ id: "fit-page",
2925
+ icon: "fit-page",
2926
+ label: "Fit to View",
2927
+ type: "button",
2928
+ group: "zoom",
2929
+ execute: () => {
2930
+ instance.fitToPage?.();
2931
+ }
2932
+ },
2933
+ {
2934
+ id: "rotate-cw",
2935
+ icon: "rotate-cw",
2936
+ label: "Rotate",
2937
+ type: "button",
2938
+ group: "view",
2939
+ execute: () => {
2940
+ instance.rotateCW?.();
2941
+ }
2942
+ },
2779
2943
  {
2780
2944
  id: "copy",
2781
2945
  icon: "copy",
@@ -2825,6 +2989,7 @@ var CodePlugin = class {
2825
2989
  container.style.padding = "16px";
2826
2990
  container.style.boxSizing = "border-box";
2827
2991
  let fontSize = 13;
2992
+ let rotation = 0;
2828
2993
  const pre = document.createElement("pre");
2829
2994
  pre.style.margin = "0";
2830
2995
  pre.style.fontFamily = "Consolas, Menlo, Monaco, monospace";
@@ -2907,6 +3072,22 @@ var CodePlugin = class {
2907
3072
  fontSize = Math.round(13 * level);
2908
3073
  pre.style.fontSize = `${fontSize}px`;
2909
3074
  },
3075
+ fitToPage: () => {
3076
+ fontSize = 13;
3077
+ rotation = 0;
3078
+ pre.style.fontSize = "13px";
3079
+ pre.style.transform = "none";
3080
+ },
3081
+ rotateCW: () => {
3082
+ rotation = (rotation + 90) % 360;
3083
+ pre.style.transform = `rotate(${rotation}deg)`;
3084
+ pre.style.transformOrigin = "top left";
3085
+ },
3086
+ rotateCCW: () => {
3087
+ rotation = (rotation - 90 + 360) % 360;
3088
+ pre.style.transform = `rotate(${rotation}deg)`;
3089
+ pre.style.transformOrigin = "top left";
3090
+ },
2910
3091
  download: () => {
2911
3092
  const mimeType = ctx.metadata.mimeType || "text/plain";
2912
3093
  const blob = new Blob([ctx.buffer], { type: mimeType });
@@ -3418,6 +3599,14 @@ var PptxPlugin = class {
3418
3599
  group: "zoom",
3419
3600
  execute: () => instance.fitToPage?.()
3420
3601
  },
3602
+ {
3603
+ id: "rotate-cw",
3604
+ icon: "rotate-cw",
3605
+ label: "Rotate",
3606
+ type: "button",
3607
+ group: "view",
3608
+ execute: () => instance.rotateCW?.()
3609
+ },
3421
3610
  {
3422
3611
  id: "download",
3423
3612
  icon: "download",
@@ -3442,6 +3631,7 @@ var PptxPlugin = class {
3442
3631
  const slideCount = renderer.slidePaths?.length || 1;
3443
3632
  let currentSlide = 1;
3444
3633
  let scale = 1;
3634
+ let rotation = 0;
3445
3635
  const wrapper = document.createElement("div");
3446
3636
  wrapper.className = "fp-pptx-wrapper";
3447
3637
  wrapper.style.cssText = `
@@ -3464,6 +3654,8 @@ var PptxPlugin = class {
3464
3654
  background: #ffffff;
3465
3655
  transform-origin: top center;
3466
3656
  transition: transform 0.2s ease;
3657
+ max-width: calc(100% - 32px);
3658
+ max-height: calc(100% - 48px);
3467
3659
  `;
3468
3660
  const canvas = document.createElement("canvas");
3469
3661
  slideContainer.appendChild(canvas);
@@ -3471,10 +3663,22 @@ var PptxPlugin = class {
3471
3663
  ctx.container.innerHTML = "";
3472
3664
  ctx.container.style.overflow = "auto";
3473
3665
  ctx.container.appendChild(wrapper);
3666
+ const calculateFitScale = () => {
3667
+ const availW = Math.max(200, ctx.container.clientWidth - 48);
3668
+ const availH = Math.max(200, ctx.container.clientHeight - 72);
3669
+ const cW = canvas.offsetWidth || 1280;
3670
+ const cH = canvas.offsetHeight || 720;
3671
+ return Math.min(1, Math.min(availW / cW, availH / cH));
3672
+ };
3673
+ const applyTransform = () => {
3674
+ slideContainer.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
3675
+ };
3474
3676
  const renderCurrentSlide = async () => {
3475
3677
  try {
3476
3678
  await renderer.renderSlide(currentSlide - 1, canvas, 1280);
3477
3679
  ctx.emit("page-change", { page: currentSlide, totalPages: slideCount });
3680
+ scale = calculateFitScale();
3681
+ applyTransform();
3478
3682
  } catch (err) {
3479
3683
  console.error("[PptxPlugin] Failed to render slide:", err);
3480
3684
  }
@@ -3497,21 +3701,30 @@ var PptxPlugin = class {
3497
3701
  getPageCount: () => slideCount,
3498
3702
  getCurrentPage: () => currentSlide,
3499
3703
  zoomIn: () => {
3500
- scale += 0.1;
3501
- slideContainer.style.transform = `scale(${scale})`;
3704
+ scale += 0.15;
3705
+ applyTransform();
3502
3706
  },
3503
3707
  zoomOut: () => {
3504
- scale = Math.max(0.2, scale - 0.1);
3505
- slideContainer.style.transform = `scale(${scale})`;
3708
+ scale = Math.max(0.2, scale - 0.15);
3709
+ applyTransform();
3506
3710
  },
3507
3711
  getZoom: () => scale,
3508
3712
  setZoom: (level) => {
3509
3713
  scale = level;
3510
- slideContainer.style.transform = `scale(${scale})`;
3714
+ applyTransform();
3511
3715
  },
3512
3716
  fitToPage: () => {
3513
- scale = 1;
3514
- slideContainer.style.transform = "scale(1)";
3717
+ scale = calculateFitScale();
3718
+ rotation = 0;
3719
+ applyTransform();
3720
+ },
3721
+ rotateCW: () => {
3722
+ rotation = (rotation + 90) % 360;
3723
+ applyTransform();
3724
+ },
3725
+ rotateCCW: () => {
3726
+ rotation = (rotation - 90 + 360) % 360;
3727
+ applyTransform();
3515
3728
  },
3516
3729
  getThumbnails: () => {
3517
3730
  const list = [];
@@ -3792,6 +4005,14 @@ var RtfPlugin = class {
3792
4005
  group: "zoom",
3793
4006
  execute: () => instance.fitToPage?.()
3794
4007
  },
4008
+ {
4009
+ id: "rotate-cw",
4010
+ icon: "rotate-cw",
4011
+ label: "Rotate",
4012
+ type: "button",
4013
+ group: "view",
4014
+ execute: () => instance.rotateCW?.()
4015
+ },
3795
4016
  {
3796
4017
  id: "download",
3797
4018
  icon: "download",
@@ -3828,7 +4049,24 @@ var RtfPlugin = class {
3828
4049
  ctx.container.style.backgroundColor = "#f1f5f9";
3829
4050
  ctx.container.appendChild(wrapper);
3830
4051
  let scale = 1;
4052
+ let rotation = 0;
3831
4053
  let pageElements = [];
4054
+ const calculateFitScale = () => {
4055
+ const activeEl = pageElements[currentPage - 1] || wrapper;
4056
+ const elW = activeEl.offsetWidth || 850;
4057
+ const elH = activeEl.offsetHeight || 1e3;
4058
+ const availW = Math.max(200, ctx.container.clientWidth - 48);
4059
+ const availH = Math.max(200, ctx.container.clientHeight - 80);
4060
+ return Math.min(1.1, Math.min(availW / elW, availH / elH));
4061
+ };
4062
+ const applyTransform = () => {
4063
+ wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
4064
+ wrapper.style.transformOrigin = "top center";
4065
+ };
4066
+ setTimeout(() => {
4067
+ scale = calculateFitScale();
4068
+ applyTransform();
4069
+ }, 60);
3832
4070
  try {
3833
4071
  if (typeof RTFJS.loggingEnabled === "function") {
3834
4072
  RTFJS.loggingEnabled(false);
@@ -3905,21 +4143,30 @@ var RtfPlugin = class {
3905
4143
  getCurrentPage: () => currentPage,
3906
4144
  goToPage: (page) => showPage(page),
3907
4145
  zoomIn: () => {
3908
- scale += 0.1;
3909
- wrapper.style.transform = `scale(${scale})`;
4146
+ scale += 0.15;
4147
+ applyTransform();
3910
4148
  },
3911
4149
  zoomOut: () => {
3912
- scale = Math.max(0.2, scale - 0.1);
3913
- wrapper.style.transform = `scale(${scale})`;
4150
+ scale = Math.max(0.2, scale - 0.15);
4151
+ applyTransform();
3914
4152
  },
3915
4153
  getZoom: () => scale,
3916
4154
  setZoom: (level) => {
3917
4155
  scale = level;
3918
- wrapper.style.transform = `scale(${scale})`;
4156
+ applyTransform();
3919
4157
  },
3920
4158
  fitToPage: () => {
3921
- scale = 1;
3922
- wrapper.style.transform = "scale(1)";
4159
+ scale = calculateFitScale();
4160
+ rotation = 0;
4161
+ applyTransform();
4162
+ },
4163
+ rotateCW: () => {
4164
+ rotation = (rotation + 90) % 360;
4165
+ applyTransform();
4166
+ },
4167
+ rotateCCW: () => {
4168
+ rotation = (rotation - 90 + 360) % 360;
4169
+ applyTransform();
3923
4170
  },
3924
4171
  download: () => {
3925
4172
  const blob = new Blob([ctx.buffer], { type: "application/rtf" });
@@ -4147,11 +4394,19 @@ var OpenDocumentPlugin = class {
4147
4394
  {
4148
4395
  id: "fit-page",
4149
4396
  icon: "fit-page",
4150
- label: "Fit to Page",
4397
+ label: isPresentation ? "Fit to Slide" : "Fit to Page",
4151
4398
  type: "button",
4152
4399
  group: "zoom",
4153
4400
  execute: () => instance.fitToPage?.()
4154
4401
  },
4402
+ {
4403
+ id: "rotate-cw",
4404
+ icon: "rotate-cw",
4405
+ label: "Rotate",
4406
+ type: "button",
4407
+ group: "view",
4408
+ execute: () => instance.rotateCW?.()
4409
+ },
4155
4410
  {
4156
4411
  id: "download",
4157
4412
  icon: "download",
@@ -4215,6 +4470,22 @@ var OpenDocumentPlugin = class {
4215
4470
  container.appendChild(wrapper);
4216
4471
  ctx.container.appendChild(container);
4217
4472
  let scale = 1;
4473
+ let rotation = 0;
4474
+ const calculateFitScale = () => {
4475
+ const elW = wrapper.offsetWidth || 850;
4476
+ const elH = wrapper.offsetHeight || (isPresentation ? 540 : 1e3);
4477
+ const availW = Math.max(200, ctx.container.clientWidth - 48);
4478
+ const availH = Math.max(200, ctx.container.clientHeight - 80);
4479
+ return Math.min(1, Math.min(availW / elW, availH / elH));
4480
+ };
4481
+ const applyTransform = () => {
4482
+ wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
4483
+ wrapper.style.transformOrigin = "top center";
4484
+ };
4485
+ setTimeout(() => {
4486
+ scale = calculateFitScale();
4487
+ applyTransform();
4488
+ }, 60);
4218
4489
  let currentPage = 1;
4219
4490
  let totalPages = 1;
4220
4491
  let slides = [];
@@ -4273,21 +4544,30 @@ var OpenDocumentPlugin = class {
4273
4544
  destroy: cleanup,
4274
4545
  isPresentation,
4275
4546
  zoomIn: () => {
4276
- scale += 0.1;
4277
- wrapper.style.transform = `scale(${scale})`;
4547
+ scale += 0.15;
4548
+ applyTransform();
4278
4549
  },
4279
4550
  zoomOut: () => {
4280
- scale = Math.max(0.2, scale - 0.1);
4281
- wrapper.style.transform = `scale(${scale})`;
4551
+ scale = Math.max(0.2, scale - 0.15);
4552
+ applyTransform();
4282
4553
  },
4283
4554
  getZoom: () => scale,
4284
4555
  setZoom: (level) => {
4285
4556
  scale = level;
4286
- wrapper.style.transform = `scale(${scale})`;
4557
+ applyTransform();
4287
4558
  },
4288
4559
  fitToPage: () => {
4289
- scale = 1;
4290
- wrapper.style.transform = "scale(1)";
4560
+ scale = calculateFitScale();
4561
+ rotation = 0;
4562
+ applyTransform();
4563
+ },
4564
+ rotateCW: () => {
4565
+ rotation = (rotation + 90) % 360;
4566
+ applyTransform();
4567
+ },
4568
+ rotateCCW: () => {
4569
+ rotation = (rotation - 90 + 360) % 360;
4570
+ applyTransform();
4291
4571
  },
4292
4572
  goToPage,
4293
4573
  getPageCount: () => totalPages,
@@ -4557,6 +4837,14 @@ var DocPlugin = class {
4557
4837
  group: "zoom",
4558
4838
  execute: () => instance.fitToPage?.()
4559
4839
  },
4840
+ {
4841
+ id: "rotate-cw",
4842
+ icon: "rotate-cw",
4843
+ label: "Rotate",
4844
+ type: "button",
4845
+ group: "view",
4846
+ execute: () => instance.rotateCW?.()
4847
+ },
4560
4848
  {
4561
4849
  id: "copy",
4562
4850
  icon: "copy",
@@ -4695,6 +4983,23 @@ var DocPlugin = class {
4695
4983
  if (totalPages > 1) {
4696
4984
  showPage(1);
4697
4985
  }
4986
+ let rotation = 0;
4987
+ const calculateFitScale = () => {
4988
+ const activeCard = pageCards[currentPage - 1] || wrapper;
4989
+ const elW = activeCard.offsetWidth || 850;
4990
+ const elH = activeCard.offsetHeight || 1e3;
4991
+ const availW = Math.max(200, ctx.container.clientWidth - 48);
4992
+ const availH = Math.max(200, ctx.container.clientHeight - 80);
4993
+ return Math.min(1.1, Math.min(availW / elW, availH / elH));
4994
+ };
4995
+ const applyTransform = () => {
4996
+ wrapper.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
4997
+ wrapper.style.transformOrigin = "top center";
4998
+ };
4999
+ setTimeout(() => {
5000
+ scale = calculateFitScale();
5001
+ applyTransform();
5002
+ }, 60);
4698
5003
  const cleanup = () => {
4699
5004
  indicator?.remove();
4700
5005
  container.remove();
@@ -4707,21 +5012,30 @@ var DocPlugin = class {
4707
5012
  getCurrentPage: () => currentPage,
4708
5013
  goToPage: (page) => showPage(page),
4709
5014
  zoomIn: () => {
4710
- scale += 0.1;
4711
- wrapper.style.transform = `scale(${scale})`;
5015
+ scale += 0.15;
5016
+ applyTransform();
4712
5017
  },
4713
5018
  zoomOut: () => {
4714
- scale = Math.max(0.2, scale - 0.1);
4715
- wrapper.style.transform = `scale(${scale})`;
5019
+ scale = Math.max(0.2, scale - 0.15);
5020
+ applyTransform();
4716
5021
  },
4717
5022
  getZoom: () => scale,
4718
5023
  setZoom: (level) => {
4719
5024
  scale = level;
4720
- wrapper.style.transform = `scale(${scale})`;
5025
+ applyTransform();
4721
5026
  },
4722
5027
  fitToPage: () => {
4723
- scale = 1;
4724
- wrapper.style.transform = "scale(1)";
5028
+ scale = calculateFitScale();
5029
+ rotation = 0;
5030
+ applyTransform();
5031
+ },
5032
+ rotateCW: () => {
5033
+ rotation = (rotation + 90) % 360;
5034
+ applyTransform();
5035
+ },
5036
+ rotateCCW: () => {
5037
+ rotation = (rotation - 90 + 360) % 360;
5038
+ applyTransform();
4725
5039
  },
4726
5040
  copy: () => {
4727
5041
  navigator.clipboard.writeText(extractedRawText);
@@ -4961,6 +5275,14 @@ var PptPlugin = class {
4961
5275
  group: "zoom",
4962
5276
  execute: () => instance.fitToPage?.()
4963
5277
  },
5278
+ {
5279
+ id: "rotate-cw",
5280
+ icon: "rotate-cw",
5281
+ label: "Rotate",
5282
+ type: "button",
5283
+ group: "view",
5284
+ execute: () => instance.rotateCW?.()
5285
+ },
4964
5286
  {
4965
5287
  id: "download",
4966
5288
  icon: "download",
@@ -4994,22 +5316,36 @@ var PptPlugin = class {
4994
5316
  const slideCard = document.createElement("div");
4995
5317
  slideCard.className = "fp-ppt-slide-card";
4996
5318
  slideCard.style.width = "960px";
4997
- slideCard.style.maxWidth = "92%";
5319
+ slideCard.style.maxWidth = "calc(100% - 32px)";
5320
+ slideCard.style.maxHeight = "calc(100% - 48px)";
4998
5321
  slideCard.style.aspectRatio = "16 / 9";
4999
5322
  slideCard.style.backgroundColor = "#ffffff";
5000
5323
  slideCard.style.boxShadow = "0 12px 40px rgba(0,0,0,0.35)";
5001
5324
  slideCard.style.borderRadius = "8px";
5002
5325
  slideCard.style.boxSizing = "border-box";
5003
5326
  slideCard.style.position = "relative";
5004
- slideCard.style.overflow = "hidden";
5005
- slideCard.style.transformOrigin = "center center";
5006
5327
  slideCard.style.transition = "transform 0.2s ease";
5328
+ slideCard.style.transformOrigin = "center center";
5329
+ slideCard.style.flexShrink = "0";
5007
5330
  container.appendChild(slideCard);
5008
5331
  ctx.container.appendChild(container);
5009
5332
  let scale = 1;
5333
+ let rotation = 0;
5010
5334
  let currentSlide = 1;
5011
5335
  let slides = [];
5012
5336
  const createdBlobUrls = [];
5337
+ const calculateFitScale = () => {
5338
+ const availW = Math.max(200, container.clientWidth - 48);
5339
+ const availH = Math.max(200, container.clientHeight - 72);
5340
+ return Math.min(1, Math.min(availW / 960, availH / 540));
5341
+ };
5342
+ const applyTransform = () => {
5343
+ slideCard.style.transform = `scale(${scale}) rotate(${rotation}deg)`;
5344
+ };
5345
+ setTimeout(() => {
5346
+ scale = calculateFitScale();
5347
+ applyTransform();
5348
+ }, 60);
5013
5349
  try {
5014
5350
  const cfbf = new CfbfReader(ctx.buffer);
5015
5351
  const pptStream = cfbf.readStream("PowerPoint Document");
@@ -5110,21 +5446,30 @@ var PptPlugin = class {
5110
5446
  return {
5111
5447
  destroy: cleanup,
5112
5448
  zoomIn: () => {
5113
- scale += 0.1;
5114
- slideCard.style.transform = `scale(${scale})`;
5449
+ scale += 0.15;
5450
+ applyTransform();
5115
5451
  },
5116
5452
  zoomOut: () => {
5117
- scale = Math.max(0.3, scale - 0.1);
5118
- slideCard.style.transform = `scale(${scale})`;
5453
+ scale = Math.max(0.2, scale - 0.15);
5454
+ applyTransform();
5119
5455
  },
5120
5456
  getZoom: () => scale,
5121
5457
  setZoom: (level) => {
5122
5458
  scale = level;
5123
- slideCard.style.transform = `scale(${scale})`;
5459
+ applyTransform();
5124
5460
  },
5125
5461
  fitToPage: () => {
5126
- scale = 1;
5127
- slideCard.style.transform = "scale(1)";
5462
+ scale = calculateFitScale();
5463
+ rotation = 0;
5464
+ applyTransform();
5465
+ },
5466
+ rotateCW: () => {
5467
+ rotation = (rotation + 90) % 360;
5468
+ applyTransform();
5469
+ },
5470
+ rotateCCW: () => {
5471
+ rotation = (rotation - 90 + 360) % 360;
5472
+ applyTransform();
5128
5473
  },
5129
5474
  goToPage: (page) => {
5130
5475
  if (page >= 1 && page <= totalSlides) {