@excalidraw/excalidraw 0.17.1-1ed53b1 → 0.17.1-22b3927

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 (122) 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-VC7RRIDZ.js} +230 -93
  4. package/dist/browser/dev/excalidraw-assets-dev/chunk-VC7RRIDZ.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-J7S3ALXP.js} +2 -2
  9. package/dist/browser/dev/index.js +335 -116
  10. package/dist/browser/dev/index.js.map +4 -4
  11. package/dist/browser/prod/excalidraw-assets/chunk-CWO763YJ.js +55 -0
  12. package/dist/browser/prod/excalidraw-assets/{chunk-O4AI3NNG.js → chunk-IZMZ6RPD.js} +1 -1
  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-SZBFRCU2.js +1 -0
  16. package/dist/browser/prod/index.js +24 -24
  17. package/dist/dev/{en-UQDDYCH7.json → en-OIPCBIOA.json} +3 -1
  18. package/dist/dev/index.js +576 -207
  19. package/dist/dev/index.js.map +4 -4
  20. package/dist/excalidraw/actions/actionAddToLibrary.d.ts +3 -3
  21. package/dist/excalidraw/actions/actionAlign.d.ts +6 -6
  22. package/dist/excalidraw/actions/actionBoundText.d.ts +3 -3
  23. package/dist/excalidraw/actions/actionBoundText.js +3 -1
  24. package/dist/excalidraw/actions/actionCanvas.d.ts +13 -13
  25. package/dist/excalidraw/actions/actionClipboard.d.ts +12 -12
  26. package/dist/excalidraw/actions/actionDeleteSelected.d.ts +3 -3
  27. package/dist/excalidraw/actions/actionDistribute.d.ts +2 -2
  28. package/dist/excalidraw/actions/actionDuplicateSelection.d.ts +1 -1
  29. package/dist/excalidraw/actions/actionElementLock.d.ts +2 -2
  30. package/dist/excalidraw/actions/actionExport.d.ts +11 -11
  31. package/dist/excalidraw/actions/actionFinalize.d.ts +2 -2
  32. package/dist/excalidraw/actions/actionFlip.d.ts +2 -2
  33. package/dist/excalidraw/actions/actionFrame.d.ts +312 -4
  34. package/dist/excalidraw/actions/actionGroup.d.ts +312 -2
  35. package/dist/excalidraw/actions/actionHistory.js +4 -4
  36. package/dist/excalidraw/actions/actionLinearEditor.d.ts +1 -1
  37. package/dist/excalidraw/actions/actionLink.d.ts +1 -1
  38. package/dist/excalidraw/actions/actionMenu.d.ts +3 -3
  39. package/dist/excalidraw/actions/actionNavigate.d.ts +2 -2
  40. package/dist/excalidraw/actions/actionProperties.d.ts +13 -13
  41. package/dist/excalidraw/actions/actionProperties.js +1 -1
  42. package/dist/excalidraw/actions/actionSelectAll.d.ts +1 -1
  43. package/dist/excalidraw/actions/actionStyles.d.ts +5 -2
  44. package/dist/excalidraw/actions/actionTextAutoResize.d.ts +17 -0
  45. package/dist/excalidraw/actions/actionTextAutoResize.js +38 -0
  46. package/dist/excalidraw/actions/actionToggleGridMode.d.ts +1 -1
  47. package/dist/excalidraw/actions/actionToggleObjectsSnapMode.d.ts +1 -1
  48. package/dist/excalidraw/actions/actionToggleStats.d.ts +1 -1
  49. package/dist/excalidraw/actions/actionToggleViewMode.d.ts +1 -1
  50. package/dist/excalidraw/actions/actionToggleZenMode.d.ts +1 -1
  51. package/dist/excalidraw/actions/actionZindex.d.ts +4 -4
  52. package/dist/excalidraw/actions/types.d.ts +1 -1
  53. package/dist/excalidraw/change.js +13 -6
  54. package/dist/excalidraw/components/Actions.js +1 -1
  55. package/dist/excalidraw/components/App.d.ts +2 -2
  56. package/dist/excalidraw/components/App.js +133 -51
  57. package/dist/excalidraw/components/ButtonIconSelect.js +1 -1
  58. package/dist/excalidraw/components/CheckboxItem.js +1 -1
  59. package/dist/excalidraw/components/CommandPalette/CommandPalette.js +2 -2
  60. package/dist/excalidraw/components/ContextMenu.js +1 -1
  61. package/dist/excalidraw/components/Dialog.js +1 -1
  62. package/dist/excalidraw/components/FollowMode/FollowMode.js +1 -1
  63. package/dist/excalidraw/components/IconPicker.js +2 -2
  64. package/dist/excalidraw/components/LayerUI.js +2 -2
  65. package/dist/excalidraw/components/MobileMenu.js +1 -1
  66. package/dist/excalidraw/components/PasteChartDialog.js +1 -1
  67. package/dist/excalidraw/components/canvases/InteractiveCanvas.d.ts +3 -2
  68. package/dist/excalidraw/components/canvases/InteractiveCanvas.js +4 -2
  69. package/dist/excalidraw/components/canvases/StaticCanvas.d.ts +1 -1
  70. package/dist/excalidraw/components/canvases/StaticCanvas.js +2 -2
  71. package/dist/excalidraw/components/icons.js +6 -2
  72. package/dist/excalidraw/constants.d.ts +1 -0
  73. package/dist/excalidraw/constants.js +5 -0
  74. package/dist/excalidraw/data/restore.js +3 -0
  75. package/dist/excalidraw/element/dragElements.d.ts +2 -2
  76. package/dist/excalidraw/element/dragElements.js +27 -3
  77. package/dist/excalidraw/element/embeddable.d.ts +1 -1
  78. package/dist/excalidraw/element/index.d.ts +1 -1
  79. package/dist/excalidraw/element/index.js +1 -1
  80. package/dist/excalidraw/element/mutateElement.d.ts +1 -1
  81. package/dist/excalidraw/element/mutateElement.js +5 -3
  82. package/dist/excalidraw/element/newElement.d.ts +2 -5
  83. package/dist/excalidraw/element/newElement.js +16 -14
  84. package/dist/excalidraw/element/resizeElements.js +73 -21
  85. package/dist/excalidraw/element/resizeTest.js +2 -4
  86. package/dist/excalidraw/element/textElement.d.ts +1 -0
  87. package/dist/excalidraw/element/textElement.js +11 -3
  88. package/dist/excalidraw/element/textWysiwyg.d.ts +10 -4
  89. package/dist/excalidraw/element/textWysiwyg.js +38 -17
  90. package/dist/excalidraw/element/transformHandles.js +0 -10
  91. package/dist/excalidraw/element/types.d.ts +7 -0
  92. package/dist/excalidraw/fractionalIndex.js +2 -4
  93. package/dist/excalidraw/locales/en.json +3 -1
  94. package/dist/excalidraw/mermaid.d.ts +2 -0
  95. package/dist/excalidraw/mermaid.js +28 -0
  96. package/dist/excalidraw/renderer/interactiveScene.d.ts +1 -1
  97. package/dist/excalidraw/renderer/interactiveScene.js +31 -5
  98. package/dist/excalidraw/renderer/renderElement.d.ts +2 -2
  99. package/dist/excalidraw/renderer/renderElement.js +2 -2
  100. package/dist/excalidraw/scene/Fonts.d.ts +1 -3
  101. package/dist/excalidraw/scene/Fonts.js +6 -12
  102. package/dist/excalidraw/scene/Renderer.d.ts +1 -1
  103. package/dist/excalidraw/scene/Renderer.js +2 -3
  104. package/dist/excalidraw/scene/Scene.d.ts +10 -4
  105. package/dist/excalidraw/scene/Scene.js +14 -8
  106. package/dist/excalidraw/scene/export.js +1 -1
  107. package/dist/excalidraw/scene/types.d.ts +2 -1
  108. package/dist/excalidraw/snapping.js +2 -1
  109. package/dist/excalidraw/store.d.ts +32 -2
  110. package/dist/excalidraw/store.js +27 -0
  111. package/dist/excalidraw/types.d.ts +1 -0
  112. package/dist/prod/{en-UQDDYCH7.json → en-OIPCBIOA.json} +3 -1
  113. package/dist/prod/index.js +42 -42
  114. package/package.json +2 -2
  115. package/dist/browser/dev/excalidraw-assets-dev/chunk-JKPJV7MZ.js.map +0 -7
  116. package/dist/browser/dev/excalidraw-assets-dev/chunk-OKAZAA6U.js.map +0 -7
  117. package/dist/browser/dev/excalidraw-assets-dev/dist-ITJNUBZF.js.map +0 -7
  118. package/dist/browser/prod/excalidraw-assets/chunk-SXBDZOS3.js +0 -55
  119. package/dist/browser/prod/excalidraw-assets/dist-54276HPL.js +0 -6
  120. package/dist/browser/prod/excalidraw-assets/image-VAGBVQ3G.js +0 -1
  121. /package/dist/browser/dev/excalidraw-assets-dev/{en-BF4XUPIZ.js.map → en-Y27YPU72.js.map} +0 -0
  122. /package/dist/browser/dev/excalidraw-assets-dev/{image-LVS32KQQ.js.map → image-J7S3ALXP.js.map} +0 -0
