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
@@ -7,6 +7,7 @@ var clsx = require('clsx');
7
7
  var tailwindMerge = require('tailwind-merge');
8
8
  var lu = require('react-icons/lu');
9
9
  var pptxViewerCore = require('pptx-viewer-core');
10
+ var DOMPurify = require('dompurify');
10
11
  var reactI18next = require('react-i18next');
11
12
  var html2canvasPro = require('html2canvas-pro');
12
13
  var JSZip = require('jszip');
@@ -33,6 +34,7 @@ function _interopNamespace(e) {
33
34
 
34
35
  var React10__namespace = /*#__PURE__*/_interopNamespace(React10);
35
36
  var ReactDOM__namespace = /*#__PURE__*/_interopNamespace(ReactDOM);
37
+ var DOMPurify__default = /*#__PURE__*/_interopDefault(DOMPurify);
36
38
  var html2canvasPro__default = /*#__PURE__*/_interopDefault(html2canvasPro);
37
39
  var JSZip__default = /*#__PURE__*/_interopDefault(JSZip);
38
40
 
@@ -196,7 +198,7 @@ function generateUUID() {
196
198
  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];
197
199
  return uuid.toLowerCase();
198
200
  }
199
- function clamp(value, min2, max2) {
201
+ function clamp2(value, min2, max2) {
200
202
  return Math.max(min2, Math.min(max2, value));
201
203
  }
202
204
  function euclideanModulo(n, m2) {
@@ -571,7 +573,7 @@ function _generateTables() {
571
573
  }
572
574
  function toHalfFloat(val2) {
573
575
  if (Math.abs(val2) > 65504) warn("DataUtils.toHalfFloat(): Value out of range.");
574
- val2 = clamp(val2, -65504, 65504);
576
+ val2 = clamp2(val2, -65504, 65504);
575
577
  _tables.floatView[0] = val2;
576
578
  const f = _tables.uint32View[0];
577
579
  const e2 = f >> 23 & 511;
@@ -2060,7 +2062,7 @@ var init_three_core = __esm({
2060
2062
  * @param {number} max - The max value.
2061
2063
  * @return {number} The clamped value.
2062
2064
  */
2063
- clamp,
2065
+ clamp: clamp2,
2064
2066
  /**
2065
2067
  * Computes the Euclidean modulo of the given parameters that
2066
2068
  * is `( ( n % m ) + m ) % m`.
@@ -2587,8 +2589,8 @@ var init_three_core = __esm({
2587
2589
  * @return {Vector2} A reference to this vector.
2588
2590
  */
2589
2591
  clamp(min2, max2) {
2590
- this.x = clamp(this.x, min2.x, max2.x);
2591
- this.y = clamp(this.y, min2.y, max2.y);
2592
+ this.x = clamp2(this.x, min2.x, max2.x);
2593
+ this.y = clamp2(this.y, min2.y, max2.y);
2592
2594
  return this;
2593
2595
  }
2594
2596
  /**
@@ -2602,8 +2604,8 @@ var init_three_core = __esm({
2602
2604
  * @return {Vector2} A reference to this vector.
2603
2605
  */
2604
2606
  clampScalar(minVal, maxVal) {
2605
- this.x = clamp(this.x, minVal, maxVal);
2606
- this.y = clamp(this.y, minVal, maxVal);
2607
+ this.x = clamp2(this.x, minVal, maxVal);
2608
+ this.y = clamp2(this.y, minVal, maxVal);
2607
2609
  return this;
2608
2610
  }
2609
2611
  /**
@@ -2618,7 +2620,7 @@ var init_three_core = __esm({
2618
2620
  */
2619
2621
  clampLength(min2, max2) {
2620
2622
  const length2 = this.length();
2621
- return this.divideScalar(length2 || 1).multiplyScalar(clamp(length2, min2, max2));
2623
+ return this.divideScalar(length2 || 1).multiplyScalar(clamp2(length2, min2, max2));
2622
2624
  }
2623
2625
  /**
2624
2626
  * The components of this vector are rounded down to the nearest integer value.
@@ -2743,7 +2745,7 @@ var init_three_core = __esm({
2743
2745
  const denominator = Math.sqrt(this.lengthSq() * v.lengthSq());
2744
2746
  if (denominator === 0) return Math.PI / 2;
2745
2747
  const theta = this.dot(v) / denominator;
2746
- return Math.acos(clamp(theta, -1, 1));
2748
+ return Math.acos(clamp2(theta, -1, 1));
2747
2749
  }
2748
2750
  /**
2749
2751
  * Computes the distance from the given vector to this instance.
@@ -3230,7 +3232,7 @@ var init_three_core = __esm({
3230
3232
  * @return {number} The angle in radians.
3231
3233
  */
3232
3234
  angleTo(q) {
3233
- return 2 * Math.acos(Math.abs(clamp(this.dot(q), -1, 1)));
3235
+ return 2 * Math.acos(Math.abs(clamp2(this.dot(q), -1, 1)));
3234
3236
  }
3235
3237
  /**
3236
3238
  * Rotates this quaternion by a given angular step to the given quaternion.
@@ -3936,9 +3938,9 @@ var init_three_core = __esm({
3936
3938
  * @return {Vector3} A reference to this vector.
3937
3939
  */
3938
3940
  clamp(min2, max2) {
3939
- this.x = clamp(this.x, min2.x, max2.x);
3940
- this.y = clamp(this.y, min2.y, max2.y);
3941
- this.z = clamp(this.z, min2.z, max2.z);
3941
+ this.x = clamp2(this.x, min2.x, max2.x);
3942
+ this.y = clamp2(this.y, min2.y, max2.y);
3943
+ this.z = clamp2(this.z, min2.z, max2.z);
3942
3944
  return this;
3943
3945
  }
3944
3946
  /**
@@ -3952,9 +3954,9 @@ var init_three_core = __esm({
3952
3954
  * @return {Vector3} A reference to this vector.
3953
3955
  */
3954
3956
  clampScalar(minVal, maxVal) {
3955
- this.x = clamp(this.x, minVal, maxVal);
3956
- this.y = clamp(this.y, minVal, maxVal);
3957
- this.z = clamp(this.z, minVal, maxVal);
3957
+ this.x = clamp2(this.x, minVal, maxVal);
3958
+ this.y = clamp2(this.y, minVal, maxVal);
3959
+ this.z = clamp2(this.z, minVal, maxVal);
3958
3960
  return this;
3959
3961
  }
3960
3962
  /**
@@ -3969,7 +3971,7 @@ var init_three_core = __esm({
3969
3971
  */
3970
3972
  clampLength(min2, max2) {
3971
3973
  const length2 = this.length();
3972
- return this.divideScalar(length2 || 1).multiplyScalar(clamp(length2, min2, max2));
3974
+ return this.divideScalar(length2 || 1).multiplyScalar(clamp2(length2, min2, max2));
3973
3975
  }
3974
3976
  /**
3975
3977
  * The components of this vector are rounded down to the nearest integer value.
@@ -4179,7 +4181,7 @@ var init_three_core = __esm({
4179
4181
  const denominator = Math.sqrt(this.lengthSq() * v.lengthSq());
4180
4182
  if (denominator === 0) return Math.PI / 2;
4181
4183
  const theta = this.dot(v) / denominator;
4182
- return Math.acos(clamp(theta, -1, 1));
4184
+ return Math.acos(clamp2(theta, -1, 1));
4183
4185
  }
4184
4186
  /**
4185
4187
  * Computes the distance from the given vector to this instance.
@@ -5868,10 +5870,10 @@ var init_three_core = __esm({
5868
5870
  * @return {Vector4} A reference to this vector.
5869
5871
  */
5870
5872
  clamp(min2, max2) {
5871
- this.x = clamp(this.x, min2.x, max2.x);
5872
- this.y = clamp(this.y, min2.y, max2.y);
5873
- this.z = clamp(this.z, min2.z, max2.z);
5874
- this.w = clamp(this.w, min2.w, max2.w);
5873
+ this.x = clamp2(this.x, min2.x, max2.x);
5874
+ this.y = clamp2(this.y, min2.y, max2.y);
5875
+ this.z = clamp2(this.z, min2.z, max2.z);
5876
+ this.w = clamp2(this.w, min2.w, max2.w);
5875
5877
  return this;
5876
5878
  }
5877
5879
  /**
@@ -5885,10 +5887,10 @@ var init_three_core = __esm({
5885
5887
  * @return {Vector4} A reference to this vector.
5886
5888
  */
5887
5889
  clampScalar(minVal, maxVal) {
5888
- this.x = clamp(this.x, minVal, maxVal);
5889
- this.y = clamp(this.y, minVal, maxVal);
5890
- this.z = clamp(this.z, minVal, maxVal);
5891
- this.w = clamp(this.w, minVal, maxVal);
5890
+ this.x = clamp2(this.x, minVal, maxVal);
5891
+ this.y = clamp2(this.y, minVal, maxVal);
5892
+ this.z = clamp2(this.z, minVal, maxVal);
5893
+ this.w = clamp2(this.w, minVal, maxVal);
5892
5894
  return this;
5893
5895
  }
5894
5896
  /**
@@ -5903,7 +5905,7 @@ var init_three_core = __esm({
5903
5905
  */
5904
5906
  clampLength(min2, max2) {
5905
5907
  const length2 = this.length();
5906
- return this.divideScalar(length2 || 1).multiplyScalar(clamp(length2, min2, max2));
5908
+ return this.divideScalar(length2 || 1).multiplyScalar(clamp2(length2, min2, max2));
5907
5909
  }
5908
5910
  /**
5909
5911
  * The components of this vector are rounded down to the nearest integer value.
@@ -7686,7 +7688,7 @@ var init_three_core = __esm({
7686
7688
  const m31 = te[2], m32 = te[6], m33 = te[10];
7687
7689
  switch (order) {
7688
7690
  case "XYZ":
7689
- this._y = Math.asin(clamp(m13, -1, 1));
7691
+ this._y = Math.asin(clamp2(m13, -1, 1));
7690
7692
  if (Math.abs(m13) < 0.9999999) {
7691
7693
  this._x = Math.atan2(-m23, m33);
7692
7694
  this._z = Math.atan2(-m12, m11);
@@ -7696,7 +7698,7 @@ var init_three_core = __esm({
7696
7698
  }
7697
7699
  break;
7698
7700
  case "YXZ":
7699
- this._x = Math.asin(-clamp(m23, -1, 1));
7701
+ this._x = Math.asin(-clamp2(m23, -1, 1));
7700
7702
  if (Math.abs(m23) < 0.9999999) {
7701
7703
  this._y = Math.atan2(m13, m33);
7702
7704
  this._z = Math.atan2(m21, m22);
@@ -7706,7 +7708,7 @@ var init_three_core = __esm({
7706
7708
  }
7707
7709
  break;
7708
7710
  case "ZXY":
7709
- this._x = Math.asin(clamp(m32, -1, 1));
7711
+ this._x = Math.asin(clamp2(m32, -1, 1));
7710
7712
  if (Math.abs(m32) < 0.9999999) {
7711
7713
  this._y = Math.atan2(-m31, m33);
7712
7714
  this._z = Math.atan2(-m12, m22);
@@ -7716,7 +7718,7 @@ var init_three_core = __esm({
7716
7718
  }
7717
7719
  break;
7718
7720
  case "ZYX":
7719
- this._y = Math.asin(-clamp(m31, -1, 1));
7721
+ this._y = Math.asin(-clamp2(m31, -1, 1));
7720
7722
  if (Math.abs(m31) < 0.9999999) {
7721
7723
  this._x = Math.atan2(m32, m33);
7722
7724
  this._z = Math.atan2(m21, m11);
@@ -7726,7 +7728,7 @@ var init_three_core = __esm({
7726
7728
  }
7727
7729
  break;
7728
7730
  case "YZX":
7729
- this._z = Math.asin(clamp(m21, -1, 1));
7731
+ this._z = Math.asin(clamp2(m21, -1, 1));
7730
7732
  if (Math.abs(m21) < 0.9999999) {
7731
7733
  this._x = Math.atan2(-m23, m22);
7732
7734
  this._y = Math.atan2(-m31, m11);
@@ -7736,7 +7738,7 @@ var init_three_core = __esm({
7736
7738
  }
7737
7739
  break;
7738
7740
  case "XZY":
7739
- this._z = Math.asin(-clamp(m12, -1, 1));
7741
+ this._z = Math.asin(-clamp2(m12, -1, 1));
7740
7742
  if (Math.abs(m12) < 0.9999999) {
7741
7743
  this._x = Math.atan2(m32, m22);
7742
7744
  this._y = Math.atan2(m13, m11);
@@ -9360,8 +9362,8 @@ var init_three_core = __esm({
9360
9362
  */
9361
9363
  setHSL(h2, s, l2, colorSpace = ColorManagement.workingColorSpace) {
9362
9364
  h2 = euclideanModulo(h2, 1);
9363
- s = clamp(s, 0, 1);
9364
- l2 = clamp(l2, 0, 1);
9365
+ s = clamp2(s, 0, 1);
9366
+ l2 = clamp2(l2, 0, 1);
9365
9367
  if (s === 0) {
9366
9368
  this.r = this.g = this.b = l2;
9367
9369
  } else {
@@ -9547,7 +9549,7 @@ var init_three_core = __esm({
9547
9549
  */
9548
9550
  getHex(colorSpace = SRGBColorSpace) {
9549
9551
  ColorManagement.workingToColorSpace(_color.copy(this), colorSpace);
9550
- 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));
9552
+ 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));
9551
9553
  }
9552
9554
  /**
9553
9555
  * Returns the hexadecimal value of this color as a string (for example, 'FFFFFF').
@@ -18478,13 +18480,13 @@ var init_three_core = __esm({
18478
18480
  vec.crossVectors(tangents[i3 - 1], tangents[i3]);
18479
18481
  if (vec.length() > Number.EPSILON) {
18480
18482
  vec.normalize();
18481
- const theta = Math.acos(clamp(tangents[i3 - 1].dot(tangents[i3]), -1, 1));
18483
+ const theta = Math.acos(clamp2(tangents[i3 - 1].dot(tangents[i3]), -1, 1));
18482
18484
  normals[i3].applyMatrix4(mat.makeRotationAxis(vec, theta));
18483
18485
  }
18484
18486
  binormals[i3].crossVectors(tangents[i3], normals[i3]);
18485
18487
  }
18486
18488
  if (closed === true) {
18487
- let theta = Math.acos(clamp(normals[0].dot(normals[segments]), -1, 1));
18489
+ let theta = Math.acos(clamp2(normals[0].dot(normals[segments]), -1, 1));
18488
18490
  theta /= segments;
18489
18491
  if (tangents[0].dot(vec.crossVectors(normals[0], normals[segments])) > 0) {
18490
18492
  theta = -theta;
@@ -20273,7 +20275,7 @@ var init_three_core = __esm({
20273
20275
  phiLength
20274
20276
  };
20275
20277
  segments = Math.floor(segments);
20276
- phiLength = clamp(phiLength, 0, Math.PI * 2);
20278
+ phiLength = clamp2(phiLength, 0, Math.PI * 2);
20277
20279
  const indices = [];
20278
20280
  const vertices = [];
20279
20281
  const uvs = [];
@@ -21489,7 +21491,7 @@ var init_three_core = __esm({
21489
21491
  this.ior = 1.5;
21490
21492
  Object.defineProperty(this, "reflectivity", {
21491
21493
  get: function() {
21492
- return clamp(2.5 * (this.ior - 1) / (this.ior + 1), 0, 1);
21494
+ return clamp2(2.5 * (this.ior - 1) / (this.ior + 1), 0, 1);
21493
21495
  },
21494
21496
  set: function(reflectivity) {
21495
21497
  this.ior = (1 + 0.4 * reflectivity) / (1 - 0.4 * reflectivity);
@@ -29757,7 +29759,7 @@ var init_three_core = __esm({
29757
29759
  */
29758
29760
  makeSafe() {
29759
29761
  const EPS = 1e-6;
29760
- this.phi = clamp(this.phi, EPS, Math.PI - EPS);
29762
+ this.phi = clamp2(this.phi, EPS, Math.PI - EPS);
29761
29763
  return this;
29762
29764
  }
29763
29765
  /**
@@ -29785,7 +29787,7 @@ var init_three_core = __esm({
29785
29787
  this.phi = 0;
29786
29788
  } else {
29787
29789
  this.theta = Math.atan2(x2, z);
29788
- this.phi = Math.acos(clamp(y / this.radius, -1, 1));
29790
+ this.phi = Math.acos(clamp2(y / this.radius, -1, 1));
29789
29791
  }
29790
29792
  return this;
29791
29793
  }
@@ -30300,7 +30302,7 @@ var init_three_core = __esm({
30300
30302
  const startEnd_startP = _startEnd.dot(_startP);
30301
30303
  let t2 = startEnd_startP / startEnd2;
30302
30304
  if (clampToLine) {
30303
- t2 = clamp(t2, 0, 1);
30305
+ t2 = clamp2(t2, 0, 1);
30304
30306
  }
30305
30307
  return t2;
30306
30308
  }
@@ -30346,27 +30348,27 @@ var init_three_core = __esm({
30346
30348
  if (a2 <= EPSILON) {
30347
30349
  s = 0;
30348
30350
  t2 = f / e2;
30349
- t2 = clamp(t2, 0, 1);
30351
+ t2 = clamp2(t2, 0, 1);
30350
30352
  } else {
30351
30353
  const c3 = _d1.dot(_r);
30352
30354
  if (e2 <= EPSILON) {
30353
30355
  t2 = 0;
30354
- s = clamp(-c3 / a2, 0, 1);
30356
+ s = clamp2(-c3 / a2, 0, 1);
30355
30357
  } else {
30356
30358
  const b2 = _d1.dot(_d2);
30357
30359
  const denom = a2 * e2 - b2 * b2;
30358
30360
  if (denom !== 0) {
30359
- s = clamp((b2 * f - c3 * e2) / denom, 0, 1);
30361
+ s = clamp2((b2 * f - c3 * e2) / denom, 0, 1);
30360
30362
  } else {
30361
30363
  s = 0;
30362
30364
  }
30363
30365
  t2 = (b2 * s + f) / e2;
30364
30366
  if (t2 < 0) {
30365
30367
  t2 = 0;
30366
- s = clamp(-c3 / a2, 0, 1);
30368
+ s = clamp2(-c3 / a2, 0, 1);
30367
30369
  } else if (t2 > 1) {
30368
30370
  t2 = 1;
30369
- s = clamp((b2 - c3) / a2, 0, 1);
30371
+ s = clamp2((b2 - c3) / a2, 0, 1);
30370
30372
  }
30371
30373
  }
30372
30374
  }
@@ -43596,7 +43598,7 @@ var require_use_sync_external_store_shim_development = __commonJS({
43596
43598
  return x2 === y && (0 !== x2 || 1 / x2 === 1 / y) || x2 !== x2 && y !== y;
43597
43599
  }
43598
43600
  function useSyncExternalStore$2(subscribe3, getSnapshot2) {
43599
- didWarnOld18Alpha || void 0 === React97.startTransition || (didWarnOld18Alpha = true, console.error(
43601
+ didWarnOld18Alpha || void 0 === React100.startTransition || (didWarnOld18Alpha = true, console.error(
43600
43602
  "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."
43601
43603
  ));
43602
43604
  var value = getSnapshot2();
@@ -43606,7 +43608,7 @@ var require_use_sync_external_store_shim_development = __commonJS({
43606
43608
  "The result of getSnapshot should be cached to avoid an infinite loop"
43607
43609
  ), didWarnUncachedGetSnapshot = true);
43608
43610
  }
43609
- cachedValue = useState85({
43611
+ cachedValue = useState86({
43610
43612
  inst: { value, getSnapshot: getSnapshot2 }
43611
43613
  });
43612
43614
  var inst = cachedValue[0].inst, forceUpdate = cachedValue[1];
@@ -43644,8 +43646,8 @@ var require_use_sync_external_store_shim_development = __commonJS({
43644
43646
  return getSnapshot2();
43645
43647
  }
43646
43648
  "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
43647
- 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;
43648
- exports$1.useSyncExternalStore = void 0 !== React97.useSyncExternalStore ? React97.useSyncExternalStore : shim;
43649
+ 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;
43650
+ exports$1.useSyncExternalStore = void 0 !== React100.useSyncExternalStore ? React100.useSyncExternalStore : shim;
43649
43651
  "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());
43650
43652
  })();
43651
43653
  }
@@ -43668,7 +43670,7 @@ var require_with_selector_development = __commonJS({
43668
43670
  return x2 === y && (0 !== x2 || 1 / x2 === 1 / y) || x2 !== x2 && y !== y;
43669
43671
  }
43670
43672
  "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
43671
- 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;
43673
+ 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;
43672
43674
  exports$1.useSyncExternalStoreWithSelector = function(subscribe3, getSnapshot2, getServerSnapshot2, selector, isEqual) {
43673
43675
  var instRef = useRef73(null);
43674
43676
  if (null === instRef.current) {
@@ -70305,6 +70307,55 @@ var ACTION_BUTTON_PRESETS = [
70305
70307
  label: "Return",
70306
70308
  defaultAction: "prevSlide",
70307
70309
  iconPath: "M18 8 L18 14 L6 14 M6 14 L10 10 M6 14 L10 18"
70310
+ },
70311
+ {
70312
+ shapeType: "actionButtonHome",
70313
+ label: "Home",
70314
+ defaultAction: "firstSlide",
70315
+ // House: roof + body
70316
+ iconPath: "M12 4 L20 11 L20 20 L14 20 L14 14 L10 14 L10 20 L4 20 L4 11 Z"
70317
+ },
70318
+ {
70319
+ shapeType: "actionButtonHelp",
70320
+ label: "Help",
70321
+ defaultAction: "none",
70322
+ // Question mark
70323
+ iconPath: "M9 9 a3 3 0 1 1 4 2.8 c-1 0.4 -1 1.2 -1 2 M12 17 v0.5"
70324
+ },
70325
+ {
70326
+ shapeType: "actionButtonInformation",
70327
+ label: "Information",
70328
+ defaultAction: "none",
70329
+ // Lower-case "i": dot + body
70330
+ iconPath: "M12 6 v0.01 M12 10 v8"
70331
+ },
70332
+ {
70333
+ shapeType: "actionButtonDocument",
70334
+ label: "Document",
70335
+ defaultAction: "none",
70336
+ // Document with folded corner
70337
+ iconPath: "M6 4 L14 4 L18 8 L18 20 L6 20 Z M14 4 L14 8 L18 8"
70338
+ },
70339
+ {
70340
+ shapeType: "actionButtonSound",
70341
+ label: "Sound",
70342
+ defaultAction: "none",
70343
+ // Speaker cone + sound waves
70344
+ 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"
70345
+ },
70346
+ {
70347
+ shapeType: "actionButtonMovie",
70348
+ label: "Movie",
70349
+ defaultAction: "none",
70350
+ // Film strip with play triangle
70351
+ iconPath: "M4 6 L20 6 L20 18 L4 18 Z M10 9 L15 12 L10 15 Z"
70352
+ },
70353
+ {
70354
+ shapeType: "actionButtonBlank",
70355
+ label: "Custom",
70356
+ defaultAction: "none",
70357
+ // No glyph — empty path. The button still renders as a rounded rect via clip-path.
70358
+ iconPath: ""
70308
70359
  }
70309
70360
  ];
70310
70361
  Object.fromEntries(ACTION_BUTTON_PRESETS.map((p3) => [p3.shapeType, p3.defaultAction]));
@@ -72584,8 +72635,8 @@ function hexToRgb2(hex) {
72584
72635
  };
72585
72636
  }
72586
72637
  function rgbToHex(r2, g2, b2) {
72587
- const clamp2 = (v) => Math.max(0, Math.min(255, Math.round(v)));
72588
- return `#${clamp2(r2).toString(16).padStart(2, "0")}${clamp2(g2).toString(16).padStart(2, "0")}${clamp2(b2).toString(16).padStart(2, "0")}`;
72638
+ const clamp3 = (v) => Math.max(0, Math.min(255, Math.round(v)));
72639
+ return `#${clamp3(r2).toString(16).padStart(2, "0")}${clamp3(g2).toString(16).padStart(2, "0")}${clamp3(b2).toString(16).padStart(2, "0")}`;
72589
72640
  }
72590
72641
  function rgbToHsl(r2, g2, b2) {
72591
72642
  const rn = r2 / 255;
@@ -74089,6 +74140,13 @@ function hasDistinctScriptFonts(fonts) {
74089
74140
  }
74090
74141
  return Boolean(fonts.eastAsia) && fonts.eastAsia !== base || Boolean(fonts.complexScript) && fonts.complexScript !== base || Boolean(fonts.symbol) && fonts.symbol !== base;
74091
74142
  }
74143
+ function sanitizeMathMl(markup) {
74144
+ const purify = DOMPurify__default.default;
74145
+ if (typeof purify.sanitize !== "function") {
74146
+ return markup;
74147
+ }
74148
+ return purify.sanitize(markup, { USE_PROFILES: { mathMl: true, svg: true } });
74149
+ }
74092
74150
  function renderScriptAwareText(text2, needsScriptFonts, scriptFonts, baseFontFamily, keyPrefix) {
74093
74151
  if (!needsScriptFonts || !text2) {
74094
74152
  return text2;
@@ -74179,14 +74237,15 @@ function renderSegmentContent(elementId, segmentIndex, textValue, lines, needsSc
74179
74237
  }
74180
74238
  function renderEquationSegment(elementId, segmentIndex, equationXml, equationNumber) {
74181
74239
  const mathml = convertOmmlToMathMl(equationXml);
74182
- const equationContent = mathml ? /* @__PURE__ */ jsxRuntime.jsx(
74240
+ const safeMathml = mathml ? sanitizeMathMl(mathml) : "";
74241
+ const equationContent = safeMathml ? /* @__PURE__ */ jsxRuntime.jsx(
74183
74242
  "span",
74184
74243
  {
74185
74244
  className: "inline-block align-middle",
74186
74245
  style: {
74187
74246
  fontFamily: '"Cambria Math", "STIX Two Math", serif'
74188
74247
  },
74189
- dangerouslySetInnerHTML: { __html: mathml }
74248
+ dangerouslySetInnerHTML: { __html: safeMathml }
74190
74249
  }
74191
74250
  ) : /* @__PURE__ */ jsxRuntime.jsx("span", { className: "inline-block px-1 py-0.5 rounded text-xs bg-gray-200/20 text-gray-400 italic", children: "Equation" });
74192
74251
  if (equationNumber) {
@@ -74787,6 +74846,11 @@ function getShapeClipPath(shapeType, adjustments, width, height) {
74787
74846
  }
74788
74847
  const normalized = shapeType.toLowerCase();
74789
74848
  if (normalized === "round1rect" || normalized === "round2samerect" || normalized === "round2diagrect" || normalized === "sniproundrect" || normalized === "snip1rect" || normalized === "snip2diagrect") {
74849
+ if (adjustments?.adj !== void 0 && width && height) {
74850
+ const ratio = Math.min(Math.max(adjustments.adj / 1e5, 0), 0.5);
74851
+ const radiusPx = Math.round(Math.min(width, height) * ratio);
74852
+ return `inset(0 round ${radiusPx}px)`;
74853
+ }
74790
74854
  return "inset(0 round 18px)";
74791
74855
  }
74792
74856
  if (normalized === "can" || normalized === "cylinder") {
@@ -75433,6 +75497,128 @@ function mapDagBlendModeToCss(blend) {
75433
75497
  return void 0;
75434
75498
  }
75435
75499
  }
75500
+ function getImageAlphaFilterId(elementId) {
75501
+ return `imgalpha-${elementId}`;
75502
+ }
75503
+ function hasAdvancedImageAlphaEffects(element2) {
75504
+ if (!pptxViewerCore.isImageLikeElement(element2)) {
75505
+ return false;
75506
+ }
75507
+ const e2 = element2.imageEffects;
75508
+ if (!e2) {
75509
+ return false;
75510
+ }
75511
+ return Boolean(
75512
+ 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
75513
+ );
75514
+ }
75515
+ function renderImageAlphaSvgFilter(element2) {
75516
+ if (!pptxViewerCore.isImageLikeElement(element2)) {
75517
+ return null;
75518
+ }
75519
+ const e2 = element2.imageEffects;
75520
+ if (!e2 || !hasAdvancedImageAlphaEffects(element2)) {
75521
+ return null;
75522
+ }
75523
+ const filterId = getImageAlphaFilterId(element2.id);
75524
+ const primitives = [];
75525
+ let resultIdx = 0;
75526
+ let inputRef = "SourceGraphic";
75527
+ const next = (jsx229) => {
75528
+ const result = `r${resultIdx++}`;
75529
+ primitives.push(jsx229(inputRef, result));
75530
+ inputRef = result;
75531
+ };
75532
+ if (typeof e2.alphaModFix === "number") {
75533
+ const mul = clamp(e2.alphaModFix / 100, 0, 1);
75534
+ next((inp, out) => /* @__PURE__ */ jsxRuntime.jsx(
75535
+ "feColorMatrix",
75536
+ {
75537
+ in: inp,
75538
+ result: out,
75539
+ type: "matrix",
75540
+ values: `1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 ${mul} 0`
75541
+ },
75542
+ out
75543
+ ));
75544
+ }
75545
+ if (e2.alphaInv) {
75546
+ next((inp, out) => /* @__PURE__ */ jsxRuntime.jsx("feComponentTransfer", { in: inp, result: out, children: /* @__PURE__ */ jsxRuntime.jsx("feFuncA", { type: "linear", slope: -1, intercept: 1 }) }, out));
75547
+ }
75548
+ if (e2.alphaCeiling) {
75549
+ next((inp, out) => /* @__PURE__ */ jsxRuntime.jsx("feComponentTransfer", { in: inp, result: out, children: /* @__PURE__ */ jsxRuntime.jsx("feFuncA", { type: "discrete", tableValues: "0 1 1 1 1 1 1 1 1 1" }) }, out));
75550
+ }
75551
+ if (e2.alphaFloor) {
75552
+ next((inp, out) => /* @__PURE__ */ jsxRuntime.jsx("feComponentTransfer", { in: inp, result: out, children: /* @__PURE__ */ jsxRuntime.jsx("feFuncA", { type: "discrete", tableValues: "0 0 0 0 0 0 0 0 0 1" }) }, out));
75553
+ }
75554
+ if (typeof e2.alphaRepl === "number") {
75555
+ const a2 = clamp(e2.alphaRepl / 100, 0, 1);
75556
+ next((inp, out) => /* @__PURE__ */ jsxRuntime.jsx("feComponentTransfer", { in: inp, result: out, children: /* @__PURE__ */ jsxRuntime.jsx("feFuncA", { type: "linear", slope: 0, intercept: a2 }) }, out));
75557
+ }
75558
+ if (typeof e2.alphaBiLevel === "number") {
75559
+ const t2 = clamp(e2.alphaBiLevel / 100, 0, 1);
75560
+ const table = Array.from({ length: 10 }, (_, i3) => i3 / 10 >= t2 ? "1" : "0").join(" ");
75561
+ next((inp, out) => /* @__PURE__ */ jsxRuntime.jsx("feComponentTransfer", { in: inp, result: out, children: /* @__PURE__ */ jsxRuntime.jsx("feFuncA", { type: "discrete", tableValues: table }) }, out));
75562
+ }
75563
+ if (typeof e2.biLevel === "number") {
75564
+ next((inp, out) => /* @__PURE__ */ jsxRuntime.jsx("feColorMatrix", { in: inp, result: out, type: "saturate", values: "0" }, out));
75565
+ const t2 = clamp(e2.biLevel / 100, 0, 1);
75566
+ const tbl = t2 < 0.5 ? "0 1" : "0 1";
75567
+ next((inp, out) => /* @__PURE__ */ jsxRuntime.jsxs("feComponentTransfer", { in: inp, result: out, children: [
75568
+ /* @__PURE__ */ jsxRuntime.jsx("feFuncR", { type: "discrete", tableValues: tbl }),
75569
+ /* @__PURE__ */ jsxRuntime.jsx("feFuncG", { type: "discrete", tableValues: tbl }),
75570
+ /* @__PURE__ */ jsxRuntime.jsx("feFuncB", { type: "discrete", tableValues: tbl })
75571
+ ] }, out));
75572
+ }
75573
+ if (e2.lum && (typeof e2.lum.bright === "number" || typeof e2.lum.contrast === "number")) {
75574
+ const b2 = (e2.lum.bright ?? 0) / 100;
75575
+ const c2 = 1 + (e2.lum.contrast ?? 0) / 100;
75576
+ const slope = c2;
75577
+ const intercept = b2 + (1 - c2) / 2;
75578
+ next((inp, out) => /* @__PURE__ */ jsxRuntime.jsxs("feComponentTransfer", { in: inp, result: out, children: [
75579
+ /* @__PURE__ */ jsxRuntime.jsx("feFuncR", { type: "linear", slope, intercept }),
75580
+ /* @__PURE__ */ jsxRuntime.jsx("feFuncG", { type: "linear", slope, intercept }),
75581
+ /* @__PURE__ */ jsxRuntime.jsx("feFuncB", { type: "linear", slope, intercept })
75582
+ ] }, out));
75583
+ }
75584
+ if (e2.hsl && typeof e2.hsl.sat === "number") {
75585
+ const v = clamp(1 + e2.hsl.sat / 100, 0, 2);
75586
+ next((inp, out) => /* @__PURE__ */ jsxRuntime.jsx("feColorMatrix", { in: inp, result: out, type: "saturate", values: String(v) }, out));
75587
+ }
75588
+ if (e2.tint && typeof e2.tint.amt === "number" && e2.tint.amt < 0) {
75589
+ const v = clamp(1 + e2.tint.amt / 100, 0, 1);
75590
+ next((inp, out) => /* @__PURE__ */ jsxRuntime.jsx("feColorMatrix", { in: inp, result: out, type: "saturate", values: String(v) }, out));
75591
+ }
75592
+ if (e2.clrRepl) {
75593
+ const c2 = hexToRgbUnit(e2.clrRepl.color);
75594
+ next((inp, out) => /* @__PURE__ */ jsxRuntime.jsx(
75595
+ "feColorMatrix",
75596
+ {
75597
+ in: inp,
75598
+ result: out,
75599
+ type: "matrix",
75600
+ values: `0 0 0 0 ${c2.r} 0 0 0 0 ${c2.g} 0 0 0 0 ${c2.b} 0 0 0 1 0`
75601
+ },
75602
+ out
75603
+ ));
75604
+ }
75605
+ if (primitives.length === 0) {
75606
+ return null;
75607
+ }
75608
+ return /* @__PURE__ */ jsxRuntime.jsx(
75609
+ "svg",
75610
+ {
75611
+ width: 0,
75612
+ height: 0,
75613
+ style: { position: "absolute", overflow: "hidden" },
75614
+ "aria-hidden": "true",
75615
+ children: /* @__PURE__ */ jsxRuntime.jsx("defs", { children: /* @__PURE__ */ jsxRuntime.jsx("filter", { id: filterId, colorInterpolationFilters: "sRGB", children: primitives }) })
75616
+ }
75617
+ );
75618
+ }
75619
+ function clamp(v, lo, hi) {
75620
+ return v < lo ? lo : v > hi ? hi : v;
75621
+ }
75436
75622
  function getDagDuotoneFilterId(elementId) {
75437
75623
  return `dag-duotone-${elementId}`;
75438
75624
  }
@@ -75516,6 +75702,9 @@ function getImageEffectsFilter(element2, options) {
75516
75702
  const filterId = getDuotoneFilterId(element2.id);
75517
75703
  filters.push(`url(#${filterId})`);
75518
75704
  }
75705
+ if (hasAdvancedImageAlphaEffects(element2)) {
75706
+ filters.push(`url(#${getImageAlphaFilterId(element2.id)})`);
75707
+ }
75519
75708
  if (effects.artisticEffect) {
75520
75709
  const radius = effects.artisticRadius ?? 5;
75521
75710
  if (needsSvgArtisticFilter(effects.artisticEffect)) {
@@ -75689,6 +75878,39 @@ function getEffectDagCssFilter(style, elementId) {
75689
75878
  return filters.length > 0 ? filters.join(" ") : void 0;
75690
75879
  }
75691
75880
  var getEffectDagFilter = getEffectDagCssFilter;
75881
+ function getResolvedShapeClipPathFor(shapeType, width, height, adjustments) {
75882
+ if (!shapeType) {
75883
+ return void 0;
75884
+ }
75885
+ if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
75886
+ return getShapeClipPath(shapeType, adjustments, width, height);
75887
+ }
75888
+ if (adjustments && Object.keys(adjustments).length > 0) {
75889
+ const adjusted = pptxViewerCore.getAdjustmentAwareShapeClipPath(shapeType, width, height, adjustments);
75890
+ if (adjusted !== void 0) {
75891
+ return adjusted;
75892
+ }
75893
+ }
75894
+ const fromPreset = pptxViewerCore.getShapeClipPathFromPreset(shapeType, width, height, adjustments);
75895
+ if (fromPreset !== void 0) {
75896
+ return fromPreset;
75897
+ }
75898
+ const cloud = pptxViewerCore.getCloudPathForRendering(shapeType, width, height);
75899
+ if (cloud !== void 0) {
75900
+ return cloud;
75901
+ }
75902
+ return getShapeClipPath(shapeType, adjustments, width, height);
75903
+ }
75904
+ function getResolvedShapeClipPath(element2, width, height) {
75905
+ const shapeType = element2.shapeType;
75906
+ if (!shapeType) {
75907
+ return void 0;
75908
+ }
75909
+ const w = element2.width;
75910
+ const h2 = element2.height;
75911
+ const adjustments = element2.shapeAdjustments;
75912
+ return getResolvedShapeClipPathFor(shapeType, w, h2, adjustments);
75913
+ }
75692
75914
 
75693
75915
  // src/viewer/utils/shape-round-rect.ts
75694
75916
  function localClampAdjustment(value) {
@@ -75760,6 +75982,27 @@ var MATERIAL_MAP = {
75760
75982
  filter: "brightness(1.1) contrast(0.85)",
75761
75983
  // Translucent powder: slight translucent glow
75762
75984
  backgroundImage: "radial-gradient(ellipse at 30% 30%, rgba(255,255,255,0.1) 0%, transparent 60%)"
75985
+ },
75986
+ // Legacy materials (PowerPoint 2007 / earlier). Render as muted variants
75987
+ // of the modern equivalents so legacy decks still resemble the originals.
75988
+ legacyMatte: {
75989
+ filter: "brightness(0.92) saturate(0.85)",
75990
+ backgroundImage: "linear-gradient(180deg, rgba(255,255,255,0.03) 0%, transparent 50%, rgba(0,0,0,0.04) 100%)"
75991
+ },
75992
+ legacyPlastic: {
75993
+ filter: "brightness(1.02) contrast(1.03)",
75994
+ boxShadow: "inset -2px -2px 5px rgba(255,255,255,0.3)",
75995
+ backgroundImage: "radial-gradient(ellipse 35% 25% at 25% 20%, rgba(255,255,255,0.15) 0%, transparent 70%)"
75996
+ },
75997
+ legacyMetal: {
75998
+ filter: "brightness(1.05) contrast(1.1) saturate(1.1)",
75999
+ boxShadow: "inset -2px -2px 6px rgba(255,255,255,0.35), inset 1px 1px 3px rgba(255,255,255,0.15)",
76000
+ 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%)"
76001
+ },
76002
+ legacyWireframe: {
76003
+ filter: "brightness(1) contrast(1.4) saturate(0.6)",
76004
+ // Wireframe: high contrast outline-emphasising look
76005
+ boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.4)"
75763
76006
  }
75764
76007
  };
75765
76008
  function getMaterialCssOverrides(material) {
@@ -76714,8 +76957,7 @@ function getShapeVisualStyle(element2, hasFill, fillColor, strokeWidth, strokeCo
76714
76957
  return {};
76715
76958
  }
76716
76959
  const normalizedShapeType = getShapeType(element2.shapeType);
76717
- const shapeType = element2.shapeType || normalizedShapeType;
76718
- const clipPath = getShapeClipPath(shapeType);
76960
+ const clipPath = getResolvedShapeClipPath(element2);
76719
76961
  const fillOpacity = element2.shapeStyle?.fillOpacity;
76720
76962
  const strokeOpacity = element2.shapeStyle?.strokeOpacity;
76721
76963
  const strokeDash = normalizeStrokeDashType(element2.shapeStyle?.strokeDash);
@@ -77317,7 +77559,7 @@ function getImageMaskStyle(element2) {
77317
77559
  if (normalized === "can" || normalized === "cylinder") {
77318
77560
  return { borderRadius: "48% / 12%" };
77319
77561
  }
77320
- const clipPath = getShapeClipPath(shapeType);
77562
+ const clipPath = getResolvedShapeClipPath(element2);
77321
77563
  if (!clipPath) {
77322
77564
  return void 0;
77323
77565
  }
@@ -77980,8 +78222,8 @@ function hexToRgb3(hex) {
77980
78222
  };
77981
78223
  }
77982
78224
  function rgbToHex2(r2, g2, b2) {
77983
- const clamp2 = (v) => Math.max(0, Math.min(255, Math.round(v)));
77984
- 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()}`;
78225
+ const clamp3 = (v) => Math.max(0, Math.min(255, Math.round(v)));
78226
+ 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()}`;
77985
78227
  }
77986
78228
  function tintColor(hex, tintFactor) {
77987
78229
  const { r: r2, g: g2, b: b2 } = hexToRgb3(hex);
@@ -78912,13 +79154,13 @@ function cellStyleToCss(style) {
78912
79154
  }
78913
79155
  if (style.textDirection) {
78914
79156
  switch (style.textDirection) {
78915
- case "vertical":
79157
+ case "vert":
78916
79158
  case "eaVert":
78917
79159
  case "wordArtVert":
78918
79160
  case "wordArtVertRtl":
78919
79161
  css.writingMode = "vertical-rl";
78920
79162
  break;
78921
- case "vertical270":
79163
+ case "vert270":
78922
79164
  case "mongolianVert":
78923
79165
  css.writingMode = "vertical-lr";
78924
79166
  break;
@@ -79445,8 +79687,8 @@ function hexToRgb4(hex) {
79445
79687
  ];
79446
79688
  }
79447
79689
  function rgbToHex3(r2, g2, b2) {
79448
- const clamp2 = (v) => Math.max(0, Math.min(255, Math.round(v)));
79449
- return `#${clamp2(r2).toString(16).padStart(2, "0")}${clamp2(g2).toString(16).padStart(2, "0")}${clamp2(b2).toString(16).padStart(2, "0")}`;
79690
+ const clamp3 = (v) => Math.max(0, Math.min(255, Math.round(v)));
79691
+ return `#${clamp3(r2).toString(16).padStart(2, "0")}${clamp3(g2).toString(16).padStart(2, "0")}${clamp3(b2).toString(16).padStart(2, "0")}`;
79450
79692
  }
79451
79693
  function tint(hex, amount) {
79452
79694
  const [r2, g2, b2] = hexToRgb4(hex);
@@ -81364,7 +81606,7 @@ function resolveRegionCode(label) {
81364
81606
  return REGION_ALIAS_MAP[normalized];
81365
81607
  }
81366
81608
  function lerpColor(a2, b2, t2) {
81367
- const clamp2 = (v) => Math.max(0, Math.min(255, Math.round(v)));
81609
+ const clamp3 = (v) => Math.max(0, Math.min(255, Math.round(v)));
81368
81610
  const parse = (hex) => {
81369
81611
  const h2 = hex.replace("#", "");
81370
81612
  return [
@@ -81375,9 +81617,9 @@ function lerpColor(a2, b2, t2) {
81375
81617
  };
81376
81618
  const [r1, g1, b1] = parse(a2);
81377
81619
  const [r2, g2, b22] = parse(b2);
81378
- const r3 = clamp2(r1 + (r2 - r1) * t2);
81379
- const g3 = clamp2(g1 + (g2 - g1) * t2);
81380
- const bl = clamp2(b1 + (b22 - b1) * t2);
81620
+ const r3 = clamp3(r1 + (r2 - r1) * t2);
81621
+ const g3 = clamp3(g1 + (g2 - g1) * t2);
81622
+ const bl = clamp3(b1 + (b22 - b1) * t2);
81381
81623
  return `#${r3.toString(16).padStart(2, "0")}${g3.toString(16).padStart(2, "0")}${bl.toString(16).padStart(2, "0")}`;
81382
81624
  }
81383
81625
  function sequentialColorScale(t2) {
@@ -86543,7 +86785,7 @@ function ResizeHandle({
86543
86785
  }
86544
86786
  );
86545
86787
  }
86546
- function SlideThumbnail({
86788
+ function SlideThumbnailImpl({
86547
86789
  slide,
86548
86790
  templateElements,
86549
86791
  canvasSize
@@ -86781,6 +87023,37 @@ function ThumbnailTable({
86781
87023
  }
86782
87024
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "w-full h-full flex items-center justify-center text-[10px] text-muted-foreground pointer-events-none", children: "Table" });
86783
87025
  }
87026
+ function arePropsEqual(prev, next) {
87027
+ if (prev.slide.id !== next.slide.id) {
87028
+ return false;
87029
+ }
87030
+ if (prev.slide.isDirty !== next.slide.isDirty) {
87031
+ return false;
87032
+ }
87033
+ if (prev.slide.hidden !== next.slide.hidden) {
87034
+ return false;
87035
+ }
87036
+ if (prev.slide.elements !== next.slide.elements) {
87037
+ return false;
87038
+ }
87039
+ if (prev.slide.backgroundColor !== next.slide.backgroundColor) {
87040
+ return false;
87041
+ }
87042
+ if (prev.slide.backgroundImage !== next.slide.backgroundImage) {
87043
+ return false;
87044
+ }
87045
+ if (prev.slide.backgroundGradient !== next.slide.backgroundGradient) {
87046
+ return false;
87047
+ }
87048
+ if (prev.templateElements !== next.templateElements) {
87049
+ return false;
87050
+ }
87051
+ if (prev.canvasSize.width !== next.canvasSize.width || prev.canvasSize.height !== next.canvasSize.height) {
87052
+ return false;
87053
+ }
87054
+ return true;
87055
+ }
87056
+ var SlideThumbnail = React10__namespace.default.memo(SlideThumbnailImpl, arePropsEqual);
86784
87057
  function ContextMenu({
86785
87058
  contextMenuState,
86786
87059
  mode,
@@ -88098,6 +88371,59 @@ function WarpedText({
88098
88371
  }
88099
88372
  );
88100
88373
  }
88374
+ var GLYPH_BY_SHAPE = Object.fromEntries(
88375
+ ACTION_BUTTON_PRESETS.map((p3) => [p3.shapeType, p3.iconPath])
88376
+ );
88377
+ GLYPH_BY_SHAPE["actionButtonForwardOrNext"] = GLYPH_BY_SHAPE["actionButtonForwardNext"];
88378
+ GLYPH_BY_SHAPE["actionButtonBackOrPrevious"] = GLYPH_BY_SHAPE["actionButtonBackPrevious"];
88379
+ function isActionButtonShape(shapeType) {
88380
+ return Boolean(shapeType && shapeType in GLYPH_BY_SHAPE);
88381
+ }
88382
+ function getActionButtonGlyphPath(shapeType) {
88383
+ if (!shapeType) {
88384
+ return void 0;
88385
+ }
88386
+ const path = GLYPH_BY_SHAPE[shapeType];
88387
+ return path && path.length > 0 ? path : void 0;
88388
+ }
88389
+ function ActionButtonGlyphOverlay({
88390
+ element: element2,
88391
+ color
88392
+ }) {
88393
+ const shapeType = "shapeType" in element2 ? element2.shapeType : void 0;
88394
+ const path = getActionButtonGlyphPath(shapeType);
88395
+ if (!path) {
88396
+ return null;
88397
+ }
88398
+ const stroke = color ?? ("textStyle" in element2 && element2.textStyle?.color || "#ffffff");
88399
+ return /* @__PURE__ */ jsxRuntime.jsx(
88400
+ "svg",
88401
+ {
88402
+ viewBox: "0 0 24 24",
88403
+ width: "100%",
88404
+ height: "100%",
88405
+ preserveAspectRatio: "xMidYMid meet",
88406
+ style: {
88407
+ position: "absolute",
88408
+ inset: 0,
88409
+ pointerEvents: "none",
88410
+ padding: "20%"
88411
+ },
88412
+ "aria-hidden": "true",
88413
+ children: /* @__PURE__ */ jsxRuntime.jsx(
88414
+ "path",
88415
+ {
88416
+ d: path,
88417
+ fill: "none",
88418
+ stroke,
88419
+ strokeWidth: 2,
88420
+ strokeLinecap: "round",
88421
+ strokeLinejoin: "round"
88422
+ }
88423
+ )
88424
+ }
88425
+ );
88426
+ }
88101
88427
  function ColorChangedImage({
88102
88428
  src,
88103
88429
  clrChange,
@@ -88368,6 +88694,7 @@ function renderImg(el, style, filter, alt, opacity) {
88368
88694
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
88369
88695
  tileDuotoneColors && renderDuotoneSvgFilter(el.id, tileDuotoneColors.color1, tileDuotoneColors.color2),
88370
88696
  renderArtisticEffectSvgFilter(el.id, artisticEffectName, artisticRadius),
88697
+ renderImageAlphaSvgFilter(el),
88371
88698
  /* @__PURE__ */ jsxRuntime.jsx(
88372
88699
  "div",
88373
88700
  {
@@ -88389,6 +88716,7 @@ function renderImg(el, style, filter, alt, opacity) {
88389
88716
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
88390
88717
  duotoneColors && !useDuotoneCanvas && renderDuotoneSvgFilter(el.id, duotoneColors.color1, duotoneColors.color2),
88391
88718
  renderArtisticEffectSvgFilter(el.id, artisticEffectName, artisticRadius),
88719
+ renderImageAlphaSvgFilter(el),
88392
88720
  useDuotoneCanvas && duotoneColors ? /* @__PURE__ */ jsxRuntime.jsx(
88393
88721
  DuotoneImage,
88394
88722
  {
@@ -90514,7 +90842,7 @@ function BendingProcessRenderer({
90514
90842
  }
90515
90843
  );
90516
90844
  }
90517
- function SmartArtRenderer({
90845
+ function SmartArtRendererImpl({
90518
90846
  element: element2,
90519
90847
  className = ""
90520
90848
  }) {
@@ -90625,6 +90953,30 @@ function renderLayout(layoutType, element2, nodes, palette, style) {
90625
90953
  }
90626
90954
  return /* @__PURE__ */ jsxRuntime.jsx(ListRenderer, { element: element2, nodes, palette, style });
90627
90955
  }
90956
+ function arePropsEqual2(prev, next) {
90957
+ if (prev.className !== next.className) {
90958
+ return false;
90959
+ }
90960
+ if (prev.element.id !== next.element.id) {
90961
+ return false;
90962
+ }
90963
+ if (prev.element.type !== next.element.type) {
90964
+ return false;
90965
+ }
90966
+ if (prev.element.width !== next.element.width || prev.element.height !== next.element.height) {
90967
+ return false;
90968
+ }
90969
+ if (prev.element.x !== next.element.x || prev.element.y !== next.element.y) {
90970
+ return false;
90971
+ }
90972
+ const prevData = prev.element.type === "smartArt" ? prev.element.smartArtData : void 0;
90973
+ const nextData = next.element.type === "smartArt" ? next.element.smartArtData : void 0;
90974
+ if (prevData !== nextData) {
90975
+ return false;
90976
+ }
90977
+ return true;
90978
+ }
90979
+ var SmartArtRenderer = React10__namespace.default.memo(SmartArtRendererImpl, arePropsEqual2);
90628
90980
  function ZoomElementRenderer({
90629
90981
  element: element2,
90630
90982
  slides,
@@ -90873,8 +91225,11 @@ function renderBody(el, isImg, isEditing, editText, spellCheck, txtSE, txtS, vec
90873
91225
  ...scene3dStyle.perspective ? { perspective: scene3dStyle.perspective } : {},
90874
91226
  ...scene3dStyle.transformStyle ? { transformStyle: scene3dStyle.transformStyle } : {}
90875
91227
  } : void 0;
91228
+ const shapeTypeForGlyph = "shapeType" in el ? el.shapeType : void 0;
91229
+ const showActionButtonGlyph = isActionButtonShape(shapeTypeForGlyph);
90876
91230
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
90877
91231
  vecShape,
91232
+ showActionButtonGlyph && /* @__PURE__ */ jsxRuntime.jsx(ActionButtonGlyphOverlay, { element: el }),
90878
91233
  isTxtEl ? useSvgWarp ? /* @__PURE__ */ jsxRuntime.jsx(
90879
91234
  "div",
90880
91235
  {
@@ -93663,7 +94018,7 @@ function SectionBlock({
93663
94018
  )
93664
94019
  ] });
93665
94020
  }
93666
- function SlideCard({
94021
+ function SlideCardImpl({
93667
94022
  slide,
93668
94023
  index,
93669
94024
  isActive,
@@ -93717,6 +94072,49 @@ function SlideCard({
93717
94072
  }
93718
94073
  );
93719
94074
  }
94075
+ function arePropsEqual3(prev, next) {
94076
+ if (prev.slide.id !== next.slide.id) {
94077
+ return false;
94078
+ }
94079
+ if (prev.slide.isDirty !== next.slide.isDirty) {
94080
+ return false;
94081
+ }
94082
+ if (prev.slide.hidden !== next.slide.hidden) {
94083
+ return false;
94084
+ }
94085
+ if (prev.slide.elements !== next.slide.elements) {
94086
+ return false;
94087
+ }
94088
+ if (prev.index !== next.index) {
94089
+ return false;
94090
+ }
94091
+ if (prev.isActive !== next.isActive) {
94092
+ return false;
94093
+ }
94094
+ if (prev.isDragTarget !== next.isDragTarget) {
94095
+ return false;
94096
+ }
94097
+ if (prev.isSelected !== next.isSelected) {
94098
+ return false;
94099
+ }
94100
+ if (prev.selectedCount !== next.selectedCount) {
94101
+ return false;
94102
+ }
94103
+ if (prev.selectionOrder !== next.selectionOrder) {
94104
+ return false;
94105
+ }
94106
+ if (prev.canEdit !== next.canEdit) {
94107
+ return false;
94108
+ }
94109
+ if (prev.canvasSize.width !== next.canvasSize.width || prev.canvasSize.height !== next.canvasSize.height) {
94110
+ return false;
94111
+ }
94112
+ 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) {
94113
+ return false;
94114
+ }
94115
+ return true;
94116
+ }
94117
+ var SlideCard = React10__namespace.default.memo(SlideCardImpl, arePropsEqual3);
93720
94118
  function SorterContextMenu({
93721
94119
  x: x2,
93722
94120
  y,
@@ -94510,6 +94908,14 @@ function getCurrentParagraphIndex(editorEl, segments) {
94510
94908
  }
94511
94909
  return paraIdx;
94512
94910
  }
94911
+ var CSS_VALUE_SAFE = /^[a-zA-Z0-9 _,.\-+#'%/]{1,100}$/;
94912
+ function isCssValueSafe(value) {
94913
+ if (value === void 0 || value === null) {
94914
+ return false;
94915
+ }
94916
+ const str = String(value);
94917
+ return str.length > 0 && CSS_VALUE_SAFE.test(str);
94918
+ }
94513
94919
  function deriveStyleFromElement(element2, inheritedStyle) {
94514
94920
  const style = { ...inheritedStyle };
94515
94921
  const tagName = element2.tagName.toLowerCase();
@@ -94642,17 +95048,17 @@ function segmentsToEditorHtml(segments) {
94642
95048
  if (segment.style.strikethrough) {
94643
95049
  inlineStyles.push("text-decoration:line-through");
94644
95050
  }
94645
- if (segment.style.color) {
95051
+ if (segment.style.color && isCssValueSafe(segment.style.color)) {
94646
95052
  inlineStyles.push(`color:${segment.style.color}`);
94647
95053
  }
94648
- if (segment.style.fontSize) {
94649
- inlineStyles.push(`font-size:${segment.style.fontSize}pt`);
95054
+ if (segment.style.fontSize && Number.isFinite(Number(segment.style.fontSize))) {
95055
+ inlineStyles.push(`font-size:${Number(segment.style.fontSize)}pt`);
94650
95056
  }
94651
- if (segment.style.fontFamily) {
95057
+ if (segment.style.fontFamily && isCssValueSafe(segment.style.fontFamily)) {
94652
95058
  inlineStyles.push(`font-family:${segment.style.fontFamily}`);
94653
95059
  }
94654
95060
  const text2 = escapeHtml(segment.text);
94655
- if (segment.style.hyperlink) {
95061
+ if (segment.style.hyperlink && isUrlSafe(segment.style.hyperlink)) {
94656
95062
  const href = escapeHtml(segment.style.hyperlink);
94657
95063
  return `<a href="${href}" style="color:#4a9eff;text-decoration:underline;cursor:pointer" data-hyperlink="${href}">${text2}</a>`;
94658
95064
  }
@@ -94735,7 +95141,8 @@ function renderRichNotesSegments(segments) {
94735
95141
  if (segment.style.fontFamily) {
94736
95142
  style.fontFamily = segment.style.fontFamily;
94737
95143
  }
94738
- if (segment.style.hyperlink) {
95144
+ if (segment.style.hyperlink && isUrlSafe(segment.style.hyperlink)) {
95145
+ const safeHref = segment.style.hyperlink;
94739
95146
  style.color = "#4a9eff";
94740
95147
  style.textDecoration = "underline";
94741
95148
  style.cursor = "pointer";
@@ -94743,11 +95150,11 @@ function renderRichNotesSegments(segments) {
94743
95150
  /* @__PURE__ */ jsxRuntime.jsx(
94744
95151
  "a",
94745
95152
  {
94746
- href: segment.style.hyperlink,
95153
+ href: safeHref,
94747
95154
  style,
94748
95155
  onClick: (e2) => {
94749
95156
  e2.preventDefault();
94750
- window.open(segment.style.hyperlink, "_blank");
95157
+ safeOpenUrl(safeHref);
94751
95158
  },
94752
95159
  children: segment.text
94753
95160
  },
@@ -95299,7 +95706,7 @@ function useSlideNotes({
95299
95706
  const href = target.getAttribute("data-hyperlink") || target.getAttribute("href");
95300
95707
  if (href && (e2.ctrlKey || e2.metaKey)) {
95301
95708
  e2.preventDefault();
95302
- window.open(href, "_blank");
95709
+ safeOpenUrl(href);
95303
95710
  }
95304
95711
  }, []);
95305
95712
  return {
@@ -96972,7 +97379,7 @@ function renderNotesSegments(segments) {
96972
97379
  return React10__namespace.default.createElement("span", { key: `seg-${index}`, style }, segment.text);
96973
97380
  });
96974
97381
  }
96975
- function ScaledSlidePreview({
97382
+ function ScaledSlidePreviewImpl({
96976
97383
  slide,
96977
97384
  templateElements,
96978
97385
  canvasSize,
@@ -97119,6 +97526,40 @@ function ScaledSlidePreview({
97119
97526
  }
97120
97527
  );
97121
97528
  }
97529
+ function arePropsEqual4(prev, next) {
97530
+ if (prev.slide.id !== next.slide.id) {
97531
+ return false;
97532
+ }
97533
+ if (prev.slide.isDirty !== next.slide.isDirty) {
97534
+ return false;
97535
+ }
97536
+ if (prev.slide.hidden !== next.slide.hidden) {
97537
+ return false;
97538
+ }
97539
+ if (prev.slide.elements !== next.slide.elements) {
97540
+ return false;
97541
+ }
97542
+ if (prev.slide.backgroundColor !== next.slide.backgroundColor) {
97543
+ return false;
97544
+ }
97545
+ if (prev.slide.backgroundImage !== next.slide.backgroundImage) {
97546
+ return false;
97547
+ }
97548
+ if (prev.slide.backgroundGradient !== next.slide.backgroundGradient) {
97549
+ return false;
97550
+ }
97551
+ if (prev.templateElements !== next.templateElements) {
97552
+ return false;
97553
+ }
97554
+ if (prev.canvasSize.width !== next.canvasSize.width || prev.canvasSize.height !== next.canvasSize.height) {
97555
+ return false;
97556
+ }
97557
+ if (prev.className !== next.className) {
97558
+ return false;
97559
+ }
97560
+ return true;
97561
+ }
97562
+ var ScaledSlidePreview = React10__namespace.default.memo(ScaledSlidePreviewImpl, arePropsEqual4);
97122
97563
  function PresenterView({
97123
97564
  slides,
97124
97565
  currentSlideIndex,
@@ -111422,6 +111863,13 @@ function convertOmmlToLatex(omml) {
111422
111863
  }
111423
111864
  return ommlChildrenToLatex(oMath);
111424
111865
  }
111866
+ function sanitizeMathMl2(markup) {
111867
+ const purify = DOMPurify__default.default;
111868
+ if (typeof purify.sanitize !== "function") {
111869
+ return markup;
111870
+ }
111871
+ return purify.sanitize(markup, { USE_PROFILES: { mathMl: true, svg: true } });
111872
+ }
111425
111873
  var TEMPLATES = [
111426
111874
  {
111427
111875
  label: "Fraction",
@@ -111487,7 +111935,8 @@ var TEMPLATES = [
111487
111935
  var TEMPLATE_MATHML = TEMPLATES.map((tmpl) => {
111488
111936
  try {
111489
111937
  const tmplOmml = convertLatexToOmml(tmpl.latex);
111490
- return convertOmmlToMathMl(tmplOmml);
111938
+ const raw = convertOmmlToMathMl(tmplOmml);
111939
+ return raw ? sanitizeMathMl2(raw) : "";
111491
111940
  } catch {
111492
111941
  return "";
111493
111942
  }
@@ -111499,7 +111948,7 @@ function MathMlPreview({ mathml }) {
111499
111948
  return;
111500
111949
  }
111501
111950
  if (mathml) {
111502
- containerRef.current.innerHTML = mathml;
111951
+ containerRef.current.innerHTML = sanitizeMathMl2(mathml);
111503
111952
  } else {
111504
111953
  containerRef.current.innerHTML = "";
111505
111954
  }
@@ -111527,6 +111976,7 @@ function EquationEditorDialog({
111527
111976
  return convertOmmlToLatex(existingOmml);
111528
111977
  }, [existingOmml]);
111529
111978
  const [latex, setLatex] = React10.useState(initialLatex);
111979
+ const deferredLatex = React10.useDeferredValue(latex);
111530
111980
  const textareaRef = React10.useRef(null);
111531
111981
  React10.useEffect(() => {
111532
111982
  if (isOpen) {
@@ -111535,17 +111985,17 @@ function EquationEditorDialog({
111535
111985
  }
111536
111986
  }, [isOpen, initialLatex]);
111537
111987
  const { mathml, omml } = React10.useMemo(() => {
111538
- if (!latex.trim()) {
111988
+ if (!deferredLatex.trim()) {
111539
111989
  return { mathml: "", omml: {} };
111540
111990
  }
111541
111991
  try {
111542
- const ommlObj = convertLatexToOmml(latex);
111992
+ const ommlObj = convertLatexToOmml(deferredLatex);
111543
111993
  const mathmlStr = convertOmmlToMathMl(ommlObj);
111544
111994
  return { mathml: mathmlStr, omml: ommlObj };
111545
111995
  } catch {
111546
111996
  return { mathml: "", omml: {} };
111547
111997
  }
111548
- }, [latex]);
111998
+ }, [deferredLatex]);
111549
111999
  const handleInsert = React10.useCallback(() => {
111550
112000
  if (!latex.trim()) {
111551
112001
  return;
@@ -114309,6 +114759,7 @@ function useEditorHistory(input) {
114309
114759
  const historyFutureRef = React10.useRef([]);
114310
114760
  const lastHistorySnapshotRef = React10.useRef(null);
114311
114761
  const lastHistorySerializedRef = React10.useRef("");
114762
+ const lastCheapHashRef = React10.useRef("");
114312
114763
  const isApplyingHistoryRef = React10.useRef(false);
114313
114764
  const unlockHistoryTimerRef = React10.useRef(null);
114314
114765
  const [canUndo, setCanUndo] = React10.useState(false);
@@ -114450,15 +114901,21 @@ function useEditorHistory(input) {
114450
114901
  if (hasActivePointerInteraction()) {
114451
114902
  return;
114452
114903
  }
114904
+ const cheapHash = `${slides.length}|${activeSlideIndex}|${canvasSize.width}x${canvasSize.height}|${slides.map((s) => `${s.id}:${s.elements.length}`).join("/")}`;
114905
+ if (cheapHash === lastCheapHashRef.current) {
114906
+ return;
114907
+ }
114453
114908
  const snapshot2 = buildHistorySnapshot();
114454
114909
  const serialized = JSON.stringify(snapshot2);
114455
114910
  if (serialized === lastHistorySerializedRef.current) {
114911
+ lastCheapHashRef.current = cheapHash;
114456
114912
  return;
114457
114913
  }
114458
114914
  const previousSnapshot = lastHistorySnapshotRef.current;
114459
114915
  if (!previousSnapshot) {
114460
114916
  lastHistorySnapshotRef.current = cloneHistorySnapshot(snapshot2);
114461
114917
  lastHistorySerializedRef.current = serialized;
114918
+ lastCheapHashRef.current = cheapHash;
114462
114919
  updateHistoryAvailability();
114463
114920
  return;
114464
114921
  }
@@ -114469,13 +114926,18 @@ function useEditorHistory(input) {
114469
114926
  historyFutureRef.current = [];
114470
114927
  lastHistorySnapshotRef.current = cloneHistorySnapshot(snapshot2);
114471
114928
  lastHistorySerializedRef.current = serialized;
114929
+ lastCheapHashRef.current = cheapHash;
114472
114930
  updateHistoryAvailability();
114473
114931
  }, [
114932
+ activeSlideIndex,
114474
114933
  buildHistorySnapshot,
114934
+ canvasSize.height,
114935
+ canvasSize.width,
114475
114936
  error2,
114476
114937
  hasActivePointerInteraction,
114477
114938
  loading2,
114478
114939
  pointerCommitNonce,
114940
+ slides,
114479
114941
  updateHistoryAvailability
114480
114942
  ]);
114481
114943
  return {
@@ -118168,6 +118630,7 @@ var DB_NAME2 = "pptx-viewer-audience";
118168
118630
  var DB_VERSION2 = 1;
118169
118631
  var STORE_NAME2 = "content";
118170
118632
  var CONTENT_KEY = "pptx-bytes";
118633
+ var MAX_CONTENT_AGE_MS = 5 * 60 * 1e3;
118171
118634
  function openDb() {
118172
118635
  return new Promise((resolve2, reject) => {
118173
118636
  const request = indexedDB.open(DB_NAME2, DB_VERSION2);
@@ -118187,7 +118650,8 @@ async function storeAudienceContent(content) {
118187
118650
  const tx = db.transaction(STORE_NAME2, "readwrite");
118188
118651
  const store = tx.objectStore(STORE_NAME2);
118189
118652
  const bytes = content instanceof Uint8Array ? content : new Uint8Array(content);
118190
- store.put(bytes, CONTENT_KEY);
118653
+ const record = { bytes, createdAt: Date.now() };
118654
+ store.put(record, CONTENT_KEY);
118191
118655
  tx.oncomplete = () => {
118192
118656
  db.close();
118193
118657
  resolve2();
@@ -118208,13 +118672,24 @@ async function loadAudienceContent() {
118208
118672
  request.onsuccess = () => {
118209
118673
  db.close();
118210
118674
  const result = request.result;
118211
- if (result instanceof Uint8Array) {
118212
- resolve2(result);
118213
- } else if (result instanceof ArrayBuffer) {
118214
- resolve2(new Uint8Array(result));
118215
- } else {
118216
- resolve2(null);
118675
+ if (result && typeof result === "object" && "bytes" in result && "createdAt" in result) {
118676
+ const record = result;
118677
+ const age = Date.now() - record.createdAt;
118678
+ if (age > MAX_CONTENT_AGE_MS) {
118679
+ resolve2(null);
118680
+ return;
118681
+ }
118682
+ const raw = record.bytes;
118683
+ if (raw instanceof Uint8Array) {
118684
+ resolve2(raw);
118685
+ } else if (raw instanceof ArrayBuffer) {
118686
+ resolve2(new Uint8Array(raw));
118687
+ } else {
118688
+ resolve2(null);
118689
+ }
118690
+ return;
118217
118691
  }
118692
+ resolve2(null);
118218
118693
  };
118219
118694
  request.onerror = () => {
118220
118695
  db.close();
@@ -118247,14 +118722,34 @@ async function clearAudienceContent() {
118247
118722
  var PRESENTER_CHANNEL_NAME = "pptx-viewer-presenter";
118248
118723
  var AUDIENCE_HASH = "#pptx-audience";
118249
118724
  var PRESENTER_MSG_ORIGIN = "pptx-viewer-presenter";
118725
+ var AUDIENCE_NONCE_KEY = "nonce";
118726
+ function generateSessionId() {
118727
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
118728
+ return crypto.randomUUID();
118729
+ }
118730
+ return `s${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
118731
+ }
118732
+ function parseAudienceNonce() {
118733
+ const hash = window.location.hash;
118734
+ if (!hash.startsWith(AUDIENCE_HASH)) {
118735
+ return null;
118736
+ }
118737
+ const trailing = hash.slice(AUDIENCE_HASH.length);
118738
+ if (!trailing) {
118739
+ return null;
118740
+ }
118741
+ const params2 = new URLSearchParams(trailing.replace(/^[&;?]/, ""));
118742
+ return params2.get(AUDIENCE_NONCE_KEY);
118743
+ }
118250
118744
  function isAudienceTab() {
118251
- return window.location.hash === AUDIENCE_HASH;
118745
+ return window.location.hash.startsWith(AUDIENCE_HASH);
118252
118746
  }
118253
118747
  function usePresenterWindow(input) {
118254
118748
  const { currentSlideIndex, isPresenterMode, content } = input;
118255
118749
  const audienceWindowRef = React10.useRef(null);
118256
118750
  const channelRef = React10.useRef(null);
118257
118751
  const pollTimerRef = React10.useRef(null);
118752
+ const sessionIdRef = React10.useRef("");
118258
118753
  const getChannel2 = React10.useCallback(() => {
118259
118754
  if (!channelRef.current) {
118260
118755
  channelRef.current = new BroadcastChannel(PRESENTER_CHANNEL_NAME);
@@ -118266,10 +118761,14 @@ function usePresenterWindow(input) {
118266
118761
  }, []);
118267
118762
  const syncSlideToAudience = React10.useCallback(
118268
118763
  (slideIndex) => {
118764
+ if (!sessionIdRef.current) {
118765
+ return;
118766
+ }
118269
118767
  const msg = {
118270
118768
  origin: PRESENTER_MSG_ORIGIN,
118271
118769
  type: "presenter-slide-change",
118272
- slideIndex
118770
+ slideIndex,
118771
+ sessionId: sessionIdRef.current
118273
118772
  };
118274
118773
  try {
118275
118774
  getChannel2().postMessage(msg);
@@ -118279,13 +118778,16 @@ function usePresenterWindow(input) {
118279
118778
  [getChannel2]
118280
118779
  );
118281
118780
  const closeAudienceWindow = React10.useCallback(() => {
118282
- try {
118283
- const exitMsg = {
118284
- origin: PRESENTER_MSG_ORIGIN,
118285
- type: "presenter-exit"
118286
- };
118287
- getChannel2().postMessage(exitMsg);
118288
- } catch {
118781
+ if (sessionIdRef.current) {
118782
+ try {
118783
+ const exitMsg = {
118784
+ origin: PRESENTER_MSG_ORIGIN,
118785
+ type: "presenter-exit",
118786
+ sessionId: sessionIdRef.current
118787
+ };
118788
+ getChannel2().postMessage(exitMsg);
118789
+ } catch {
118790
+ }
118289
118791
  }
118290
118792
  const win = audienceWindowRef.current;
118291
118793
  if (win && !win.closed) {
@@ -118295,6 +118797,7 @@ function usePresenterWindow(input) {
118295
118797
  }
118296
118798
  }
118297
118799
  audienceWindowRef.current = null;
118800
+ sessionIdRef.current = "";
118298
118801
  if (pollTimerRef.current !== null) {
118299
118802
  clearInterval(pollTimerRef.current);
118300
118803
  pollTimerRef.current = null;
@@ -118305,20 +118808,53 @@ function usePresenterWindow(input) {
118305
118808
  if (isAudienceWindowOpen()) {
118306
118809
  closeAudienceWindow();
118307
118810
  }
118308
- if (content) {
118309
- void storeAudienceContent(content);
118310
- }
118311
- const url = new URL(window.location.href);
118312
- url.hash = AUDIENCE_HASH;
118313
- const win = window.open(url.toString(), "_blank");
118314
- if (!win) {
118811
+ const blankWin = window.open("about:blank", "_blank");
118812
+ if (!blankWin) {
118315
118813
  return false;
118316
118814
  }
118317
- audienceWindowRef.current = win;
118815
+ audienceWindowRef.current = blankWin;
118816
+ const sessionId = generateSessionId();
118817
+ sessionIdRef.current = sessionId;
118818
+ const audienceUrl = new URL(window.location.href);
118819
+ const params2 = new URLSearchParams();
118820
+ params2.set(AUDIENCE_NONCE_KEY, sessionId);
118821
+ audienceUrl.hash = `${AUDIENCE_HASH}&${params2.toString()}`;
118822
+ const navigateOrClose = (ok) => {
118823
+ const win = audienceWindowRef.current;
118824
+ if (!win || win.closed) {
118825
+ return;
118826
+ }
118827
+ if (!ok) {
118828
+ try {
118829
+ win.close();
118830
+ } catch {
118831
+ }
118832
+ audienceWindowRef.current = null;
118833
+ sessionIdRef.current = "";
118834
+ return;
118835
+ }
118836
+ try {
118837
+ win.location.replace(audienceUrl.toString());
118838
+ } catch {
118839
+ try {
118840
+ win.close();
118841
+ } catch {
118842
+ }
118843
+ audienceWindowRef.current = null;
118844
+ sessionIdRef.current = "";
118845
+ }
118846
+ };
118847
+ if (content) {
118848
+ void storeAudienceContent(content).then(() => navigateOrClose(true)).catch(() => navigateOrClose(false));
118849
+ } else {
118850
+ navigateOrClose(true);
118851
+ }
118318
118852
  window.setTimeout(() => syncSlideToAudience(currentSlideIndex), 1500);
118319
118853
  pollTimerRef.current = setInterval(() => {
118320
- if (win.closed) {
118854
+ const win = audienceWindowRef.current;
118855
+ if (!win || win.closed) {
118321
118856
  audienceWindowRef.current = null;
118857
+ sessionIdRef.current = "";
118322
118858
  if (pollTimerRef.current !== null) {
118323
118859
  clearInterval(pollTimerRef.current);
118324
118860
  pollTimerRef.current = null;
@@ -118346,6 +118882,13 @@ function usePresenterWindow(input) {
118346
118882
  closeAudienceWindow();
118347
118883
  }
118348
118884
  }, [isPresenterMode, closeAudienceWindow]);
118885
+ React10.useEffect(() => {
118886
+ const handleBeforeUnload = () => {
118887
+ void clearAudienceContent();
118888
+ };
118889
+ window.addEventListener("beforeunload", handleBeforeUnload);
118890
+ return () => window.removeEventListener("beforeunload", handleBeforeUnload);
118891
+ }, []);
118349
118892
  return {
118350
118893
  openAudienceWindow,
118351
118894
  closeAudienceWindow,
@@ -118383,6 +118926,7 @@ function useAudienceMode(input) {
118383
118926
  if (!isAudienceTab()) {
118384
118927
  return;
118385
118928
  }
118929
+ const expectedSessionId = parseAudienceNonce();
118386
118930
  let channel;
118387
118931
  try {
118388
118932
  channel = new BroadcastChannel(PRESENTER_CHANNEL_NAME);
@@ -118394,6 +118938,9 @@ function useAudienceMode(input) {
118394
118938
  if (!data || data.origin !== PRESENTER_MSG_ORIGIN) {
118395
118939
  return;
118396
118940
  }
118941
+ if (expectedSessionId && data.sessionId !== expectedSessionId) {
118942
+ return;
118943
+ }
118397
118944
  if (data.type === "presenter-slide-change") {
118398
118945
  onSetActiveSlideIndex(data.slideIndex);
118399
118946
  }
@@ -119327,6 +119874,12 @@ function useTouchGestures(input) {
119327
119874
  callbacksRef.current = callbacks;
119328
119875
  const scaleRef = React10.useRef(currentScale);
119329
119876
  scaleRef.current = currentScale;
119877
+ const [targetVersion, setTargetVersion] = React10.useState(0);
119878
+ const lastTargetRef = React10.useRef(null);
119879
+ if (targetRef.current !== lastTargetRef.current) {
119880
+ lastTargetRef.current = targetRef.current;
119881
+ queueMicrotask(() => setTargetVersion((v) => v + 1));
119882
+ }
119330
119883
  React10.useEffect(() => {
119331
119884
  const el = targetRef.current;
119332
119885
  if (!el || !enabled) {
@@ -119415,7 +119968,7 @@ function useTouchGestures(input) {
119415
119968
  el.removeEventListener("touchcancel", handleTouchCancel);
119416
119969
  cancelLongPress();
119417
119970
  };
119418
- }, [targetRef, enabled]);
119971
+ }, [targetRef, enabled, targetVersion]);
119419
119972
  }
119420
119973
 
119421
119974
  // src/viewer/utils/dom-helpers.ts
@@ -119436,11 +119989,31 @@ function safeConfirm(message) {
119436
119989
  return false;
119437
119990
  }
119438
119991
  }
119992
+ function sanitizeDownloadFilename(input) {
119993
+ if (typeof input !== "string" || input.trim().length === 0) {
119994
+ return "presentation.pptx";
119995
+ }
119996
+ let cleaned = input.replace(/[\x00-\x1f\x7f"\\/:*?<>|]/g, "_").replace(/\.\./g, "__").replace(/^\.+/, "").trim();
119997
+ if (cleaned.length === 0) {
119998
+ return "presentation.pptx";
119999
+ }
120000
+ if (cleaned.length > 200) {
120001
+ const dot = cleaned.lastIndexOf(".");
120002
+ if (dot > 0 && cleaned.length - dot <= 16) {
120003
+ const ext = cleaned.slice(dot);
120004
+ cleaned = cleaned.slice(0, 200 - ext.length) + ext;
120005
+ } else {
120006
+ cleaned = cleaned.slice(0, 200);
120007
+ }
120008
+ }
120009
+ return cleaned;
120010
+ }
119439
120011
  function downloadBlob(blob, filename) {
120012
+ const safeName = sanitizeDownloadFilename(filename);
119440
120013
  const url = URL.createObjectURL(blob);
119441
120014
  const a2 = document.createElement("a");
119442
120015
  a2.href = url;
119443
- a2.download = filename;
120016
+ a2.download = safeName;
119444
120017
  document.body.appendChild(a2);
119445
120018
  a2.click();
119446
120019
  setTimeout(() => {
@@ -119871,27 +120444,82 @@ function openAutosaveDb2() {
119871
120444
  req.onerror = () => reject(req.error);
119872
120445
  });
119873
120446
  }
119874
- async function saveToIndexedDb(filePath, data) {
120447
+ async function deleteOldestAutosaveEntry() {
119875
120448
  const db = await openAutosaveDb2();
119876
- return new Promise((resolve2, reject) => {
119877
- const tx = db.transaction(STORE_NAME3, "readwrite");
119878
- const store = tx.objectStore(STORE_NAME3);
119879
- store.put({
119880
- key: filePath,
119881
- data,
119882
- timestamp: Date.now(),
119883
- size: data.byteLength
119884
- });
119885
- tx.oncomplete = () => {
119886
- db.close();
119887
- resolve2(true);
119888
- };
119889
- tx.onerror = () => {
119890
- db.close();
119891
- reject(tx.error);
119892
- };
120449
+ return new Promise((resolve2) => {
120450
+ try {
120451
+ const tx = db.transaction(STORE_NAME3, "readwrite");
120452
+ const store = tx.objectStore(STORE_NAME3);
120453
+ let oldestKey = null;
120454
+ let oldestTimestamp = Infinity;
120455
+ const cursorReq = store.openCursor();
120456
+ cursorReq.onsuccess = () => {
120457
+ const cursor = cursorReq.result;
120458
+ if (cursor) {
120459
+ const value = cursor.value;
120460
+ if (typeof value.timestamp === "number" && value.timestamp < oldestTimestamp) {
120461
+ oldestTimestamp = value.timestamp;
120462
+ oldestKey = cursor.primaryKey;
120463
+ }
120464
+ cursor.continue();
120465
+ } else if (oldestKey !== null) {
120466
+ store.delete(oldestKey);
120467
+ }
120468
+ };
120469
+ tx.oncomplete = () => {
120470
+ db.close();
120471
+ resolve2(oldestKey !== null);
120472
+ };
120473
+ tx.onerror = () => {
120474
+ db.close();
120475
+ resolve2(false);
120476
+ };
120477
+ } catch {
120478
+ try {
120479
+ db.close();
120480
+ } catch {
120481
+ }
120482
+ resolve2(false);
120483
+ }
119893
120484
  });
119894
120485
  }
120486
+ function putAutosaveRecord(filePath, data) {
120487
+ return openAutosaveDb2().then(
120488
+ (db) => new Promise((resolve2, reject) => {
120489
+ const tx = db.transaction(STORE_NAME3, "readwrite");
120490
+ const store = tx.objectStore(STORE_NAME3);
120491
+ store.put({
120492
+ key: filePath,
120493
+ data,
120494
+ timestamp: Date.now(),
120495
+ size: data.byteLength
120496
+ });
120497
+ tx.oncomplete = () => {
120498
+ db.close();
120499
+ resolve2(true);
120500
+ };
120501
+ tx.onerror = () => {
120502
+ db.close();
120503
+ reject(tx.error);
120504
+ };
120505
+ })
120506
+ );
120507
+ }
120508
+ async function saveToIndexedDb(filePath, data) {
120509
+ try {
120510
+ return await putAutosaveRecord(filePath, data);
120511
+ } catch (err) {
120512
+ const errName = err instanceof Error || err instanceof DOMException ? err.name : "";
120513
+ if (errName !== "QuotaExceededError") {
120514
+ throw err;
120515
+ }
120516
+ const deleted = await deleteOldestAutosaveEntry();
120517
+ if (!deleted) {
120518
+ throw err;
120519
+ }
120520
+ return putAutosaveRecord(filePath, data);
120521
+ }
120522
+ }
119895
120523
  function useAutosave(input) {
119896
120524
  const {
119897
120525
  isDirty,
@@ -120008,6 +120636,25 @@ function collectReferencedFontFamilies(slides) {
120008
120636
  }
120009
120637
  return families;
120010
120638
  }
120639
+ var FONT_NAME_UNSAFE_CHARS = /["\\\n\r;}<>]/;
120640
+ var FONT_FORMAT_ALLOWED = /* @__PURE__ */ new Set([
120641
+ "truetype",
120642
+ "opentype",
120643
+ "woff",
120644
+ "woff2",
120645
+ "svg",
120646
+ "embedded-opentype"
120647
+ ]);
120648
+ var FONT_DATA_URL_PATTERN = /^data:font\/[a-z0-9+.-]+(?:;charset=[a-z0-9-]+)?;base64,[A-Za-z0-9+/=]+$/i;
120649
+ function isFontDataUrlSafe(url) {
120650
+ if (typeof url !== "string" || url.length === 0) {
120651
+ return false;
120652
+ }
120653
+ if (url.startsWith("blob:")) {
120654
+ return true;
120655
+ }
120656
+ return FONT_DATA_URL_PATTERN.test(url);
120657
+ }
120011
120658
  function useFontInjection({ embeddedFonts, slides }) {
120012
120659
  React10.useEffect(() => {
120013
120660
  if (!embeddedFonts.length) {
@@ -120015,17 +120662,28 @@ function useFontInjection({ embeddedFonts, slides }) {
120015
120662
  }
120016
120663
  const styleEl = document.createElement("style");
120017
120664
  styleEl.id = EMBEDDED_FONTS_STYLE_ID;
120018
- const cssRules = embeddedFonts.map((font) => {
120665
+ const cssRules = embeddedFonts.flatMap((font) => {
120666
+ if (typeof font.name !== "string" || font.name.length === 0 || FONT_NAME_UNSAFE_CHARS.test(font.name)) {
120667
+ return [];
120668
+ }
120669
+ if (!isFontDataUrlSafe(font.dataUrl)) {
120670
+ return [];
120671
+ }
120672
+ const fontFormat = font.format ?? "truetype";
120673
+ if (!FONT_FORMAT_ALLOWED.has(fontFormat)) {
120674
+ return [];
120675
+ }
120019
120676
  const fontWeight = font.bold ? "700" : "400";
120020
120677
  const fontStyleCss = font.italic ? "italic" : "normal";
120021
- const fontFormat = font.format ?? "truetype";
120022
- return `@font-face {
120678
+ return [
120679
+ `@font-face {
120023
120680
  font-family: "${font.name}";
120024
120681
  src: url("${font.dataUrl}") format("${fontFormat}");
120025
120682
  font-weight: ${fontWeight};
120026
120683
  font-style: ${fontStyleCss};
120027
120684
  font-display: swap;
120028
- }`;
120685
+ }`
120686
+ ];
120029
120687
  }).join("\n");
120030
120688
  styleEl.textContent = cssRules;
120031
120689
  document.head.appendChild(styleEl);
@@ -120054,7 +120712,7 @@ function useFontInjection({ embeddedFonts, slides }) {
120054
120712
  const linkEl = document.createElement("link");
120055
120713
  linkEl.id = GOOGLE_FONTS_LINK_ID;
120056
120714
  linkEl.rel = "stylesheet";
120057
- linkEl.href = `https://fonts.googleapis.com/css2?${googleFamilies.map((f) => `family=${GOOGLE_FONTS_AVAILABLE[f]}`).join("&")}&display=swap`;
120715
+ linkEl.href = `https://fonts.googleapis.com/css2?${googleFamilies.map((f) => `family=${encodeURIComponent(GOOGLE_FONTS_AVAILABLE[f])}`).join("&")}&display=swap`;
120058
120716
  document.head.appendChild(linkEl);
120059
120717
  return () => {
120060
120718
  const existing = document.getElementById(GOOGLE_FONTS_LINK_ID);
@@ -120070,13 +120728,18 @@ function useFontInjection({ embeddedFonts, slides }) {
120070
120728
  }
120071
120729
  const styleEl = document.createElement("style");
120072
120730
  styleEl.id = SYMBOL_FONTS_STYLE_ID;
120073
- const rules = neededSymbolFonts.map(
120074
- (font) => `@font-face {
120731
+ const rules = neededSymbolFonts.flatMap((font) => {
120732
+ if (typeof font !== "string" || FONT_NAME_UNSAFE_CHARS.test(font)) {
120733
+ return [];
120734
+ }
120735
+ return [
120736
+ `@font-face {
120075
120737
  font-family: "${font}";
120076
120738
  src: local("${font}"), local("${font} Regular");
120077
120739
  font-display: swap;
120078
120740
  }`
120079
- ).join("\n");
120741
+ ];
120742
+ }).join("\n");
120080
120743
  styleEl.textContent = rules;
120081
120744
  document.head.appendChild(styleEl);
120082
120745
  return () => {
@@ -120219,16 +120882,17 @@ function useLoadContent({
120219
120882
  `[pptx] Large file detected (${fileSizeMB.toFixed(1)} MB). Loading may use significant memory.`
120220
120883
  );
120221
120884
  }
120222
- if (handlerRef.current) {
120223
- handlerRef.current.dispose();
120224
- handlerRef.current = null;
120225
- }
120885
+ const previousHandler = handlerRef.current;
120226
120886
  const handler = new pptxViewerCore.PptxHandler();
120227
120887
  const parsed = await handler.load(buffer);
120228
120888
  if (cancelled || token !== renderTokenRef.current) {
120229
120889
  handler.dispose();
120230
120890
  return;
120231
120891
  }
120892
+ if (previousHandler) {
120893
+ previousHandler.dispose();
120894
+ }
120895
+ handlerRef.current = null;
120232
120896
  const mediaElements = [];
120233
120897
  for (const slide of parsed.slides) {
120234
120898
  collectMediaElements(slide.elements, mediaElements);
@@ -120273,6 +120937,7 @@ function useLoadContent({
120273
120937
  })
120274
120938
  );
120275
120939
  const { paths: imagePaths, refs: imageRefs } = collectImagePaths(parsed.slides);
120940
+ let nextSlides = parsed.slides;
120276
120941
  if (imagePaths.size > 0) {
120277
120942
  const resolvedMap = /* @__PURE__ */ new Map();
120278
120943
  await Promise.all(
@@ -120286,15 +120951,47 @@ function useLoadContent({
120286
120951
  }
120287
120952
  })
120288
120953
  );
120954
+ const elementPatches = /* @__PURE__ */ new Map();
120289
120955
  for (const ref of imageRefs) {
120290
120956
  const url = resolvedMap.get(ref.path);
120291
- if (url) {
120292
- ref.element[ref.field] = url;
120957
+ if (!url) {
120958
+ continue;
120293
120959
  }
120960
+ const id2 = ref.element.id;
120961
+ const existing = elementPatches.get(id2) ?? {};
120962
+ existing[ref.field] = url;
120963
+ elementPatches.set(id2, existing);
120964
+ }
120965
+ if (elementPatches.size > 0) {
120966
+ const patchElements = (elements) => {
120967
+ let mutated = false;
120968
+ const next = elements.map((el) => {
120969
+ let updated = el;
120970
+ const patch = elementPatches.get(el.id);
120971
+ if (patch) {
120972
+ updated = { ...el, ...patch };
120973
+ }
120974
+ if (updated.type === "group" && updated.children?.length) {
120975
+ const newChildren = patchElements(updated.children);
120976
+ if (newChildren !== updated.children) {
120977
+ updated = { ...updated, children: newChildren };
120978
+ }
120979
+ }
120980
+ if (updated !== el) {
120981
+ mutated = true;
120982
+ }
120983
+ return updated;
120984
+ });
120985
+ return mutated ? next : elements;
120986
+ };
120987
+ nextSlides = parsed.slides.map((s) => {
120988
+ const newElements = patchElements(s.elements);
120989
+ return newElements === s.elements ? s : { ...s, elements: newElements };
120990
+ });
120294
120991
  }
120295
120992
  }
120296
120993
  handlerRef.current = handler;
120297
- setSlides(parsed.slides);
120994
+ setSlides(nextSlides);
120298
120995
  setTemplateElementsBySlideId({});
120299
120996
  setCanvasSize({
120300
120997
  width: parsed.width ?? DEFAULT_CANVAS_WIDTH,
@@ -121468,7 +122165,19 @@ function injectFontFaces(svg, fontFaces) {
121468
122165
  if (!fontFaces.length) {
121469
122166
  return svg;
121470
122167
  }
121471
- const styleBlock = `<style type="text/css">${fontFaces.map((f) => f.css).join("\n")}</style>`;
122168
+ const safeFontFaces = fontFaces.filter((f) => {
122169
+ if (f.css.toLowerCase().includes("</style")) {
122170
+ console.warn(
122171
+ `[export-svg] Dropping @font-face entry for "${f.family}" containing "</style" \u2014 would break out of the <style> block.`
122172
+ );
122173
+ return false;
122174
+ }
122175
+ return true;
122176
+ });
122177
+ if (!safeFontFaces.length) {
122178
+ return svg;
122179
+ }
122180
+ const styleBlock = `<style type="text/css">${safeFontFaces.map((f) => f.css).join("\n")}</style>`;
121472
122181
  if (svg.includes("<defs>")) {
121473
122182
  return svg.replace("<defs>", `<defs>${styleBlock}`);
121474
122183
  }
@@ -121794,7 +122503,7 @@ function useExportHandlers(input) {
121794
122503
  activeSlideIndexForGuides,
121795
122504
  modalControls
121796
122505
  });
121797
- const handleExportPng = async () => {
122506
+ const handleExportPng = React10.useCallback(async () => {
121798
122507
  const stageEl = canvasStageRef.current;
121799
122508
  if (!stageEl) {
121800
122509
  return;
@@ -121806,8 +122515,8 @@ function useExportHandlers(input) {
121806
122515
  } catch (err) {
121807
122516
  console.error("[PowerPointViewer] PNG export failed:", err);
121808
122517
  }
121809
- };
121810
- const handleExportPdf = async () => {
122518
+ }, [canvasStageRef, activeSlideIndex, activeSlide?.backgroundColor]);
122519
+ const handleExportPdf = React10.useCallback(async () => {
121811
122520
  if (!canvasStageRef.current) {
121812
122521
  return;
121813
122522
  }
@@ -121848,8 +122557,8 @@ function useExportHandlers(input) {
121848
122557
  exportAbortRef.current = null;
121849
122558
  setExportModalOpen(false);
121850
122559
  }
121851
- };
121852
- const handleExportNotesPdf = async () => {
122560
+ }, [canvasStageRef, slides.length, setActiveSlideIndex, activeSlideIndex]);
122561
+ const handleExportNotesPdf = React10.useCallback(async () => {
121853
122562
  if (!canvasStageRef.current) {
121854
122563
  return;
121855
122564
  }
@@ -121892,8 +122601,8 @@ function useExportHandlers(input) {
121892
122601
  exportAbortRef.current = null;
121893
122602
  setExportModalOpen(false);
121894
122603
  }
121895
- };
121896
- const handleCopySlideAsImage = async () => {
122604
+ }, [canvasStageRef, slides, setActiveSlideIndex, activeSlideIndex]);
122605
+ const handleCopySlideAsImage = React10.useCallback(async () => {
121897
122606
  const stageEl = canvasStageRef.current;
121898
122607
  if (!stageEl) {
121899
122608
  return;
@@ -121905,8 +122614,8 @@ function useExportHandlers(input) {
121905
122614
  } catch (err) {
121906
122615
  console.error("[PowerPointViewer] Copy slide as image failed:", err);
121907
122616
  }
121908
- };
121909
- const handleExportVideo = async () => {
122617
+ }, [canvasStageRef, activeSlide?.backgroundColor]);
122618
+ const handleExportVideo = React10.useCallback(async () => {
121910
122619
  if (!canvasStageRef.current) {
121911
122620
  return;
121912
122621
  }
@@ -121948,8 +122657,8 @@ function useExportHandlers(input) {
121948
122657
  exportAbortRef.current = null;
121949
122658
  setExportModalOpen(false);
121950
122659
  }
121951
- };
121952
- const handleExportGif = async () => {
122660
+ }, [canvasStageRef, slides.length, setActiveSlideIndex, activeSlideIndex]);
122661
+ const handleExportGif = React10.useCallback(async () => {
121953
122662
  if (!canvasStageRef.current) {
121954
122663
  return;
121955
122664
  }
@@ -121987,7 +122696,7 @@ function useExportHandlers(input) {
121987
122696
  exportAbortRef.current = null;
121988
122697
  setExportModalOpen(false);
121989
122698
  }
121990
- };
122699
+ }, [canvasStageRef, slides.length, setActiveSlideIndex, activeSlideIndex]);
121991
122700
  const handleCancelExport = React10.useCallback(() => {
121992
122701
  exportAbortRef.current?.abort();
121993
122702
  exportAbortRef.current = null;
@@ -122013,6 +122722,15 @@ function useExportHandlers(input) {
122013
122722
  exportStatusMessage
122014
122723
  };
122015
122724
  }
122725
+ function escapeHtmlAttr(value) {
122726
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
122727
+ }
122728
+ function safeDataImageSrc(src) {
122729
+ if (typeof src !== "string" || !src.startsWith("data:image/")) {
122730
+ return "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNgAAIAAAUAAen63NgAAAAASUVORK5CYII=";
122731
+ }
122732
+ return escapeHtmlAttr(src);
122733
+ }
122016
122734
  function openPrintWindow(title, bodyHtml, orientation, colorFilter, frameSlides) {
122017
122735
  const printWindow = window.open("", "_blank", "noopener,noreferrer");
122018
122736
  if (!printWindow) {
@@ -122195,7 +122913,7 @@ function usePrintHandlers(input) {
122195
122913
  const slideImages = slideIndices.map((idx) => allImages[idx]).filter(Boolean);
122196
122914
  if (settings.printWhat === "slides") {
122197
122915
  const bodyHtml = slideImages.map(
122198
- (img, i3) => `<section class="page slide-page"><img class="slide-img" src="${img}" alt="Slide ${slideIndices[i3] + 1}" /></section>`
122916
+ (img, i3) => `<section class="page slide-page"><img class="slide-img" src="${safeDataImageSrc(img)}" alt="Slide ${slideIndices[i3] + 1}" /></section>`
122199
122917
  ).join("");
122200
122918
  openPrintWindow(
122201
122919
  "Slides",
@@ -122211,7 +122929,7 @@ function usePrintHandlers(input) {
122211
122929
  const idx = slideIndices[i3];
122212
122930
  const notes = slides[idx]?.notes?.trim() || "";
122213
122931
  return `<section class="page notes-page">
122214
- <img class="notes-slide" src="${img}" alt="Slide ${idx + 1}" />
122932
+ <img class="notes-slide" src="${safeDataImageSrc(img)}" alt="Slide ${idx + 1}" />
122215
122933
  <div class="notes-text">${escapeHtml2(notes)}</div>
122216
122934
  </section>`;
122217
122935
  }).join("");
@@ -122243,14 +122961,14 @@ function usePrintHandlers(input) {
122243
122961
  if (isThreePerPage) {
122244
122962
  const rows = Array.from({ length: spp }, (_, cellIndex) => {
122245
122963
  const img = pageImgs[cellIndex];
122246
- const slideCell = img ? `<div class="handout-cell"><img src="${img}" alt="Slide ${slideIndices[i3 + cellIndex] + 1}" /></div>` : `<div class="handout-cell"></div>`;
122964
+ const slideCell = img ? `<div class="handout-cell"><img src="${safeDataImageSrc(img)}" alt="Slide ${slideIndices[i3 + cellIndex] + 1}" /></div>` : `<div class="handout-cell"></div>`;
122247
122965
  return `<div class="handout-row-3">${slideCell}${buildNoteLines()}</div>`;
122248
122966
  }).join("");
122249
122967
  pages.push(`<section class="page"><div class="handout-grid-3">${rows}</div></section>`);
122250
122968
  } else {
122251
122969
  const cells = Array.from({ length: spp }, (_, cellIndex) => {
122252
122970
  const img = pageImgs[cellIndex];
122253
- return img ? `<div class="handout-cell"><img src="${img}" alt="Slide ${slideIndices[i3 + cellIndex] + 1}" /></div>` : `<div class="handout-cell"></div>`;
122971
+ return img ? `<div class="handout-cell"><img src="${safeDataImageSrc(img)}" alt="Slide ${slideIndices[i3 + cellIndex] + 1}" /></div>` : `<div class="handout-cell"></div>`;
122254
122972
  }).join("");
122255
122973
  pages.push(
122256
122974
  `<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>`