@excalidraw/excalidraw 0.17.1-1ed53b1 → 0.17.1-2f9526d

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 (76) hide show
  1. package/dist/browser/dev/excalidraw-assets-dev/{chunk-JKPJV7MZ.js → chunk-Q6A4M3MN.js} +4 -2
  2. package/dist/browser/dev/excalidraw-assets-dev/chunk-Q6A4M3MN.js.map +7 -0
  3. package/dist/browser/dev/excalidraw-assets-dev/{chunk-OKAZAA6U.js → chunk-Q6NFAEKN.js} +156 -74
  4. package/dist/browser/dev/excalidraw-assets-dev/chunk-Q6NFAEKN.js.map +7 -0
  5. package/dist/browser/dev/excalidraw-assets-dev/{dist-ITJNUBZF.js → dist-6QVAH5JA.js} +36 -14
  6. package/dist/browser/dev/excalidraw-assets-dev/dist-6QVAH5JA.js.map +7 -0
  7. package/dist/browser/dev/excalidraw-assets-dev/{en-BF4XUPIZ.js → en-Y27YPU72.js} +2 -2
  8. package/dist/browser/dev/excalidraw-assets-dev/{image-LVS32KQQ.js → image-Y5X7K6KW.js} +2 -2
  9. package/dist/browser/dev/index.js +159 -86
  10. package/dist/browser/dev/index.js.map +3 -3
  11. package/dist/browser/prod/excalidraw-assets/{chunk-O4AI3NNG.js → chunk-IZMZ6RPD.js} +1 -1
  12. package/dist/browser/prod/excalidraw-assets/chunk-MDMKPHYD.js +55 -0
  13. package/dist/browser/prod/excalidraw-assets/dist-567JAXHK.js +7 -0
  14. package/dist/browser/prod/excalidraw-assets/{en-N7CLNF6C.js → en-GSUSWMSH.js} +1 -1
  15. package/dist/browser/prod/excalidraw-assets/image-7MVXYJUE.js +1 -0
  16. package/dist/browser/prod/index.js +14 -14
  17. package/dist/dev/{en-UQDDYCH7.json → en-OIPCBIOA.json} +3 -1
  18. package/dist/dev/index.js +320 -157
  19. package/dist/dev/index.js.map +4 -4
  20. package/dist/excalidraw/actions/actionBoundText.js +3 -1
  21. package/dist/excalidraw/actions/actionHistory.js +4 -4
  22. package/dist/excalidraw/actions/actionProperties.js +1 -1
  23. package/dist/excalidraw/actions/actionTextAutoResize.d.ts +17 -0
  24. package/dist/excalidraw/actions/actionTextAutoResize.js +38 -0
  25. package/dist/excalidraw/actions/types.d.ts +1 -1
  26. package/dist/excalidraw/components/Actions.js +1 -1
  27. package/dist/excalidraw/components/App.d.ts +1 -1
  28. package/dist/excalidraw/components/App.js +47 -32
  29. package/dist/excalidraw/components/ButtonIconSelect.js +1 -1
  30. package/dist/excalidraw/components/CheckboxItem.js +1 -1
  31. package/dist/excalidraw/components/CommandPalette/CommandPalette.js +2 -2
  32. package/dist/excalidraw/components/ContextMenu.js +1 -1
  33. package/dist/excalidraw/components/Dialog.js +1 -1
  34. package/dist/excalidraw/components/FollowMode/FollowMode.js +1 -1
  35. package/dist/excalidraw/components/IconPicker.js +2 -2
  36. package/dist/excalidraw/components/LayerUI.js +2 -2
  37. package/dist/excalidraw/components/MobileMenu.js +1 -1
  38. package/dist/excalidraw/components/PasteChartDialog.js +1 -1
  39. package/dist/excalidraw/components/canvases/InteractiveCanvas.d.ts +1 -1
  40. package/dist/excalidraw/components/canvases/InteractiveCanvas.js +2 -2
  41. package/dist/excalidraw/components/canvases/StaticCanvas.d.ts +1 -1
  42. package/dist/excalidraw/components/canvases/StaticCanvas.js +2 -2
  43. package/dist/excalidraw/components/icons.js +6 -2
  44. package/dist/excalidraw/data/restore.js +1 -0
  45. package/dist/excalidraw/element/index.d.ts +1 -1
  46. package/dist/excalidraw/element/index.js +1 -1
  47. package/dist/excalidraw/element/mutateElement.d.ts +1 -1
  48. package/dist/excalidraw/element/mutateElement.js +5 -3
  49. package/dist/excalidraw/element/newElement.d.ts +0 -5
  50. package/dist/excalidraw/element/newElement.js +15 -13
  51. package/dist/excalidraw/element/resizeElements.js +71 -22
  52. package/dist/excalidraw/element/resizeTest.js +2 -4
  53. package/dist/excalidraw/element/textElement.js +8 -3
  54. package/dist/excalidraw/element/textWysiwyg.d.ts +8 -3
  55. package/dist/excalidraw/element/textWysiwyg.js +6 -8
  56. package/dist/excalidraw/element/transformHandles.js +0 -10
  57. package/dist/excalidraw/element/types.d.ts +7 -0
  58. package/dist/excalidraw/locales/en.json +3 -1
  59. package/dist/excalidraw/scene/Fonts.d.ts +1 -3
  60. package/dist/excalidraw/scene/Fonts.js +6 -12
  61. package/dist/excalidraw/scene/Renderer.d.ts +1 -1
  62. package/dist/excalidraw/scene/Renderer.js +2 -3
  63. package/dist/excalidraw/scene/Scene.d.ts +10 -4
  64. package/dist/excalidraw/scene/Scene.js +14 -8
  65. package/dist/excalidraw/scene/export.js +1 -1
  66. package/dist/prod/{en-UQDDYCH7.json → en-OIPCBIOA.json} +3 -1
  67. package/dist/prod/index.js +28 -28
  68. package/package.json +2 -2
  69. package/dist/browser/dev/excalidraw-assets-dev/chunk-JKPJV7MZ.js.map +0 -7
  70. package/dist/browser/dev/excalidraw-assets-dev/chunk-OKAZAA6U.js.map +0 -7
  71. package/dist/browser/dev/excalidraw-assets-dev/dist-ITJNUBZF.js.map +0 -7
  72. package/dist/browser/prod/excalidraw-assets/chunk-SXBDZOS3.js +0 -55
  73. package/dist/browser/prod/excalidraw-assets/dist-54276HPL.js +0 -6
  74. package/dist/browser/prod/excalidraw-assets/image-VAGBVQ3G.js +0 -1
  75. /package/dist/browser/dev/excalidraw-assets-dev/{en-BF4XUPIZ.js.map → en-Y27YPU72.js.map} +0 -0
  76. /package/dist/browser/dev/excalidraw-assets-dev/{image-LVS32KQQ.js.map → image-Y5X7K6KW.js.map} +0 -0
