@excalidraw/element 0.18.0-d2463f87d → 0.18.0-dda3affcb

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 (45) hide show
  1. package/dist/dev/index.js +225 -110
  2. package/dist/dev/index.js.map +3 -3
  3. package/dist/prod/index.js +11 -11
  4. package/dist/types/common/src/constants.d.ts +1 -0
  5. package/dist/types/element/src/align.d.ts +2 -1
  6. package/dist/types/element/src/delta.d.ts +12 -2
  7. package/dist/types/element/src/distribute.d.ts +2 -1
  8. package/dist/types/element/src/groups.d.ts +1 -0
  9. package/dist/types/element/src/linearElementEditor.d.ts +2 -1
  10. package/dist/types/element/src/store.d.ts +2 -1
  11. package/dist/types/excalidraw/actions/actionAddToLibrary.d.ts +0 -3
  12. package/dist/types/excalidraw/actions/actionBoundText.d.ts +0 -2
  13. package/dist/types/excalidraw/actions/actionCanvas.d.ts +0 -13
  14. package/dist/types/excalidraw/actions/actionClipboard.d.ts +3 -8
  15. package/dist/types/excalidraw/actions/actionCropEditor.d.ts +0 -1
  16. package/dist/types/excalidraw/actions/actionDeleteSelected.d.ts +3 -6
  17. package/dist/types/excalidraw/actions/actionElementLink.d.ts +0 -1
  18. package/dist/types/excalidraw/actions/actionElementLock.d.ts +0 -2
  19. package/dist/types/excalidraw/actions/actionEmbeddable.d.ts +0 -1
  20. package/dist/types/excalidraw/actions/actionExport.d.ts +0 -9
  21. package/dist/types/excalidraw/actions/actionFinalize.d.ts +2 -3
  22. package/dist/types/excalidraw/actions/actionFrame.d.ts +0 -4
  23. package/dist/types/excalidraw/actions/actionGroup.d.ts +0 -2
  24. package/dist/types/excalidraw/actions/actionLinearEditor.d.ts +33 -3
  25. package/dist/types/excalidraw/actions/actionLink.d.ts +0 -1
  26. package/dist/types/excalidraw/actions/actionMenu.d.ts +0 -3
  27. package/dist/types/excalidraw/actions/actionNavigate.d.ts +0 -2
  28. package/dist/types/excalidraw/actions/actionProperties.d.ts +0 -15
  29. package/dist/types/excalidraw/actions/actionSelectAll.d.ts +0 -1
  30. package/dist/types/excalidraw/actions/actionStyles.d.ts +0 -1
  31. package/dist/types/excalidraw/actions/actionToggleGridMode.d.ts +0 -1
  32. package/dist/types/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +0 -1
  33. package/dist/types/excalidraw/actions/actionToggleSearchMenu.d.ts +0 -1
  34. package/dist/types/excalidraw/actions/actionToggleStats.d.ts +0 -1
  35. package/dist/types/excalidraw/actions/actionToggleViewMode.d.ts +0 -1
  36. package/dist/types/excalidraw/actions/actionToggleZenMode.d.ts +0 -1
  37. package/dist/types/excalidraw/components/Actions.d.ts +0 -4
  38. package/dist/types/excalidraw/components/App.d.ts +1 -0
  39. package/dist/types/excalidraw/components/Ellipsify.d.ts +4 -0
  40. package/dist/types/excalidraw/data/reconcile.d.ts +1 -0
  41. package/dist/types/excalidraw/data/restore.d.ts +6 -1
  42. package/dist/types/excalidraw/index.d.ts +2 -1
  43. package/dist/types/excalidraw/renderer/staticScene.d.ts +4 -1
  44. package/dist/types/excalidraw/types.d.ts +4 -4
  45. package/package.json +3 -3
