@myoc/element 0.19.519 → 0.19.520

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 (60) hide show
  1. package/dist/dev/index.js +1138 -355
  2. package/dist/dev/index.js.map +4 -4
  3. package/dist/prod/index.js +13 -13
  4. package/dist/types/common/src/constants.d.ts +8 -5
  5. package/dist/types/common/src/utils.d.ts +0 -42
  6. package/dist/types/element/src/comparisons.d.ts +1 -0
  7. package/dist/types/element/src/image.d.ts +5 -0
  8. package/dist/types/element/src/newElement.d.ts +2 -0
  9. package/dist/types/element/src/types.d.ts +8 -0
  10. package/dist/types/excalidraw/actions/actionBoundText.d.ts +4 -2
  11. package/dist/types/excalidraw/actions/actionCanvas.d.ts +22 -11
  12. package/dist/types/excalidraw/actions/actionClipboard.d.ts +4 -2
  13. package/dist/types/excalidraw/actions/actionCropEditor.d.ts +2 -1
  14. package/dist/types/excalidraw/actions/actionDeleteSelected.d.ts +6 -3
  15. package/dist/types/excalidraw/actions/actionDeselect.d.ts +2 -1
  16. package/dist/types/excalidraw/actions/actionElementLink.d.ts +2 -1
  17. package/dist/types/excalidraw/actions/actionElementLock.d.ts +4 -2
  18. package/dist/types/excalidraw/actions/actionEmbeddable.d.ts +2 -1
  19. package/dist/types/excalidraw/actions/actionExport.d.ts +4 -2
  20. package/dist/types/excalidraw/actions/actionFrame.d.ts +10 -4
  21. package/dist/types/excalidraw/actions/actionGroup.d.ts +4 -2
  22. package/dist/types/excalidraw/actions/actionLinearEditor.d.ts +4 -1
  23. package/dist/types/excalidraw/actions/actionLink.d.ts +2 -1
  24. package/dist/types/excalidraw/actions/actionMenu.d.ts +2 -1
  25. package/dist/types/excalidraw/actions/actionProperties.d.ts +16 -5
  26. package/dist/types/excalidraw/actions/actionSelectAll.d.ts +2 -1
  27. package/dist/types/excalidraw/actions/actionStyles.d.ts +2 -1
  28. package/dist/types/excalidraw/actions/actionToggleArrowBinding.d.ts +2 -1
  29. package/dist/types/excalidraw/actions/actionToggleGridMode.d.ts +2 -1
  30. package/dist/types/excalidraw/actions/actionToggleMidpointSnapping.d.ts +2 -1
  31. package/dist/types/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +2 -1
  32. package/dist/types/excalidraw/actions/actionToggleSearchMenu.d.ts +2 -1
  33. package/dist/types/excalidraw/actions/actionToggleStats.d.ts +2 -1
  34. package/dist/types/excalidraw/actions/actionToggleViewMode.d.ts +2 -1
  35. package/dist/types/excalidraw/actions/actionToggleZenMode.d.ts +2 -1
  36. package/dist/types/excalidraw/actions/index.d.ts +1 -1
  37. package/dist/types/excalidraw/actions/types.d.ts +1 -1
  38. package/dist/types/excalidraw/appState.d.ts +2 -1
  39. package/dist/types/excalidraw/components/App.d.ts +12 -28
  40. package/dist/types/excalidraw/components/ColorPicker/colorPickerUtils.d.ts +1 -1
  41. package/dist/types/excalidraw/components/ConvertElementTypePopup.d.ts +1 -1
  42. package/dist/types/excalidraw/components/EyeDropper.d.ts +1 -1
  43. package/dist/types/excalidraw/components/OverwriteConfirm/OverwriteConfirmState.d.ts +1 -1
  44. package/dist/types/excalidraw/components/SearchMenu.d.ts +1 -1
  45. package/dist/types/excalidraw/components/Sidebar/Sidebar.d.ts +1 -1
  46. package/dist/types/excalidraw/components/canvases/StaticCanvas.d.ts +2 -1
  47. package/dist/types/excalidraw/components/icons.d.ts +2 -0
  48. package/dist/types/excalidraw/data/blob.d.ts +2 -1
  49. package/dist/types/excalidraw/data/json.d.ts +2 -1
  50. package/dist/types/excalidraw/renderer/animation.d.ts +1 -0
  51. package/dist/types/excalidraw/scene/Renderer.d.ts +2 -0
  52. package/dist/types/excalidraw/scene/index.d.ts +1 -1
  53. package/dist/types/excalidraw/scene/types.d.ts +1 -0
  54. package/dist/types/excalidraw/scroll.d.ts +46 -0
  55. package/dist/types/excalidraw/types.d.ts +16 -3
  56. package/dist/types/laser-pointer/src/index.d.ts +2 -0
  57. package/dist/types/laser-pointer/src/math.d.ts +16 -0
  58. package/dist/types/laser-pointer/src/simplify.d.ts +2 -0
  59. package/dist/types/laser-pointer/src/state.d.ts +35 -0
  60. package/package.json +5 -4
