@tldraw/editor 5.4.2 → 5.5.0-canary.0241f11f2e99

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 (135) hide show
  1. package/dist-cjs/index.d.ts +7 -2
  2. package/dist-cjs/index.js +1 -1
  3. package/dist-cjs/lib/constants.js +2 -0
  4. package/dist-cjs/lib/constants.js.map +2 -2
  5. package/dist-cjs/lib/editor/Editor.js +131 -151
  6. package/dist-cjs/lib/editor/Editor.js.map +3 -3
  7. package/dist-cjs/lib/editor/derivations/bindingsIndex.js +2 -0
  8. package/dist-cjs/lib/editor/derivations/bindingsIndex.js.map +2 -2
  9. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js +10 -6
  10. package/dist-cjs/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.js.map +2 -2
  11. package/dist-cjs/lib/editor/managers/ThemeManager/ThemeManager.js +7 -3
  12. package/dist-cjs/lib/editor/managers/ThemeManager/ThemeManager.js.map +2 -2
  13. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js +14 -6
  14. package/dist-cjs/lib/editor/shapes/group/GroupShapeUtil.js.map +3 -3
  15. package/dist-cjs/lib/exports/ExportDelay.js +10 -2
  16. package/dist-cjs/lib/exports/ExportDelay.js.map +2 -2
  17. package/dist-cjs/lib/exports/FontEmbedder.js +25 -11
  18. package/dist-cjs/lib/exports/FontEmbedder.js.map +2 -2
  19. package/dist-cjs/lib/exports/StyleEmbedder.js +9 -4
  20. package/dist-cjs/lib/exports/StyleEmbedder.js.map +2 -2
  21. package/dist-cjs/lib/exports/fetchCache.js +3 -0
  22. package/dist-cjs/lib/exports/fetchCache.js.map +2 -2
  23. package/dist-cjs/lib/exports/getSvgAsImage.js +10 -4
  24. package/dist-cjs/lib/exports/getSvgAsImage.js.map +2 -2
  25. package/dist-cjs/lib/exports/getSvgJsx.js +18 -9
  26. package/dist-cjs/lib/exports/getSvgJsx.js.map +2 -2
  27. package/dist-cjs/lib/exports/parseCss.js +1 -1
  28. package/dist-cjs/lib/exports/parseCss.js.map +1 -1
  29. package/dist-cjs/lib/license/LicenseManager.js +9 -1
  30. package/dist-cjs/lib/license/LicenseManager.js.map +2 -2
  31. package/dist-cjs/lib/license/LicenseProvider.js +12 -4
  32. package/dist-cjs/lib/license/LicenseProvider.js.map +2 -2
  33. package/dist-cjs/lib/primitives/geometry/Arc2d.js +1 -1
  34. package/dist-cjs/lib/primitives/geometry/Arc2d.js.map +2 -2
  35. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js +1 -1
  36. package/dist-cjs/lib/primitives/geometry/CubicBezier2d.js.map +2 -2
  37. package/dist-cjs/lib/primitives/geometry/Edge2d.js +1 -1
  38. package/dist-cjs/lib/primitives/geometry/Edge2d.js.map +2 -2
  39. package/dist-cjs/lib/primitives/geometry/Group2d.js +8 -5
  40. package/dist-cjs/lib/primitives/geometry/Group2d.js.map +2 -2
  41. package/dist-cjs/lib/primitives/geometry/Point2d.js +1 -1
  42. package/dist-cjs/lib/primitives/geometry/Point2d.js.map +2 -2
  43. package/dist-cjs/lib/utils/sync/LocalIndexedDb.js +13 -17
  44. package/dist-cjs/lib/utils/sync/LocalIndexedDb.js.map +2 -2
  45. package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js +78 -40
  46. package/dist-cjs/lib/utils/sync/TLLocalSyncClient.js.map +2 -2
  47. package/dist-cjs/version.js +3 -3
  48. package/dist-cjs/version.js.map +1 -1
  49. package/dist-esm/index.d.mts +7 -2
  50. package/dist-esm/index.mjs +1 -1
  51. package/dist-esm/lib/constants.mjs +2 -0
  52. package/dist-esm/lib/constants.mjs.map +2 -2
  53. package/dist-esm/lib/editor/Editor.mjs +132 -151
  54. package/dist-esm/lib/editor/Editor.mjs.map +3 -3
  55. package/dist-esm/lib/editor/derivations/bindingsIndex.mjs +2 -0
  56. package/dist-esm/lib/editor/derivations/bindingsIndex.mjs.map +2 -2
  57. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs +10 -6
  58. package/dist-esm/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.mjs.map +2 -2
  59. package/dist-esm/lib/editor/managers/ThemeManager/ThemeManager.mjs +7 -3
  60. package/dist-esm/lib/editor/managers/ThemeManager/ThemeManager.mjs.map +2 -2
  61. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs +14 -6
  62. package/dist-esm/lib/editor/shapes/group/GroupShapeUtil.mjs.map +3 -3
  63. package/dist-esm/lib/exports/ExportDelay.mjs +11 -3
  64. package/dist-esm/lib/exports/ExportDelay.mjs.map +2 -2
  65. package/dist-esm/lib/exports/FontEmbedder.mjs +25 -11
  66. package/dist-esm/lib/exports/FontEmbedder.mjs.map +2 -2
  67. package/dist-esm/lib/exports/StyleEmbedder.mjs +9 -4
  68. package/dist-esm/lib/exports/StyleEmbedder.mjs.map +2 -2
  69. package/dist-esm/lib/exports/fetchCache.mjs +3 -0
  70. package/dist-esm/lib/exports/fetchCache.mjs.map +2 -2
  71. package/dist-esm/lib/exports/getSvgAsImage.mjs +10 -4
  72. package/dist-esm/lib/exports/getSvgAsImage.mjs.map +2 -2
  73. package/dist-esm/lib/exports/getSvgJsx.mjs +18 -9
  74. package/dist-esm/lib/exports/getSvgJsx.mjs.map +2 -2
  75. package/dist-esm/lib/exports/parseCss.mjs +1 -1
  76. package/dist-esm/lib/exports/parseCss.mjs.map +1 -1
  77. package/dist-esm/lib/license/LicenseManager.mjs +9 -1
  78. package/dist-esm/lib/license/LicenseManager.mjs.map +2 -2
  79. package/dist-esm/lib/license/LicenseProvider.mjs +13 -5
  80. package/dist-esm/lib/license/LicenseProvider.mjs.map +2 -2
  81. package/dist-esm/lib/primitives/geometry/Arc2d.mjs +1 -1
  82. package/dist-esm/lib/primitives/geometry/Arc2d.mjs.map +2 -2
  83. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs +1 -1
  84. package/dist-esm/lib/primitives/geometry/CubicBezier2d.mjs.map +2 -2
  85. package/dist-esm/lib/primitives/geometry/Edge2d.mjs +1 -1
  86. package/dist-esm/lib/primitives/geometry/Edge2d.mjs.map +2 -2
  87. package/dist-esm/lib/primitives/geometry/Group2d.mjs +12 -6
  88. package/dist-esm/lib/primitives/geometry/Group2d.mjs.map +2 -2
  89. package/dist-esm/lib/primitives/geometry/Point2d.mjs +1 -1
  90. package/dist-esm/lib/primitives/geometry/Point2d.mjs.map +2 -2
  91. package/dist-esm/lib/utils/sync/LocalIndexedDb.mjs +13 -17
  92. package/dist-esm/lib/utils/sync/LocalIndexedDb.mjs.map +2 -2
  93. package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs +78 -40
  94. package/dist-esm/lib/utils/sync/TLLocalSyncClient.mjs.map +2 -2
  95. package/dist-esm/version.mjs +3 -3
  96. package/dist-esm/version.mjs.map +1 -1
  97. package/package.json +7 -7
  98. package/src/lib/constants.ts +8 -0
  99. package/src/lib/editor/Editor.test.ts +20 -0
  100. package/src/lib/editor/Editor.ts +202 -255
  101. package/src/lib/editor/derivations/bindingsIndex.ts +3 -0
  102. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.test.ts +35 -4
  103. package/src/lib/editor/managers/EdgeScrollManager/EdgeScrollManager.ts +13 -7
  104. package/src/lib/editor/managers/ThemeManager/ThemeManager.test.ts +53 -0
  105. package/src/lib/editor/managers/ThemeManager/ThemeManager.ts +9 -3
  106. package/src/lib/editor/shapes/group/GroupShapeUtil.tsx +23 -8
  107. package/src/lib/exports/ExportDelay.tsx +16 -4
  108. package/src/lib/exports/FontEmbedder.test.ts +99 -0
  109. package/src/lib/exports/FontEmbedder.ts +32 -11
  110. package/src/lib/exports/StyleEmbedder.ts +13 -4
  111. package/src/lib/exports/fetchCache.test.ts +22 -0
  112. package/src/lib/exports/fetchCache.ts +4 -0
  113. package/src/lib/exports/getSvgAsImage.ts +13 -5
  114. package/src/lib/exports/getSvgJsx.test.ts +43 -1
  115. package/src/lib/exports/getSvgJsx.tsx +28 -16
  116. package/src/lib/exports/parseCss.test.ts +4 -0
  117. package/src/lib/exports/parseCss.ts +2 -2
  118. package/src/lib/license/LicenseManager.ts +13 -1
  119. package/src/lib/license/LicenseProvider.test.tsx +76 -0
  120. package/src/lib/license/LicenseProvider.tsx +19 -5
  121. package/src/lib/primitives/geometry/Arc2d.test.ts +9 -0
  122. package/src/lib/primitives/geometry/Arc2d.ts +1 -1
  123. package/src/lib/primitives/geometry/CubicBezier2d.test.ts +13 -0
  124. package/src/lib/primitives/geometry/CubicBezier2d.ts +1 -1
  125. package/src/lib/primitives/geometry/Edge2d.test.ts +21 -3
  126. package/src/lib/primitives/geometry/Edge2d.ts +1 -1
  127. package/src/lib/primitives/geometry/Group2d.test.ts +26 -5
  128. package/src/lib/primitives/geometry/Group2d.ts +18 -7
  129. package/src/lib/primitives/geometry/Point2d.test.ts +14 -3
  130. package/src/lib/primitives/geometry/Point2d.ts +1 -1
  131. package/src/lib/utils/sync/LocalIndexedDb.test.ts +28 -0
  132. package/src/lib/utils/sync/LocalIndexedDb.ts +18 -20
  133. package/src/lib/utils/sync/TLLocalSyncClient.test.ts +236 -0
  134. package/src/lib/utils/sync/TLLocalSyncClient.ts +106 -54
  135. package/src/version.ts +3 -3