package/dist/dev/index.js CHANGED
@@ -8127,7 +8127,8 @@ var LinearElementEditor = class _LinearElementEditor {
8127
8127
  segmentMidPointHoveredCoords;
8128
8128
  elbowed;
8129
8129
  customLineAngle;
8130
- constructor(element, elementsMap) {
8130
+ isEditing;
8131
+ constructor(element, elementsMap, isEditing = false) {
8131
8132
  this.elementId = element.id;
8132
8133
  if (!pointsEqual6(element.points[0], pointFrom8(0, 0))) {
8133
8134
  console.error("Linear element is not normalized", Error().stack);
@@ -8158,6 +8159,7 @@ var LinearElementEditor = class _LinearElementEditor {
8158
8159
  this.segmentMidPointHoveredCoords = null;
8159
8160
  this.elbowed = isElbowArrow(element) && element.elbowed;
8160
8161
  this.customLineAngle = null;
8162
+ this.isEditing = isEditing;
8161
8163
  }
8162
8164
  // ---------------------------------------------------------------------------
8163
8165
  // static methods
@@ -8175,11 +8177,11 @@ var LinearElementEditor = class _LinearElementEditor {
8175
8177
  return null;
8176
8178
  }
8177
8179
  static handleBoxSelection(event, appState, setState, elementsMap) {
8178
- if (!appState.editingLinearElement || !appState.selectionElement) {
8180
+ if (!appState.selectedLinearElement?.isEditing || !appState.selectionElement) {
8179
8181
  return false;
8180
8182
  }
8181
- const { editingLinearElement } = appState;
8182
- const { selectedPointsIndices, elementId } = editingLinearElement;
8183
+ const { selectedLinearElement } = appState;
8184
+ const { selectedPointsIndices, elementId } = selectedLinearElement;
8183
8185
  const element = _LinearElementEditor.getElement(elementId, elementsMap);
8184
8186
  if (!element) {
8185
8187
  return false;
@@ -8201,8 +8203,8 @@ var LinearElementEditor = class _LinearElementEditor {
8201
8203
  return true;
8202
8204
  });
8203
8205
  setState({
8204
- editingLinearElement: {
8205
- ...editingLinearElement,
8206
+ selectedLinearElement: {
8207
+ ...selectedLinearElement,
8206
8208
  selectedPointsIndices: nextSelectedPoints.length ? nextSelectedPoints : null
8207
8209
  }
8208
8210
  });
@@ -8356,7 +8358,6 @@ var LinearElementEditor = class _LinearElementEditor {
8356
8358
  };
8357
8359
  return {
8358
8360
  ...app.state,
8359
- editingLinearElement: app.state.editingLinearElement ? newLinearElementEditor : null,
8360
8361
  selectedLinearElement: newLinearElementEditor,
8361
8362
  suggestedBindings
8362
8363
  };
@@ -8440,7 +8441,7 @@ var LinearElementEditor = class _LinearElementEditor {
8440
8441
  }
8441
8442
  static getEditorMidPoints = (element, elementsMap, appState) => {
8442
8443
  const boundText = getBoundTextElement(element, elementsMap);
8443
- if (!isElbowArrow(element) && !appState.editingLinearElement && element.points.length > 2 && !boundText) {
8444
+ if (!isElbowArrow(element) && !appState.selectedLinearElement?.isEditing && element.points.length > 2 && !boundText) {
8444
8445
  return [];
8445
8446
  }
8446
8447
  const points = _LinearElementEditor.getPointsGlobalCoordinates(
@@ -8490,7 +8491,7 @@ var LinearElementEditor = class _LinearElementEditor {
8490
8491
  element,
8491
8492
  elementsMap
8492
8493
  );
8493
- if (points.length >= 3 && !appState.editingLinearElement && !isElbowArrow(element)) {
8494
+ if (points.length >= 3 && !appState.selectedLinearElement?.isEditing && !isElbowArrow(element)) {
8494
8495
  return null;
8495
8496
  }
8496
8497
  const threshold = (_LinearElementEditor.POINT_HANDLE_SIZE + 1) / appState.zoom.value;
@@ -8630,7 +8631,7 @@ var LinearElementEditor = class _LinearElementEditor {
8630
8631
  segmentMidpoint,
8631
8632
  elementsMap
8632
8633
  );
8633
- } else if (event.altKey && appState.editingLinearElement) {
8634
+ } else if (event.altKey && appState.selectedLinearElement?.isEditing) {
8634
8635
  if (linearElementEditor.lastUncommittedPoint == null) {
8635
8636
  scene.mutateElement(element, {
8636
8637
  points: [
@@ -8740,14 +8741,14 @@ var LinearElementEditor = class _LinearElementEditor {
8740
8741
  }
8741
8742
  static handlePointerMove(event, scenePointerX, scenePointerY, app) {
8742
8743
  const appState = app.state;
8743
- if (!appState.editingLinearElement) {
8744
+ if (!appState.selectedLinearElement?.isEditing) {
8744
8745
  return null;
8745
8746
  }
8746
- const { elementId, lastUncommittedPoint } = appState.editingLinearElement;
8747
+ const { elementId, lastUncommittedPoint } = appState.selectedLinearElement;
8747
8748
  const elementsMap = app.scene.getNonDeletedElementsMap();
8748
8749
  const element = _LinearElementEditor.getElement(elementId, elementsMap);
8749
8750
  if (!element) {
8750
- return appState.editingLinearElement;
8751
+ return appState.selectedLinearElement;
8751
8752
  }
8752
8753
  const { points } = element;
8753
8754
  const lastPoint = points[points.length - 1];
@@ -8755,10 +8756,10 @@ var LinearElementEditor = class _LinearElementEditor {
8755
8756
  if (lastPoint === lastUncommittedPoint) {
8756
8757
  _LinearElementEditor.deletePoints(element, app, [points.length - 1]);
8757
8758
  }
8758
- return {
8759
- ...appState.editingLinearElement,
8759
+ return appState.selectedLinearElement?.lastUncommittedPoint ? {
8760
+ ...appState.selectedLinearElement,
8760
8761
  lastUncommittedPoint: null
8761
- };
8762
+ } : appState.selectedLinearElement;
8762
8763
  }
8763
8764
  let newPoint;
8764
8765
  if (shouldRotateWithDiscreteAngle(event) && points.length >= 2) {
@@ -8778,8 +8779,8 @@ var LinearElementEditor = class _LinearElementEditor {
8778
8779
  newPoint = _LinearElementEditor.createPointAt(
8779
8780
  element,
8780
8781
  elementsMap,
8781
- scenePointerX - appState.editingLinearElement.pointerOffset.x,
8782
- scenePointerY - appState.editingLinearElement.pointerOffset.y,
8782
+ scenePointerX - appState.selectedLinearElement.pointerOffset.x,
8783
+ scenePointerY - appState.selectedLinearElement.pointerOffset.y,
8783
8784
  event[KEYS2.CTRL_OR_CMD] || isElbowArrow(element) ? null : app.getEffectiveGridSize()
8784
8785
  );
8785
8786
  }
@@ -8800,7 +8801,7 @@ var LinearElementEditor = class _LinearElementEditor {
8800
8801
  _LinearElementEditor.addPoints(element, app.scene, [newPoint]);
8801
8802
  }
8802
8803
  return {
8803
- ...appState.editingLinearElement,
8804
+ ...appState.selectedLinearElement,
8804
8805
  lastUncommittedPoint: element.points[element.points.length - 1]
8805
8806
  };
8806
8807
  }
@@ -8906,11 +8907,11 @@ var LinearElementEditor = class _LinearElementEditor {
8906
8907
  // ---------------------------------------------------------------------------
8907
8908
  static duplicateSelectedPoints(appState, scene) {
8908
8909
  invariant6(
8909
- appState.editingLinearElement,
8910
+ appState.selectedLinearElement?.isEditing,
8910
8911
  "Not currently editing a linear element"
8911
8912
  );
8912
8913
  const elementsMap = scene.getNonDeletedElementsMap();
8913
- const { selectedPointsIndices, elementId } = appState.editingLinearElement;
8914
+ const { selectedPointsIndices, elementId } = appState.selectedLinearElement;
8914
8915
  const element = _LinearElementEditor.getElement(elementId, elementsMap);
8915
8916
  invariant6(
8916
8917
  element,
@@ -8957,14 +8958,14 @@ var LinearElementEditor = class _LinearElementEditor {
8957
8958
  }
8958
8959
  return {
8959
8960
  ...appState,
8960
- editingLinearElement: {
8961
- ...appState.editingLinearElement,
8961
+ selectedLinearElement: {
8962
+ ...appState.selectedLinearElement,
8962
8963
  selectedPointsIndices: nextSelectedIndices
8963
8964
  }
8964
8965
  };
8965
8966
  }
8966
8967
  static deletePoints(element, app, pointIndices) {
8967
- const isUncommittedPoint = app.state.editingLinearElement?.lastUncommittedPoint === element.points[element.points.length - 1];
8968
+ const isUncommittedPoint = app.state.selectedLinearElement?.isEditing && app.state.selectedLinearElement?.lastUncommittedPoint === element.points[element.points.length - 1];
8968
8969
  const nextPoints = element.points.filter((_, idx) => {
8969
8970
  return !pointIndices.includes(idx);
8970
8971
  });
@@ -9073,7 +9074,7 @@ var LinearElementEditor = class _LinearElementEditor {
9073
9074
  pointFrom8(origin.x, origin.y),
9074
9075
  pointFrom8(pointerCoords.x, pointerCoords.y)
9075
9076
  );
9076
- if (!appState.editingLinearElement && dist < DRAGGING_THRESHOLD / appState.zoom.value) {
9077
+ if (!appState.selectedLinearElement?.isEditing && dist < DRAGGING_THRESHOLD / appState.zoom.value) {
9077
9078
  return false;
9078
9079
  }
9079
9080
  return true;
@@ -9826,6 +9827,55 @@ var getNewGroupIdsForDuplication = (groupIds, editingGroupId, mapper) => {
9826
9827
  }
9827
9828
  return copy;
9828
9829
  };
9830
+ var getSelectedElementsByGroup = (selectedElements, elementsMap, appState) => {
9831
+ const selectedGroupIds = getSelectedGroupIds(appState);
9832
+ const unboundElements = selectedElements.filter(
9833
+ (element) => !isBoundToContainer(element)
9834
+ );
9835
+ const groups = /* @__PURE__ */ new Map();
9836
+ const elements = /* @__PURE__ */ new Map();
9837
+ const addToElementsMap = (element) => {
9838
+ const currentElementMembers = elements.get(element.id) || [];
9839
+ const boundTextElement = getBoundTextElement(element, elementsMap);
9840
+ if (boundTextElement) {
9841
+ currentElementMembers.push(boundTextElement);
9842
+ }
9843
+ elements.set(element.id, [...currentElementMembers, element]);
9844
+ };
9845
+ const addToGroupsMap = (element, groupId) => {
9846
+ const currentGroupMembers = groups.get(groupId) || [];
9847
+ const boundTextElement = getBoundTextElement(element, elementsMap);
9848
+ if (boundTextElement) {
9849
+ currentGroupMembers.push(boundTextElement);
9850
+ }
9851
+ groups.set(groupId, [...currentGroupMembers, element]);
9852
+ };
9853
+ const handleSingleSelectedGroupCase = (element, selectedGroupId) => {
9854
+ const indexOfSelectedGroupId = element.groupIds.indexOf(selectedGroupId, 0);
9855
+ const nestedGroupCount = element.groupIds.slice(
9856
+ 0,
9857
+ indexOfSelectedGroupId
9858
+ ).length;
9859
+ return nestedGroupCount > 0 ? addToGroupsMap(element, element.groupIds[indexOfSelectedGroupId - 1]) : addToElementsMap(element);
9860
+ };
9861
+ const isAllInSameGroup = selectedElements.every(
9862
+ (element) => isSelectedViaGroup(appState, element)
9863
+ );
9864
+ unboundElements.forEach((element) => {
9865
+ const selectedGroupId = getSelectedGroupIdForElement(
9866
+ element,
9867
+ appState.selectedGroupIds
9868
+ );
9869
+ if (!selectedGroupId) {
9870
+ addToElementsMap(element);
9871
+ } else if (selectedGroupIds.length === 1 && isAllInSameGroup) {
9872
+ handleSingleSelectedGroupCase(element, selectedGroupId);
9873
+ } else {
9874
+ addToGroupsMap(element, selectedGroupId);
9875
+ }
9876
+ });
9877
+ return Array.from(groups.values()).concat(Array.from(elements.values()));
9878
+ };
9829
9879
 
9830
9880
  // src/selection.ts
9831
9881
  var excludeElementsInFramesFromSelection = (selectedElements) => {
@@ -10513,6 +10563,11 @@ var getCanvasPadding = (element) => {
10513
10563
  return element.strokeWidth * 12;
10514
10564
  case "text":
10515
10565
  return element.fontSize / 2;
10566
+ case "arrow":
10567
+ if (element.endArrowhead || element.endArrowhead) {
10568
+ return 40;
10569
+ }
10570
+ return 20;
10516
10571
  default:
10517
10572
  return 20;
10518
10573
  }
@@ -12742,11 +12797,11 @@ var getNormalizedDimensions = (element) => {
12742
12797
 
12743
12798
  // src/align.ts
12744
12799
  init_define_import_meta_env();
12745
- var alignElements = (selectedElements, alignment, scene) => {
12746
- const elementsMap = scene.getNonDeletedElementsMap();
12747
- const groups = getMaximumGroups(
12800
+ var alignElements = (selectedElements, alignment, scene, appState) => {
12801
+ const groups = getSelectedElementsByGroup(
12748
12802
  selectedElements,
12749
- elementsMap
12803
+ scene.getNonDeletedElementsMap(),
12804
+ appState
12750
12805
  );
12751
12806
  const selectionBoundingBox = getCommonBoundingBox(selectedElements);
12752
12807
  return groups.flatMap((group) => {
@@ -13182,8 +13237,9 @@ var Store = class {
13182
13237
  constructor(app) {
13183
13238
  this.app = app;
13184
13239
  }
13185
- // internally used by history
13240
+ // for internal use by history
13186
13241
  onDurableIncrementEmitter = new Emitter();
13242
+ // for public use as part of onIncrement API
13187
13243
  onStoreIncrementEmitter = new Emitter();
13188
13244
  scheduledMacroActions = /* @__PURE__ */ new Set();
13189
13245
  scheduledMicroActions = [];
@@ -13491,8 +13547,14 @@ var StoreDelta = class {
13491
13547
  /**
13492
13548
  * Apply the delta to the passed elements and appState, does not modify the snapshot.
13493
13549
  */
13494
- static applyTo(delta, elements, appState) {
13495
- const [nextElements, elementsContainVisibleChange] = delta.elements.applyTo(elements);
13550
+ static applyTo(delta, elements, appState, options = {
13551
+ excludedProperties: /* @__PURE__ */ new Set()
13552
+ }) {
13553
+ const [nextElements, elementsContainVisibleChange] = delta.elements.applyTo(
13554
+ elements,
13555
+ StoreSnapshot.empty().elements,
13556
+ options
13557
+ );
13496
13558
  const [nextAppState, appStateContainsVisibleChange] = delta.appState.applyTo(appState, nextElements);
13497
13559
  const appliedVisibleChanges = elementsContainVisibleChange || appStateContainsVisibleChange;
13498
13560
  return [nextElements, nextAppState, appliedVisibleChanges];
@@ -13753,8 +13815,7 @@ var getDefaultObservedAppState = () => {
13753
13815
  viewBackgroundColor: COLOR_PALETTE2.white,
13754
13816
  selectedElementIds: {},
13755
13817
  selectedGroupIds: {},
13756
- editingLinearElementId: null,
13757
- selectedLinearElementId: null,
13818
+ selectedLinearElement: null,
13758
13819
  croppingElementId: null,
13759
13820
  activeLockedId: null,
13760
13821
  lockedMultiSelections: {}
@@ -13770,10 +13831,10 @@ var getObservedAppState = (appState) => {
13770
13831
  croppingElementId: appState.croppingElementId,
13771
13832
  activeLockedId: appState.activeLockedId,
13772
13833
  lockedMultiSelections: appState.lockedMultiSelections,
13773
- editingLinearElementId: appState.editingLinearElement?.elementId ?? // prefer app state, as it's likely newer
13774
- appState.editingLinearElementId ?? // fallback to observed app state, as it's likely older coming from a previous snapshot
13775
- null,
13776
- selectedLinearElementId: appState.selectedLinearElement?.elementId ?? appState.selectedLinearElementId ?? null
13834
+ selectedLinearElement: appState.selectedLinearElement ? {
13835
+ elementId: appState.selectedLinearElement.elementId,
13836
+ isEditing: !!appState.selectedLinearElement.isEditing
13837
+ } : null
13777
13838
  };
13778
13839
  Reflect.defineProperty(observedAppState, hiddenObservedAppStateProp, {
13779
13840
  value: true,
@@ -14577,6 +14638,15 @@ var Delta = class _Delta {
14577
14638
  static isEmpty(delta) {
14578
14639
  return !Object.keys(delta.deleted).length && !Object.keys(delta.inserted).length;
14579
14640
  }
14641
+ /**
14642
+ * Merges two deltas into a new one.
14643
+ */
14644
+ static merge(delta1, delta2) {
14645
+ return _Delta.create(
14646
+ { ...delta1.deleted, ...delta2.deleted },
14647
+ { ...delta1.inserted, ...delta2.inserted }
14648
+ );
14649
+ }
14580
14650
  /**
14581
14651
  * Merges deleted and inserted object partials.
14582
14652
  */
@@ -14822,50 +14892,45 @@ var AppStateDelta = class _AppStateDelta {
14822
14892
  const inversedDelta = Delta.create(this.delta.inserted, this.delta.deleted);
14823
14893
  return new _AppStateDelta(inversedDelta);
14824
14894
  }
14895
+ squash(delta) {
14896
+ this.delta = Delta.merge(this.delta, delta.delta);
14897
+ return this;
14898
+ }
14825
14899
  applyTo(appState, nextElements) {
14826
14900
  try {
14827
14901
  const {
14828
- selectedElementIds: removedSelectedElementIds = {},
14829
- selectedGroupIds: removedSelectedGroupIds = {}
14902
+ selectedElementIds: deletedSelectedElementIds = {},
14903
+ selectedGroupIds: deletedSelectedGroupIds = {}
14830
14904
  } = this.delta.deleted;
14831
14905
  const {
14832
- selectedElementIds: addedSelectedElementIds = {},
14833
- selectedGroupIds: addedSelectedGroupIds = {},
14834
- selectedLinearElementId,
14835
- editingLinearElementId,
14906
+ selectedElementIds: insertedSelectedElementIds = {},
14907
+ selectedGroupIds: insertedSelectedGroupIds = {},
14908
+ selectedLinearElement: insertedSelectedLinearElement,
14836
14909
  ...directlyApplicablePartial
14837
14910
  } = this.delta.inserted;
14838
14911
  const mergedSelectedElementIds = Delta.mergeObjects(
14839
14912
  appState.selectedElementIds,
14840
- addedSelectedElementIds,
14841
- removedSelectedElementIds
14913
+ insertedSelectedElementIds,
14914
+ deletedSelectedElementIds
14842
14915
  );
14843
14916
  const mergedSelectedGroupIds = Delta.mergeObjects(
14844
14917
  appState.selectedGroupIds,
14845
- addedSelectedGroupIds,
14846
- removedSelectedGroupIds
14918
+ insertedSelectedGroupIds,
14919
+ deletedSelectedGroupIds
14847
14920
  );
14848
- const selectedLinearElement = selectedLinearElementId && nextElements.has(selectedLinearElementId) ? new LinearElementEditor(
14849
- nextElements.get(
14850
- selectedLinearElementId
14851
- ),
14852
- nextElements
14853
- ) : null;
14854
- const editingLinearElement = editingLinearElementId && nextElements.has(editingLinearElementId) ? new LinearElementEditor(
14921
+ const selectedLinearElement = insertedSelectedLinearElement && nextElements.has(insertedSelectedLinearElement.elementId) ? new LinearElementEditor(
14855
14922
  nextElements.get(
14856
- editingLinearElementId
14923
+ insertedSelectedLinearElement.elementId
14857
14924
  ),
14858
- nextElements
14925
+ nextElements,
14926
+ insertedSelectedLinearElement.isEditing
14859
14927
  ) : null;
14860
14928
  const nextAppState = {
14861
14929
  ...appState,
14862
14930
  ...directlyApplicablePartial,
14863
14931
  selectedElementIds: mergedSelectedElementIds,
14864
14932
  selectedGroupIds: mergedSelectedGroupIds,
14865
- selectedLinearElement: typeof selectedLinearElementId !== "undefined" ? selectedLinearElement : appState.selectedLinearElement,
14866
- // otherwise assign what we had before
14867
- editingLinearElement: typeof editingLinearElementId !== "undefined" ? editingLinearElement : appState.editingLinearElement
14868
- // otherwise assign what we had before
14933
+ selectedLinearElement: typeof insertedSelectedLinearElement !== "undefined" ? selectedLinearElement : appState.selectedLinearElement
14869
14934
  };
14870
14935
  const constainsVisibleChanges = this.filterInvisibleChanges(
14871
14936
  appState,
@@ -14955,57 +15020,44 @@ var AppStateDelta = class _AppStateDelta {
14955
15020
  nextAppState[key] = null;
14956
15021
  }
14957
15022
  break;
14958
- case "selectedLinearElementId":
14959
- case "editingLinearElementId":
14960
- const appStateKey = _AppStateDelta.convertToAppStateKey(key);
14961
- const linearElement = nextAppState[appStateKey];
14962
- if (!linearElement) {
15023
+ case "selectedLinearElement":
15024
+ const nextLinearElement = nextAppState[key];
15025
+ if (!nextLinearElement) {
14963
15026
  visibleDifferenceFlag.value = true;
14964
15027
  } else {
14965
- const element = nextElements.get(linearElement.elementId);
15028
+ const element = nextElements.get(nextLinearElement.elementId);
14966
15029
  if (element && !element.isDeleted) {
14967
15030
  visibleDifferenceFlag.value = true;
14968
15031
  } else {
14969
- nextAppState[appStateKey] = null;
15032
+ nextAppState[key] = null;
14970
15033
  }
14971
15034
  }
14972
15035
  break;
14973
- case "lockedMultiSelections": {
15036
+ case "lockedMultiSelections":
14974
15037
  const prevLockedUnits = prevAppState[key] || {};
14975
15038
  const nextLockedUnits = nextAppState[key] || {};
14976
15039
  if (!isShallowEqual2(prevLockedUnits, nextLockedUnits)) {
14977
15040
  visibleDifferenceFlag.value = true;
14978
15041
  }
14979
15042
  break;
14980
- }
14981
- case "activeLockedId": {
15043
+ case "activeLockedId":
14982
15044
  const prevHitLockedId = prevAppState[key] || null;
14983
15045
  const nextHitLockedId = nextAppState[key] || null;
14984
15046
  if (prevHitLockedId !== nextHitLockedId) {
14985
15047
  visibleDifferenceFlag.value = true;
14986
15048
  }
14987
15049
  break;
14988
- }
14989
- default: {
15050
+ default:
14990
15051
  assertNever4(
14991
15052
  key,
14992
15053
  `Unknown ObservedElementsAppState's key "${key}"`,
14993
15054
  true
14994
15055
  );
14995
- }
14996
15056
  }
14997
15057
  }
14998
15058
  }
14999
15059
  return visibleDifferenceFlag.value;
15000
15060
  }
15001
- static convertToAppStateKey(key) {
15002
- switch (key) {
15003
- case "selectedLinearElementId":
15004
- return "selectedLinearElement";
15005
- case "editingLinearElementId":
15006
- return "editingLinearElement";
15007
- }
15008
- }
15009
15061
  static filterSelectedElements(selectedElementIds, elements, visibleDifferenceFlag) {
15010
15062
  const ids = Object.keys(selectedElementIds);
15011
15063
  if (!ids.length) {
@@ -15044,8 +15096,7 @@ var AppStateDelta = class _AppStateDelta {
15044
15096
  editingGroupId,
15045
15097
  selectedGroupIds,
15046
15098
  selectedElementIds,
15047
- editingLinearElementId,
15048
- selectedLinearElementId,
15099
+ selectedLinearElement,
15049
15100
  croppingElementId,
15050
15101
  lockedMultiSelections,
15051
15102
  activeLockedId,
@@ -15199,7 +15250,7 @@ var ElementsDelta = class _ElementsDelta {
15199
15250
  for (const prevElement of prevElements.values()) {
15200
15251
  const nextElement = nextElements.get(prevElement.id);
15201
15252
  if (!nextElement) {
15202
- const deleted = { ...prevElement, isDeleted: false };
15253
+ const deleted = { ...prevElement };
15203
15254
  const inserted = {
15204
15255
  isDeleted: true,
15205
15256
  version: prevElement.version + 1,
@@ -15210,7 +15261,9 @@ var ElementsDelta = class _ElementsDelta {
15210
15261
  inserted,
15211
15262
  _ElementsDelta.stripIrrelevantProps
15212
15263
  );
15213
- removed[prevElement.id] = delta;
15264
+ if (!prevElement.isDeleted) {
15265
+ removed[prevElement.id] = delta;
15266
+ }
15214
15267
  }
15215
15268
  }
15216
15269
  for (const nextElement of nextElements.values()) {
@@ -15222,15 +15275,16 @@ var ElementsDelta = class _ElementsDelta {
15222
15275
  versionNonce: randomInteger4()
15223
15276
  };
15224
15277
  const inserted = {
15225
- ...nextElement,
15226
- isDeleted: false
15278
+ ...nextElement
15227
15279
  };
15228
15280
  const delta = Delta.create(
15229
15281
  deleted,
15230
15282
  inserted,
15231
15283
  _ElementsDelta.stripIrrelevantProps
15232
15284
  );
15233
- added[nextElement.id] = delta;
15285
+ if (!nextElement.isDeleted) {
15286
+ added[nextElement.id] = delta;
15287
+ }
15234
15288
  continue;
15235
15289
  }
15236
15290
  if (prevElement.versionNonce !== nextElement.versionNonce) {
@@ -15251,7 +15305,11 @@ var ElementsDelta = class _ElementsDelta {
15251
15305
  }
15252
15306
  continue;
15253
15307
  }
15254
- if (!Delta.isEmpty(delta)) {
15308
+ const strippedDeleted = _ElementsDelta.stripVersionProps(delta.deleted);
15309
+ const strippedInserted = _ElementsDelta.stripVersionProps(
15310
+ delta.inserted
15311
+ );
15312
+ if (Delta.isInnerDifferent(strippedDeleted, strippedInserted, true)) {
15255
15313
  updated[nextElement.id] = delta;
15256
15314
  }
15257
15315
  }
@@ -15264,8 +15322,8 @@ var ElementsDelta = class _ElementsDelta {
15264
15322
  inverse() {
15265
15323
  const inverseInternal = (deltas) => {
15266
15324
  const inversedDeltas = {};
15267
- for (const [id, delta] of Object.entries(deltas)) {
15268
- inversedDeltas[id] = Delta.create(delta.inserted, delta.deleted);
15325
+ for (const [id, { inserted, deleted }] of Object.entries(deltas)) {
15326
+ inversedDeltas[id] = Delta.create({ ...inserted }, { ...deleted });
15269
15327
  }
15270
15328
  return inversedDeltas;
15271
15329
  };
@@ -15329,7 +15387,13 @@ var ElementsDelta = class _ElementsDelta {
15329
15387
  } else {
15330
15388
  latestDelta = delta;
15331
15389
  }
15332
- if (Delta.isInnerDifferent(latestDelta.deleted, latestDelta.inserted)) {
15390
+ const strippedDeleted = _ElementsDelta.stripVersionProps(
15391
+ latestDelta.deleted
15392
+ );
15393
+ const strippedInserted = _ElementsDelta.stripVersionProps(
15394
+ latestDelta.inserted
15395
+ );
15396
+ if (Delta.isInnerDifferent(strippedDeleted, strippedInserted)) {
15333
15397
  modifiedDeltas[id] = latestDelta;
15334
15398
  }
15335
15399
  }
@@ -15397,6 +15461,34 @@ var ElementsDelta = class _ElementsDelta {
15397
15461
  return [nextElements, flags.containsVisibleDifference];
15398
15462
  }
15399
15463
  }
15464
+ squash(delta) {
15465
+ const { added, removed, updated } = delta;
15466
+ for (const [id, nextDelta] of Object.entries(added)) {
15467
+ const prevDelta = this.added[id];
15468
+ if (!prevDelta) {
15469
+ this.added[id] = nextDelta;
15470
+ } else {
15471
+ this.added[id] = Delta.merge(prevDelta, nextDelta);
15472
+ }
15473
+ }
15474
+ for (const [id, nextDelta] of Object.entries(removed)) {
15475
+ const prevDelta = this.removed[id];
15476
+ if (!prevDelta) {
15477
+ this.removed[id] = nextDelta;
15478
+ } else {
15479
+ this.removed[id] = Delta.merge(prevDelta, nextDelta);
15480
+ }
15481
+ }
15482
+ for (const [id, nextDelta] of Object.entries(updated)) {
15483
+ const prevDelta = this.updated[id];
15484
+ if (!prevDelta) {
15485
+ this.updated[id] = nextDelta;
15486
+ } else {
15487
+ this.updated[id] = Delta.merge(prevDelta, nextDelta);
15488
+ }
15489
+ }
15490
+ return this;
15491
+ }
15400
15492
  static createApplier = (nextElements, snapshot, options, flags) => (deltas) => {
15401
15493
  const getElement = _ElementsDelta.createGetter(
15402
15494
  nextElements,
@@ -15540,19 +15632,10 @@ var ElementsDelta = class _ElementsDelta {
15540
15632
  const prevAffectedElements = new Map(
15541
15633
  Array.from(prevElements).filter(([id]) => nextAffectedElements.has(id))
15542
15634
  );
15543
- const { added, removed, updated } = _ElementsDelta.calculate(
15544
- prevAffectedElements,
15545
- nextAffectedElements
15635
+ this.squash(
15636
+ // technically we could do better here if perf. would become an issue
15637
+ _ElementsDelta.calculate(prevAffectedElements, nextAffectedElements)
15546
15638
  );
15547
- for (const [id, delta] of Object.entries(added)) {
15548
- this.added[id] = delta;
15549
- }
15550
- for (const [id, delta] of Object.entries(removed)) {
15551
- this.removed[id] = delta;
15552
- }
15553
- for (const [id, delta] of Object.entries(updated)) {
15554
- this.updated[id] = delta;
15555
- }
15556
15639
  return nextAffectedElements;
15557
15640
  }
15558
15641
  /**
@@ -15675,14 +15758,22 @@ var ElementsDelta = class _ElementsDelta {
15675
15758
  const { id, updated, ...strippedPartial } = partial;
15676
15759
  return strippedPartial;
15677
15760
  }
15761
+ static stripVersionProps(partial) {
15762
+ const { version, versionNonce, ...strippedPartial } = partial;
15763
+ return strippedPartial;
15764
+ }
15678
15765
  };
15679
15766
 
15680
15767
  // src/distribute.ts
15681
15768
  init_define_import_meta_env();
15682
- var distributeElements = (selectedElements, elementsMap, distribution) => {
15769
+ var distributeElements = (selectedElements, elementsMap, distribution, appState) => {
15683
15770
  const [start, mid, end, extent] = distribution.axis === "x" ? ["minX", "midX", "maxX", "width"] : ["minY", "midY", "maxY", "height"];
15684
15771
  const bounds = getCommonBoundingBox(selectedElements);
15685
- const groups = getMaximumGroups(selectedElements, elementsMap).map((group) => [group, getCommonBoundingBox(group)]).sort((a2, b2) => a2[1][mid] - b2[1][mid]);
15772
+ const groups = getSelectedElementsByGroup(
15773
+ selectedElements,
15774
+ elementsMap,
15775
+ appState
15776
+ ).map((group) => [group, getCommonBoundingBox(group)]).sort((a2, b2) => a2[1][mid] - b2[1][mid]);
15686
15777
  let span = 0;
15687
15778
  for (const group of groups) {
15688
15779
  span += group[1][extent];
@@ -16325,7 +16416,7 @@ var newImageElement = (opts) => {
16325
16416
 
16326
16417
  // src/embeddable.ts
16327
16418
  var embeddedLinkCache = /* @__PURE__ */ new Map();
16328
- var RE_YOUTUBE = /^(?:http(?:s)?:\/\/)?(?:www\.)?youtu(?:be\.com|\.be)\/(embed\/|watch\?v=|shorts\/|playlist\?list=|embed\/videoseries\?list=)?([a-zA-Z0-9_-]+)(?:\?t=|&t=|\?start=|&start=)?([a-zA-Z0-9_-]+)?[^\s]*$/;
16419
+ var RE_YOUTUBE = /^(?:http(?:s)?:\/\/)?(?:www\.)?youtu(?:be\.com|\.be)\/(embed\/|watch\?v=|shorts\/|playlist\?list=|embed\/videoseries\?list=)?([a-zA-Z0-9_-]+)/;
16329
16420
  var RE_VIMEO = /^(?:http(?:s)?:\/\/)?(?:(?:w){3}\.)?(?:player\.)?vimeo\.com\/(?:video\/)?([^?\s]+)(?:\?.*)?$/;
16330
16421
  var RE_FIGMA = /^https:\/\/(?:www\.)?figma\.com/;
16331
16422
  var RE_GH_GIST = /^https:\/\/gist\.github\.com\/([\w_-]+)\/([\w_-]+)/;
@@ -16338,6 +16429,28 @@ var RE_GENERIC_EMBED = /^<(?:iframe|blockquote)[\s\S]*?\s(?:src|href)=["']([^"']
16338
16429
  var RE_GIPHY = /giphy.com\/(?:clips|embed|gifs)\/[a-zA-Z0-9]*?-?([a-zA-Z0-9]+)(?:[^a-zA-Z0-9]|$)/;
16339
16430
  var RE_REDDIT = /^(?:http(?:s)?:\/\/)?(?:www\.)?reddit\.com\/r\/([a-zA-Z0-9_]+)\/comments\/([a-zA-Z0-9_]+)\/([a-zA-Z0-9_]+)\/?(?:\?[^#\s]*)?(?:#[^\s]*)?$/;
16340
16431
  var RE_REDDIT_EMBED = /^<blockquote[\s\S]*?\shref=["'](https?:\/\/(?:www\.)?reddit\.com\/[^"']*)/i;
16432
+ var parseYouTubeTimestamp = (url) => {
16433
+ let timeParam;
16434
+ try {
16435
+ const urlObj = new URL(url.startsWith("http") ? url : `https://${url}`);
16436
+ timeParam = urlObj.searchParams.get("t") || urlObj.searchParams.get("start");
16437
+ } catch (error) {
16438
+ const timeMatch2 = url.match(/[?&#](?:t|start)=([^&#\s]+)/);
16439
+ timeParam = timeMatch2?.[1];
16440
+ }
16441
+ if (!timeParam) {
16442
+ return 0;
16443
+ }
16444
+ if (/^\d+$/.test(timeParam)) {
16445
+ return parseInt(timeParam, 10);
16446
+ }
16447
+ const timeMatch = timeParam.match(/^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/);
16448
+ if (!timeMatch) {
16449
+ return 0;
16450
+ }
16451
+ const [, hours = "0", minutes = "0", seconds = "0"] = timeMatch;
16452
+ return parseInt(hours) * 3600 + parseInt(minutes) * 60 + parseInt(seconds);
16453
+ };
16341
16454
  var ALLOWED_DOMAINS = /* @__PURE__ */ new Set([
16342
16455
  "youtube.com",
16343
16456
  "youtu.be",
@@ -16386,7 +16499,8 @@ var getEmbedLink = (link) => {
16386
16499
  let aspectRatio = { w: 560, h: 840 };
16387
16500
  const ytLink = link.match(RE_YOUTUBE);
16388
16501
  if (ytLink?.[2]) {
16389
- const time = ytLink[3] ? `&start=${ytLink[3]}` : ``;
16502
+ const startTime = parseYouTubeTimestamp(originalLink);
16503
+ const time = startTime > 0 ? `&start=${startTime}` : ``;
16390
16504
  const isPortrait = link.includes("shorts");
16391
16505
  type = "video";
16392
16506
  switch (ytLink[1]) {
@@ -18371,7 +18485,7 @@ var getTransformHandles = (element, zoom, elementsMap, pointerType = "mouse", om
18371
18485
  );
18372
18486
  };
18373
18487
  var shouldShowBoundingBox = (elements, appState) => {
18374
- if (appState.editingLinearElement) {
18488
+ if (appState.selectedLinearElement?.isEditing) {
18375
18489
  return false;
18376
18490
  }
18377
18491
  if (elements.length > 1) {
@@ -19119,6 +19233,7 @@ export {
19119
19233
  getRootElements,
19120
19234
  getSceneVersion,
19121
19235
  getSelectedElements,
19236
+ getSelectedElementsByGroup,
19122
19237
  getSelectedGroupForElement,
19123
19238
  getSelectedGroupIdForElement,
19124
19239
  getSelectedGroupIds,