package/dist/dev/index.js CHANGED
@@ -1832,9 +1832,9 @@ init_define_import_meta_env();
1832
1832
  function rotatePoints(points, center, degrees) {
1833
1833
  if (points && points.length) {
1834
1834
  const [cx, cy] = center;
1835
- const angle = Math.PI / 180 * degrees;
1836
- const cos = Math.cos(angle);
1837
- const sin = Math.sin(angle);
1835
+ const angle2 = Math.PI / 180 * degrees;
1836
+ const cos = Math.cos(angle2);
1837
+ const sin = Math.sin(angle2);
1838
1838
  for (const p of points) {
1839
1839
  const [x, y] = p;
1840
1840
  p[0] = (x - cx) * cos - (y - cy) * sin + cx;
@@ -1851,21 +1851,21 @@ function areSamePoints(p1, p2) {
1851
1851
  return p1[0] === p2[0] && p1[1] === p2[1];
1852
1852
  }
1853
1853
  function hachureLines(polygons, hachureGap, hachureAngle, hachureStepOffset = 1) {
1854
- const angle = hachureAngle;
1854
+ const angle2 = hachureAngle;
1855
1855
  const gap = Math.max(hachureGap, 0.1);
1856
1856
  const polygonList = polygons[0] && polygons[0][0] && typeof polygons[0][0] === "number" ? [polygons] : polygons;
1857
1857
  const rotationCenter = [0, 0];
1858
- if (angle) {
1858
+ if (angle2) {
1859
1859
  for (const polygon3 of polygonList) {
1860
- rotatePoints(polygon3, rotationCenter, angle);
1860
+ rotatePoints(polygon3, rotationCenter, angle2);
1861
1861
  }
1862
1862
  }
1863
1863
  const lines = straightHachureLines(polygonList, gap, hachureStepOffset);
1864
- if (angle) {
1864
+ if (angle2) {
1865
1865
  for (const polygon3 of polygonList) {
1866
- rotatePoints(polygon3, rotationCenter, -angle);
1866
+ rotatePoints(polygon3, rotationCenter, -angle2);
1867
1867
  }
1868
- rotateLines(lines, rotationCenter, -angle);
1868
+ rotateLines(lines, rotationCenter, -angle2);
1869
1869
  }
1870
1870
  return lines;
1871
1871
  }
@@ -1976,7 +1976,7 @@ function straightHachureLines(polygons, gap, hachureStepOffset) {
1976
1976
  // ../../node_modules/roughjs/bin/fillers/scan-line-hachure.js
1977
1977
  function polygonHachureLines(polygonList, o) {
1978
1978
  var _a;
1979
- const angle = o.hachureAngle + 90;
1979
+ const angle2 = o.hachureAngle + 90;
1980
1980
  let gap = o.hachureGap;
1981
1981
  if (gap < 0) {
1982
1982
  gap = o.strokeWidth * 4;
@@ -1988,7 +1988,7 @@ function polygonHachureLines(polygonList, o) {
1988
1988
  skipOffset = gap;
1989
1989
  }
1990
1990
  }
1991
- return hachureLines(polygonList, gap, angle, skipOffset || 1);
1991
+ return hachureLines(polygonList, gap, angle2, skipOffset || 1);
1992
1992
  }
1993
1993
 
1994
1994
  // ../../node_modules/roughjs/bin/fillers/hachure-filler.js
@@ -2526,7 +2526,7 @@ function normalize(segments) {
2526
2526
  case "A": {
2527
2527
  const r1 = Math.abs(data[0]);
2528
2528
  const r2 = Math.abs(data[1]);
2529
- const angle = data[2];
2529
+ const angle2 = data[2];
2530
2530
  const largeArcFlag = data[3];
2531
2531
  const sweepFlag = data[4];
2532
2532
  const x = data[5];
@@ -2537,7 +2537,7 @@ function normalize(segments) {
2537
2537
  cy = y;
2538
2538
  } else {
2539
2539
  if (cx !== x || cy !== y) {
2540
- const curves = arcToCubicCurves(cx, cy, x, y, r1, r2, angle, largeArcFlag, sweepFlag);
2540
+ const curves = arcToCubicCurves(cx, cy, x, y, r1, r2, angle2, largeArcFlag, sweepFlag);
2541
2541
  curves.forEach(function(curve4) {
2542
2542
  out.push({ key: "C", data: curve4 });
2543
2543
  });
@@ -2565,8 +2565,8 @@ function rotate(x, y, angleRad) {
2565
2565
  const Y = x * Math.sin(angleRad) + y * Math.cos(angleRad);
2566
2566
  return [X, Y];
2567
2567
  }
2568
- function arcToCubicCurves(x1, y1, x2, y2, r1, r2, angle, largeArcFlag, sweepFlag, recursive) {
2569
- const angleRad = degToRad(angle);
2568
+ function arcToCubicCurves(x1, y1, x2, y2, r1, r2, angle2, largeArcFlag, sweepFlag, recursive) {
2569
+ const angleRad = degToRad(angle2);
2570
2570
  let params = [];
2571
2571
  let f1 = 0, f2 = 0, cx = 0, cy = 0;
2572
2572
  if (recursive) {
@@ -2623,7 +2623,7 @@ function arcToCubicCurves(x1, y1, x2, y2, r1, r2, angle, largeArcFlag, sweepFlag
2623
2623
  }
2624
2624
  x2 = cx + r1 * Math.cos(f2);
2625
2625
  y2 = cy + r2 * Math.sin(f2);
2626
- params = arcToCubicCurves(x2, y2, x2old, y2old, r1, r2, angle, 0, sweepFlag, [f2, f2old, cx, cy]);
2626
+ params = arcToCubicCurves(x2, y2, x2old, y2old, r1, r2, angle2, 0, sweepFlag, [f2, f2old, cx, cy]);
2627
2627
  }
2628
2628
  df = f2 - f1;
2629
2629
  const c1 = Math.cos(f1);
@@ -2829,8 +2829,8 @@ function patternFillArc(x, y, width, height, start2, stop2, o) {
2829
2829
  }
2830
2830
  const increment = (stp - strt) / o.curveStepCount;
2831
2831
  const points = [];
2832
- for (let angle = strt; angle <= stp; angle = angle + increment) {
2833
- points.push([cx + rx * Math.cos(angle), cy + ry * Math.sin(angle)]);
2832
+ for (let angle2 = strt; angle2 <= stp; angle2 = angle2 + increment) {
2833
+ points.push([cx + rx * Math.cos(angle2), cy + ry * Math.sin(angle2)]);
2834
2834
  }
2835
2835
  points.push([cx + rx * Math.cos(stp), cy + ry * Math.sin(stp)]);
2836
2836
  points.push([cx, cy]);
@@ -3016,10 +3016,10 @@ function _computeEllipsePoints(increment, cx, cy, rx, ry, offset, overlap, o) {
3016
3016
  cx + rx * Math.cos(-increment),
3017
3017
  cy + ry * Math.sin(-increment)
3018
3018
  ]);
3019
- for (let angle = 0; angle <= Math.PI * 2; angle = angle + increment) {
3019
+ for (let angle2 = 0; angle2 <= Math.PI * 2; angle2 = angle2 + increment) {
3020
3020
  const p = [
3021
- cx + rx * Math.cos(angle),
3022
- cy + ry * Math.sin(angle)
3021
+ cx + rx * Math.cos(angle2),
3022
+ cy + ry * Math.sin(angle2)
3023
3023
  ];
3024
3024
  corePoints.push(p);
3025
3025
  allPoints.push(p);
@@ -3039,10 +3039,10 @@ function _computeEllipsePoints(increment, cx, cy, rx, ry, offset, overlap, o) {
3039
3039
  _offsetOpt(offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment)
3040
3040
  ]);
3041
3041
  const endAngle = Math.PI * 2 + radOffset - 0.01;
3042
- for (let angle = radOffset; angle < endAngle; angle = angle + increment) {
3042
+ for (let angle2 = radOffset; angle2 < endAngle; angle2 = angle2 + increment) {
3043
3043
  const p = [
3044
- _offsetOpt(offset, o) + cx + rx * Math.cos(angle),
3045
- _offsetOpt(offset, o) + cy + ry * Math.sin(angle)
3044
+ _offsetOpt(offset, o) + cx + rx * Math.cos(angle2),
3045
+ _offsetOpt(offset, o) + cy + ry * Math.sin(angle2)
3046
3046
  ];
3047
3047
  corePoints.push(p);
3048
3048
  allPoints.push(p);
@@ -3069,10 +3069,10 @@ function _arc(increment, cx, cy, rx, ry, strt, stp, offset, o) {
3069
3069
  _offsetOpt(offset, o) + cx + 0.9 * rx * Math.cos(radOffset - increment),
3070
3070
  _offsetOpt(offset, o) + cy + 0.9 * ry * Math.sin(radOffset - increment)
3071
3071
  ]);
3072
- for (let angle = radOffset; angle <= stp; angle = angle + increment) {
3072
+ for (let angle2 = radOffset; angle2 <= stp; angle2 = angle2 + increment) {
3073
3073
  points.push([
3074
- _offsetOpt(offset, o) + cx + rx * Math.cos(angle),
3075
- _offsetOpt(offset, o) + cy + ry * Math.sin(angle)
3074
+ _offsetOpt(offset, o) + cx + rx * Math.cos(angle2),
3075
+ _offsetOpt(offset, o) + cy + ry * Math.sin(angle2)
3076
3076
  ]);
3077
3077
  }
3078
3078
  points.push([
@@ -3898,24 +3898,24 @@ import {
3898
3898
  } from "@excalidraw/math";
3899
3899
  import { getElementAbsoluteCoords } from "@excalidraw/element";
3900
3900
  var getPolygonShape = (element) => {
3901
- const { angle, width, height, x, y } = element;
3901
+ const { angle: angle2, width, height, x, y } = element;
3902
3902
  const cx = x + width / 2;
3903
3903
  const cy = y + height / 2;
3904
3904
  const center = pointFrom(cx, cy);
3905
3905
  let data;
3906
3906
  if (element.type === "diamond") {
3907
3907
  data = polygon2(
3908
- pointRotateRads(pointFrom(cx, y), center, angle),
3909
- pointRotateRads(pointFrom(x + width, cy), center, angle),
3910
- pointRotateRads(pointFrom(cx, y + height), center, angle),
3911
- pointRotateRads(pointFrom(x, cy), center, angle)
3908
+ pointRotateRads(pointFrom(cx, y), center, angle2),
3909
+ pointRotateRads(pointFrom(x + width, cy), center, angle2),
3910
+ pointRotateRads(pointFrom(cx, y + height), center, angle2),
3911
+ pointRotateRads(pointFrom(x, cy), center, angle2)
3912
3912
  );
3913
3913
  } else {
3914
3914
  data = polygon2(
3915
- pointRotateRads(pointFrom(x, y), center, angle),
3916
- pointRotateRads(pointFrom(x + width, y), center, angle),
3917
- pointRotateRads(pointFrom(x + width, y + height), center, angle),
3918
- pointRotateRads(pointFrom(x, y + height), center, angle)
3915
+ pointRotateRads(pointFrom(x, y), center, angle2),
3916
+ pointRotateRads(pointFrom(x + width, y), center, angle2),
3917
+ pointRotateRads(pointFrom(x + width, y + height), center, angle2),
3918
+ pointRotateRads(pointFrom(x, y + height), center, angle2)
3919
3919
  );
3920
3920
  }
3921
3921
  return {
@@ -3924,12 +3924,12 @@ var getPolygonShape = (element) => {
3924
3924
  };
3925
3925
  };
3926
3926
  var getEllipseShape = (element) => {
3927
- const { width, height, angle, x, y } = element;
3927
+ const { width, height, angle: angle2, x, y } = element;
3928
3928
  return {
3929
3929
  type: "ellipse",
3930
3930
  data: {
3931
3931
  center: pointFrom(x + width / 2, y + height / 2),
3932
- angle,
3932
+ angle: angle2,
3933
3933
  halfWidth: width / 2,
3934
3934
  halfHeight: height / 2
3935
3935
  }
@@ -4213,6 +4213,362 @@ function ae(e, t = {}) {
4213
4213
  return ce(me(e, t), t);
4214
4214
  }
4215
4215
 
4216
+ // ../laser-pointer/src/index.ts
4217
+ init_define_import_meta_env();
4218
+
4219
+ // ../laser-pointer/src/state.ts
4220
+ init_define_import_meta_env();
4221
+
4222
+ // ../laser-pointer/src/math.ts
4223
+ init_define_import_meta_env();
4224
+ function add([ax, ay, ar], [bx, by, br]) {
4225
+ return [ax + bx, ay + by, ar + br];
4226
+ }
4227
+ function sub([ax, ay, ar], [bx, by, br]) {
4228
+ return [ax - bx, ay - by, ar - br];
4229
+ }
4230
+ function smul([x, y, r], s) {
4231
+ return [x * s, y * s, r * s];
4232
+ }
4233
+ function norm([x, y, r]) {
4234
+ return [x / Math.sqrt(x ** 2 + y ** 2), y / Math.sqrt(x ** 2 + y ** 2), r];
4235
+ }
4236
+ function rot([x, y, r], rad) {
4237
+ return [
4238
+ Math.cos(rad) * x - Math.sin(rad) * y,
4239
+ Math.sin(rad) * x + Math.cos(rad) * y,
4240
+ r
4241
+ ];
4242
+ }
4243
+ function plerp(a2, b2, t) {
4244
+ return add(a2, smul(sub(b2, a2), t));
4245
+ }
4246
+ function angle(p, p1, p2) {
4247
+ return Math.atan2(p2[1] - p[1], p2[0] - p[0]) - Math.atan2(p1[1] - p[1], p1[0] - p[0]);
4248
+ }
4249
+ function normAngle(a2) {
4250
+ return Math.atan2(Math.sin(a2), Math.cos(a2));
4251
+ }
4252
+ function mag([x, y]) {
4253
+ return Math.sqrt(x ** 2 + y ** 2);
4254
+ }
4255
+ function dist([ax, ay], [bx, by]) {
4256
+ return Math.sqrt((bx - ax) ** 2 + (by - ay) ** 2);
4257
+ }
4258
+ function runLength(ps) {
4259
+ if (ps.length < 2) {
4260
+ return 0;
4261
+ }
4262
+ let len = 0;
4263
+ for (let i = 1; i <= ps.length - 1; i++) {
4264
+ len += dist(ps[i - 1], ps[i]);
4265
+ }
4266
+ len += dist(ps[ps.length - 2], ps[ps.length - 1]);
4267
+ return len;
4268
+ }
4269
+ var clamp = (v, min, max) => Math.max(min, Math.min(max, v));
4270
+ function distancePointToSegment(p3, p1, p2) {
4271
+ const sMag = dist(p1, p2);
4272
+ if (sMag === 0) {
4273
+ return dist(p3, p1);
4274
+ }
4275
+ const u = clamp(
4276
+ ((p3[0] - p1[0]) * (p2[0] - p1[0]) + (p3[1] - p1[1]) * (p2[1] - p1[1])) / sMag ** 2,
4277
+ 0,
4278
+ 1
4279
+ );
4280
+ const pi = [
4281
+ p1[0] + u * (p2[0] - p1[0]),
4282
+ p1[1] + u * (p2[1] - p1[1]),
4283
+ p3[2]
4284
+ ];
4285
+ return dist(pi, p3);
4286
+ }
4287
+
4288
+ // ../laser-pointer/src/simplify.ts
4289
+ init_define_import_meta_env();
4290
+ function douglasPeucker(points, epsilon) {
4291
+ if (epsilon === 0) {
4292
+ return points;
4293
+ }
4294
+ if (points.length <= 2) {
4295
+ return points;
4296
+ }
4297
+ const first = points[0];
4298
+ const last = points[points.length - 1];
4299
+ const [maxDistance, maxIndex] = points.reduce(
4300
+ ([maxDistance2, maxIndex2], point, index) => {
4301
+ const distance3 = distancePointToSegment(point, first, last);
4302
+ return distance3 > maxDistance2 ? [distance3, index] : [maxDistance2, maxIndex2];
4303
+ },
4304
+ [0, -1]
4305
+ );
4306
+ if (maxDistance >= epsilon) {
4307
+ const maxIndexPoint = points[maxIndex];
4308
+ return [
4309
+ ...douglasPeucker(
4310
+ [first, ...points.slice(1, maxIndex), maxIndexPoint],
4311
+ epsilon
4312
+ ).slice(0, -1),
4313
+ maxIndexPoint,
4314
+ ...douglasPeucker(
4315
+ [maxIndexPoint, ...points.slice(maxIndex, -1), last],
4316
+ epsilon
4317
+ ).slice(1)
4318
+ ];
4319
+ }
4320
+ return [first, last];
4321
+ }
4322
+
4323
+ // ../laser-pointer/src/state.ts
4324
+ var LaserPointer = class _LaserPointer {
4325
+ static defaults = {
4326
+ size: 2,
4327
+ streamline: 0.45,
4328
+ simplify: 0.1,
4329
+ simplifyPhase: "output",
4330
+ keepHead: false,
4331
+ sizeMapping: () => 1
4332
+ };
4333
+ static constants = {
4334
+ cornerDetectionMaxAngle: 75,
4335
+ cornerDetectionVariance: (s) => s > 35 ? 0.5 : 1,
4336
+ maxTailLength: 50
4337
+ };
4338
+ options;
4339
+ constructor(options) {
4340
+ this.options = Object.assign({}, _LaserPointer.defaults, options);
4341
+ }
4342
+ originalPoints = [];
4343
+ stablePoints = [];
4344
+ tailPoints = [];
4345
+ isFresh = true;
4346
+ get lastPoint() {
4347
+ return this.tailPoints[this.tailPoints.length - 1] ?? this.stablePoints[this.stablePoints.length - 1];
4348
+ }
4349
+ addPoint(point) {
4350
+ const lastPoint = this.originalPoints[this.originalPoints.length - 1];
4351
+ if (lastPoint && lastPoint[0] === point[0] && lastPoint[1] === point[1]) {
4352
+ return;
4353
+ }
4354
+ this.originalPoints.push(point);
4355
+ if (this.isFresh) {
4356
+ this.isFresh = false;
4357
+ this.stablePoints.push(point);
4358
+ return;
4359
+ }
4360
+ if (this.options.streamline > 0) {
4361
+ point = plerp(this.lastPoint, point, 1 - this.options.streamline);
4362
+ }
4363
+ this.tailPoints.push(point);
4364
+ if (runLength(this.tailPoints) > _LaserPointer.constants.maxTailLength) {
4365
+ this.stabilizeTail();
4366
+ }
4367
+ }
4368
+ close() {
4369
+ this.stabilizeTail();
4370
+ }
4371
+ stabilizeTail() {
4372
+ if (this.options.simplify > 0 && this.options.simplifyPhase === "tail") {
4373
+ throw new Error("Not implemented yet");
4374
+ } else {
4375
+ this.stablePoints.push(...this.tailPoints);
4376
+ this.tailPoints = [];
4377
+ }
4378
+ }
4379
+ getSize(sizeOverride, pressure, index, totalLength, runningLength) {
4380
+ return (sizeOverride ?? this.options.size) * this.options.sizeMapping({
4381
+ pressure,
4382
+ runningLength,
4383
+ currentIndex: index,
4384
+ totalLength
4385
+ });
4386
+ }
4387
+ getStrokeOutline(sizeOverride) {
4388
+ if (this.isFresh) {
4389
+ return [];
4390
+ }
4391
+ let points = [...this.stablePoints, ...this.tailPoints];
4392
+ if (this.options.simplify > 0 && this.options.simplifyPhase === "input") {
4393
+ points = douglasPeucker(points, this.options.simplify);
4394
+ }
4395
+ const len = points.length;
4396
+ if (len === 0) {
4397
+ return [];
4398
+ }
4399
+ if (len === 1) {
4400
+ const c = points[0];
4401
+ const size = this.getSize(sizeOverride, c[2], 0, len, 0);
4402
+ if (size < 0.5) {
4403
+ return [];
4404
+ }
4405
+ const ps = [];
4406
+ for (let theta = 0; theta <= Math.PI * 2; theta += Math.PI / 16) {
4407
+ ps.push(add(c, smul(rot([1, 0, 0], theta), size)));
4408
+ }
4409
+ ps.push(
4410
+ add(
4411
+ c,
4412
+ smul(
4413
+ [1, 0, 0],
4414
+ this.getSize(sizeOverride, c[2], 0, len, 0)
4415
+ )
4416
+ )
4417
+ );
4418
+ return ps;
4419
+ }
4420
+ if (len === 2) {
4421
+ const c = points[0];
4422
+ const n = points[1];
4423
+ const cSize = this.getSize(sizeOverride, c[2], 0, len, 0);
4424
+ const nSize = this.getSize(sizeOverride, n[2], 0, len, 0);
4425
+ if (cSize < 0.5 || nSize < 0.5) {
4426
+ return [];
4427
+ }
4428
+ const ps = [];
4429
+ const pAngle = angle(c, [c[0], c[1] - 100, c[2]], n);
4430
+ for (let theta = pAngle; theta <= Math.PI + pAngle; theta += Math.PI / 16) {
4431
+ ps.push(add(c, smul(rot([1, 0, 0], theta), cSize)));
4432
+ }
4433
+ for (let theta = Math.PI + pAngle; theta <= Math.PI * 2 + pAngle; theta += Math.PI / 16) {
4434
+ ps.push(add(n, smul(rot([1, 0, 0], theta), nSize)));
4435
+ }
4436
+ ps.push(ps[0]);
4437
+ return ps;
4438
+ }
4439
+ const forwardPoints = [];
4440
+ const backwardPoints = [];
4441
+ let speed = 0;
4442
+ let prevSpeed = 0;
4443
+ let visibleStartIndex = 0;
4444
+ let runningLength = 0;
4445
+ for (let i = 1; i < len - 1; i++) {
4446
+ const p = points[i - 1];
4447
+ const c = points[i];
4448
+ const n = points[i + 1];
4449
+ const pressure = c[2];
4450
+ const d = dist(p, c);
4451
+ runningLength += d;
4452
+ speed = prevSpeed + (d - prevSpeed) * 0.2;
4453
+ const cSize = this.getSize(sizeOverride, pressure, i, len, runningLength);
4454
+ if (cSize === 0) {
4455
+ visibleStartIndex = i + 1;
4456
+ continue;
4457
+ }
4458
+ const dirPC = norm(sub(p, c));
4459
+ const dirNC = norm(sub(n, c));
4460
+ const p1dirPC = rot(dirPC, Math.PI / 2);
4461
+ const p2dirPC = rot(dirPC, -Math.PI / 2);
4462
+ const p1dirNC = rot(dirNC, Math.PI / 2);
4463
+ const p2dirNC = rot(dirNC, -Math.PI / 2);
4464
+ const p1PC = add(c, smul(p1dirPC, cSize));
4465
+ const p2PC = add(c, smul(p2dirPC, cSize));
4466
+ const p1NC = add(c, smul(p1dirNC, cSize));
4467
+ const p2NC = add(c, smul(p2dirNC, cSize));
4468
+ const ftdir = add(p1dirPC, p2dirNC);
4469
+ const btdir = add(p2dirPC, p1dirNC);
4470
+ const paPC = add(
4471
+ c,
4472
+ smul(mag(ftdir) === 0 ? dirPC : norm(ftdir), cSize)
4473
+ );
4474
+ const paNC = add(
4475
+ c,
4476
+ smul(mag(btdir) === 0 ? dirNC : norm(btdir), cSize)
4477
+ );
4478
+ const cAngle = normAngle(angle(c, p, n));
4479
+ const D_ANGLE = _LaserPointer.constants.cornerDetectionMaxAngle / 180 * Math.PI * _LaserPointer.constants.cornerDetectionVariance(speed);
4480
+ if (Math.abs(cAngle) < D_ANGLE) {
4481
+ const tAngle = Math.abs(normAngle(Math.PI - cAngle));
4482
+ if (tAngle === 0) {
4483
+ continue;
4484
+ }
4485
+ if (cAngle < 0) {
4486
+ backwardPoints.push(p2PC, paNC);
4487
+ for (let theta = 0; theta <= tAngle; theta += tAngle / 4) {
4488
+ forwardPoints.push(add(c, rot(smul(p1dirPC, cSize), theta)));
4489
+ }
4490
+ for (let theta = tAngle; theta >= 0; theta -= tAngle / 4) {
4491
+ backwardPoints.push(add(c, rot(smul(p1dirPC, cSize), theta)));
4492
+ }
4493
+ backwardPoints.push(paNC, p1NC);
4494
+ } else {
4495
+ forwardPoints.push(p1PC, paPC);
4496
+ for (let theta = 0; theta <= tAngle; theta += tAngle / 4) {
4497
+ backwardPoints.push(
4498
+ add(c, rot(smul(p1dirPC, -cSize), -theta))
4499
+ );
4500
+ }
4501
+ for (let theta = tAngle; theta >= 0; theta -= tAngle / 4) {
4502
+ forwardPoints.push(
4503
+ add(c, rot(smul(p1dirPC, -cSize), -theta))
4504
+ );
4505
+ }
4506
+ forwardPoints.push(paPC, p2NC);
4507
+ }
4508
+ } else {
4509
+ forwardPoints.push(paPC);
4510
+ backwardPoints.push(paNC);
4511
+ }
4512
+ prevSpeed = speed;
4513
+ }
4514
+ if (visibleStartIndex >= len - 2) {
4515
+ if (this.options.keepHead) {
4516
+ const c = points[len - 1];
4517
+ const ps = [];
4518
+ for (let theta = 0; theta <= Math.PI * 2; theta += Math.PI / 16) {
4519
+ ps.push(
4520
+ add(
4521
+ c,
4522
+ smul(rot([1, 0, 0], theta), this.options.size)
4523
+ )
4524
+ );
4525
+ }
4526
+ ps.push(add(c, smul([1, 0, 0], this.options.size)));
4527
+ return ps;
4528
+ }
4529
+ return [];
4530
+ }
4531
+ const first = points[visibleStartIndex];
4532
+ const second = points[visibleStartIndex + 1];
4533
+ const penultimate = points[len - 2];
4534
+ const ultimate = points[len - 1];
4535
+ const dirFS = norm(sub(second, first));
4536
+ const dirPU = norm(sub(penultimate, ultimate));
4537
+ const ppdirFS = rot(dirFS, -Math.PI / 2);
4538
+ const ppdirPU = rot(dirPU, Math.PI / 2);
4539
+ const startCapSize = this.getSize(sizeOverride, first[2], 0, len, 0);
4540
+ const startCap = [];
4541
+ const endCapSize = this.options.keepHead ? this.options.size : this.getSize(sizeOverride, penultimate[2], len - 2, len, runningLength);
4542
+ const endCap = [];
4543
+ if (startCapSize > 0.1) {
4544
+ for (let theta = 0; theta <= Math.PI; theta += Math.PI / 16) {
4545
+ startCap.unshift(
4546
+ add(first, rot(smul(ppdirFS, startCapSize), -theta))
4547
+ );
4548
+ }
4549
+ startCap.unshift(add(first, smul(ppdirFS, -startCapSize)));
4550
+ } else {
4551
+ startCap.push(first);
4552
+ }
4553
+ for (let theta = 0; theta <= Math.PI * 3; theta += Math.PI / 16) {
4554
+ endCap.push(add(ultimate, rot(smul(ppdirPU, -endCapSize), -theta)));
4555
+ }
4556
+ const strokeOutline = [
4557
+ ...startCap,
4558
+ ...forwardPoints,
4559
+ ...endCap.reverse(),
4560
+ ...backwardPoints.reverse()
4561
+ ];
4562
+ if (startCap.length > 0) {
4563
+ strokeOutline.push(startCap[0]);
4564
+ }
4565
+ if (this.options.simplify > 0 && this.options.simplifyPhase === "output") {
4566
+ return douglasPeucker(strokeOutline, this.options.simplify);
4567
+ }
4568
+ return strokeOutline;
4569
+ }
4570
+ };
4571
+
4216
4572
  // src/shape.ts
4217
4573
  import {
4218
4574
  pointFrom as pointFrom12,
@@ -4226,7 +4582,8 @@ import {
4226
4582
  assertNever as assertNever2,
4227
4583
  COLOR_PALETTE,
4228
4584
  LINE_POLYGON_POINT_MERGE_DISTANCE,
4229
- applyDarkModeFilter as applyDarkModeFilter2
4585
+ applyDarkModeFilter as applyDarkModeFilter2,
4586
+ DEFAULT_STROKE_STREAMLINE
4230
4587
  } from "@excalidraw/common";
4231
4588
 
4232
4589
  // src/renderElement.ts
@@ -4243,7 +4600,7 @@ import {
4243
4600
  ELEMENT_READY_TO_ERASE_OPACITY,
4244
4601
  FRAME_STYLE,
4245
4602
  DARK_THEME_FILTER,
4246
- MIME_TYPES,
4603
+ MIME_TYPES as MIME_TYPES2,
4247
4604
  THEME,
4248
4605
  distance as distance2,
4249
4606
  getFontString as getFontString3,
@@ -4266,7 +4623,7 @@ import {
4266
4623
  vectorAdd as vectorAdd2,
4267
4624
  vectorScale as vectorScale2,
4268
4625
  pointFromVector as pointFromVector2,
4269
- clamp,
4626
+ clamp as clamp2,
4270
4627
  isCloseTo
4271
4628
  } from "@excalidraw/math";
4272
4629
  var MINIMAL_CROP_SIZE = 10;
@@ -4300,7 +4657,7 @@ var cropElement = (element, elementsMap, transformHandle, naturalWidth, naturalH
4300
4657
  let changeInHeight = pointerY - element.y;
4301
4658
  let changeInWidth = pointerX - element.x;
4302
4659
  if (transformHandle.includes("n")) {
4303
- nextHeight = clamp(
4660
+ nextHeight = clamp2(
4304
4661
  element.height - changeInHeight,
4305
4662
  MINIMAL_CROP_SIZE,
4306
4663
  isFlippedByY ? uncroppedHeight - croppedTop : element.height + croppedTop
@@ -4308,7 +4665,7 @@ var cropElement = (element, elementsMap, transformHandle, naturalWidth, naturalH
4308
4665
  }
4309
4666
  if (transformHandle.includes("s")) {
4310
4667
  changeInHeight = pointerY - element.y - element.height;
4311
- nextHeight = clamp(
4668
+ nextHeight = clamp2(
4312
4669
  element.height + changeInHeight,
4313
4670
  MINIMAL_CROP_SIZE,
4314
4671
  isFlippedByY ? element.height + croppedTop : uncroppedHeight - croppedTop
@@ -4316,14 +4673,14 @@ var cropElement = (element, elementsMap, transformHandle, naturalWidth, naturalH
4316
4673
  }
4317
4674
  if (transformHandle.includes("e")) {
4318
4675
  changeInWidth = pointerX - element.x - element.width;
4319
- nextWidth = clamp(
4676
+ nextWidth = clamp2(
4320
4677
  element.width + changeInWidth,
4321
4678
  MINIMAL_CROP_SIZE,
4322
4679
  isFlippedByX ? element.width + croppedLeft : uncroppedWidth - croppedLeft
4323
4680
  );
4324
4681
  }
4325
4682
  if (transformHandle.includes("w")) {
4326
- nextWidth = clamp(
4683
+ nextWidth = clamp2(
4327
4684
  element.width - changeInWidth,
4328
4685
  MINIMAL_CROP_SIZE,
4329
4686
  isFlippedByX ? uncroppedWidth - croppedLeft : element.width + croppedLeft
@@ -4363,7 +4720,7 @@ var cropElement = (element, elementsMap, transformHandle, naturalWidth, naturalH
4363
4720
  const distanceToLeft = croppedLeft + element.width / 2;
4364
4721
  const distanceToRight = uncroppedWidth - croppedLeft - element.width / 2;
4365
4722
  const MAX_WIDTH = Math.min(distanceToLeft, distanceToRight) * 2;
4366
- nextWidth = clamp(
4723
+ nextWidth = clamp2(
4367
4724
  nextHeight * widthAspectRatio,
4368
4725
  MINIMAL_CROP_SIZE,
4369
4726
  MAX_WIDTH
@@ -4381,7 +4738,7 @@ var cropElement = (element, elementsMap, transformHandle, naturalWidth, naturalH
4381
4738
  const distanceToLeft = croppedLeft + element.width / 2;
4382
4739
  const distanceToRight = uncroppedWidth - croppedLeft - element.width / 2;
4383
4740
  const MAX_WIDTH = Math.min(distanceToLeft, distanceToRight) * 2;
4384
- nextWidth = clamp(
4741
+ nextWidth = clamp2(
4385
4742
  nextHeight * widthAspectRatio,
4386
4743
  MINIMAL_CROP_SIZE,
4387
4744
  MAX_WIDTH
@@ -4399,7 +4756,7 @@ var cropElement = (element, elementsMap, transformHandle, naturalWidth, naturalH
4399
4756
  const distanceToTop = croppedTop + element.height / 2;
4400
4757
  const distanceToBottom = uncroppedHeight - croppedTop - element.height / 2;
4401
4758
  const MAX_HEIGHT = Math.min(distanceToTop, distanceToBottom) * 2;
4402
- nextHeight = clamp(
4759
+ nextHeight = clamp2(
4403
4760
  nextWidth / widthAspectRatio,
4404
4761
  MINIMAL_CROP_SIZE,
4405
4762
  MAX_HEIGHT
@@ -4417,7 +4774,7 @@ var cropElement = (element, elementsMap, transformHandle, naturalWidth, naturalH
4417
4774
  const distanceToTop = croppedTop + element.height / 2;
4418
4775
  const distanceToBottom = uncroppedHeight - croppedTop - element.height / 2;
4419
4776
  const MAX_HEIGHT = Math.min(distanceToTop, distanceToBottom) * 2;
4420
- nextHeight = clamp(
4777
+ nextHeight = clamp2(
4421
4778
  nextWidth / widthAspectRatio,
4422
4779
  MINIMAL_CROP_SIZE,
4423
4780
  MAX_HEIGHT
@@ -4434,7 +4791,7 @@ var cropElement = (element, elementsMap, transformHandle, naturalWidth, naturalH
4434
4791
  if (widthAspectRatio) {
4435
4792
  if (changeInWidth > -changeInHeight) {
4436
4793
  const MAX_HEIGHT = isFlippedByY ? uncroppedHeight - croppedTop : croppedTop + element.height;
4437
- nextHeight = clamp(
4794
+ nextHeight = clamp2(
4438
4795
  nextWidth / widthAspectRatio,
4439
4796
  MINIMAL_CROP_SIZE,
4440
4797
  MAX_HEIGHT
@@ -4442,7 +4799,7 @@ var cropElement = (element, elementsMap, transformHandle, naturalWidth, naturalH
4442
4799
  nextWidth = nextHeight * widthAspectRatio;
4443
4800
  } else {
4444
4801
  const MAX_WIDTH = isFlippedByX ? croppedLeft + element.width : uncroppedWidth - croppedLeft;
4445
- nextWidth = clamp(
4802
+ nextWidth = clamp2(
4446
4803
  nextHeight * widthAspectRatio,
4447
4804
  MINIMAL_CROP_SIZE,
4448
4805
  MAX_WIDTH
@@ -4457,7 +4814,7 @@ var cropElement = (element, elementsMap, transformHandle, naturalWidth, naturalH
4457
4814
  if (widthAspectRatio) {
4458
4815
  if (changeInWidth < changeInHeight) {
4459
4816
  const MAX_HEIGHT = isFlippedByY ? uncroppedHeight - croppedTop : croppedTop + element.height;
4460
- nextHeight = clamp(
4817
+ nextHeight = clamp2(
4461
4818
  nextWidth / widthAspectRatio,
4462
4819
  MINIMAL_CROP_SIZE,
4463
4820
  MAX_HEIGHT
@@ -4465,7 +4822,7 @@ var cropElement = (element, elementsMap, transformHandle, naturalWidth, naturalH
4465
4822
  nextWidth = nextHeight * widthAspectRatio;
4466
4823
  } else {
4467
4824
  const MAX_WIDTH = isFlippedByX ? uncroppedWidth - croppedLeft : croppedLeft + element.width;
4468
- nextWidth = clamp(
4825
+ nextWidth = clamp2(
4469
4826
  nextHeight * widthAspectRatio,
4470
4827
  MINIMAL_CROP_SIZE,
4471
4828
  MAX_WIDTH
@@ -4480,7 +4837,7 @@ var cropElement = (element, elementsMap, transformHandle, naturalWidth, naturalH
4480
4837
  if (widthAspectRatio) {
4481
4838
  if (changeInWidth > changeInHeight) {
4482
4839
  const MAX_HEIGHT = isFlippedByY ? croppedTop + element.height : uncroppedHeight - croppedTop;
4483
- nextHeight = clamp(
4840
+ nextHeight = clamp2(
4484
4841
  nextWidth / widthAspectRatio,
4485
4842
  MINIMAL_CROP_SIZE,
4486
4843
  MAX_HEIGHT
@@ -4488,7 +4845,7 @@ var cropElement = (element, elementsMap, transformHandle, naturalWidth, naturalH
4488
4845
  nextWidth = nextHeight * widthAspectRatio;
4489
4846
  } else {
4490
4847
  const MAX_WIDTH = isFlippedByX ? croppedLeft + element.width : uncroppedWidth - croppedLeft;
4491
- nextWidth = clamp(
4848
+ nextWidth = clamp2(
4492
4849
  nextHeight * widthAspectRatio,
4493
4850
  MINIMAL_CROP_SIZE,
4494
4851
  MAX_WIDTH
@@ -4503,7 +4860,7 @@ var cropElement = (element, elementsMap, transformHandle, naturalWidth, naturalH
4503
4860
  if (widthAspectRatio) {
4504
4861
  if (-changeInWidth > changeInHeight) {
4505
4862
  const MAX_HEIGHT = isFlippedByY ? croppedTop + element.height : uncroppedHeight - croppedTop;
4506
- nextHeight = clamp(
4863
+ nextHeight = clamp2(
4507
4864
  nextWidth / widthAspectRatio,
4508
4865
  MINIMAL_CROP_SIZE,
4509
4866
  MAX_HEIGHT
@@ -4511,7 +4868,7 @@ var cropElement = (element, elementsMap, transformHandle, naturalWidth, naturalH
4511
4868
  nextWidth = nextHeight * widthAspectRatio;
4512
4869
  } else {
4513
4870
  const MAX_WIDTH = isFlippedByX ? uncroppedWidth - croppedLeft : croppedLeft + element.width;
4514
- nextWidth = clamp(
4871
+ nextWidth = clamp2(
4515
4872
  nextHeight * widthAspectRatio,
4516
4873
  MINIMAL_CROP_SIZE,
4517
4874
  MAX_WIDTH
@@ -4579,17 +4936,17 @@ var recomputeOrigin = (stateAtCropStart, transformHandle, width, height, shouldM
4579
4936
  newTopLeft[1] = startCenter[1] - newBoundsHeight / 2;
4580
4937
  }
4581
4938
  }
4582
- const angle = stateAtCropStart.angle;
4583
- const rotatedTopLeft = pointRotateRads2(newTopLeft, startCenter, angle);
4939
+ const angle2 = stateAtCropStart.angle;
4940
+ const rotatedTopLeft = pointRotateRads2(newTopLeft, startCenter, angle2);
4584
4941
  const newCenter = [
4585
4942
  newTopLeft[0] + Math.abs(newBoundsWidth) / 2,
4586
4943
  newTopLeft[1] + Math.abs(newBoundsHeight) / 2
4587
4944
  ];
4588
- const rotatedNewCenter = pointRotateRads2(newCenter, startCenter, angle);
4945
+ const rotatedNewCenter = pointRotateRads2(newCenter, startCenter, angle2);
4589
4946
  newTopLeft = pointRotateRads2(
4590
4947
  rotatedTopLeft,
4591
4948
  rotatedNewCenter,
4592
- -angle
4949
+ -angle2
4593
4950
  );
4594
4951
  const newOrigin = [...newTopLeft];
4595
4952
  newOrigin[0] += stateAtCropStart.x - newBoundsX1;
@@ -4744,7 +5101,7 @@ import {
4744
5101
  } from "@excalidraw/common";
4745
5102
  import {
4746
5103
  PRECISION as PRECISION2,
4747
- clamp as clamp3,
5104
+ clamp as clamp4,
4748
5105
  lineSegment as lineSegment4,
4749
5106
  pointDistance as pointDistance4,
4750
5107
  pointDistanceSq,
@@ -6290,8 +6647,8 @@ var computeBoundTextPosition = (container, boundTextElement, elementsMap) => {
6290
6647
  } else {
6291
6648
  x = containerCoords.x + (maxContainerWidth / 2 - boundTextElement.width / 2);
6292
6649
  }
6293
- const angle = container.angle ?? 0;
6294
- if (angle !== 0) {
6650
+ const angle2 = container.angle ?? 0;
6651
+ if (angle2 !== 0) {
6295
6652
  const contentCenter = pointFrom4(
6296
6653
  containerCoords.x + maxContainerWidth / 2,
6297
6654
  containerCoords.y + maxContainerHeight / 2
@@ -6300,7 +6657,7 @@ var computeBoundTextPosition = (container, boundTextElement, elementsMap) => {
6300
6657
  x + boundTextElement.width / 2,
6301
6658
  y + boundTextElement.height / 2
6302
6659
  );
6303
- const [rx, ry] = pointRotateRads4(textCenter, contentCenter, angle);
6660
+ const [rx, ry] = pointRotateRads4(textCenter, contentCenter, angle2);
6304
6661
  return {
6305
6662
  x: rx - boundTextElement.width / 2,
6306
6663
  y: ry - boundTextElement.height / 2
@@ -6557,6 +6914,7 @@ var hasBackground = (type) => type === "rectangle" || type === "iframe" || type
6557
6914
  var hasStrokeColor = (type) => type === "rectangle" || type === "ellipse" || type === "diamond" || type === "freedraw" || type === "arrow" || type === "line" || type === "text" || type === "embeddable";
6558
6915
  var hasStrokeWidth = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "freedraw" || type === "arrow" || type === "line";
6559
6916
  var hasStrokeStyle = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "ellipse" || type === "diamond" || type === "arrow" || type === "line";
6917
+ var hasFreedrawMode = (type) => type === "freedraw";
6560
6918
  var canChangeRoundness = (type) => type === "rectangle" || type === "iframe" || type === "embeddable" || type === "line" || type === "diamond" || type === "image";
6561
6919
  var toolIsArrow = (type) => type === "arrow";
6562
6920
  var canHaveArrowheads = (type) => type === "arrow";
@@ -6625,8 +6983,8 @@ var hitElementItself = ({
6625
6983
  cachedHit = result;
6626
6984
  return result;
6627
6985
  };
6628
- var isPointInRotatedBounds = (point, bounds, angle, tolerance = 0) => {
6629
- const adjustedPoint = angle === 0 ? point : pointRotateRads6(point, getCenterForBounds(bounds), -angle);
6986
+ var isPointInRotatedBounds = (point, bounds, angle2, tolerance = 0) => {
6987
+ const adjustedPoint = angle2 === 0 ? point : pointRotateRads6(point, getCenterForBounds(bounds), -angle2);
6630
6988
  return isPointWithinBounds(
6631
6989
  pointFrom5(bounds[0] - tolerance, bounds[1] - tolerance),
6632
6990
  adjustedPoint,
@@ -6802,7 +7160,7 @@ var intersectElementWithLineSegment = (element, elementsMap, line2, offset = 0,
6802
7160
  );
6803
7161
  }
6804
7162
  };
6805
- var curveIntersections = (curves, segment, intersections, center, angle, onlyFirst = false) => {
7163
+ var curveIntersections = (curves, segment, intersections, center, angle2, onlyFirst = false) => {
6806
7164
  for (const c of curves) {
6807
7165
  const b1 = getCubicBezierCurveBound(c[0], c[1], c[2], c[3]);
6808
7166
  const b2 = [
@@ -6817,7 +7175,7 @@ var curveIntersections = (curves, segment, intersections, center, angle, onlyFir
6817
7175
  const hits = curveIntersectLineSegment(c, segment);
6818
7176
  if (hits.length > 0) {
6819
7177
  for (const j of hits) {
6820
- intersections.push(pointRotateRads6(j, center, angle));
7178
+ intersections.push(pointRotateRads6(j, center, angle2));
6821
7179
  }
6822
7180
  if (onlyFirst) {
6823
7181
  return intersections;
@@ -6826,11 +7184,11 @@ var curveIntersections = (curves, segment, intersections, center, angle, onlyFir
6826
7184
  }
6827
7185
  return intersections;
6828
7186
  };
6829
- var lineIntersections = (lines, segment, intersections, center, angle, onlyFirst = false) => {
7187
+ var lineIntersections = (lines, segment, intersections, center, angle2, onlyFirst = false) => {
6830
7188
  for (const l2 of lines) {
6831
7189
  const intersection = lineSegmentIntersectionPoints2(l2, segment);
6832
7190
  if (intersection) {
6833
- intersections.push(pointRotateRads6(intersection, center, angle));
7191
+ intersections.push(pointRotateRads6(intersection, center, angle2));
6834
7192
  if (onlyFirst) {
6835
7193
  return intersections;
6836
7194
  }
@@ -6976,7 +7334,7 @@ var isPointInElement = (point, element, elementsMap) => {
6976
7334
  };
6977
7335
  var isBindableElementInsideOtherBindable = (innerElement, outerElement, elementsMap) => {
6978
7336
  const getCornerPoints = (element, offset2) => {
6979
- const { x, y, width, height, angle } = element;
7337
+ const { x, y, width, height, angle: angle2 } = element;
6980
7338
  const center = elementCenterPoint(element, elementsMap);
6981
7339
  if (element.type === "diamond") {
6982
7340
  const [topX, topY, rightX, rightY, bottomX, bottomY, leftX, leftY] = getDiamondPoints(element);
@@ -6990,7 +7348,7 @@ var isBindableElementInsideOtherBindable = (innerElement, outerElement, elements
6990
7348
  pointFrom5(x + leftX - offset2, y + leftY)
6991
7349
  // left
6992
7350
  ];
6993
- return corners2.map((corner) => pointRotateRads6(corner, center, angle));
7351
+ return corners2.map((corner) => pointRotateRads6(corner, center, angle2));
6994
7352
  }
6995
7353
  if (element.type === "ellipse") {
6996
7354
  const cx = x + width / 2;
@@ -7007,7 +7365,7 @@ var isBindableElementInsideOtherBindable = (innerElement, outerElement, elements
7007
7365
  pointFrom5(cx - rx - offset2, cy)
7008
7366
  // left
7009
7367
  ];
7010
- return corners2.map((corner) => pointRotateRads6(corner, center, angle));
7368
+ return corners2.map((corner) => pointRotateRads6(corner, center, angle2));
7011
7369
  }
7012
7370
  const corners = [
7013
7371
  pointFrom5(x - offset2, y - offset2),
@@ -7019,7 +7377,7 @@ var isBindableElementInsideOtherBindable = (innerElement, outerElement, elements
7019
7377
  pointFrom5(x - offset2, y + height + offset2)
7020
7378
  // bottom-left
7021
7379
  ];
7022
- return corners.map((corner) => pointRotateRads6(corner, center, angle));
7380
+ return corners.map((corner) => pointRotateRads6(corner, center, angle2));
7023
7381
  };
7024
7382
  const offset = -1 * Math.max(innerElement.width, innerElement.height) / 20;
7025
7383
  const innerCorners = getCornerPoints(innerElement, offset);
@@ -7245,7 +7603,7 @@ import {
7245
7603
  // src/elbowArrow.ts
7246
7604
  init_define_import_meta_env();
7247
7605
  import {
7248
- clamp as clamp2,
7606
+ clamp as clamp3,
7249
7607
  pointDistance as pointDistance3,
7250
7608
  pointFrom as pointFrom7,
7251
7609
  pointScaleFromOrigin as pointScaleFromOrigin2,
@@ -8589,12 +8947,12 @@ var normalizeArrowElementUpdate = (global2, nextFixedSegments, startIsSpecial, e
8589
8947
  );
8590
8948
  }
8591
8949
  points = points.map(
8592
- ([x, y]) => pointFrom7(clamp2(x, -1e6, 1e6), clamp2(y, -1e6, 1e6))
8950
+ ([x, y]) => pointFrom7(clamp3(x, -1e6, 1e6), clamp3(y, -1e6, 1e6))
8593
8951
  );
8594
8952
  return {
8595
8953
  points,
8596
- x: clamp2(offsetX, -1e6, 1e6),
8597
- y: clamp2(offsetY, -1e6, 1e6),
8954
+ x: clamp3(offsetX, -1e6, 1e6),
8955
+ y: clamp3(offsetY, -1e6, 1e6),
8598
8956
  fixedSegments: (nextFixedSegments?.length ?? 0) > 0 ? nextFixedSegments : null,
8599
8957
  ...getSizeFromPoints(points),
8600
8958
  startIsSpecial,
@@ -8806,7 +9164,7 @@ var getBindingGap = (bindTarget, opts) => {
8806
9164
  var maxBindingDistance_simple = (zoom) => {
8807
9165
  const BASE_BINDING_DISTANCE = Math.max(BASE_BINDING_GAP, 15);
8808
9166
  const zoomValue = zoom?.value && zoom.value < 1 ? zoom.value : 1;
8809
- return clamp3(
9167
+ return clamp4(
8810
9168
  // reducing zoom impact so that the diff between binding distance and
8811
9169
  // binding gap is kept to minimum when possible
8812
9170
  BASE_BINDING_DISTANCE / (zoomValue * 1.5),
@@ -9118,10 +9476,12 @@ var getBindingStrategyForDraggingBindingElementEndpoints_simple = (arrow, draggi
9118
9476
  const endDragged = draggingPoints.has(endIdx);
9119
9477
  let start2 = { mode: void 0 };
9120
9478
  let end = { mode: void 0 };
9121
- invariant7(
9122
- arrow.points.length > 1,
9123
- "Do not attempt to bind linear elements with a single point"
9124
- );
9479
+ if (arrow.points.length < 2) {
9480
+ console.error(
9481
+ "Attempting to bind a linear element with less than 2 points"
9482
+ );
9483
+ return { start: { mode: void 0 }, end: { mode: void 0 } };
9484
+ }
9125
9485
  if (!startDragged && !endDragged) {
9126
9486
  return { start: start2, end };
9127
9487
  }
@@ -9282,10 +9642,12 @@ var getBindingStrategyForDraggingBindingElementEndpoints_complex = (arrow, dragg
9282
9642
  const endDragged = draggingPoints.has(endIdx);
9283
9643
  let start2 = { mode: void 0 };
9284
9644
  let end = { mode: void 0 };
9285
- invariant7(
9286
- arrow.points.length > 1,
9287
- "Do not attempt to bind linear elements with a single point"
9288
- );
9645
+ if (arrow.points.length < 2) {
9646
+ console.error(
9647
+ "Attempting to bind a linear element with less than 2 points"
9648
+ );
9649
+ return { start: { mode: void 0 }, end: { mode: void 0 } };
9650
+ }
9289
9651
  if (!startDragged && !endDragged) {
9290
9652
  return { start: start2, end };
9291
9653
  }
@@ -9770,12 +10132,12 @@ var avoidRectangularCorner = (arrowElement, bindTarget, elementsMap, p) => {
9770
10132
  return p;
9771
10133
  };
9772
10134
  var snapToMid = (bindTarget, elementsMap, p, tolerance = 0.05, arrowElement) => {
9773
- const { x, y, width, height, angle } = bindTarget;
10135
+ const { x, y, width, height, angle: angle2 } = bindTarget;
9774
10136
  const center = elementCenterPoint(bindTarget, elementsMap, -0.1, -0.1);
9775
- const nonRotated = pointRotateRads8(p, center, -angle);
10137
+ const nonRotated = pointRotateRads8(p, center, -angle2);
9776
10138
  const bindingGap = arrowElement ? getBindingGap(bindTarget, arrowElement) : 0;
9777
- const verticalThreshold = clamp3(tolerance * height, 5, 80);
9778
- const horizontalThreshold = clamp3(tolerance * width, 5, 80);
10139
+ const verticalThreshold = clamp4(tolerance * height, 5, 80);
10140
+ const horizontalThreshold = clamp4(tolerance * width, 5, 80);
9779
10141
  if (pointDistance4(center, nonRotated) < bindingGap) {
9780
10142
  return void 0;
9781
10143
  }
@@ -9783,25 +10145,25 @@ var snapToMid = (bindTarget, elementsMap, p, tolerance = 0.05, arrowElement) =>
9783
10145
  return pointRotateRads8(
9784
10146
  pointFrom8(x - bindingGap, center[1]),
9785
10147
  center,
9786
- angle
10148
+ angle2
9787
10149
  );
9788
10150
  } else if (nonRotated[1] <= y + height / 2 && nonRotated[0] > center[0] - horizontalThreshold && nonRotated[0] < center[0] + horizontalThreshold) {
9789
10151
  return pointRotateRads8(
9790
10152
  pointFrom8(center[0], y - bindingGap),
9791
10153
  center,
9792
- angle
10154
+ angle2
9793
10155
  );
9794
10156
  } else if (nonRotated[0] >= x + width / 2 && nonRotated[1] > center[1] - verticalThreshold && nonRotated[1] < center[1] + verticalThreshold) {
9795
10157
  return pointRotateRads8(
9796
10158
  pointFrom8(x + width + bindingGap, center[1]),
9797
10159
  center,
9798
- angle
10160
+ angle2
9799
10161
  );
9800
10162
  } else if (nonRotated[1] >= y + height / 2 && nonRotated[0] > center[0] - horizontalThreshold && nonRotated[0] < center[0] + horizontalThreshold) {
9801
10163
  return pointRotateRads8(
9802
10164
  pointFrom8(center[0], y + height + bindingGap),
9803
10165
  center,
9804
- angle
10166
+ angle2
9805
10167
  );
9806
10168
  } else if (bindTarget.type === "diamond") {
9807
10169
  const distance3 = bindingGap;
@@ -9822,16 +10184,16 @@ var snapToMid = (bindTarget, elementsMap, p, tolerance = 0.05, arrowElement) =>
9822
10184
  y + 3 * height / 4 + distance3
9823
10185
  );
9824
10186
  if (pointDistance4(topLeft, nonRotated) < Math.max(horizontalThreshold, verticalThreshold)) {
9825
- return pointRotateRads8(topLeft, center, angle);
10187
+ return pointRotateRads8(topLeft, center, angle2);
9826
10188
  }
9827
10189
  if (pointDistance4(topRight, nonRotated) < Math.max(horizontalThreshold, verticalThreshold)) {
9828
- return pointRotateRads8(topRight, center, angle);
10190
+ return pointRotateRads8(topRight, center, angle2);
9829
10191
  }
9830
10192
  if (pointDistance4(bottomLeft, nonRotated) < Math.max(horizontalThreshold, verticalThreshold)) {
9831
- return pointRotateRads8(bottomLeft, center, angle);
10193
+ return pointRotateRads8(bottomLeft, center, angle2);
9832
10194
  }
9833
10195
  if (pointDistance4(bottomRight, nonRotated) < Math.max(horizontalThreshold, verticalThreshold)) {
9834
- return pointRotateRads8(bottomRight, center, angle);
10196
+ return pointRotateRads8(bottomRight, center, angle2);
9835
10197
  }
9836
10198
  }
9837
10199
  return void 0;
@@ -10401,11 +10763,11 @@ var getShapeSideAdaptive = (fixedPoint, shapeType) => {
10401
10763
  const [x, y] = fixedPoint;
10402
10764
  const centerX = x - 0.5;
10403
10765
  const centerY = y - 0.5;
10404
- let angle = Math.atan2(centerY, centerX);
10405
- if (angle < 0) {
10406
- angle += 2 * Math.PI;
10766
+ let angle2 = Math.atan2(centerY, centerX);
10767
+ if (angle2 < 0) {
10768
+ angle2 += 2 * Math.PI;
10407
10769
  }
10408
- const degrees = angle * 180 / Math.PI;
10770
+ const degrees = angle2 * 180 / Math.PI;
10409
10771
  const config = SHAPE_CONFIGS[shapeType];
10410
10772
  const boundaries = getSectorBoundaries(config);
10411
10773
  for (const boundary of boundaries) {
@@ -10560,33 +10922,33 @@ var getBindingSideMidPoint = (binding, elementsMap) => {
10560
10922
  break;
10561
10923
  }
10562
10924
  case "top-right": {
10563
- const angle = -Math.PI / 4;
10564
- const ellipseX = radiusX * Math.cos(angle);
10565
- const ellipseY = radiusY * Math.sin(angle);
10925
+ const angle2 = -Math.PI / 4;
10926
+ const ellipseX = radiusX * Math.cos(angle2);
10927
+ const ellipseY = radiusY * Math.sin(angle2);
10566
10928
  x = ellipseCenterX + ellipseX + OFFSET * 0.707;
10567
10929
  y = ellipseCenterY + ellipseY - OFFSET * 0.707;
10568
10930
  break;
10569
10931
  }
10570
10932
  case "bottom-right": {
10571
- const angle = Math.PI / 4;
10572
- const ellipseX = radiusX * Math.cos(angle);
10573
- const ellipseY = radiusY * Math.sin(angle);
10933
+ const angle2 = Math.PI / 4;
10934
+ const ellipseX = radiusX * Math.cos(angle2);
10935
+ const ellipseY = radiusY * Math.sin(angle2);
10574
10936
  x = ellipseCenterX + ellipseX + OFFSET * 0.707;
10575
10937
  y = ellipseCenterY + ellipseY + OFFSET * 0.707;
10576
10938
  break;
10577
10939
  }
10578
10940
  case "bottom-left": {
10579
- const angle = 3 * Math.PI / 4;
10580
- const ellipseX = radiusX * Math.cos(angle);
10581
- const ellipseY = radiusY * Math.sin(angle);
10941
+ const angle2 = 3 * Math.PI / 4;
10942
+ const ellipseX = radiusX * Math.cos(angle2);
10943
+ const ellipseY = radiusY * Math.sin(angle2);
10582
10944
  x = ellipseCenterX + ellipseX - OFFSET * 0.707;
10583
10945
  y = ellipseCenterY + ellipseY + OFFSET * 0.707;
10584
10946
  break;
10585
10947
  }
10586
10948
  case "top-left": {
10587
- const angle = -3 * Math.PI / 4;
10588
- const ellipseX = radiusX * Math.cos(angle);
10589
- const ellipseY = radiusY * Math.sin(angle);
10949
+ const angle2 = -3 * Math.PI / 4;
10950
+ const ellipseX = radiusX * Math.cos(angle2);
10951
+ const ellipseY = radiusY * Math.sin(angle2);
10590
10952
  x = ellipseCenterX + ellipseX - OFFSET * 0.707;
10591
10953
  y = ellipseCenterY + ellipseY - OFFSET * 0.707;
10592
10954
  break;
@@ -10700,6 +11062,7 @@ var getMidPoint = (p1, p2) => {
10700
11062
  // src/zindex.ts
10701
11063
  init_define_import_meta_env();
10702
11064
  import { arrayToMap as arrayToMap6, findIndex, findLastIndex } from "@excalidraw/common";
11065
+ import { isFiniteNumber } from "@excalidraw/math";
10703
11066
 
10704
11067
  // src/groups.ts
10705
11068
  init_define_import_meta_env();
@@ -11276,9 +11639,6 @@ var addElementsToFrame = (allElements, elementsToAdd, frame) => {
11276
11639
  if (isFrameLikeElement(element) || element.frameId && otherFrames.has(element.frameId)) {
11277
11640
  continue;
11278
11641
  }
11279
- if (element.frameId && element.frameId !== frame.id) {
11280
- continue;
11281
- }
11282
11642
  finalElementsToAdd.add(element);
11283
11643
  const boundTextElement = getBoundTextElement(element, elementsMap);
11284
11644
  if (boundTextElement && !finalElementsToAdd.has(boundTextElement)) {
@@ -12137,7 +12497,34 @@ var getTargetElementsMap = (elements, indices) => {
12137
12497
  return acc;
12138
12498
  }, /* @__PURE__ */ new Map());
12139
12499
  };
12500
+ var hasSameElementIds = (prevElements, nextElements) => {
12501
+ if (prevElements.length !== nextElements.length) {
12502
+ console.error(
12503
+ "z-index reordering failed: resulting array have different lengths"
12504
+ );
12505
+ return false;
12506
+ }
12507
+ const prevElementIdCounts = /* @__PURE__ */ new Map();
12508
+ for (const element of prevElements) {
12509
+ prevElementIdCounts.set(
12510
+ element.id,
12511
+ (prevElementIdCounts.get(element.id) || 0) + 1
12512
+ );
12513
+ }
12514
+ for (const element of nextElements) {
12515
+ const count = prevElementIdCounts.get(element.id);
12516
+ if (!count) {
12517
+ console.error(
12518
+ "z-index reordering failed: element id mismatch / duplicate ids"
12519
+ );
12520
+ return false;
12521
+ }
12522
+ prevElementIdCounts.set(element.id, count - 1);
12523
+ }
12524
+ return true;
12525
+ };
12140
12526
  var shiftElementsByOne = (elements, appState, direction, scene) => {
12527
+ const originalElements = elements;
12141
12528
  const indicesToMove = getIndicesToMove(elements, appState);
12142
12529
  const targetElementsMap = getTargetElementsMap(elements, indicesToMove);
12143
12530
  let groupedIndices = toContiguousGroups(indicesToMove);
@@ -12182,11 +12569,17 @@ var shiftElementsByOne = (elements, appState, direction, scene) => {
12182
12569
  ...trailingElements
12183
12570
  ];
12184
12571
  });
12572
+ if (!hasSameElementIds(originalElements, elements)) {
12573
+ return originalElements;
12574
+ }
12185
12575
  syncMovedIndices(elements, targetElementsMap);
12186
12576
  return elements;
12187
12577
  };
12188
12578
  var shiftElementsToEnd = (elements, appState, direction, containingFrame, elementsToBeMoved) => {
12189
12579
  const indicesToMove = getIndicesToMove(elements, appState, elementsToBeMoved);
12580
+ if (indicesToMove.length === 0) {
12581
+ return elements;
12582
+ }
12190
12583
  const targetElementsMap = getTargetElementsMap(elements, indicesToMove);
12191
12584
  const displacedElements = [];
12192
12585
  let leadingIndex;
@@ -12233,6 +12626,12 @@ var shiftElementsToEnd = (elements, appState, direction, containingFrame, elemen
12233
12626
  if (leadingIndex === -1) {
12234
12627
  leadingIndex = 0;
12235
12628
  }
12629
+ const isValidIndex = (index) => {
12630
+ return isFiniteNumber(index) && index >= 0;
12631
+ };
12632
+ if (!isValidIndex(leadingIndex) || !isValidIndex(trailingIndex) || leadingIndex > trailingIndex || indicesToMove.some((index) => index < leadingIndex || index > trailingIndex)) {
12633
+ return elements;
12634
+ }
12236
12635
  for (let index = leadingIndex; index < trailingIndex + 1; index++) {
12237
12636
  if (!indicesToMove.includes(index)) {
12238
12637
  displacedElements.push(elements[index]);
@@ -12252,6 +12651,9 @@ var shiftElementsToEnd = (elements, appState, direction, containingFrame, elemen
12252
12651
  ...targetElements,
12253
12652
  ...trailingElements
12254
12653
  ];
12654
+ if (!hasSameElementIds(elements, nextElements)) {
12655
+ return elements;
12656
+ }
12255
12657
  syncMovedIndices(nextElements, targetElementsMap);
12256
12658
  return nextElements;
12257
12659
  };
@@ -12293,7 +12695,7 @@ function shiftElementsAccountingForFrames(allElements, appState, direction, shif
12293
12695
  );
12294
12696
  for (const [frameId, children] of frameChildrenSets) {
12295
12697
  nextElements = shiftFunction(
12296
- allElements,
12698
+ nextElements,
12297
12699
  appState,
12298
12700
  direction,
12299
12701
  frameId,
@@ -13416,11 +13818,11 @@ var LinearElementEditor = class _LinearElementEditor {
13416
13818
  return false;
13417
13819
  }
13418
13820
  const origin = linearElementEditor.initialState.origin;
13419
- const dist = pointDistance5(
13821
+ const dist2 = pointDistance5(
13420
13822
  origin,
13421
13823
  pointFrom10(pointerCoords.x, pointerCoords.y)
13422
13824
  );
13423
- if (!appState.selectedLinearElement?.isEditing && dist < DRAGGING_THRESHOLD / appState.zoom.value) {
13825
+ if (!appState.selectedLinearElement?.isEditing && dist2 < DRAGGING_THRESHOLD / appState.zoom.value) {
13424
13826
  return false;
13425
13827
  }
13426
13828
  return true;
@@ -13983,6 +14385,333 @@ var pointDraggingUpdates = (selectedPointsIndices, deltaX, deltaY, scenePointerX
13983
14385
  };
13984
14386
  var determineCustomLinearAngle = (pivotPoint, draggedPoint) => Math.atan2(draggedPoint[1] - pivotPoint[1], draggedPoint[0] - pivotPoint[0]);
13985
14387
 
14388
+ // src/image.ts
14389
+ init_define_import_meta_env();
14390
+ import { MIME_TYPES, SVG_NS } from "@excalidraw/common";
14391
+
14392
+ // ../../node_modules/thumbhash/thumbhash.js
14393
+ init_define_import_meta_env();
14394
+ function rgbaToThumbHash(w, h, rgba) {
14395
+ if (w > 100 || h > 100)
14396
+ throw new Error(`${w}x${h} doesn't fit in 100x100`);
14397
+ let { PI, round, max, cos, abs } = Math;
14398
+ let avg_r = 0, avg_g = 0, avg_b = 0, avg_a = 0;
14399
+ for (let i = 0, j = 0; i < w * h; i++, j += 4) {
14400
+ let alpha = rgba[j + 3] / 255;
14401
+ avg_r += alpha / 255 * rgba[j];
14402
+ avg_g += alpha / 255 * rgba[j + 1];
14403
+ avg_b += alpha / 255 * rgba[j + 2];
14404
+ avg_a += alpha;
14405
+ }
14406
+ if (avg_a) {
14407
+ avg_r /= avg_a;
14408
+ avg_g /= avg_a;
14409
+ avg_b /= avg_a;
14410
+ }
14411
+ let hasAlpha = avg_a < w * h;
14412
+ let l_limit = hasAlpha ? 5 : 7;
14413
+ let lx = max(1, round(l_limit * w / max(w, h)));
14414
+ let ly = max(1, round(l_limit * h / max(w, h)));
14415
+ let l2 = [];
14416
+ let p = [];
14417
+ let q = [];
14418
+ let a2 = [];
14419
+ for (let i = 0, j = 0; i < w * h; i++, j += 4) {
14420
+ let alpha = rgba[j + 3] / 255;
14421
+ let r = avg_r * (1 - alpha) + alpha / 255 * rgba[j];
14422
+ let g = avg_g * (1 - alpha) + alpha / 255 * rgba[j + 1];
14423
+ let b2 = avg_b * (1 - alpha) + alpha / 255 * rgba[j + 2];
14424
+ l2[i] = (r + g + b2) / 3;
14425
+ p[i] = (r + g) / 2 - b2;
14426
+ q[i] = r - g;
14427
+ a2[i] = alpha;
14428
+ }
14429
+ let encodeChannel = (channel, nx, ny) => {
14430
+ let dc = 0, ac = [], scale = 0, fx = [];
14431
+ for (let cy = 0; cy < ny; cy++) {
14432
+ for (let cx = 0; cx * ny < nx * (ny - cy); cx++) {
14433
+ let f = 0;
14434
+ for (let x = 0; x < w; x++)
14435
+ fx[x] = cos(PI / w * cx * (x + 0.5));
14436
+ for (let y = 0; y < h; y++)
14437
+ for (let x = 0, fy = cos(PI / h * cy * (y + 0.5)); x < w; x++)
14438
+ f += channel[x + y * w] * fx[x] * fy;
14439
+ f /= w * h;
14440
+ if (cx || cy) {
14441
+ ac.push(f);
14442
+ scale = max(scale, abs(f));
14443
+ } else {
14444
+ dc = f;
14445
+ }
14446
+ }
14447
+ }
14448
+ if (scale)
14449
+ for (let i = 0; i < ac.length; i++)
14450
+ ac[i] = 0.5 + 0.5 / scale * ac[i];
14451
+ return [dc, ac, scale];
14452
+ };
14453
+ let [l_dc, l_ac, l_scale] = encodeChannel(l2, max(3, lx), max(3, ly));
14454
+ let [p_dc, p_ac, p_scale] = encodeChannel(p, 3, 3);
14455
+ let [q_dc, q_ac, q_scale] = encodeChannel(q, 3, 3);
14456
+ let [a_dc, a_ac, a_scale] = hasAlpha ? encodeChannel(a2, 5, 5) : [];
14457
+ let isLandscape = w > h;
14458
+ let header24 = round(63 * l_dc) | round(31.5 + 31.5 * p_dc) << 6 | round(31.5 + 31.5 * q_dc) << 12 | round(31 * l_scale) << 18 | hasAlpha << 23;
14459
+ let header16 = (isLandscape ? ly : lx) | round(63 * p_scale) << 3 | round(63 * q_scale) << 9 | isLandscape << 15;
14460
+ let hash = [header24 & 255, header24 >> 8 & 255, header24 >> 16, header16 & 255, header16 >> 8];
14461
+ let ac_start = hasAlpha ? 6 : 5;
14462
+ let ac_index = 0;
14463
+ if (hasAlpha)
14464
+ hash.push(round(15 * a_dc) | round(15 * a_scale) << 4);
14465
+ for (let ac of hasAlpha ? [l_ac, p_ac, q_ac, a_ac] : [l_ac, p_ac, q_ac])
14466
+ for (let f of ac)
14467
+ hash[ac_start + (ac_index >> 1)] |= round(15 * f) << ((ac_index++ & 1) << 2);
14468
+ return new Uint8Array(hash);
14469
+ }
14470
+ function thumbHashToRGBA(hash) {
14471
+ let { PI, min, max, cos, round } = Math;
14472
+ let header24 = hash[0] | hash[1] << 8 | hash[2] << 16;
14473
+ let header16 = hash[3] | hash[4] << 8;
14474
+ let l_dc = (header24 & 63) / 63;
14475
+ let p_dc = (header24 >> 6 & 63) / 31.5 - 1;
14476
+ let q_dc = (header24 >> 12 & 63) / 31.5 - 1;
14477
+ let l_scale = (header24 >> 18 & 31) / 31;
14478
+ let hasAlpha = header24 >> 23;
14479
+ let p_scale = (header16 >> 3 & 63) / 63;
14480
+ let q_scale = (header16 >> 9 & 63) / 63;
14481
+ let isLandscape = header16 >> 15;
14482
+ let lx = max(3, isLandscape ? hasAlpha ? 5 : 7 : header16 & 7);
14483
+ let ly = max(3, isLandscape ? header16 & 7 : hasAlpha ? 5 : 7);
14484
+ let a_dc = hasAlpha ? (hash[5] & 15) / 15 : 1;
14485
+ let a_scale = (hash[5] >> 4) / 15;
14486
+ let ac_start = hasAlpha ? 6 : 5;
14487
+ let ac_index = 0;
14488
+ let decodeChannel = (nx, ny, scale) => {
14489
+ let ac = [];
14490
+ for (let cy = 0; cy < ny; cy++)
14491
+ for (let cx = cy ? 0 : 1; cx * ny < nx * (ny - cy); cx++)
14492
+ ac.push(((hash[ac_start + (ac_index >> 1)] >> ((ac_index++ & 1) << 2) & 15) / 7.5 - 1) * scale);
14493
+ return ac;
14494
+ };
14495
+ let l_ac = decodeChannel(lx, ly, l_scale);
14496
+ let p_ac = decodeChannel(3, 3, p_scale * 1.25);
14497
+ let q_ac = decodeChannel(3, 3, q_scale * 1.25);
14498
+ let a_ac = hasAlpha && decodeChannel(5, 5, a_scale);
14499
+ let ratio = thumbHashToApproximateAspectRatio(hash);
14500
+ let w = round(ratio > 1 ? 32 : 32 * ratio);
14501
+ let h = round(ratio > 1 ? 32 / ratio : 32);
14502
+ let rgba = new Uint8Array(w * h * 4), fx = [], fy = [];
14503
+ for (let y = 0, i = 0; y < h; y++) {
14504
+ for (let x = 0; x < w; x++, i += 4) {
14505
+ let l2 = l_dc, p = p_dc, q = q_dc, a2 = a_dc;
14506
+ for (let cx = 0, n = max(lx, hasAlpha ? 5 : 3); cx < n; cx++)
14507
+ fx[cx] = cos(PI / w * (x + 0.5) * cx);
14508
+ for (let cy = 0, n = max(ly, hasAlpha ? 5 : 3); cy < n; cy++)
14509
+ fy[cy] = cos(PI / h * (y + 0.5) * cy);
14510
+ for (let cy = 0, j = 0; cy < ly; cy++)
14511
+ for (let cx = cy ? 0 : 1, fy2 = fy[cy] * 2; cx * ly < lx * (ly - cy); cx++, j++)
14512
+ l2 += l_ac[j] * fx[cx] * fy2;
14513
+ for (let cy = 0, j = 0; cy < 3; cy++) {
14514
+ for (let cx = cy ? 0 : 1, fy2 = fy[cy] * 2; cx < 3 - cy; cx++, j++) {
14515
+ let f = fx[cx] * fy2;
14516
+ p += p_ac[j] * f;
14517
+ q += q_ac[j] * f;
14518
+ }
14519
+ }
14520
+ if (hasAlpha)
14521
+ for (let cy = 0, j = 0; cy < 5; cy++)
14522
+ for (let cx = cy ? 0 : 1, fy2 = fy[cy] * 2; cx < 5 - cy; cx++, j++)
14523
+ a2 += a_ac[j] * fx[cx] * fy2;
14524
+ let b2 = l2 - 2 / 3 * p;
14525
+ let r = (3 * l2 - b2 + q) / 2;
14526
+ let g = r - q;
14527
+ rgba[i] = max(0, 255 * min(1, r));
14528
+ rgba[i + 1] = max(0, 255 * min(1, g));
14529
+ rgba[i + 2] = max(0, 255 * min(1, b2));
14530
+ rgba[i + 3] = max(0, 255 * min(1, a2));
14531
+ }
14532
+ }
14533
+ return { w, h, rgba };
14534
+ }
14535
+ function thumbHashToApproximateAspectRatio(hash) {
14536
+ let header = hash[3];
14537
+ let hasAlpha = hash[2] & 128;
14538
+ let isLandscape = hash[4] & 128;
14539
+ let lx = isLandscape ? hasAlpha ? 5 : 7 : header & 7;
14540
+ let ly = isLandscape ? header & 7 : hasAlpha ? 5 : 7;
14541
+ return lx / ly;
14542
+ }
14543
+
14544
+ // src/image.ts
14545
+ var THUMB_HASH_MAX_DIMENSION = 100;
14546
+ var thumbHashCanvasCache = /* @__PURE__ */ new Map();
14547
+ var invalidThumbHashes = /* @__PURE__ */ new Set();
14548
+ var bytesToBase64 = (bytes) => {
14549
+ let binary = "";
14550
+ for (const byte of bytes) {
14551
+ binary += String.fromCharCode(byte);
14552
+ }
14553
+ return btoa(binary);
14554
+ };
14555
+ var base64ToBytes = (base64) => {
14556
+ const binary = atob(base64);
14557
+ const bytes = new Uint8Array(binary.length);
14558
+ for (let index = 0; index < binary.length; index++) {
14559
+ bytes[index] = binary.charCodeAt(index);
14560
+ }
14561
+ return bytes;
14562
+ };
14563
+ var generateThumbHash = (image) => {
14564
+ const naturalWidth = image.naturalWidth || image.width;
14565
+ const naturalHeight = image.naturalHeight || image.height;
14566
+ if (!naturalWidth || !naturalHeight) {
14567
+ return null;
14568
+ }
14569
+ const scale = Math.min(
14570
+ 1,
14571
+ THUMB_HASH_MAX_DIMENSION / Math.max(naturalWidth, naturalHeight)
14572
+ );
14573
+ const width = Math.max(1, Math.round(naturalWidth * scale));
14574
+ const height = Math.max(1, Math.round(naturalHeight * scale));
14575
+ const canvas = document.createElement("canvas");
14576
+ canvas.width = width;
14577
+ canvas.height = height;
14578
+ const context = canvas.getContext("2d");
14579
+ if (!context) {
14580
+ return null;
14581
+ }
14582
+ context.drawImage(image, 0, 0, width, height);
14583
+ const imageData = context.getImageData(0, 0, width, height);
14584
+ return bytesToBase64(rgbaToThumbHash(width, height, imageData.data));
14585
+ };
14586
+ var getThumbHashPlaceholder = (thumbHash) => {
14587
+ if (invalidThumbHashes.has(thumbHash)) {
14588
+ return null;
14589
+ }
14590
+ const cached = thumbHashCanvasCache.get(thumbHash);
14591
+ if (cached) {
14592
+ return cached;
14593
+ }
14594
+ try {
14595
+ const { w, h, rgba } = thumbHashToRGBA(base64ToBytes(thumbHash));
14596
+ const canvas = document.createElement("canvas");
14597
+ canvas.width = w;
14598
+ canvas.height = h;
14599
+ const context = canvas.getContext("2d");
14600
+ if (!context) {
14601
+ return null;
14602
+ }
14603
+ const imageData = context.createImageData(w, h);
14604
+ imageData.data.set(rgba);
14605
+ context.putImageData(imageData, 0, 0);
14606
+ thumbHashCanvasCache.set(thumbHash, canvas);
14607
+ return canvas;
14608
+ } catch (error) {
14609
+ invalidThumbHashes.add(thumbHash);
14610
+ console.warn("Invalid ThumbHash", error);
14611
+ return null;
14612
+ }
14613
+ };
14614
+ var loadHTMLImageElement = (dataURL) => {
14615
+ return new Promise((resolve, reject) => {
14616
+ const image = new Image();
14617
+ image.onload = () => {
14618
+ resolve(image);
14619
+ };
14620
+ image.onerror = (error) => {
14621
+ reject(error);
14622
+ };
14623
+ image.src = dataURL;
14624
+ });
14625
+ };
14626
+ var updateImageCache = async ({
14627
+ fileIds,
14628
+ files,
14629
+ imageCache
14630
+ }) => {
14631
+ const updatedFiles = /* @__PURE__ */ new Map();
14632
+ const erroredFiles = /* @__PURE__ */ new Map();
14633
+ await Promise.all(
14634
+ fileIds.reduce((promises, fileId) => {
14635
+ const fileData = files[fileId];
14636
+ if (fileData && !updatedFiles.has(fileId)) {
14637
+ updatedFiles.set(fileId, true);
14638
+ return promises.concat(
14639
+ (async () => {
14640
+ try {
14641
+ if (fileData.mimeType === MIME_TYPES.binary) {
14642
+ throw new Error("Only images can be added to ImageCache");
14643
+ }
14644
+ const imagePromise = loadHTMLImageElement(fileData.dataURL);
14645
+ const data = {
14646
+ image: imagePromise,
14647
+ mimeType: fileData.mimeType
14648
+ };
14649
+ imageCache.set(fileId, data);
14650
+ const image = await imagePromise;
14651
+ imageCache.set(fileId, { ...data, image });
14652
+ } catch (error) {
14653
+ erroredFiles.set(fileId, true);
14654
+ }
14655
+ })()
14656
+ );
14657
+ }
14658
+ return promises;
14659
+ }, [])
14660
+ );
14661
+ return {
14662
+ imageCache,
14663
+ /** includes errored files because they cache was updated nonetheless */
14664
+ updatedFiles,
14665
+ /** files that failed when creating HTMLImageElement */
14666
+ erroredFiles
14667
+ };
14668
+ };
14669
+ var getInitializedImageElements = (elements) => elements.filter(
14670
+ (element) => isInitializedImageElement(element)
14671
+ );
14672
+ var isHTMLSVGElement = (node) => {
14673
+ return node?.nodeName.toLowerCase() === "svg";
14674
+ };
14675
+ var normalizeSVG = (SVGString) => {
14676
+ const doc = new DOMParser().parseFromString(SVGString, MIME_TYPES.svg);
14677
+ const svg = doc.querySelector("svg");
14678
+ const errorNode = doc.querySelector("parsererror");
14679
+ if (errorNode || !isHTMLSVGElement(svg)) {
14680
+ throw new Error("Invalid SVG");
14681
+ } else {
14682
+ if (!svg.hasAttribute("xmlns")) {
14683
+ svg.setAttribute("xmlns", SVG_NS);
14684
+ }
14685
+ let width = svg.getAttribute("width");
14686
+ let height = svg.getAttribute("height");
14687
+ if (width?.includes("%") || width === "auto") {
14688
+ width = null;
14689
+ }
14690
+ if (height?.includes("%") || height === "auto") {
14691
+ height = null;
14692
+ }
14693
+ const viewBox = svg.getAttribute("viewBox");
14694
+ if (!width || !height) {
14695
+ width = width || "50";
14696
+ height = height || "50";
14697
+ if (viewBox) {
14698
+ const match = viewBox.match(
14699
+ /\d+ +\d+ +(\d+(?:\.\d+)?) +(\d+(?:\.\d+)?)/
14700
+ );
14701
+ if (match) {
14702
+ [, width, height] = match;
14703
+ }
14704
+ }
14705
+ svg.setAttribute("width", width);
14706
+ svg.setAttribute("height", height);
14707
+ }
14708
+ if (!viewBox) {
14709
+ svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
14710
+ }
14711
+ return svg.outerHTML;
14712
+ }
14713
+ };
14714
+
13986
14715
  // src/renderElement.ts
13987
14716
  var isPendingImageElement = (element, renderConfig) => isInitializedImageElement(element) && !renderConfig.imageCache.has(element.fileId);
13988
14717
  var getCanvasPadding = (element) => {
@@ -14112,14 +14841,27 @@ var generateElementCanvas = (element, elementsMap, zoom, renderConfig, appState)
14112
14841
  };
14113
14842
  var DEFAULT_LINK_SIZE = 14;
14114
14843
  var IMAGE_PLACEHOLDER_IMG = typeof document !== "undefined" ? document.createElement("img") : { src: "" };
14115
- IMAGE_PLACEHOLDER_IMG.src = `data:${MIME_TYPES.svg},${encodeURIComponent(
14844
+ IMAGE_PLACEHOLDER_IMG.src = `data:${MIME_TYPES2.svg},${encodeURIComponent(
14116
14845
  `<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="image" class="svg-inline--fa fa-image fa-w-16" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="#888" d="M464 448H48c-26.51 0-48-21.49-48-48V112c0-26.51 21.49-48 48-48h416c26.51 0 48 21.49 48 48v288c0 26.51-21.49 48-48 48zM112 120c-30.928 0-56 25.072-56 56s25.072 56 56 56 56-25.072 56-56-25.072-56-56-56zM64 384h384V272l-87.515-87.515c-4.686-4.686-12.284-4.686-16.971 0L208 320l-55.515-55.515c-4.686-4.686-12.284-4.686-16.971 0L64 336v48z"></path></svg>`
14117
14846
  )}`;
14118
14847
  var IMAGE_ERROR_PLACEHOLDER_IMG = typeof document !== "undefined" ? document.createElement("img") : { src: "" };
14119
- IMAGE_ERROR_PLACEHOLDER_IMG.src = `data:${MIME_TYPES.svg},${encodeURIComponent(
14848
+ IMAGE_ERROR_PLACEHOLDER_IMG.src = `data:${MIME_TYPES2.svg},${encodeURIComponent(
14120
14849
  `<svg viewBox="0 0 668 668" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2"><path d="M464 448H48c-26.51 0-48-21.49-48-48V112c0-26.51 21.49-48 48-48h416c26.51 0 48 21.49 48 48v288c0 26.51-21.49 48-48 48ZM112 120c-30.928 0-56 25.072-56 56s25.072 56 56 56 56-25.072 56-56-25.072-56-56-56ZM64 384h384V272l-87.515-87.515c-4.686-4.686-12.284-4.686-16.971 0L208 320l-55.515-55.515c-4.686-4.686-12.284-4.686-16.971 0L64 336v48Z" style="fill:#888;fill-rule:nonzero" transform="matrix(.81709 0 0 .81709 124.825 145.825)"/><path d="M256 8C119.034 8 8 119.033 8 256c0 136.967 111.034 248 248 248s248-111.034 248-248S392.967 8 256 8Zm130.108 117.892c65.448 65.448 70 165.481 20.677 235.637L150.47 105.216c70.204-49.356 170.226-44.735 235.638 20.676ZM125.892 386.108c-65.448-65.448-70-165.481-20.677-235.637L361.53 406.784c-70.203 49.356-170.226 44.736-235.638-20.676Z" style="fill:#888;fill-rule:nonzero" transform="matrix(.30366 0 0 .30366 506.822 60.065)"/></svg>`
14121
14850
  )}`;
14122
14851
  var drawImagePlaceholder = (element, context, theme) => {
14852
+ if (element.status !== "error" && element.thumbHash) {
14853
+ const thumbHashPlaceholder = getThumbHashPlaceholder(element.thumbHash);
14854
+ if (thumbHashPlaceholder) {
14855
+ context.drawImage(
14856
+ thumbHashPlaceholder,
14857
+ 0,
14858
+ 0,
14859
+ element.width,
14860
+ element.height
14861
+ );
14862
+ return;
14863
+ }
14864
+ }
14123
14865
  context.fillStyle = theme === THEME.DARK ? "#2E2E2E" : "#E7E7E7";
14124
14866
  context.fillRect(0, 0, element.width, element.height);
14125
14867
  const imageMinWidthOrHeight = Math.min(element.width, element.height);
@@ -14191,13 +14933,42 @@ var drawElementOnCanvas = (element, rc, context, renderConfig) => {
14191
14933
  );
14192
14934
  context.clip();
14193
14935
  }
14936
+ const transitionDuration = renderConfig.imageTransitionDuration ?? 0;
14937
+ if (!renderConfig.isExporting && cacheEntry?.transitionStart !== void 0) {
14938
+ if (transitionDuration > 0) {
14939
+ const transitionProgress = Math.min(
14940
+ 1,
14941
+ (performance.now() - cacheEntry.transitionStart) / transitionDuration
14942
+ );
14943
+ if (transitionProgress < 1) {
14944
+ if (cacheEntry.placeholderImage) {
14945
+ context.drawImage(
14946
+ cacheEntry.placeholderImage,
14947
+ 0,
14948
+ 0,
14949
+ element.width,
14950
+ element.height
14951
+ );
14952
+ } else {
14953
+ drawImagePlaceholder(element, context, renderConfig.theme);
14954
+ }
14955
+ context.globalAlpha *= transitionProgress;
14956
+ } else {
14957
+ delete cacheEntry.placeholderImage;
14958
+ delete cacheEntry.transitionStart;
14959
+ }
14960
+ } else {
14961
+ delete cacheEntry.placeholderImage;
14962
+ delete cacheEntry.transitionStart;
14963
+ }
14964
+ }
14194
14965
  const { x, y, width, height } = element.crop ? element.crop : {
14195
14966
  x: 0,
14196
14967
  y: 0,
14197
14968
  width: img.naturalWidth,
14198
14969
  height: img.naturalHeight
14199
14970
  };
14200
- const shouldInvertImage = renderConfig.theme === THEME.DARK && cacheEntry?.mimeType === MIME_TYPES.svg;
14971
+ const shouldInvertImage = renderConfig.theme === THEME.DARK && cacheEntry?.mimeType === MIME_TYPES2.svg;
14201
14972
  if (shouldInvertImage && isSafari) {
14202
14973
  const devicePixelRatio = window.devicePixelRatio || 1;
14203
14974
  const tempCanvas = document.createElement("canvas");
@@ -14317,8 +15088,9 @@ var generateElementWithCanvas = (element, elementsMap, renderConfig, appState) =
14317
15088
  const boundTextElement = getBoundTextElement(element, elementsMap);
14318
15089
  const boundTextElementVersion = boundTextElement?.version || null;
14319
15090
  const imageCrop = isImageElement(element) ? element.crop : null;
15091
+ const hasActiveImageTransition = isInitializedImageElement(element) && renderConfig.imageCache.get(element.fileId)?.transitionStart !== void 0;
14320
15092
  const containingFrameOpacity = getContainingFrame(element, elementsMap)?.opacity || 100;
14321
- if (!prevElementWithCanvas || shouldRegenerateBecauseZoom || prevElementWithCanvas.theme !== appState.theme || prevElementWithCanvas.boundTextElementVersion !== boundTextElementVersion || prevElementWithCanvas.imageCrop !== imageCrop || prevElementWithCanvas.containingFrameOpacity !== containingFrameOpacity || // since we rotate the canvas when copying from cached canvas, we don't
15093
+ if (!prevElementWithCanvas || shouldRegenerateBecauseZoom || prevElementWithCanvas.theme !== appState.theme || prevElementWithCanvas.boundTextElementVersion !== boundTextElementVersion || prevElementWithCanvas.imageCrop !== imageCrop || hasActiveImageTransition || prevElementWithCanvas.containingFrameOpacity !== containingFrameOpacity || // since we rotate the canvas when copying from cached canvas, we don't
14322
15094
  // regenerate the cached canvas. But we need to in case of labels which are
14323
15095
  // cached alongside the arrow, and we want the labels to remain unrotated
14324
15096
  // with respect to the arrow.
@@ -14494,8 +15266,10 @@ var renderElement = (element, elementsMap, allElementsMap, rc, context, renderCo
14494
15266
  case "embeddable": {
14495
15267
  if (renderConfig.isExporting) {
14496
15268
  const [x1, y1, x2, y2] = getElementAbsoluteCoords2(element, elementsMap);
14497
- const cx = (x1 + x2) / 2 + appState.scrollX;
14498
- const cy = (y1 + y2) / 2 + appState.scrollY;
15269
+ const centerX = (x1 + x2) / 2;
15270
+ const centerY = (y1 + y2) / 2;
15271
+ const cx = centerX + appState.scrollX;
15272
+ const cy = centerY + appState.scrollY;
14499
15273
  let shiftX = (x2 - x1) / 2 - (element.x - x1);
14500
15274
  let shiftY = (y2 - y1) / 2 - (element.y - y1);
14501
15275
  if (isTextElement(element)) {
@@ -14514,46 +15288,33 @@ var renderElement = (element, elementsMap, allElementsMap, rc, context, renderCo
14514
15288
  context.translate(cx, cy);
14515
15289
  const boundTextElement = getBoundTextElement(element, elementsMap);
14516
15290
  if (isArrowElement(element) && boundTextElement) {
14517
- const tempCanvas = document.createElement("canvas");
14518
- const tempCanvasContext = tempCanvas.getContext("2d");
14519
- const maxDim = Math.max(distance2(x1, x2), distance2(y1, y2));
14520
- const padding = getCanvasPadding(element);
14521
- tempCanvas.width = maxDim * appState.exportScale + padding * 10 * appState.exportScale;
14522
- tempCanvas.height = maxDim * appState.exportScale + padding * 10 * appState.exportScale;
14523
- tempCanvasContext.translate(
14524
- tempCanvas.width / 2,
14525
- tempCanvas.height / 2
14526
- );
14527
- tempCanvasContext.scale(appState.exportScale, appState.exportScale);
14528
15291
  shiftX = element.width / 2 - (element.x - x1);
14529
15292
  shiftY = element.height / 2 - (element.y - y1);
14530
- tempCanvasContext.rotate(element.angle);
14531
- const tempRc = rough_default.canvas(tempCanvas);
14532
- tempCanvasContext.translate(-shiftX, -shiftY);
14533
- drawElementOnCanvas(element, tempRc, tempCanvasContext, renderConfig);
14534
- tempCanvasContext.translate(shiftX, shiftY);
14535
- tempCanvasContext.rotate(-element.angle);
15293
+ context.save();
15294
+ context.rotate(element.angle);
15295
+ context.translate(-shiftX, -shiftY);
15296
+ drawElementOnCanvas(element, rc, context, renderConfig);
15297
+ context.restore();
14536
15298
  const [, , , , boundTextCx, boundTextCy] = getElementAbsoluteCoords2(
14537
15299
  boundTextElement,
14538
15300
  elementsMap
14539
15301
  );
14540
- const boundTextShiftX = (x1 + x2) / 2 - boundTextCx;
14541
- const boundTextShiftY = (y1 + y2) / 2 - boundTextCy;
14542
- tempCanvasContext.translate(-boundTextShiftX, -boundTextShiftY);
14543
- tempCanvasContext.clearRect(
14544
- -boundTextElement.width / 2,
14545
- -boundTextElement.height / 2,
14546
- boundTextElement.width,
14547
- boundTextElement.height
14548
- );
14549
- context.scale(1 / appState.exportScale, 1 / appState.exportScale);
14550
- context.drawImage(
14551
- tempCanvas,
14552
- -tempCanvas.width / 2,
14553
- -tempCanvas.height / 2,
14554
- tempCanvas.width,
14555
- tempCanvas.height
14556
- );
15302
+ const holeX = boundTextCx - centerX - boundTextElement.width / 2 - BOUND_TEXT_PADDING3;
15303
+ const holeY = boundTextCy - centerY - boundTextElement.height / 2 - BOUND_TEXT_PADDING3;
15304
+ const holeWidth = boundTextElement.width + BOUND_TEXT_PADDING3 * 2;
15305
+ const holeHeight = boundTextElement.height + BOUND_TEXT_PADDING3 * 2;
15306
+ const isTransparentHole = "viewBackgroundColor" in appState && (appState.viewBackgroundColor === "transparent" || !appState.viewBackgroundColor);
15307
+ if (!isTransparentHole) {
15308
+ context.save();
15309
+ context.fillStyle = applyDarkModeFilter(
15310
+ renderConfig.canvasBackgroundColor,
15311
+ renderConfig.theme === THEME.DARK
15312
+ );
15313
+ context.fillRect(holeX, holeY, holeWidth, holeHeight);
15314
+ context.restore();
15315
+ } else {
15316
+ context.clearRect(holeX, holeY, holeWidth, holeHeight);
15317
+ }
14557
15318
  } else {
14558
15319
  context.rotate(element.angle);
14559
15320
  if (element.type === "image") {
@@ -15474,19 +16235,46 @@ var getFreeDrawSvgPath = (element) => {
15474
16235
  getFreedrawOutlinePoints(element)
15475
16236
  );
15476
16237
  };
15477
- var getFreedrawOutlinePoints = (element) => {
15478
- const inputPoints = element.simulatePressure ? element.points : element.points.length ? element.points.map(([x, y], i) => [x, y, element.pressures[i]]) : [[0, 0, 0.5]];
16238
+ var VARIABLE_WIDTH_FREEDRAW = {
16239
+ /** Stroke size relative to `strokeWidth` for pressure-sensitive strokes. */
16240
+ SIZE_FACTOR: 4.25,
16241
+ THINNING: 0.6,
16242
+ SMOOTHING: 0.5
16243
+ };
16244
+ var CONSTANT_WIDTH_FREEDRAW = {
16245
+ /** Stroke size relative to `strokeWidth` for uniform (laser) strokes. */
16246
+ SIZE_FACTOR: 1.4
16247
+ };
16248
+ var getFreedrawStreamline = (element) => element.strokeOptions?.streamline ?? DEFAULT_STROKE_STREAMLINE;
16249
+ var getVariableWidthFreedrawOutline = (element) => {
16250
+ const inputPoints = element.simulatePressure ? element.points : element.points.length ? element.points.map(
16251
+ ([x, y], i) => [x, y, element.pressures[i]]
16252
+ ) : [[0, 0, 0.5]];
15479
16253
  return ae(inputPoints, {
15480
16254
  simulatePressure: element.simulatePressure,
15481
- size: element.strokeWidth * 4.25,
15482
- thinning: 0.6,
15483
- smoothing: 0.5,
15484
- streamline: 0.5,
16255
+ size: element.strokeWidth * VARIABLE_WIDTH_FREEDRAW.SIZE_FACTOR,
16256
+ thinning: VARIABLE_WIDTH_FREEDRAW.THINNING,
16257
+ smoothing: VARIABLE_WIDTH_FREEDRAW.SMOOTHING,
16258
+ streamline: getFreedrawStreamline(element),
15485
16259
  easing: (t) => Math.sin(t * Math.PI / 2),
15486
16260
  // https://easings.net/#easeOutSine
15487
16261
  last: true
15488
16262
  });
15489
16263
  };
16264
+ var createLaserPointer = (element) => new LaserPointer({
16265
+ size: element.strokeWidth * CONSTANT_WIDTH_FREEDRAW.SIZE_FACTOR,
16266
+ streamline: getFreedrawStreamline(element),
16267
+ simplify: 0,
16268
+ sizeMapping: (details) => Math.max(0.1, details.pressure)
16269
+ });
16270
+ var getConstantWidthFreedrawOutline = (element) => {
16271
+ const laserPointer = createLaserPointer(element);
16272
+ element.points.map(([x, y]) => laserPointer.addPoint([x, y, 1]));
16273
+ return laserPointer.getStrokeOutline().map(([x, y]) => [x, y]);
16274
+ };
16275
+ var getFreedrawOutlinePoints = (element) => {
16276
+ return element.strokeOptions?.variability === "constant" ? getConstantWidthFreedrawOutline(element) : getVariableWidthFreedrawOutline(element);
16277
+ };
15490
16278
  var med = (A2, B2) => {
15491
16279
  return [(A2[0] + B2[0]) / 2, (A2[1] + B2[1]) / 2];
15492
16280
  };
@@ -15763,15 +16551,15 @@ var getElementLineSegments = (element, elementsMap) => {
15763
16551
  var _isRectanguloidElement = (element) => {
15764
16552
  return element != null && (element.type === "rectangle" || element.type === "image" || element.type === "iframe" || element.type === "embeddable" || element.type === "frame" || element.type === "magicframe" || element.type === "text" && !element.containerId);
15765
16553
  };
15766
- var getRotatedSides = (sides, center, angle) => {
16554
+ var getRotatedSides = (sides, center, angle2) => {
15767
16555
  return sides.map((side) => {
15768
16556
  return lineSegment6(
15769
- pointRotateRads12(side[0], center, angle),
15770
- pointRotateRads12(side[1], center, angle)
16557
+ pointRotateRads12(side[0], center, angle2),
16558
+ pointRotateRads12(side[1], center, angle2)
15771
16559
  );
15772
16560
  });
15773
16561
  };
15774
- var getSegmentsOnCurve = (curve4, center, angle) => {
16562
+ var getSegmentsOnCurve = (curve4, center, angle2) => {
15775
16563
  const points = pointsOnBezierCurves(curve4, 10);
15776
16564
  let i = 0;
15777
16565
  const segments = [];
@@ -15781,12 +16569,12 @@ var getSegmentsOnCurve = (curve4, center, angle) => {
15781
16569
  pointRotateRads12(
15782
16570
  pointFrom13(points[i][0], points[i][1]),
15783
16571
  center,
15784
- angle
16572
+ angle2
15785
16573
  ),
15786
16574
  pointRotateRads12(
15787
16575
  pointFrom13(points[i + 1][0], points[i + 1][1]),
15788
16576
  center,
15789
- angle
16577
+ angle2
15790
16578
  )
15791
16579
  )
15792
16580
  );
@@ -16028,29 +16816,29 @@ var getArrowheadPoints = (element, shape, position, arrowhead, offsetMultiplier
16028
16816
  const diameter = Math.hypot(ys - ty, xs - tx) + element.strokeWidth - 2;
16029
16817
  return [tx, ty, diameter];
16030
16818
  }
16031
- const angle = getArrowheadAngle(arrowhead);
16819
+ const angle2 = getArrowheadAngle(arrowhead);
16032
16820
  if (arrowhead === "cardinality_many" || arrowhead === "cardinality_one_or_many") {
16033
16821
  const [x32, y32] = pointRotateRads12(
16034
16822
  pointFrom13(tx, ty),
16035
16823
  pointFrom13(xs, ys),
16036
- degreesToRadians(-angle)
16824
+ degreesToRadians(-angle2)
16037
16825
  );
16038
16826
  const [x42, y42] = pointRotateRads12(
16039
16827
  pointFrom13(tx, ty),
16040
16828
  pointFrom13(xs, ys),
16041
- degreesToRadians(angle)
16829
+ degreesToRadians(angle2)
16042
16830
  );
16043
16831
  return [xs, ys, x32, y32, x42, y42];
16044
16832
  }
16045
16833
  const [x3, y3] = pointRotateRads12(
16046
16834
  pointFrom13(xs, ys),
16047
16835
  pointFrom13(tx, ty),
16048
- -angle * Math.PI / 180
16836
+ -angle2 * Math.PI / 180
16049
16837
  );
16050
16838
  const [x4, y4] = pointRotateRads12(
16051
16839
  pointFrom13(xs, ys),
16052
16840
  pointFrom13(tx, ty),
16053
- degreesToRadians(angle)
16841
+ degreesToRadians(angle2)
16054
16842
  );
16055
16843
  if (arrowhead === "diamond" || arrowhead === "diamond_outline") {
16056
16844
  let ox;
@@ -16620,18 +17408,18 @@ var getPerfectElementSize = (elementType, width, height) => {
16620
17408
  var getLockedLinearCursorAlignSize = (originX, originY, x, y, customAngle) => {
16621
17409
  let width = x - originX;
16622
17410
  let height = y - originY;
16623
- const angle = Math.atan2(height, width);
16624
- let lockedAngle = Math.round(angle / SHIFT_LOCKING_ANGLE) * SHIFT_LOCKING_ANGLE;
17411
+ const angle2 = Math.atan2(height, width);
17412
+ let lockedAngle = Math.round(angle2 / SHIFT_LOCKING_ANGLE) * SHIFT_LOCKING_ANGLE;
16625
17413
  if (customAngle) {
16626
17414
  const lower = Math.floor(customAngle / SHIFT_LOCKING_ANGLE) * SHIFT_LOCKING_ANGLE;
16627
17415
  if (radiansBetweenAngles(
16628
- angle,
17416
+ angle2,
16629
17417
  lower,
16630
17418
  lower + SHIFT_LOCKING_ANGLE
16631
17419
  )) {
16632
- if (radiansDifference(angle, customAngle) < SHIFT_LOCKING_ANGLE / 6) {
17420
+ if (radiansDifference(angle2, customAngle) < SHIFT_LOCKING_ANGLE / 6) {
16633
17421
  lockedAngle = customAngle;
16634
- } else if (normalizeRadians(angle) > normalizeRadians(customAngle)) {
17422
+ } else if (normalizeRadians(angle2) > normalizeRadians(customAngle)) {
16635
17423
  lockedAngle = lower + SHIFT_LOCKING_ANGLE;
16636
17424
  } else {
16637
17425
  lockedAngle = lower;
@@ -16846,7 +17634,7 @@ var compareScores = (a2, b2) => {
16846
17634
  return 0;
16847
17635
  };
16848
17636
  var compareBlocks = (a2, b2) => b2.packedW * b2.packedH - a2.packedW * a2.packedH || Math.max(b2.packedW, b2.packedH) - Math.max(a2.packedW, a2.packedH) || b2.packedH - a2.packedH || b2.packedW - a2.packedW;
16849
- var clamp4 = (value, min, max) => Math.min(Math.max(value, min), max);
17637
+ var clamp5 = (value, min, max) => Math.min(Math.max(value, min), max);
16850
17638
  var intersects = (a2, b2) => a2.x < b2.x + b2.w && a2.x + a2.w > b2.x && a2.y < b2.y + b2.h && a2.y + a2.h > b2.y;
16851
17639
  var contains = (outer, inner) => inner.x >= outer.x && inner.y >= outer.y && inner.x + inner.w <= outer.x + outer.w && inner.y + inner.h <= outer.y + outer.h;
16852
17640
  var getLayoutBounds = (blocks) => {
@@ -16958,8 +17746,8 @@ var getCandidatePositions = (freeRect, block, heuristic, targetCenter) => {
16958
17746
  ];
16959
17747
  if (heuristic === "center-distance") {
16960
17748
  positions.push({
16961
- x: clamp4(targetCenter.x - block.actualW / 2, freeRect.x, maxX),
16962
- y: clamp4(targetCenter.y - block.actualH / 2, freeRect.y, maxY)
17749
+ x: clamp5(targetCenter.x - block.actualW / 2, freeRect.x, maxX),
17750
+ y: clamp5(targetCenter.y - block.actualH / 2, freeRect.y, maxY)
16963
17751
  });
16964
17752
  }
16965
17753
  return positions.filter(
@@ -20210,6 +20998,7 @@ import {
20210
20998
  DEFAULT_FONT_SIZE as DEFAULT_FONT_SIZE3,
20211
20999
  DEFAULT_TEXT_ALIGN,
20212
21000
  DEFAULT_VERTICAL_ALIGN,
21001
+ DEFAULT_STROKE_STREAMLINE as DEFAULT_STROKE_STREAMLINE2,
20213
21002
  VERTICAL_ALIGN as VERTICAL_ALIGN2,
20214
21003
  randomInteger as randomInteger5,
20215
21004
  randomId as randomId3,
@@ -20229,7 +21018,7 @@ var _newElementBase = (type, {
20229
21018
  opacity = DEFAULT_ELEMENT_PROPS.opacity,
20230
21019
  width = 0,
20231
21020
  height = 0,
20232
- angle = 0,
21021
+ angle: angle2 = 0,
20233
21022
  groupIds = [],
20234
21023
  frameId = null,
20235
21024
  index = null,
@@ -20256,7 +21045,7 @@ var _newElementBase = (type, {
20256
21045
  y,
20257
21046
  width,
20258
21047
  height,
20259
- angle,
21048
+ angle: angle2,
20260
21049
  strokeColor,
20261
21050
  backgroundColor,
20262
21051
  fillStyle,
@@ -20413,9 +21202,9 @@ var getAdjustedDimensions = (element, elementsMap, nextText) => {
20413
21202
  y: Number.isFinite(y) ? y : element.y
20414
21203
  };
20415
21204
  };
20416
- var adjustXYWithRotation = (sides, x, y, angle, deltaX1, deltaY1, deltaX2, deltaY2) => {
20417
- const cos = Math.cos(angle);
20418
- const sin = Math.sin(angle);
21205
+ var adjustXYWithRotation = (sides, x, y, angle2, deltaX1, deltaY1, deltaX2, deltaY2) => {
21206
+ const cos = Math.cos(angle2);
21207
+ const sin = Math.sin(angle2);
20419
21208
  if (sides.e && sides.w) {
20420
21209
  x += deltaX1 + deltaX2;
20421
21210
  } else if (sides.e) {
@@ -20463,7 +21252,11 @@ var newFreeDrawElement = (opts) => {
20463
21252
  ..._newElementBase(opts.type, opts),
20464
21253
  points: opts.points || [],
20465
21254
  pressures: opts.pressures || [],
20466
- simulatePressure: opts.simulatePressure
21255
+ simulatePressure: opts.simulatePressure,
21256
+ strokeOptions: opts.strokeOptions ?? {
21257
+ variability: "variable",
21258
+ streamline: DEFAULT_STROKE_STREAMLINE2
21259
+ }
20467
21260
  };
20468
21261
  };
20469
21262
  var newLinearElement = (opts) => {
@@ -20518,6 +21311,7 @@ var newImageElement = (opts) => {
20518
21311
  status: opts.status ?? "pending",
20519
21312
  fileId: opts.fileId ?? null,
20520
21313
  fileName: opts.fileName ?? null,
21314
+ thumbHash: opts.thumbHash ?? null,
20521
21315
  scale: opts.scale ?? [1, 1],
20522
21316
  crop: opts.crop ?? null
20523
21317
  };
@@ -21744,110 +22538,6 @@ var handleFocusPointHover = (arrow, scenePointerX, scenePointerY, scene, appStat
21744
22538
  return null;
21745
22539
  };
21746
22540
 
21747
- // src/image.ts
21748
- init_define_import_meta_env();
21749
- import { MIME_TYPES as MIME_TYPES2, SVG_NS } from "@excalidraw/common";
21750
- var loadHTMLImageElement = (dataURL) => {
21751
- return new Promise((resolve, reject) => {
21752
- const image = new Image();
21753
- image.onload = () => {
21754
- resolve(image);
21755
- };
21756
- image.onerror = (error) => {
21757
- reject(error);
21758
- };
21759
- image.src = dataURL;
21760
- });
21761
- };
21762
- var updateImageCache = async ({
21763
- fileIds,
21764
- files,
21765
- imageCache
21766
- }) => {
21767
- const updatedFiles = /* @__PURE__ */ new Map();
21768
- const erroredFiles = /* @__PURE__ */ new Map();
21769
- await Promise.all(
21770
- fileIds.reduce((promises, fileId) => {
21771
- const fileData = files[fileId];
21772
- if (fileData && !updatedFiles.has(fileId)) {
21773
- updatedFiles.set(fileId, true);
21774
- return promises.concat(
21775
- (async () => {
21776
- try {
21777
- if (fileData.mimeType === MIME_TYPES2.binary) {
21778
- throw new Error("Only images can be added to ImageCache");
21779
- }
21780
- const imagePromise = loadHTMLImageElement(fileData.dataURL);
21781
- const data = {
21782
- image: imagePromise,
21783
- mimeType: fileData.mimeType
21784
- };
21785
- imageCache.set(fileId, data);
21786
- const image = await imagePromise;
21787
- imageCache.set(fileId, { ...data, image });
21788
- } catch (error) {
21789
- erroredFiles.set(fileId, true);
21790
- }
21791
- })()
21792
- );
21793
- }
21794
- return promises;
21795
- }, [])
21796
- );
21797
- return {
21798
- imageCache,
21799
- /** includes errored files because they cache was updated nonetheless */
21800
- updatedFiles,
21801
- /** files that failed when creating HTMLImageElement */
21802
- erroredFiles
21803
- };
21804
- };
21805
- var getInitializedImageElements = (elements) => elements.filter(
21806
- (element) => isInitializedImageElement(element)
21807
- );
21808
- var isHTMLSVGElement = (node) => {
21809
- return node?.nodeName.toLowerCase() === "svg";
21810
- };
21811
- var normalizeSVG = (SVGString) => {
21812
- const doc = new DOMParser().parseFromString(SVGString, MIME_TYPES2.svg);
21813
- const svg = doc.querySelector("svg");
21814
- const errorNode = doc.querySelector("parsererror");
21815
- if (errorNode || !isHTMLSVGElement(svg)) {
21816
- throw new Error("Invalid SVG");
21817
- } else {
21818
- if (!svg.hasAttribute("xmlns")) {
21819
- svg.setAttribute("xmlns", SVG_NS);
21820
- }
21821
- let width = svg.getAttribute("width");
21822
- let height = svg.getAttribute("height");
21823
- if (width?.includes("%") || width === "auto") {
21824
- width = null;
21825
- }
21826
- if (height?.includes("%") || height === "auto") {
21827
- height = null;
21828
- }
21829
- const viewBox = svg.getAttribute("viewBox");
21830
- if (!width || !height) {
21831
- width = width || "50";
21832
- height = height || "50";
21833
- if (viewBox) {
21834
- const match = viewBox.match(
21835
- /\d+ +\d+ +(\d+(?:\.\d+)?) +(\d+(?:\.\d+)?)/
21836
- );
21837
- if (match) {
21838
- [, width, height] = match;
21839
- }
21840
- }
21841
- svg.setAttribute("width", width);
21842
- svg.setAttribute("height", height);
21843
- }
21844
- if (!viewBox) {
21845
- svg.setAttribute("viewBox", `0 0 ${width} ${height}`);
21846
- }
21847
- return svg.outerHTML;
21848
- }
21849
- };
21850
-
21851
22541
  // src/normalise.ts
21852
22542
  init_define_import_meta_env();
21853
22543
  var getGroupCenter = (boundingBox) => ({
@@ -22118,20 +22808,20 @@ var rotateSingleElement = (element, scene, pointerX, pointerY, shouldRotateWithD
22118
22808
  );
22119
22809
  const cx = (x1 + x2) / 2;
22120
22810
  const cy = (y1 + y2) / 2;
22121
- let angle;
22811
+ let angle2;
22122
22812
  if (isFrameLikeElement(element)) {
22123
- angle = 0;
22813
+ angle2 = 0;
22124
22814
  } else {
22125
- angle = 5 * Math.PI / 2 + Math.atan2(pointerY - cy, pointerX - cx);
22815
+ angle2 = 5 * Math.PI / 2 + Math.atan2(pointerY - cy, pointerX - cx);
22126
22816
  if (shouldRotateWithDiscreteAngle2) {
22127
- angle = angle + SHIFT_LOCKING_ANGLE2 / 2;
22128
- angle = angle - angle % SHIFT_LOCKING_ANGLE2;
22817
+ angle2 = angle2 + SHIFT_LOCKING_ANGLE2 / 2;
22818
+ angle2 = angle2 - angle2 % SHIFT_LOCKING_ANGLE2;
22129
22819
  }
22130
- angle = normalizeRadians2(angle);
22820
+ angle2 = normalizeRadians2(angle2);
22131
22821
  }
22132
22822
  const boundTextElementId = getBoundTextElementId(element);
22133
22823
  let update = {
22134
- angle
22824
+ angle: angle2
22135
22825
  };
22136
22826
  if (isBindingElement(element)) {
22137
22827
  update = {
@@ -22154,7 +22844,7 @@ var rotateSingleElement = (element, scene, pointerX, pointerY, shouldRotateWithD
22154
22844
  scene.getNonDeletedElementsMap()
22155
22845
  );
22156
22846
  scene.mutateElement(textElement, {
22157
- angle,
22847
+ angle: angle2,
22158
22848
  x,
22159
22849
  y
22160
22850
  });
@@ -22322,45 +23012,45 @@ var getResizeOffsetXY = (transformHandleType, selectedElements, elementsMap, x,
22322
23012
  const [x1, y1, x2, y2] = selectedElements.length === 1 ? getElementAbsoluteCoords2(selectedElements[0], elementsMap) : getCommonBounds(selectedElements);
22323
23013
  const cx = (x1 + x2) / 2;
22324
23014
  const cy = (y1 + y2) / 2;
22325
- const angle = selectedElements.length === 1 ? selectedElements[0].angle : 0;
23015
+ const angle2 = selectedElements.length === 1 ? selectedElements[0].angle : 0;
22326
23016
  [x, y] = pointRotateRads13(
22327
23017
  pointFrom16(x, y),
22328
23018
  pointFrom16(cx, cy),
22329
- -angle
23019
+ -angle2
22330
23020
  );
22331
23021
  switch (transformHandleType) {
22332
23022
  case "n":
22333
23023
  return pointRotateRads13(
22334
23024
  pointFrom16(x - (x1 + x2) / 2, y - y1),
22335
23025
  pointFrom16(0, 0),
22336
- angle
23026
+ angle2
22337
23027
  );
22338
23028
  case "s":
22339
23029
  return pointRotateRads13(
22340
23030
  pointFrom16(x - (x1 + x2) / 2, y - y2),
22341
23031
  pointFrom16(0, 0),
22342
- angle
23032
+ angle2
22343
23033
  );
22344
23034
  case "w":
22345
23035
  return pointRotateRads13(
22346
23036
  pointFrom16(x - x1, y - (y1 + y2) / 2),
22347
23037
  pointFrom16(0, 0),
22348
- angle
23038
+ angle2
22349
23039
  );
22350
23040
  case "e":
22351
23041
  return pointRotateRads13(
22352
23042
  pointFrom16(x - x2, y - (y1 + y2) / 2),
22353
23043
  pointFrom16(0, 0),
22354
- angle
23044
+ angle2
22355
23045
  );
22356
23046
  case "nw":
22357
- return pointRotateRads13(pointFrom16(x - x1, y - y1), pointFrom16(0, 0), angle);
23047
+ return pointRotateRads13(pointFrom16(x - x1, y - y1), pointFrom16(0, 0), angle2);
22358
23048
  case "ne":
22359
- return pointRotateRads13(pointFrom16(x - x2, y - y1), pointFrom16(0, 0), angle);
23049
+ return pointRotateRads13(pointFrom16(x - x2, y - y1), pointFrom16(0, 0), angle2);
22360
23050
  case "sw":
22361
- return pointRotateRads13(pointFrom16(x - x1, y - y2), pointFrom16(0, 0), angle);
23051
+ return pointRotateRads13(pointFrom16(x - x1, y - y2), pointFrom16(0, 0), angle2);
22362
23052
  case "se":
22363
- return pointRotateRads13(pointFrom16(x - x2, y - y2), pointFrom16(0, 0), angle);
23053
+ return pointRotateRads13(pointFrom16(x - x2, y - y2), pointFrom16(0, 0), angle2);
22364
23054
  default:
22365
23055
  return [0, 0];
22366
23056
  }
@@ -22404,7 +23094,7 @@ var getResizeAnchor = (handleDirection, shouldMaintainAspectRatio, shouldResizeF
22404
23094
  }
22405
23095
  return "top-right";
22406
23096
  };
22407
- var getResizedOrigin = (prevOrigin, prevWidth, prevHeight, newWidth, newHeight, angle, handleDirection, shouldMaintainAspectRatio, shouldResizeFromCenter) => {
23097
+ var getResizedOrigin = (prevOrigin, prevWidth, prevHeight, newWidth, newHeight, angle2, handleDirection, shouldMaintainAspectRatio, shouldResizeFromCenter) => {
22408
23098
  const anchor = getResizeAnchor(
22409
23099
  handleDirection,
22410
23100
  shouldMaintainAspectRatio,
@@ -22414,23 +23104,23 @@ var getResizedOrigin = (prevOrigin, prevWidth, prevHeight, newWidth, newHeight,
22414
23104
  switch (anchor) {
22415
23105
  case "top-left":
22416
23106
  return {
22417
- x: x + (prevWidth - newWidth) / 2 + (newWidth - prevWidth) / 2 * Math.cos(angle) + (prevHeight - newHeight) / 2 * Math.sin(angle),
22418
- y: y + (prevHeight - newHeight) / 2 + (newWidth - prevWidth) / 2 * Math.sin(angle) + (newHeight - prevHeight) / 2 * Math.cos(angle)
23107
+ x: x + (prevWidth - newWidth) / 2 + (newWidth - prevWidth) / 2 * Math.cos(angle2) + (prevHeight - newHeight) / 2 * Math.sin(angle2),
23108
+ y: y + (prevHeight - newHeight) / 2 + (newWidth - prevWidth) / 2 * Math.sin(angle2) + (newHeight - prevHeight) / 2 * Math.cos(angle2)
22419
23109
  };
22420
23110
  case "top-right":
22421
23111
  return {
22422
- x: x + (prevWidth - newWidth) / 2 * (Math.cos(angle) + 1) + (prevHeight - newHeight) / 2 * Math.sin(angle),
22423
- y: y + (prevHeight - newHeight) / 2 + (prevWidth - newWidth) / 2 * Math.sin(angle) + (newHeight - prevHeight) / 2 * Math.cos(angle)
23112
+ x: x + (prevWidth - newWidth) / 2 * (Math.cos(angle2) + 1) + (prevHeight - newHeight) / 2 * Math.sin(angle2),
23113
+ y: y + (prevHeight - newHeight) / 2 + (prevWidth - newWidth) / 2 * Math.sin(angle2) + (newHeight - prevHeight) / 2 * Math.cos(angle2)
22424
23114
  };
22425
23115
  case "bottom-left":
22426
23116
  return {
22427
- x: x + (prevWidth - newWidth) / 2 * (1 - Math.cos(angle)) + (newHeight - prevHeight) / 2 * Math.sin(angle),
22428
- y: y + (prevHeight - newHeight) / 2 * (Math.cos(angle) + 1) + (newWidth - prevWidth) / 2 * Math.sin(angle)
23117
+ x: x + (prevWidth - newWidth) / 2 * (1 - Math.cos(angle2)) + (newHeight - prevHeight) / 2 * Math.sin(angle2),
23118
+ y: y + (prevHeight - newHeight) / 2 * (Math.cos(angle2) + 1) + (newWidth - prevWidth) / 2 * Math.sin(angle2)
22429
23119
  };
22430
23120
  case "bottom-right":
22431
23121
  return {
22432
- x: x + (prevWidth - newWidth) / 2 * (Math.cos(angle) + 1) + (newHeight - prevHeight) / 2 * Math.sin(angle),
22433
- y: y + (prevHeight - newHeight) / 2 * (Math.cos(angle) + 1) + (prevWidth - newWidth) / 2 * Math.sin(angle)
23122
+ x: x + (prevWidth - newWidth) / 2 * (Math.cos(angle2) + 1) + (newHeight - prevHeight) / 2 * Math.sin(angle2),
23123
+ y: y + (prevHeight - newHeight) / 2 * (Math.cos(angle2) + 1) + (prevWidth - newWidth) / 2 * Math.sin(angle2)
22434
23124
  };
22435
23125
  case "center":
22436
23126
  return {
@@ -22439,23 +23129,23 @@ var getResizedOrigin = (prevOrigin, prevWidth, prevHeight, newWidth, newHeight,
22439
23129
  };
22440
23130
  case "east-side":
22441
23131
  return {
22442
- x: x + (prevWidth - newWidth) / 2 * (Math.cos(angle) + 1),
22443
- y: y + (prevWidth - newWidth) / 2 * Math.sin(angle) + (prevHeight - newHeight) / 2
23132
+ x: x + (prevWidth - newWidth) / 2 * (Math.cos(angle2) + 1),
23133
+ y: y + (prevWidth - newWidth) / 2 * Math.sin(angle2) + (prevHeight - newHeight) / 2
22444
23134
  };
22445
23135
  case "west-side":
22446
23136
  return {
22447
- x: x + (prevWidth - newWidth) / 2 * (1 - Math.cos(angle)),
22448
- y: y + (newWidth - prevWidth) / 2 * Math.sin(angle) + (prevHeight - newHeight) / 2
23137
+ x: x + (prevWidth - newWidth) / 2 * (1 - Math.cos(angle2)),
23138
+ y: y + (newWidth - prevWidth) / 2 * Math.sin(angle2) + (prevHeight - newHeight) / 2
22449
23139
  };
22450
23140
  case "north-side":
22451
23141
  return {
22452
- x: x + (prevWidth - newWidth) / 2 + (prevHeight - newHeight) / 2 * Math.sin(angle),
22453
- y: y + (newHeight - prevHeight) / 2 * (Math.cos(angle) - 1)
23142
+ x: x + (prevWidth - newWidth) / 2 + (prevHeight - newHeight) / 2 * Math.sin(angle2),
23143
+ y: y + (newHeight - prevHeight) / 2 * (Math.cos(angle2) - 1)
22454
23144
  };
22455
23145
  case "south-side":
22456
23146
  return {
22457
- x: x + (prevWidth - newWidth) / 2 + (newHeight - prevHeight) / 2 * Math.sin(angle),
22458
- y: y + (prevHeight - newHeight) / 2 * (Math.cos(angle) + 1)
23147
+ x: x + (prevWidth - newWidth) / 2 + (newHeight - prevHeight) / 2 * Math.sin(angle2),
23148
+ y: y + (prevHeight - newHeight) / 2 * (Math.cos(angle2) + 1)
22459
23149
  };
22460
23150
  }
22461
23151
  };
@@ -22877,7 +23567,7 @@ var resizeMultipleElements = (selectedElements, elementsMap, handleDirection, sc
22877
23567
  }
22878
23568
  const width2 = orig.width * scaleX;
22879
23569
  const height2 = orig.height * scaleY;
22880
- const angle = normalizeRadians2(
23570
+ const angle2 = normalizeRadians2(
22881
23571
  orig.angle * flipFactorX * flipFactorY
22882
23572
  );
22883
23573
  const isLinearOrFreeDraw = isLinearElement(orig) || isFreeDrawElement(orig);
@@ -22898,7 +23588,7 @@ var resizeMultipleElements = (selectedElements, elementsMap, handleDirection, sc
22898
23588
  y,
22899
23589
  width: width2,
22900
23590
  height: height2,
22901
- angle,
23591
+ angle: angle2,
22902
23592
  ...rescaledPoints
22903
23593
  };
22904
23594
  if (isElbowArrow(orig)) {
@@ -22966,7 +23656,7 @@ var resizeMultipleElements = (selectedElements, elementsMap, handleDirection, sc
22966
23656
  element,
22967
23657
  update: { boundTextFontSize, ...update }
22968
23658
  } of elementsAndUpdates) {
22969
- const { angle } = update;
23659
+ const { angle: angle2 } = update;
22970
23660
  scene.mutateElement(element, update);
22971
23661
  updateBoundElements(element, scene, {
22972
23662
  simultaneouslyUpdated: elementsToUpdate
@@ -22987,7 +23677,7 @@ var resizeMultipleElements = (selectedElements, elementsMap, handleDirection, sc
22987
23677
  if (boundTextElement && boundTextFontSize) {
22988
23678
  scene.mutateElement(boundTextElement, {
22989
23679
  fontSize: boundTextFontSize,
22990
- angle: isLinearElement(element) ? void 0 : angle
23680
+ angle: isLinearElement(element) ? void 0 : angle2
22991
23681
  });
22992
23682
  handleBindTextResize(element, scene, handleDirection, true);
22993
23683
  }
@@ -23052,11 +23742,11 @@ var OMIT_SIDES_FOR_LINE_BACKSLASH = {
23052
23742
  n: true,
23053
23743
  w: true
23054
23744
  };
23055
- var generateTransformHandle = (x, y, width, height, cx, cy, angle) => {
23745
+ var generateTransformHandle = (x, y, width, height, cx, cy, angle2) => {
23056
23746
  const [xx, yy] = pointRotateRads14(
23057
23747
  pointFrom17(x + width / 2, y + height / 2),
23058
23748
  pointFrom17(cx, cy),
23059
- angle
23749
+ angle2
23060
23750
  );
23061
23751
  return [xx - width / 2, yy - height / 2, width, height];
23062
23752
  };
@@ -23072,7 +23762,7 @@ var getOmitSidesForEditorInterface = (editorInterface) => {
23072
23762
  }
23073
23763
  return {};
23074
23764
  };
23075
- var getTransformHandlesFromCoords = ([x1, y1, x2, y2, cx, cy], angle, zoom, pointerType, omitSides = {}, margin = 4, spacing = DEFAULT_TRANSFORM_HANDLE_SPACING) => {
23765
+ var getTransformHandlesFromCoords = ([x1, y1, x2, y2, cx, cy], angle2, zoom, pointerType, omitSides = {}, margin = 4, spacing = DEFAULT_TRANSFORM_HANDLE_SPACING) => {
23076
23766
  const size = transformHandleSizes[pointerType];
23077
23767
  const handleWidth = size / zoom.value;
23078
23768
  const handleHeight = size / zoom.value;
@@ -23090,7 +23780,7 @@ var getTransformHandlesFromCoords = ([x1, y1, x2, y2, cx, cy], angle, zoom, poin
23090
23780
  handleHeight,
23091
23781
  cx,
23092
23782
  cy,
23093
- angle
23783
+ angle2
23094
23784
  ),
23095
23785
  ne: omitSides.ne ? void 0 : generateTransformHandle(
23096
23786
  x2 + dashedLineMargin - centeringOffset,
@@ -23099,7 +23789,7 @@ var getTransformHandlesFromCoords = ([x1, y1, x2, y2, cx, cy], angle, zoom, poin
23099
23789
  handleHeight,
23100
23790
  cx,
23101
23791
  cy,
23102
- angle
23792
+ angle2
23103
23793
  ),
23104
23794
  sw: omitSides.sw ? void 0 : generateTransformHandle(
23105
23795
  x1 - dashedLineMargin - handleMarginX + centeringOffset,
@@ -23108,7 +23798,7 @@ var getTransformHandlesFromCoords = ([x1, y1, x2, y2, cx, cy], angle, zoom, poin
23108
23798
  handleHeight,
23109
23799
  cx,
23110
23800
  cy,
23111
- angle
23801
+ angle2
23112
23802
  ),
23113
23803
  se: omitSides.se ? void 0 : generateTransformHandle(
23114
23804
  x2 + dashedLineMargin - centeringOffset,
@@ -23117,7 +23807,7 @@ var getTransformHandlesFromCoords = ([x1, y1, x2, y2, cx, cy], angle, zoom, poin
23117
23807
  handleHeight,
23118
23808
  cx,
23119
23809
  cy,
23120
- angle
23810
+ angle2
23121
23811
  ),
23122
23812
  rotation: omitSides.rotation ? void 0 : generateTransformHandle(
23123
23813
  x1 + width / 2 - handleWidth / 2,
@@ -23126,7 +23816,7 @@ var getTransformHandlesFromCoords = ([x1, y1, x2, y2, cx, cy], angle, zoom, poin
23126
23816
  handleHeight,
23127
23817
  cx,
23128
23818
  cy,
23129
- angle
23819
+ angle2
23130
23820
  )
23131
23821
  };
23132
23822
  const minimumSizeForEightHandles = 5 * transformHandleSizes.mouse / zoom.value;
@@ -23139,7 +23829,7 @@ var getTransformHandlesFromCoords = ([x1, y1, x2, y2, cx, cy], angle, zoom, poin
23139
23829
  handleHeight,
23140
23830
  cx,
23141
23831
  cy,
23142
- angle
23832
+ angle2
23143
23833
  );
23144
23834
  }
23145
23835
  if (!omitSides.s) {
@@ -23150,7 +23840,7 @@ var getTransformHandlesFromCoords = ([x1, y1, x2, y2, cx, cy], angle, zoom, poin
23150
23840
  handleHeight,
23151
23841
  cx,
23152
23842
  cy,
23153
- angle
23843
+ angle2
23154
23844
  );
23155
23845
  }
23156
23846
  }
@@ -23163,7 +23853,7 @@ var getTransformHandlesFromCoords = ([x1, y1, x2, y2, cx, cy], angle, zoom, poin
23163
23853
  handleHeight,
23164
23854
  cx,
23165
23855
  cy,
23166
- angle
23856
+ angle2
23167
23857
  );
23168
23858
  }
23169
23859
  if (!omitSides.e) {
@@ -23174,7 +23864,7 @@ var getTransformHandlesFromCoords = ([x1, y1, x2, y2, cx, cy], angle, zoom, poin
23174
23864
  handleHeight,
23175
23865
  cx,
23176
23866
  cy,
23177
- angle
23867
+ angle2
23178
23868
  );
23179
23869
  }
23180
23870
  }
@@ -23343,10 +24033,10 @@ var getTransformHandleTypeFromCoords = ([x1, y1, x2, y2], scenePointerX, scenePo
23343
24033
  return false;
23344
24034
  };
23345
24035
  var RESIZE_CURSORS = ["ns", "nesw", "ew", "nwse"];
23346
- var rotateResizeCursor = (cursor, angle) => {
24036
+ var rotateResizeCursor = (cursor, angle2) => {
23347
24037
  const index = RESIZE_CURSORS.indexOf(cursor);
23348
24038
  if (index >= 0) {
23349
- const a2 = Math.round(angle / (Math.PI / 4));
24039
+ const a2 = Math.round(angle2 / (Math.PI / 4));
23350
24040
  cursor = RESIZE_CURSORS[(index + a2) % RESIZE_CURSORS.length];
23351
24041
  }
23352
24042
  return cursor;
@@ -23388,11 +24078,11 @@ var getCursorForResizingElement = (resizingElement) => {
23388
24078
  }
23389
24079
  return cursor ? `${cursor}-resize` : "";
23390
24080
  };
23391
- var getSelectionBorders = ([x1, y1], [x2, y2], center, angle) => {
23392
- const topLeft = pointRotateRads15(pointFrom18(x1, y1), center, angle);
23393
- const topRight = pointRotateRads15(pointFrom18(x2, y1), center, angle);
23394
- const bottomLeft = pointRotateRads15(pointFrom18(x1, y2), center, angle);
23395
- const bottomRight = pointRotateRads15(pointFrom18(x2, y2), center, angle);
24081
+ var getSelectionBorders = ([x1, y1], [x2, y2], center, angle2) => {
24082
+ const topLeft = pointRotateRads15(pointFrom18(x1, y1), center, angle2);
24083
+ const topRight = pointRotateRads15(pointFrom18(x2, y1), center, angle2);
24084
+ const bottomLeft = pointRotateRads15(pointFrom18(x1, y2), center, angle2);
24085
+ const bottomRight = pointRotateRads15(pointFrom18(x2, y2), center, angle2);
23396
24086
  return {
23397
24087
  n: [topLeft, topRight],
23398
24088
  e: [topRight, bottomRight],
@@ -24631,6 +25321,96 @@ var SloppinessCartoonistIcon = createIcon(
24631
25321
  ),
24632
25322
  modifiedTablerIconProps
24633
25323
  );
25324
+ var strokeVariabilityConstantIcon = createIcon(
25325
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { children: [
25326
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
25327
+ "path",
25328
+ {
25329
+ d: "M4 12 C 5 8, 6 8, 8 12",
25330
+ fill: "none",
25331
+ strokeWidth: "1",
25332
+ strokeLinecap: "round",
25333
+ strokeLinejoin: "round"
25334
+ }
25335
+ ),
25336
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
25337
+ "path",
25338
+ {
25339
+ d: "M8 12 C 9 16, 10 16, 12 12",
25340
+ fill: "none",
25341
+ strokeWidth: "1",
25342
+ strokeLinecap: "round",
25343
+ strokeLinejoin: "round"
25344
+ }
25345
+ ),
25346
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
25347
+ "path",
25348
+ {
25349
+ d: "M12 12 C 14 8, 15 8, 16 12",
25350
+ fill: "none",
25351
+ strokeWidth: "1",
25352
+ strokeLinecap: "round",
25353
+ strokeLinejoin: "round"
25354
+ }
25355
+ ),
25356
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
25357
+ "path",
25358
+ {
25359
+ d: "M16 12 C 17 16, 18 16, 19 12",
25360
+ fill: "none",
25361
+ strokeWidth: "1",
25362
+ strokeLinecap: "round",
25363
+ strokeLinejoin: "round"
25364
+ }
25365
+ )
25366
+ ] }),
25367
+ tablerIconProps
25368
+ );
25369
+ var strokeVariabilityVariableIcon = createIcon(
25370
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { children: [
25371
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
25372
+ "path",
25373
+ {
25374
+ d: "M4 12 C 5 8, 6 8, 8 12",
25375
+ fill: "none",
25376
+ strokeWidth: "1.5",
25377
+ strokeLinecap: "round",
25378
+ strokeLinejoin: "round"
25379
+ }
25380
+ ),
25381
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
25382
+ "path",
25383
+ {
25384
+ d: "M8 12 C 9 16, 10 16, 12 12",
25385
+ fill: "none",
25386
+ strokeWidth: "2",
25387
+ strokeLinecap: "round",
25388
+ strokeLinejoin: "round"
25389
+ }
25390
+ ),
25391
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
25392
+ "path",
25393
+ {
25394
+ d: "M12 12 C 14 8, 15 8, 16 12",
25395
+ fill: "none",
25396
+ strokeWidth: "2.75",
25397
+ strokeLinecap: "round",
25398
+ strokeLinejoin: "round"
25399
+ }
25400
+ ),
25401
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
25402
+ "path",
25403
+ {
25404
+ d: "M16 12 C 17 16, 18 16, 19 12",
25405
+ fill: "none",
25406
+ strokeWidth: "3.25",
25407
+ strokeLinecap: "round",
25408
+ strokeLinejoin: "round"
25409
+ }
25410
+ )
25411
+ ] }),
25412
+ tablerIconProps
25413
+ );
24634
25414
  var EdgeSharpIcon = createIcon(
24635
25415
  /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("svg", { strokeWidth: "1.5", children: [
24636
25416
  /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: "M3.33334 9.99998V6.66665C3.33334 6.04326 3.33403 4.9332 3.33539 3.33646C4.95233 3.33436 6.06276 3.33331 6.66668 3.33331H10" }),
@@ -26681,6 +27461,7 @@ export {
26681
27461
  frameAndChildrenSelectedTogether,
26682
27462
  generateLinearCollisionShape,
26683
27463
  generateRoughOptions,
27464
+ generateThumbHash,
26684
27465
  getActiveTextElement,
26685
27466
  getAllHoveredElementAtPoint,
26686
27467
  getApproxMinLineHeight,
@@ -26785,6 +27566,7 @@ export {
26785
27566
  getTextFromElements,
26786
27567
  getTextHeight,
26787
27568
  getTextWidth,
27569
+ getThumbHashPlaceholder,
26788
27570
  getToolbarTools,
26789
27571
  getTransformHandleTypeFromCoords,
26790
27572
  getTransformHandles,
@@ -26806,6 +27588,7 @@ export {
26806
27588
  hasBackground,
26807
27589
  hasBoundTextElement,
26808
27590
  hasBoundingBox,
27591
+ hasFreedrawMode,
26809
27592
  hasStrokeColor,
26810
27593
  hasStrokeStyle,
26811
27594
  hasStrokeWidth,