@@ -202,6 +202,7 @@ import {
202
202
  getLinkHandleFromCoords,
203
203
  getLockedLinearCursorAlignSize,
204
204
  getMaximumGroups,
205
+ getMinTextElementWidth,
205
206
  getNearestScrollableContainer,
206
207
  getNonDeletedElements,
207
208
  getNormalizedCanvasDimensions,
@@ -404,18 +405,17 @@ import {
404
405
  updateObject,
405
406
  updateOriginalContainerCache,
406
407
  updateStable,
407
- updateTextElement,
408
408
  viewportCoordsToSceneCoords,
409
409
  wrapEvent,
410
410
  wrapText
411
- } from "./excalidraw-assets-dev/chunk-OKAZAA6U.js";
411
+ } from "./excalidraw-assets-dev/chunk-VC7RRIDZ.js";
412
412
  import {
413
413
  define_import_meta_env_default,
414
414
  init_define_import_meta_env
415
415
  } from "./excalidraw-assets-dev/chunk-YRUDZAGT.js";
416
416
  import {
417
417
  en_default
418
- } from "./excalidraw-assets-dev/chunk-JKPJV7MZ.js";
418
+ } from "./excalidraw-assets-dev/chunk-Q6A4M3MN.js";
419
419
  import {
420
420
  percentages_default
421
421
  } from "./excalidraw-assets-dev/chunk-YZTYRBEQ.js";