package/dist/dev/index.js CHANGED
@@ -1412,7 +1412,16 @@ var init_Scene = __esm({
1412
1412
  elements: null,
1413
1413
  cache: /* @__PURE__ */ new Map()
1414
1414
  };
1415
- versionNonce;
1415
+ /**
1416
+ * Random integer regenerated each scene update.
1417
+ *
1418
+ * Does not relate to elements versions, it's only a renderer
1419
+ * cache-invalidation nonce at the moment.
1420
+ */
1421
+ sceneNonce;
1422
+ getSceneNonce() {
1423
+ return this.sceneNonce;
1424
+ }
1416
1425
  getNonDeletedElementsMap() {
1417
1426
  return this.nonDeletedElementsMap;
1418
1427
  }
@@ -1457,9 +1466,6 @@ var init_Scene = __esm({
1457
1466
  getElement(id) {
1458
1467
  return this.elementsMap.get(id) || null;
1459
1468
  }
1460
- getVersionNonce() {
1461
- return this.versionNonce;
1462
- }
1463
1469
  getNonDeletedElement(id) {
1464
1470
  const element = this.getElement(id);
1465
1471
  if (element && isNonDeletedElement(element)) {
@@ -1516,15 +1522,15 @@ var init_Scene = __esm({
1516
1522
  this.nonDeletedElementsMap = nonDeletedElements.elementsMap;
1517
1523
  this.frames = nextFrameLikes;
1518
1524
  this.nonDeletedFramesLikes = getNonDeletedElements(this.frames).elements;
1519
- this.informMutation();
1525
+ this.triggerUpdate();
1520
1526
  }
1521
- informMutation() {
1522
- this.versionNonce = randomInteger();
1527
+ triggerUpdate() {
1528
+ this.sceneNonce = randomInteger();
1523
1529
  for (const callback of Array.from(this.callbacks)) {
1524
1530
  callback();
1525
1531
  }
1526
1532
  }
1527
- addCallback(cb) {
1533
+ onUpdate(cb) {
1528
1534
  if (this.callbacks.has(cb)) {
1529
1535
  throw new Error();
1530
1536
  }
@@ -4996,7 +5002,7 @@ var init_containerCache = __esm({
4996
5002
  });
4997
5003
 
4998
5004
  // element/textElement.ts
4999
- var normalizeText, splitIntoLines, redrawTextBoundingBox, bindTextToShapeAfterDuplication, handleBindTextResize, computeBoundTextPosition, measureText, detectLineHeight, getLineHeightInPx, getVerticalOffset, getApproxMinLineHeight, canvas, getLineWidth, getTextWidth, getTextHeight, parseTokens, wrapText, charWidth, DUMMY_TEXT, getApproxMinLineWidth, getMaxCharWidth, getBoundTextElementId, getBoundTextElement, getContainerElement, getContainerCenter, getContainerCoords, getTextElementAngle, shouldAllowVerticalAlign, suppportsHorizontalAlign, VALID_CONTAINER_TYPES, isValidTextContainer, computeContainerDimensionForBoundText, getBoundTextMaxWidth, getBoundTextMaxHeight, isMeasureTextSupported, DEFAULT_LINE_HEIGHT, FONT_METRICS, getDefaultLineHeight;
5005
+ var normalizeText, splitIntoLines, redrawTextBoundingBox, bindTextToShapeAfterDuplication, handleBindTextResize, computeBoundTextPosition, measureText, detectLineHeight, getLineHeightInPx, getVerticalOffset, getApproxMinLineHeight, canvas, getLineWidth, getTextWidth, getTextHeight, parseTokens, wrapText, charWidth, DUMMY_TEXT, getApproxMinLineWidth, getMinCharWidth, getMaxCharWidth, getBoundTextElementId, getBoundTextElement, getContainerElement, getContainerCenter, getContainerCoords, getTextElementAngle, shouldAllowVerticalAlign, suppportsHorizontalAlign, VALID_CONTAINER_TYPES, isValidTextContainer, computeContainerDimensionForBoundText, getBoundTextMaxWidth, getBoundTextMaxHeight, isMeasureTextSupported, DEFAULT_LINE_HEIGHT, FONT_METRICS, getDefaultLineHeight;
5000
5006
  var init_textElement = __esm({
5001
5007
  "element/textElement.ts"() {
5002
5008
  "use strict";
@@ -5025,8 +5031,8 @@ var init_textElement = __esm({
5025
5031
  angle: container?.angle ?? textElement.angle
5026
5032
  };
5027
5033
  boundTextUpdates.text = textElement.text;
5028
- if (container) {
5029
- maxWidth = getBoundTextMaxWidth(container, textElement);
5034
+ if (container || !textElement.autoResize) {
5035
+ maxWidth = container ? getBoundTextMaxWidth(container, textElement) : textElement.width;
5030
5036
  boundTextUpdates.text = wrapText(
5031
5037
  textElement.originalText,
5032
5038
  getFontString(textElement),
@@ -5038,7 +5044,9 @@ var init_textElement = __esm({
5038
5044
  getFontString(textElement),
5039
5045
  textElement.lineHeight
5040
5046
  );
5041
- boundTextUpdates.width = metrics.width;
5047
+ if (textElement.autoResize) {
5048
+ boundTextUpdates.width = metrics.width;
5049
+ }
5042
5050
  boundTextUpdates.height = metrics.height;
5043
5051
  if (container) {
5044
5052
  const maxContainerHeight = getBoundTextMaxHeight(
@@ -5371,6 +5379,14 @@ var init_textElement = __esm({
5371
5379
  }
5372
5380
  return maxCharWidth + BOUND_TEXT_PADDING * 2;
5373
5381
  };
5382
+ getMinCharWidth = (font) => {
5383
+ const cache = charWidth.getCache(font);
5384
+ if (!cache) {
5385
+ return 0;
5386
+ }
5387
+ const cacheWithOutEmpty = cache.filter((val) => val !== void 0);
5388
+ return Math.min(...cacheWithOutEmpty);
5389
+ };
5374
5390
  getMaxCharWidth = (font) => {
5375
5391
  const cache = charWidth.getCache(font);
5376
5392
  if (!cache) {
@@ -6757,7 +6773,7 @@ var init_cursor = __esm({
6757
6773
  });
6758
6774
 
6759
6775
  // element/newElement.ts
6760
- var _newElementBase, newElement, newEmbeddableElement, newIframeElement, newFrameElement, newMagicFrameElement, getTextElementPositionOffsets, newTextElement, getAdjustedDimensions, refreshTextDimensions, updateTextElement, newFreeDrawElement, newLinearElement, newImageElement, _deepCopyElement, deepCopyElement, regenerateId, duplicateElement, duplicateElements;
6776
+ var _newElementBase, newElement, newEmbeddableElement, newIframeElement, newFrameElement, newMagicFrameElement, getTextElementPositionOffsets, newTextElement, getAdjustedDimensions, refreshTextDimensions, newFreeDrawElement, newLinearElement, newImageElement, _deepCopyElement, deepCopyElement, regenerateId, duplicateElement, duplicateElements;
6761
6777
  var init_newElement = __esm({
6762
6778
  "element/newElement.ts"() {
6763
6779
  "use strict";
@@ -6877,36 +6893,41 @@ var init_newElement = __esm({
6877
6893
  { textAlign, verticalAlign },
6878
6894
  metrics
6879
6895
  );
6896
+ const textElementProps = {
6897
+ ..._newElementBase("text", opts),
6898
+ text,
6899
+ fontSize,
6900
+ fontFamily,
6901
+ textAlign,
6902
+ verticalAlign,
6903
+ x: opts.x - offsets.x,
6904
+ y: opts.y - offsets.y,
6905
+ width: metrics.width,
6906
+ height: metrics.height,
6907
+ containerId: opts.containerId || null,
6908
+ originalText: text,
6909
+ autoResize: true,
6910
+ lineHeight
6911
+ };
6880
6912
  const textElement = newElementWith(
6881
- {
6882
- ..._newElementBase("text", opts),
6883
- text,
6884
- fontSize,
6885
- fontFamily,
6886
- textAlign,
6887
- verticalAlign,
6888
- x: opts.x - offsets.x,
6889
- y: opts.y - offsets.y,
6890
- width: metrics.width,
6891
- height: metrics.height,
6892
- containerId: opts.containerId || null,
6893
- originalText: text,
6894
- lineHeight
6895
- },
6913
+ textElementProps,
6896
6914
  {}
6897
6915
  );
6898
6916
  return textElement;
6899
6917
  };
6900
6918
  getAdjustedDimensions = (element, elementsMap, nextText) => {
6901
- const { width: nextWidth, height: nextHeight } = measureText(
6919
+ let { width: nextWidth, height: nextHeight } = measureText(
6902
6920
  nextText,
6903
6921
  getFontString(element),
6904
6922
  element.lineHeight
6905
6923
  );
6924
+ if (!element.autoResize) {
6925
+ nextWidth = element.width;
6926
+ }
6906
6927
  const { textAlign, verticalAlign } = element;
6907
6928
  let x;
6908
6929
  let y;
6909
- if (textAlign === "center" && verticalAlign === VERTICAL_ALIGN.MIDDLE && !element.containerId) {
6930
+ if (textAlign === "center" && verticalAlign === VERTICAL_ALIGN.MIDDLE && !element.containerId && element.autoResize) {
6910
6931
  const prevMetrics = measureText(
6911
6932
  element.text,
6912
6933
  getFontString(element),
@@ -6956,27 +6977,16 @@ var init_newElement = __esm({
6956
6977
  if (textElement.isDeleted) {
6957
6978
  return;
6958
6979
  }
6959
- if (container) {
6980
+ if (container || !textElement.autoResize) {
6960
6981
  text = wrapText(
6961
6982
  text,
6962
6983
  getFontString(textElement),
6963
- getBoundTextMaxWidth(container, textElement)
6984
+ container ? getBoundTextMaxWidth(container, textElement) : textElement.width
6964
6985
  );
6965
6986
  }
6966
6987
  const dimensions = getAdjustedDimensions(textElement, elementsMap, text);
6967
6988
  return { text, ...dimensions };
6968
6989
  };
6969
- updateTextElement = (textElement, container, elementsMap, {
6970
- text,
6971
- isDeleted,
6972
- originalText
6973
- }) => {
6974
- return newElementWith(textElement, {
6975
- originalText,
6976
- isDeleted: isDeleted ?? textElement.isDeleted,
6977
- ...refreshTextDimensions(textElement, container, elementsMap, originalText)
6978
- });
6979
- };
6980
6990
  newFreeDrawElement = (opts) => {
6981
6991
  return {
6982
6992
  ..._newElementBase(opts.type, opts),
@@ -9446,6 +9456,7 @@ var init_restore = __esm({
9446
9456
  verticalAlign: element.verticalAlign || DEFAULT_VERTICAL_ALIGN,
9447
9457
  containerId: element.containerId ?? null,
9448
9458
  originalText: element.originalText || text,
9459
+ autoResize: element.autoResize ?? true,
9449
9460
  lineHeight
9450
9461
  });
9451
9462
  if (!text && !element.isDeleted) {
@@ -11303,7 +11314,7 @@ var init_export = __esm({
11303
11314
  }
11304
11315
  let assetPath = "https://excalidraw.com/";
11305
11316
  if (define_import_meta_env_default.VITE_IS_EXCALIDRAW_NPM_PACKAGE) {
11306
- assetPath = window.EXCALIDRAW_ASSET_PATH || `https://unpkg.com/${define_import_meta_env_default.VITE_PKG_NAME}@${define_import_meta_env_default.PKG_VERSION}`;
11317
+ assetPath = window.EXCALIDRAW_ASSET_PATH || `https://unpkg.com/${define_import_meta_env_default.VITE_PKG_NAME}@${define_import_meta_env_default.VITE_PKG_VERSION}`;
11307
11318
  if (assetPath?.startsWith("/")) {
11308
11319
  assetPath = assetPath.replace("/", `${window.location.origin}/`);
11309
11320
  }
@@ -13524,11 +13535,11 @@ var init_mutateElement = __esm({
13524
13535
  element.versionNonce = randomInteger();
13525
13536
  element.updated = getUpdatedTimestamp();
13526
13537
  if (informMutation) {
13527
- Scene_default.getScene(element)?.informMutation();
13538
+ Scene_default.getScene(element)?.triggerUpdate();
13528
13539
  }
13529
13540
  return element;
13530
13541
  };
13531
- newElementWith = (element, updates) => {
13542
+ newElementWith = (element, updates, force = false) => {
13532
13543
  let didChange = false;
13533
13544
  for (const key in updates) {
13534
13545
  const value = updates[key];
@@ -13540,7 +13551,7 @@ var init_mutateElement = __esm({
13540
13551
  didChange = true;
13541
13552
  }
13542
13553
  }
13543
- if (!didChange) {
13554
+ if (!didChange && !force) {
13544
13555
  return element;
13545
13556
  }
13546
13557
  return {
@@ -13657,14 +13668,13 @@ var init_sizeHelpers = __esm({
13657
13668
  });
13658
13669
 
13659
13670
  // element/transformHandles.ts
13660
- var transformHandleSizes, ROTATION_RESIZE_HANDLE_GAP, DEFAULT_OMIT_SIDES, OMIT_SIDES_FOR_TEXT_ELEMENT, OMIT_SIDES_FOR_LINE_SLASH, OMIT_SIDES_FOR_LINE_BACKSLASH, generateTransformHandle, canResizeFromSides, getOmitSidesForDevice, getTransformHandlesFromCoords, getTransformHandles, shouldShowBoundingBox;
13671
+ var transformHandleSizes, ROTATION_RESIZE_HANDLE_GAP, DEFAULT_OMIT_SIDES, OMIT_SIDES_FOR_LINE_SLASH, OMIT_SIDES_FOR_LINE_BACKSLASH, generateTransformHandle, canResizeFromSides, getOmitSidesForDevice, getTransformHandlesFromCoords, getTransformHandles, shouldShowBoundingBox;
13661
13672
  var init_transformHandles = __esm({
13662
13673
  "element/transformHandles.ts"() {
13663
13674
  "use strict";
13664
13675
  init_define_import_meta_env();
13665
13676
  init_bounds();
13666
13677
  init_math();
13667
- init_element();
13668
13678
  init_typeChecks();
13669
13679
  init_constants();
13670
13680
  transformHandleSizes = {
@@ -13679,12 +13689,6 @@ var init_transformHandles = __esm({
13679
13689
  n: true,
13680
13690
  w: true
13681
13691
  };
13682
- OMIT_SIDES_FOR_TEXT_ELEMENT = {
13683
- e: true,
13684
- s: true,
13685
- n: true,
13686
- w: true
13687
- };
13688
13692
  OMIT_SIDES_FOR_LINE_SLASH = {
13689
13693
  e: true,
13690
13694
  s: true,
@@ -13845,8 +13849,6 @@ var init_transformHandles = __esm({
13845
13849
  omitSides = OMIT_SIDES_FOR_LINE_BACKSLASH;
13846
13850
  }
13847
13851
  }
13848
- } else if (isTextElement(element)) {
13849
- omitSides = OMIT_SIDES_FOR_TEXT_ELEMENT;
13850
13852
  } else if (isFrameLikeElement(element)) {
13851
13853
  omitSides = {
13852
13854
  ...omitSides,
@@ -13920,7 +13922,7 @@ var init_resizeTest = __esm({
13920
13922
  element,
13921
13923
  elementsMap
13922
13924
  );
13923
- if (element.type !== "text" && !(isLinearElement(element) && element.points.length <= 2)) {
13925
+ if (!(isLinearElement(element) && element.points.length <= 2)) {
13924
13926
  const SPACING = SIDE_RESIZING_THRESHOLD / zoom.value;
13925
13927
  const sides = getSelectionBorders(
13926
13928
  [x1 - SPACING, y1 - SPACING],
@@ -14088,8 +14090,9 @@ var init_resizeElements = __esm({
14088
14090
  shouldRotateWithDiscreteAngle2
14089
14091
  );
14090
14092
  updateBoundElements(element, elementsMap);
14091
- } else if (isTextElement(element) && (transformHandleType === "nw" || transformHandleType === "ne" || transformHandleType === "sw" || transformHandleType === "se")) {
14093
+ } else if (isTextElement(element) && transformHandleType) {
14092
14094
  resizeSingleTextElement(
14095
+ originalElements,
14093
14096
  element,
14094
14097
  elementsMap,
14095
14098
  transformHandleType,
@@ -14191,7 +14194,7 @@ var init_resizeElements = __esm({
14191
14194
  size: nextFontSize
14192
14195
  };
14193
14196
  };
14194
- resizeSingleTextElement = (element, elementsMap, transformHandleType, shouldResizeFromCenter2, pointerX, pointerY) => {
14197
+ resizeSingleTextElement = (originalElements, element, elementsMap, transformHandleType, shouldResizeFromCenter2, pointerX, pointerY) => {
14195
14198
  const [x1, y1, x2, y2, cx, cy] = getElementAbsoluteCoords(
14196
14199
  element,
14197
14200
  elementsMap
@@ -14205,17 +14208,19 @@ var init_resizeElements = __esm({
14205
14208
  );
14206
14209
  let scaleX = 0;
14207
14210
  let scaleY = 0;
14208
- if (transformHandleType.includes("e")) {
14209
- scaleX = (rotatedX - x1) / (x2 - x1);
14210
- }
14211
- if (transformHandleType.includes("w")) {
14212
- scaleX = (x2 - rotatedX) / (x2 - x1);
14213
- }
14214
- if (transformHandleType.includes("n")) {
14215
- scaleY = (y2 - rotatedY) / (y2 - y1);
14216
- }
14217
- if (transformHandleType.includes("s")) {
14218
- scaleY = (rotatedY - y1) / (y2 - y1);
14211
+ if (transformHandleType !== "e" && transformHandleType !== "w") {
14212
+ if (transformHandleType.includes("e")) {
14213
+ scaleX = (rotatedX - x1) / (x2 - x1);
14214
+ }
14215
+ if (transformHandleType.includes("w")) {
14216
+ scaleX = (x2 - rotatedX) / (x2 - x1);
14217
+ }
14218
+ if (transformHandleType.includes("n")) {
14219
+ scaleY = (y2 - rotatedY) / (y2 - y1);
14220
+ }
14221
+ if (transformHandleType.includes("s")) {
14222
+ scaleY = (rotatedY - y1) / (y2 - y1);
14223
+ }
14219
14224
  }
14220
14225
  const scale = Math.max(scaleX, scaleY);
14221
14226
  if (scale > 0) {
@@ -14270,6 +14275,83 @@ var init_resizeElements = __esm({
14270
14275
  y: nextY
14271
14276
  });
14272
14277
  }
14278
+ if (transformHandleType === "e" || transformHandleType === "w") {
14279
+ const stateAtResizeStart = originalElements.get(element.id);
14280
+ const [x12, y12, x22, y22] = getResizedElementAbsoluteCoords(
14281
+ stateAtResizeStart,
14282
+ stateAtResizeStart.width,
14283
+ stateAtResizeStart.height,
14284
+ true
14285
+ );
14286
+ const startTopLeft = [x12, y12];
14287
+ const startBottomRight = [x22, y22];
14288
+ const startCenter = centerPoint(startTopLeft, startBottomRight);
14289
+ const rotatedPointer = rotatePoint(
14290
+ [pointerX, pointerY],
14291
+ startCenter,
14292
+ -stateAtResizeStart.angle
14293
+ );
14294
+ const [esx1, , esx2] = getResizedElementAbsoluteCoords(
14295
+ element,
14296
+ element.width,
14297
+ element.height,
14298
+ true
14299
+ );
14300
+ const boundsCurrentWidth = esx2 - esx1;
14301
+ const atStartBoundsWidth = startBottomRight[0] - startTopLeft[0];
14302
+ const minWidth = getMinCharWidth(getFontString(element)) + BOUND_TEXT_PADDING * 2;
14303
+ let scaleX2 = atStartBoundsWidth / boundsCurrentWidth;
14304
+ if (transformHandleType.includes("e")) {
14305
+ scaleX2 = (rotatedPointer[0] - startTopLeft[0]) / boundsCurrentWidth;
14306
+ }
14307
+ if (transformHandleType.includes("w")) {
14308
+ scaleX2 = (startBottomRight[0] - rotatedPointer[0]) / boundsCurrentWidth;
14309
+ }
14310
+ const newWidth = element.width * scaleX2 < minWidth ? minWidth : element.width * scaleX2;
14311
+ const text = wrapText(
14312
+ element.originalText,
14313
+ getFontString(element),
14314
+ Math.abs(newWidth)
14315
+ );
14316
+ const metrics = measureText(
14317
+ text,
14318
+ getFontString(element),
14319
+ element.lineHeight
14320
+ );
14321
+ const eleNewHeight = metrics.height;
14322
+ const [newBoundsX1, newBoundsY1, newBoundsX2, newBoundsY2] = getResizedElementAbsoluteCoords(
14323
+ stateAtResizeStart,
14324
+ newWidth,
14325
+ eleNewHeight,
14326
+ true
14327
+ );
14328
+ const newBoundsWidth = newBoundsX2 - newBoundsX1;
14329
+ const newBoundsHeight = newBoundsY2 - newBoundsY1;
14330
+ let newTopLeft = [...startTopLeft];
14331
+ if (["n", "w", "nw"].includes(transformHandleType)) {
14332
+ newTopLeft = [
14333
+ startBottomRight[0] - Math.abs(newBoundsWidth),
14334
+ startTopLeft[1]
14335
+ ];
14336
+ }
14337
+ const angle = stateAtResizeStart.angle;
14338
+ const rotatedTopLeft = rotatePoint(newTopLeft, startCenter, angle);
14339
+ const newCenter = [
14340
+ newTopLeft[0] + Math.abs(newBoundsWidth) / 2,
14341
+ newTopLeft[1] + Math.abs(newBoundsHeight) / 2
14342
+ ];
14343
+ const rotatedNewCenter = rotatePoint(newCenter, startCenter, angle);
14344
+ newTopLeft = rotatePoint(rotatedTopLeft, rotatedNewCenter, -angle);
14345
+ const resizedElement = {
14346
+ width: Math.abs(newWidth),
14347
+ height: Math.abs(metrics.height),
14348
+ x: newTopLeft[0],
14349
+ y: newTopLeft[1],
14350
+ text,
14351
+ autoResize: false
14352
+ };
14353
+ mutateElement(element, resizedElement);
14354
+ }
14273
14355
  };
14274
14356
  resizeSingleElement = (originalElements, shouldMaintainAspectRatio2, element, elementsMap, transformHandleDirection, shouldResizeFromCenter2, pointerX, pointerY) => {
14275
14357
  const stateAtResizeStart = originalElements.get(element.id);
@@ -14661,7 +14743,7 @@ var init_resizeElements = __esm({
14661
14743
  handleBindTextResize(element, elementsMap, transformHandleType, true);
14662
14744
  }
14663
14745
  }
14664
- Scene_default.getScene(elementsAndUpdates[0].element)?.informMutation();
14746
+ Scene_default.getScene(elementsAndUpdates[0].element)?.triggerUpdate();
14665
14747
  };
14666
14748
  rotateMultipleElements = (originalElements, elements, elementsMap, pointerX, pointerY, shouldRotateWithDiscreteAngle2, centerX, centerY) => {
14667
14749
  let centerAngle = 5 * Math.PI / 2 + Math.atan2(pointerY - centerY, pointerX - centerX);
@@ -14706,7 +14788,7 @@ var init_resizeElements = __esm({
14706
14788
  );
14707
14789
  }
14708
14790
  });
14709
- Scene_default.getScene(elements[0])?.informMutation();
14791
+ Scene_default.getScene(elements[0])?.triggerUpdate();
14710
14792
  };
14711
14793
  getResizeOffsetXY = (transformHandleType, selectedElements, elementsMap, x, y) => {
14712
14794
  const [x1, y1, x2, y2] = selectedElements.length === 1 ? getElementAbsoluteCoords(selectedElements[0], elementsMap) : getCommonBounds(selectedElements);
@@ -15193,7 +15275,7 @@ init_define_import_meta_env();
15193
15275
 
15194
15276
  // i18n.ts
15195
15277
  init_define_import_meta_env();
15196
- import fallbackLangData from "./en-UQDDYCH7.json";
15278
+ import fallbackLangData from "./en-OIPCBIOA.json";
15197
15279
  import percentages from "./percentages-UCQDHIQF.json";
15198
15280
 
15199
15281
  // jotai.ts
@@ -15217,7 +15299,7 @@ var globImport_locales_json = __glob({
15217
15299
  "./locales/da-DK.json": () => import("./da-DK-WBEQB3CJ.json"),
15218
15300
  "./locales/de-DE.json": () => import("./de-DE-VEIMCP7R.json"),
15219
15301
  "./locales/el-GR.json": () => import("./el-GR-TKRKG5GQ.json"),
15220
- "./locales/en.json": () => import("./en-UQDDYCH7.json"),
15302
+ "./locales/en.json": () => import("./en-OIPCBIOA.json"),
15221
15303
  "./locales/es-ES.json": () => import("./es-ES-TOLWEZNW.json"),
15222
15304
  "./locales/eu-ES.json": () => import("./eu-ES-7CDRJQWJ.json"),
15223
15305
  "./locales/fa-IR.json": () => import("./fa-IR-527E2XGU.json"),
@@ -16302,12 +16384,16 @@ var arrownNarrowUpJSX = /* @__PURE__ */ jsxs3("g", { strokeWidth: 1.5, children:
16302
16384
  var BringForwardIcon = createIcon(arrownNarrowUpJSX, tablerIconProps);
16303
16385
  var SendBackwardIcon = createIcon(arrownNarrowUpJSX, {
16304
16386
  ...tablerIconProps,
16305
- transform: "rotate(180)"
16387
+ style: {
16388
+ transform: "rotate(180deg)"
16389
+ }
16306
16390
  });
16307
16391
  var BringToFrontIcon = createIcon(arrowBarToTopJSX, tablerIconProps);
16308
16392
  var SendToBackIcon = createIcon(arrowBarToTopJSX, {
16309
16393
  ...tablerIconProps,
16310
- transform: "rotate(180)"
16394
+ style: {
16395
+ transform: "rotate(180deg)"
16396
+ }
16311
16397
  });
16312
16398
  var AlignTopIcon = createIcon(
16313
16399
  /* @__PURE__ */ jsxs3(Fragment, { children: [
@@ -18625,6 +18711,7 @@ var ButtonIconSelect = (props) => /* @__PURE__ */ jsx9("div", { className: "butt
18625
18711
  (option) => props.type === "button" ? /* @__PURE__ */ jsx9(
18626
18712
  "button",
18627
18713
  {
18714
+ type: "button",
18628
18715
  onClick: (event) => props.onClick(option.value, event),
18629
18716
  className: clsx4({
18630
18717
  active: option.active ?? props.value === option.value
@@ -20133,6 +20220,7 @@ function Picker2({
20133
20220
  children: /* @__PURE__ */ jsx20("div", { className: "picker-content", ref: rGallery, children: options.map((option, i) => /* @__PURE__ */ jsxs12(
20134
20221
  "button",
20135
20222
  {
20223
+ type: "button",
20136
20224
  className: clsx11("picker-option", {
20137
20225
  active: value === option.value
20138
20226
  }),
@@ -20179,6 +20267,7 @@ function IconPicker({
20179
20267
  "button",
20180
20268
  {
20181
20269
  name: group,
20270
+ type: "button",
20182
20271
  className: isActive ? "active" : "",
20183
20272
  "aria-label": label,
20184
20273
  onClick: () => setActive(!isActive),
@@ -20262,7 +20351,7 @@ var getFormValue = function(elements, appState, getAttribute, isRelevantElement,
20262
20351
  return ret;
20263
20352
  };
20264
20353
  var offsetElementAfterFontResize = (prevElement, nextElement) => {
20265
- if (isBoundToContainer(nextElement)) {
20354
+ if (isBoundToContainer(nextElement) || !nextElement.autoResize) {
20266
20355
  return nextElement;
20267
20356
  }
20268
20357
  return mutateElement(
@@ -22184,7 +22273,16 @@ var CheckboxItem = ({ children, checked, onChange, className }) => {
22184
22273
  ).focus();
22185
22274
  },
22186
22275
  children: [
22187
- /* @__PURE__ */ jsx27("button", { className: "Checkbox-box", role: "checkbox", "aria-checked": checked, children: checkIcon }),
22276
+ /* @__PURE__ */ jsx27(
22277
+ "button",
22278
+ {
22279
+ type: "button",
22280
+ className: "Checkbox-box",
22281
+ role: "checkbox",
22282
+ "aria-checked": checked,
22283
+ children: checkIcon
22284
+ }
22285
+ ),
22188
22286
  /* @__PURE__ */ jsx27("div", { className: "Checkbox-label", children })
22189
22287
  ]
22190
22288
  }
@@ -24272,7 +24370,8 @@ var actionBindText = register({
24272
24370
  mutateElement(textElement, {
24273
24371
  containerId: container.id,
24274
24372
  verticalAlign: VERTICAL_ALIGN.MIDDLE,
24275
- textAlign: TEXT_ALIGN.CENTER
24373
+ textAlign: TEXT_ALIGN.CENTER,
24374
+ autoResize: true
24276
24375
  });
24277
24376
  mutateElement(container, {
24278
24377
  boundElements: (container.boundElements || []).concat({
@@ -24394,7 +24493,8 @@ var actionWrapTextInContainer = register({
24394
24493
  containerId: container.id,
24395
24494
  verticalAlign: VERTICAL_ALIGN.MIDDLE,
24396
24495
  boundElements: null,
24397
- textAlign: TEXT_ALIGN.CENTER
24496
+ textAlign: TEXT_ALIGN.CENTER,
24497
+ autoResize: true
24398
24498
  },
24399
24499
  false
24400
24500
  );
@@ -27673,6 +27773,7 @@ var Dialog = (props) => {
27673
27773
  onClick: onClose,
27674
27774
  title: t("buttons.close"),
27675
27775
  "aria-label": t("buttons.close"),
27776
+ type: "button",
27676
27777
  children: CloseIcon
27677
27778
  }
27678
27779
  ),
@@ -28524,6 +28625,7 @@ var ExitZenModeAction = ({
28524
28625
  }) => /* @__PURE__ */ jsx62(
28525
28626
  "button",
28526
28627
  {
28628
+ type: "button",
28527
28629
  className: clsx28("disable-zen-mode", {
28528
28630
  "disable-zen-mode--visible": showExitZenModeBtn
28529
28631
  }),
@@ -29001,7 +29103,7 @@ function CommandPaletteInner({
29001
29103
  ...command,
29002
29104
  icon: command.icon || boltIcon,
29003
29105
  order: command.order ?? getCategoryOrder(command.category),
29004
- haystack: `${deburr(command.label)} ${command.keywords?.join(" ") || ""}`
29106
+ haystack: `${deburr(command.label.toLocaleLowerCase())} ${command.keywords?.join(" ") || ""}`
29005
29107
  };
29006
29108
  });
29007
29109
  setAllCommands(allCommands2);
@@ -29172,7 +29274,9 @@ function CommandPaletteInner({
29172
29274
  setCurrentCommand(showLastUsed ? lastUsed : matchingCommands[0] || null);
29173
29275
  return;
29174
29276
  }
29175
- const _query = deburr(commandSearch.replace(/[<>-_| ]/g, ""));
29277
+ const _query = deburr(
29278
+ commandSearch.toLocaleLowerCase().replace(/[<>_| -]/g, "")
29279
+ );
29176
29280
  matchingCommands = fuzzy.filter(_query, matchingCommands, {
29177
29281
  extract: (command) => command.haystack
29178
29282
  }).sort((a, b) => b.score - a.score).map((item) => item.original);
@@ -29556,7 +29660,10 @@ var createUndoAction = (history, store) => ({
29556
29660
  PanelComponent: ({ updateData, data }) => {
29557
29661
  const { isUndoStackEmpty } = useEmitter(
29558
29662
  history.onHistoryChangedEmitter,
29559
- new HistoryChangedEvent()
29663
+ new HistoryChangedEvent(
29664
+ history.isUndoStackEmpty,
29665
+ history.isRedoStackEmpty
29666
+ )
29560
29667
  );
29561
29668
  return /* @__PURE__ */ jsx66(
29562
29669
  ToolButton,
@@ -29566,7 +29673,8 @@ var createUndoAction = (history, store) => ({
29566
29673
  "aria-label": t("buttons.undo"),
29567
29674
  onClick: updateData,
29568
29675
  size: data?.size || "medium",
29569
- disabled: isUndoStackEmpty
29676
+ disabled: isUndoStackEmpty,
29677
+ "data-testid": "button-undo"
29570
29678
  }
29571
29679
  );
29572
29680
  }
@@ -29590,7 +29698,10 @@ var createRedoAction = (history, store) => ({
29590
29698
  PanelComponent: ({ updateData, data }) => {
29591
29699
  const { isRedoStackEmpty } = useEmitter(
29592
29700
  history.onHistoryChangedEmitter,
29593
- new HistoryChangedEvent()
29701
+ new HistoryChangedEvent(
29702
+ history.isUndoStackEmpty,
29703
+ history.isRedoStackEmpty
29704
+ )
29594
29705
  );
29595
29706
  return /* @__PURE__ */ jsx66(
29596
29707
  ToolButton,
@@ -29600,7 +29711,8 @@ var createRedoAction = (history, store) => ({
29600
29711
  "aria-label": t("buttons.redo"),
29601
29712
  onClick: updateData,
29602
29713
  size: data?.size || "medium",
29603
- disabled: isRedoStackEmpty
29714
+ disabled: isRedoStackEmpty,
29715
+ "data-testid": "button-redo"
29604
29716
  }
29605
29717
  );
29606
29718
  }
@@ -29843,6 +29955,7 @@ var ContextMenu = React24.memo(
29843
29955
  children: /* @__PURE__ */ jsxs37(
29844
29956
  "button",
29845
29957
  {
29958
+ type: "button",
29846
29959
  className: clsx30("context-menu-item", {
29847
29960
  dangerous: actionName === "deleteSelectedElements",
29848
29961
  checkmark: item.checked?.(appState)
@@ -30850,6 +30963,7 @@ var MobileMenu = ({
30850
30963
  appState.scrolledOutside && !appState.openMenu && !appState.openSidebar && /* @__PURE__ */ jsx81(
30851
30964
  "button",
30852
30965
  {
30966
+ type: "button",
30853
30967
  className: "scroll-back-to-content",
30854
30968
  onClick: () => {
30855
30969
  setAppState((appState2) => ({
@@ -30915,6 +31029,7 @@ var ChartPreviewBtn = (props) => {
30915
31029
  return /* @__PURE__ */ jsx82(
30916
31030
  "button",
30917
31031
  {
31032
+ type: "button",
30918
31033
  className: "ChartPreview",
30919
31034
  onClick: () => {
30920
31035
  if (chartElements) {
@@ -34303,7 +34418,7 @@ var LayerUI = ({
34303
34418
  );
34304
34419
  ShapeCache.delete(element);
34305
34420
  }
34306
- Scene_default.getScene(selectedElements[0])?.informMutation();
34421
+ Scene_default.getScene(selectedElements[0])?.triggerUpdate();
34307
34422
  } else if (colorPickerType === "elementBackground") {
34308
34423
  setAppState({
34309
34424
  currentItemBackgroundColor: color
@@ -34413,6 +34528,7 @@ var LayerUI = ({
34413
34528
  appState.scrolledOutside && /* @__PURE__ */ jsx117(
34414
34529
  "button",
34415
34530
  {
34531
+ type: "button",
34416
34532
  className: "scroll-back-to-content",
34417
34533
  onClick: () => {
34418
34534
  setAppState((appState2) => ({
@@ -34558,19 +34674,12 @@ init_transformHandles();
34558
34674
  init_define_import_meta_env();
34559
34675
  init_element();
34560
34676
  init_mutateElement();
34561
- init_textElement();
34562
- init_typeChecks();
34563
34677
  init_utils();
34564
34678
  init_ShapeCache();
34565
34679
  var Fonts = class _Fonts {
34566
34680
  scene;
34567
- onSceneUpdated;
34568
- constructor({
34569
- scene,
34570
- onSceneUpdated
34571
- }) {
34681
+ constructor({ scene }) {
34572
34682
  this.scene = scene;
34573
- this.onSceneUpdated = onSceneUpdated;
34574
34683
  }
34575
34684
  // it's ok to track fonts across multiple instances only once, so let's use
34576
34685
  // a static member to reduce memory footprint
@@ -34601,21 +34710,15 @@ var Fonts = class _Fonts {
34601
34710
  }
34602
34711
  let didUpdate = false;
34603
34712
  this.scene.mapElements((element) => {
34604
- if (isTextElement(element) && !isBoundToContainer(element)) {
34605
- ShapeCache.delete(element);
34713
+ if (isTextElement(element)) {
34606
34714
  didUpdate = true;
34607
- return newElementWith(element, {
34608
- ...refreshTextDimensions(
34609
- element,
34610
- getContainerElement(element, this.scene.getNonDeletedElementsMap()),
34611
- this.scene.getNonDeletedElementsMap()
34612
- )
34613
- });
34715
+ ShapeCache.delete(element);
34716
+ return newElementWith(element, {}, true);
34614
34717
  }
34615
34718
  return element;
34616
34719
  });
34617
34720
  if (didUpdate) {
34618
- this.onSceneUpdated();
34721
+ this.scene.triggerUpdate();
34619
34722
  }
34620
34723
  };
34621
34724
  loadFontsForElements = async (elements) => {
@@ -37175,8 +37278,8 @@ var getRelevantAppStateProps = (appState) => ({
37175
37278
  zenModeEnabled: appState.zenModeEnabled
37176
37279
  });
37177
37280
  var areEqual2 = (prevProps, nextProps) => {
37178
- if (prevProps.selectionNonce !== nextProps.selectionNonce || prevProps.versionNonce !== nextProps.versionNonce || prevProps.scale !== nextProps.scale || // we need to memoize on elementsMap because they may have renewed
37179
- // even if versionNonce didn't change (e.g. we filter elements out based
37281
+ if (prevProps.selectionNonce !== nextProps.selectionNonce || prevProps.sceneNonce !== nextProps.sceneNonce || prevProps.scale !== nextProps.scale || // we need to memoize on elementsMap because they may have renewed
37282
+ // even if sceneNonce didn't change (e.g. we filter elements out based
37180
37283
  // on appState)
37181
37284
  prevProps.elementsMap !== nextProps.elementsMap || prevProps.visibleElements !== nextProps.visibleElements || prevProps.selectedElements !== nextProps.selectedElements) {
37182
37285
  return false;
@@ -37264,8 +37367,8 @@ var getRelevantAppStateProps2 = (appState) => ({
37264
37367
  editingGroupId: appState.editingGroupId
37265
37368
  });
37266
37369
  var areEqual3 = (prevProps, nextProps) => {
37267
- if (prevProps.versionNonce !== nextProps.versionNonce || prevProps.scale !== nextProps.scale || // we need to memoize on elementsMap because they may have renewed
37268
- // even if versionNonce didn't change (e.g. we filter elements out based
37370
+ if (prevProps.sceneNonce !== nextProps.sceneNonce || prevProps.scale !== nextProps.scale || // we need to memoize on elementsMap because they may have renewed
37371
+ // even if sceneNonce didn't change (e.g. we filter elements out based
37269
37372
  // on appState)
37270
37373
  prevProps.elementsMap !== nextProps.elementsMap || prevProps.visibleElements !== nextProps.visibleElements) {
37271
37374
  return false;
@@ -37353,9 +37456,8 @@ var Renderer = class {
37353
37456
  width,
37354
37457
  editingElement,
37355
37458
  pendingImageElementId,
37356
- // unused but serves we cache on it to invalidate elements if they
37357
- // get mutated
37358
- versionNonce: _versionNonce
37459
+ // cache-invalidation nonce
37460
+ sceneNonce: _sceneNonce
37359
37461
  }) => {
37360
37462
  const elements = this.scene.getNonDeletedElements();
37361
37463
  const elementsMap = getRenderableElements({
@@ -37597,7 +37699,15 @@ var FollowMode = ({
37597
37699
  }
37598
37700
  )
37599
37701
  ] }),
37600
- /* @__PURE__ */ jsx125("button", { onClick: onDisconnect, className: "follow-mode__disconnect-btn", children: CloseIcon })
37702
+ /* @__PURE__ */ jsx125(
37703
+ "button",
37704
+ {
37705
+ type: "button",
37706
+ onClick: onDisconnect,
37707
+ className: "follow-mode__disconnect-btn",
37708
+ children: CloseIcon
37709
+ }
37710
+ )
37601
37711
  ] }) });
37602
37712
  };
37603
37713
  var FollowMode_default = FollowMode;
@@ -37997,6 +38107,8 @@ var textWysiwyg = ({
37997
38107
  if (!container) {
37998
38108
  maxWidth = (appState.width - 8 - viewportX) / appState.zoom.value;
37999
38109
  textElementWidth = Math.min(textElementWidth, maxWidth);
38110
+ } else {
38111
+ textElementWidth += 0.5;
38000
38112
  }
38001
38113
  const editorMaxHeight = (appState.height - viewportY) / appState.zoom.value;
38002
38114
  Object.assign(editable.style, {
@@ -38037,7 +38149,7 @@ var textWysiwyg = ({
38037
38149
  editable.classList.add("excalidraw-wysiwyg");
38038
38150
  let whiteSpace = "pre";
38039
38151
  let wordBreak = "normal";
38040
- if (isBoundToContainer(element)) {
38152
+ if (isBoundToContainer(element) || !element.autoResize) {
38041
38153
  whiteSpace = "pre-wrap";
38042
38154
  wordBreak = "break-word";
38043
38155
  }
@@ -38212,13 +38324,11 @@ var textWysiwyg = ({
38212
38324
  if (!updateElement) {
38213
38325
  return;
38214
38326
  }
38215
- let text = editable.value;
38216
38327
  const container = getContainerElement(
38217
38328
  updateElement,
38218
38329
  app.scene.getNonDeletedElementsMap()
38219
38330
  );
38220
38331
  if (container) {
38221
- text = updateElement.text;
38222
38332
  if (editable.value.trim()) {
38223
38333
  const boundTextElementId = getBoundTextElementId(container);
38224
38334
  if (!boundTextElementId || boundTextElementId !== element.id) {
@@ -38247,9 +38357,8 @@ var textWysiwyg = ({
38247
38357
  );
38248
38358
  }
38249
38359
  onSubmit({
38250
- text,
38251
38360
  viaKeyboard: submittedViaKeyboard,
38252
- originalText: editable.value
38361
+ nextOriginalText: editable.value
38253
38362
  });
38254
38363
  };
38255
38364
  const cleanup = () => {
@@ -38308,7 +38417,7 @@ var textWysiwyg = ({
38308
38417
  window.addEventListener("blur", handleSubmit);
38309
38418
  }
38310
38419
  };
38311
- const unbindUpdate = Scene_default.getScene(element).addCallback(() => {
38420
+ const unbindUpdate = Scene_default.getScene(element).onUpdate(() => {
38312
38421
  updateWysiwygStyle();
38313
38422
  const isColorPickerActive = !!document.activeElement?.closest(
38314
38423
  ".color-picker-content"
@@ -38341,6 +38450,51 @@ var textWysiwyg = ({
38341
38450
  // components/App.tsx
38342
38451
  init_fractionalIndex();
38343
38452
  init_helpers();
38453
+
38454
+ // actions/actionTextAutoResize.ts
38455
+ init_define_import_meta_env();
38456
+ init_element();
38457
+ init_mutateElement();
38458
+ init_textElement();
38459
+ init_scene();
38460
+ init_store();
38461
+ init_utils();
38462
+ init_register();
38463
+ var actionTextAutoResize = register({
38464
+ name: "autoResize",
38465
+ label: "labels.autoResize",
38466
+ icon: null,
38467
+ trackEvent: { category: "element" },
38468
+ predicate: (elements, appState, _, app) => {
38469
+ const selectedElements = getSelectedElements(elements, appState);
38470
+ return selectedElements.length === 1 && isTextElement(selectedElements[0]) && !selectedElements[0].autoResize;
38471
+ },
38472
+ perform: (elements, appState, _, app) => {
38473
+ const selectedElements = getSelectedElements(elements, appState);
38474
+ return {
38475
+ appState,
38476
+ elements: elements.map((element) => {
38477
+ if (element.id === selectedElements[0].id && isTextElement(element)) {
38478
+ const metrics = measureText(
38479
+ element.originalText,
38480
+ getFontString(element),
38481
+ element.lineHeight
38482
+ );
38483
+ return newElementWith(element, {
38484
+ autoResize: true,
38485
+ width: metrics.width,
38486
+ height: metrics.height,
38487
+ text: element.originalText
38488
+ });
38489
+ }
38490
+ return element;
38491
+ }),
38492
+ storeAction: StoreAction.CAPTURE
38493
+ };
38494
+ }
38495
+ });
38496
+
38497
+ // components/App.tsx
38344
38498
  import { Fragment as Fragment21, jsx as jsx126, jsxs as jsxs71 } from "react/jsx-runtime";
38345
38499
  var AppContext = React38.createContext(null);
38346
38500
  var AppPropsContext = React38.createContext(null);
@@ -38545,10 +38699,7 @@ var App = class _App extends React38.Component {
38545
38699
  container: this.excalidrawContainerRef.current,
38546
38700
  id: this.id
38547
38701
  };
38548
- this.fonts = new Fonts({
38549
- scene: this.scene,
38550
- onSceneUpdated: this.onSceneUpdated
38551
- });
38702
+ this.fonts = new Fonts({ scene: this.scene });
38552
38703
  this.history = new History();
38553
38704
  this.actionManager.registerAll(actions);
38554
38705
  this.actionManager.registerAction(
@@ -38707,7 +38858,7 @@ var App = class _App extends React38.Component {
38707
38858
  return false;
38708
38859
  });
38709
38860
  if (updated) {
38710
- this.scene.informMutation();
38861
+ this.scene.triggerUpdate();
38711
38862
  }
38712
38863
  this.iFrameRefs.forEach((ref, id) => {
38713
38864
  if (!iframeLikes.has(id)) {
@@ -39114,9 +39265,9 @@ var App = class _App extends React38.Component {
39114
39265
  render() {
39115
39266
  const selectedElements = this.scene.getSelectedElements(this.state);
39116
39267
  const { renderTopRightUI, renderCustomStats } = this.props;
39117
- const versionNonce = this.scene.getVersionNonce();
39268
+ const sceneNonce = this.scene.getSceneNonce();
39118
39269
  const { elementsMap, visibleElements } = this.renderer.getRenderableElements({
39119
- versionNonce,
39270
+ sceneNonce,
39120
39271
  zoom: this.state.zoom,
39121
39272
  offsetLeft: this.state.offsetLeft,
39122
39273
  offsetTop: this.state.offsetTop,
@@ -39313,7 +39464,7 @@ var App = class _App extends React38.Component {
39313
39464
  elementsMap,
39314
39465
  allElementsMap,
39315
39466
  visibleElements,
39316
- versionNonce,
39467
+ sceneNonce,
39317
39468
  selectionNonce: this.state.selectionElement?.versionNonce,
39318
39469
  scale: window.devicePixelRatio,
39319
39470
  appState: this.state,
@@ -39335,7 +39486,7 @@ var App = class _App extends React38.Component {
39335
39486
  elementsMap,
39336
39487
  visibleElements,
39337
39488
  selectedElements,
39338
- versionNonce,
39489
+ sceneNonce,
39339
39490
  selectionNonce: this.state.selectionElement?.versionNonce,
39340
39491
  scale: window.devicePixelRatio,
39341
39492
  appState: this.state,
@@ -39429,7 +39580,7 @@ var App = class _App extends React38.Component {
39429
39580
  );
39430
39581
  }
39431
39582
  this.magicGenerations.set(frameElement.id, data);
39432
- this.onSceneUpdated();
39583
+ this.triggerRender();
39433
39584
  };
39434
39585
  getTextFromElements(elements) {
39435
39586
  const text = elements.reduce((acc, element) => {
@@ -39913,7 +40064,7 @@ var App = class _App extends React38.Component {
39913
40064
  this.store.onStoreIncrementEmitter.on((increment) => {
39914
40065
  this.history.record(increment.elementsChange, increment.appStateChange);
39915
40066
  });
39916
- this.scene.addCallback(this.onSceneUpdated);
40067
+ this.scene.onUpdate(this.triggerRender);
39917
40068
  this.addEventListeners();
39918
40069
  if (this.props.autoFocus && this.excalidrawContainerRef.current) {
39919
40070
  this.focusContainer();
@@ -39949,6 +40100,7 @@ var App = class _App extends React38.Component {
39949
40100
  componentWillUnmount() {
39950
40101
  this.renderer.destroy();
39951
40102
  this.scene = new Scene_default();
40103
+ this.fonts = new Fonts({ scene: this.scene });
39952
40104
  this.renderer = new Renderer(this.scene);
39953
40105
  this.files = {};
39954
40106
  this.imageCache.clear();
@@ -40042,6 +40194,7 @@ var App = class _App extends React38.Component {
40042
40194
  ),
40043
40195
  addEventListener(window, "focus" /* FOCUS */, () => {
40044
40196
  this.maybeCleanupAfterMissingPointerUp(null);
40197
+ this.triggerRender(true);
40045
40198
  })
40046
40199
  );
40047
40200
  if (this.state.viewModeEnabled) {
@@ -40826,7 +40979,7 @@ var App = class _App extends React38.Component {
40826
40979
  ShapeCache.delete(element);
40827
40980
  }
40828
40981
  });
40829
- this.scene.informMutation();
40982
+ this.scene.triggerUpdate();
40830
40983
  this.addNewImagesToImageCache();
40831
40984
  }
40832
40985
  );
@@ -40866,8 +41019,12 @@ var App = class _App extends React38.Component {
40866
41019
  }
40867
41020
  }
40868
41021
  );
40869
- onSceneUpdated = () => {
40870
- this.setState({});
41022
+ triggerRender = (force) => {
41023
+ if (force === true) {
41024
+ this.scene.triggerUpdate();
41025
+ } else {
41026
+ this.setState({});
41027
+ }
40871
41028
  };
40872
41029
  /**
40873
41030
  * @returns whether the menu was toggled on or off
@@ -41270,21 +41427,22 @@ var App = class _App extends React38.Component {
41270
41427
  isExistingElement = false
41271
41428
  }) {
41272
41429
  const elementsMap = this.scene.getElementsMapIncludingDeleted();
41273
- const updateElement = (text, originalText, isDeleted) => {
41430
+ const updateElement = (nextOriginalText, isDeleted) => {
41274
41431
  this.scene.replaceAllElements([
41275
41432
  // Not sure why we include deleted elements as well hence using deleted elements map
41276
41433
  ...this.scene.getElementsIncludingDeleted().map((_element) => {
41277
41434
  if (_element.id === element.id && isTextElement(_element)) {
41278
- return updateTextElement(
41279
- _element,
41280
- getContainerElement(_element, elementsMap),
41281
- elementsMap,
41282
- {
41283
- text,
41284
- isDeleted,
41285
- originalText
41286
- }
41287
- );
41435
+ return newElementWith(_element, {
41436
+ originalText: nextOriginalText,
41437
+ isDeleted: isDeleted ?? _element.isDeleted,
41438
+ // returns (wrapped) text and new dimensions
41439
+ ...refreshTextDimensions(
41440
+ _element,
41441
+ getContainerElement(_element, elementsMap),
41442
+ elementsMap,
41443
+ nextOriginalText
41444
+ )
41445
+ });
41288
41446
  }
41289
41447
  return _element;
41290
41448
  })
@@ -41306,15 +41464,15 @@ var App = class _App extends React38.Component {
41306
41464
  viewportY - this.state.offsetTop
41307
41465
  ];
41308
41466
  },
41309
- onChange: withBatchedUpdates((text) => {
41310
- updateElement(text, text, false);
41467
+ onChange: withBatchedUpdates((nextOriginalText) => {
41468
+ updateElement(nextOriginalText, false);
41311
41469
  if (isNonDeletedElement(element)) {
41312
41470
  updateBoundElements(element, elementsMap);
41313
41471
  }
41314
41472
  }),
41315
- onSubmit: withBatchedUpdates(({ text, viaKeyboard, originalText }) => {
41316
- const isDeleted = !text.trim();
41317
- updateElement(text, originalText, isDeleted);
41473
+ onSubmit: withBatchedUpdates(({ viaKeyboard, nextOriginalText }) => {
41474
+ const isDeleted = !nextOriginalText.trim();
41475
+ updateElement(nextOriginalText, isDeleted);
41318
41476
  if (!isDeleted && viaKeyboard) {
41319
41477
  const elementIdToSelect = element.containerId ? element.containerId : element.id;
41320
41478
  this.setState((prevState) => ({
@@ -41349,7 +41507,7 @@ var App = class _App extends React38.Component {
41349
41507
  app: this
41350
41508
  });
41351
41509
  this.deselectElements();
41352
- updateElement(element.text, element.originalText, false);
41510
+ updateElement(element.originalText, false);
41353
41511
  }
41354
41512
  deselectElements() {
41355
41513
  this.setState({
@@ -41848,8 +42006,11 @@ var App = class _App extends React38.Component {
41848
42006
  );
41849
42007
  this.translateCanvas({
41850
42008
  zoom: zoomState.zoom,
41851
- scrollX: zoomState.scrollX + deltaX / nextZoom,
41852
- scrollY: zoomState.scrollY + deltaY / nextZoom,
42009
+ // 2x multiplier is just a magic number that makes this work correctly
42010
+ // on touchscreen devices (note: if we get report that panning is slower/faster
42011
+ // than actual movement, consider swapping with devicePixelRatio)
42012
+ scrollX: zoomState.scrollX + 2 * (deltaX / nextZoom),
42013
+ scrollY: zoomState.scrollY + 2 * (deltaY / nextZoom),
41853
42014
  shouldCacheIgnoreZoom: true
41854
42015
  });
41855
42016
  });
@@ -42198,7 +42359,7 @@ var App = class _App extends React38.Component {
42198
42359
  }
42199
42360
  }
42200
42361
  this.elementsPendingErasure = new Set(this.elementsPendingErasure);
42201
- this.onSceneUpdated();
42362
+ this.triggerRender();
42202
42363
  }
42203
42364
  };
42204
42365
  // set touch moving for mobile context menu
@@ -44002,7 +44163,7 @@ var App = class _App extends React38.Component {
44002
44163
  [linearElement],
44003
44164
  this.scene.getNonDeletedElementsMap()
44004
44165
  );
44005
- this.scene.informMutation();
44166
+ this.scene.triggerUpdate();
44006
44167
  }
44007
44168
  }
44008
44169
  }
@@ -44355,7 +44516,7 @@ var App = class _App extends React38.Component {
44355
44516
  }
44356
44517
  restoreReadyToEraseElements = () => {
44357
44518
  this.elementsPendingErasure = /* @__PURE__ */ new Set();
44358
- this.onSceneUpdated();
44519
+ this.triggerRender();
44359
44520
  };
44360
44521
  eraseElements = () => {
44361
44522
  let didChange = false;
@@ -44670,7 +44831,7 @@ var App = class _App extends React38.Component {
44670
44831
  files
44671
44832
  );
44672
44833
  if (updatedFiles.size) {
44673
- this.scene.informMutation();
44834
+ this.scene.triggerUpdate();
44674
44835
  }
44675
44836
  }
44676
44837
  };
@@ -45143,6 +45304,7 @@ var App = class _App extends React38.Component {
45143
45304
  return [actionCopy, ...options];
45144
45305
  }
45145
45306
  return [
45307
+ CONTEXT_MENU_SEPARATOR,
45146
45308
  actionCut,
45147
45309
  actionCopy,
45148
45310
  actionPaste,
@@ -45155,6 +45317,7 @@ var App = class _App extends React38.Component {
45155
45317
  actionPasteStyles,
45156
45318
  CONTEXT_MENU_SEPARATOR,
45157
45319
  actionGroup,
45320
+ actionTextAutoResize,
45158
45321
  actionUnbindText,
45159
45322
  actionBindText,
45160
45323
  actionWrapTextInContainer,