@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
@@ -404,18 +404,17 @@ import {
404
404
  updateObject,
405
405
  updateOriginalContainerCache,
406
406
  updateStable,
407
- updateTextElement,
408
407
  viewportCoordsToSceneCoords,
409
408
  wrapEvent,
410
409
  wrapText
411
- } from "./excalidraw-assets-dev/chunk-OKAZAA6U.js";
410
+ } from "./excalidraw-assets-dev/chunk-Q6NFAEKN.js";
412
411
  import {
413
412
  define_import_meta_env_default,
414
413
  init_define_import_meta_env
415
414
  } from "./excalidraw-assets-dev/chunk-YRUDZAGT.js";
416
415
  import {
417
416
  en_default
418
- } from "./excalidraw-assets-dev/chunk-JKPJV7MZ.js";
417
+ } from "./excalidraw-assets-dev/chunk-Q6A4M3MN.js";
419
418
  import {
420
419
  percentages_default
421
420
  } from "./excalidraw-assets-dev/chunk-YZTYRBEQ.js";
@@ -2679,7 +2678,7 @@ var globImport_locales_json = __glob({
2679
2678
  "./locales/da-DK.json": () => import("./excalidraw-assets-dev/da-DK-6OKJ2GQ6.js"),
2680
2679
  "./locales/de-DE.json": () => import("./excalidraw-assets-dev/de-DE-BD5ICOQ2.js"),
2681
2680
  "./locales/el-GR.json": () => import("./excalidraw-assets-dev/el-GR-JFDBTDHS.js"),
2682
- "./locales/en.json": () => import("./excalidraw-assets-dev/en-BF4XUPIZ.js"),
2681
+ "./locales/en.json": () => import("./excalidraw-assets-dev/en-Y27YPU72.js"),
2683
2682
  "./locales/es-ES.json": () => import("./excalidraw-assets-dev/es-ES-CBBTGYGR.js"),
2684
2683
  "./locales/eu-ES.json": () => import("./excalidraw-assets-dev/eu-ES-CHAPMSLE.js"),
2685
2684
  "./locales/fa-IR.json": () => import("./excalidraw-assets-dev/fa-IR-LOJOKEJO.js"),
@@ -3781,12 +3780,16 @@ var arrownNarrowUpJSX = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { str
3781
3780
  var BringForwardIcon = createIcon(arrownNarrowUpJSX, tablerIconProps);
3782
3781
  var SendBackwardIcon = createIcon(arrownNarrowUpJSX, {
3783
3782
  ...tablerIconProps,
3784
- transform: "rotate(180)"
3783
+ style: {
3784
+ transform: "rotate(180deg)"
3785
+ }
3785
3786
  });
3786
3787
  var BringToFrontIcon = createIcon(arrowBarToTopJSX, tablerIconProps);
3787
3788
  var SendToBackIcon = createIcon(arrowBarToTopJSX, {
3788
3789
  ...tablerIconProps,
3789
- transform: "rotate(180)"
3790
+ style: {
3791
+ transform: "rotate(180deg)"
3792
+ }
3790
3793
  });
3791
3794
  var AlignTopIcon = createIcon(
3792
3795
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
@@ -6066,6 +6069,7 @@ var ButtonIconSelect = (props) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("
6066
6069
  (option) => props.type === "button" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6067
6070
  "button",
6068
6071
  {
6072
+ type: "button",
6069
6073
  onClick: (event) => props.onClick(option.value, event),
6070
6074
  className: clsx_m_default({
6071
6075
  active: option.active ?? props.value === option.value
@@ -10593,6 +10597,7 @@ function Picker2({
10593
10597
  children: /* @__PURE__ */ (0, import_jsx_runtime21.jsx)("div", { className: "picker-content", ref: rGallery, children: options.map((option, i3) => /* @__PURE__ */ (0, import_jsx_runtime21.jsxs)(
10594
10598
  "button",
10595
10599
  {
10600
+ type: "button",
10596
10601
  className: clsx_m_default("picker-option", {
10597
10602
  active: value === option.value
10598
10603
  }),
@@ -10639,6 +10644,7 @@ function IconPicker({
10639
10644
  "button",
10640
10645
  {
10641
10646
  name: group,
10647
+ type: "button",
10642
10648
  className: isActive ? "active" : "",
10643
10649
  "aria-label": label,
10644
10650
  onClick: () => setActive(!isActive),
@@ -10710,7 +10716,7 @@ var getFormValue = function(elements, appState, getAttribute, isRelevantElement,
10710
10716
  return ret;
10711
10717
  };
10712
10718
  var offsetElementAfterFontResize = (prevElement, nextElement) => {
10713
- if (isBoundToContainer(nextElement)) {
10719
+ if (isBoundToContainer(nextElement) || !nextElement.autoResize) {
10714
10720
  return nextElement;
10715
10721
  }
10716
10722
  return mutateElement(
@@ -12497,7 +12503,7 @@ var exportCanvas = async (type, elements, appState, files, {
12497
12503
  let blob = canvasToBlob(tempCanvas);
12498
12504
  if (appState.exportEmbedScene) {
12499
12505
  blob = blob.then(
12500
- (blob2) => import("./excalidraw-assets-dev/image-LVS32KQQ.js").then(
12506
+ (blob2) => import("./excalidraw-assets-dev/image-Y5X7K6KW.js").then(
12501
12507
  ({ encodePngMetadata }) => encodePngMetadata({
12502
12508
  blob: blob2,
12503
12509
  metadata: serializeAsJSON(elements, appState, files, "local")
@@ -12586,7 +12592,16 @@ var CheckboxItem = ({ children, checked, onChange, className }) => {
12586
12592
  ).focus();
12587
12593
  },
12588
12594
  children: [
12589
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("button", { className: "Checkbox-box", role: "checkbox", "aria-checked": checked, children: checkIcon }),
12595
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
12596
+ "button",
12597
+ {
12598
+ type: "button",
12599
+ className: "Checkbox-box",
12600
+ role: "checkbox",
12601
+ "aria-checked": checked,
12602
+ children: checkIcon
12603
+ }
12604
+ ),
12590
12605
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "Checkbox-label", children })
12591
12606
  ]
12592
12607
  }
@@ -14570,7 +14585,8 @@ var actionBindText = register({
14570
14585
  mutateElement(textElement, {
14571
14586
  containerId: container.id,
14572
14587
  verticalAlign: VERTICAL_ALIGN.MIDDLE,
14573
- textAlign: TEXT_ALIGN.CENTER
14588
+ textAlign: TEXT_ALIGN.CENTER,
14589
+ autoResize: true
14574
14590
  });
14575
14591
  mutateElement(container, {
14576
14592
  boundElements: (container.boundElements || []).concat({
@@ -14692,7 +14708,8 @@ var actionWrapTextInContainer = register({
14692
14708
  containerId: container.id,
14693
14709
  verticalAlign: VERTICAL_ALIGN.MIDDLE,
14694
14710
  boundElements: null,
14695
- textAlign: TEXT_ALIGN.CENTER
14711
+ textAlign: TEXT_ALIGN.CENTER,
14712
+ autoResize: true
14696
14713
  },
14697
14714
  false
14698
14715
  );
@@ -17869,6 +17886,7 @@ var Dialog = (props) => {
17869
17886
  onClick: onClose,
17870
17887
  title: t("buttons.close"),
17871
17888
  "aria-label": t("buttons.close"),
17889
+ type: "button",
17872
17890
  children: CloseIcon
17873
17891
  }
17874
17892
  ),
@@ -18814,6 +18832,7 @@ var ExitZenModeAction = ({
18814
18832
  }) => /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
18815
18833
  "button",
18816
18834
  {
18835
+ type: "button",
18817
18836
  className: clsx_m_default("disable-zen-mode", {
18818
18837
  "disable-zen-mode--visible": showExitZenModeBtn
18819
18838
  }),
@@ -19290,7 +19309,7 @@ function CommandPaletteInner({
19290
19309
  ...command,
19291
19310
  icon: command.icon || boltIcon,
19292
19311
  order: command.order ?? getCategoryOrder(command.category),
19293
- haystack: `${deburr(command.label)} ${command.keywords?.join(" ") || ""}`
19312
+ haystack: `${deburr(command.label.toLocaleLowerCase())} ${command.keywords?.join(" ") || ""}`
19294
19313
  };
19295
19314
  });
19296
19315
  setAllCommands(allCommands2);
@@ -19461,7 +19480,9 @@ function CommandPaletteInner({
19461
19480
  setCurrentCommand(showLastUsed ? lastUsed : matchingCommands[0] || null);
19462
19481
  return;
19463
19482
  }
19464
- const _query = deburr(commandSearch.replace(/[<>-_| ]/g, ""));
19483
+ const _query = deburr(
19484
+ commandSearch.toLocaleLowerCase().replace(/[<>_| -]/g, "")
19485
+ );
19465
19486
  matchingCommands = import_fuzzy.default.filter(_query, matchingCommands, {
19466
19487
  extract: (command) => command.haystack
19467
19488
  }).sort((a3, b3) => b3.score - a3.score).map((item) => item.original);
@@ -19834,7 +19855,10 @@ var createUndoAction = (history, store) => ({
19834
19855
  PanelComponent: ({ updateData, data }) => {
19835
19856
  const { isUndoStackEmpty } = useEmitter(
19836
19857
  history.onHistoryChangedEmitter,
19837
- new HistoryChangedEvent()
19858
+ new HistoryChangedEvent(
19859
+ history.isUndoStackEmpty,
19860
+ history.isRedoStackEmpty
19861
+ )
19838
19862
  );
19839
19863
  return /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(
19840
19864
  ToolButton,
@@ -19844,7 +19868,8 @@ var createUndoAction = (history, store) => ({
19844
19868
  "aria-label": t("buttons.undo"),
19845
19869
  onClick: updateData,
19846
19870
  size: data?.size || "medium",
19847
- disabled: isUndoStackEmpty
19871
+ disabled: isUndoStackEmpty,
19872
+ "data-testid": "button-undo"
19848
19873
  }
19849
19874
  );
19850
19875
  }
@@ -19868,7 +19893,10 @@ var createRedoAction = (history, store) => ({
19868
19893
  PanelComponent: ({ updateData, data }) => {
19869
19894
  const { isRedoStackEmpty } = useEmitter(
19870
19895
  history.onHistoryChangedEmitter,
19871
- new HistoryChangedEvent()
19896
+ new HistoryChangedEvent(
19897
+ history.isUndoStackEmpty,
19898
+ history.isRedoStackEmpty
19899
+ )
19872
19900
  );
19873
19901
  return /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(
19874
19902
  ToolButton,
@@ -19878,7 +19906,8 @@ var createRedoAction = (history, store) => ({
19878
19906
  "aria-label": t("buttons.redo"),
19879
19907
  onClick: updateData,
19880
19908
  size: data?.size || "medium",
19881
- disabled: isRedoStackEmpty
19909
+ disabled: isRedoStackEmpty,
19910
+ "data-testid": "button-redo"
19882
19911
  }
19883
19912
  );
19884
19913
  }
@@ -20093,6 +20122,7 @@ var ContextMenu = import_react71.default.memo(
20093
20122
  children: /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)(
20094
20123
  "button",
20095
20124
  {
20125
+ type: "button",
20096
20126
  className: clsx_m_default("context-menu-item", {
20097
20127
  dangerous: actionName === "deleteSelectedElements",
20098
20128
  checkmark: item.checked?.(appState)
@@ -21068,6 +21098,7 @@ var MobileMenu = ({
21068
21098
  appState.scrolledOutside && !appState.openMenu && !appState.openSidebar && /* @__PURE__ */ (0, import_jsx_runtime83.jsx)(
21069
21099
  "button",
21070
21100
  {
21101
+ type: "button",
21071
21102
  className: "scroll-back-to-content",
21072
21103
  onClick: () => {
21073
21104
  setAppState((appState2) => ({
@@ -21130,6 +21161,7 @@ var ChartPreviewBtn = (props) => {
21130
21161
  return /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(
21131
21162
  "button",
21132
21163
  {
21164
+ type: "button",
21133
21165
  className: "ChartPreview",
21134
21166
  onClick: () => {
21135
21167
  if (chartElements) {
@@ -24439,7 +24471,7 @@ TTD mermaid definition render errror: ${error3.message}`,
24439
24471
  refOnGenerate.current = onGenerate;
24440
24472
  const [mermaidToExcalidrawLib, setMermaidToExcalidrawLib] = (0, import_react93.useState)({
24441
24473
  loaded: false,
24442
- api: import("./excalidraw-assets-dev/dist-ITJNUBZF.js")
24474
+ api: import("./excalidraw-assets-dev/dist-6QVAH5JA.js")
24443
24475
  });
24444
24476
  (0, import_react93.useEffect)(() => {
24445
24477
  const fn = async () => {
@@ -24927,7 +24959,7 @@ var LayerUI = ({
24927
24959
  );
24928
24960
  ShapeCache.delete(element);
24929
24961
  }
24930
- Scene_default.getScene(selectedElements[0])?.informMutation();
24962
+ Scene_default.getScene(selectedElements[0])?.triggerUpdate();
24931
24963
  } else if (colorPickerType === "elementBackground") {
24932
24964
  setAppState({
24933
24965
  currentItemBackgroundColor: color
@@ -25037,6 +25069,7 @@ var LayerUI = ({
25037
25069
  appState.scrolledOutside && /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(
25038
25070
  "button",
25039
25071
  {
25072
+ type: "button",
25040
25073
  className: "scroll-back-to-content",
25041
25074
  onClick: () => {
25042
25075
  setAppState((appState2) => ({
@@ -25173,13 +25206,8 @@ var import_lodash2 = __toESM(require_lodash(), 1);
25173
25206
  init_define_import_meta_env();
25174
25207
  var Fonts = class _Fonts {
25175
25208
  scene;
25176
- onSceneUpdated;
25177
- constructor({
25178
- scene,
25179
- onSceneUpdated
25180
- }) {
25209
+ constructor({ scene }) {
25181
25210
  this.scene = scene;
25182
- this.onSceneUpdated = onSceneUpdated;
25183
25211
  }
25184
25212
  // it's ok to track fonts across multiple instances only once, so let's use
25185
25213
  // a static member to reduce memory footprint
@@ -25210,21 +25238,15 @@ var Fonts = class _Fonts {
25210
25238
  }
25211
25239
  let didUpdate = false;
25212
25240
  this.scene.mapElements((element) => {
25213
- if (isTextElement(element) && !isBoundToContainer(element)) {
25214
- ShapeCache.delete(element);
25241
+ if (isTextElement(element)) {
25215
25242
  didUpdate = true;
25216
- return newElementWith(element, {
25217
- ...refreshTextDimensions(
25218
- element,
25219
- getContainerElement(element, this.scene.getNonDeletedElementsMap()),
25220
- this.scene.getNonDeletedElementsMap()
25221
- )
25222
- });
25243
+ ShapeCache.delete(element);
25244
+ return newElementWith(element, {}, true);
25223
25245
  }
25224
25246
  return element;
25225
25247
  });
25226
25248
  if (didUpdate) {
25227
- this.onSceneUpdated();
25249
+ this.scene.triggerUpdate();
25228
25250
  }
25229
25251
  };
25230
25252
  loadFontsForElements = async (elements) => {
@@ -27737,8 +27759,8 @@ var getRelevantAppStateProps = (appState) => ({
27737
27759
  zenModeEnabled: appState.zenModeEnabled
27738
27760
  });
27739
27761
  var areEqual2 = (prevProps, nextProps) => {
27740
- if (prevProps.selectionNonce !== nextProps.selectionNonce || prevProps.versionNonce !== nextProps.versionNonce || prevProps.scale !== nextProps.scale || // we need to memoize on elementsMap because they may have renewed
27741
- // even if versionNonce didn't change (e.g. we filter elements out based
27762
+ if (prevProps.selectionNonce !== nextProps.selectionNonce || prevProps.sceneNonce !== nextProps.sceneNonce || prevProps.scale !== nextProps.scale || // we need to memoize on elementsMap because they may have renewed
27763
+ // even if sceneNonce didn't change (e.g. we filter elements out based
27742
27764
  // on appState)
27743
27765
  prevProps.elementsMap !== nextProps.elementsMap || prevProps.visibleElements !== nextProps.visibleElements || prevProps.selectedElements !== nextProps.selectedElements) {
27744
27766
  return false;
@@ -27824,8 +27846,8 @@ var getRelevantAppStateProps2 = (appState) => ({
27824
27846
  editingGroupId: appState.editingGroupId
27825
27847
  });
27826
27848
  var areEqual3 = (prevProps, nextProps) => {
27827
- if (prevProps.versionNonce !== nextProps.versionNonce || prevProps.scale !== nextProps.scale || // we need to memoize on elementsMap because they may have renewed
27828
- // even if versionNonce didn't change (e.g. we filter elements out based
27849
+ if (prevProps.sceneNonce !== nextProps.sceneNonce || prevProps.scale !== nextProps.scale || // we need to memoize on elementsMap because they may have renewed
27850
+ // even if sceneNonce didn't change (e.g. we filter elements out based
27829
27851
  // on appState)
27830
27852
  prevProps.elementsMap !== nextProps.elementsMap || prevProps.visibleElements !== nextProps.visibleElements) {
27831
27853
  return false;
@@ -27909,9 +27931,8 @@ var Renderer = class {
27909
27931
  width,
27910
27932
  editingElement,
27911
27933
  pendingImageElementId,
27912
- // unused but serves we cache on it to invalidate elements if they
27913
- // get mutated
27914
- versionNonce: _versionNonce
27934
+ // cache-invalidation nonce
27935
+ sceneNonce: _sceneNonce
27915
27936
  }) => {
27916
27937
  const elements = this.scene.getNonDeletedElements();
27917
27938
  const elementsMap = getRenderableElements({
@@ -28138,7 +28159,15 @@ var FollowMode = ({
28138
28159
  }
28139
28160
  )
28140
28161
  ] }),
28141
- /* @__PURE__ */ (0, import_jsx_runtime127.jsx)("button", { onClick: onDisconnect, className: "follow-mode__disconnect-btn", children: CloseIcon })
28162
+ /* @__PURE__ */ (0, import_jsx_runtime127.jsx)(
28163
+ "button",
28164
+ {
28165
+ type: "button",
28166
+ onClick: onDisconnect,
28167
+ className: "follow-mode__disconnect-btn",
28168
+ children: CloseIcon
28169
+ }
28170
+ )
28142
28171
  ] }) });
28143
28172
  };
28144
28173
  var FollowMode_default = FollowMode;
@@ -28871,6 +28900,8 @@ var textWysiwyg = ({
28871
28900
  if (!container) {
28872
28901
  maxWidth = (appState.width - 8 - viewportX) / appState.zoom.value;
28873
28902
  textElementWidth = Math.min(textElementWidth, maxWidth);
28903
+ } else {
28904
+ textElementWidth += 0.5;
28874
28905
  }
28875
28906
  const editorMaxHeight = (appState.height - viewportY) / appState.zoom.value;
28876
28907
  Object.assign(editable.style, {
@@ -28911,7 +28942,7 @@ var textWysiwyg = ({
28911
28942
  editable.classList.add("excalidraw-wysiwyg");
28912
28943
  let whiteSpace = "pre";
28913
28944
  let wordBreak = "normal";
28914
- if (isBoundToContainer(element)) {
28945
+ if (isBoundToContainer(element) || !element.autoResize) {
28915
28946
  whiteSpace = "pre-wrap";
28916
28947
  wordBreak = "break-word";
28917
28948
  }
@@ -29086,13 +29117,11 @@ var textWysiwyg = ({
29086
29117
  if (!updateElement) {
29087
29118
  return;
29088
29119
  }
29089
- let text = editable.value;
29090
29120
  const container = getContainerElement(
29091
29121
  updateElement,
29092
29122
  app.scene.getNonDeletedElementsMap()
29093
29123
  );
29094
29124
  if (container) {
29095
- text = updateElement.text;
29096
29125
  if (editable.value.trim()) {
29097
29126
  const boundTextElementId = getBoundTextElementId(container);
29098
29127
  if (!boundTextElementId || boundTextElementId !== element.id) {
@@ -29121,9 +29150,8 @@ var textWysiwyg = ({
29121
29150
  );
29122
29151
  }
29123
29152
  onSubmit({
29124
- text,
29125
29153
  viaKeyboard: submittedViaKeyboard,
29126
- originalText: editable.value
29154
+ nextOriginalText: editable.value
29127
29155
  });
29128
29156
  };
29129
29157
  const cleanup = () => {
@@ -29182,7 +29210,7 @@ var textWysiwyg = ({
29182
29210
  window.addEventListener("blur", handleSubmit);
29183
29211
  }
29184
29212
  };
29185
- const unbindUpdate = Scene_default.getScene(element).addCallback(() => {
29213
+ const unbindUpdate = Scene_default.getScene(element).onUpdate(() => {
29186
29214
  updateWysiwygStyle();
29187
29215
  const isColorPickerActive = !!document.activeElement?.closest(
29188
29216
  ".color-picker-content"
@@ -29212,6 +29240,42 @@ var textWysiwyg = ({
29212
29240
  excalidrawContainer?.querySelector(".excalidraw-textEditorContainer").appendChild(editable);
29213
29241
  };
29214
29242
 
29243
+ // actions/actionTextAutoResize.ts
29244
+ init_define_import_meta_env();
29245
+ var actionTextAutoResize = register({
29246
+ name: "autoResize",
29247
+ label: "labels.autoResize",
29248
+ icon: null,
29249
+ trackEvent: { category: "element" },
29250
+ predicate: (elements, appState, _, app) => {
29251
+ const selectedElements = getSelectedElements(elements, appState);
29252
+ return selectedElements.length === 1 && isTextElement(selectedElements[0]) && !selectedElements[0].autoResize;
29253
+ },
29254
+ perform: (elements, appState, _, app) => {
29255
+ const selectedElements = getSelectedElements(elements, appState);
29256
+ return {
29257
+ appState,
29258
+ elements: elements.map((element) => {
29259
+ if (element.id === selectedElements[0].id && isTextElement(element)) {
29260
+ const metrics = measureText(
29261
+ element.originalText,
29262
+ getFontString(element),
29263
+ element.lineHeight
29264
+ );
29265
+ return newElementWith(element, {
29266
+ autoResize: true,
29267
+ width: metrics.width,
29268
+ height: metrics.height,
29269
+ text: element.originalText
29270
+ });
29271
+ }
29272
+ return element;
29273
+ }),
29274
+ storeAction: StoreAction.CAPTURE
29275
+ };
29276
+ }
29277
+ });
29278
+
29215
29279
  // components/App.tsx
29216
29280
  var import_jsx_runtime128 = __toESM(require_jsx_runtime(), 1);
29217
29281
  var AppContext = import_react100.default.createContext(null);
@@ -29417,10 +29481,7 @@ var App = class _App extends import_react100.default.Component {
29417
29481
  container: this.excalidrawContainerRef.current,
29418
29482
  id: this.id
29419
29483
  };
29420
- this.fonts = new Fonts({
29421
- scene: this.scene,
29422
- onSceneUpdated: this.onSceneUpdated
29423
- });
29484
+ this.fonts = new Fonts({ scene: this.scene });
29424
29485
  this.history = new History();
29425
29486
  this.actionManager.registerAll(actions);
29426
29487
  this.actionManager.registerAction(
@@ -29579,7 +29640,7 @@ var App = class _App extends import_react100.default.Component {
29579
29640
  return false;
29580
29641
  });
29581
29642
  if (updated) {
29582
- this.scene.informMutation();
29643
+ this.scene.triggerUpdate();
29583
29644
  }
29584
29645
  this.iFrameRefs.forEach((ref, id) => {
29585
29646
  if (!iframeLikes.has(id)) {
@@ -29986,9 +30047,9 @@ var App = class _App extends import_react100.default.Component {
29986
30047
  render() {
29987
30048
  const selectedElements = this.scene.getSelectedElements(this.state);
29988
30049
  const { renderTopRightUI, renderCustomStats } = this.props;
29989
- const versionNonce = this.scene.getVersionNonce();
30050
+ const sceneNonce = this.scene.getSceneNonce();
29990
30051
  const { elementsMap, visibleElements } = this.renderer.getRenderableElements({
29991
- versionNonce,
30052
+ sceneNonce,
29992
30053
  zoom: this.state.zoom,
29993
30054
  offsetLeft: this.state.offsetLeft,
29994
30055
  offsetTop: this.state.offsetTop,
@@ -30185,7 +30246,7 @@ var App = class _App extends import_react100.default.Component {
30185
30246
  elementsMap,
30186
30247
  allElementsMap,
30187
30248
  visibleElements,
30188
- versionNonce,
30249
+ sceneNonce,
30189
30250
  selectionNonce: this.state.selectionElement?.versionNonce,
30190
30251
  scale: window.devicePixelRatio,
30191
30252
  appState: this.state,
@@ -30207,7 +30268,7 @@ var App = class _App extends import_react100.default.Component {
30207
30268
  elementsMap,
30208
30269
  visibleElements,
30209
30270
  selectedElements,
30210
- versionNonce,
30271
+ sceneNonce,
30211
30272
  selectionNonce: this.state.selectionElement?.versionNonce,
30212
30273
  scale: window.devicePixelRatio,
30213
30274
  appState: this.state,
@@ -30301,7 +30362,7 @@ var App = class _App extends import_react100.default.Component {
30301
30362
  );
30302
30363
  }
30303
30364
  this.magicGenerations.set(frameElement.id, data);
30304
- this.onSceneUpdated();
30365
+ this.triggerRender();
30305
30366
  };
30306
30367
  getTextFromElements(elements) {
30307
30368
  const text = elements.reduce((acc, element) => {
@@ -30785,7 +30846,7 @@ var App = class _App extends import_react100.default.Component {
30785
30846
  this.store.onStoreIncrementEmitter.on((increment) => {
30786
30847
  this.history.record(increment.elementsChange, increment.appStateChange);
30787
30848
  });
30788
- this.scene.addCallback(this.onSceneUpdated);
30849
+ this.scene.onUpdate(this.triggerRender);
30789
30850
  this.addEventListeners();
30790
30851
  if (this.props.autoFocus && this.excalidrawContainerRef.current) {
30791
30852
  this.focusContainer();
@@ -30821,6 +30882,7 @@ var App = class _App extends import_react100.default.Component {
30821
30882
  componentWillUnmount() {
30822
30883
  this.renderer.destroy();
30823
30884
  this.scene = new Scene_default();
30885
+ this.fonts = new Fonts({ scene: this.scene });
30824
30886
  this.renderer = new Renderer(this.scene);
30825
30887
  this.files = {};
30826
30888
  this.imageCache.clear();
@@ -30914,6 +30976,7 @@ var App = class _App extends import_react100.default.Component {
30914
30976
  ),
30915
30977
  addEventListener(window, "focus" /* FOCUS */, () => {
30916
30978
  this.maybeCleanupAfterMissingPointerUp(null);
30979
+ this.triggerRender(true);
30917
30980
  })
30918
30981
  );
30919
30982
  if (this.state.viewModeEnabled) {
@@ -31698,7 +31761,7 @@ var App = class _App extends import_react100.default.Component {
31698
31761
  ShapeCache.delete(element);
31699
31762
  }
31700
31763
  });
31701
- this.scene.informMutation();
31764
+ this.scene.triggerUpdate();
31702
31765
  this.addNewImagesToImageCache();
31703
31766
  }
31704
31767
  );
@@ -31738,8 +31801,12 @@ var App = class _App extends import_react100.default.Component {
31738
31801
  }
31739
31802
  }
31740
31803
  );
31741
- onSceneUpdated = () => {
31742
- this.setState({});
31804
+ triggerRender = (force) => {
31805
+ if (force === true) {
31806
+ this.scene.triggerUpdate();
31807
+ } else {
31808
+ this.setState({});
31809
+ }
31743
31810
  };
31744
31811
  /**
31745
31812
  * @returns whether the menu was toggled on or off
@@ -32142,21 +32209,22 @@ var App = class _App extends import_react100.default.Component {
32142
32209
  isExistingElement = false
32143
32210
  }) {
32144
32211
  const elementsMap = this.scene.getElementsMapIncludingDeleted();
32145
- const updateElement = (text, originalText, isDeleted) => {
32212
+ const updateElement = (nextOriginalText, isDeleted) => {
32146
32213
  this.scene.replaceAllElements([
32147
32214
  // Not sure why we include deleted elements as well hence using deleted elements map
32148
32215
  ...this.scene.getElementsIncludingDeleted().map((_element) => {
32149
32216
  if (_element.id === element.id && isTextElement(_element)) {
32150
- return updateTextElement(
32151
- _element,
32152
- getContainerElement(_element, elementsMap),
32153
- elementsMap,
32154
- {
32155
- text,
32156
- isDeleted,
32157
- originalText
32158
- }
32159
- );
32217
+ return newElementWith(_element, {
32218
+ originalText: nextOriginalText,
32219
+ isDeleted: isDeleted ?? _element.isDeleted,
32220
+ // returns (wrapped) text and new dimensions
32221
+ ...refreshTextDimensions(
32222
+ _element,
32223
+ getContainerElement(_element, elementsMap),
32224
+ elementsMap,
32225
+ nextOriginalText
32226
+ )
32227
+ });
32160
32228
  }
32161
32229
  return _element;
32162
32230
  })
@@ -32178,15 +32246,15 @@ var App = class _App extends import_react100.default.Component {
32178
32246
  viewportY - this.state.offsetTop
32179
32247
  ];
32180
32248
  },
32181
- onChange: withBatchedUpdates((text) => {
32182
- updateElement(text, text, false);
32249
+ onChange: withBatchedUpdates((nextOriginalText) => {
32250
+ updateElement(nextOriginalText, false);
32183
32251
  if (isNonDeletedElement(element)) {
32184
32252
  updateBoundElements(element, elementsMap);
32185
32253
  }
32186
32254
  }),
32187
- onSubmit: withBatchedUpdates(({ text, viaKeyboard, originalText }) => {
32188
- const isDeleted = !text.trim();
32189
- updateElement(text, originalText, isDeleted);
32255
+ onSubmit: withBatchedUpdates(({ viaKeyboard, nextOriginalText }) => {
32256
+ const isDeleted = !nextOriginalText.trim();
32257
+ updateElement(nextOriginalText, isDeleted);
32190
32258
  if (!isDeleted && viaKeyboard) {
32191
32259
  const elementIdToSelect = element.containerId ? element.containerId : element.id;
32192
32260
  this.setState((prevState) => ({
@@ -32221,7 +32289,7 @@ var App = class _App extends import_react100.default.Component {
32221
32289
  app: this
32222
32290
  });
32223
32291
  this.deselectElements();
32224
- updateElement(element.text, element.originalText, false);
32292
+ updateElement(element.originalText, false);
32225
32293
  }
32226
32294
  deselectElements() {
32227
32295
  this.setState({
@@ -32720,8 +32788,11 @@ var App = class _App extends import_react100.default.Component {
32720
32788
  );
32721
32789
  this.translateCanvas({
32722
32790
  zoom: zoomState.zoom,
32723
- scrollX: zoomState.scrollX + deltaX / nextZoom,
32724
- scrollY: zoomState.scrollY + deltaY / nextZoom,
32791
+ // 2x multiplier is just a magic number that makes this work correctly
32792
+ // on touchscreen devices (note: if we get report that panning is slower/faster
32793
+ // than actual movement, consider swapping with devicePixelRatio)
32794
+ scrollX: zoomState.scrollX + 2 * (deltaX / nextZoom),
32795
+ scrollY: zoomState.scrollY + 2 * (deltaY / nextZoom),
32725
32796
  shouldCacheIgnoreZoom: true
32726
32797
  });
32727
32798
  });
@@ -33070,7 +33141,7 @@ var App = class _App extends import_react100.default.Component {
33070
33141
  }
33071
33142
  }
33072
33143
  this.elementsPendingErasure = new Set(this.elementsPendingErasure);
33073
- this.onSceneUpdated();
33144
+ this.triggerRender();
33074
33145
  }
33075
33146
  };
33076
33147
  // set touch moving for mobile context menu
@@ -34874,7 +34945,7 @@ var App = class _App extends import_react100.default.Component {
34874
34945
  [linearElement],
34875
34946
  this.scene.getNonDeletedElementsMap()
34876
34947
  );
34877
- this.scene.informMutation();
34948
+ this.scene.triggerUpdate();
34878
34949
  }
34879
34950
  }
34880
34951
  }
@@ -35227,7 +35298,7 @@ var App = class _App extends import_react100.default.Component {
35227
35298
  }
35228
35299
  restoreReadyToEraseElements = () => {
35229
35300
  this.elementsPendingErasure = /* @__PURE__ */ new Set();
35230
- this.onSceneUpdated();
35301
+ this.triggerRender();
35231
35302
  };
35232
35303
  eraseElements = () => {
35233
35304
  let didChange = false;
@@ -35542,7 +35613,7 @@ var App = class _App extends import_react100.default.Component {
35542
35613
  files
35543
35614
  );
35544
35615
  if (updatedFiles.size) {
35545
- this.scene.informMutation();
35616
+ this.scene.triggerUpdate();
35546
35617
  }
35547
35618
  }
35548
35619
  };
@@ -36015,6 +36086,7 @@ var App = class _App extends import_react100.default.Component {
36015
36086
  return [actionCopy, ...options];
36016
36087
  }
36017
36088
  return [
36089
+ CONTEXT_MENU_SEPARATOR,
36018
36090
  actionCut,
36019
36091
  actionCopy,
36020
36092
  actionPaste,
@@ -36027,6 +36099,7 @@ var App = class _App extends import_react100.default.Component {
36027
36099
  actionPasteStyles,
36028
36100
  CONTEXT_MENU_SEPARATOR,
36029
36101
  actionGroup,
36102
+ actionTextAutoResize,
36030
36103
  actionUnbindText,
36031
36104
  actionBindText,
36032
36105
  actionWrapTextInContainer,