@@ -2679,7 +2679,7 @@ var globImport_locales_json = __glob({
2679
2679
  "./locales/da-DK.json": () => import("./excalidraw-assets-dev/da-DK-6OKJ2GQ6.js"),
2680
2680
  "./locales/de-DE.json": () => import("./excalidraw-assets-dev/de-DE-BD5ICOQ2.js"),
2681
2681
  "./locales/el-GR.json": () => import("./excalidraw-assets-dev/el-GR-JFDBTDHS.js"),
2682
- "./locales/en.json": () => import("./excalidraw-assets-dev/en-BF4XUPIZ.js"),
2682
+ "./locales/en.json": () => import("./excalidraw-assets-dev/en-Y27YPU72.js"),
2683
2683
  "./locales/es-ES.json": () => import("./excalidraw-assets-dev/es-ES-CBBTGYGR.js"),
2684
2684
  "./locales/eu-ES.json": () => import("./excalidraw-assets-dev/eu-ES-CHAPMSLE.js"),
2685
2685
  "./locales/fa-IR.json": () => import("./excalidraw-assets-dev/fa-IR-LOJOKEJO.js"),
@@ -3781,12 +3781,16 @@ var arrownNarrowUpJSX = /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("g", { str
3781
3781
  var BringForwardIcon = createIcon(arrownNarrowUpJSX, tablerIconProps);
3782
3782
  var SendBackwardIcon = createIcon(arrownNarrowUpJSX, {
3783
3783
  ...tablerIconProps,
3784
- transform: "rotate(180)"
3784
+ style: {
3785
+ transform: "rotate(180deg)"
3786
+ }
3785
3787
  });
3786
3788
  var BringToFrontIcon = createIcon(arrowBarToTopJSX, tablerIconProps);
3787
3789
  var SendToBackIcon = createIcon(arrowBarToTopJSX, {
3788
3790
  ...tablerIconProps,
3789
- transform: "rotate(180)"
3791
+ style: {
3792
+ transform: "rotate(180deg)"
3793
+ }
3790
3794
  });
3791
3795
  var AlignTopIcon = createIcon(
3792
3796
  /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
@@ -6066,6 +6070,7 @@ var ButtonIconSelect = (props) => /* @__PURE__ */ (0, import_jsx_runtime9.jsx)("
6066
6070
  (option) => props.type === "button" ? /* @__PURE__ */ (0, import_jsx_runtime9.jsx)(
6067
6071
  "button",
6068
6072
  {
6073
+ type: "button",
6069
6074
  onClick: (event) => props.onClick(option.value, event),
6070
6075
  className: clsx_m_default({
6071
6076
  active: option.active ?? props.value === option.value
@@ -10593,6 +10598,7 @@ function Picker2({
10593
10598
  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
10599
  "button",
10595
10600
  {
10601
+ type: "button",
10596
10602
  className: clsx_m_default("picker-option", {
10597
10603
  active: value === option.value
10598
10604
  }),
@@ -10639,6 +10645,7 @@ function IconPicker({
10639
10645
  "button",
10640
10646
  {
10641
10647
  name: group,
10648
+ type: "button",
10642
10649
  className: isActive ? "active" : "",
10643
10650
  "aria-label": label,
10644
10651
  onClick: () => setActive(!isActive),
@@ -10710,7 +10717,7 @@ var getFormValue = function(elements, appState, getAttribute, isRelevantElement,
10710
10717
  return ret;
10711
10718
  };
10712
10719
  var offsetElementAfterFontResize = (prevElement, nextElement) => {
10713
- if (isBoundToContainer(nextElement)) {
10720
+ if (isBoundToContainer(nextElement) || !nextElement.autoResize) {
10714
10721
  return nextElement;
10715
10722
  }
10716
10723
  return mutateElement(
@@ -12497,7 +12504,7 @@ var exportCanvas = async (type, elements, appState, files, {
12497
12504
  let blob = canvasToBlob(tempCanvas);
12498
12505
  if (appState.exportEmbedScene) {
12499
12506
  blob = blob.then(
12500
- (blob2) => import("./excalidraw-assets-dev/image-LVS32KQQ.js").then(
12507
+ (blob2) => import("./excalidraw-assets-dev/image-J7S3ALXP.js").then(
12501
12508
  ({ encodePngMetadata }) => encodePngMetadata({
12502
12509
  blob: blob2,
12503
12510
  metadata: serializeAsJSON(elements, appState, files, "local")
@@ -12586,7 +12593,16 @@ var CheckboxItem = ({ children, checked, onChange, className }) => {
12586
12593
  ).focus();
12587
12594
  },
12588
12595
  children: [
12589
- /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("button", { className: "Checkbox-box", role: "checkbox", "aria-checked": checked, children: checkIcon }),
12596
+ /* @__PURE__ */ (0, import_jsx_runtime28.jsx)(
12597
+ "button",
12598
+ {
12599
+ type: "button",
12600
+ className: "Checkbox-box",
12601
+ role: "checkbox",
12602
+ "aria-checked": checked,
12603
+ children: checkIcon
12604
+ }
12605
+ ),
12590
12606
  /* @__PURE__ */ (0, import_jsx_runtime28.jsx)("div", { className: "Checkbox-label", children })
12591
12607
  ]
12592
12608
  }
@@ -14570,7 +14586,8 @@ var actionBindText = register({
14570
14586
  mutateElement(textElement, {
14571
14587
  containerId: container.id,
14572
14588
  verticalAlign: VERTICAL_ALIGN.MIDDLE,
14573
- textAlign: TEXT_ALIGN.CENTER
14589
+ textAlign: TEXT_ALIGN.CENTER,
14590
+ autoResize: true
14574
14591
  });
14575
14592
  mutateElement(container, {
14576
14593
  boundElements: (container.boundElements || []).concat({
@@ -14692,7 +14709,8 @@ var actionWrapTextInContainer = register({
14692
14709
  containerId: container.id,
14693
14710
  verticalAlign: VERTICAL_ALIGN.MIDDLE,
14694
14711
  boundElements: null,
14695
- textAlign: TEXT_ALIGN.CENTER
14712
+ textAlign: TEXT_ALIGN.CENTER,
14713
+ autoResize: true
14696
14714
  },
14697
14715
  false
14698
14716
  );
@@ -17869,6 +17887,7 @@ var Dialog = (props) => {
17869
17887
  onClick: onClose,
17870
17888
  title: t("buttons.close"),
17871
17889
  "aria-label": t("buttons.close"),
17890
+ type: "button",
17872
17891
  children: CloseIcon
17873
17892
  }
17874
17893
  ),
@@ -18814,6 +18833,7 @@ var ExitZenModeAction = ({
18814
18833
  }) => /* @__PURE__ */ (0, import_jsx_runtime64.jsx)(
18815
18834
  "button",
18816
18835
  {
18836
+ type: "button",
18817
18837
  className: clsx_m_default("disable-zen-mode", {
18818
18838
  "disable-zen-mode--visible": showExitZenModeBtn
18819
18839
  }),
@@ -19290,7 +19310,7 @@ function CommandPaletteInner({
19290
19310
  ...command,
19291
19311
  icon: command.icon || boltIcon,
19292
19312
  order: command.order ?? getCategoryOrder(command.category),
19293
- haystack: `${deburr(command.label)} ${command.keywords?.join(" ") || ""}`
19313
+ haystack: `${deburr(command.label.toLocaleLowerCase())} ${command.keywords?.join(" ") || ""}`
19294
19314
  };
19295
19315
  });
19296
19316
  setAllCommands(allCommands2);
@@ -19461,7 +19481,9 @@ function CommandPaletteInner({
19461
19481
  setCurrentCommand(showLastUsed ? lastUsed : matchingCommands[0] || null);
19462
19482
  return;
19463
19483
  }
19464
- const _query = deburr(commandSearch.replace(/[<>-_| ]/g, ""));
19484
+ const _query = deburr(
19485
+ commandSearch.toLocaleLowerCase().replace(/[<>_| -]/g, "")
19486
+ );
19465
19487
  matchingCommands = import_fuzzy.default.filter(_query, matchingCommands, {
19466
19488
  extract: (command) => command.haystack
19467
19489
  }).sort((a3, b3) => b3.score - a3.score).map((item) => item.original);
@@ -19834,7 +19856,10 @@ var createUndoAction = (history, store) => ({
19834
19856
  PanelComponent: ({ updateData, data }) => {
19835
19857
  const { isUndoStackEmpty } = useEmitter(
19836
19858
  history.onHistoryChangedEmitter,
19837
- new HistoryChangedEvent()
19859
+ new HistoryChangedEvent(
19860
+ history.isUndoStackEmpty,
19861
+ history.isRedoStackEmpty
19862
+ )
19838
19863
  );
19839
19864
  return /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(
19840
19865
  ToolButton,
@@ -19844,7 +19869,8 @@ var createUndoAction = (history, store) => ({
19844
19869
  "aria-label": t("buttons.undo"),
19845
19870
  onClick: updateData,
19846
19871
  size: data?.size || "medium",
19847
- disabled: isUndoStackEmpty
19872
+ disabled: isUndoStackEmpty,
19873
+ "data-testid": "button-undo"
19848
19874
  }
19849
19875
  );
19850
19876
  }
@@ -19868,7 +19894,10 @@ var createRedoAction = (history, store) => ({
19868
19894
  PanelComponent: ({ updateData, data }) => {
19869
19895
  const { isRedoStackEmpty } = useEmitter(
19870
19896
  history.onHistoryChangedEmitter,
19871
- new HistoryChangedEvent()
19897
+ new HistoryChangedEvent(
19898
+ history.isUndoStackEmpty,
19899
+ history.isRedoStackEmpty
19900
+ )
19872
19901
  );
19873
19902
  return /* @__PURE__ */ (0, import_jsx_runtime68.jsx)(
19874
19903
  ToolButton,
@@ -19878,7 +19907,8 @@ var createRedoAction = (history, store) => ({
19878
19907
  "aria-label": t("buttons.redo"),
19879
19908
  onClick: updateData,
19880
19909
  size: data?.size || "medium",
19881
- disabled: isRedoStackEmpty
19910
+ disabled: isRedoStackEmpty,
19911
+ "data-testid": "button-redo"
19882
19912
  }
19883
19913
  );
19884
19914
  }
@@ -20093,6 +20123,7 @@ var ContextMenu = import_react71.default.memo(
20093
20123
  children: /* @__PURE__ */ (0, import_jsx_runtime70.jsxs)(
20094
20124
  "button",
20095
20125
  {
20126
+ type: "button",
20096
20127
  className: clsx_m_default("context-menu-item", {
20097
20128
  dangerous: actionName === "deleteSelectedElements",
20098
20129
  checkmark: item.checked?.(appState)
@@ -21068,6 +21099,7 @@ var MobileMenu = ({
21068
21099
  appState.scrolledOutside && !appState.openMenu && !appState.openSidebar && /* @__PURE__ */ (0, import_jsx_runtime83.jsx)(
21069
21100
  "button",
21070
21101
  {
21102
+ type: "button",
21071
21103
  className: "scroll-back-to-content",
21072
21104
  onClick: () => {
21073
21105
  setAppState((appState2) => ({
@@ -21130,6 +21162,7 @@ var ChartPreviewBtn = (props) => {
21130
21162
  return /* @__PURE__ */ (0, import_jsx_runtime84.jsx)(
21131
21163
  "button",
21132
21164
  {
21165
+ type: "button",
21133
21166
  className: "ChartPreview",
21134
21167
  onClick: () => {
21135
21168
  if (chartElements) {
@@ -24439,7 +24472,7 @@ TTD mermaid definition render errror: ${error3.message}`,
24439
24472
  refOnGenerate.current = onGenerate;
24440
24473
  const [mermaidToExcalidrawLib, setMermaidToExcalidrawLib] = (0, import_react93.useState)({
24441
24474
  loaded: false,
24442
- api: import("./excalidraw-assets-dev/dist-ITJNUBZF.js")
24475
+ api: import("./excalidraw-assets-dev/dist-6QVAH5JA.js")
24443
24476
  });
24444
24477
  (0, import_react93.useEffect)(() => {
24445
24478
  const fn = async () => {
@@ -24927,7 +24960,7 @@ var LayerUI = ({
24927
24960
  );
24928
24961
  ShapeCache.delete(element);
24929
24962
  }
24930
- Scene_default.getScene(selectedElements[0])?.informMutation();
24963
+ Scene_default.getScene(selectedElements[0])?.triggerUpdate();
24931
24964
  } else if (colorPickerType === "elementBackground") {
24932
24965
  setAppState({
24933
24966
  currentItemBackgroundColor: color
@@ -25037,6 +25070,7 @@ var LayerUI = ({
25037
25070
  appState.scrolledOutside && /* @__PURE__ */ (0, import_jsx_runtime119.jsx)(
25038
25071
  "button",
25039
25072
  {
25073
+ type: "button",
25040
25074
  className: "scroll-back-to-content",
25041
25075
  onClick: () => {
25042
25076
  setAppState((appState2) => ({
@@ -25173,13 +25207,8 @@ var import_lodash2 = __toESM(require_lodash(), 1);
25173
25207
  init_define_import_meta_env();
25174
25208
  var Fonts = class _Fonts {
25175
25209
  scene;
25176
- onSceneUpdated;
25177
- constructor({
25178
- scene,
25179
- onSceneUpdated
25180
- }) {
25210
+ constructor({ scene }) {
25181
25211
  this.scene = scene;
25182
- this.onSceneUpdated = onSceneUpdated;
25183
25212
  }
25184
25213
  // it's ok to track fonts across multiple instances only once, so let's use
25185
25214
  // a static member to reduce memory footprint
@@ -25210,21 +25239,15 @@ var Fonts = class _Fonts {
25210
25239
  }
25211
25240
  let didUpdate = false;
25212
25241
  this.scene.mapElements((element) => {
25213
- if (isTextElement(element) && !isBoundToContainer(element)) {
25214
- ShapeCache.delete(element);
25242
+ if (isTextElement(element)) {
25215
25243
  didUpdate = true;
25216
- return newElementWith(element, {
25217
- ...refreshTextDimensions(
25218
- element,
25219
- getContainerElement(element, this.scene.getNonDeletedElementsMap()),
25220
- this.scene.getNonDeletedElementsMap()
25221
- )
25222
- });
25244
+ ShapeCache.delete(element);
25245
+ return newElementWith(element, {}, true);
25223
25246
  }
25224
25247
  return element;
25225
25248
  });
25226
25249
  if (didUpdate) {
25227
- this.onSceneUpdated();
25250
+ this.scene.triggerUpdate();
25228
25251
  }
25229
25252
  };
25230
25253
  loadFontsForElements = async (elements) => {
@@ -26301,7 +26324,7 @@ var getSnapLinesAtPointer = (elements, appState, pointer, event, elementsMap) =>
26301
26324
  };
26302
26325
  };
26303
26326
  var isActiveToolNonLinearSnappable = (activeToolType) => {
26304
- return activeToolType === TOOL_TYPE.rectangle || activeToolType === TOOL_TYPE.ellipse || activeToolType === TOOL_TYPE.diamond || activeToolType === TOOL_TYPE.frame || activeToolType === TOOL_TYPE.magicframe || activeToolType === TOOL_TYPE.image;
26327
+ return activeToolType === TOOL_TYPE.rectangle || activeToolType === TOOL_TYPE.ellipse || activeToolType === TOOL_TYPE.diamond || activeToolType === TOOL_TYPE.frame || activeToolType === TOOL_TYPE.magicframe || activeToolType === TOOL_TYPE.image || activeToolType === TOOL_TYPE.text;
26305
26328
  };
26306
26329
 
26307
26330
  // components/BraveMeasureTextError.tsx
@@ -27138,7 +27161,7 @@ var renderBindingHighlightForSuggestedPointBinding = (context, suggestedBinding,
27138
27161
  fillCircle(context, x3, y3, threshold);
27139
27162
  });
27140
27163
  };
27141
- var renderSelectionBorder = (context, appState, elementProperties, padding = DEFAULT_TRANSFORM_HANDLE_SPACING * 2) => {
27164
+ var renderSelectionBorder = (context, appState, elementProperties) => {
27142
27165
  const {
27143
27166
  angle,
27144
27167
  elementX1,
@@ -27153,6 +27176,7 @@ var renderSelectionBorder = (context, appState, elementProperties, padding = DEF
27153
27176
  } = elementProperties;
27154
27177
  const elementWidth = elementX2 - elementX1;
27155
27178
  const elementHeight = elementY2 - elementY1;
27179
+ const padding = DEFAULT_TRANSFORM_HANDLE_SPACING * 2;
27156
27180
  const linePadding = padding / appState.zoom.value;
27157
27181
  const lineWidth = 8 / appState.zoom.value;
27158
27182
  const spaceWidth = 4 / appState.zoom.value;
@@ -27337,11 +27361,28 @@ var renderTransformHandles = (context, renderConfig, appState, transformHandles,
27337
27361
  }
27338
27362
  });
27339
27363
  };
27364
+ var renderTextBox = (text, context, appState, selectionColor) => {
27365
+ context.save();
27366
+ const padding = DEFAULT_TRANSFORM_HANDLE_SPACING * 2 / appState.zoom.value;
27367
+ const width = text.width + padding * 2;
27368
+ const height = text.height + padding * 2;
27369
+ const cx = text.x + width / 2;
27370
+ const cy = text.y + height / 2;
27371
+ const shiftX = -(width / 2 + padding);
27372
+ const shiftY = -(height / 2 + padding);
27373
+ context.translate(cx + appState.scrollX, cy + appState.scrollY);
27374
+ context.rotate(text.angle);
27375
+ context.lineWidth = 1 / appState.zoom.value;
27376
+ context.strokeStyle = selectionColor;
27377
+ context.strokeRect(shiftX, shiftY, width, height);
27378
+ context.restore();
27379
+ };
27340
27380
  var _renderInteractiveScene = ({
27341
27381
  canvas,
27342
27382
  elementsMap,
27343
27383
  visibleElements,
27344
27384
  selectedElements,
27385
+ allElementsMap,
27345
27386
  scale,
27346
27387
  appState,
27347
27388
  renderConfig,
@@ -27380,11 +27421,27 @@ var _renderInteractiveScene = ({
27380
27421
  }
27381
27422
  if (appState.selectionElement) {
27382
27423
  try {
27383
- renderSelectionElement(appState.selectionElement, context, appState);
27424
+ renderSelectionElement(
27425
+ appState.selectionElement,
27426
+ context,
27427
+ appState,
27428
+ renderConfig.selectionColor
27429
+ );
27384
27430
  } catch (error) {
27385
27431
  console.error(error);
27386
27432
  }
27387
27433
  }
27434
+ if (appState.editingElement && isTextElement(appState.editingElement)) {
27435
+ const textElement = allElementsMap.get(appState.editingElement.id);
27436
+ if (textElement && !textElement.autoResize) {
27437
+ renderTextBox(
27438
+ textElement,
27439
+ context,
27440
+ appState,
27441
+ renderConfig.selectionColor
27442
+ );
27443
+ }
27444
+ }
27388
27445
  if (appState.isBindingEnabled) {
27389
27446
  appState.suggestedBindings.filter((binding) => binding != null).forEach((suggestedBinding) => {
27390
27447
  renderBindingHighlight(
@@ -27508,7 +27565,8 @@ var _renderInteractiveScene = ({
27508
27565
  // when we render we don't know which pointer type so use mouse,
27509
27566
  getOmitSidesForDevice(device)
27510
27567
  );
27511
- if (!appState.viewModeEnabled && showBoundingBox) {
27568
+ if (!appState.viewModeEnabled && showBoundingBox && // do not show transform handles when text is being edited
27569
+ !isTextElement(appState.editingElement)) {
27512
27570
  renderTransformHandles(
27513
27571
  context,
27514
27572
  renderConfig,
@@ -27667,6 +27725,7 @@ var InteractiveCanvas = (props) => {
27667
27725
  elementsMap: props.elementsMap,
27668
27726
  visibleElements: props.visibleElements,
27669
27727
  selectedElements: props.selectedElements,
27728
+ allElementsMap: props.allElementsMap,
27670
27729
  scale: window.devicePixelRatio,
27671
27730
  appState: props.appState,
27672
27731
  renderConfig: {
@@ -27734,11 +27793,12 @@ var getRelevantAppStateProps = (appState) => ({
27734
27793
  // Necessary for collab. sessions
27735
27794
  activeEmbeddable: appState.activeEmbeddable,
27736
27795
  snapLines: appState.snapLines,
27737
- zenModeEnabled: appState.zenModeEnabled
27796
+ zenModeEnabled: appState.zenModeEnabled,
27797
+ editingElement: appState.editingElement
27738
27798
  });
27739
27799
  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
27800
+ if (prevProps.selectionNonce !== nextProps.selectionNonce || prevProps.sceneNonce !== nextProps.sceneNonce || prevProps.scale !== nextProps.scale || // we need to memoize on elementsMap because they may have renewed
27801
+ // even if sceneNonce didn't change (e.g. we filter elements out based
27742
27802
  // on appState)
27743
27803
  prevProps.elementsMap !== nextProps.elementsMap || prevProps.visibleElements !== nextProps.visibleElements || prevProps.selectedElements !== nextProps.selectedElements) {
27744
27804
  return false;
@@ -27824,8 +27884,8 @@ var getRelevantAppStateProps2 = (appState) => ({
27824
27884
  editingGroupId: appState.editingGroupId
27825
27885
  });
27826
27886
  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
27887
+ if (prevProps.sceneNonce !== nextProps.sceneNonce || prevProps.scale !== nextProps.scale || // we need to memoize on elementsMap because they may have renewed
27888
+ // even if sceneNonce didn't change (e.g. we filter elements out based
27829
27889
  // on appState)
27830
27890
  prevProps.elementsMap !== nextProps.elementsMap || prevProps.visibleElements !== nextProps.visibleElements) {
27831
27891
  return false;
@@ -27909,9 +27969,8 @@ var Renderer = class {
27909
27969
  width,
27910
27970
  editingElement,
27911
27971
  pendingImageElementId,
27912
- // unused but serves we cache on it to invalidate elements if they
27913
- // get mutated
27914
- versionNonce: _versionNonce
27972
+ // cache-invalidation nonce
27973
+ sceneNonce: _sceneNonce
27915
27974
  }) => {
27916
27975
  const elements = this.scene.getNonDeletedElements();
27917
27976
  const elementsMap = getRenderableElements({
@@ -28138,7 +28197,15 @@ var FollowMode = ({
28138
28197
  }
28139
28198
  )
28140
28199
  ] }),
28141
- /* @__PURE__ */ (0, import_jsx_runtime127.jsx)("button", { onClick: onDisconnect, className: "follow-mode__disconnect-btn", children: CloseIcon })
28200
+ /* @__PURE__ */ (0, import_jsx_runtime127.jsx)(
28201
+ "button",
28202
+ {
28203
+ type: "button",
28204
+ onClick: onDisconnect,
28205
+ className: "follow-mode__disconnect-btn",
28206
+ children: CloseIcon
28207
+ }
28208
+ )
28142
28209
  ] }) });
28143
28210
  };
28144
28211
  var FollowMode_default = FollowMode;
@@ -28765,7 +28832,8 @@ var textWysiwyg = ({
28765
28832
  element,
28766
28833
  canvas,
28767
28834
  excalidrawContainer,
28768
- app
28835
+ app,
28836
+ autoSelect = true
28769
28837
  }) => {
28770
28838
  const textPropertiesUpdated = (updatedTextElement, editable2) => {
28771
28839
  if (!editable2.style.fontFamily || !editable2.style.fontSize) {
@@ -28871,6 +28939,8 @@ var textWysiwyg = ({
28871
28939
  if (!container) {
28872
28940
  maxWidth = (appState.width - 8 - viewportX) / appState.zoom.value;
28873
28941
  textElementWidth = Math.min(textElementWidth, maxWidth);
28942
+ } else {
28943
+ textElementWidth += 0.5;
28874
28944
  }
28875
28945
  const editorMaxHeight = (appState.height - viewportY) / appState.zoom.value;
28876
28946
  Object.assign(editable.style, {
@@ -28911,7 +28981,7 @@ var textWysiwyg = ({
28911
28981
  editable.classList.add("excalidraw-wysiwyg");
28912
28982
  let whiteSpace = "pre";
28913
28983
  let wordBreak = "normal";
28914
- if (isBoundToContainer(element)) {
28984
+ if (isBoundToContainer(element) || !element.autoResize) {
28915
28985
  whiteSpace = "pre-wrap";
28916
28986
  wordBreak = "break-word";
28917
28987
  }
@@ -29079,6 +29149,10 @@ var textWysiwyg = ({
29079
29149
  };
29080
29150
  let submittedViaKeyboard = false;
29081
29151
  const handleSubmit = () => {
29152
+ if (isDestroyed) {
29153
+ return;
29154
+ }
29155
+ isDestroyed = true;
29082
29156
  cleanup();
29083
29157
  const updateElement = Scene_default.getScene(element)?.getElement(
29084
29158
  element.id
@@ -29086,13 +29160,11 @@ var textWysiwyg = ({
29086
29160
  if (!updateElement) {
29087
29161
  return;
29088
29162
  }
29089
- let text = editable.value;
29090
29163
  const container = getContainerElement(
29091
29164
  updateElement,
29092
29165
  app.scene.getNonDeletedElementsMap()
29093
29166
  );
29094
29167
  if (container) {
29095
- text = updateElement.text;
29096
29168
  if (editable.value.trim()) {
29097
29169
  const boundTextElementId = getBoundTextElementId(container);
29098
29170
  if (!boundTextElementId || boundTextElementId !== element.id) {
@@ -29121,16 +29193,11 @@ var textWysiwyg = ({
29121
29193
  );
29122
29194
  }
29123
29195
  onSubmit({
29124
- text,
29125
29196
  viaKeyboard: submittedViaKeyboard,
29126
- originalText: editable.value
29197
+ nextOriginalText: editable.value
29127
29198
  });
29128
29199
  };
29129
29200
  const cleanup = () => {
29130
- if (isDestroyed) {
29131
- return;
29132
- }
29133
- isDestroyed = true;
29134
29201
  editable.onblur = null;
29135
29202
  editable.oninput = null;
29136
29203
  editable.onkeydown = null;
@@ -29180,9 +29247,15 @@ var textWysiwyg = ({
29180
29247
  editable.onblur = null;
29181
29248
  window.addEventListener("pointerup", bindBlurEvent);
29182
29249
  window.addEventListener("blur", handleSubmit);
29250
+ } else if (event.target instanceof HTMLElement && !event.target.contains(editable) && // Vitest simply ignores stopPropagation, capture-mode, or rAF
29251
+ // so without introducing crazier hacks, nothing we can do
29252
+ !isTestEnv()) {
29253
+ requestAnimationFrame(() => {
29254
+ handleSubmit();
29255
+ });
29183
29256
  }
29184
29257
  };
29185
- const unbindUpdate = Scene_default.getScene(element).addCallback(() => {
29258
+ const unbindUpdate = Scene_default.getScene(element).onUpdate(() => {
29186
29259
  updateWysiwygStyle();
29187
29260
  const isColorPickerActive = !!document.activeElement?.closest(
29188
29261
  ".color-picker-content"
@@ -29192,7 +29265,9 @@ var textWysiwyg = ({
29192
29265
  }
29193
29266
  });
29194
29267
  let isDestroyed = false;
29195
- editable.select();
29268
+ if (autoSelect) {
29269
+ editable.select();
29270
+ }
29196
29271
  bindBlurEvent();
29197
29272
  let observer = null;
29198
29273
  if (canvas && "ResizeObserver" in window) {
@@ -29203,7 +29278,10 @@ var textWysiwyg = ({
29203
29278
  } else {
29204
29279
  window.addEventListener("resize", updateWysiwygStyle);
29205
29280
  }
29206
- window.addEventListener("pointerdown", onPointerDown);
29281
+ editable.onpointerdown = (event) => event.stopPropagation();
29282
+ requestAnimationFrame(() => {
29283
+ window.addEventListener("pointerdown", onPointerDown, { capture: true });
29284
+ });
29207
29285
  window.addEventListener("wheel", stopEvent, {
29208
29286
  passive: false,
29209
29287
  capture: true
@@ -29212,6 +29290,72 @@ var textWysiwyg = ({
29212
29290
  excalidrawContainer?.querySelector(".excalidraw-textEditorContainer").appendChild(editable);
29213
29291
  };
29214
29292
 
29293
+ // actions/actionTextAutoResize.ts
29294
+ init_define_import_meta_env();
29295
+ var actionTextAutoResize = register({
29296
+ name: "autoResize",
29297
+ label: "labels.autoResize",
29298
+ icon: null,
29299
+ trackEvent: { category: "element" },
29300
+ predicate: (elements, appState, _, app) => {
29301
+ const selectedElements = getSelectedElements(elements, appState);
29302
+ return selectedElements.length === 1 && isTextElement(selectedElements[0]) && !selectedElements[0].autoResize;
29303
+ },
29304
+ perform: (elements, appState, _, app) => {
29305
+ const selectedElements = getSelectedElements(elements, appState);
29306
+ return {
29307
+ appState,
29308
+ elements: elements.map((element) => {
29309
+ if (element.id === selectedElements[0].id && isTextElement(element)) {
29310
+ const metrics = measureText(
29311
+ element.originalText,
29312
+ getFontString(element),
29313
+ element.lineHeight
29314
+ );
29315
+ return newElementWith(element, {
29316
+ autoResize: true,
29317
+ width: metrics.width,
29318
+ height: metrics.height,
29319
+ text: element.originalText
29320
+ });
29321
+ }
29322
+ return element;
29323
+ }),
29324
+ storeAction: StoreAction.CAPTURE
29325
+ };
29326
+ }
29327
+ });
29328
+
29329
+ // mermaid.ts
29330
+ init_define_import_meta_env();
29331
+ var isMaybeMermaidDefinition = (text) => {
29332
+ const chartTypes = [
29333
+ "flowchart",
29334
+ "sequenceDiagram",
29335
+ "classDiagram",
29336
+ "stateDiagram",
29337
+ "stateDiagram-v2",
29338
+ "erDiagram",
29339
+ "journey",
29340
+ "gantt",
29341
+ "pie",
29342
+ "quadrantChart",
29343
+ "requirementDiagram",
29344
+ "gitGraph",
29345
+ "C4Context",
29346
+ "mindmap",
29347
+ "timeline",
29348
+ "zenuml",
29349
+ "sankey",
29350
+ "xychart",
29351
+ "block"
29352
+ ];
29353
+ const re = new RegExp(
29354
+ `^(?:%%{.*?}%%[\\s\\n]*)?\\b${chartTypes.map((x3) => `${x3}(-beta)?`).join("|")}\\b`
29355
+ );
29356
+ return re.test(text.trim());
29357
+ };
29358
+
29215
29359
  // components/App.tsx
29216
29360
  var import_jsx_runtime128 = __toESM(require_jsx_runtime(), 1);
29217
29361
  var AppContext = import_react100.default.createContext(null);
@@ -29417,10 +29561,7 @@ var App = class _App extends import_react100.default.Component {
29417
29561
  container: this.excalidrawContainerRef.current,
29418
29562
  id: this.id
29419
29563
  };
29420
- this.fonts = new Fonts({
29421
- scene: this.scene,
29422
- onSceneUpdated: this.onSceneUpdated
29423
- });
29564
+ this.fonts = new Fonts({ scene: this.scene });
29424
29565
  this.history = new History();
29425
29566
  this.actionManager.registerAll(actions);
29426
29567
  this.actionManager.registerAction(
@@ -29579,7 +29720,7 @@ var App = class _App extends import_react100.default.Component {
29579
29720
  return false;
29580
29721
  });
29581
29722
  if (updated) {
29582
- this.scene.informMutation();
29723
+ this.scene.triggerUpdate();
29583
29724
  }
29584
29725
  this.iFrameRefs.forEach((ref, id) => {
29585
29726
  if (!iframeLikes.has(id)) {
@@ -29986,9 +30127,9 @@ var App = class _App extends import_react100.default.Component {
29986
30127
  render() {
29987
30128
  const selectedElements = this.scene.getSelectedElements(this.state);
29988
30129
  const { renderTopRightUI, renderCustomStats } = this.props;
29989
- const versionNonce = this.scene.getVersionNonce();
30130
+ const sceneNonce = this.scene.getSceneNonce();
29990
30131
  const { elementsMap, visibleElements } = this.renderer.getRenderableElements({
29991
- versionNonce,
30132
+ sceneNonce,
29992
30133
  zoom: this.state.zoom,
29993
30134
  offsetLeft: this.state.offsetLeft,
29994
30135
  offsetTop: this.state.offsetTop,
@@ -30185,7 +30326,7 @@ var App = class _App extends import_react100.default.Component {
30185
30326
  elementsMap,
30186
30327
  allElementsMap,
30187
30328
  visibleElements,
30188
- versionNonce,
30329
+ sceneNonce,
30189
30330
  selectionNonce: this.state.selectionElement?.versionNonce,
30190
30331
  scale: window.devicePixelRatio,
30191
30332
  appState: this.state,
@@ -30206,8 +30347,9 @@ var App = class _App extends import_react100.default.Component {
30206
30347
  canvas: this.interactiveCanvas,
30207
30348
  elementsMap,
30208
30349
  visibleElements,
30350
+ allElementsMap,
30209
30351
  selectedElements,
30210
- versionNonce,
30352
+ sceneNonce,
30211
30353
  selectionNonce: this.state.selectionElement?.versionNonce,
30212
30354
  scale: window.devicePixelRatio,
30213
30355
  appState: this.state,
@@ -30301,7 +30443,7 @@ var App = class _App extends import_react100.default.Component {
30301
30443
  );
30302
30444
  }
30303
30445
  this.magicGenerations.set(frameElement.id, data);
30304
- this.onSceneUpdated();
30446
+ this.triggerRender();
30305
30447
  };
30306
30448
  getTextFromElements(elements) {
30307
30449
  const text = elements.reduce((acc, element) => {
@@ -30785,7 +30927,7 @@ var App = class _App extends import_react100.default.Component {
30785
30927
  this.store.onStoreIncrementEmitter.on((increment) => {
30786
30928
  this.history.record(increment.elementsChange, increment.appStateChange);
30787
30929
  });
30788
- this.scene.addCallback(this.onSceneUpdated);
30930
+ this.scene.onUpdate(this.triggerRender);
30789
30931
  this.addEventListeners();
30790
30932
  if (this.props.autoFocus && this.excalidrawContainerRef.current) {
30791
30933
  this.focusContainer();
@@ -30821,6 +30963,7 @@ var App = class _App extends import_react100.default.Component {
30821
30963
  componentWillUnmount() {
30822
30964
  this.renderer.destroy();
30823
30965
  this.scene = new Scene_default();
30966
+ this.fonts = new Fonts({ scene: this.scene });
30824
30967
  this.renderer = new Renderer(this.scene);
30825
30968
  this.files = {};
30826
30969
  this.imageCache.clear();
@@ -30885,7 +31028,7 @@ var App = class _App extends import_react100.default.Component {
30885
31028
  addEventListener(document, "keyup" /* KEYUP */, this.onKeyUp, { passive: true }),
30886
31029
  addEventListener(
30887
31030
  document,
30888
- "mousemove" /* MOUSE_MOVE */,
31031
+ "pointermove" /* POINTER_MOVE */,
30889
31032
  this.updateCurrentCursorPosition
30890
31033
  ),
30891
31034
  // rerender text elements on font load to fix #637 && #1553
@@ -30914,6 +31057,7 @@ var App = class _App extends import_react100.default.Component {
30914
31057
  ),
30915
31058
  addEventListener(window, "focus" /* FOCUS */, () => {
30916
31059
  this.maybeCleanupAfterMissingPointerUp(null);
31060
+ this.triggerRender(true);
30917
31061
  })
30918
31062
  );
30919
31063
  if (this.state.viewModeEnabled) {
@@ -31245,6 +31389,27 @@ var App = class _App extends import_react100.default.Component {
31245
31389
  retainSeed: isPlainPaste
31246
31390
  });
31247
31391
  } else if (data.text) {
31392
+ if (data.text && isMaybeMermaidDefinition(data.text)) {
31393
+ const api = await import("./excalidraw-assets-dev/dist-6QVAH5JA.js");
31394
+ try {
31395
+ const { elements: skeletonElements, files } = await api.parseMermaidToExcalidraw(data.text, {
31396
+ fontSize: DEFAULT_FONT_SIZE
31397
+ });
31398
+ const elements = convertToExcalidrawElements(skeletonElements, {
31399
+ regenerateIds: true
31400
+ });
31401
+ this.addElementsFromPasteOrLibrary({
31402
+ elements,
31403
+ files,
31404
+ position: "cursor"
31405
+ });
31406
+ return;
31407
+ } catch (err) {
31408
+ console.warn(
31409
+ `parsing pasted text as mermaid definition failed: ${err.message}`
31410
+ );
31411
+ }
31412
+ }
31248
31413
  const nonEmptyLines = normalizeEOL(data.text).split(/\n+/).map((s3) => s3.trim()).filter(Boolean);
31249
31414
  const embbeddableUrls = nonEmptyLines.map((str) => maybeParseEmbedSrc(str)).filter((string) => {
31250
31415
  return embeddableURLValidator(string, this.props.validateEmbeddable) && (/^(http|https):\/\/[^\s/$.?#].[^\s]*$/.test(string) || getEmbedLink(string)?.type === "video");
@@ -31464,28 +31629,42 @@ var App = class _App extends import_react100.default.Component {
31464
31629
  text,
31465
31630
  fontSize: this.state.currentItemFontSize,
31466
31631
  fontFamily: this.state.currentItemFontFamily,
31467
- textAlign: this.state.currentItemTextAlign,
31632
+ textAlign: DEFAULT_TEXT_ALIGN,
31468
31633
  verticalAlign: DEFAULT_VERTICAL_ALIGN,
31469
31634
  locked: false
31470
31635
  };
31636
+ const fontString = getFontString({
31637
+ fontSize: textElementProps.fontSize,
31638
+ fontFamily: textElementProps.fontFamily
31639
+ });
31640
+ const lineHeight = getDefaultLineHeight(textElementProps.fontFamily);
31641
+ const [x1, , x22] = getVisibleSceneBounds(this.state);
31642
+ const maxTextWidth = Math.max(Math.min((x22 - x1) * 0.5, 800), 200);
31471
31643
  const LINE_GAP = 10;
31472
31644
  let currentY = y3;
31473
31645
  const lines = isPlainPaste ? [text] : text.split("\n");
31474
31646
  const textElements = lines.reduce(
31475
31647
  (acc, line, idx) => {
31476
- const text2 = line.trim();
31477
- const lineHeight = getDefaultLineHeight(textElementProps.fontFamily);
31478
- if (text2.length) {
31648
+ const originalText = line.trim();
31649
+ if (originalText.length) {
31479
31650
  const topLayerFrame = this.getTopLayerFrameAtSceneCoords({
31480
31651
  x: x3,
31481
31652
  y: currentY
31482
31653
  });
31654
+ let metrics = measureText(originalText, fontString, lineHeight);
31655
+ const isTextWrapped = metrics.width > maxTextWidth;
31656
+ const text2 = isTextWrapped ? wrapText(originalText, fontString, maxTextWidth) : originalText;
31657
+ metrics = isTextWrapped ? measureText(text2, fontString, lineHeight) : metrics;
31658
+ const startX = x3 - metrics.width / 2;
31659
+ const startY = currentY - metrics.height / 2;
31483
31660
  const element = newTextElement({
31484
31661
  ...textElementProps,
31485
- x: x3,
31486
- y: currentY,
31662
+ x: startX,
31663
+ y: startY,
31487
31664
  text: text2,
31665
+ originalText,
31488
31666
  lineHeight,
31667
+ autoResize: !isTextWrapped,
31489
31668
  frameId: topLayerFrame ? topLayerFrame.id : null
31490
31669
  });
31491
31670
  acc.push(element);
@@ -31698,7 +31877,7 @@ var App = class _App extends import_react100.default.Component {
31698
31877
  ShapeCache.delete(element);
31699
31878
  }
31700
31879
  });
31701
- this.scene.informMutation();
31880
+ this.scene.triggerUpdate();
31702
31881
  this.addNewImagesToImageCache();
31703
31882
  }
31704
31883
  );
@@ -31738,8 +31917,12 @@ var App = class _App extends import_react100.default.Component {
31738
31917
  }
31739
31918
  }
31740
31919
  );
31741
- onSceneUpdated = () => {
31742
- this.setState({});
31920
+ triggerRender = (force) => {
31921
+ if (force === true) {
31922
+ this.scene.triggerUpdate();
31923
+ } else {
31924
+ this.setState({});
31925
+ }
31743
31926
  };
31744
31927
  /**
31745
31928
  * @returns whether the menu was toggled on or off
@@ -32142,21 +32325,22 @@ var App = class _App extends import_react100.default.Component {
32142
32325
  isExistingElement = false
32143
32326
  }) {
32144
32327
  const elementsMap = this.scene.getElementsMapIncludingDeleted();
32145
- const updateElement = (text, originalText, isDeleted) => {
32328
+ const updateElement = (nextOriginalText, isDeleted) => {
32146
32329
  this.scene.replaceAllElements([
32147
32330
  // Not sure why we include deleted elements as well hence using deleted elements map
32148
32331
  ...this.scene.getElementsIncludingDeleted().map((_element) => {
32149
32332
  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
- );
32333
+ return newElementWith(_element, {
32334
+ originalText: nextOriginalText,
32335
+ isDeleted: isDeleted ?? _element.isDeleted,
32336
+ // returns (wrapped) text and new dimensions
32337
+ ...refreshTextDimensions(
32338
+ _element,
32339
+ getContainerElement(_element, elementsMap),
32340
+ elementsMap,
32341
+ nextOriginalText
32342
+ )
32343
+ });
32160
32344
  }
32161
32345
  return _element;
32162
32346
  })
@@ -32178,15 +32362,15 @@ var App = class _App extends import_react100.default.Component {
32178
32362
  viewportY - this.state.offsetTop
32179
32363
  ];
32180
32364
  },
32181
- onChange: withBatchedUpdates((text) => {
32182
- updateElement(text, text, false);
32365
+ onChange: withBatchedUpdates((nextOriginalText) => {
32366
+ updateElement(nextOriginalText, false);
32183
32367
  if (isNonDeletedElement(element)) {
32184
32368
  updateBoundElements(element, elementsMap);
32185
32369
  }
32186
32370
  }),
32187
- onSubmit: withBatchedUpdates(({ text, viaKeyboard, originalText }) => {
32188
- const isDeleted = !text.trim();
32189
- updateElement(text, originalText, isDeleted);
32371
+ onSubmit: withBatchedUpdates(({ viaKeyboard, nextOriginalText }) => {
32372
+ const isDeleted = !nextOriginalText.trim();
32373
+ updateElement(nextOriginalText, isDeleted);
32190
32374
  if (!isDeleted && viaKeyboard) {
32191
32375
  const elementIdToSelect = element.containerId ? element.containerId : element.id;
32192
32376
  this.setState((prevState) => ({
@@ -32218,10 +32402,15 @@ var App = class _App extends import_react100.default.Component {
32218
32402
  }),
32219
32403
  element,
32220
32404
  excalidrawContainer: this.excalidrawContainerRef.current,
32221
- app: this
32405
+ app: this,
32406
+ // when text is selected, it's hard (at least on iOS) to re-position the
32407
+ // caret (i.e. deselect). There's not much use for always selecting
32408
+ // the text on edit anyway (and users can select-all from contextmenu
32409
+ // if needed)
32410
+ autoSelect: !this.device.isTouchScreen
32222
32411
  });
32223
32412
  this.deselectElements();
32224
- updateElement(element.text, element.originalText, false);
32413
+ updateElement(element.originalText, false);
32225
32414
  }
32226
32415
  deselectElements() {
32227
32416
  this.setState({
@@ -32420,7 +32609,8 @@ var App = class _App extends import_react100.default.Component {
32420
32609
  sceneX,
32421
32610
  sceneY,
32422
32611
  insertAtParentCenter = true,
32423
- container
32612
+ container,
32613
+ autoEdit = true
32424
32614
  }) => {
32425
32615
  let shouldBindToContainer = false;
32426
32616
  let parentCenterPosition = insertAtParentCenter && this.getTextWysiwygSnappedToCenterPosition(
@@ -32523,12 +32713,16 @@ var App = class _App extends import_react100.default.Component {
32523
32713
  this.scene.insertElement(element);
32524
32714
  }
32525
32715
  }
32526
- this.setState({
32527
- editingElement: element
32528
- });
32529
- this.handleTextWysiwyg(element, {
32530
- isExistingElement: !!existingTextElement
32531
- });
32716
+ if (autoEdit || existingTextElement || container) {
32717
+ this.handleTextWysiwyg(element, {
32718
+ isExistingElement: !!existingTextElement
32719
+ });
32720
+ } else {
32721
+ this.setState({
32722
+ draggingElement: element,
32723
+ multiElement: null
32724
+ });
32725
+ }
32532
32726
  };
32533
32727
  handleCanvasDoubleClick = (event) => {
32534
32728
  if (this.state.multiElement) {
@@ -32720,8 +32914,11 @@ var App = class _App extends import_react100.default.Component {
32720
32914
  );
32721
32915
  this.translateCanvas({
32722
32916
  zoom: zoomState.zoom,
32723
- scrollX: zoomState.scrollX + deltaX / nextZoom,
32724
- scrollY: zoomState.scrollY + deltaY / nextZoom,
32917
+ // 2x multiplier is just a magic number that makes this work correctly
32918
+ // on touchscreen devices (note: if we get report that panning is slower/faster
32919
+ // than actual movement, consider swapping with devicePixelRatio)
32920
+ scrollX: zoomState.scrollX + 2 * (deltaX / nextZoom),
32921
+ scrollY: zoomState.scrollY + 2 * (deltaY / nextZoom),
32725
32922
  shouldCacheIgnoreZoom: true
32726
32923
  });
32727
32924
  });
@@ -33070,7 +33267,7 @@ var App = class _App extends import_react100.default.Component {
33070
33267
  }
33071
33268
  }
33072
33269
  this.elementsPendingErasure = new Set(this.elementsPendingErasure);
33073
- this.onSceneUpdated();
33270
+ this.triggerRender();
33074
33271
  }
33075
33272
  };
33076
33273
  // set touch moving for mobile context menu
@@ -33264,7 +33461,6 @@ var App = class _App extends import_react100.default.Component {
33264
33461
  }
33265
33462
  if (this.state.activeTool.type === "text") {
33266
33463
  this.handleTextOnPointerDown(event, pointerDownState);
33267
- return;
33268
33464
  } else if (this.state.activeTool.type === "arrow" || this.state.activeTool.type === "line") {
33269
33465
  this.handleLinearElementOnPointerDown(
33270
33466
  event,
@@ -33834,7 +34030,8 @@ var App = class _App extends import_react100.default.Component {
33834
34030
  sceneX,
33835
34031
  sceneY,
33836
34032
  insertAtParentCenter: !event.altKey,
33837
- container
34033
+ container,
34034
+ autoEdit: false
33838
34035
  });
33839
34036
  resetCursor(this.interactiveCanvas);
33840
34037
  if (!this.state.activeTool.locked) {
@@ -34839,6 +35036,24 @@ var App = class _App extends import_react100.default.Component {
34839
35036
  }
34840
35037
  return;
34841
35038
  }
35039
+ if (isTextElement(draggingElement)) {
35040
+ const minWidth = getMinTextElementWidth(
35041
+ getFontString({
35042
+ fontSize: draggingElement.fontSize,
35043
+ fontFamily: draggingElement.fontFamily
35044
+ }),
35045
+ draggingElement.lineHeight
35046
+ );
35047
+ if (draggingElement.width < minWidth) {
35048
+ mutateElement(draggingElement, {
35049
+ autoResize: true
35050
+ });
35051
+ }
35052
+ this.resetCursor();
35053
+ this.handleTextWysiwyg(draggingElement, {
35054
+ isExistingElement: true
35055
+ });
35056
+ }
34842
35057
  if (activeTool.type !== "selection" && draggingElement && isInvisiblySmallElement(draggingElement)) {
34843
35058
  this.updateScene({
34844
35059
  elements: this.scene.getElementsIncludingDeleted().filter((el) => el.id !== draggingElement.id),
@@ -34874,7 +35089,7 @@ var App = class _App extends import_react100.default.Component {
34874
35089
  [linearElement],
34875
35090
  this.scene.getNonDeletedElementsMap()
34876
35091
  );
34877
- this.scene.informMutation();
35092
+ this.scene.triggerUpdate();
34878
35093
  }
34879
35094
  }
34880
35095
  }
@@ -35227,7 +35442,7 @@ var App = class _App extends import_react100.default.Component {
35227
35442
  }
35228
35443
  restoreReadyToEraseElements = () => {
35229
35444
  this.elementsPendingErasure = /* @__PURE__ */ new Set();
35230
- this.onSceneUpdated();
35445
+ this.triggerRender();
35231
35446
  };
35232
35447
  eraseElements = () => {
35233
35448
  let didChange = false;
@@ -35542,7 +35757,7 @@ var App = class _App extends import_react100.default.Component {
35542
35757
  files
35543
35758
  );
35544
35759
  if (updatedFiles.size) {
35545
- this.scene.informMutation();
35760
+ this.scene.triggerUpdate();
35546
35761
  }
35547
35762
  }
35548
35763
  };
@@ -35828,7 +36043,8 @@ var App = class _App extends import_react100.default.Component {
35828
36043
  distance(pointerDownState.origin.x, pointerCoords.x),
35829
36044
  distance(pointerDownState.origin.y, pointerCoords.y),
35830
36045
  shouldMaintainAspectRatio(event),
35831
- shouldResizeFromCenter(event)
36046
+ shouldResizeFromCenter(event),
36047
+ this.state.zoom.value
35832
36048
  );
35833
36049
  } else {
35834
36050
  let [gridX, gridY] = getGridPoint(
@@ -35869,6 +36085,7 @@ var App = class _App extends import_react100.default.Component {
35869
36085
  distance(pointerDownState.originInGrid.y, gridY),
35870
36086
  isImageElement(draggingElement) ? !shouldMaintainAspectRatio(event) : shouldMaintainAspectRatio(event),
35871
36087
  shouldResizeFromCenter(event),
36088
+ this.state.zoom.value,
35872
36089
  aspectRatio,
35873
36090
  this.state.originSnapOffset
35874
36091
  );
@@ -36015,6 +36232,7 @@ var App = class _App extends import_react100.default.Component {
36015
36232
  return [actionCopy, ...options];
36016
36233
  }
36017
36234
  return [
36235
+ CONTEXT_MENU_SEPARATOR,
36018
36236
  actionCut,
36019
36237
  actionCopy,
36020
36238
  actionPaste,
@@ -36027,6 +36245,7 @@ var App = class _App extends import_react100.default.Component {
36027
36245
  actionPasteStyles,
36028
36246
  CONTEXT_MENU_SEPARATOR,
36029
36247
  actionGroup,
36248
+ actionTextAutoResize,
36030
36249
  actionUnbindText,
36031
36250
  actionBindText,
36032
36251
  actionWrapTextInContainer,