pptx-react-viewer 1.1.4 → 1.1.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.
Files changed (38) hide show
  1. package/dist/index.js +865 -159
  2. package/dist/index.mjs +866 -161
  3. package/dist/viewer/index.js +883 -165
  4. package/dist/viewer/index.mjs +884 -167
  5. package/node_modules/emf-converter/dist/index.d.mts +2 -2
  6. package/node_modules/emf-converter/dist/index.d.ts +2 -2
  7. package/node_modules/emf-converter/dist/index.js +91 -33
  8. package/node_modules/emf-converter/dist/index.mjs +91 -33
  9. package/node_modules/emf-converter/package.json +1 -1
  10. package/node_modules/mtx-decompressor/dist/index.js +39 -9
  11. package/node_modules/mtx-decompressor/dist/index.mjs +39 -9
  12. package/node_modules/mtx-decompressor/package.json +1 -1
  13. package/node_modules/pptx-viewer-core/dist/{SvgExporter-BQ4KbRO9.d.mts → SvgExporter-BTkk4oNQ.d.mts} +1 -1
  14. package/node_modules/pptx-viewer-core/dist/{SvgExporter-0TxiiorD.d.ts → SvgExporter-CTDG-t_z.d.ts} +1 -1
  15. package/node_modules/pptx-viewer-core/dist/cli/index.d.mts +2 -2
  16. package/node_modules/pptx-viewer-core/dist/cli/index.d.ts +2 -2
  17. package/node_modules/pptx-viewer-core/dist/cli/index.js +0 -0
  18. package/node_modules/pptx-viewer-core/dist/cli/index.mjs +0 -0
  19. package/node_modules/pptx-viewer-core/dist/converter/index.d.mts +3 -3
  20. package/node_modules/pptx-viewer-core/dist/converter/index.d.ts +3 -3
  21. package/node_modules/pptx-viewer-core/dist/converter/index.js +0 -0
  22. package/node_modules/pptx-viewer-core/dist/converter/index.mjs +0 -0
  23. package/node_modules/pptx-viewer-core/dist/index.d.mts +961 -59
  24. package/node_modules/pptx-viewer-core/dist/index.d.ts +961 -59
  25. package/node_modules/pptx-viewer-core/dist/index.js +29880 -16692
  26. package/node_modules/pptx-viewer-core/dist/index.mjs +29859 -16692
  27. package/node_modules/pptx-viewer-core/dist/{presentation-ArhfImJ5.d.mts → presentation-4fhI3din.d.mts} +835 -26
  28. package/node_modules/pptx-viewer-core/dist/{presentation-ArhfImJ5.d.ts → presentation-4fhI3din.d.ts} +835 -26
  29. package/node_modules/pptx-viewer-core/dist/{signature-inspection-status-BcJSdOvb.d.mts → signature-inspection-status-BCUpfCQh.d.mts} +13 -2
  30. package/node_modules/pptx-viewer-core/dist/{signature-inspection-status-BcJSdOvb.d.ts → signature-inspection-status-BCUpfCQh.d.ts} +13 -2
  31. package/node_modules/pptx-viewer-core/dist/signature-node/index.d.mts +2 -2
  32. package/node_modules/pptx-viewer-core/dist/signature-node/index.d.ts +2 -2
  33. package/node_modules/pptx-viewer-core/dist/signature-node/index.js +17 -3
  34. package/node_modules/pptx-viewer-core/dist/signature-node/index.mjs +16 -4
  35. package/node_modules/pptx-viewer-core/dist/{text-operations-rhJV-A_W.d.ts → text-operations-B9EwbptL.d.ts} +1 -1
  36. package/node_modules/pptx-viewer-core/dist/{text-operations-CLj-sJyk.d.mts → text-operations-C89Jn6S0.d.mts} +1 -1
  37. package/node_modules/pptx-viewer-core/package.json +1 -1
  38. package/package.json +6 -4
package/dist/index.mjs CHANGED
@@ -1,11 +1,12 @@
1
1
  import * as React10 from 'react';
2
- import React10__default, { createContext, useState, useEffect, useMemo, Suspense, useCallback, forwardRef, useRef, useSyncExternalStore, useImperativeHandle, useContext, useLayoutEffect } from 'react';
2
+ import React10__default, { createContext, useState, useEffect, useMemo, Suspense, useCallback, forwardRef, useRef, useSyncExternalStore, useImperativeHandle, useContext, useLayoutEffect, useDeferredValue } from 'react';
3
3
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
4
4
  import * as ReactDOM from 'react-dom/client';
5
5
  import { clsx } from 'clsx';
6
6
  import { twMerge } from 'tailwind-merge';
7
7
  import { LuMessageSquare, LuEyeOff, LuSettings, LuX, LuCast, LuShieldCheck, LuPlus, LuPanelLeftClose, LuStickyNote, LuMonitor, LuColumns2, LuPresentation, LuMinus, LuClock, LuDownload, LuTrash2, LuCheck, LuLock, LuEye, LuFileText, LuType, LuLoader, LuShieldAlert, LuSignature, LuInfo, LuTriangleAlert, LuPrinter, LuPenTool, LuWifi, LuWifiOff, LuUsers, LuCopy, LuMonitorOff, LuChevronLeft, LuChevronRight, LuPlay, LuPause, LuPanelLeft, LuUndo, LuRedo, LuSearch, LuShare2, LuPanelRight, LuFolderOpen, LuVideo, LuImage, LuClipboardPaste, LuScissors, LuPaintbrush, LuChevronDown, LuSquare, LuDatabase, LuLayers, LuAArrowUp, LuAArrowDown, LuRemoveFormatting, LuHighlighter, LuList, LuListOrdered, LuIndentDecrease, LuIndentIncrease, LuChevronUp, LuPalette, LuPencil, LuPaintBucket, LuSparkles, LuCaptions, LuSpellCheck, LuGitCompare, LuPipette, LuCaseSensitive, LuReplace, LuTimer, LuMousePointer2, LuEraser, LuGripVertical, LuUpload, LuBold, LuItalic, LuUnderline, LuStrikethrough, LuLink, LuGrid2X2, LuCopyPlus, LuEllipsis, LuCircle, LuMoveRight, LuTriangle, LuDiamond, LuAlignLeft, LuAlignCenter, LuAlignRight, LuAlignJustify, LuSpline, LuSettings2, LuMove, LuRadio, LuArrowDown, LuArrowUp, LuArrowRight, LuArrowLeft, LuReply, LuRotateCw, LuBookmark } from 'react-icons/lu';
8
- import { hasShapeProperties, hasTextProperties, SWITCHABLE_LAYOUT_TYPES, isCalloutShape, getCalloutLeaderLineGeometry, buildCalloutLeaderLineSvgPath, getCalloutViewBoxBounds, isInkElement, getLinkedTextBoxSegments, isImageLikeElement, getSubstituteFontFamily, PptxHandler, EncryptedFileError, guidePxToEmu, guideEmuToPx, THEME_COLOR_SCHEME_KEYS, hslToRgb, PRESET_COLOR_MAP, elementActionToPptxAction, mergeShapes, SvgExporter, applyDrawingColorTransforms as applyDrawingColorTransforms$1, getPresetShapeClipPath, svgPathToPolygons, polygonsToSvgPath, EMU_PER_PX as EMU_PER_PX$1, chartDataChangeType, chartDataUpdatePoint, chartDataAddCategory, chartDataRemoveCategory, chartDataAddSeries, chartDataRemoveSeries, getOleObjectTypeLabel, pptxActionToElementAction, hasNonTrivialOverride, COLOR_MAP_ALIAS_KEYS, DEFAULT_COLOR_MAP, addSmartArtNodeAsChild, updateSmartArtNodeText, removeSmartArtNode, switchSmartArtLayout } from 'pptx-viewer-core';
8
+ import { hasShapeProperties, hasTextProperties, SWITCHABLE_LAYOUT_TYPES, isCalloutShape, getCalloutLeaderLineGeometry, buildCalloutLeaderLineSvgPath, getCalloutViewBoxBounds, isInkElement, getLinkedTextBoxSegments, isImageLikeElement, getSubstituteFontFamily, getAdjustmentAwareShapeClipPath, getShapeClipPathFromPreset, getCloudPathForRendering, PptxHandler, EncryptedFileError, guidePxToEmu, guideEmuToPx, THEME_COLOR_SCHEME_KEYS, hslToRgb, PRESET_COLOR_MAP, elementActionToPptxAction, mergeShapes, SvgExporter, applyDrawingColorTransforms as applyDrawingColorTransforms$1, getPresetShapeClipPath, svgPathToPolygons, polygonsToSvgPath, EMU_PER_PX as EMU_PER_PX$1, chartDataChangeType, chartDataUpdatePoint, chartDataAddCategory, chartDataRemoveCategory, chartDataAddSeries, chartDataRemoveSeries, getOleObjectTypeLabel, pptxActionToElementAction, hasNonTrivialOverride, COLOR_MAP_ALIAS_KEYS, DEFAULT_COLOR_MAP, addSmartArtNodeAsChild, updateSmartArtNodeText, removeSmartArtNode, switchSmartArtLayout } from 'pptx-viewer-core';
9
+ import DOMPurify from 'dompurify';
9
10
  import { useTranslation } from 'react-i18next';
10
11
  import html2canvasPro from 'html2canvas-pro';
11
12
  import JSZip from 'jszip';
@@ -170,7 +171,7 @@ function generateUUID() {
170
171
  const uuid = _lut[d0 & 255] + _lut[d0 >> 8 & 255] + _lut[d0 >> 16 & 255] + _lut[d0 >> 24 & 255] + "-" + _lut[d1 & 255] + _lut[d1 >> 8 & 255] + "-" + _lut[d1 >> 16 & 15 | 64] + _lut[d1 >> 24 & 255] + "-" + _lut[d2 & 63 | 128] + _lut[d2 >> 8 & 255] + "-" + _lut[d2 >> 16 & 255] + _lut[d2 >> 24 & 255] + _lut[d3 & 255] + _lut[d3 >> 8 & 255] + _lut[d3 >> 16 & 255] + _lut[d3 >> 24 & 255];
171
172
  return uuid.toLowerCase();
172
173
  }
