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
@@ -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, applyThemeToData, THEME_PRESETS } 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, applyThemeToData, THEME_PRESETS } 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, useMemo42 = 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, useMemo42 = 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) {
@@ -70279,6 +70280,55 @@ var ACTION_BUTTON_PRESETS = [
70279
70280
  label: "Return",
70280
70281
  defaultAction: "prevSlide",
70281
70282
  iconPath: "M18 8 L18 14 L6 14 M6 14 L10 10 M6 14 L10 18"
70283
+ },
70284
+ {
70285
+ shapeType: "actionButtonHome",
70286
+ label: "Home",
70287
+ defaultAction: "firstSlide",
70288
+ // House: roof + body
70289
+ iconPath: "M12 4 L20 11 L20 20 L14 20 L14 14 L10 14 L10 20 L4 20 L4 11 Z"
70290
+ },
70291
+ {
70292
+ shapeType: "actionButtonHelp",
70293
+ label: "Help",
70294
+ defaultAction: "none",
70295
+ // Question mark
70296
+ iconPath: "M9 9 a3 3 0 1 1 4 2.8 c-1 0.4 -1 1.2 -1 2 M12 17 v0.5"
70297
+ },
70298
+ {
70299
+ shapeType: "actionButtonInformation",
70300
+ label: "Information",
70301
+ defaultAction: "none",
70302
+ // Lower-case "i": dot + body
70303
+ iconPath: "M12 6 v0.01 M12 10 v8"
70304
+ },
70305
+ {
70306
+ shapeType: "actionButtonDocument",
70307
+ label: "Document",
70308
+ defaultAction: "none",
70309
+ // Document with folded corner
70310
+ iconPath: "M6 4 L14 4 L18 8 L18 20 L6 20 Z M14 4 L14 8 L18 8"
70311
+ },
70312
+ {
70313
+ shapeType: "actionButtonSound",
70314
+ label: "Sound",
70315
+ defaultAction: "none",
70316
+ // Speaker cone + sound waves
70317
+ 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"
70318
+ },
70319
+ {
70320
+ shapeType: "actionButtonMovie",
70321
+ label: "Movie",
70322
+ defaultAction: "none",
70323
+ // Film strip with play triangle
70324
+ iconPath: "M4 6 L20 6 L20 18 L4 18 Z M10 9 L15 12 L10 15 Z"
70325
+ },
70326
+ {
70327
+ shapeType: "actionButtonBlank",
70328
+ label: "Custom",
70329
+ defaultAction: "none",
70330
+ // No glyph — empty path. The button still renders as a rounded rect via clip-path.
70331
+ iconPath: ""
70282
70332
  }
70283
70333
  ];
70284
70334
  Object.fromEntries(ACTION_BUTTON_PRESETS.map((p3) => [p3.shapeType, p3.defaultAction]));
@@ -72558,8 +72608,8 @@ function hexToRgb2(hex) {
72558
72608
  };
72559
72609
  }
72560
72610
  function rgbToHex(r2, g2, b2) {
72561
- const clamp2 = (v) => Math.max(0, Math.min(255, Math.round(v)));
72562
- return `#${clamp2(r2).toString(16).padStart(2, "0")}${clamp2(g2).toString(16).padStart(2, "0")}${clamp2(b2).toString(16).padStart(2, "0")}`;
72611
+ const clamp3 = (v) => Math.max(0, Math.min(255, Math.round(v)));
72612
+ return `#${clamp3(r2).toString(16).padStart(2, "0")}${clamp3(g2).toString(16).padStart(2, "0")}${clamp3(b2).toString(16).padStart(2, "0")}`;
72563
72613
  }
72564
72614
  function rgbToHsl(r2, g2, b2) {
72565
72615
  const rn = r2 / 255;
@@ -74063,6 +74113,13 @@ function hasDistinctScriptFonts(fonts) {
74063
74113
  }
74064
74114
  return Boolean(fonts.eastAsia) && fonts.eastAsia !== base || Boolean(fonts.complexScript) && fonts.complexScript !== base || Boolean(fonts.symbol) && fonts.symbol !== base;
74065
74115
  }
74116
+ function sanitizeMathMl(markup) {
74117
+ const purify = DOMPurify;
74118
+ if (typeof purify.sanitize !== "function") {
74119
+ return markup;
74120
+ }
74121
+ return purify.sanitize(markup, { USE_PROFILES: { mathMl: true, svg: true } });
74122
+ }
74066
74123
  function renderScriptAwareText(text2, needsScriptFonts, scriptFonts, baseFontFamily, keyPrefix) {
74067
74124
  if (!needsScriptFonts || !text2) {
74068
74125
  return text2;
@@ -74153,14 +74210,15 @@ function renderSegmentContent(elementId, segmentIndex, textValue, lines, needsSc
74153
74210
  }
74154
74211
  function renderEquationSegment(elementId, segmentIndex, equationXml, equationNumber) {
74155
74212
  const mathml = convertOmmlToMathMl(equationXml);
74156
- const equationContent = mathml ? /* @__PURE__ */ jsx(
74213
+ const safeMathml = mathml ? sanitizeMathMl(mathml) : "";
74214
+ const equationContent = safeMathml ? /* @__PURE__ */ jsx(
74157
74215
  "span",
74158
74216
  {
74159
74217
  className: "inline-block align-middle",
74160
74218
  style: {
74161
74219
  fontFamily: '"Cambria Math", "STIX Two Math", serif'
74162
74220
  },
74163
- dangerouslySetInnerHTML: { __html: mathml }
74221
+ dangerouslySetInnerHTML: { __html: safeMathml }
74164
74222
  }
74165
74223
  ) : /* @__PURE__ */ jsx("span", { className: "inline-block px-1 py-0.5 rounded text-xs bg-gray-200/20 text-gray-400 italic", children: "Equation" });
74166
74224
  if (equationNumber) {
@@ -74761,6 +74819,11 @@ function getShapeClipPath(shapeType, adjustments, width, height) {
74761
74819
  }
74762
74820
  const normalized = shapeType.toLowerCase();
74763
74821
  if (normalized === "round1rect" || normalized === "round2samerect" || normalized === "round2diagrect" || normalized === "sniproundrect" || normalized === "snip1rect" || normalized === "snip2diagrect") {
74822
+ if (adjustments?.adj !== void 0 && width && height) {
74823
+ const ratio = Math.min(Math.max(adjustments.adj / 1e5, 0), 0.5);
74824
+ const radiusPx = Math.round(Math.min(width, height) * ratio);
74825
+ return `inset(0 round ${radiusPx}px)`;
74826
+ }
74764
74827
  return "inset(0 round 18px)";
74765
74828
  }
74766
74829
  if (normalized === "can" || normalized === "cylinder") {
@@ -75407,6 +75470,128 @@ function mapDagBlendModeToCss(blend) {
75407
75470
  return void 0;
75408
75471
  }
75409
75472
  }
75473
+ function getImageAlphaFilterId(elementId) {
75474
+ return `imgalpha-${elementId}`;
75475
+ }
75476
+ function hasAdvancedImageAlphaEffects(element2) {
75477
+ if (!isImageLikeElement(element2)) {
75478
+ return false;
75479
+ }
75480
+ const e2 = element2.imageEffects;
75481
+ if (!e2) {
75482
+ return false;
75483
+ }
75484
+ return Boolean(
75485
+ 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
75486
+ );
75487
+ }
75488
+ function renderImageAlphaSvgFilter(element2) {
75489
+ if (!isImageLikeElement(element2)) {
75490
+ return null;
75491
+ }
75492
+ const e2 = element2.imageEffects;
75493
+ if (!e2 || !hasAdvancedImageAlphaEffects(element2)) {
75494
+ return null;
75495
+ }
75496
+ const filterId = getImageAlphaFilterId(element2.id);
75497
+ const primitives = [];
75498
+ let resultIdx = 0;
75499
+ let inputRef = "SourceGraphic";
75500
+ const next = (jsx229) => {
75501
+ const result = `r${resultIdx++}`;
75502
+ primitives.push(jsx229(inputRef, result));
75503
+ inputRef = result;
75504
+ };
75505
+ if (typeof e2.alphaModFix === "number") {
75506
+ const mul = clamp(e2.alphaModFix / 100, 0, 1);
75507
+ next((inp, out) => /* @__PURE__ */ jsx(
75508
+ "feColorMatrix",
75509
+ {
75510
+ in: inp,
75511
+ result: out,
75512
+ type: "matrix",
75513
+ values: `1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 ${mul} 0`
75514
+ },
75515
+ out
75516
+ ));
75517
+ }
75518
+ if (e2.alphaInv) {
75519
+ next((inp, out) => /* @__PURE__ */ jsx("feComponentTransfer", { in: inp, result: out, children: /* @__PURE__ */ jsx("feFuncA", { type: "linear", slope: -1, intercept: 1 }) }, out));
75520
+ }
75521
+ if (e2.alphaCeiling) {
75522
+ 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));
75523
+ }
75524
+ if (e2.alphaFloor) {
75525
+ 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));
75526
+ }
75527
+ if (typeof e2.alphaRepl === "number") {
75528
+ const a2 = clamp(e2.alphaRepl / 100, 0, 1);
75529
+ next((inp, out) => /* @__PURE__ */ jsx("feComponentTransfer", { in: inp, result: out, children: /* @__PURE__ */ jsx("feFuncA", { type: "linear", slope: 0, intercept: a2 }) }, out));
75530
+ }
75531
+ if (typeof e2.alphaBiLevel === "number") {
75532
+ const t2 = clamp(e2.alphaBiLevel / 100, 0, 1);
75533
+ const table = Array.from({ length: 10 }, (_, i3) => i3 / 10 >= t2 ? "1" : "0").join(" ");
75534
+ next((inp, out) => /* @__PURE__ */ jsx("feComponentTransfer", { in: inp, result: out, children: /* @__PURE__ */ jsx("feFuncA", { type: "discrete", tableValues: table }) }, out));
75535
+ }
75536
+ if (typeof e2.biLevel === "number") {
75537
+ next((inp, out) => /* @__PURE__ */ jsx("feColorMatrix", { in: inp, result: out, type: "saturate", values: "0" }, out));
75538
+ const t2 = clamp(e2.biLevel / 100, 0, 1);
75539
+ const tbl = t2 < 0.5 ? "0 1" : "0 1";
75540
+ next((inp, out) => /* @__PURE__ */ jsxs("feComponentTransfer", { in: inp, result: out, children: [
75541
+ /* @__PURE__ */ jsx("feFuncR", { type: "discrete", tableValues: tbl }),
75542
+ /* @__PURE__ */ jsx("feFuncG", { type: "discrete", tableValues: tbl }),
75543
+ /* @__PURE__ */ jsx("feFuncB", { type: "discrete", tableValues: tbl })
75544
+ ] }, out));
75545
+ }
75546
+ if (e2.lum && (typeof e2.lum.bright === "number" || typeof e2.lum.contrast === "number")) {
75547
+ const b2 = (e2.lum.bright ?? 0) / 100;
75548
+ const c2 = 1 + (e2.lum.contrast ?? 0) / 100;
75549
+ const slope = c2;
75550
+ const intercept = b2 + (1 - c2) / 2;
75551
+ next((inp, out) => /* @__PURE__ */ jsxs("feComponentTransfer", { in: inp, result: out, children: [
75552
+ /* @__PURE__ */ jsx("feFuncR", { type: "linear", slope, intercept }),
75553
+ /* @__PURE__ */ jsx("feFuncG", { type: "linear", slope, intercept }),
75554
+ /* @__PURE__ */ jsx("feFuncB", { type: "linear", slope, intercept })
75555
+ ] }, out));
75556
+ }
75557
+ if (e2.hsl && typeof e2.hsl.sat === "number") {
75558
+ const v = clamp(1 + e2.hsl.sat / 100, 0, 2);
75559
+ next((inp, out) => /* @__PURE__ */ jsx("feColorMatrix", { in: inp, result: out, type: "saturate", values: String(v) }, out));
75560
+ }
75561
+ if (e2.tint && typeof e2.tint.amt === "number" && e2.tint.amt < 0) {
75562
+ const v = clamp(1 + e2.tint.amt / 100, 0, 1);
75563
+ next((inp, out) => /* @__PURE__ */ jsx("feColorMatrix", { in: inp, result: out, type: "saturate", values: String(v) }, out));
75564
+ }
75565
+ if (e2.clrRepl) {
75566
+ const c2 = hexToRgbUnit(e2.clrRepl.color);
75567
+ next((inp, out) => /* @__PURE__ */ jsx(
75568
+ "feColorMatrix",
75569
+ {
75570
+ in: inp,
75571
+ result: out,
75572
+ type: "matrix",
75573
+ values: `0 0 0 0 ${c2.r} 0 0 0 0 ${c2.g} 0 0 0 0 ${c2.b} 0 0 0 1 0`
75574
+ },
75575
+ out
75576
+ ));
75577
+ }
75578
+ if (primitives.length === 0) {
75579
+ return null;
75580
+ }
75581
+ return /* @__PURE__ */ jsx(
75582
+ "svg",
75583
+ {
75584
+ width: 0,
75585
+ height: 0,
75586
+ style: { position: "absolute", overflow: "hidden" },
75587
+ "aria-hidden": "true",
75588
+ children: /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx("filter", { id: filterId, colorInterpolationFilters: "sRGB", children: primitives }) })
75589
+ }
75590
+ );
75591
+ }
75592
+ function clamp(v, lo, hi) {
75593
+ return v < lo ? lo : v > hi ? hi : v;
75594
+ }
75410
75595
  function getDagDuotoneFilterId(elementId) {
75411
75596
  return `dag-duotone-${elementId}`;
75412
75597
  }