@@ -75,6 +75,7 @@ import {
75
75
  import {
76
76
  DEFAULT_ANIMATION_OPTIONS,
77
77
  DEFAULT_CAMERA_OPTIONS,
78
+ FRAME_MS_60HZ,
78
79
  INTERNAL_POINTER_IDS,
79
80
  LEFT_MOUSE_BUTTON,
80
81
  MIDDLE_MOUSE_BUTTON,
@@ -135,6 +136,10 @@ import { UserPreferencesManager } from "./managers/UserPreferencesManager/UserPr
135
136
  import { OverlayManager } from "./overlays/OverlayManager.mjs";
136
137
  import { RootState } from "./tools/RootState.mjs";
137
138
  const RENDERING_SHAPES_SORT_CACHE_THRESHOLD = 100;
139
+ const AXIS = {
140
+ horizontal: { val: "x", min: "minX", max: "maxX", dim: "width" },
141
+ vertical: { val: "y", min: "minY", max: "maxY", dim: "height" }
142
+ };
138
143
  class Editor extends EventEmitter {
139
144
  id = uniqueId();
140
145
  constructor({
@@ -461,7 +466,7 @@ class Editor extends EventEmitter {
461
466
  }
462
467
  }
463
468
  if (deleteBindingIds.length) {
464
- this.deleteBindings(deleteBindingIds);
469
+ this.store.remove(deleteBindingIds);
465
470
  }
466
471
  }
467
472
  },
@@ -1421,6 +1426,7 @@ class Editor extends EventEmitter {
1421
1426
  * @public
1422
1427
  **/
1423
1428
  updateDocumentSettings(settings) {
1429
+ if (this.getIsReadonly()) return this;
1424
1430
  this.run(
1425
1431
  () => {
1426
1432
  this.store.put([{ ...this.getDocumentSettings(), ...settings }]);
@@ -1580,7 +1586,7 @@ class Editor extends EventEmitter {
1580
1586
  * @public
1581
1587
  */
1582
1588
  select(...shapes) {
1583
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((shape) => shape.id);
1589
+ const ids = toShapeIds(shapes);
1584
1590
  this.setSelectedShapes(ids);
1585
1591
  return this;
1586
1592
  }
@@ -1595,7 +1601,7 @@ class Editor extends EventEmitter {
1595
1601
  * @public
1596
1602
  */
1597
1603
  deselect(...shapes) {
1598
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((shape) => shape.id);
1604
+ const ids = toShapeIds(shapes);
1599
1605
  const selectedShapeIds = this.getSelectedShapeIds();
1600
1606
  if (selectedShapeIds.length > 0 && ids.length > 0) {
1601
1607
  this.setSelectedShapes(selectedShapeIds.filter((id) => !ids.includes(id)));
@@ -1633,7 +1639,7 @@ class Editor extends EventEmitter {
1633
1639
  }
1634
1640
  const ids = this.getSortedChildIdsForParent(parentToSelectWithinId);
1635
1641
  if (ids.length <= 0) return this;
1636
- this.setSelectedShapes(this._getUnlockedShapeIds(ids));
1642
+ this.setSelectedShapes(ids.filter((id) => !this.getShape(id)?.isLocked));
1637
1643
  return this;
1638
1644
  }
1639
1645
  /**
@@ -1650,14 +1656,18 @@ class Editor extends EventEmitter {
1650
1656
  const selectedShapeIds = this.getSelectedShapeIds();
1651
1657
  const firstParentId = selectedShapeIds[0] ? this.getShape(selectedShapeIds[0])?.parentId : null;
1652
1658
  const isSelectedWithinContainer = firstParentId && selectedShapeIds.every((shapeId) => this.getShape(shapeId)?.parentId === firstParentId) && !isPageId(firstParentId);
1653
- const filteredShapes = isSelectedWithinContainer ? this.getCurrentPageShapes().filter((shape2) => shape2.parentId === firstParentId) : this.getCurrentPageShapes().filter((shape2) => isPageId(shape2.parentId));
1654
- const readingOrderShapes = isSelectedWithinContainer ? this._getShapesInReadingOrder(filteredShapes) : this.getCurrentPageShapesInReadingOrder();
1659
+ const filteredShapes = this.getCurrentPageShapes().filter(
1660
+ (shape2) => !this.isShapeOrAncestorLocked(shape2) && (isSelectedWithinContainer ? shape2.parentId === firstParentId : isPageId(shape2.parentId))
1661
+ );
1662
+ const readingOrderShapes = this._getShapesInReadingOrder(filteredShapes);
1655
1663
  const currentShapeId = selectedShapeIds.length === 1 ? selectedShapeIds[0] : readingOrderShapes.find((shape2) => selectedShapeIds.includes(shape2.id))?.id;
1656
1664
  let adjacentShapeId;
1657
1665
  if (direction === "next" || direction === "prev") {
1658
1666
  const shapeIds = readingOrderShapes.map((shape2) => shape2.id);
1667
+ if (shapeIds.length === 0) return;
1659
1668
  const currentIndex = currentShapeId ? shapeIds.indexOf(currentShapeId) : -1;
1660
- const adjacentIndex = (currentIndex + (direction === "next" ? 1 : -1) + shapeIds.length) % shapeIds.length;
1669
+ const startIndex = currentIndex === -1 && direction === "prev" ? 0 : currentIndex;
1670
+ const adjacentIndex = (startIndex + (direction === "next" ? 1 : -1) + shapeIds.length) % shapeIds.length;
1661
1671
  adjacentShapeId = shapeIds[adjacentIndex];
1662
1672
  } else {
1663
1673
  if (!currentShapeId) return;
@@ -1756,13 +1766,14 @@ class Editor extends EventEmitter {
1756
1766
  });
1757
1767
  if (shapesInDirection.length === 0) return currentShapeId;
1758
1768
  const lowestScoringShape = minBy(shapesInDirection, ({ center }) => {
1759
- const distance = Vec.Dist2(currentCenter, center);
1769
+ const distance = Vec.Dist(currentCenter, center);
1760
1770
  const dirProp = ["left", "right"].includes(direction) ? "x" : "y";
1761
1771
  const directionalDistance = Math.abs(center[dirProp] - currentCenter[dirProp]);
1762
1772
  const offProp = ["left", "right"].includes(direction) ? "y" : "x";
1763
1773
  const offAxisDeviation = Math.abs(center[offProp] - currentCenter[offProp]);
1764
- const angle = Math.abs(Vec.Angle(currentCenter, center) * (180 / Math.PI));
1765
- const angleDeviation = Math.abs(angle - directionToAngle[direction]);
1774
+ const angle = (Vec.Angle(currentCenter, center) * (180 / Math.PI) + 360) % 360;
1775
+ const rawAngleDeviation = Math.abs(angle - directionToAngle[direction]);
1776
+ const angleDeviation = Math.min(rawAngleDeviation, 360 - rawAngleDeviation);
1766
1777
  return distance * 1 + // Base distance
1767
1778
  offAxisDeviation * 2 + // Heavy penalty for off-axis deviation
1768
1779
  (distance - directionalDistance) * 1.5 + // Penalty for diagonal distance
@@ -2100,7 +2111,7 @@ class Editor extends EventEmitter {
2100
2111
  * @public
2101
2112
  */
2102
2113
  setHintingShapes(shapes) {
2103
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((shape) => shape.id);
2114
+ const ids = toShapeIds(shapes);
2104
2115
  this.run(
2105
2116
  () => {
2106
2117
  this._updateCurrentPageState({ hintingShapeIds: dedupe(ids) });
@@ -2130,7 +2141,7 @@ class Editor extends EventEmitter {
2130
2141
  * @public
2131
2142
  */
2132
2143
  setErasingShapes(shapes) {
2133
- const ids = typeof shapes[0] === "string" ? shapes.slice() : shapes.map((shape) => shape.id);
2144
+ const ids = toShapeIds(shapes).slice();
2134
2145
  ids.sort();
2135
2146
  const erasingShapeIds = this.getErasingShapeIds();
2136
2147
  this.run(
@@ -2537,14 +2548,16 @@ class Editor extends EventEmitter {
2537
2548
  setCamera(point, opts) {
2538
2549
  const { isLocked } = this._cameraOptions.__unsafe__getWithoutCapture();
2539
2550
  if (isLocked && !opts?.force) return this;
2551
+ const _point = new Vec(point.x, point.y, point.z ?? this.getZoomLevel());
2552
+ if (!Number.isFinite(_point.x) || !Number.isFinite(_point.y) || !Number.isFinite(_point.z)) {
2553
+ throw Error(
2554
+ `Editor.setCamera: expected finite values, got (${_point.x}, ${_point.y}, ${_point.z}).`
2555
+ );
2556
+ }
2540
2557
  this.stopCameraAnimation();
2541
2558
  if (this.getInstanceState().followingUserId) {
2542
2559
  this.stopFollowingUser();
2543
2560
  }
2544
- const _point = Vec.Cast(point);
2545
- if (!Number.isFinite(_point.x)) _point.x = 0;
2546
- if (!Number.isFinite(_point.y)) _point.y = 0;
2547
- if (_point.z === void 0 || !Number.isFinite(_point.z)) point.z = this.getZoomLevel();
2548
2561
  const camera = this.getConstrainedCamera(_point, opts);
2549
2562
  if (opts?.animation) {
2550
2563
  const { width, height } = this.getViewportScreenBounds();
@@ -2923,7 +2936,7 @@ class Editor extends EventEmitter {
2923
2936
  newCx += center.x / newCz - center.x / cz;
2924
2937
  newCy += center.y / newCz - center.y / cz;
2925
2938
  }
2926
- currentSpeed *= 1 - friction;
2939
+ currentSpeed *= (1 - friction) ** (elapsed / FRAME_MS_60HZ);
2927
2940
  if (currentSpeed < speedThreshold) {
2928
2941
  cancel();
2929
2942
  } else {
@@ -2957,6 +2970,7 @@ class Editor extends EventEmitter {
2957
2970
  }
2958
2971
  const isOnSamePage = presence.currentPageId === this.getCurrentPageId();
2959
2972
  if (!isOnSamePage) {
2973
+ this.markHistoryStoppingPoint("change-page");
2960
2974
  this.setCurrentPage(presence.currentPageId);
2961
2975
  }
2962
2976
  if (opts && opts.animation && !isOnSamePage) {
@@ -3788,7 +3802,9 @@ class Editor extends EventEmitter {
3788
3802
  if (ids.length <= 0) return this;
3789
3803
  this.run(
3790
3804
  () => {
3791
- this.store.props.assets.remove?.(ids);
3805
+ Promise.resolve(this.store.props.assets.remove?.(ids)).catch(
3806
+ (err) => console.error("Error while removing assets from the asset store:", err)
3807
+ );
3792
3808
  this.store.remove(ids);
3793
3809
  },
3794
3810
  { history: "ignore" }
@@ -4170,7 +4186,7 @@ class Editor extends EventEmitter {
4170
4186
  if (shapes.length === 0) {
4171
4187
  return;
4172
4188
  }
4173
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4189
+ const ids = toShapeIds(shapes);
4174
4190
  const freshShapes = compact(ids.map((id) => this.getShape(id)));
4175
4191
  if (freshShapes.length === 1) {
4176
4192
  const parentId = freshShapes[0].parentId;
@@ -4673,7 +4689,7 @@ class Editor extends EventEmitter {
4673
4689
  * @public
4674
4690
  */
4675
4691
  reparentShapes(shapes, parentId, insertIndex) {
4676
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
4692
+ const ids = toShapeIds(shapes);
4677
4693
  if (ids.length === 0) return this;
4678
4694
  const changes = [];
4679
4695
  const parentTransform = isPageId(parentId) ? Mat.Identity() : this.getShapePageTransform(parentId);
@@ -4902,6 +4918,7 @@ class Editor extends EventEmitter {
4902
4918
  * binding, but the `type`, `toId`, and `fromId` must all be provided.
4903
4919
  */
4904
4920
  createBindings(partials) {
4921
+ if (this.getIsReadonly()) return this;
4905
4922
  const bindings = [];
4906
4923
  for (const partial of partials) {
4907
4924
  const fromShape = this.getShape(partial.fromId);
@@ -4936,6 +4953,7 @@ class Editor extends EventEmitter {
4936
4953
  * binding is skipped. The changes from the partial are merged into the existing record.
4937
4954
  */
4938
4955
  updateBindings(partials) {
4956
+ if (this.getIsReadonly()) return this;
4939
4957
  const updated = [];
4940
4958
  for (const partial of partials) {
4941
4959
  if (!partial) continue;
@@ -4964,6 +4982,16 @@ class Editor extends EventEmitter {
4964
4982
  * Delete several bindings by their IDs. If a binding ID doesn't exist, it's ignored.
4965
4983
  */
4966
4984
  deleteBindings(bindings, { isolateShapes = false } = {}) {
4985
+ if (this.getIsReadonly()) return this;
4986
+ return this._deleteBindings(bindings, { isolateShapes });
4987
+ }
4988
+ /**
4989
+ * Unguarded so that withIsolatedShapes can transiently isolate shapes for copy and export in
4990
+ * readonly mode; the public deleteBindings is what readonly blocks.
4991
+ *
4992
+ * @internal
4993
+ */
4994
+ _deleteBindings(bindings, { isolateShapes = false } = {}) {
4967
4995
  const ids = bindings.map((binding) => typeof binding === "string" ? binding : binding.id);
4968
4996
  if (isolateShapes) {
4969
4997
  this.store.atomic(() => {
@@ -5022,7 +5050,7 @@ class Editor extends EventEmitter {
5022
5050
  * @param opts - The options for the rotation.
5023
5051
  */
5024
5052
  rotateShapesBy(shapes, delta, opts) {
5025
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5053
+ const ids = toShapeIds(shapes);
5026
5054
  if (ids.length <= 0) return this;
5027
5055
  const snapshot = getRotationSnapshot({ editor: this, ids });
5028
5056
  if (!snapshot) return this;
@@ -5059,6 +5087,14 @@ class Editor extends EventEmitter {
5059
5087
  }
5060
5088
  return workingShape;
5061
5089
  }
5090
+ // Counter-rotates the page-space delta into the parent's space; without that a child of a rotated
5091
+ // frame or group would move along its parent's axes instead of the page's.
5092
+ // todo: a shape laid out together with its own parent moves twice, once with the parent and once
5093
+ // on its own; the layout commands should skip shapes whose ancestors are also being laid out
5094
+ getChangesToTranslateShapeByPageDelta(shape, pageDelta) {
5095
+ const localDelta = Vec.From(pageDelta).rot(-this.getShapeParentTransform(shape).rotation());
5096
+ return this.getChangesToTranslateShape(shape, localDelta.add(shape));
5097
+ }
5062
5098
  /**
5063
5099
  * Move shapes by a delta.
5064
5100
  *
@@ -5071,14 +5107,12 @@ class Editor extends EventEmitter {
5071
5107
  * @param offset - The offset to apply to the shapes.
5072
5108
  */
5073
5109
  nudgeShapes(shapes, offset) {
5074
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5110
+ const ids = toShapeIds(shapes);
5075
5111
  if (ids.length <= 0) return this;
5076
5112
  const changes = [];
5077
5113
  for (const id of ids) {
5078
5114
  const shape = this.getShape(id);
5079
- const localDelta = Vec.From(offset);
5080
- localDelta.rot(-this.getShapeParentTransform(shape).rotation());
5081
- changes.push(this.getChangesToTranslateShape(shape, localDelta.add(shape)));
5115
+ changes.push(this.getChangesToTranslateShapeByPageDelta(shape, offset));
5082
5116
  }
5083
5117
  this.updateShapes(changes);
5084
5118
  return this;
@@ -5099,7 +5133,7 @@ class Editor extends EventEmitter {
5099
5133
  */
5100
5134
  duplicateShapes(shapes, offset) {
5101
5135
  this.run(() => {
5102
- const _ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5136
+ const _ids = toShapeIds(shapes);
5103
5137
  const ids = this._shouldIgnoreShapeLock ? _ids : this._getUnlockedShapeIds(_ids);
5104
5138
  if (ids.length <= 0) return this;
5105
5139
  const initialIds = new Set(ids);
@@ -5208,7 +5242,7 @@ class Editor extends EventEmitter {
5208
5242
  * @public
5209
5243
  */
5210
5244
  moveShapesToPage(shapes, pageId) {
5211
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5245
+ const ids = toShapeIds(shapes);
5212
5246
  if (ids.length === 0) return this;
5213
5247
  if (this.getIsReadonly()) return this;
5214
5248
  const currentPageId = this.getCurrentPageId();
@@ -5244,7 +5278,7 @@ class Editor extends EventEmitter {
5244
5278
  * @public
5245
5279
  */
5246
5280
  toggleLock(shapes) {
5247
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5281
+ const ids = toShapeIds(shapes);
5248
5282
  if (this.getIsReadonly() || ids.length === 0) return this;
5249
5283
  let allLocked = true;
5250
5284
  const shapesToToggle = [];
@@ -5283,7 +5317,7 @@ class Editor extends EventEmitter {
5283
5317
  * @public
5284
5318
  */
5285
5319
  sendToBack(shapes) {
5286
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5320
+ const ids = toShapeIds(shapes);
5287
5321
  const changes = getReorderingShapesChanges(this, "toBack", ids, {
5288
5322
  considerAllShapes: true
5289
5323
  });
@@ -5313,7 +5347,7 @@ class Editor extends EventEmitter {
5313
5347
  * @public
5314
5348
  */
5315
5349
  sendBackward(shapes, opts = {}) {
5316
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5350
+ const ids = toShapeIds(shapes);
5317
5351
  const changes = getReorderingShapesChanges(this, "backward", ids, opts);
5318
5352
  if (changes) this.updateShapes(changes);
5319
5353
  return this;
@@ -5341,7 +5375,7 @@ class Editor extends EventEmitter {
5341
5375
  * @public
5342
5376
  */
5343
5377
  bringForward(shapes, opts = {}) {
5344
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5378
+ const ids = toShapeIds(shapes);
5345
5379
  const changes = getReorderingShapesChanges(this, "forward", ids, opts);
5346
5380
  if (changes) this.updateShapes(changes);
5347
5381
  return this;
@@ -5360,7 +5394,7 @@ class Editor extends EventEmitter {
5360
5394
  * @public
5361
5395
  */
5362
5396
  bringToFront(shapes) {
5363
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5397
+ const ids = toShapeIds(shapes);
5364
5398
  const changes = getReorderingShapesChanges(this, "toFront", ids);
5365
5399
  if (changes) this.updateShapes(changes);
5366
5400
  return this;
@@ -5372,7 +5406,7 @@ class Editor extends EventEmitter {
5372
5406
  * @internal
5373
5407
  */
5374
5408
  getShapeClusters(shapes, type, opts) {
5375
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5409
+ const ids = toShapeIds(shapes);
5376
5410
  let freshShapes = compact(ids.map((id) => this.getShape(id)));
5377
5411
  if (opts?.filterAxisAligned) {
5378
5412
  freshShapes = freshShapes.filter((s) => {
@@ -5397,7 +5431,7 @@ class Editor extends EventEmitter {
5397
5431
  const shapesMovingTogether = [shape];
5398
5432
  const boundsOfShapesMovingTogether = [shapePageBounds];
5399
5433
  this.collectShapesViaArrowBindings({
5400
- bindings: this.getBindingsToShape(shape.id, "arrow"),
5434
+ bindings: this.getBindingsInvolvingShape(shape.id, "arrow"),
5401
5435
  initialShapes: freshShapes,
5402
5436
  resultShapes: shapesMovingTogether,
5403
5437
  resultBounds: boundsOfShapesMovingTogether,
@@ -5453,7 +5487,7 @@ class Editor extends EventEmitter {
5453
5487
  */
5454
5488
  flipShapes(shapes, operation) {
5455
5489
  if (this.getIsReadonly()) return this;
5456
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5490
+ const ids = toShapeIds(shapes);
5457
5491
  const shapesToFlipFirstPass = compact(ids.map((id) => this.getShape(id)));
5458
5492
  for (const shape of shapesToFlipFirstPass) {
5459
5493
  if (this.isShapeOfType(shape, "group")) {
@@ -5527,21 +5561,7 @@ class Editor extends EventEmitter {
5527
5561
  const { clusters: shapeClustersToStack } = this.getShapeClusters(shapes, "stack");
5528
5562
  const len = shapeClustersToStack.length;
5529
5563
  if (_gap === 0 && len < 3 || len < 2) return this;
5530
- let val;
5531
- let min;
5532
- let max;
5533
- let dim;
5534
- if (operation === "horizontal") {
5535
- val = "x";
5536
- min = "minX";
5537
- max = "maxX";
5538
- dim = "width";
5539
- } else {
5540
- val = "y";
5541
- min = "minY";
5542
- max = "maxY";
5543
- dim = "height";
5544
- }
5564
+ const { val, min, max, dim } = AXIS[operation];
5545
5565
  let shapeGap = 0;
5546
5566
  shapeClustersToStack.sort((a, b) => a.pageBounds[min] - b.pageBounds[min]);
5547
5567
  if (_gap === 0) {
@@ -5580,13 +5600,7 @@ class Editor extends EventEmitter {
5580
5600
  const delta = new Vec();
5581
5601
  delta[val] = v + shapeGap - pageBounds[val];
5582
5602
  for (const shape of shapes2) {
5583
- const shapeDelta = delta.clone();
5584
- const parent = this.getShapeParent(shape);
5585
- if (parent) {
5586
- shapeDelta.rot(-this.getShapePageTransform(parent).rotation());
5587
- }
5588
- shapeDelta.add(shape);
5589
- changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
5603
+ changes.push(this.getChangesToTranslateShapeByPageDelta(shape, delta));
5590
5604
  }
5591
5605
  v += pageBounds[dim] + shapeGap;
5592
5606
  }
@@ -5666,13 +5680,7 @@ class Editor extends EventEmitter {
5666
5680
  for (const { shapes: shapes2, pageBounds, nextPageBounds } of shapeClustersToPack) {
5667
5681
  const delta = Vec.Sub(nextPageBounds.point, pageBounds.point).add(centerDelta);
5668
5682
  for (const shape of shapes2) {
5669
- const shapeDelta = delta.clone();
5670
- const parent = this.getShapeParent(shape);
5671
- if (parent) {
5672
- shapeDelta.rot(-this.getShapeParentTransform(shape).rotation());
5673
- }
5674
- shapeDelta.add(shape);
5675
- changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
5683
+ changes.push(this.getChangesToTranslateShapeByPageDelta(shape, delta));
5676
5684
  }
5677
5685
  }
5678
5686
  if (changes.length) {
@@ -5732,13 +5740,7 @@ class Editor extends EventEmitter {
5732
5740
  }
5733
5741
  }
5734
5742
  for (const shape of shapes2) {
5735
- const shapeDelta = delta.clone();
5736
- const parent = this.getShapeParent(shape);
5737
- if (parent) {
5738
- shapeDelta.rot(-this.getShapePageTransform(parent).rotation());
5739
- }
5740
- shapeDelta.add(shape);
5741
- changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
5743
+ changes.push(this.getChangesToTranslateShapeByPageDelta(shape, delta));
5742
5744
  }
5743
5745
  });
5744
5746
  this.updateShapes(changes);
@@ -5762,27 +5764,13 @@ class Editor extends EventEmitter {
5762
5764
  if (this.getIsReadonly()) return this;
5763
5765
  const { clusters: shapeClustersToDistribute } = this.getShapeClusters(shapes, "distribute");
5764
5766
  if (shapeClustersToDistribute.length < 3) return this;
5765
- let val;
5766
- let min;
5767
- let max;
5768
- let dim;
5769
- if (operation === "horizontal") {
5770
- val = "x";
5771
- min = "minX";
5772
- max = "maxX";
5773
- dim = "width";
5774
- } else {
5775
- val = "y";
5776
- min = "minY";
5777
- max = "maxY";
5778
- dim = "height";
5779
- }
5767
+ const { val, min, max, dim } = AXIS[operation];
5780
5768
  const changes = [];
5781
5769
  const first = shapeClustersToDistribute.sort((a, b) => a.pageBounds[min] - b.pageBounds[min])[0];
5782
5770
  const last2 = shapeClustersToDistribute.sort((a, b) => b.pageBounds[max] - a.pageBounds[max])[0];
5783
5771
  if (first === last2) {
5784
5772
  const excludedShapeIds = new Set(first.shapes.map((s) => s.id));
5785
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
5773
+ const ids = toShapeIds(shapes);
5786
5774
  return this.distributeShapes(
5787
5775
  ids.filter((id) => !excludedShapeIds.has(id)),
5788
5776
  operation
@@ -5806,13 +5794,7 @@ class Editor extends EventEmitter {
5806
5794
  delta[val] = last2.pageBounds[max] - pageBounds[max] - 1;
5807
5795
  }
5808
5796
  for (const shape of shapes2) {
5809
- const shapeDelta = delta.clone();
5810
- const parent = this.getShapeParent(shape);
5811
- if (parent) {
5812
- shapeDelta.rot(-this.getShapePageTransform(parent).rotation());
5813
- }
5814
- shapeDelta.add(shape);
5815
- changes.push(this.getChangesToTranslateShape(shape, shapeDelta));
5797
+ changes.push(this.getChangesToTranslateShapeByPageDelta(shape, delta));
5816
5798
  }
5817
5799
  v += pageBounds[dim] + gap;
5818
5800
  }
@@ -5842,32 +5824,17 @@ class Editor extends EventEmitter {
5842
5824
  );
5843
5825
  if (shapeClustersToStretch.length < 2) return this;
5844
5826
  const commonBounds = Box.Common(allBounds);
5845
- let val;
5846
- let min;
5847
- let dim;
5848
- if (operation === "horizontal") {
5849
- val = "x";
5850
- min = "minX";
5851
- dim = "width";
5852
- } else {
5853
- val = "y";
5854
- min = "minY";
5855
- dim = "height";
5856
- }
5827
+ const { val, min, dim } = AXIS[operation];
5857
5828
  this.run(() => {
5858
5829
  shapeClustersToStretch.forEach(({ shapes: shapes2, pageBounds }) => {
5859
- const localOffset = new Vec();
5860
- localOffset[val] = commonBounds[min] - pageBounds[min];
5830
+ const pageOffset = new Vec();
5831
+ pageOffset[val] = commonBounds[min] - pageBounds[min];
5861
5832
  const scaleOrigin = pageBounds.center.clone();
5862
5833
  scaleOrigin[val] = commonBounds[min];
5863
5834
  const scale = new Vec(1, 1);
5864
5835
  scale[val] = commonBounds[dim] / pageBounds[dim];
5865
5836
  for (const shape of shapes2) {
5866
- const shapeLocalOffset = localOffset.clone();
5867
- shapeLocalOffset.rot(-this.getShapeParentTransform(shape).rotation());
5868
- shapeLocalOffset.add(shape);
5869
- const changes = this.getChangesToTranslateShape(shape, shapeLocalOffset);
5870
- this.updateShape(changes);
5837
+ this.updateShape(this.getChangesToTranslateShapeByPageDelta(shape, pageOffset));
5871
5838
  this.resizeShape(shape.id, scale, {
5872
5839
  initialBounds: this.getShapeGeometry(shape).bounds,
5873
5840
  scaleOrigin,
@@ -5910,7 +5877,7 @@ class Editor extends EventEmitter {
5910
5877
  const scaleY = targetBounds.height / commonBounds.height;
5911
5878
  const scale = new Vec(scaleX, scaleY);
5912
5879
  shapeClusters.forEach(({ shapes: shapes2, pageBounds }) => {
5913
- const localOffset = new Vec(
5880
+ const pageOffset = new Vec(
5914
5881
  targetBounds.minX - commonBounds.minX + (pageBounds.minX - commonBounds.minX) * (scaleX - 1),
5915
5882
  targetBounds.minY - commonBounds.minY + (pageBounds.minY - commonBounds.minY) * (scaleY - 1)
5916
5883
  );
@@ -5919,11 +5886,7 @@ class Editor extends EventEmitter {
5919
5886
  targetBounds.minY + (pageBounds.minY - commonBounds.minY) * scaleY
5920
5887
  );
5921
5888
  for (const shape of shapes2) {
5922
- const shapeLocalOffset = localOffset.clone();
5923
- shapeLocalOffset.rot(-this.getShapeParentTransform(shape).rotation());
5924
- shapeLocalOffset.add(shape);
5925
- const changes = this.getChangesToTranslateShape(shape, shapeLocalOffset);
5926
- this.updateShape(changes);
5889
+ this.updateShape(this.getChangesToTranslateShapeByPageDelta(shape, pageOffset));
5927
5890
  this.resizeShape(shape.id, scale, {
5928
5891
  initialBounds: this.getShapeGeometry(shape).bounds,
5929
5892
  scaleOrigin,
@@ -6342,12 +6305,17 @@ class Editor extends EventEmitter {
6342
6305
  let remaining = duration;
6343
6306
  let t;
6344
6307
  const animations = [];
6308
+ const ignoreShapeLock = this._shouldIgnoreShapeLock;
6345
6309
  let partial, result;
6346
6310
  for (let i = 0, n = partials.length; i < n; i++) {
6347
6311
  partial = partials[i];
6348
6312
  if (!partial) continue;
6349
6313
  const shape = this.getShape(partial.id);
6350
6314
  if (!shape) continue;
6315
+ const unlocks = shape.isLocked && Object.hasOwn(partial, "isLocked") && !partial.isLocked;
6316
+ if (!ignoreShapeLock && !unlocks && this.isShapeOrAncestorLocked(shape)) {
6317
+ continue;
6318
+ }
6351
6319
  result = {
6352
6320
  start: structuredClone(shape),
6353
6321
  end: applyPartialToRecordWithProps(structuredClone(shape), partial)
@@ -6363,7 +6331,7 @@ class Editor extends EventEmitter {
6363
6331
  (p) => p && animatingShapes2.get(p.id) === animationId
6364
6332
  );
6365
6333
  if (partialsToUpdate.length) {
6366
- this.updateShapes(partialsToUpdate);
6334
+ this.run(() => this.updateShapes(partialsToUpdate), { ignoreShapeLock });
6367
6335
  }
6368
6336
  this.off("tick", handleTick);
6369
6337
  return;
@@ -6396,13 +6364,13 @@ class Editor extends EventEmitter {
6396
6364
  throw Error("Editor.groupShapes: must provide an array of shapes or shape ids");
6397
6365
  }
6398
6366
  if (this.getIsReadonly()) return this;
6399
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
6400
- if (ids.length <= 1) return this;
6367
+ const ids = toShapeIds(shapes);
6401
6368
  const shapesToGroup = compact(
6402
6369
  (this._shouldIgnoreShapeLock ? ids : this._getUnlockedShapeIds(ids)).map(
6403
6370
  (id) => this.getShape(id)
6404
6371
  )
6405
6372
  );
6373
+ if (shapesToGroup.length <= 1) return this;
6406
6374
  const sortedShapeIds = shapesToGroup.sort(sortByIndex).map((s) => s.id);
6407
6375
  const childBounds = compact(shapesToGroup.map((shape) => this.getShapePageBounds(shape)));
6408
6376
  const pageBounds = Box.Common(childBounds);
@@ -6411,8 +6379,11 @@ class Editor extends EventEmitter {
6411
6379
  }
6412
6380
  const { x, y } = pageBounds.point;
6413
6381
  const parentId = this.findCommonAncestor(shapesToGroup) ?? this.getCurrentPageId();
6414
- if (this.getCurrentToolId() !== "select") return this;
6415
- if (!this.isIn("select.idle")) {
6382
+ if (!this.canCreateShapes([groupId])) {
6383
+ alertMaxShapes(this);
6384
+ return this;
6385
+ }
6386
+ if (this.isIn("select") && !this.isIn("select.idle")) {
6416
6387
  this.cancel();
6417
6388
  }
6418
6389
  const shapesWithRootParent = shapesToGroup.filter((shape) => shape.parentId === parentId).sort(sortByIndex);
@@ -6440,15 +6411,14 @@ class Editor extends EventEmitter {
6440
6411
  ungroupShapes(shapes, opts = {}) {
6441
6412
  if (this.getIsReadonly()) return this;
6442
6413
  const { select = true } = opts;
6443
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
6414
+ const ids = toShapeIds(shapes);
6444
6415
  const shapesToUngroup = compact(
6445
6416
  (this._shouldIgnoreShapeLock ? ids : this._getUnlockedShapeIds(ids)).map(
6446
6417
  (id) => this.getShape(id)
6447
6418
  )
6448
6419
  );
6449
6420
  if (shapesToUngroup.length === 0) return this;
6450
- if (this.getCurrentToolId() !== "select") return this;
6451
- if (!this.isIn("select.idle")) {
6421
+ if (this.isIn("select") && !this.isIn("select.idle")) {
6452
6422
  this.cancel();
6453
6423
  }
6454
6424
  const idsToSelect = /* @__PURE__ */ new Set();
@@ -6462,16 +6432,16 @@ class Editor extends EventEmitter {
6462
6432
  });
6463
6433
  if (groups.length === 0) return this;
6464
6434
  this.run(() => {
6465
- let group;
6466
6435
  for (let i = 0, n = groups.length; i < n; i++) {
6467
- group = groups[i];
6436
+ const group = this.getShape(groups[i].id);
6437
+ if (!group) continue;
6468
6438
  const childIds = this.getSortedChildIdsForParent(group.id);
6469
6439
  for (let j = 0, n2 = childIds.length; j < n2; j++) {
6470
6440
  idsToSelect.add(childIds[j]);
6471
6441
  }
6472
6442
  this.reparentShapes(childIds, group.parentId, group.index);
6473
6443
  }
6474
- this.deleteShapes(groups.map((group2) => group2.id));
6444
+ this.deleteShapes(groups.map((group) => group.id));
6475
6445
  if (select) {
6476
6446
  this.select(...idsToSelect);
6477
6447
  }
@@ -6552,14 +6522,14 @@ class Editor extends EventEmitter {
6552
6522
  }
6553
6523
  /** @internal */
6554
6524
  _getUnlockedShapeIds(ids) {
6555
- return ids.filter((id) => !this.getShape(id)?.isLocked);
6525
+ return ids.filter((id) => !this.isShapeOrAncestorLocked(id));
6556
6526
  }
6557
6527
  deleteShapes(_ids) {
6558
6528
  if (this.getIsReadonly()) return this;
6559
6529
  if (!Array.isArray(_ids)) {
6560
6530
  throw Error("Editor.deleteShapes: must provide an array of shapes or shapeIds");
6561
6531
  }
6562
- const shapeIds = typeof _ids[0] === "string" ? _ids : _ids.map((s) => s.id);
6532
+ const shapeIds = toShapeIds(_ids);
6563
6533
  const shapeIdsToDelete = this._shouldIgnoreShapeLock ? shapeIds : this._getUnlockedShapeIds(shapeIds);
6564
6534
  if (shapeIdsToDelete.length === 0) return this;
6565
6535
  const allShapeIdsToDelete = new Set(shapeIdsToDelete);
@@ -6904,7 +6874,7 @@ class Editor extends EventEmitter {
6904
6874
  * @public
6905
6875
  */
6906
6876
  getContentFromCurrentPage(shapes) {
6907
- const ids = typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
6877
+ const ids = toShapeIds(shapes);
6908
6878
  if (!ids) return;
6909
6879
  if (ids.length === 0) return;
6910
6880
  const shapeIds = this.getShapeAndDescendantIds(ids);
@@ -6972,21 +6942,29 @@ class Editor extends EventEmitter {
6972
6942
  await Promise.allSettled(
6973
6943
  content.assets.map(async (asset) => {
6974
6944
  if ((asset.type === "image" || asset.type === "video") && !asset.props.src?.startsWith("data:image") && !asset.props.src?.startsWith("data:video") && !asset.props.src?.startsWith("http")) {
6975
- const assetWithDataUrl = structuredClone(asset);
6976
- const objectUrl = await this.store.props.assets.resolve(asset, {
6977
- screenScale: 1,
6978
- steppedScreenScale: 1,
6979
- dpr: 1,
6980
- networkEffectiveType: null,
6981
- shouldResolveToOriginal: true
6982
- });
6983
- assetWithDataUrl.props.src = await FileHelpers.blobToDataUrl(
6984
- await fetch(objectUrl).then((r) => r.blob())
6985
- );
6986
- assets.push(assetWithDataUrl);
6987
- } else {
6988
- assets.push(asset);
6945
+ try {
6946
+ const objectUrl = await this.store.props.assets.resolve(asset, {
6947
+ screenScale: 1,
6948
+ steppedScreenScale: 1,
6949
+ dpr: 1,
6950
+ networkEffectiveType: null,
6951
+ shouldResolveToOriginal: true
6952
+ });
6953
+ if (objectUrl) {
6954
+ const response = await fetch(objectUrl);
6955
+ if (response.ok) {
6956
+ const assetWithDataUrl = structuredClone(asset);
6957
+ assetWithDataUrl.props.src = await FileHelpers.blobToDataUrl(await response.blob());
6958
+ assets.push(assetWithDataUrl);
6959
+ return;
6960
+ }
6961
+ console.warn(`Could not inline asset ${asset.id}: fetch returned ${response.status}`);
6962
+ }
6963
+ } catch (err) {
6964
+ console.warn(`Could not inline asset ${asset.id}`, err);
6965
+ }
6989
6966
  }
6967
+ assets.push(asset);
6990
6968
  })
6991
6969
  );
6992
6970
  content.assets = assets;
@@ -7280,7 +7258,7 @@ class Editor extends EventEmitter {
7280
7258
  * @public
7281
7259
  */
7282
7260
  async getSvgElement(shapes, opts = {}) {
7283
- const ids = shapes.length === 0 ? this.getCurrentPageShapeIdsSorted() : typeof shapes[0] === "string" ? shapes : shapes.map((s) => s.id);
7261
+ const ids = shapes.length === 0 ? this.getCurrentPageShapeIdsSorted() : toShapeIds(shapes);
7284
7262
  if (ids.length === 0) return void 0;
7285
7263
  await this.fonts.loadRequiredFontsForCurrentPage(this.options.maxFontsToLoadBeforeRender);
7286
7264
  return exportToSvg(this, ids, opts);
@@ -8334,8 +8312,8 @@ class Editor extends EventEmitter {
8334
8312
  break;
8335
8313
  }
8336
8314
  case "keyboard": {
8337
- if (info.key === "ShiftRight") info.key = "ShiftLeft";
8338
- if (info.key === "AltRight") info.key = "AltLeft";
8315
+ if (info.code === "ShiftRight") info.code = "ShiftLeft";
8316
+ if (info.code === "AltRight") info.code = "AltLeft";
8339
8317
  if (info.code === "ControlRight") info.code = "ControlLeft";
8340
8318
  if (info.code === "MetaRight") info.code = "MetaLeft";
8341
8319
  switch (info.name) {
@@ -8705,6 +8683,9 @@ function pushShapeWithDescendants(editor, id, result) {
8705
8683
  pushShapeWithDescendants(editor, childIds[i], result);
8706
8684
  }
8707
8685
  }
8686
+ function toShapeIds(shapes) {
8687
+ return typeof shapes[0] === "string" ? shapes : shapes.map((shape) => shape.id);
8688
+ }
8708
8689
  function withIsolatedShapes(editor, shapeIds, callback) {
8709
8690
  let result;
8710
8691
  editor.run(
@@ -8727,7 +8708,7 @@ function withIsolatedShapes(editor, shapeIds, callback) {
8727
8708
  }
8728
8709
  }
8729
8710
  }
8730
- editor.deleteBindings([...bindingsToRemove], { isolateShapes: true });
8711
+ editor._deleteBindings([...bindingsToRemove], { isolateShapes: true });
8731
8712
  try {
8732
8713
  result = Result.ok(callback(bindingsWithBoth));
8733
8714
  } catch (error) {