173
- function clamp(value, min2, max2) {
174
+ function clamp2(value, min2, max2) {
174
175
  return Math.max(min2, Math.min(max2, value));
175
176
  }
176
177
  function euclideanModulo(n, m2) {
@@ -545,7 +546,7 @@ function _generateTables() {
545
546
  }
546
547
  function toHalfFloat(val2) {
547
548
  if (Math.abs(val2) > 65504) warn("DataUtils.toHalfFloat(): Value out of range.");
548
- val2 = clamp(val2, -65504, 65504);
549
+ val2 = clamp2(val2, -65504, 65504);
549
550
  _tables.floatView[0] = val2;
550
551
  const f = _tables.uint32View[0];
551
552
  const e2 = f >> 23 & 511;
@@ -2034,7 +2035,7 @@ var init_three_core = __esm({
2034
2035
  * @param {number} max - The max value.
2035
2036
  * @return {number} The clamped value.
2036
2037
  */
2037
- clamp,
2038
+ clamp: clamp2,
2038
2039
  /**
2039
2040
  * Computes the Euclidean modulo of the given parameters that
2040
2041
  * is `( ( n % m ) + m ) % m`.
@@ -2561,8 +2562,8 @@ var init_three_core = __esm({
2561
2562
  * @return {Vector2} A reference to this vector.
2562
2563
  */
2563
2564
  clamp(min2, max2) {
2564
- this.x = clamp(this.x, min2.x, max2.x);
2565
- this.y = clamp(this.y, min2.y, max2.y);
2565
+ this.x = clamp2(this.x, min2.x, max2.x);
2566
+ this.y = clamp2(this.y, min2.y, max2.y);
2566
2567
  return this;
2567
2568
  }
2568
2569
  /**
@@ -2576,8 +2577,8 @@ var init_three_core = __esm({
2576
2577
  * @return {Vector2} A reference to this vector.
2577
2578
  */
2578
2579
  clampScalar(minVal, maxVal) {
2579
- this.x = clamp(this.x, minVal, maxVal);
2580
- this.y = clamp(this.y, minVal, maxVal);
2580
+ this.x = clamp2(this.x, minVal, maxVal);
2581
+ this.y = clamp2(this.y, minVal, maxVal);
2581
2582
  return this;
2582
2583
  }
2583
2584
  /**
@@ -2592,7 +2593,7 @@ var init_three_core = __esm({
2592
2593
  */
2593
2594
  clampLength(min2, max2) {
2594
2595
  const length2 = this.length();
2595
- return this.divideScalar(length2 || 1).multiplyScalar(clamp(length2, min2, max2));
2596
+ return this.divideScalar(length2 || 1).multiplyScalar(clamp2(length2, min2, max2));
2596
2597
  }
2597
2598
  /**
2598
2599
  * The components of this vector are rounded down to the nearest integer value.
@@ -2717,7 +2718,7 @@ var init_three_core = __esm({
2717
2718
  const denominator = Math.sqrt(this.lengthSq() * v.lengthSq());
2718
2719
  if (denominator === 0) return Math.PI / 2;
2719
2720
  const theta = this.dot(v) / denominator;
2720
- return Math.acos(clamp(theta, -1, 1));
2721
+ return Math.acos(clamp2(theta, -1, 1));
2721
2722
  }
2722
2723
  /**
2723
2724
  * Computes the distance from the given vector to this instance.
@@ -3204,7 +3205,7 @@ var init_three_core = __esm({
3204
3205
  * @return {number} The angle in radians.
3205
3206
  */
3206
3207
  angleTo(q) {
3207
- return 2 * Math.acos(Math.abs(clamp(this.dot(q), -1, 1)));
3208
+ return 2 * Math.acos(Math.abs(clamp2(this.dot(q), -1, 1)));
3208
3209
  }
3209
3210
  /**
3210
3211
  * Rotates this quaternion by a given angular step to the given quaternion.
@@ -3910,9 +3911,9 @@ var init_three_core = __esm({
3910
3911
  * @return {Vector3} A reference to this vector.
3911
3912
  */
3912
3913
  clamp(min2, max2) {
3913
- this.x = clamp(this.x, min2.x, max2.x);
3914
- this.y = clamp(this.y, min2.y, max2.y);
3915
- this.z = clamp(this.z, min2.z, max2.z);
3914
+ this.x = clamp2(this.x, min2.x, max2.x);
3915
+ this.y = clamp2(this.y, min2.y, max2.y);
3916
+ this.z = clamp2(this.z, min2.z, max2.z);
3916
3917
  return this;
3917
3918
  }
3918
3919
  /**
@@ -3926,9 +3927,9 @@ var init_three_core = __esm({
3926
3927
  * @return {Vector3} A reference to this vector.
3927
3928
  */
3928
3929
  clampScalar(minVal, maxVal) {
3929
- this.x = clamp(this.x, minVal, maxVal);
3930
- this.y = clamp(this.y, minVal, maxVal);
3931
- this.z = clamp(this.z, minVal, maxVal);
3930
+ this.x = clamp2(this.x, minVal, maxVal);
3931
+ this.y = clamp2(this.y, minVal, maxVal);
3932
+ this.z = clamp2(this.z, minVal, maxVal);
3932
3933
  return this;
3933
3934
  }
3934
3935
  /**
@@ -3943,7 +3944,7 @@ var init_three_core = __esm({
3943
3944
  */
3944
3945
  clampLength(min2, max2) {
3945
3946
  const length2 = this.length();
3946
- return this.divideScalar(length2 || 1).multiplyScalar(clamp(length2, min2, max2));
3947
+ return this.divideScalar(length2 || 1).multiplyScalar(clamp2(length2, min2, max2));
3947
3948
  }
3948
3949
  /**
3949
3950
  * The components of this vector are rounded down to the nearest integer value.
@@ -4153,7 +4154,7 @@ var init_three_core = __esm({
4153
4154
  const denominator = Math.sqrt(this.lengthSq() * v.lengthSq());
4154
4155
  if (denominator === 0) return Math.PI / 2;
4155
4156
  const theta = this.dot(v) / denominator;
4156
- return Math.acos(clamp(theta, -1, 1));
4157
+ return Math.acos(clamp2(theta, -1, 1));
4157
4158
  }
4158
4159
  /**
4159
4160
  * Computes the distance from the given vector to this instance.
@@ -5842,10 +5843,10 @@ var init_three_core = __esm({
5842
5843
  * @return {Vector4} A reference to this vector.
5843
5844
  */
5844
5845
  clamp(min2, max2) {
5845
- this.x = clamp(this.x, min2.x, max2.x);
5846
- this.y = clamp(this.y, min2.y, max2.y);
5847
- this.z = clamp(this.z, min2.z, max2.z);
5848
- this.w = clamp(this.w, min2.w, max2.w);
5846
+ this.x = clamp2(this.x, min2.x, max2.x);
5847
+ this.y = clamp2(this.y, min2.y, max2.y);
5848
+ this.z = clamp2(this.z, min2.z, max2.z);
5849
+ this.w = clamp2(this.w, min2.w, max2.w);
5849
5850
  return this;
5850
5851
  }
5851
5852
  /**
@@ -5859,10 +5860,10 @@ var init_three_core = __esm({
5859
5860
  * @return {Vector4} A reference to this vector.
5860
5861
  */
5861
5862
  clampScalar(minVal, maxVal) {
5862
- this.x = clamp(this.x, minVal, maxVal);
5863
- this.y = clamp(this.y, minVal, maxVal);
5864
- this.z = clamp(this.z, minVal, maxVal);
5865
- this.w = clamp(this.w, minVal, maxVal);
5863
+ this.x = clamp2(this.x, minVal, maxVal);
5864
+ this.y = clamp2(this.y, minVal, maxVal);
5865
+ this.z = clamp2(this.z, minVal, maxVal);
5866
+ this.w = clamp2(this.w, minVal, maxVal);
5866
5867
  return this;
5867
5868
  }
5868
5869
  /**
@@ -5877,7 +5878,7 @@ var init_three_core = __esm({
5877
5878
  */
5878
5879
  clampLength(min2, max2) {
5879
5880
  const length2 = this.length();
5880
- return this.divideScalar(length2 || 1).multiplyScalar(clamp(length2, min2, max2));
5881
+ return this.divideScalar(length2 || 1).multiplyScalar(clamp2(length2, min2, max2));
5881
5882
  }
5882
5883
  /**
5883
5884
  * The components of this vector are rounded down to the nearest integer value.
@@ -7660,7 +7661,7 @@ var init_three_core = __esm({
7660
7661
  const m31 = te[2], m32 = te[6], m33 = te[10];
7661
7662
  switch (order) {
7662
7663
  case "XYZ":
7663
- this._y = Math.asin(clamp(m13, -1, 1));
7664
+ this._y = Math.asin(clamp2(m13, -1, 1));
7664
7665
  if (Math.abs(m13) < 0.9999999) {
7665
7666
  this._x = Math.atan2(-m23, m33);
7666
7667
  this._z = Math.atan2(-m12, m11);
@@ -7670,7 +7671,7 @@ var init_three_core = __esm({
7670
7671
  }
7671
7672
  break;
7672
7673
  case "YXZ":
7673
- this._x = Math.asin(-clamp(m23, -1, 1));
7674
+ this._x = Math.asin(-clamp2(m23, -1, 1));
7674
7675
  if (Math.abs(m23) < 0.9999999) {
7675
7676
  this._y = Math.atan2(m13, m33);
7676
7677
  this._z = Math.atan2(m21, m22);
@@ -7680,7 +7681,7 @@ var init_three_core = __esm({
7680
7681
  }
7681
7682
  break;
7682
7683
  case "ZXY":
7683
- this._x = Math.asin(clamp(m32, -1, 1));
7684
+ this._x = Math.asin(clamp2(m32, -1, 1));
7684
7685
  if (Math.abs(m32) < 0.9999999) {
7685
7686
  this._y = Math.atan2(-m31, m33);
7686
7687
  this._z = Math.atan2(-m12, m22);
@@ -7690,7 +7691,7 @@ var init_three_core = __esm({
7690
7691
  }
7691
7692
  break;
7692
7693
  case "ZYX":
7693
- this._y = Math.asin(-clamp(m31, -1, 1));
7694
+ this._y = Math.asin(-clamp2(m31, -1, 1));
7694
7695
  if (Math.abs(m31) < 0.9999999) {
7695
7696
  this._x = Math.atan2(m32, m33);
7696
7697
  this._z = Math.atan2(m21, m11);
@@ -7700,7 +7701,7 @@ var init_three_core = __esm({
7700
7701
  }
7701
7702
  break;
7702
7703
  case "YZX":
7703
- this._z = Math.asin(clamp(m21, -1, 1));
7704
+ this._z = Math.asin(clamp2(m21, -1, 1));
7704
7705
  if (Math.abs(m21) < 0.9999999) {
7705
7706
  this._x = Math.atan2(-m23, m22);
7706
7707
  this._y = Math.atan2(-m31, m11);
@@ -7710,7 +7711,7 @@ var init_three_core = __esm({
7710
7711
  }
7711
7712
  break;
7712
7713
  case "XZY":
7713
- this._z = Math.asin(-clamp(m12, -1, 1));
7714
+ this._z = Math.asin(-clamp2(m12, -1, 1));
7714
7715
  if (Math.abs(m12) < 0.9999999) {
7715
7716
  this._x = Math.atan2(m32, m22);
7716
7717
  this._y = Math.atan2(m13, m11);
@@ -9334,8 +9335,8 @@ var init_three_core = __esm({
9334
9335
  */
9335
9336
  setHSL(h2, s, l2, colorSpace = ColorManagement.workingColorSpace) {
9336
9337
  h2 = euclideanModulo(h2, 1);
9337
- s = clamp(s, 0, 1);
9338
- l2 = clamp(l2, 0, 1);
9338
+ s = clamp2(s, 0, 1);
9339
+ l2 = clamp2(l2, 0, 1);
9339
9340
  if (s === 0) {
9340
9341
  this.r = this.g = this.b = l2;
9341
9342
  } else {
@@ -9521,7 +9522,7 @@ var init_three_core = __esm({
9521
9522
  */
9522
9523
  getHex(colorSpace = SRGBColorSpace) {
9523
9524
  ColorManagement.workingToColorSpace(_color.copy(this), colorSpace);
9524
- return Math.round(clamp(_color.r * 255, 0, 255)) * 65536 + Math.round(clamp(_color.g * 255, 0, 255)) * 256 + Math.round(clamp(_color.b * 255, 0, 255));
9525
+ return Math.round(clamp2(_color.r * 255, 0, 255)) * 65536 + Math.round(clamp2(_color.g * 255, 0, 255)) * 256 + Math.round(clamp2(_color.b * 255, 0, 255));
9525
9526
  }
9526
9527
  /**
9527
9528
  * Returns the hexadecimal value of this color as a string (for example, 'FFFFFF').
@@ -18452,13 +18453,13 @@ var init_three_core = __esm({
18452
18453
  vec.crossVectors(tangents[i3 - 1], tangents[i3]);
18453
18454
  if (vec.length() > Number.EPSILON) {
18454
18455
  vec.normalize();
18455
- const theta = Math.acos(clamp(tangents[i3 - 1].dot(tangents[i3]), -1, 1));
18456
+ const theta = Math.acos(clamp2(tangents[i3 - 1].dot(tangents[i3]), -1, 1));
18456
18457
  normals[i3].applyMatrix4(mat.makeRotationAxis(vec, theta));
18457
18458
  }
18458
18459
  binormals[i3].crossVectors(tangents[i3], normals[i3]);
18459
18460
  }
18460
18461
  if (closed === true) {
18461
- let theta = Math.acos(clamp(normals[0].dot(normals[segments]), -1, 1));
18462
+ let theta = Math.acos(clamp2(normals[0].dot(normals[segments]), -1, 1));
18462
18463
  theta /= segments;
18463
18464
  if (tangents[0].dot(vec.crossVectors(normals[0], normals[segments])) > 0) {
18464
18465
  theta = -theta;
@@ -20247,7 +20248,7 @@ var init_three_core = __esm({
20247
20248
  phiLength
20248
20249
  };
20249
20250
  segments = Math.floor(segments);
20250
- phiLength = clamp(phiLength, 0, Math.PI * 2);
20251
+ phiLength = clamp2(phiLength, 0, Math.PI * 2);
20251
20252
  const indices = [];
20252
20253
  const vertices = [];
20253
20254
  const uvs = [];
@@ -21463,7 +21464,7 @@ var init_three_core = __esm({
21463
21464
  this.ior = 1.5;
21464
21465
  Object.defineProperty(this, "reflectivity", {
21465
21466
  get: function() {
21466
- return clamp(2.5 * (this.ior - 1) / (this.ior + 1), 0, 1);
21467
+ return clamp2(2.5 * (this.ior - 1) / (this.ior + 1), 0, 1);
21467
21468
  },
21468
21469
  set: function(reflectivity) {
21469
21470
  this.ior = (1 + 0.4 * reflectivity) / (1 - 0.4 * reflectivity);
@@ -29731,7 +29732,7 @@ var init_three_core = __esm({
29731
29732
  */
29732
29733
  makeSafe() {
29733
29734
  const EPS = 1e-6;
29734
- this.phi = clamp(this.phi, EPS, Math.PI - EPS);
29735
+ this.phi = clamp2(this.phi, EPS, Math.PI - EPS);
29735
29736
  return this;
29736
29737
  }
29737
29738
  /**
@@ -29759,7 +29760,7 @@ var init_three_core = __esm({
29759
29760
  this.phi = 0;
29760
29761
  } else {
29761
29762
  this.theta = Math.atan2(x2, z);
29762
- this.phi = Math.acos(clamp(y / this.radius, -1, 1));
29763
+ this.phi = Math.acos(clamp2(y / this.radius, -1, 1));
29763
29764
  }
29764
29765
  return this;
29765
29766
  }
@@ -30274,7 +30275,7 @@ var init_three_core = __esm({
30274
30275
  const startEnd_startP = _startEnd.dot(_startP);
30275
30276
  let t2 = startEnd_startP / startEnd2;
30276
30277
  if (clampToLine) {
30277
- t2 = clamp(t2, 0, 1);
30278
+ t2 = clamp2(t2, 0, 1);
30278
30279
  }
30279
30280
  return t2;
30280
30281
  }
@@ -30320,27 +30321,27 @@ var init_three_core = __esm({
30320
30321
  if (a2 <= EPSILON) {
30321
30322
  s = 0;
30322
30323
  t2 = f / e2;
30323
- t2 = clamp(t2, 0, 1);
30324
+ t2 = clamp2(t2, 0, 1);
30324
30325
  } else {
30325
30326
  const c3 = _d1.dot(_r);
30326
30327
  if (e2 <= EPSILON) {
30327
30328
  t2 = 0;
30328
- s = clamp(-c3 / a2, 0, 1);
30329
+ s = clamp2(-c3 / a2, 0, 1);
30329
30330
  } else {
30330
30331
  const b2 = _d1.dot(_d2);
30331
30332
  const denom = a2 * e2 - b2 * b2;
30332
30333
  if (denom !== 0) {
30333
- s = clamp((b2 * f - c3 * e2) / denom, 0, 1);
30334
+ s = clamp2((b2 * f - c3 * e2) / denom, 0, 1);
30334
30335
  } else {
30335
30336
  s = 0;
30336
30337
  }
30337
30338
  t2 = (b2 * s + f) / e2;
30338
30339
  if (t2 < 0) {
30339
30340
  t2 = 0;
30340
- s = clamp(-c3 / a2, 0, 1);
30341
+ s = clamp2(-c3 / a2, 0, 1);
30341
30342
  } else if (t2 > 1) {
30342
30343
  t2 = 1;
30343
- s = clamp((b2 - c3) / a2, 0, 1);
30344
+ s = clamp2((b2 - c3) / a2, 0, 1);
30344
30345
  }
30345
30346
  }
30346
30347
  }
@@ -43570,7 +43571,7 @@ var require_use_sync_external_store_shim_development = __commonJS({
43570
43571
  return x2 === y && (0 !== x2 || 1 / x2 === 1 / y) || x2 !== x2 && y !== y;
43571
43572
  }
43572
43573
  function useSyncExternalStore$2(subscribe3, getSnapshot2) {
43573
- didWarnOld18Alpha || void 0 === React97.startTransition || (didWarnOld18Alpha = true, console.error(
43574
+ didWarnOld18Alpha || void 0 === React100.startTransition || (didWarnOld18Alpha = true, console.error(
43574
43575
  "You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release."
43575
43576
  ));
43576
43577
  var value = getSnapshot2();
@@ -43580,7 +43581,7 @@ var require_use_sync_external_store_shim_development = __commonJS({
43580
43581
  "The result of getSnapshot should be cached to avoid an infinite loop"
43581
43582
  ), didWarnUncachedGetSnapshot = true);
43582
43583
  }
43583
- cachedValue = useState85({
43584
+ cachedValue = useState86({
43584
43585
  inst: { value, getSnapshot: getSnapshot2 }
43585
43586
  });
43586
43587
  var inst = cachedValue[0].inst, forceUpdate = cachedValue[1];
@@ -43618,8 +43619,8 @@ var require_use_sync_external_store_shim_development = __commonJS({
43618
43619
  return getSnapshot2();
43619
43620
  }
43620
43621
  "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
43621
- var React97 = __require("react"), objectIs = "function" === typeof Object.is ? Object.is : is2, useState85 = React97.useState, useEffect72 = React97.useEffect, useLayoutEffect7 = React97.useLayoutEffect, useDebugValue = React97.useDebugValue, didWarnOld18Alpha = false, didWarnUncachedGetSnapshot = false, shim = "undefined" === typeof window || "undefined" === typeof window.document || "undefined" === typeof window.document.createElement ? useSyncExternalStore$1 : useSyncExternalStore$2;
43622
- exports$1.useSyncExternalStore = void 0 !== React97.useSyncExternalStore ? React97.useSyncExternalStore : shim;
43622
+ var React100 = __require("react"), objectIs = "function" === typeof Object.is ? Object.is : is2, useState86 = React100.useState, useEffect72 = React100.useEffect, useLayoutEffect7 = React100.useLayoutEffect, useDebugValue = React100.useDebugValue, didWarnOld18Alpha = false, didWarnUncachedGetSnapshot = false, shim = "undefined" === typeof window || "undefined" === typeof window.document || "undefined" === typeof window.document.createElement ? useSyncExternalStore$1 : useSyncExternalStore$2;
43623
+ exports$1.useSyncExternalStore = void 0 !== React100.useSyncExternalStore ? React100.useSyncExternalStore : shim;
43623
43624
  "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());
43624
43625
  })();
43625
43626
  }
@@ -43642,7 +43643,7 @@ var require_with_selector_development = __commonJS({
43642
43643
  return x2 === y && (0 !== x2 || 1 / x2 === 1 / y) || x2 !== x2 && y !== y;
43643
43644
  }
43644
43645
  "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
43645
- var React97 = __require("react"), shim = require_shim(), objectIs = "function" === typeof Object.is ? Object.is : is2, useSyncExternalStore3 = shim.useSyncExternalStore, useRef73 = React97.useRef, useEffect72 = React97.useEffect, useMemo41 = React97.useMemo, useDebugValue = React97.useDebugValue;
43646
+ var React100 = __require("react"), shim = require_shim(), objectIs = "function" === typeof Object.is ? Object.is : is2, useSyncExternalStore3 = shim.useSyncExternalStore, useRef73 = React100.useRef, useEffect72 = React100.useEffect, useMemo41 = React100.useMemo, useDebugValue = React100.useDebugValue;
43646
43647
  exports$1.useSyncExternalStoreWithSelector = function(subscribe3, getSnapshot2, getServerSnapshot2, selector, isEqual) {
43647
43648
  var instRef = useRef73(null);
43648
43649
  if (null === instRef.current) {
@@ -70290,6 +70291,55 @@ var ACTION_BUTTON_PRESETS = [
70290
70291
  label: "Return",
70291
70292
  defaultAction: "prevSlide",
70292
70293
  iconPath: "M18 8 L18 14 L6 14 M6 14 L10 10 M6 14 L10 18"
70294
+ },
70295
+ {
70296
+ shapeType: "actionButtonHome",
70297
+ label: "Home",
70298
+ defaultAction: "firstSlide",
70299
+ // House: roof + body
70300
+ iconPath: "M12 4 L20 11 L20 20 L14 20 L14 14 L10 14 L10 20 L4 20 L4 11 Z"
70301
+ },
70302
+ {
70303
+ shapeType: "actionButtonHelp",
70304
+ label: "Help",
70305
+ defaultAction: "none",
70306
+ // Question mark
70307
+ iconPath: "M9 9 a3 3 0 1 1 4 2.8 c-1 0.4 -1 1.2 -1 2 M12 17 v0.5"
70308
+ },
70309
+ {
70310
+ shapeType: "actionButtonInformation",
70311
+ label: "Information",
70312
+ defaultAction: "none",
70313
+ // Lower-case "i": dot + body
70314
+ iconPath: "M12 6 v0.01 M12 10 v8"
70315
+ },
70316
+ {
70317
+ shapeType: "actionButtonDocument",
70318
+ label: "Document",
70319
+ defaultAction: "none",
70320
+ // Document with folded corner
70321
+ iconPath: "M6 4 L14 4 L18 8 L18 20 L6 20 Z M14 4 L14 8 L18 8"
70322
+ },
70323
+ {
70324
+ shapeType: "actionButtonSound",
70325
+ label: "Sound",
70326
+ defaultAction: "none",
70327
+ // Speaker cone + sound waves
70328
+ iconPath: "M4 10 L4 14 L8 14 L12 18 L12 6 L8 10 Z M16 9 a4 4 0 0 1 0 6 M18 7 a7 7 0 0 1 0 10"
70329
+ },
70330
+ {
70331
+ shapeType: "actionButtonMovie",
70332
+ label: "Movie",
70333
+ defaultAction: "none",
70334
+ // Film strip with play triangle
70335
+ iconPath: "M4 6 L20 6 L20 18 L4 18 Z M10 9 L15 12 L10 15 Z"
70336
+ },
70337
+ {
70338
+ shapeType: "actionButtonBlank",
70339
+ label: "Custom",
70340
+ defaultAction: "none",
70341
+ // No glyph — empty path. The button still renders as a rounded rect via clip-path.
70342
+ iconPath: ""
70293
70343
  }
70294
70344
  ];
70295
70345
  Object.fromEntries(ACTION_BUTTON_PRESETS.map((p3) => [p3.shapeType, p3.defaultAction]));
@@ -72569,8 +72619,8 @@ function hexToRgb2(hex) {
72569
72619
  };
72570
72620
  }
72571
72621
  function rgbToHex(r2, g2, b2) {
72572
- const clamp2 = (v) => Math.max(0, Math.min(255, Math.round(v)));
72573
- return `#${clamp2(r2).toString(16).padStart(2, "0")}${clamp2(g2).toString(16).padStart(2, "0")}${clamp2(b2).toString(16).padStart(2, "0")}`;
72622
+ const clamp3 = (v) => Math.max(0, Math.min(255, Math.round(v)));
72623
+ return `#${clamp3(r2).toString(16).padStart(2, "0")}${clamp3(g2).toString(16).padStart(2, "0")}${clamp3(b2).toString(16).padStart(2, "0")}`;
72574
72624
  }
72575
72625
  function rgbToHsl(r2, g2, b2) {
72576
72626
  const rn = r2 / 255;
@@ -74074,6 +74124,13 @@ function hasDistinctScriptFonts(fonts) {
74074
74124
  }
74075
74125
  return Boolean(fonts.eastAsia) && fonts.eastAsia !== base || Boolean(fonts.complexScript) && fonts.complexScript !== base || Boolean(fonts.symbol) && fonts.symbol !== base;
74076
74126
  }
74127
+ function sanitizeMathMl(markup) {
74128
+ const purify = DOMPurify;
74129
+ if (typeof purify.sanitize !== "function") {
74130
+ return markup;
74131
+ }
74132
+ return purify.sanitize(markup, { USE_PROFILES: { mathMl: true, svg: true } });
74133
+ }
74077
74134
  function renderScriptAwareText(text2, needsScriptFonts, scriptFonts, baseFontFamily, keyPrefix) {
74078
74135
  if (!needsScriptFonts || !text2) {
74079
74136
  return text2;
@@ -74164,14 +74221,15 @@ function renderSegmentContent(elementId, segmentIndex, textValue, lines, needsSc
74164
74221
  }
74165
74222
  function renderEquationSegment(elementId, segmentIndex, equationXml, equationNumber) {
74166
74223
  const mathml = convertOmmlToMathMl(equationXml);
74167
- const equationContent = mathml ? /* @__PURE__ */ jsx(
74224
+ const safeMathml = mathml ? sanitizeMathMl(mathml) : "";
74225
+ const equationContent = safeMathml ? /* @__PURE__ */ jsx(
74168
74226
  "span",
74169
74227
  {
74170
74228
  className: "inline-block align-middle",
74171
74229
  style: {
74172
74230
  fontFamily: '"Cambria Math", "STIX Two Math", serif'
74173
74231
  },
74174
- dangerouslySetInnerHTML: { __html: mathml }
74232
+ dangerouslySetInnerHTML: { __html: safeMathml }
74175
74233
  }
74176
74234
  ) : /* @__PURE__ */ jsx("span", { className: "inline-block px-1 py-0.5 rounded text-xs bg-gray-200/20 text-gray-400 italic", children: "Equation" });
74177
74235
  if (equationNumber) {
@@ -74772,6 +74830,11 @@ function getShapeClipPath(shapeType, adjustments, width, height) {
74772
74830
  }
74773
74831
  const normalized = shapeType.toLowerCase();
74774
74832
  if (normalized === "round1rect" || normalized === "round2samerect" || normalized === "round2diagrect" || normalized === "sniproundrect" || normalized === "snip1rect" || normalized === "snip2diagrect") {
74833
+ if (adjustments?.adj !== void 0 && width && height) {
74834
+ const ratio = Math.min(Math.max(adjustments.adj / 1e5, 0), 0.5);
74835
+ const radiusPx = Math.round(Math.min(width, height) * ratio);
74836
+ return `inset(0 round ${radiusPx}px)`;
74837
+ }
74775
74838
  return "inset(0 round 18px)";
74776
74839
  }
74777
74840
  if (normalized === "can" || normalized === "cylinder") {
@@ -75418,6 +75481,128 @@ function mapDagBlendModeToCss(blend) {
75418
75481
  return void 0;
75419
75482
  }
75420
75483
  }
75484
+ function getImageAlphaFilterId(elementId) {
75485
+ return `imgalpha-${elementId}`;
75486
+ }
75487
+ function hasAdvancedImageAlphaEffects(element2) {
75488
+ if (!isImageLikeElement(element2)) {
75489
+ return false;
75490
+ }
75491
+ const e2 = element2.imageEffects;
75492
+ if (!e2) {
75493
+ return false;
75494
+ }
75495
+ return Boolean(
75496
+ typeof e2.alphaModFix === "number" || e2.alphaInv || e2.alphaCeiling || e2.alphaFloor || typeof e2.alphaRepl === "number" || typeof e2.alphaBiLevel === "number" || typeof e2.biLevel === "number" || e2.lum && (typeof e2.lum.bright === "number" || typeof e2.lum.contrast === "number") || e2.hsl && (typeof e2.hsl.sat === "number" || typeof e2.hsl.lum === "number") || e2.tint && typeof e2.tint.amt === "number" || e2.clrRepl
75497
+ );
75498
+ }
75499
+ function renderImageAlphaSvgFilter(element2) {
75500
+ if (!isImageLikeElement(element2)) {
75501
+ return null;
75502
+ }
75503
+ const e2 = element2.imageEffects;
75504
+ if (!e2 || !hasAdvancedImageAlphaEffects(element2)) {
75505
+ return null;
75506
+ }
75507
+ const filterId = getImageAlphaFilterId(element2.id);
75508
+ const primitives = [];
75509
+ let resultIdx = 0;
75510
+ let inputRef = "SourceGraphic";
75511
+ const next = (jsx229) => {
75512
+ const result = `r${resultIdx++}`;
75513
+ primitives.push(jsx229(inputRef, result));
75514
+ inputRef = result;
75515
+ };
75516
+ if (typeof e2.alphaModFix === "number") {
75517
+ const mul = clamp(e2.alphaModFix / 100, 0, 1);
75518
+ next((inp, out) => /* @__PURE__ */ jsx(
75519
+ "feColorMatrix",
75520
+ {
75521
+ in: inp,
75522
+ result: out,
75523
+ type: "matrix",
75524
+ values: `1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 ${mul} 0`
75525
+ },
75526
+ out
75527
+ ));
75528
+ }
75529
+ if (e2.alphaInv) {
75530
+ next((inp, out) => /* @__PURE__ */ jsx("feComponentTransfer", { in: inp, result: out, children: /* @__PURE__ */ jsx("feFuncA", { type: "linear", slope: -1, intercept: 1 }) }, out));
75531
+ }
75532
+ if (e2.alphaCeiling) {
75533
+ next((inp, out) => /* @__PURE__ */ jsx("feComponentTransfer", { in: inp, result: out, children: /* @__PURE__ */ jsx("feFuncA", { type: "discrete", tableValues: "0 1 1 1 1 1 1 1 1 1" }) }, out));
75534
+ }
75535
+ if (e2.alphaFloor) {
75536
+ next((inp, out) => /* @__PURE__ */ jsx("feComponentTransfer", { in: inp, result: out, children: /* @__PURE__ */ jsx("feFuncA", { type: "discrete", tableValues: "0 0 0 0 0 0 0 0 0 1" }) }, out));
75537
+ }
75538
+ if (typeof e2.alphaRepl === "number") {
75539
+ const a2 = clamp(e2.alphaRepl / 100, 0, 1);
75540
+ next((inp, out) => /* @__PURE__ */ jsx("feComponentTransfer", { in: inp, result: out, children: /* @__PURE__ */ jsx("feFuncA", { type: "linear", slope: 0, intercept: a2 }) }, out));
75541
+ }
75542
+ if (typeof e2.alphaBiLevel === "number") {
75543
+ const t2 = clamp(e2.alphaBiLevel / 100, 0, 1);
75544
+ const table = Array.from({ length: 10 }, (_, i3) => i3 / 10 >= t2 ? "1" : "0").join(" ");
75545
+ next((inp, out) => /* @__PURE__ */ jsx("feComponentTransfer", { in: inp, result: out, children: /* @__PURE__ */ jsx("feFuncA", { type: "discrete", tableValues: table }) }, out));
75546
+ }
75547
+ if (typeof e2.biLevel === "number") {
75548
+ next((inp, out) => /* @__PURE__ */ jsx("feColorMatrix", { in: inp, result: out, type: "saturate", values: "0" }, out));
75549
+ const t2 = clamp(e2.biLevel / 100, 0, 1);
75550
+ const tbl = t2 < 0.5 ? "0 1" : "0 1";
75551
+ next((inp, out) => /* @__PURE__ */ jsxs("feComponentTransfer", { in: inp, result: out, children: [
75552
+ /* @__PURE__ */ jsx("feFuncR", { type: "discrete", tableValues: tbl }),
75553
+ /* @__PURE__ */ jsx("feFuncG", { type: "discrete", tableValues: tbl }),
75554
+ /* @__PURE__ */ jsx("feFuncB", { type: "discrete", tableValues: tbl })
75555
+ ] }, out));
75556
+ }
75557
+ if (e2.lum && (typeof e2.lum.bright === "number" || typeof e2.lum.contrast === "number")) {
75558
+ const b2 = (e2.lum.bright ?? 0) / 100;
75559
+ const c2 = 1 + (e2.lum.contrast ?? 0) / 100;
75560
+ const slope = c2;
75561
+ const intercept = b2 + (1 - c2) / 2;
75562
+ next((inp, out) => /* @__PURE__ */ jsxs("feComponentTransfer", { in: inp, result: out, children: [
75563
+ /* @__PURE__ */ jsx("feFuncR", { type: "linear", slope, intercept }),
75564
+ /* @__PURE__ */ jsx("feFuncG", { type: "linear", slope, intercept }),
75565
+ /* @__PURE__ */ jsx("feFuncB", { type: "linear", slope, intercept })
75566
+ ] }, out));
75567
+ }
75568
+ if (e2.hsl && typeof e2.hsl.sat === "number") {
75569
+ const v = clamp(1 + e2.hsl.sat / 100, 0, 2);
75570
+ next((inp, out) => /* @__PURE__ */ jsx("feColorMatrix", { in: inp, result: out, type: "saturate", values: String(v) }, out));
75571
+ }
75572
+ if (e2.tint && typeof e2.tint.amt === "number" && e2.tint.amt < 0) {
75573
+ const v = clamp(1 + e2.tint.amt / 100, 0, 1);
75574
+ next((inp, out) => /* @__PURE__ */ jsx("feColorMatrix", { in: inp, result: out, type: "saturate", values: String(v) }, out));
75575
+ }
75576
+ if (e2.clrRepl) {
75577
+ const c2 = hexToRgbUnit(e2.clrRepl.color);
75578
+ next((inp, out) => /* @__PURE__ */ jsx(
75579
+ "feColorMatrix",
75580
+ {
75581
+ in: inp,
75582
+ result: out,
75583
+ type: "matrix",
75584
+ values: `0 0 0 0 ${c2.r} 0 0 0 0 ${c2.g} 0 0 0 0 ${c2.b} 0 0 0 1 0`
75585
+ },
75586
+ out
75587
+ ));
75588
+ }
75589
+ if (primitives.length === 0) {
75590
+ return null;
75591
+ }
75592
+ return /* @__PURE__ */ jsx(
75593
+ "svg",
75594
+ {
75595
+ width: 0,
75596
+ height: 0,
75597
+ style: { position: "absolute", overflow: "hidden" },
75598
+ "aria-hidden": "true",
75599
+ children: /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("filter", { id: filterId, colorInterpolationFilters: "sRGB", children: primitives }) })
75600
+ }
75601
+ );
75602
+ }
75603
+ function clamp(v, lo, hi) {
75604
+ return v < lo ? lo : v > hi ? hi : v;
75605
+ }
75421
75606
  function getDagDuotoneFilterId(elementId) {
75422
75607
  return `dag-duotone-${elementId}`;
75423
75608
  }
@@ -75501,6 +75686,9 @@ function getImageEffectsFilter(element2, options) {
75501
75686
  const filterId = getDuotoneFilterId(element2.id);
75502
75687
  filters.push(`url(#${filterId})`);
75503
75688
  }
75689
+ if (hasAdvancedImageAlphaEffects(element2)) {
75690
+ filters.push(`url(#${getImageAlphaFilterId(element2.id)})`);
75691
+ }
75504
75692
  if (effects.artisticEffect) {
75505
75693
  const radius = effects.artisticRadius ?? 5;
75506
75694
  if (needsSvgArtisticFilter(effects.artisticEffect)) {
@@ -75674,6 +75862,39 @@ function getEffectDagCssFilter(style, elementId) {
75674
75862
  return filters.length > 0 ? filters.join(" ") : void 0;
75675
75863
  }
75676
75864
  var getEffectDagFilter = getEffectDagCssFilter;
75865
+ function getResolvedShapeClipPathFor(shapeType, width, height, adjustments) {
75866
+ if (!shapeType) {
75867
+ return void 0;
75868
+ }
75869
+ if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
75870
+ return getShapeClipPath(shapeType, adjustments, width, height);
75871
+ }
75872
+ if (adjustments && Object.keys(adjustments).length > 0) {
75873
+ const adjusted = getAdjustmentAwareShapeClipPath(shapeType, width, height, adjustments);
75874
+ if (adjusted !== void 0) {
75875
+ return adjusted;
75876
+ }
75877
+ }
75878
+ const fromPreset = getShapeClipPathFromPreset(shapeType, width, height, adjustments);
75879
+ if (fromPreset !== void 0) {
75880
+ return fromPreset;
75881
+ }
75882
+ const cloud = getCloudPathForRendering(shapeType, width, height);
75883
+ if (cloud !== void 0) {
75884
+ return cloud;
75885
+ }
75886
+ return getShapeClipPath(shapeType, adjustments, width, height);
75887
+ }
75888
+ function getResolvedShapeClipPath(element2, width, height) {
75889
+ const shapeType = element2.shapeType;
75890
+ if (!shapeType) {
75891
+ return void 0;
75892
+ }
75893
+ const w = element2.width;
75894
+ const h2 = element2.height;
75895
+ const adjustments = element2.shapeAdjustments;
75896
+ return getResolvedShapeClipPathFor(shapeType, w, h2, adjustments);
75897
+ }
75677
75898
 
75678
75899
  // src/viewer/utils/shape-round-rect.ts
75679
75900
  function localClampAdjustment(value) {
@@ -75745,6 +75966,27 @@ var MATERIAL_MAP = {
75745
75966
  filter: "brightness(1.1) contrast(0.85)",
75746
75967
  // Translucent powder: slight translucent glow
75747
75968
  backgroundImage: "radial-gradient(ellipse at 30% 30%, rgba(255,255,255,0.1) 0%, transparent 60%)"
75969
+ },
75970
+ // Legacy materials (PowerPoint 2007 / earlier). Render as muted variants
75971
+ // of the modern equivalents so legacy decks still resemble the originals.
75972
+ legacyMatte: {
75973
+ filter: "brightness(0.92) saturate(0.85)",
75974
+ backgroundImage: "linear-gradient(180deg, rgba(255,255,255,0.03) 0%, transparent 50%, rgba(0,0,0,0.04) 100%)"
75975
+ },
75976
+ legacyPlastic: {
75977
+ filter: "brightness(1.02) contrast(1.03)",
75978
+ boxShadow: "inset -2px -2px 5px rgba(255,255,255,0.3)",
75979
+ backgroundImage: "radial-gradient(ellipse 35% 25% at 25% 20%, rgba(255,255,255,0.15) 0%, transparent 70%)"
75980
+ },
75981
+ legacyMetal: {
75982
+ filter: "brightness(1.05) contrast(1.1) saturate(1.1)",
75983
+ boxShadow: "inset -2px -2px 6px rgba(255,255,255,0.35), inset 1px 1px 3px rgba(255,255,255,0.15)",
75984
+ backgroundImage: "linear-gradient(135deg, rgba(255,255,255,0.18) 0%, rgba(255,255,255,0.06) 25%, transparent 50%, rgba(0,0,0,0.05) 80%)"
75985
+ },
75986
+ legacyWireframe: {
75987
+ filter: "brightness(1) contrast(1.4) saturate(0.6)",
75988
+ // Wireframe: high contrast outline-emphasising look
75989
+ boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.4)"
75748
75990
  }
75749
75991
  };
75750
75992
  function getMaterialCssOverrides(material) {
@@ -76699,8 +76941,7 @@ function getShapeVisualStyle(element2, hasFill, fillColor, strokeWidth, strokeCo
76699
76941
  return {};
76700
76942
  }
76701
76943
  const normalizedShapeType = getShapeType(element2.shapeType);
76702
- const shapeType = element2.shapeType || normalizedShapeType;
76703
- const clipPath = getShapeClipPath(shapeType);
76944
+ const clipPath = getResolvedShapeClipPath(element2);
76704
76945
  const fillOpacity = element2.shapeStyle?.fillOpacity;
76705
76946
  const strokeOpacity = element2.shapeStyle?.strokeOpacity;
76706
76947
  const strokeDash = normalizeStrokeDashType(element2.shapeStyle?.strokeDash);
@@ -77302,7 +77543,7 @@ function getImageMaskStyle(element2) {
77302
77543
  if (normalized === "can" || normalized === "cylinder") {
77303
77544
  return { borderRadius: "48% / 12%" };
77304
77545
  }
77305
- const clipPath = getShapeClipPath(shapeType);
77546
+ const clipPath = getResolvedShapeClipPath(element2);
77306
77547
  if (!clipPath) {
77307
77548
  return void 0;
77308
77549
  }
@@ -77965,8 +78206,8 @@ function hexToRgb3(hex) {
77965
78206
  };
77966
78207
  }
77967
78208
  function rgbToHex2(r2, g2, b2) {
77968
- const clamp2 = (v) => Math.max(0, Math.min(255, Math.round(v)));
77969
- return `#${clamp2(r2).toString(16).padStart(2, "0").toUpperCase()}${clamp2(g2).toString(16).padStart(2, "0").toUpperCase()}${clamp2(b2).toString(16).padStart(2, "0").toUpperCase()}`;
78209
+ const clamp3 = (v) => Math.max(0, Math.min(255, Math.round(v)));
78210
+ return `#${clamp3(r2).toString(16).padStart(2, "0").toUpperCase()}${clamp3(g2).toString(16).padStart(2, "0").toUpperCase()}${clamp3(b2).toString(16).padStart(2, "0").toUpperCase()}`;
77970
78211
  }
77971
78212
  function tintColor(hex, tintFactor) {
77972
78213
  const { r: r2, g: g2, b: b2 } = hexToRgb3(hex);
@@ -78897,13 +79138,13 @@ function cellStyleToCss(style) {
78897
79138
  }
78898
79139
  if (style.textDirection) {
78899
79140
  switch (style.textDirection) {
78900
- case "vertical":
79141
+ case "vert":
78901
79142
  case "eaVert":
78902
79143
  case "wordArtVert":
78903
79144
  case "wordArtVertRtl":
78904
79145
  css.writingMode = "vertical-rl";
78905
79146
  break;
78906
- case "vertical270":
79147
+ case "vert270":
78907
79148
  case "mongolianVert":
78908
79149
  css.writingMode = "vertical-lr";
78909
79150
  break;
@@ -79430,8 +79671,8 @@ function hexToRgb4(hex) {
79430
79671
  ];
79431
79672
  }
79432
79673
  function rgbToHex3(r2, g2, b2) {
79433
- const clamp2 = (v) => Math.max(0, Math.min(255, Math.round(v)));
79434
- return `#${clamp2(r2).toString(16).padStart(2, "0")}${clamp2(g2).toString(16).padStart(2, "0")}${clamp2(b2).toString(16).padStart(2, "0")}`;
79674
+ const clamp3 = (v) => Math.max(0, Math.min(255, Math.round(v)));
79675
+ return `#${clamp3(r2).toString(16).padStart(2, "0")}${clamp3(g2).toString(16).padStart(2, "0")}${clamp3(b2).toString(16).padStart(2, "0")}`;
79435
79676
  }
79436
79677
  function tint(hex, amount) {
79437
79678
  const [r2, g2, b2] = hexToRgb4(hex);
@@ -81349,7 +81590,7 @@ function resolveRegionCode(label) {
81349
81590
  return REGION_ALIAS_MAP[normalized];
81350
81591
  }
81351
81592
  function lerpColor(a2, b2, t2) {
81352
- const clamp2 = (v) => Math.max(0, Math.min(255, Math.round(v)));
81593
+ const clamp3 = (v) => Math.max(0, Math.min(255, Math.round(v)));
81353
81594
  const parse = (hex) => {
81354
81595
  const h2 = hex.replace("#", "");
81355
81596
  return [
@@ -81360,9 +81601,9 @@ function lerpColor(a2, b2, t2) {
81360
81601
  };
81361
81602
  const [r1, g1, b1] = parse(a2);
81362
81603
  const [r2, g2, b22] = parse(b2);
81363
- const r3 = clamp2(r1 + (r2 - r1) * t2);
81364
- const g3 = clamp2(g1 + (g2 - g1) * t2);
81365
- const bl = clamp2(b1 + (b22 - b1) * t2);
81604
+ const r3 = clamp3(r1 + (r2 - r1) * t2);
81605
+ const g3 = clamp3(g1 + (g2 - g1) * t2);
81606
+ const bl = clamp3(b1 + (b22 - b1) * t2);
81366
81607
  return `#${r3.toString(16).padStart(2, "0")}${g3.toString(16).padStart(2, "0")}${bl.toString(16).padStart(2, "0")}`;
81367
81608
  }
81368
81609
  function sequentialColorScale(t2) {
@@ -86528,7 +86769,7 @@ function ResizeHandle({
86528
86769
  }
86529
86770
  );
86530
86771
  }
86531
- function SlideThumbnail({
86772
+ function SlideThumbnailImpl({
86532
86773
  slide,
86533
86774
  templateElements,
86534
86775
  canvasSize
@@ -86766,6 +87007,37 @@ function ThumbnailTable({
86766
87007
  }
86767
87008
  return /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center text-[10px] text-muted-foreground pointer-events-none", children: "Table" });
86768
87009
  }
87010
+ function arePropsEqual(prev, next) {
87011
+ if (prev.slide.id !== next.slide.id) {
87012
+ return false;
87013
+ }
87014
+ if (prev.slide.isDirty !== next.slide.isDirty) {
87015
+ return false;
87016
+ }
87017
+ if (prev.slide.hidden !== next.slide.hidden) {
87018
+ return false;
87019
+ }
87020
+ if (prev.slide.elements !== next.slide.elements) {
87021
+ return false;
87022
+ }
87023
+ if (prev.slide.backgroundColor !== next.slide.backgroundColor) {
87024
+ return false;
87025
+ }
87026
+ if (prev.slide.backgroundImage !== next.slide.backgroundImage) {
87027
+ return false;
87028
+ }
87029
+ if (prev.slide.backgroundGradient !== next.slide.backgroundGradient) {
87030
+ return false;
87031
+ }
87032
+ if (prev.templateElements !== next.templateElements) {
87033
+ return false;
87034
+ }
87035
+ if (prev.canvasSize.width !== next.canvasSize.width || prev.canvasSize.height !== next.canvasSize.height) {
87036
+ return false;
87037
+ }
87038
+ return true;
87039
+ }
87040
+ var SlideThumbnail = React10__default.memo(SlideThumbnailImpl, arePropsEqual);
86769
87041
  function ContextMenu({
86770
87042
  contextMenuState,
86771
87043
  mode,
@@ -88083,6 +88355,59 @@ function WarpedText({
88083
88355
  }
88084
88356
  );
88085
88357
  }
88358
+ var GLYPH_BY_SHAPE = Object.fromEntries(
88359
+ ACTION_BUTTON_PRESETS.map((p3) => [p3.shapeType, p3.iconPath])
88360
+ );
88361
+ GLYPH_BY_SHAPE["actionButtonForwardOrNext"] = GLYPH_BY_SHAPE["actionButtonForwardNext"];
88362
+ GLYPH_BY_SHAPE["actionButtonBackOrPrevious"] = GLYPH_BY_SHAPE["actionButtonBackPrevious"];
88363
+ function isActionButtonShape(shapeType) {
88364
+ return Boolean(shapeType && shapeType in GLYPH_BY_SHAPE);
88365
+ }
88366
+ function getActionButtonGlyphPath(shapeType) {
88367
+ if (!shapeType) {
88368
+ return void 0;
88369
+ }
88370
+ const path = GLYPH_BY_SHAPE[shapeType];
88371
+ return path && path.length > 0 ? path : void 0;
88372
+ }
88373
+ function ActionButtonGlyphOverlay({
88374
+ element: element2,
88375
+ color
88376
+ }) {
88377
+ const shapeType = "shapeType" in element2 ? element2.shapeType : void 0;
88378
+ const path = getActionButtonGlyphPath(shapeType);
88379
+ if (!path) {
88380
+ return null;
88381
+ }
88382
+ const stroke = color ?? ("textStyle" in element2 && element2.textStyle?.color || "#ffffff");
88383
+ return /* @__PURE__ */ jsx(
88384
+ "svg",
88385
+ {
88386
+ viewBox: "0 0 24 24",
88387
+ width: "100%",
88388
+ height: "100%",
88389
+ preserveAspectRatio: "xMidYMid meet",
88390
+ style: {
88391
+ position: "absolute",
88392
+ inset: 0,
88393
+ pointerEvents: "none",
88394
+ padding: "20%"
88395
+ },
88396
+ "aria-hidden": "true",
88397
+ children: /* @__PURE__ */ jsx(
88398
+ "path",
88399
+ {
88400
+ d: path,
88401
+ fill: "none",
88402
+ stroke,
88403
+ strokeWidth: 2,
88404
+ strokeLinecap: "round",
88405
+ strokeLinejoin: "round"
88406
+ }
88407
+ )
88408
+ }
88409
+ );
88410
+ }
88086
88411
  function ColorChangedImage({
88087
88412
  src,
88088
88413
  clrChange,
@@ -88353,6 +88678,7 @@ function renderImg(el, style, filter, alt, opacity) {
88353
88678
  return /* @__PURE__ */ jsxs(Fragment, { children: [
88354
88679
  tileDuotoneColors && renderDuotoneSvgFilter(el.id, tileDuotoneColors.color1, tileDuotoneColors.color2),
88355
88680
  renderArtisticEffectSvgFilter(el.id, artisticEffectName, artisticRadius),
88681
+ renderImageAlphaSvgFilter(el),
88356
88682
  /* @__PURE__ */ jsx(
88357
88683
  "div",
88358
88684
  {
@@ -88374,6 +88700,7 @@ function renderImg(el, style, filter, alt, opacity) {
88374
88700
  return /* @__PURE__ */ jsxs(Fragment, { children: [
88375
88701
  duotoneColors && !useDuotoneCanvas && renderDuotoneSvgFilter(el.id, duotoneColors.color1, duotoneColors.color2),
88376
88702
  renderArtisticEffectSvgFilter(el.id, artisticEffectName, artisticRadius),
88703
+ renderImageAlphaSvgFilter(el),
88377
88704
  useDuotoneCanvas && duotoneColors ? /* @__PURE__ */ jsx(
88378
88705
  DuotoneImage,
88379
88706
  {
@@ -90499,7 +90826,7 @@ function BendingProcessRenderer({
90499
90826
  }
90500
90827
  );
90501
90828
  }
90502
- function SmartArtRenderer({
90829
+ function SmartArtRendererImpl({
90503
90830
  element: element2,
90504
90831
  className = ""
90505
90832
  }) {
@@ -90610,6 +90937,30 @@ function renderLayout(layoutType, element2, nodes, palette, style) {
90610
90937
  }
90611
90938
  return /* @__PURE__ */ jsx(ListRenderer, { element: element2, nodes, palette, style });
90612
90939
  }
90940
+ function arePropsEqual2(prev, next) {
90941
+ if (prev.className !== next.className) {
90942
+ return false;
90943
+ }
90944
+ if (prev.element.id !== next.element.id) {
90945
+ return false;
90946
+ }
90947
+ if (prev.element.type !== next.element.type) {
90948
+ return false;
90949
+ }
90950
+ if (prev.element.width !== next.element.width || prev.element.height !== next.element.height) {
90951
+ return false;
90952
+ }
90953
+ if (prev.element.x !== next.element.x || prev.element.y !== next.element.y) {
90954
+ return false;
90955
+ }
90956
+ const prevData = prev.element.type === "smartArt" ? prev.element.smartArtData : void 0;
90957
+ const nextData = next.element.type === "smartArt" ? next.element.smartArtData : void 0;
90958
+ if (prevData !== nextData) {
90959
+ return false;
90960
+ }
90961
+ return true;
90962
+ }
90963
+ var SmartArtRenderer = React10__default.memo(SmartArtRendererImpl, arePropsEqual2);
90613
90964
  function ZoomElementRenderer({
90614
90965
  element: element2,
90615
90966
  slides,
@@ -90858,8 +91209,11 @@ function renderBody(el, isImg, isEditing, editText, spellCheck, txtSE, txtS, vec
90858
91209
  ...scene3dStyle.perspective ? { perspective: scene3dStyle.perspective } : {},
90859
91210
  ...scene3dStyle.transformStyle ? { transformStyle: scene3dStyle.transformStyle } : {}
90860
91211
  } : void 0;
91212
+ const shapeTypeForGlyph = "shapeType" in el ? el.shapeType : void 0;
91213
+ const showActionButtonGlyph = isActionButtonShape(shapeTypeForGlyph);
90861
91214
  return /* @__PURE__ */ jsxs(Fragment, { children: [
90862
91215
  vecShape,
91216
+ showActionButtonGlyph && /* @__PURE__ */ jsx(ActionButtonGlyphOverlay, { element: el }),
90863
91217
  isTxtEl ? useSvgWarp ? /* @__PURE__ */ jsx(
90864
91218
  "div",
90865
91219
  {
@@ -93553,7 +93907,7 @@ function SectionBlock({
93553
93907
  )
93554
93908
  ] });
93555
93909
  }
93556
- function SlideCard({
93910
+ function SlideCardImpl({
93557
93911
  slide,
93558
93912
  index,
93559
93913
  isActive,
@@ -93607,6 +93961,49 @@ function SlideCard({
93607
93961
  }
93608
93962
  );
93609
93963
  }
93964
+ function arePropsEqual3(prev, next) {
93965
+ if (prev.slide.id !== next.slide.id) {
93966
+ return false;
93967
+ }
93968
+ if (prev.slide.isDirty !== next.slide.isDirty) {
93969
+ return false;
93970
+ }
93971
+ if (prev.slide.hidden !== next.slide.hidden) {
93972
+ return false;
93973
+ }
93974
+ if (prev.slide.elements !== next.slide.elements) {
93975
+ return false;
93976
+ }
93977
+ if (prev.index !== next.index) {
93978
+ return false;
93979
+ }
93980
+ if (prev.isActive !== next.isActive) {
93981
+ return false;
93982
+ }
93983
+ if (prev.isDragTarget !== next.isDragTarget) {
93984
+ return false;
93985
+ }
93986
+ if (prev.isSelected !== next.isSelected) {
93987
+ return false;
93988
+ }
93989
+ if (prev.selectedCount !== next.selectedCount) {
93990
+ return false;
93991
+ }
93992
+ if (prev.selectionOrder !== next.selectionOrder) {
93993
+ return false;
93994
+ }
93995
+ if (prev.canEdit !== next.canEdit) {
93996
+ return false;
93997
+ }
93998
+ if (prev.canvasSize.width !== next.canvasSize.width || prev.canvasSize.height !== next.canvasSize.height) {
93999
+ return false;
94000
+ }
94001
+ if (prev.onSlideClick !== next.onSlideClick || prev.onDoubleClick !== next.onDoubleClick || prev.onContextMenu !== next.onContextMenu || prev.onDragStart !== next.onDragStart || prev.onDragOver !== next.onDragOver || prev.onDragLeave !== next.onDragLeave || prev.onDrop !== next.onDrop) {
94002
+ return false;
94003
+ }
94004
+ return true;
94005
+ }
94006
+ var SlideCard = React10__default.memo(SlideCardImpl, arePropsEqual3);
93610
94007
  function SorterContextMenu({
93611
94008
  x: x2,
93612
94009
  y,
@@ -94400,6 +94797,14 @@ function getCurrentParagraphIndex(editorEl, segments) {
94400
94797
  }
94401
94798
  return paraIdx;
94402
94799
  }
94800
+ var CSS_VALUE_SAFE = /^[a-zA-Z0-9 _,.\-+#'%/]{1,100}$/;
94801
+ function isCssValueSafe(value) {
94802
+ if (value === void 0 || value === null) {
94803
+ return false;
94804
+ }
94805
+ const str = String(value);
94806
+ return str.length > 0 && CSS_VALUE_SAFE.test(str);
94807
+ }
94403
94808
  function deriveStyleFromElement(element2, inheritedStyle) {
94404
94809
  const style = { ...inheritedStyle };
94405
94810
  const tagName = element2.tagName.toLowerCase();
@@ -94532,17 +94937,17 @@ function segmentsToEditorHtml(segments) {
94532
94937
  if (segment.style.strikethrough) {
94533
94938
  inlineStyles.push("text-decoration:line-through");
94534
94939
  }
94535
- if (segment.style.color) {
94940
+ if (segment.style.color && isCssValueSafe(segment.style.color)) {
94536
94941
  inlineStyles.push(`color:${segment.style.color}`);
94537
94942
  }
94538
- if (segment.style.fontSize) {
94539
- inlineStyles.push(`font-size:${segment.style.fontSize}pt`);
94943
+ if (segment.style.fontSize && Number.isFinite(Number(segment.style.fontSize))) {
94944
+ inlineStyles.push(`font-size:${Number(segment.style.fontSize)}pt`);
94540
94945
  }
94541
- if (segment.style.fontFamily) {
94946
+ if (segment.style.fontFamily && isCssValueSafe(segment.style.fontFamily)) {
94542
94947
  inlineStyles.push(`font-family:${segment.style.fontFamily}`);
94543
94948
  }
94544
94949
  const text2 = escapeHtml(segment.text);
94545
- if (segment.style.hyperlink) {
94950
+ if (segment.style.hyperlink && isUrlSafe(segment.style.hyperlink)) {
94546
94951
  const href = escapeHtml(segment.style.hyperlink);
94547
94952
  return `<a href="${href}" style="color:#4a9eff;text-decoration:underline;cursor:pointer" data-hyperlink="${href}">${text2}</a>`;
94548
94953
  }
@@ -94625,7 +95030,8 @@ function renderRichNotesSegments(segments) {
94625
95030
  if (segment.style.fontFamily) {
94626
95031
  style.fontFamily = segment.style.fontFamily;
94627
95032
  }
94628
- if (segment.style.hyperlink) {
95033
+ if (segment.style.hyperlink && isUrlSafe(segment.style.hyperlink)) {
95034
+ const safeHref = segment.style.hyperlink;
94629
95035
  style.color = "#4a9eff";
94630
95036
  style.textDecoration = "underline";
94631
95037
  style.cursor = "pointer";
@@ -94633,11 +95039,11 @@ function renderRichNotesSegments(segments) {
94633
95039
  /* @__PURE__ */ jsx(
94634
95040
  "a",
94635
95041
  {
94636
- href: segment.style.hyperlink,
95042
+ href: safeHref,
94637
95043
  style,
94638
95044
  onClick: (e2) => {
94639
95045
  e2.preventDefault();
94640
- window.open(segment.style.hyperlink, "_blank");
95046
+ safeOpenUrl(safeHref);
94641
95047
  },
94642
95048
  children: segment.text
94643
95049
  },
@@ -95189,7 +95595,7 @@ function useSlideNotes({
95189
95595
  const href = target.getAttribute("data-hyperlink") || target.getAttribute("href");
95190
95596
  if (href && (e2.ctrlKey || e2.metaKey)) {
95191
95597
  e2.preventDefault();
95192
- window.open(href, "_blank");
95598
+ safeOpenUrl(href);
95193
95599
  }
95194
95600
  }, []);
95195
95601
  return {
@@ -96862,7 +97268,7 @@ function renderNotesSegments(segments) {
96862
97268
  return React10__default.createElement("span", { key: `seg-${index}`, style }, segment.text);
96863
97269
  });
96864
97270
  }
96865
- function ScaledSlidePreview({
97271
+ function ScaledSlidePreviewImpl({
96866
97272
  slide,
96867
97273
  templateElements,
96868
97274
  canvasSize,
@@ -97009,6 +97415,40 @@ function ScaledSlidePreview({
97009
97415
  }
97010
97416
  );
97011
97417
  }
97418
+ function arePropsEqual4(prev, next) {
97419
+ if (prev.slide.id !== next.slide.id) {
97420
+ return false;
97421
+ }
97422
+ if (prev.slide.isDirty !== next.slide.isDirty) {
97423
+ return false;
97424
+ }
97425
+ if (prev.slide.hidden !== next.slide.hidden) {
97426
+ return false;
97427
+ }
97428
+ if (prev.slide.elements !== next.slide.elements) {
97429
+ return false;
97430
+ }
97431
+ if (prev.slide.backgroundColor !== next.slide.backgroundColor) {
97432
+ return false;
97433
+ }
97434
+ if (prev.slide.backgroundImage !== next.slide.backgroundImage) {
97435
+ return false;
97436
+ }
97437
+ if (prev.slide.backgroundGradient !== next.slide.backgroundGradient) {
97438
+ return false;
97439
+ }
97440
+ if (prev.templateElements !== next.templateElements) {
97441
+ return false;
97442
+ }
97443
+ if (prev.canvasSize.width !== next.canvasSize.width || prev.canvasSize.height !== next.canvasSize.height) {
97444
+ return false;
97445
+ }
97446
+ if (prev.className !== next.className) {
97447
+ return false;
97448
+ }
97449
+ return true;
97450
+ }
97451
+ var ScaledSlidePreview = React10__default.memo(ScaledSlidePreviewImpl, arePropsEqual4);
97012
97452
  function PresenterView({
97013
97453
  slides,
97014
97454
  currentSlideIndex,
@@ -111312,6 +111752,13 @@ function convertOmmlToLatex(omml) {
111312
111752
  }
111313
111753
  return ommlChildrenToLatex(oMath);
111314
111754
  }
111755
+ function sanitizeMathMl2(markup) {
111756
+ const purify = DOMPurify;
111757
+ if (typeof purify.sanitize !== "function") {
111758
+ return markup;
111759
+ }
111760
+ return purify.sanitize(markup, { USE_PROFILES: { mathMl: true, svg: true } });
111761
+ }
111315
111762
  var TEMPLATES = [
111316
111763
  {
111317
111764
  label: "Fraction",
@@ -111377,7 +111824,8 @@ var TEMPLATES = [
111377
111824
  var TEMPLATE_MATHML = TEMPLATES.map((tmpl) => {
111378
111825
  try {
111379
111826
  const tmplOmml = convertLatexToOmml(tmpl.latex);
111380
- return convertOmmlToMathMl(tmplOmml);
111827
+ const raw = convertOmmlToMathMl(tmplOmml);
111828
+ return raw ? sanitizeMathMl2(raw) : "";
111381
111829
  } catch {
111382
111830
  return "";
111383
111831
  }
@@ -111389,7 +111837,7 @@ function MathMlPreview({ mathml }) {
111389
111837
  return;
111390
111838
  }
111391
111839
  if (mathml) {
111392
- containerRef.current.innerHTML = mathml;
111840
+ containerRef.current.innerHTML = sanitizeMathMl2(mathml);
111393
111841
  } else {
111394
111842
  containerRef.current.innerHTML = "";
111395
111843
  }
@@ -111417,6 +111865,7 @@ function EquationEditorDialog({
111417
111865
  return convertOmmlToLatex(existingOmml);
111418
111866
  }, [existingOmml]);
111419
111867
  const [latex, setLatex] = useState(initialLatex);
111868
+ const deferredLatex = useDeferredValue(latex);
111420
111869
  const textareaRef = useRef(null);
111421
111870
  useEffect(() => {
111422
111871
  if (isOpen) {
@@ -111425,17 +111874,17 @@ function EquationEditorDialog({
111425
111874
  }
111426
111875
  }, [isOpen, initialLatex]);
111427
111876
  const { mathml, omml } = useMemo(() => {
111428
- if (!latex.trim()) {
111877
+ if (!deferredLatex.trim()) {
111429
111878
  return { mathml: "", omml: {} };
111430
111879
  }
111431
111880
  try {
111432
- const ommlObj = convertLatexToOmml(latex);
111881
+ const ommlObj = convertLatexToOmml(deferredLatex);
111433
111882
  const mathmlStr = convertOmmlToMathMl(ommlObj);
111434
111883
  return { mathml: mathmlStr, omml: ommlObj };
111435
111884
  } catch {
111436
111885
  return { mathml: "", omml: {} };
111437
111886
  }
111438
- }, [latex]);
111887
+ }, [deferredLatex]);
111439
111888
  const handleInsert = useCallback(() => {
111440
111889
  if (!latex.trim()) {
111441
111890
  return;
@@ -114170,6 +114619,7 @@ function useEditorHistory(input) {
114170
114619
  const historyFutureRef = useRef([]);
114171
114620
  const lastHistorySnapshotRef = useRef(null);
114172
114621
  const lastHistorySerializedRef = useRef("");
114622
+ const lastCheapHashRef = useRef("");
114173
114623
  const isApplyingHistoryRef = useRef(false);
114174
114624
  const unlockHistoryTimerRef = useRef(null);
114175
114625
  const [canUndo, setCanUndo] = useState(false);
@@ -114311,15 +114761,21 @@ function useEditorHistory(input) {
114311
114761
  if (hasActivePointerInteraction()) {
114312
114762
  return;
114313
114763
  }
114764
+ const cheapHash = `${slides.length}|${activeSlideIndex}|${canvasSize.width}x${canvasSize.height}|${slides.map((s) => `${s.id}:${s.elements.length}`).join("/")}`;
114765
+ if (cheapHash === lastCheapHashRef.current) {
114766
+ return;
114767
+ }
114314
114768
  const snapshot2 = buildHistorySnapshot();
114315
114769
  const serialized = JSON.stringify(snapshot2);
114316
114770
  if (serialized === lastHistorySerializedRef.current) {
114771
+ lastCheapHashRef.current = cheapHash;
114317
114772
  return;
114318
114773
  }
114319
114774
  const previousSnapshot = lastHistorySnapshotRef.current;
114320
114775
  if (!previousSnapshot) {
114321
114776
  lastHistorySnapshotRef.current = cloneHistorySnapshot(snapshot2);
114322
114777
  lastHistorySerializedRef.current = serialized;
114778
+ lastCheapHashRef.current = cheapHash;
114323
114779
  updateHistoryAvailability();
114324
114780
  return;
114325
114781
  }
@@ -114330,13 +114786,18 @@ function useEditorHistory(input) {
114330
114786
  historyFutureRef.current = [];
114331
114787
  lastHistorySnapshotRef.current = cloneHistorySnapshot(snapshot2);
114332
114788
  lastHistorySerializedRef.current = serialized;
114789
+ lastCheapHashRef.current = cheapHash;
114333
114790
  updateHistoryAvailability();
114334
114791
  }, [
114792
+ activeSlideIndex,
114335
114793
  buildHistorySnapshot,
114794
+ canvasSize.height,
114795
+ canvasSize.width,
114336
114796
  error2,
114337
114797
  hasActivePointerInteraction,
114338
114798
  loading2,
114339
114799
  pointerCommitNonce,
114800
+ slides,
114340
114801
  updateHistoryAvailability
114341
114802
  ]);
114342
114803
  return {
@@ -118048,7 +118509,8 @@ async function storeAudienceContent(content) {
118048
118509
  const tx = db.transaction(STORE_NAME2, "readwrite");
118049
118510
  const store = tx.objectStore(STORE_NAME2);
118050
118511
  const bytes = content instanceof Uint8Array ? content : new Uint8Array(content);
118051
- store.put(bytes, CONTENT_KEY);
118512
+ const record = { bytes, createdAt: Date.now() };
118513
+ store.put(record, CONTENT_KEY);
118052
118514
  tx.oncomplete = () => {
118053
118515
  db.close();
118054
118516
  resolve2();
@@ -118081,14 +118543,34 @@ async function clearAudienceContent() {
118081
118543
  var PRESENTER_CHANNEL_NAME = "pptx-viewer-presenter";
118082
118544
  var AUDIENCE_HASH = "#pptx-audience";
118083
118545
  var PRESENTER_MSG_ORIGIN = "pptx-viewer-presenter";
118546
+ var AUDIENCE_NONCE_KEY = "nonce";
118547
+ function generateSessionId() {
118548
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
118549
+ return crypto.randomUUID();
118550
+ }
118551
+ return `s${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
118552
+ }
118553
+ function parseAudienceNonce() {
118554
+ const hash = window.location.hash;
118555
+ if (!hash.startsWith(AUDIENCE_HASH)) {
118556
+ return null;
118557
+ }
118558
+ const trailing = hash.slice(AUDIENCE_HASH.length);
118559
+ if (!trailing) {
118560
+ return null;
118561
+ }
118562
+ const params2 = new URLSearchParams(trailing.replace(/^[&;?]/, ""));
118563
+ return params2.get(AUDIENCE_NONCE_KEY);
118564
+ }
118084
118565
  function isAudienceTab() {
118085
- return window.location.hash === AUDIENCE_HASH;
118566
+ return window.location.hash.startsWith(AUDIENCE_HASH);
118086
118567
  }
118087
118568
  function usePresenterWindow(input) {
118088
118569
  const { currentSlideIndex, isPresenterMode, content } = input;
118089
118570
  const audienceWindowRef = useRef(null);
118090
118571
  const channelRef = useRef(null);
118091
118572
  const pollTimerRef = useRef(null);
118573
+ const sessionIdRef = useRef("");
118092
118574
  const getChannel2 = useCallback(() => {
118093
118575
  if (!channelRef.current) {
118094
118576
  channelRef.current = new BroadcastChannel(PRESENTER_CHANNEL_NAME);
@@ -118100,10 +118582,14 @@ function usePresenterWindow(input) {
118100
118582
  }, []);
118101
118583
  const syncSlideToAudience = useCallback(
118102
118584
  (slideIndex) => {
118585
+ if (!sessionIdRef.current) {
118586
+ return;
118587
+ }
118103
118588
  const msg = {
118104
118589
  origin: PRESENTER_MSG_ORIGIN,
118105
118590
  type: "presenter-slide-change",
118106
- slideIndex
118591
+ slideIndex,
118592
+ sessionId: sessionIdRef.current
118107
118593
  };
118108
118594
  try {
118109
118595
  getChannel2().postMessage(msg);
@@ -118113,13 +118599,16 @@ function usePresenterWindow(input) {
118113
118599
  [getChannel2]
118114
118600
  );
118115
118601
  const closeAudienceWindow = useCallback(() => {
118116
- try {
118117
- const exitMsg = {
118118
- origin: PRESENTER_MSG_ORIGIN,
118119
- type: "presenter-exit"
118120
- };
118121
- getChannel2().postMessage(exitMsg);
118122
- } catch {
118602
+ if (sessionIdRef.current) {
118603
+ try {
118604
+ const exitMsg = {
118605
+ origin: PRESENTER_MSG_ORIGIN,
118606
+ type: "presenter-exit",
118607
+ sessionId: sessionIdRef.current
118608
+ };
118609
+ getChannel2().postMessage(exitMsg);
118610
+ } catch {
118611
+ }
118123
118612
  }
118124
118613
  const win = audienceWindowRef.current;
118125
118614
  if (win && !win.closed) {
@@ -118129,6 +118618,7 @@ function usePresenterWindow(input) {
118129
118618
  }
118130
118619
  }
118131
118620
  audienceWindowRef.current = null;
118621
+ sessionIdRef.current = "";
118132
118622
  if (pollTimerRef.current !== null) {
118133
118623
  clearInterval(pollTimerRef.current);
118134
118624
  pollTimerRef.current = null;
@@ -118139,20 +118629,53 @@ function usePresenterWindow(input) {
118139
118629
  if (isAudienceWindowOpen()) {
118140
118630
  closeAudienceWindow();
118141
118631
  }
118142
- if (content) {
118143
- void storeAudienceContent(content);
118144
- }
118145
- const url = new URL(window.location.href);
118146
- url.hash = AUDIENCE_HASH;
118147
- const win = window.open(url.toString(), "_blank");
118148
- if (!win) {
118632
+ const blankWin = window.open("about:blank", "_blank");
118633
+ if (!blankWin) {
118149
118634
  return false;
118150
118635
  }
118151
- audienceWindowRef.current = win;
118636
+ audienceWindowRef.current = blankWin;
118637
+ const sessionId = generateSessionId();
118638
+ sessionIdRef.current = sessionId;
118639
+ const audienceUrl = new URL(window.location.href);
118640
+ const params2 = new URLSearchParams();
118641
+ params2.set(AUDIENCE_NONCE_KEY, sessionId);
118642
+ audienceUrl.hash = `${AUDIENCE_HASH}&${params2.toString()}`;
118643
+ const navigateOrClose = (ok) => {
118644
+ const win = audienceWindowRef.current;
118645
+ if (!win || win.closed) {
118646
+ return;
118647
+ }
118648
+ if (!ok) {
118649
+ try {
118650
+ win.close();
118651
+ } catch {
118652
+ }
118653
+ audienceWindowRef.current = null;
118654
+ sessionIdRef.current = "";
118655
+ return;
118656
+ }
118657
+ try {
118658
+ win.location.replace(audienceUrl.toString());
118659
+ } catch {
118660
+ try {
118661
+ win.close();
118662
+ } catch {
118663
+ }
118664
+ audienceWindowRef.current = null;
118665
+ sessionIdRef.current = "";
118666
+ }
118667
+ };
118668
+ if (content) {
118669
+ void storeAudienceContent(content).then(() => navigateOrClose(true)).catch(() => navigateOrClose(false));
118670
+ } else {
118671
+ navigateOrClose(true);
118672
+ }
118152
118673
  window.setTimeout(() => syncSlideToAudience(currentSlideIndex), 1500);
118153
118674
  pollTimerRef.current = setInterval(() => {
118154
- if (win.closed) {
118675
+ const win = audienceWindowRef.current;
118676
+ if (!win || win.closed) {
118155
118677
  audienceWindowRef.current = null;
118678
+ sessionIdRef.current = "";
118156
118679
  if (pollTimerRef.current !== null) {
118157
118680
  clearInterval(pollTimerRef.current);
118158
118681
  pollTimerRef.current = null;
@@ -118180,6 +118703,13 @@ function usePresenterWindow(input) {
118180
118703
  closeAudienceWindow();
118181
118704
  }
118182
118705
  }, [isPresenterMode, closeAudienceWindow]);
118706
+ useEffect(() => {
118707
+ const handleBeforeUnload = () => {
118708
+ void clearAudienceContent();
118709
+ };
118710
+ window.addEventListener("beforeunload", handleBeforeUnload);
118711
+ return () => window.removeEventListener("beforeunload", handleBeforeUnload);
118712
+ }, []);
118183
118713
  return {
118184
118714
  openAudienceWindow,
118185
118715
  closeAudienceWindow,
@@ -118217,6 +118747,7 @@ function useAudienceMode(input) {
118217
118747
  if (!isAudienceTab()) {
118218
118748
  return;
118219
118749
  }
118750
+ const expectedSessionId = parseAudienceNonce();
118220
118751
  let channel;
118221
118752
  try {
118222
118753
  channel = new BroadcastChannel(PRESENTER_CHANNEL_NAME);
@@ -118228,6 +118759,9 @@ function useAudienceMode(input) {
118228
118759
  if (!data || data.origin !== PRESENTER_MSG_ORIGIN) {
118229
118760
  return;
118230
118761
  }
118762
+ if (expectedSessionId && data.sessionId !== expectedSessionId) {
118763
+ return;
118764
+ }
118231
118765
  if (data.type === "presenter-slide-change") {
118232
118766
  onSetActiveSlideIndex(data.slideIndex);
118233
118767
  }
@@ -119161,6 +119695,12 @@ function useTouchGestures(input) {
119161
119695
  callbacksRef.current = callbacks;
119162
119696
  const scaleRef = useRef(currentScale);
119163
119697
  scaleRef.current = currentScale;
119698
+ const [targetVersion, setTargetVersion] = useState(0);
119699
+ const lastTargetRef = useRef(null);
119700
+ if (targetRef.current !== lastTargetRef.current) {
119701
+ lastTargetRef.current = targetRef.current;
119702
+ queueMicrotask(() => setTargetVersion((v) => v + 1));
119703
+ }
119164
119704
  useEffect(() => {
119165
119705
  const el = targetRef.current;
119166
119706
  if (!el || !enabled) {
@@ -119249,7 +119789,7 @@ function useTouchGestures(input) {
119249
119789
  el.removeEventListener("touchcancel", handleTouchCancel);
119250
119790
  cancelLongPress();
119251
119791
  };
119252
- }, [targetRef, enabled]);
119792
+ }, [targetRef, enabled, targetVersion]);
119253
119793
  }
119254
119794
 
119255
119795
  // src/viewer/utils/dom-helpers.ts
@@ -119270,11 +119810,31 @@ function safeConfirm(message) {
119270
119810
  return false;
119271
119811
  }
119272
119812
  }
119813
+ function sanitizeDownloadFilename(input) {
119814
+ if (typeof input !== "string" || input.trim().length === 0) {
119815
+ return "presentation.pptx";
119816
+ }
119817
+ let cleaned = input.replace(/[\x00-\x1f\x7f"\\/:*?<>|]/g, "_").replace(/\.\./g, "__").replace(/^\.+/, "").trim();
119818
+ if (cleaned.length === 0) {
119819
+ return "presentation.pptx";
119820
+ }
119821
+ if (cleaned.length > 200) {
119822
+ const dot = cleaned.lastIndexOf(".");
119823
+ if (dot > 0 && cleaned.length - dot <= 16) {
119824
+ const ext = cleaned.slice(dot);
119825
+ cleaned = cleaned.slice(0, 200 - ext.length) + ext;
119826
+ } else {
119827
+ cleaned = cleaned.slice(0, 200);
119828
+ }
119829
+ }
119830
+ return cleaned;
119831
+ }
119273
119832
  function downloadBlob(blob, filename) {
119833
+ const safeName = sanitizeDownloadFilename(filename);
119274
119834
  const url = URL.createObjectURL(blob);
119275
119835
  const a2 = document.createElement("a");
119276
119836
  a2.href = url;
119277
- a2.download = filename;
119837
+ a2.download = safeName;
119278
119838
  document.body.appendChild(a2);
119279
119839
  a2.click();
119280
119840
  setTimeout(() => {
@@ -119705,27 +120265,82 @@ function openAutosaveDb2() {
119705
120265
  req.onerror = () => reject(req.error);
119706
120266
  });
119707
120267
  }
119708
- async function saveToIndexedDb(filePath, data) {
120268
+ async function deleteOldestAutosaveEntry() {
119709
120269
  const db = await openAutosaveDb2();
119710
- return new Promise((resolve2, reject) => {
119711
- const tx = db.transaction(STORE_NAME3, "readwrite");
119712
- const store = tx.objectStore(STORE_NAME3);
119713
- store.put({
119714
- key: filePath,
119715
- data,
119716
- timestamp: Date.now(),
119717
- size: data.byteLength
119718
- });
119719
- tx.oncomplete = () => {
119720
- db.close();
119721
- resolve2(true);
119722
- };
119723
- tx.onerror = () => {
119724
- db.close();
119725
- reject(tx.error);
119726
- };
120270
+ return new Promise((resolve2) => {
120271
+ try {
120272
+ const tx = db.transaction(STORE_NAME3, "readwrite");
120273
+ const store = tx.objectStore(STORE_NAME3);
120274
+ let oldestKey = null;
120275
+ let oldestTimestamp = Infinity;
120276
+ const cursorReq = store.openCursor();
120277
+ cursorReq.onsuccess = () => {
120278
+ const cursor = cursorReq.result;
120279
+ if (cursor) {
120280
+ const value = cursor.value;
120281
+ if (typeof value.timestamp === "number" && value.timestamp < oldestTimestamp) {
120282
+ oldestTimestamp = value.timestamp;
120283
+ oldestKey = cursor.primaryKey;
120284
+ }
120285
+ cursor.continue();
120286
+ } else if (oldestKey !== null) {
120287
+ store.delete(oldestKey);
120288
+ }
120289
+ };
120290
+ tx.oncomplete = () => {
120291
+ db.close();
120292
+ resolve2(oldestKey !== null);
120293
+ };
120294
+ tx.onerror = () => {
120295
+ db.close();
120296
+ resolve2(false);
120297
+ };
120298
+ } catch {
120299
+ try {
120300
+ db.close();
120301
+ } catch {
120302
+ }
120303
+ resolve2(false);
120304
+ }
119727
120305
  });
119728
120306
  }
120307
+ function putAutosaveRecord(filePath, data) {
120308
+ return openAutosaveDb2().then(
120309
+ (db) => new Promise((resolve2, reject) => {
120310
+ const tx = db.transaction(STORE_NAME3, "readwrite");
120311
+ const store = tx.objectStore(STORE_NAME3);
120312
+ store.put({
120313
+ key: filePath,
120314
+ data,
120315
+ timestamp: Date.now(),
120316
+ size: data.byteLength
120317
+ });
120318
+ tx.oncomplete = () => {
120319
+ db.close();
120320
+ resolve2(true);
120321
+ };
120322
+ tx.onerror = () => {
120323
+ db.close();
120324
+ reject(tx.error);
120325
+ };
120326
+ })
120327
+ );
120328
+ }
120329
+ async function saveToIndexedDb(filePath, data) {
120330
+ try {
120331
+ return await putAutosaveRecord(filePath, data);
120332
+ } catch (err) {
120333
+ const errName = err instanceof Error || err instanceof DOMException ? err.name : "";
120334
+ if (errName !== "QuotaExceededError") {
120335
+ throw err;
120336
+ }
120337
+ const deleted = await deleteOldestAutosaveEntry();
120338
+ if (!deleted) {
120339
+ throw err;
120340
+ }
120341
+ return putAutosaveRecord(filePath, data);
120342
+ }
120343
+ }
119729
120344
  function useAutosave(input) {
119730
120345
  const {
119731
120346
  isDirty,
@@ -119842,6 +120457,25 @@ function collectReferencedFontFamilies(slides) {
119842
120457
  }
119843
120458
  return families;
119844
120459
  }
120460
+ var FONT_NAME_UNSAFE_CHARS = /["\\\n\r;}<>]/;
120461
+ var FONT_FORMAT_ALLOWED = /* @__PURE__ */ new Set([
120462
+ "truetype",
120463
+ "opentype",
120464
+ "woff",
120465
+ "woff2",
120466
+ "svg",
120467
+ "embedded-opentype"
120468
+ ]);
120469
+ var FONT_DATA_URL_PATTERN = /^data:font\/[a-z0-9+.-]+(?:;charset=[a-z0-9-]+)?;base64,[A-Za-z0-9+/=]+$/i;
120470
+ function isFontDataUrlSafe(url) {
120471
+ if (typeof url !== "string" || url.length === 0) {
120472
+ return false;
120473
+ }
120474
+ if (url.startsWith("blob:")) {
120475
+ return true;
120476
+ }
120477
+ return FONT_DATA_URL_PATTERN.test(url);
120478
+ }
119845
120479
  function useFontInjection({ embeddedFonts, slides }) {
119846
120480
  useEffect(() => {
119847
120481
  if (!embeddedFonts.length) {
@@ -119849,17 +120483,28 @@ function useFontInjection({ embeddedFonts, slides }) {
119849
120483
  }
119850
120484
  const styleEl = document.createElement("style");
119851
120485
  styleEl.id = EMBEDDED_FONTS_STYLE_ID;
119852
- const cssRules = embeddedFonts.map((font) => {
120486
+ const cssRules = embeddedFonts.flatMap((font) => {
120487
+ if (typeof font.name !== "string" || font.name.length === 0 || FONT_NAME_UNSAFE_CHARS.test(font.name)) {
120488
+ return [];
120489
+ }
120490
+ if (!isFontDataUrlSafe(font.dataUrl)) {
120491
+ return [];
120492
+ }
120493
+ const fontFormat = font.format ?? "truetype";
120494
+ if (!FONT_FORMAT_ALLOWED.has(fontFormat)) {
120495
+ return [];
120496
+ }
119853
120497
  const fontWeight = font.bold ? "700" : "400";
119854
120498
  const fontStyleCss = font.italic ? "italic" : "normal";
119855
- const fontFormat = font.format ?? "truetype";
119856
- return `@font-face {
120499
+ return [
120500
+ `@font-face {
119857
120501
  font-family: "${font.name}";
119858
120502
  src: url("${font.dataUrl}") format("${fontFormat}");
119859
120503
  font-weight: ${fontWeight};
119860
120504
  font-style: ${fontStyleCss};
119861
120505
  font-display: swap;
119862
- }`;
120506
+ }`
120507
+ ];
119863
120508
  }).join("\n");
119864
120509
  styleEl.textContent = cssRules;
119865
120510
  document.head.appendChild(styleEl);
@@ -119888,7 +120533,7 @@ function useFontInjection({ embeddedFonts, slides }) {
119888
120533
  const linkEl = document.createElement("link");
119889
120534
  linkEl.id = GOOGLE_FONTS_LINK_ID;
119890
120535
  linkEl.rel = "stylesheet";
119891
- linkEl.href = `https://fonts.googleapis.com/css2?${googleFamilies.map((f) => `family=${GOOGLE_FONTS_AVAILABLE[f]}`).join("&")}&display=swap`;
120536
+ linkEl.href = `https://fonts.googleapis.com/css2?${googleFamilies.map((f) => `family=${encodeURIComponent(GOOGLE_FONTS_AVAILABLE[f])}`).join("&")}&display=swap`;
119892
120537
  document.head.appendChild(linkEl);
119893
120538
  return () => {
119894
120539
  const existing = document.getElementById(GOOGLE_FONTS_LINK_ID);
@@ -119904,13 +120549,18 @@ function useFontInjection({ embeddedFonts, slides }) {
119904
120549
  }
119905
120550
  const styleEl = document.createElement("style");
119906
120551
  styleEl.id = SYMBOL_FONTS_STYLE_ID;
119907
- const rules = neededSymbolFonts.map(
119908
- (font) => `@font-face {
120552
+ const rules = neededSymbolFonts.flatMap((font) => {
120553
+ if (typeof font !== "string" || FONT_NAME_UNSAFE_CHARS.test(font)) {
120554
+ return [];
120555
+ }
120556
+ return [
120557
+ `@font-face {
119909
120558
  font-family: "${font}";
119910
120559
  src: local("${font}"), local("${font} Regular");
119911
120560
  font-display: swap;
119912
120561
  }`
119913
- ).join("\n");
120562
+ ];
120563
+ }).join("\n");
119914
120564
  styleEl.textContent = rules;
119915
120565
  document.head.appendChild(styleEl);
119916
120566
  return () => {
@@ -120053,16 +120703,17 @@ function useLoadContent({
120053
120703
  `[pptx] Large file detected (${fileSizeMB.toFixed(1)} MB). Loading may use significant memory.`
120054
120704
  );
120055
120705
  }
120056
- if (handlerRef.current) {
120057
- handlerRef.current.dispose();
120058
- handlerRef.current = null;
120059
- }
120706
+ const previousHandler = handlerRef.current;
120060
120707
  const handler = new PptxHandler();
120061
120708
  const parsed = await handler.load(buffer);
120062
120709
  if (cancelled || token !== renderTokenRef.current) {
120063
120710
  handler.dispose();
120064
120711
  return;
120065
120712
  }
120713
+ if (previousHandler) {
120714
+ previousHandler.dispose();
120715
+ }
120716
+ handlerRef.current = null;
120066
120717
  const mediaElements = [];
120067
120718
  for (const slide of parsed.slides) {
120068
120719
  collectMediaElements(slide.elements, mediaElements);
@@ -120107,6 +120758,7 @@ function useLoadContent({
120107
120758
  })
120108
120759
  );
120109
120760
  const { paths: imagePaths, refs: imageRefs } = collectImagePaths(parsed.slides);
120761
+ let nextSlides = parsed.slides;
120110
120762
  if (imagePaths.size > 0) {
120111
120763
  const resolvedMap = /* @__PURE__ */ new Map();
120112
120764
  await Promise.all(
@@ -120120,15 +120772,47 @@ function useLoadContent({
120120
120772
  }
120121
120773
  })
120122
120774
  );
120775
+ const elementPatches = /* @__PURE__ */ new Map();
120123
120776
  for (const ref of imageRefs) {
120124
120777
  const url = resolvedMap.get(ref.path);
120125
- if (url) {
120126
- ref.element[ref.field] = url;
120778
+ if (!url) {
120779
+ continue;
120127
120780
  }
120781
+ const id2 = ref.element.id;
120782
+ const existing = elementPatches.get(id2) ?? {};
120783
+ existing[ref.field] = url;
120784
+ elementPatches.set(id2, existing);
120785
+ }
120786
+ if (elementPatches.size > 0) {
120787
+ const patchElements = (elements) => {
120788
+ let mutated = false;
120789
+ const next = elements.map((el) => {
120790
+ let updated = el;
120791
+ const patch = elementPatches.get(el.id);
120792
+ if (patch) {
120793
+ updated = { ...el, ...patch };
120794
+ }
120795
+ if (updated.type === "group" && updated.children?.length) {
120796
+ const newChildren = patchElements(updated.children);
120797
+ if (newChildren !== updated.children) {
120798
+ updated = { ...updated, children: newChildren };
120799
+ }
120800
+ }
120801
+ if (updated !== el) {
120802
+ mutated = true;
120803
+ }
120804
+ return updated;
120805
+ });
120806
+ return mutated ? next : elements;
120807
+ };
120808
+ nextSlides = parsed.slides.map((s) => {
120809
+ const newElements = patchElements(s.elements);
120810
+ return newElements === s.elements ? s : { ...s, elements: newElements };
120811
+ });
120128
120812
  }
120129
120813
  }
120130
120814
  handlerRef.current = handler;
120131
- setSlides(parsed.slides);
120815
+ setSlides(nextSlides);
120132
120816
  setTemplateElementsBySlideId({});
120133
120817
  setCanvasSize({
120134
120818
  width: parsed.width ?? DEFAULT_CANVAS_WIDTH,
@@ -121302,7 +121986,19 @@ function injectFontFaces(svg, fontFaces) {
121302
121986
  if (!fontFaces.length) {
121303
121987
  return svg;
121304
121988
  }
121305
- const styleBlock = `<style type="text/css">${fontFaces.map((f) => f.css).join("\n")}</style>`;
121989
+ const safeFontFaces = fontFaces.filter((f) => {
121990
+ if (f.css.toLowerCase().includes("</style")) {
121991
+ console.warn(
121992
+ `[export-svg] Dropping @font-face entry for "${f.family}" containing "</style" \u2014 would break out of the <style> block.`
121993
+ );
121994
+ return false;
121995
+ }
121996
+ return true;
121997
+ });
121998
+ if (!safeFontFaces.length) {
121999
+ return svg;
122000
+ }
122001
+ const styleBlock = `<style type="text/css">${safeFontFaces.map((f) => f.css).join("\n")}</style>`;
121306
122002
  if (svg.includes("<defs>")) {
121307
122003
  return svg.replace("<defs>", `<defs>${styleBlock}`);
121308
122004
  }
@@ -121628,7 +122324,7 @@ function useExportHandlers(input) {
121628
122324
  activeSlideIndexForGuides,
121629
122325
  modalControls
121630
122326
  });
121631
- const handleExportPng = async () => {
122327
+ const handleExportPng = useCallback(async () => {
121632
122328
  const stageEl = canvasStageRef.current;
121633
122329
  if (!stageEl) {
121634
122330
  return;
@@ -121640,8 +122336,8 @@ function useExportHandlers(input) {
121640
122336
  } catch (err) {
121641
122337
  console.error("[PowerPointViewer] PNG export failed:", err);
121642
122338
  }
121643
- };
121644
- const handleExportPdf = async () => {
122339
+ }, [canvasStageRef, activeSlideIndex, activeSlide?.backgroundColor]);
122340
+ const handleExportPdf = useCallback(async () => {
121645
122341
  if (!canvasStageRef.current) {
121646
122342
  return;
121647
122343
  }
@@ -121682,8 +122378,8 @@ function useExportHandlers(input) {
121682
122378
  exportAbortRef.current = null;
121683
122379
  setExportModalOpen(false);
121684
122380
  }
121685
- };
121686
- const handleExportNotesPdf = async () => {
122381
+ }, [canvasStageRef, slides.length, setActiveSlideIndex, activeSlideIndex]);
122382
+ const handleExportNotesPdf = useCallback(async () => {
121687
122383
  if (!canvasStageRef.current) {
121688
122384
  return;
121689
122385
  }
@@ -121726,8 +122422,8 @@ function useExportHandlers(input) {
121726
122422
  exportAbortRef.current = null;
121727
122423
  setExportModalOpen(false);
121728
122424
  }
121729
- };
121730
- const handleCopySlideAsImage = async () => {
122425
+ }, [canvasStageRef, slides, setActiveSlideIndex, activeSlideIndex]);
122426
+ const handleCopySlideAsImage = useCallback(async () => {
121731
122427
  const stageEl = canvasStageRef.current;
121732
122428
  if (!stageEl) {
121733
122429
  return;
@@ -121739,8 +122435,8 @@ function useExportHandlers(input) {
121739
122435
  } catch (err) {
121740
122436
  console.error("[PowerPointViewer] Copy slide as image failed:", err);
121741
122437
  }
121742
- };
121743
- const handleExportVideo = async () => {
122438
+ }, [canvasStageRef, activeSlide?.backgroundColor]);
122439
+ const handleExportVideo = useCallback(async () => {
121744
122440
  if (!canvasStageRef.current) {
121745
122441
  return;
121746
122442
  }
@@ -121782,8 +122478,8 @@ function useExportHandlers(input) {
121782
122478
  exportAbortRef.current = null;
121783
122479
  setExportModalOpen(false);
121784
122480
  }
121785
- };
121786
- const handleExportGif = async () => {
122481
+ }, [canvasStageRef, slides.length, setActiveSlideIndex, activeSlideIndex]);
122482
+ const handleExportGif = useCallback(async () => {
121787
122483
  if (!canvasStageRef.current) {
121788
122484
  return;
121789
122485
  }
@@ -121821,7 +122517,7 @@ function useExportHandlers(input) {
121821
122517
  exportAbortRef.current = null;
121822
122518
  setExportModalOpen(false);
121823
122519
  }
121824
- };
122520
+ }, [canvasStageRef, slides.length, setActiveSlideIndex, activeSlideIndex]);
121825
122521
  const handleCancelExport = useCallback(() => {
121826
122522
  exportAbortRef.current?.abort();
121827
122523
  exportAbortRef.current = null;
@@ -121847,6 +122543,15 @@ function useExportHandlers(input) {
121847
122543
  exportStatusMessage
121848
122544
  };
121849
122545
  }
122546
+ function escapeHtmlAttr(value) {
122547
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
122548
+ }
122549
+ function safeDataImageSrc(src) {
122550
+ if (typeof src !== "string" || !src.startsWith("data:image/")) {
122551
+ return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNgAAIAAAUAAen63NgAAAAASUVORK5CYII=";
122552
+ }
122553
+ return escapeHtmlAttr(src);
122554
+ }
121850
122555
  function openPrintWindow(title, bodyHtml, orientation, colorFilter, frameSlides) {
121851
122556
  const printWindow = window.open("", "_blank", "noopener,noreferrer");
121852
122557
  if (!printWindow) {
@@ -122029,7 +122734,7 @@ function usePrintHandlers(input) {
122029
122734
  const slideImages = slideIndices.map((idx) => allImages[idx]).filter(Boolean);
122030
122735
  if (settings.printWhat === "slides") {
122031
122736
  const bodyHtml = slideImages.map(
122032
- (img, i3) => `<section class="page slide-page"><img class="slide-img" src="${img}" alt="Slide ${slideIndices[i3] + 1}" /></section>`
122737
+ (img, i3) => `<section class="page slide-page"><img class="slide-img" src="${safeDataImageSrc(img)}" alt="Slide ${slideIndices[i3] + 1}" /></section>`
122033
122738
  ).join("");
122034
122739
  openPrintWindow(
122035
122740
  "Slides",
@@ -122045,7 +122750,7 @@ function usePrintHandlers(input) {
122045
122750
  const idx = slideIndices[i3];
122046
122751
  const notes = slides[idx]?.notes?.trim() || "";
122047
122752
  return `<section class="page notes-page">
122048
- <img class="notes-slide" src="${img}" alt="Slide ${idx + 1}" />
122753
+ <img class="notes-slide" src="${safeDataImageSrc(img)}" alt="Slide ${idx + 1}" />
122049
122754
  <div class="notes-text">${escapeHtml2(notes)}</div>
122050
122755
  </section>`;
122051
122756
  }).join("");
@@ -122077,14 +122782,14 @@ function usePrintHandlers(input) {
122077
122782
  if (isThreePerPage) {
122078
122783
  const rows = Array.from({ length: spp }, (_, cellIndex) => {
122079
122784
  const img = pageImgs[cellIndex];
122080
- const slideCell = img ? `<div class="handout-cell"><img src="${img}" alt="Slide ${slideIndices[i3 + cellIndex] + 1}" /></div>` : `<div class="handout-cell"></div>`;
122785
+ const slideCell = img ? `<div class="handout-cell"><img src="${safeDataImageSrc(img)}" alt="Slide ${slideIndices[i3 + cellIndex] + 1}" /></div>` : `<div class="handout-cell"></div>`;
122081
122786
  return `<div class="handout-row-3">${slideCell}${buildNoteLines()}</div>`;
122082
122787
  }).join("");
122083
122788
  pages.push(`<section class="page"><div class="handout-grid-3">${rows}</div></section>`);
122084
122789
  } else {
122085
122790
  const cells = Array.from({ length: spp }, (_, cellIndex) => {
122086
122791
  const img = pageImgs[cellIndex];
122087
- return img ? `<div class="handout-cell"><img src="${img}" alt="Slide ${slideIndices[i3 + cellIndex] + 1}" /></div>` : `<div class="handout-cell"></div>`;
122792
+ return img ? `<div class="handout-cell"><img src="${safeDataImageSrc(img)}" alt="Slide ${slideIndices[i3 + cellIndex] + 1}" /></div>` : `<div class="handout-cell"></div>`;
122088
122793
  }).join("");
122089
122794
  pages.push(
122090
122795
  `<section class="page"><div class="handout-grid" style="grid-template-columns: repeat(${grid.columns}, minmax(0, 1fr)); grid-template-rows: repeat(${grid.rows}, minmax(0, 1fr));">${cells}</div></section>`