@@ -75490,6 +75675,9 @@ function getImageEffectsFilter(element2, options) {
75490
75675
  const filterId = getDuotoneFilterId(element2.id);
75491
75676
  filters.push(`url(#${filterId})`);
75492
75677
  }
75678
+ if (hasAdvancedImageAlphaEffects(element2)) {
75679
+ filters.push(`url(#${getImageAlphaFilterId(element2.id)})`);
75680
+ }
75493
75681
  if (effects.artisticEffect) {
75494
75682
  const radius = effects.artisticRadius ?? 5;
75495
75683
  if (needsSvgArtisticFilter(effects.artisticEffect)) {
@@ -75663,6 +75851,39 @@ function getEffectDagCssFilter(style, elementId) {
75663
75851
  return filters.length > 0 ? filters.join(" ") : void 0;
75664
75852
  }
75665
75853
  var getEffectDagFilter = getEffectDagCssFilter;
75854
+ function getResolvedShapeClipPathFor(shapeType, width, height, adjustments) {
75855
+ if (!shapeType) {
75856
+ return void 0;
75857
+ }
75858
+ if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
75859
+ return getShapeClipPath(shapeType, adjustments, width, height);
75860
+ }
75861
+ if (adjustments && Object.keys(adjustments).length > 0) {
75862
+ const adjusted = getAdjustmentAwareShapeClipPath(shapeType, width, height, adjustments);
75863
+ if (adjusted !== void 0) {
75864
+ return adjusted;
75865
+ }
75866
+ }
75867
+ const fromPreset = getShapeClipPathFromPreset(shapeType, width, height, adjustments);
75868
+ if (fromPreset !== void 0) {
75869
+ return fromPreset;
75870
+ }
75871
+ const cloud = getCloudPathForRendering(shapeType, width, height);
75872
+ if (cloud !== void 0) {
75873
+ return cloud;
75874
+ }
75875
+ return getShapeClipPath(shapeType, adjustments, width, height);
75876
+ }
75877
+ function getResolvedShapeClipPath(element2, width, height) {
75878
+ const shapeType = element2.shapeType;
75879
+ if (!shapeType) {
75880
+ return void 0;
75881
+ }
75882
+ const w = element2.width;
75883
+ const h2 = element2.height;
75884
+ const adjustments = element2.shapeAdjustments;
75885
+ return getResolvedShapeClipPathFor(shapeType, w, h2, adjustments);
75886
+ }
75666
75887
 
75667
75888
  // src/viewer/utils/shape-round-rect.ts
75668
75889
  function localClampAdjustment(value) {
@@ -75734,6 +75955,27 @@ var MATERIAL_MAP = {
75734
75955
  filter: "brightness(1.1) contrast(0.85)",
75735
75956
  // Translucent powder: slight translucent glow
75736
75957
  backgroundImage: "radial-gradient(ellipse at 30% 30%, rgba(255,255,255,0.1) 0%, transparent 60%)"
75958
+ },
75959
+ // Legacy materials (PowerPoint 2007 / earlier). Render as muted variants
75960
+ // of the modern equivalents so legacy decks still resemble the originals.
75961
+ legacyMatte: {
75962
+ filter: "brightness(0.92) saturate(0.85)",
75963
+ backgroundImage: "linear-gradient(180deg, rgba(255,255,255,0.03) 0%, transparent 50%, rgba(0,0,0,0.04) 100%)"
75964
+ },
75965
+ legacyPlastic: {
75966
+ filter: "brightness(1.02) contrast(1.03)",
75967
+ boxShadow: "inset -2px -2px 5px rgba(255,255,255,0.3)",
75968
+ backgroundImage: "radial-gradient(ellipse 35% 25% at 25% 20%, rgba(255,255,255,0.15) 0%, transparent 70%)"
75969
+ },
75970
+ legacyMetal: {
75971
+ filter: "brightness(1.05) contrast(1.1) saturate(1.1)",
75972
+ boxShadow: "inset -2px -2px 6px rgba(255,255,255,0.35), inset 1px 1px 3px rgba(255,255,255,0.15)",
75973
+ 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%)"
75974
+ },
75975
+ legacyWireframe: {
75976
+ filter: "brightness(1) contrast(1.4) saturate(0.6)",
75977
+ // Wireframe: high contrast outline-emphasising look
75978
+ boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.4)"
75737
75979
  }
75738
75980
  };
75739
75981
  function getMaterialCssOverrides(material) {
@@ -76688,8 +76930,7 @@ function getShapeVisualStyle(element2, hasFill, fillColor, strokeWidth, strokeCo
76688
76930
  return {};
76689
76931
  }
76690
76932
  const normalizedShapeType = getShapeType(element2.shapeType);
76691
- const shapeType = element2.shapeType || normalizedShapeType;
76692
- const clipPath = getShapeClipPath(shapeType);
76933
+ const clipPath = getResolvedShapeClipPath(element2);
76693
76934
  const fillOpacity = element2.shapeStyle?.fillOpacity;
76694
76935
  const strokeOpacity = element2.shapeStyle?.strokeOpacity;
76695
76936
  const strokeDash = normalizeStrokeDashType(element2.shapeStyle?.strokeDash);
@@ -77291,7 +77532,7 @@ function getImageMaskStyle(element2) {
77291
77532
  if (normalized === "can" || normalized === "cylinder") {
77292
77533
  return { borderRadius: "48% / 12%" };
77293
77534
  }
77294
- const clipPath = getShapeClipPath(shapeType);
77535
+ const clipPath = getResolvedShapeClipPath(element2);
77295
77536
  if (!clipPath) {
77296
77537
  return void 0;
77297
77538
  }
@@ -77954,8 +78195,8 @@ function hexToRgb3(hex) {
77954
78195
  };
77955
78196
  }
77956
78197
  function rgbToHex2(r2, g2, b2) {
77957
- const clamp2 = (v) => Math.max(0, Math.min(255, Math.round(v)));
77958
- 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()}`;
78198
+ const clamp3 = (v) => Math.max(0, Math.min(255, Math.round(v)));
78199
+ 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()}`;
77959
78200
  }
77960
78201
  function tintColor(hex, tintFactor) {
77961
78202
  const { r: r2, g: g2, b: b2 } = hexToRgb3(hex);
@@ -78886,13 +79127,13 @@ function cellStyleToCss(style) {
78886
79127
  }
78887
79128
  if (style.textDirection) {
78888
79129
  switch (style.textDirection) {
78889
- case "vertical":
79130
+ case "vert":
78890
79131
  case "eaVert":
78891
79132
  case "wordArtVert":
78892
79133
  case "wordArtVertRtl":
78893
79134
  css.writingMode = "vertical-rl";
78894
79135
  break;
78895
- case "vertical270":
79136
+ case "vert270":
78896
79137
  case "mongolianVert":
78897
79138
  css.writingMode = "vertical-lr";
78898
79139
  break;
@@ -79419,8 +79660,8 @@ function hexToRgb4(hex) {
79419
79660
  ];
79420
79661
  }
79421
79662
  function rgbToHex3(r2, g2, b2) {
79422
- const clamp2 = (v) => Math.max(0, Math.min(255, Math.round(v)));
79423
- return `#${clamp2(r2).toString(16).padStart(2, "0")}${clamp2(g2).toString(16).padStart(2, "0")}${clamp2(b2).toString(16).padStart(2, "0")}`;
79663
+ const clamp3 = (v) => Math.max(0, Math.min(255, Math.round(v)));
79664
+ return `#${clamp3(r2).toString(16).padStart(2, "0")}${clamp3(g2).toString(16).padStart(2, "0")}${clamp3(b2).toString(16).padStart(2, "0")}`;
79424
79665
  }
79425
79666
  function tint(hex, amount) {
79426
79667
  const [r2, g2, b2] = hexToRgb4(hex);
@@ -81338,7 +81579,7 @@ function resolveRegionCode(label) {
81338
81579
  return REGION_ALIAS_MAP[normalized];
81339
81580
  }
81340
81581
  function lerpColor(a2, b2, t2) {
81341
- const clamp2 = (v) => Math.max(0, Math.min(255, Math.round(v)));
81582
+ const clamp3 = (v) => Math.max(0, Math.min(255, Math.round(v)));
81342
81583
  const parse = (hex) => {
81343
81584
  const h2 = hex.replace("#", "");
81344
81585
  return [
@@ -81349,9 +81590,9 @@ function lerpColor(a2, b2, t2) {
81349
81590
  };
81350
81591
  const [r1, g1, b1] = parse(a2);
81351
81592
  const [r2, g2, b22] = parse(b2);
81352
- const r3 = clamp2(r1 + (r2 - r1) * t2);
81353
- const g3 = clamp2(g1 + (g2 - g1) * t2);
81354
- const bl = clamp2(b1 + (b22 - b1) * t2);
81593
+ const r3 = clamp3(r1 + (r2 - r1) * t2);
81594
+ const g3 = clamp3(g1 + (g2 - g1) * t2);
81595
+ const bl = clamp3(b1 + (b22 - b1) * t2);
81355
81596
  return `#${r3.toString(16).padStart(2, "0")}${g3.toString(16).padStart(2, "0")}${bl.toString(16).padStart(2, "0")}`;
81356
81597
  }
81357
81598
  function sequentialColorScale(t2) {
@@ -86517,7 +86758,7 @@ function ResizeHandle({
86517
86758
  }
86518
86759
  );
86519
86760
  }
86520
- function SlideThumbnail({
86761
+ function SlideThumbnailImpl({
86521
86762
  slide,
86522
86763
  templateElements,
86523
86764
  canvasSize
@@ -86755,6 +86996,37 @@ function ThumbnailTable({
86755
86996
  }
86756
86997
  return /* @__PURE__ */ jsx("div", { className: "w-full h-full flex items-center justify-center text-[10px] text-muted-foreground pointer-events-none", children: "Table" });
86757
86998
  }
86999
+ function arePropsEqual(prev, next) {
87000
+ if (prev.slide.id !== next.slide.id) {
87001
+ return false;
87002
+ }
87003
+ if (prev.slide.isDirty !== next.slide.isDirty) {
87004
+ return false;
87005
+ }
87006
+ if (prev.slide.hidden !== next.slide.hidden) {
87007
+ return false;
87008
+ }
87009
+ if (prev.slide.elements !== next.slide.elements) {
87010
+ return false;
87011
+ }
87012
+ if (prev.slide.backgroundColor !== next.slide.backgroundColor) {
87013
+ return false;
87014
+ }
87015
+ if (prev.slide.backgroundImage !== next.slide.backgroundImage) {
87016
+ return false;
87017
+ }
87018
+ if (prev.slide.backgroundGradient !== next.slide.backgroundGradient) {
87019
+ return false;
87020
+ }
87021
+ if (prev.templateElements !== next.templateElements) {
87022
+ return false;
87023
+ }
87024
+ if (prev.canvasSize.width !== next.canvasSize.width || prev.canvasSize.height !== next.canvasSize.height) {
87025
+ return false;
87026
+ }
87027
+ return true;
87028
+ }
87029
+ var SlideThumbnail = React10__default.memo(SlideThumbnailImpl, arePropsEqual);
86758
87030
  function ContextMenu({
86759
87031
  contextMenuState,
86760
87032
  mode,
@@ -88072,6 +88344,59 @@ function WarpedText({
88072
88344
  }
88073
88345
  );
88074
88346
  }
88347
+ var GLYPH_BY_SHAPE = Object.fromEntries(
88348
+ ACTION_BUTTON_PRESETS.map((p3) => [p3.shapeType, p3.iconPath])
88349
+ );
88350
+ GLYPH_BY_SHAPE["actionButtonForwardOrNext"] = GLYPH_BY_SHAPE["actionButtonForwardNext"];
88351
+ GLYPH_BY_SHAPE["actionButtonBackOrPrevious"] = GLYPH_BY_SHAPE["actionButtonBackPrevious"];
88352
+ function isActionButtonShape(shapeType) {
88353
+ return Boolean(shapeType && shapeType in GLYPH_BY_SHAPE);
88354
+ }
88355
+ function getActionButtonGlyphPath(shapeType) {
88356
+ if (!shapeType) {
88357
+ return void 0;
88358
+ }
88359
+ const path = GLYPH_BY_SHAPE[shapeType];
88360
+ return path && path.length > 0 ? path : void 0;
88361
+ }
88362
+ function ActionButtonGlyphOverlay({
88363
+ element: element2,
88364
+ color
88365
+ }) {
88366
+ const shapeType = "shapeType" in element2 ? element2.shapeType : void 0;
88367
+ const path = getActionButtonGlyphPath(shapeType);
88368
+ if (!path) {
88369
+ return null;
88370
+ }
88371
+ const stroke = color ?? ("textStyle" in element2 && element2.textStyle?.color || "#ffffff");
88372
+ return /* @__PURE__ */ jsx(
88373
+ "svg",
88374
+ {
88375
+ viewBox: "0 0 24 24",
88376
+ width: "100%",
88377
+ height: "100%",
88378
+ preserveAspectRatio: "xMidYMid meet",
88379
+ style: {
88380
+ position: "absolute",
88381
+ inset: 0,
88382
+ pointerEvents: "none",
88383
+ padding: "20%"
88384
+ },
88385
+ "aria-hidden": "true",
88386
+ children: /* @__PURE__ */ jsx(
88387
+ "path",
88388
+ {
88389
+ d: path,
88390
+ fill: "none",
88391
+ stroke,
88392
+ strokeWidth: 2,
88393
+ strokeLinecap: "round",
88394
+ strokeLinejoin: "round"
88395
+ }
88396
+ )
88397
+ }
88398
+ );
88399
+ }
88075
88400
  function ColorChangedImage({
88076
88401
  src,
88077
88402
  clrChange,
@@ -88342,6 +88667,7 @@ function renderImg(el, style, filter, alt, opacity) {
88342
88667
  return /* @__PURE__ */ jsxs(Fragment, { children: [
88343
88668
  tileDuotoneColors && renderDuotoneSvgFilter(el.id, tileDuotoneColors.color1, tileDuotoneColors.color2),
88344
88669
  renderArtisticEffectSvgFilter(el.id, artisticEffectName, artisticRadius),
88670
+ renderImageAlphaSvgFilter(el),
88345
88671
  /* @__PURE__ */ jsx(
88346
88672
  "div",
88347
88673
  {
@@ -88363,6 +88689,7 @@ function renderImg(el, style, filter, alt, opacity) {
88363
88689
  return /* @__PURE__ */ jsxs(Fragment, { children: [
88364
88690
  duotoneColors && !useDuotoneCanvas && renderDuotoneSvgFilter(el.id, duotoneColors.color1, duotoneColors.color2),
88365
88691
  renderArtisticEffectSvgFilter(el.id, artisticEffectName, artisticRadius),
88692
+ renderImageAlphaSvgFilter(el),
88366
88693
  useDuotoneCanvas && duotoneColors ? /* @__PURE__ */ jsx(
88367
88694
  DuotoneImage,
88368
88695
  {
@@ -90488,7 +90815,7 @@ function BendingProcessRenderer({
90488
90815
  }
90489
90816
  );
90490
90817
  }
90491
- function SmartArtRenderer({
90818
+ function SmartArtRendererImpl({
90492
90819
  element: element2,
90493
90820
  className = ""
90494
90821
  }) {
@@ -90599,6 +90926,30 @@ function renderLayout(layoutType, element2, nodes, palette, style) {
90599
90926
  }
90600
90927
  return /* @__PURE__ */ jsx(ListRenderer, { element: element2, nodes, palette, style });
90601
90928
  }
90929
+ function arePropsEqual2(prev, next) {
90930
+ if (prev.className !== next.className) {
90931
+ return false;
90932
+ }
90933
+ if (prev.element.id !== next.element.id) {
90934
+ return false;
90935
+ }
90936
+ if (prev.element.type !== next.element.type) {
90937
+ return false;
90938
+ }
90939
+ if (prev.element.width !== next.element.width || prev.element.height !== next.element.height) {
90940
+ return false;
90941
+ }
90942
+ if (prev.element.x !== next.element.x || prev.element.y !== next.element.y) {
90943
+ return false;
90944
+ }
90945
+ const prevData = prev.element.type === "smartArt" ? prev.element.smartArtData : void 0;
90946
+ const nextData = next.element.type === "smartArt" ? next.element.smartArtData : void 0;
90947
+ if (prevData !== nextData) {
90948
+ return false;
90949
+ }
90950
+ return true;
90951
+ }
90952
+ var SmartArtRenderer = React10__default.memo(SmartArtRendererImpl, arePropsEqual2);
90602
90953
  function ZoomElementRenderer({
90603
90954
  element: element2,
90604
90955
  slides,
@@ -90847,8 +91198,11 @@ function renderBody(el, isImg, isEditing, editText, spellCheck, txtSE, txtS, vec
90847
91198
  ...scene3dStyle.perspective ? { perspective: scene3dStyle.perspective } : {},
90848
91199
  ...scene3dStyle.transformStyle ? { transformStyle: scene3dStyle.transformStyle } : {}
90849
91200
  } : void 0;
91201
+ const shapeTypeForGlyph = "shapeType" in el ? el.shapeType : void 0;
91202
+ const showActionButtonGlyph = isActionButtonShape(shapeTypeForGlyph);
90850
91203
  return /* @__PURE__ */ jsxs(Fragment, { children: [
90851
91204
  vecShape,
91205
+ showActionButtonGlyph && /* @__PURE__ */ jsx(ActionButtonGlyphOverlay, { element: el }),
90852
91206
  isTxtEl ? useSvgWarp ? /* @__PURE__ */ jsx(
90853
91207
  "div",
90854
91208
  {
@@ -93637,7 +93991,7 @@ function SectionBlock({
93637
93991
  )
93638
93992
  ] });
93639
93993
  }
93640
- function SlideCard({
93994
+ function SlideCardImpl({
93641
93995
  slide,
93642
93996
  index,
93643
93997
  isActive,
@@ -93691,6 +94045,49 @@ function SlideCard({
93691
94045
  }
93692
94046
  );
93693
94047
  }
94048
+ function arePropsEqual3(prev, next) {
94049
+ if (prev.slide.id !== next.slide.id) {
94050
+ return false;
94051
+ }
94052
+ if (prev.slide.isDirty !== next.slide.isDirty) {
94053
+ return false;
94054
+ }
94055
+ if (prev.slide.hidden !== next.slide.hidden) {
94056
+ return false;
94057
+ }
94058
+ if (prev.slide.elements !== next.slide.elements) {
94059
+ return false;
94060
+ }
94061
+ if (prev.index !== next.index) {
94062
+ return false;
94063
+ }
94064
+ if (prev.isActive !== next.isActive) {
94065
+ return false;
94066
+ }
94067
+ if (prev.isDragTarget !== next.isDragTarget) {
94068
+ return false;
94069
+ }
94070
+ if (prev.isSelected !== next.isSelected) {
94071
+ return false;
94072
+ }
94073
+ if (prev.selectedCount !== next.selectedCount) {
94074
+ return false;
94075
+ }
94076
+ if (prev.selectionOrder !== next.selectionOrder) {
94077
+ return false;
94078
+ }
94079
+ if (prev.canEdit !== next.canEdit) {
94080
+ return false;
94081
+ }
94082
+ if (prev.canvasSize.width !== next.canvasSize.width || prev.canvasSize.height !== next.canvasSize.height) {
94083
+ return false;
94084
+ }
94085
+ 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) {
94086
+ return false;
94087
+ }
94088
+ return true;
94089
+ }
94090
+ var SlideCard = React10__default.memo(SlideCardImpl, arePropsEqual3);
93694
94091
  function SorterContextMenu({
93695
94092
  x: x2,
93696
94093
  y,
@@ -94484,6 +94881,14 @@ function getCurrentParagraphIndex(editorEl, segments) {
94484
94881
  }
94485
94882
  return paraIdx;
94486
94883
  }
94884
+ var CSS_VALUE_SAFE = /^[a-zA-Z0-9 _,.\-+#'%/]{1,100}$/;
94885
+ function isCssValueSafe(value) {
94886
+ if (value === void 0 || value === null) {
94887
+ return false;
94888
+ }
94889
+ const str = String(value);
94890
+ return str.length > 0 && CSS_VALUE_SAFE.test(str);
94891
+ }
94487
94892
  function deriveStyleFromElement(element2, inheritedStyle) {
94488
94893
  const style = { ...inheritedStyle };
94489
94894
  const tagName = element2.tagName.toLowerCase();
@@ -94616,17 +95021,17 @@ function segmentsToEditorHtml(segments) {
94616
95021
  if (segment.style.strikethrough) {
94617
95022
  inlineStyles.push("text-decoration:line-through");
94618
95023
  }
94619
- if (segment.style.color) {
95024
+ if (segment.style.color && isCssValueSafe(segment.style.color)) {
94620
95025
  inlineStyles.push(`color:${segment.style.color}`);
94621
95026
  }
94622
- if (segment.style.fontSize) {
94623
- inlineStyles.push(`font-size:${segment.style.fontSize}pt`);
95027
+ if (segment.style.fontSize && Number.isFinite(Number(segment.style.fontSize))) {
95028
+ inlineStyles.push(`font-size:${Number(segment.style.fontSize)}pt`);
94624
95029
  }
94625
- if (segment.style.fontFamily) {
95030
+ if (segment.style.fontFamily && isCssValueSafe(segment.style.fontFamily)) {
94626
95031
  inlineStyles.push(`font-family:${segment.style.fontFamily}`);
94627
95032
  }
94628
95033
  const text2 = escapeHtml(segment.text);
94629
- if (segment.style.hyperlink) {
95034
+ if (segment.style.hyperlink && isUrlSafe(segment.style.hyperlink)) {
94630
95035
  const href = escapeHtml(segment.style.hyperlink);
94631
95036
  return `<a href="${href}" style="color:#4a9eff;text-decoration:underline;cursor:pointer" data-hyperlink="${href}">${text2}</a>`;
94632
95037
  }
@@ -94709,7 +95114,8 @@ function renderRichNotesSegments(segments) {
94709
95114
  if (segment.style.fontFamily) {
94710
95115
  style.fontFamily = segment.style.fontFamily;
94711
95116
  }
94712
- if (segment.style.hyperlink) {
95117
+ if (segment.style.hyperlink && isUrlSafe(segment.style.hyperlink)) {
95118
+ const safeHref = segment.style.hyperlink;
94713
95119
  style.color = "#4a9eff";
94714
95120
  style.textDecoration = "underline";
94715
95121
  style.cursor = "pointer";
@@ -94717,11 +95123,11 @@ function renderRichNotesSegments(segments) {
94717
95123
  /* @__PURE__ */ jsx(
94718
95124
  "a",
94719
95125
  {
94720
- href: segment.style.hyperlink,
95126
+ href: safeHref,
94721
95127
  style,
94722
95128
  onClick: (e2) => {
94723
95129
  e2.preventDefault();
94724
- window.open(segment.style.hyperlink, "_blank");
95130
+ safeOpenUrl(safeHref);
94725
95131
  },
94726
95132
  children: segment.text
94727
95133
  },
@@ -95273,7 +95679,7 @@ function useSlideNotes({
95273
95679
  const href = target.getAttribute("data-hyperlink") || target.getAttribute("href");
95274
95680
  if (href && (e2.ctrlKey || e2.metaKey)) {
95275
95681
  e2.preventDefault();
95276
- window.open(href, "_blank");
95682
+ safeOpenUrl(href);
95277
95683
  }
95278
95684
  }, []);
95279
95685
  return {
@@ -96946,7 +97352,7 @@ function renderNotesSegments(segments) {
96946
97352
  return React10__default.createElement("span", { key: `seg-${index}`, style }, segment.text);
96947
97353
  });
96948
97354
  }
96949
- function ScaledSlidePreview({
97355
+ function ScaledSlidePreviewImpl({
96950
97356
  slide,
96951
97357
  templateElements,
96952
97358
  canvasSize,
@@ -97093,6 +97499,40 @@ function ScaledSlidePreview({
97093
97499
  }
97094
97500
  );
97095
97501
  }
97502
+ function arePropsEqual4(prev, next) {
97503
+ if (prev.slide.id !== next.slide.id) {
97504
+ return false;
97505
+ }
97506
+ if (prev.slide.isDirty !== next.slide.isDirty) {
97507
+ return false;
97508
+ }
97509
+ if (prev.slide.hidden !== next.slide.hidden) {
97510
+ return false;
97511
+ }
97512
+ if (prev.slide.elements !== next.slide.elements) {
97513
+ return false;
97514
+ }
97515
+ if (prev.slide.backgroundColor !== next.slide.backgroundColor) {
97516
+ return false;
97517
+ }
97518
+ if (prev.slide.backgroundImage !== next.slide.backgroundImage) {
97519
+ return false;
97520
+ }
97521
+ if (prev.slide.backgroundGradient !== next.slide.backgroundGradient) {
97522
+ return false;
97523
+ }
97524
+ if (prev.templateElements !== next.templateElements) {
97525
+ return false;
97526
+ }
97527
+ if (prev.canvasSize.width !== next.canvasSize.width || prev.canvasSize.height !== next.canvasSize.height) {
97528
+ return false;
97529
+ }
97530
+ if (prev.className !== next.className) {
97531
+ return false;
97532
+ }
97533
+ return true;
97534
+ }
97535
+ var ScaledSlidePreview = React10__default.memo(ScaledSlidePreviewImpl, arePropsEqual4);
97096
97536
  function PresenterView({
97097
97537
  slides,
97098
97538
  currentSlideIndex,
@@ -111396,6 +111836,13 @@ function convertOmmlToLatex(omml) {
111396
111836
  }
111397
111837
  return ommlChildrenToLatex(oMath);
111398
111838
  }
111839
+ function sanitizeMathMl2(markup) {
111840
+ const purify = DOMPurify;
111841
+ if (typeof purify.sanitize !== "function") {
111842
+ return markup;
111843
+ }
111844
+ return purify.sanitize(markup, { USE_PROFILES: { mathMl: true, svg: true } });
111845
+ }
111399
111846
  var TEMPLATES = [
111400
111847
  {
111401
111848
  label: "Fraction",
@@ -111461,7 +111908,8 @@ var TEMPLATES = [
111461
111908
  var TEMPLATE_MATHML = TEMPLATES.map((tmpl) => {
111462
111909
  try {
111463
111910
  const tmplOmml = convertLatexToOmml(tmpl.latex);
111464
- return convertOmmlToMathMl(tmplOmml);
111911
+ const raw = convertOmmlToMathMl(tmplOmml);
111912
+ return raw ? sanitizeMathMl2(raw) : "";
111465
111913
  } catch {
111466
111914
  return "";
111467
111915
  }
@@ -111473,7 +111921,7 @@ function MathMlPreview({ mathml }) {
111473
111921
  return;
111474
111922
  }
111475
111923
  if (mathml) {
111476
- containerRef.current.innerHTML = mathml;
111924
+ containerRef.current.innerHTML = sanitizeMathMl2(mathml);
111477
111925
  } else {
111478
111926
  containerRef.current.innerHTML = "";
111479
111927
  }
@@ -111501,6 +111949,7 @@ function EquationEditorDialog({
111501
111949
  return convertOmmlToLatex(existingOmml);
111502
111950
  }, [existingOmml]);
111503
111951
  const [latex, setLatex] = useState(initialLatex);
111952
+ const deferredLatex = useDeferredValue(latex);
111504
111953
  const textareaRef = useRef(null);
111505
111954
  useEffect(() => {
111506
111955
  if (isOpen) {
@@ -111509,17 +111958,17 @@ function EquationEditorDialog({
111509
111958
  }
111510
111959
  }, [isOpen, initialLatex]);
111511
111960
  const { mathml, omml } = useMemo(() => {
111512
- if (!latex.trim()) {
111961
+ if (!deferredLatex.trim()) {
111513
111962
  return { mathml: "", omml: {} };
111514
111963
  }
111515
111964
  try {
111516
- const ommlObj = convertLatexToOmml(latex);
111965
+ const ommlObj = convertLatexToOmml(deferredLatex);
111517
111966
  const mathmlStr = convertOmmlToMathMl(ommlObj);
111518
111967
  return { mathml: mathmlStr, omml: ommlObj };
111519
111968
  } catch {
111520
111969
  return { mathml: "", omml: {} };
111521
111970
  }
111522
- }, [latex]);
111971
+ }, [deferredLatex]);
111523
111972
  const handleInsert = useCallback(() => {
111524
111973
  if (!latex.trim()) {
111525
111974
  return;
@@ -114283,6 +114732,7 @@ function useEditorHistory(input) {
114283
114732
  const historyFutureRef = useRef([]);
114284
114733
  const lastHistorySnapshotRef = useRef(null);
114285
114734
  const lastHistorySerializedRef = useRef("");
114735
+ const lastCheapHashRef = useRef("");
114286
114736
  const isApplyingHistoryRef = useRef(false);
114287
114737
  const unlockHistoryTimerRef = useRef(null);
114288
114738
  const [canUndo, setCanUndo] = useState(false);
@@ -114424,15 +114874,21 @@ function useEditorHistory(input) {
114424
114874
  if (hasActivePointerInteraction()) {
114425
114875
  return;
114426
114876
  }
114877
+ const cheapHash = `${slides.length}|${activeSlideIndex}|${canvasSize.width}x${canvasSize.height}|${slides.map((s) => `${s.id}:${s.elements.length}`).join("/")}`;
114878
+ if (cheapHash === lastCheapHashRef.current) {
114879
+ return;
114880
+ }
114427
114881
  const snapshot2 = buildHistorySnapshot();
114428
114882
  const serialized = JSON.stringify(snapshot2);
114429
114883
  if (serialized === lastHistorySerializedRef.current) {
114884
+ lastCheapHashRef.current = cheapHash;
114430
114885
  return;
114431
114886
  }
114432
114887
  const previousSnapshot = lastHistorySnapshotRef.current;
114433
114888
  if (!previousSnapshot) {
114434
114889
  lastHistorySnapshotRef.current = cloneHistorySnapshot(snapshot2);
114435
114890
  lastHistorySerializedRef.current = serialized;
114891
+ lastCheapHashRef.current = cheapHash;
114436
114892
  updateHistoryAvailability();
114437
114893
  return;
114438
114894
  }
@@ -114443,13 +114899,18 @@ function useEditorHistory(input) {
114443
114899
  historyFutureRef.current = [];
114444
114900
  lastHistorySnapshotRef.current = cloneHistorySnapshot(snapshot2);
114445
114901
  lastHistorySerializedRef.current = serialized;
114902
+ lastCheapHashRef.current = cheapHash;
114446
114903
  updateHistoryAvailability();
114447
114904
  }, [
114905
+ activeSlideIndex,
114448
114906
  buildHistorySnapshot,
114907
+ canvasSize.height,
114908
+ canvasSize.width,
114449
114909
  error2,
114450
114910
  hasActivePointerInteraction,
114451
114911
  loading2,
114452
114912
  pointerCommitNonce,
114913
+ slides,
114453
114914
  updateHistoryAvailability
114454
114915
  ]);
114455
114916
  return {
@@ -118142,6 +118603,7 @@ var DB_NAME2 = "pptx-viewer-audience";
118142
118603
  var DB_VERSION2 = 1;
118143
118604
  var STORE_NAME2 = "content";
118144
118605
  var CONTENT_KEY = "pptx-bytes";
118606
+ var MAX_CONTENT_AGE_MS = 5 * 60 * 1e3;
118145
118607
  function openDb() {
118146
118608
  return new Promise((resolve2, reject) => {
118147
118609
  const request = indexedDB.open(DB_NAME2, DB_VERSION2);
@@ -118161,7 +118623,8 @@ async function storeAudienceContent(content) {
118161
118623
  const tx = db.transaction(STORE_NAME2, "readwrite");
118162
118624
  const store = tx.objectStore(STORE_NAME2);
118163
118625
  const bytes = content instanceof Uint8Array ? content : new Uint8Array(content);
118164
- store.put(bytes, CONTENT_KEY);
118626
+ const record = { bytes, createdAt: Date.now() };
118627
+ store.put(record, CONTENT_KEY);
118165
118628
  tx.oncomplete = () => {
118166
118629
  db.close();
118167
118630
  resolve2();
@@ -118182,13 +118645,24 @@ async function loadAudienceContent() {
118182
118645
  request.onsuccess = () => {
118183
118646
  db.close();
118184
118647
  const result = request.result;
118185
- if (result instanceof Uint8Array) {
118186
- resolve2(result);
118187
- } else if (result instanceof ArrayBuffer) {
118188
- resolve2(new Uint8Array(result));
118189
- } else {
118190
- resolve2(null);
118648
+ if (result && typeof result === "object" && "bytes" in result && "createdAt" in result) {
118649
+ const record = result;
118650
+ const age = Date.now() - record.createdAt;
118651
+ if (age > MAX_CONTENT_AGE_MS) {
118652
+ resolve2(null);
118653
+ return;
118654
+ }
118655
+ const raw = record.bytes;
118656
+ if (raw instanceof Uint8Array) {
118657
+ resolve2(raw);
118658
+ } else if (raw instanceof ArrayBuffer) {
118659
+ resolve2(new Uint8Array(raw));
118660
+ } else {
118661
+ resolve2(null);
118662
+ }
118663
+ return;
118191
118664
  }
118665
+ resolve2(null);
118192
118666
  };
118193
118667
  request.onerror = () => {
118194
118668
  db.close();
@@ -118221,14 +118695,34 @@ async function clearAudienceContent() {
118221
118695
  var PRESENTER_CHANNEL_NAME = "pptx-viewer-presenter";
118222
118696
  var AUDIENCE_HASH = "#pptx-audience";
118223
118697
  var PRESENTER_MSG_ORIGIN = "pptx-viewer-presenter";
118698
+ var AUDIENCE_NONCE_KEY = "nonce";
118699
+ function generateSessionId() {
118700
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
118701
+ return crypto.randomUUID();
118702
+ }
118703
+ return `s${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
118704
+ }
118705
+ function parseAudienceNonce() {
118706
+ const hash = window.location.hash;
118707
+ if (!hash.startsWith(AUDIENCE_HASH)) {
118708
+ return null;
118709
+ }
118710
+ const trailing = hash.slice(AUDIENCE_HASH.length);
118711
+ if (!trailing) {
118712
+ return null;
118713
+ }
118714
+ const params2 = new URLSearchParams(trailing.replace(/^[&;?]/, ""));
118715
+ return params2.get(AUDIENCE_NONCE_KEY);
118716
+ }
118224
118717
  function isAudienceTab() {
118225
- return window.location.hash === AUDIENCE_HASH;
118718
+ return window.location.hash.startsWith(AUDIENCE_HASH);
118226
118719
  }
118227
118720
  function usePresenterWindow(input) {
118228
118721
  const { currentSlideIndex, isPresenterMode, content } = input;
118229
118722
  const audienceWindowRef = useRef(null);
118230
118723
  const channelRef = useRef(null);
118231
118724
  const pollTimerRef = useRef(null);
118725
+ const sessionIdRef = useRef("");
118232
118726
  const getChannel2 = useCallback(() => {
118233
118727
  if (!channelRef.current) {
118234
118728
  channelRef.current = new BroadcastChannel(PRESENTER_CHANNEL_NAME);
@@ -118240,10 +118734,14 @@ function usePresenterWindow(input) {
118240
118734
  }, []);
118241
118735
  const syncSlideToAudience = useCallback(
118242
118736
  (slideIndex) => {
118737
+ if (!sessionIdRef.current) {
118738
+ return;
118739
+ }
118243
118740
  const msg = {
118244
118741
  origin: PRESENTER_MSG_ORIGIN,
118245
118742
  type: "presenter-slide-change",
118246
- slideIndex
118743
+ slideIndex,
118744
+ sessionId: sessionIdRef.current
118247
118745
  };
118248
118746
  try {
118249
118747
  getChannel2().postMessage(msg);
@@ -118253,13 +118751,16 @@ function usePresenterWindow(input) {
118253
118751
  [getChannel2]
118254
118752
  );
118255
118753
  const closeAudienceWindow = useCallback(() => {
118256
- try {
118257
- const exitMsg = {
118258
- origin: PRESENTER_MSG_ORIGIN,
118259
- type: "presenter-exit"
118260
- };
118261
- getChannel2().postMessage(exitMsg);
118262
- } catch {
118754
+ if (sessionIdRef.current) {
118755
+ try {
118756
+ const exitMsg = {
118757
+ origin: PRESENTER_MSG_ORIGIN,
118758
+ type: "presenter-exit",
118759
+ sessionId: sessionIdRef.current
118760
+ };
118761
+ getChannel2().postMessage(exitMsg);
118762
+ } catch {
118763
+ }
118263
118764
  }
118264
118765
  const win = audienceWindowRef.current;
118265
118766
  if (win && !win.closed) {
@@ -118269,6 +118770,7 @@ function usePresenterWindow(input) {
118269
118770
  }
118270
118771
  }
118271
118772
  audienceWindowRef.current = null;
118773
+ sessionIdRef.current = "";
118272
118774
  if (pollTimerRef.current !== null) {
118273
118775
  clearInterval(pollTimerRef.current);
118274
118776
  pollTimerRef.current = null;
@@ -118279,20 +118781,53 @@ function usePresenterWindow(input) {
118279
118781
  if (isAudienceWindowOpen()) {
118280
118782
  closeAudienceWindow();
118281
118783
  }
118282
- if (content) {
118283
- void storeAudienceContent(content);
118284
- }
118285
- const url = new URL(window.location.href);
118286
- url.hash = AUDIENCE_HASH;
118287
- const win = window.open(url.toString(), "_blank");
118288
- if (!win) {
118784
+ const blankWin = window.open("about:blank", "_blank");
118785
+ if (!blankWin) {
118289
118786
  return false;
118290
118787
  }
118291
- audienceWindowRef.current = win;
118788
+ audienceWindowRef.current = blankWin;
118789
+ const sessionId = generateSessionId();
118790
+ sessionIdRef.current = sessionId;
118791
+ const audienceUrl = new URL(window.location.href);
118792
+ const params2 = new URLSearchParams();
118793
+ params2.set(AUDIENCE_NONCE_KEY, sessionId);
118794
+ audienceUrl.hash = `${AUDIENCE_HASH}&${params2.toString()}`;
118795
+ const navigateOrClose = (ok) => {
118796
+ const win = audienceWindowRef.current;
118797
+ if (!win || win.closed) {
118798
+ return;
118799
+ }
118800
+ if (!ok) {
118801
+ try {
118802
+ win.close();
118803
+ } catch {
118804
+ }
118805
+ audienceWindowRef.current = null;
118806
+ sessionIdRef.current = "";
118807
+ return;
118808
+ }
118809
+ try {
118810
+ win.location.replace(audienceUrl.toString());
118811
+ } catch {
118812
+ try {
118813
+ win.close();
118814
+ } catch {
118815
+ }
118816
+ audienceWindowRef.current = null;
118817
+ sessionIdRef.current = "";
118818
+ }
118819
+ };
118820
+ if (content) {
118821
+ void storeAudienceContent(content).then(() => navigateOrClose(true)).catch(() => navigateOrClose(false));
118822
+ } else {
118823
+ navigateOrClose(true);
118824
+ }
118292
118825
  window.setTimeout(() => syncSlideToAudience(currentSlideIndex), 1500);
118293
118826
  pollTimerRef.current = setInterval(() => {
118294
- if (win.closed) {
118827
+ const win = audienceWindowRef.current;
118828
+ if (!win || win.closed) {
118295
118829
  audienceWindowRef.current = null;
118830
+ sessionIdRef.current = "";
118296
118831
  if (pollTimerRef.current !== null) {
118297
118832
  clearInterval(pollTimerRef.current);
118298
118833
  pollTimerRef.current = null;
@@ -118320,6 +118855,13 @@ function usePresenterWindow(input) {
118320
118855
  closeAudienceWindow();
118321
118856
  }
118322
118857
  }, [isPresenterMode, closeAudienceWindow]);
118858
+ useEffect(() => {
118859
+ const handleBeforeUnload = () => {
118860
+ void clearAudienceContent();
118861
+ };
118862
+ window.addEventListener("beforeunload", handleBeforeUnload);
118863
+ return () => window.removeEventListener("beforeunload", handleBeforeUnload);
118864
+ }, []);
118323
118865
  return {
118324
118866
  openAudienceWindow,
118325
118867
  closeAudienceWindow,
@@ -118357,6 +118899,7 @@ function useAudienceMode(input) {
118357
118899
  if (!isAudienceTab()) {
118358
118900
  return;
118359
118901
  }
118902
+ const expectedSessionId = parseAudienceNonce();
118360
118903
  let channel;
118361
118904
  try {
118362
118905
  channel = new BroadcastChannel(PRESENTER_CHANNEL_NAME);
@@ -118368,6 +118911,9 @@ function useAudienceMode(input) {
118368
118911
  if (!data || data.origin !== PRESENTER_MSG_ORIGIN) {
118369
118912
  return;
118370
118913
  }
118914
+ if (expectedSessionId && data.sessionId !== expectedSessionId) {
118915
+ return;
118916
+ }
118371
118917
  if (data.type === "presenter-slide-change") {
118372
118918
  onSetActiveSlideIndex(data.slideIndex);
118373
118919
  }
@@ -119301,6 +119847,12 @@ function useTouchGestures(input) {
119301
119847
  callbacksRef.current = callbacks;
119302
119848
  const scaleRef = useRef(currentScale);
119303
119849
  scaleRef.current = currentScale;
119850
+ const [targetVersion, setTargetVersion] = useState(0);
119851
+ const lastTargetRef = useRef(null);
119852
+ if (targetRef.current !== lastTargetRef.current) {
119853
+ lastTargetRef.current = targetRef.current;
119854
+ queueMicrotask(() => setTargetVersion((v) => v + 1));
119855
+ }
119304
119856
  useEffect(() => {
119305
119857
  const el = targetRef.current;
119306
119858
  if (!el || !enabled) {
@@ -119389,7 +119941,7 @@ function useTouchGestures(input) {
119389
119941
  el.removeEventListener("touchcancel", handleTouchCancel);
119390
119942
  cancelLongPress();
119391
119943
  };
119392
- }, [targetRef, enabled]);
119944
+ }, [targetRef, enabled, targetVersion]);
119393
119945
  }
119394
119946
 
119395
119947
  // src/viewer/utils/dom-helpers.ts
@@ -119410,11 +119962,31 @@ function safeConfirm(message) {
119410
119962
  return false;
119411
119963
  }
119412
119964
  }
119965
+ function sanitizeDownloadFilename(input) {
119966
+ if (typeof input !== "string" || input.trim().length === 0) {
119967
+ return "presentation.pptx";
119968
+ }
119969
+ let cleaned = input.replace(/[\x00-\x1f\x7f"\\/:*?<>|]/g, "_").replace(/\.\./g, "__").replace(/^\.+/, "").trim();
119970
+ if (cleaned.length === 0) {
119971
+ return "presentation.pptx";
119972
+ }
119973
+ if (cleaned.length > 200) {
119974
+ const dot = cleaned.lastIndexOf(".");
119975
+ if (dot > 0 && cleaned.length - dot <= 16) {
119976
+ const ext = cleaned.slice(dot);
119977
+ cleaned = cleaned.slice(0, 200 - ext.length) + ext;
119978
+ } else {
119979
+ cleaned = cleaned.slice(0, 200);
119980
+ }
119981
+ }
119982
+ return cleaned;
119983
+ }
119413
119984
  function downloadBlob(blob, filename) {
119985
+ const safeName = sanitizeDownloadFilename(filename);
119414
119986
  const url = URL.createObjectURL(blob);
119415
119987
  const a2 = document.createElement("a");
119416
119988
  a2.href = url;
119417
- a2.download = filename;
119989
+ a2.download = safeName;
119418
119990
  document.body.appendChild(a2);
119419
119991
  a2.click();
119420
119992
  setTimeout(() => {
@@ -119845,27 +120417,82 @@ function openAutosaveDb2() {
119845
120417
  req.onerror = () => reject(req.error);
119846
120418
  });
119847
120419
  }
119848
- async function saveToIndexedDb(filePath, data) {
120420
+ async function deleteOldestAutosaveEntry() {
119849
120421
  const db = await openAutosaveDb2();
119850
- return new Promise((resolve2, reject) => {
119851
- const tx = db.transaction(STORE_NAME3, "readwrite");
119852
- const store = tx.objectStore(STORE_NAME3);
119853
- store.put({
119854
- key: filePath,
119855
- data,
119856
- timestamp: Date.now(),
119857
- size: data.byteLength
119858
- });
119859
- tx.oncomplete = () => {
119860
- db.close();
119861
- resolve2(true);
119862
- };
119863
- tx.onerror = () => {
119864
- db.close();
119865
- reject(tx.error);
119866
- };
120422
+ return new Promise((resolve2) => {
120423
+ try {
120424
+ const tx = db.transaction(STORE_NAME3, "readwrite");
120425
+ const store = tx.objectStore(STORE_NAME3);
120426
+ let oldestKey = null;
120427
+ let oldestTimestamp = Infinity;
120428
+ const cursorReq = store.openCursor();
120429
+ cursorReq.onsuccess = () => {
120430
+ const cursor = cursorReq.result;
120431
+ if (cursor) {
120432
+ const value = cursor.value;
120433
+ if (typeof value.timestamp === "number" && value.timestamp < oldestTimestamp) {
120434
+ oldestTimestamp = value.timestamp;
120435
+ oldestKey = cursor.primaryKey;
120436
+ }
120437
+ cursor.continue();
120438
+ } else if (oldestKey !== null) {
120439
+ store.delete(oldestKey);
120440
+ }
120441
+ };
120442
+ tx.oncomplete = () => {
120443
+ db.close();
120444
+ resolve2(oldestKey !== null);
120445
+ };
120446
+ tx.onerror = () => {
120447
+ db.close();
120448
+ resolve2(false);
120449
+ };
120450
+ } catch {
120451
+ try {
120452
+ db.close();
120453
+ } catch {
120454
+ }
120455
+ resolve2(false);
120456
+ }
119867
120457
  });
119868
120458
  }
120459
+ function putAutosaveRecord(filePath, data) {
120460
+ return openAutosaveDb2().then(
120461
+ (db) => new Promise((resolve2, reject) => {
120462
+ const tx = db.transaction(STORE_NAME3, "readwrite");
120463
+ const store = tx.objectStore(STORE_NAME3);
120464
+ store.put({
120465
+ key: filePath,
120466
+ data,
120467
+ timestamp: Date.now(),
120468
+ size: data.byteLength
120469
+ });
120470
+ tx.oncomplete = () => {
120471
+ db.close();
120472
+ resolve2(true);
120473
+ };
120474
+ tx.onerror = () => {
120475
+ db.close();
120476
+ reject(tx.error);
120477
+ };
120478
+ })
120479
+ );
120480
+ }
120481
+ async function saveToIndexedDb(filePath, data) {
120482
+ try {
120483
+ return await putAutosaveRecord(filePath, data);
120484
+ } catch (err) {
120485
+ const errName = err instanceof Error || err instanceof DOMException ? err.name : "";
120486
+ if (errName !== "QuotaExceededError") {
120487
+ throw err;
120488
+ }
120489
+ const deleted = await deleteOldestAutosaveEntry();
120490
+ if (!deleted) {
120491
+ throw err;
120492
+ }
120493
+ return putAutosaveRecord(filePath, data);
120494
+ }
120495
+ }
119869
120496
  function useAutosave(input) {
119870
120497
  const {
119871
120498
  isDirty,
@@ -119982,6 +120609,25 @@ function collectReferencedFontFamilies(slides) {
119982
120609
  }
119983
120610
  return families;
119984
120611
  }
120612
+ var FONT_NAME_UNSAFE_CHARS = /["\\\n\r;}<>]/;
120613
+ var FONT_FORMAT_ALLOWED = /* @__PURE__ */ new Set([
120614
+ "truetype",
120615
+ "opentype",
120616
+ "woff",
120617
+ "woff2",
120618
+ "svg",
120619
+ "embedded-opentype"
120620
+ ]);
120621
+ var FONT_DATA_URL_PATTERN = /^data:font\/[a-z0-9+.-]+(?:;charset=[a-z0-9-]+)?;base64,[A-Za-z0-9+/=]+$/i;
120622
+ function isFontDataUrlSafe(url) {
120623
+ if (typeof url !== "string" || url.length === 0) {
120624
+ return false;
120625
+ }
120626
+ if (url.startsWith("blob:")) {
120627
+ return true;
120628
+ }
120629
+ return FONT_DATA_URL_PATTERN.test(url);
120630
+ }
119985
120631
  function useFontInjection({ embeddedFonts, slides }) {
119986
120632
  useEffect(() => {
119987
120633
  if (!embeddedFonts.length) {
@@ -119989,17 +120635,28 @@ function useFontInjection({ embeddedFonts, slides }) {
119989
120635
  }
119990
120636
  const styleEl = document.createElement("style");
119991
120637
  styleEl.id = EMBEDDED_FONTS_STYLE_ID;
119992
- const cssRules = embeddedFonts.map((font) => {
120638
+ const cssRules = embeddedFonts.flatMap((font) => {
120639
+ if (typeof font.name !== "string" || font.name.length === 0 || FONT_NAME_UNSAFE_CHARS.test(font.name)) {
120640
+ return [];
120641
+ }
120642
+ if (!isFontDataUrlSafe(font.dataUrl)) {
120643
+ return [];
120644
+ }
120645
+ const fontFormat = font.format ?? "truetype";
120646
+ if (!FONT_FORMAT_ALLOWED.has(fontFormat)) {
120647
+ return [];
120648
+ }
119993
120649
  const fontWeight = font.bold ? "700" : "400";
119994
120650
  const fontStyleCss = font.italic ? "italic" : "normal";
119995
- const fontFormat = font.format ?? "truetype";
119996
- return `@font-face {
120651
+ return [
120652
+ `@font-face {
119997
120653
  font-family: "${font.name}";
119998
120654
  src: url("${font.dataUrl}") format("${fontFormat}");
119999
120655
  font-weight: ${fontWeight};
120000
120656
  font-style: ${fontStyleCss};
120001
120657
  font-display: swap;
120002
- }`;
120658
+ }`
120659
+ ];
120003
120660
  }).join("\n");
120004
120661
  styleEl.textContent = cssRules;
120005
120662
  document.head.appendChild(styleEl);
@@ -120028,7 +120685,7 @@ function useFontInjection({ embeddedFonts, slides }) {
120028
120685
  const linkEl = document.createElement("link");
120029
120686
  linkEl.id = GOOGLE_FONTS_LINK_ID;
120030
120687
  linkEl.rel = "stylesheet";
120031
- linkEl.href = `https://fonts.googleapis.com/css2?${googleFamilies.map((f) => `family=${GOOGLE_FONTS_AVAILABLE[f]}`).join("&")}&display=swap`;
120688
+ linkEl.href = `https://fonts.googleapis.com/css2?${googleFamilies.map((f) => `family=${encodeURIComponent(GOOGLE_FONTS_AVAILABLE[f])}`).join("&")}&display=swap`;
120032
120689
  document.head.appendChild(linkEl);
120033
120690
  return () => {
120034
120691
  const existing = document.getElementById(GOOGLE_FONTS_LINK_ID);
@@ -120044,13 +120701,18 @@ function useFontInjection({ embeddedFonts, slides }) {
120044
120701
  }
120045
120702
  const styleEl = document.createElement("style");
120046
120703
  styleEl.id = SYMBOL_FONTS_STYLE_ID;
120047
- const rules = neededSymbolFonts.map(
120048
- (font) => `@font-face {
120704
+ const rules = neededSymbolFonts.flatMap((font) => {
120705
+ if (typeof font !== "string" || FONT_NAME_UNSAFE_CHARS.test(font)) {
120706
+ return [];
120707
+ }
120708
+ return [
120709
+ `@font-face {
120049
120710
  font-family: "${font}";
120050
120711
  src: local("${font}"), local("${font} Regular");
120051
120712
  font-display: swap;
120052
120713
  }`
120053
- ).join("\n");
120714
+ ];
120715
+ }).join("\n");
120054
120716
  styleEl.textContent = rules;
120055
120717
  document.head.appendChild(styleEl);
120056
120718
  return () => {
@@ -120193,16 +120855,17 @@ function useLoadContent({
120193
120855
  `[pptx] Large file detected (${fileSizeMB.toFixed(1)} MB). Loading may use significant memory.`
120194
120856
  );
120195
120857
  }
120196
- if (handlerRef.current) {
120197
- handlerRef.current.dispose();
120198
- handlerRef.current = null;
120199
- }
120858
+ const previousHandler = handlerRef.current;
120200
120859
  const handler = new PptxHandler();
120201
120860
  const parsed = await handler.load(buffer);
120202
120861
  if (cancelled || token !== renderTokenRef.current) {
120203
120862
  handler.dispose();
120204
120863
  return;
120205
120864
  }
120865
+ if (previousHandler) {
120866
+ previousHandler.dispose();
120867
+ }
120868
+ handlerRef.current = null;
120206
120869
  const mediaElements = [];
120207
120870
  for (const slide of parsed.slides) {
120208
120871
  collectMediaElements(slide.elements, mediaElements);
@@ -120247,6 +120910,7 @@ function useLoadContent({
120247
120910
  })
120248
120911
  );
120249
120912
  const { paths: imagePaths, refs: imageRefs } = collectImagePaths(parsed.slides);
120913
+ let nextSlides = parsed.slides;
120250
120914
  if (imagePaths.size > 0) {
120251
120915
  const resolvedMap = /* @__PURE__ */ new Map();
120252
120916
  await Promise.all(
@@ -120260,15 +120924,47 @@ function useLoadContent({
120260
120924
  }
120261
120925
  })
120262
120926
  );
120927
+ const elementPatches = /* @__PURE__ */ new Map();
120263
120928
  for (const ref of imageRefs) {
120264
120929
  const url = resolvedMap.get(ref.path);
120265
- if (url) {
120266
- ref.element[ref.field] = url;
120930
+ if (!url) {
120931
+ continue;
120267
120932
  }
120933
+ const id2 = ref.element.id;
120934
+ const existing = elementPatches.get(id2) ?? {};
120935
+ existing[ref.field] = url;
120936
+ elementPatches.set(id2, existing);
120937
+ }
120938
+ if (elementPatches.size > 0) {
120939
+ const patchElements = (elements) => {
120940
+ let mutated = false;
120941
+ const next = elements.map((el) => {
120942
+ let updated = el;
120943
+ const patch = elementPatches.get(el.id);
120944
+ if (patch) {
120945
+ updated = { ...el, ...patch };
120946
+ }
120947
+ if (updated.type === "group" && updated.children?.length) {
120948
+ const newChildren = patchElements(updated.children);
120949
+ if (newChildren !== updated.children) {
120950
+ updated = { ...updated, children: newChildren };
120951
+ }
120952
+ }
120953
+ if (updated !== el) {
120954
+ mutated = true;
120955
+ }
120956
+ return updated;
120957
+ });
120958
+ return mutated ? next : elements;
120959
+ };
120960
+ nextSlides = parsed.slides.map((s) => {
120961
+ const newElements = patchElements(s.elements);
120962
+ return newElements === s.elements ? s : { ...s, elements: newElements };
120963
+ });
120268
120964
  }
120269
120965
  }
120270
120966
  handlerRef.current = handler;
120271
- setSlides(parsed.slides);
120967
+ setSlides(nextSlides);
120272
120968
  setTemplateElementsBySlideId({});
120273
120969
  setCanvasSize({
120274
120970
  width: parsed.width ?? DEFAULT_CANVAS_WIDTH,
@@ -121442,7 +122138,19 @@ function injectFontFaces(svg, fontFaces) {
121442
122138
  if (!fontFaces.length) {
121443
122139
  return svg;
121444
122140
  }
121445
- const styleBlock = `<style type="text/css">${fontFaces.map((f) => f.css).join("\n")}</style>`;
122141
+ const safeFontFaces = fontFaces.filter((f) => {
122142
+ if (f.css.toLowerCase().includes("</style")) {
122143
+ console.warn(
122144
+ `[export-svg] Dropping @font-face entry for "${f.family}" containing "</style" \u2014 would break out of the <style> block.`
122145
+ );
122146
+ return false;
122147
+ }
122148
+ return true;
122149
+ });
122150
+ if (!safeFontFaces.length) {
122151
+ return svg;
122152
+ }
122153
+ const styleBlock = `<style type="text/css">${safeFontFaces.map((f) => f.css).join("\n")}</style>`;
121446
122154
  if (svg.includes("<defs>")) {
121447
122155
  return svg.replace("<defs>", `<defs>${styleBlock}`);
121448
122156
  }
@@ -121768,7 +122476,7 @@ function useExportHandlers(input) {
121768
122476
  activeSlideIndexForGuides,
121769
122477
  modalControls
121770
122478
  });
121771
- const handleExportPng = async () => {
122479
+ const handleExportPng = useCallback(async () => {
121772
122480
  const stageEl = canvasStageRef.current;
121773
122481
  if (!stageEl) {
121774
122482
  return;
@@ -121780,8 +122488,8 @@ function useExportHandlers(input) {
121780
122488
  } catch (err) {
121781
122489
  console.error("[PowerPointViewer] PNG export failed:", err);
121782
122490
  }
121783
- };
121784
- const handleExportPdf = async () => {
122491
+ }, [canvasStageRef, activeSlideIndex, activeSlide?.backgroundColor]);
122492
+ const handleExportPdf = useCallback(async () => {
121785
122493
  if (!canvasStageRef.current) {
121786
122494
  return;
121787
122495
  }
@@ -121822,8 +122530,8 @@ function useExportHandlers(input) {
121822
122530
  exportAbortRef.current = null;
121823
122531
  setExportModalOpen(false);
121824
122532
  }
121825
- };
121826
- const handleExportNotesPdf = async () => {
122533
+ }, [canvasStageRef, slides.length, setActiveSlideIndex, activeSlideIndex]);
122534
+ const handleExportNotesPdf = useCallback(async () => {
121827
122535
  if (!canvasStageRef.current) {
121828
122536
  return;
121829
122537
  }
@@ -121866,8 +122574,8 @@ function useExportHandlers(input) {
121866
122574
  exportAbortRef.current = null;
121867
122575
  setExportModalOpen(false);
121868
122576
  }
121869
- };
121870
- const handleCopySlideAsImage = async () => {
122577
+ }, [canvasStageRef, slides, setActiveSlideIndex, activeSlideIndex]);
122578
+ const handleCopySlideAsImage = useCallback(async () => {
121871
122579
  const stageEl = canvasStageRef.current;
121872
122580
  if (!stageEl) {
121873
122581
  return;
@@ -121879,8 +122587,8 @@ function useExportHandlers(input) {
121879
122587
  } catch (err) {
121880
122588
  console.error("[PowerPointViewer] Copy slide as image failed:", err);
121881
122589
  }
121882
- };
121883
- const handleExportVideo = async () => {
122590
+ }, [canvasStageRef, activeSlide?.backgroundColor]);
122591
+ const handleExportVideo = useCallback(async () => {
121884
122592
  if (!canvasStageRef.current) {
121885
122593
  return;
121886
122594
  }
@@ -121922,8 +122630,8 @@ function useExportHandlers(input) {
121922
122630
  exportAbortRef.current = null;
121923
122631
  setExportModalOpen(false);
121924
122632
  }
121925
- };
121926
- const handleExportGif = async () => {
122633
+ }, [canvasStageRef, slides.length, setActiveSlideIndex, activeSlideIndex]);
122634
+ const handleExportGif = useCallback(async () => {
121927
122635
  if (!canvasStageRef.current) {
121928
122636
  return;
121929
122637
  }
@@ -121961,7 +122669,7 @@ function useExportHandlers(input) {
121961
122669
  exportAbortRef.current = null;
121962
122670
  setExportModalOpen(false);
121963
122671
  }
121964
- };
122672
+ }, [canvasStageRef, slides.length, setActiveSlideIndex, activeSlideIndex]);
121965
122673
  const handleCancelExport = useCallback(() => {
121966
122674
  exportAbortRef.current?.abort();
121967
122675
  exportAbortRef.current = null;
@@ -121987,6 +122695,15 @@ function useExportHandlers(input) {
121987
122695
  exportStatusMessage
121988
122696
  };
121989
122697
  }
122698
+ function escapeHtmlAttr(value) {
122699
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
122700
+ }
122701
+ function safeDataImageSrc(src) {
122702
+ if (typeof src !== "string" || !src.startsWith("data:image/")) {
122703
+ return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNgAAIAAAUAAen63NgAAAAASUVORK5CYII=";
122704
+ }
122705
+ return escapeHtmlAttr(src);
122706
+ }
121990
122707
  function openPrintWindow(title, bodyHtml, orientation, colorFilter, frameSlides) {
121991
122708
  const printWindow = window.open("", "_blank", "noopener,noreferrer");
121992
122709
  if (!printWindow) {
@@ -122169,7 +122886,7 @@ function usePrintHandlers(input) {
122169
122886
  const slideImages = slideIndices.map((idx) => allImages[idx]).filter(Boolean);
122170
122887
  if (settings.printWhat === "slides") {
122171
122888
  const bodyHtml = slideImages.map(
122172
- (img, i3) => `<section class="page slide-page"><img class="slide-img" src="${img}" alt="Slide ${slideIndices[i3] + 1}" /></section>`
122889
+ (img, i3) => `<section class="page slide-page"><img class="slide-img" src="${safeDataImageSrc(img)}" alt="Slide ${slideIndices[i3] + 1}" /></section>`
122173
122890
  ).join("");
122174
122891
  openPrintWindow(
122175
122892
  "Slides",
@@ -122185,7 +122902,7 @@ function usePrintHandlers(input) {
122185
122902
  const idx = slideIndices[i3];
122186
122903
  const notes = slides[idx]?.notes?.trim() || "";
122187
122904
  return `<section class="page notes-page">
122188
- <img class="notes-slide" src="${img}" alt="Slide ${idx + 1}" />
122905
+ <img class="notes-slide" src="${safeDataImageSrc(img)}" alt="Slide ${idx + 1}" />
122189
122906
  <div class="notes-text">${escapeHtml2(notes)}</div>
122190
122907
  </section>`;
122191
122908
  }).join("");
@@ -122217,14 +122934,14 @@ function usePrintHandlers(input) {
122217
122934
  if (isThreePerPage) {
122218
122935
  const rows = Array.from({ length: spp }, (_, cellIndex) => {
122219
122936
  const img = pageImgs[cellIndex];
122220
- const slideCell = img ? `<div class="handout-cell"><img src="${img}" alt="Slide ${slideIndices[i3 + cellIndex] + 1}" /></div>` : `<div class="handout-cell"></div>`;
122937
+ const slideCell = img ? `<div class="handout-cell"><img src="${safeDataImageSrc(img)}" alt="Slide ${slideIndices[i3 + cellIndex] + 1}" /></div>` : `<div class="handout-cell"></div>`;
122221
122938
  return `<div class="handout-row-3">${slideCell}${buildNoteLines()}</div>`;
122222
122939
  }).join("");
122223
122940
  pages.push(`<section class="page"><div class="handout-grid-3">${rows}</div></section>`);
122224
122941
  } else {
122225
122942
  const cells = Array.from({ length: spp }, (_, cellIndex) => {
122226
122943
  const img = pageImgs[cellIndex];
122227
- return img ? `<div class="handout-cell"><img src="${img}" alt="Slide ${slideIndices[i3 + cellIndex] + 1}" /></div>` : `<div class="handout-cell"></div>`;
122944
+ return img ? `<div class="handout-cell"><img src="${safeDataImageSrc(img)}" alt="Slide ${slideIndices[i3 + cellIndex] + 1}" /></div>` : `<div class="handout-cell"></div>`;
122228
122945
  }).join("");
122229
122946
  pages.push(
122230
122947
  `<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>`