@elixpo/lixsketch 5.4.7 → 5.5.1

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 (218) hide show
  1. package/dist/react/AIRenderer-M4LMJ66J.js +1105 -0
  2. package/dist/react/AIRenderer-M4LMJ66J.js.map +7 -0
  3. package/dist/react/Arrow-BJDAXPMH.js +8 -0
  4. package/dist/react/Arrow-BJDAXPMH.js.map +7 -0
  5. package/dist/react/Circle-KPG7TKIX.js +8 -0
  6. package/dist/react/Circle-KPG7TKIX.js.map +7 -0
  7. package/dist/react/CodeShape-K4DIZAIV.js +8 -0
  8. package/dist/react/CodeShape-K4DIZAIV.js.map +7 -0
  9. package/dist/react/CopyPaste-4CKJRHJ3.js +569 -0
  10. package/dist/react/CopyPaste-4CKJRHJ3.js.map +7 -0
  11. package/dist/react/EraserTrail-PA6IQLQN.js +20 -0
  12. package/dist/react/EraserTrail-PA6IQLQN.js.map +7 -0
  13. package/dist/react/EventDispatcher-SRFH6LQZ.js +463 -0
  14. package/dist/react/EventDispatcher-SRFH6LQZ.js.map +7 -0
  15. package/dist/react/Frame-6UR5XPAX.js +10 -0
  16. package/dist/react/Frame-6UR5XPAX.js.map +7 -0
  17. package/dist/react/FreehandStroke-CK24NUT3.js +8 -0
  18. package/dist/react/FreehandStroke-CK24NUT3.js.map +7 -0
  19. package/dist/react/GraphEngine-QE5B2PMR.js +340 -0
  20. package/dist/react/GraphEngine-QE5B2PMR.js.map +7 -0
  21. package/dist/react/GraphMathParser-2WMTY4IE.js +10 -0
  22. package/dist/react/GraphMathParser-2WMTY4IE.js.map +7 -0
  23. package/dist/react/IconShape-KMIA6T2H.js +10 -0
  24. package/dist/react/IconShape-KMIA6T2H.js.map +7 -0
  25. package/dist/react/ImageShape-CHHGBXUQ.js +10 -0
  26. package/dist/react/ImageShape-CHHGBXUQ.js.map +7 -0
  27. package/dist/react/LayerOrder-XCHF4MSS.js +66 -0
  28. package/dist/react/LayerOrder-XCHF4MSS.js.map +7 -0
  29. package/dist/react/Line-2KOQEEGA.js +10 -0
  30. package/dist/react/Line-2KOQEEGA.js.map +7 -0
  31. package/dist/react/LixScriptParser-JYWJX375.js +1033 -0
  32. package/dist/react/LixScriptParser-JYWJX375.js.map +7 -0
  33. package/dist/react/MermaidFlowchartRenderer-4QBAZ5MB.js +412 -0
  34. package/dist/react/MermaidFlowchartRenderer-4QBAZ5MB.js.map +7 -0
  35. package/dist/react/MermaidSequenceParser-UKG2NSZY.js +8 -0
  36. package/dist/react/MermaidSequenceParser-UKG2NSZY.js.map +7 -0
  37. package/dist/react/MermaidSequenceRenderer-HPLHY5II.js +387 -0
  38. package/dist/react/MermaidSequenceRenderer-HPLHY5II.js.map +7 -0
  39. package/dist/react/Rectangle-6FE7RN6W.js +8 -0
  40. package/dist/react/Rectangle-6FE7RN6W.js.map +7 -0
  41. package/dist/react/ResizeCode-WAMDTKSW.js +2 -0
  42. package/dist/react/ResizeCode-WAMDTKSW.js.map +7 -0
  43. package/dist/react/ResizeShapes-LKEQTITP.js +250 -0
  44. package/dist/react/ResizeShapes-LKEQTITP.js.map +7 -0
  45. package/dist/react/SceneSerializer-4U4B24JM.js +739 -0
  46. package/dist/react/SceneSerializer-4U4B24JM.js.map +7 -0
  47. package/dist/react/Selection-VVUHY5YJ.js +34 -0
  48. package/dist/react/Selection-VVUHY5YJ.js.map +7 -0
  49. package/dist/react/SketchEngine-YA4RG3KX.js +413 -0
  50. package/dist/react/SketchEngine-YA4RG3KX.js.map +7 -0
  51. package/dist/react/TextShape-54ZJ66E3.js +8 -0
  52. package/dist/react/TextShape-54ZJ66E3.js.map +7 -0
  53. package/dist/react/UndoRedo-M2JZUBKN.js +30 -0
  54. package/dist/react/UndoRedo-M2JZUBKN.js.map +7 -0
  55. package/dist/react/ZoomPan-U3TXC5AJ.js +186 -0
  56. package/dist/react/ZoomPan-U3TXC5AJ.js.map +7 -0
  57. package/dist/react/arrowTool-CWEMIRXK.js +17 -0
  58. package/dist/react/arrowTool-CWEMIRXK.js.map +7 -0
  59. package/dist/react/boxicons-5JNJQPUP.woff +0 -0
  60. package/dist/react/boxicons-6NVWQSCQ.eot +0 -0
  61. package/dist/react/boxicons-AQ5N6WP7.woff2 +0 -0
  62. package/dist/react/boxicons-GDR4JU3X.ttf +0 -0
  63. package/dist/react/boxicons-NNFHLMOR.svg +1660 -0
  64. package/dist/react/chunk-2OQXI7QB.js +564 -0
  65. package/dist/react/chunk-2OQXI7QB.js.map +7 -0
  66. package/dist/react/chunk-5OUBTHHO.js +187 -0
  67. package/dist/react/chunk-5OUBTHHO.js.map +7 -0
  68. package/dist/react/chunk-6PFXZKL2.js +628 -0
  69. package/dist/react/chunk-6PFXZKL2.js.map +7 -0
  70. package/dist/react/chunk-6SFNWGGZ.js +453 -0
  71. package/dist/react/chunk-6SFNWGGZ.js.map +7 -0
  72. package/dist/react/chunk-7TZSIR6V.js +210 -0
  73. package/dist/react/chunk-7TZSIR6V.js.map +7 -0
  74. package/dist/react/chunk-AJAZC37E.js +662 -0
  75. package/dist/react/chunk-AJAZC37E.js.map +7 -0
  76. package/dist/react/chunk-BU4D2WYQ.js +1213 -0
  77. package/dist/react/chunk-BU4D2WYQ.js.map +7 -0
  78. package/dist/react/chunk-BZE4CEBG.js +747 -0
  79. package/dist/react/chunk-BZE4CEBG.js.map +7 -0
  80. package/dist/react/chunk-CB2RANCB.js +212 -0
  81. package/dist/react/chunk-CB2RANCB.js.map +7 -0
  82. package/dist/react/chunk-DNAG7ML6.js +128 -0
  83. package/dist/react/chunk-DNAG7ML6.js.map +7 -0
  84. package/dist/react/chunk-HZWLPXFN.js +198 -0
  85. package/dist/react/chunk-HZWLPXFN.js.map +7 -0
  86. package/dist/react/chunk-I6HCCIXB.js +572 -0
  87. package/dist/react/chunk-I6HCCIXB.js.map +7 -0
  88. package/dist/react/chunk-IZ5OGWGW.js +235 -0
  89. package/dist/react/chunk-IZ5OGWGW.js.map +7 -0
  90. package/dist/react/chunk-JJCJNLNN.js +1490 -0
  91. package/dist/react/chunk-JJCJNLNN.js.map +7 -0
  92. package/dist/react/chunk-JQ3IRFDP.js +222 -0
  93. package/dist/react/chunk-JQ3IRFDP.js.map +7 -0
  94. package/dist/react/chunk-L5OARZJ7.js +1134 -0
  95. package/dist/react/chunk-L5OARZJ7.js.map +7 -0
  96. package/dist/react/chunk-LHRTXNVE.js +1600 -0
  97. package/dist/react/chunk-LHRTXNVE.js.map +7 -0
  98. package/dist/react/chunk-Q2GA4MBZ.js +1653 -0
  99. package/dist/react/chunk-Q2GA4MBZ.js.map +7 -0
  100. package/dist/react/chunk-QNSE5BAY.js +519 -0
  101. package/dist/react/chunk-QNSE5BAY.js.map +7 -0
  102. package/dist/react/chunk-QXFBG7OM.js +191 -0
  103. package/dist/react/chunk-QXFBG7OM.js.map +7 -0
  104. package/dist/react/chunk-T6ZVV5ZO.js +714 -0
  105. package/dist/react/chunk-T6ZVV5ZO.js.map +7 -0
  106. package/dist/react/chunk-URB6MY46.js +974 -0
  107. package/dist/react/chunk-URB6MY46.js.map +7 -0
  108. package/dist/react/chunk-VN62KLOC.js +931 -0
  109. package/dist/react/chunk-VN62KLOC.js.map +7 -0
  110. package/dist/react/chunk-VQVEPJSA.js +158 -0
  111. package/dist/react/chunk-VQVEPJSA.js.map +7 -0
  112. package/dist/react/chunk-XISEAGBV.js +1632 -0
  113. package/dist/react/chunk-XISEAGBV.js.map +7 -0
  114. package/dist/react/chunk-YKJUBFHE.js +575 -0
  115. package/dist/react/chunk-YKJUBFHE.js.map +7 -0
  116. package/dist/react/circleTool-NUATSEI2.js +14 -0
  117. package/dist/react/circleTool-NUATSEI2.js.map +7 -0
  118. package/dist/react/codeTool-MHCB2FNB.js +38 -0
  119. package/dist/react/codeTool-MHCB2FNB.js.map +7 -0
  120. package/dist/react/eraserTool-2GS2LQCS.js +103 -0
  121. package/dist/react/eraserTool-2GS2LQCS.js.map +7 -0
  122. package/dist/react/frameTool-PWVKKXSP.js +14 -0
  123. package/dist/react/frameTool-PWVKKXSP.js.map +7 -0
  124. package/dist/react/freehandTool-SLYGASVP.js +14 -0
  125. package/dist/react/freehandTool-SLYGASVP.js.map +7 -0
  126. package/dist/react/iconTool-GPZ7O7NH.js +18 -0
  127. package/dist/react/iconTool-GPZ7O7NH.js.map +7 -0
  128. package/dist/react/imageTool-D5R4XGEU.js +15 -0
  129. package/dist/react/imageTool-D5R4XGEU.js.map +7 -0
  130. package/dist/react/index.js +5349 -0
  131. package/dist/react/index.js.map +7 -0
  132. package/dist/react/laserTool-XVMV63AV.js +239 -0
  133. package/dist/react/laserTool-XVMV63AV.js.map +7 -0
  134. package/dist/react/lineTool-WNI5EMZ5.js +14 -0
  135. package/dist/react/lineTool-WNI5EMZ5.js.map +7 -0
  136. package/dist/react/lixCode-OJMVUXXI.ttf +0 -0
  137. package/dist/react/lixDefault-JEAPAUJL.ttf +0 -0
  138. package/dist/react/lixFancy-BPXYM5CQ.ttf +0 -0
  139. package/dist/react/lixFont-CPKEUDVM.woff2 +0 -0
  140. package/dist/react/rectangleTool-U7DZ6ZTA.js +14 -0
  141. package/dist/react/rectangleTool-U7DZ6ZTA.js.map +7 -0
  142. package/dist/react/styles.css +6627 -0
  143. package/dist/react/textTool-M7S6F3VC.js +21 -0
  144. package/dist/react/textTool-M7S6F3VC.js.map +7 -0
  145. package/package.json +39 -5
  146. package/src/EngineShortcuts.js +83 -27
  147. package/src/core/AIRenderer.js +30 -18
  148. package/src/core/GraphEngine.js +64 -41
  149. package/src/core/MermaidFlowchartRenderer.js +153 -88
  150. package/src/core/MermaidSequenceRenderer.js +168 -93
  151. package/src/core/SceneSerializer.js +86 -2
  152. package/src/core/Selection.js +119 -19
  153. package/src/core/UndoRedo.js +82 -15
  154. package/src/core/ZoomPan.js +15 -5
  155. package/src/index.js +14 -1
  156. package/src/react/LixSketchCanvas.jsx +347 -0
  157. package/src/react/components/AppMenu.jsx +482 -0
  158. package/src/react/components/Footer.jsx +85 -0
  159. package/src/react/components/Toolbar.jsx +140 -0
  160. package/src/react/components/canvas/CanvasLoadingOverlay.jsx +76 -0
  161. package/src/react/components/canvas/ContextMenu.jsx +466 -0
  162. package/src/react/components/canvas/FindBar.jsx +177 -0
  163. package/src/react/components/canvas/ImageSourcePicker.jsx +133 -0
  164. package/src/react/components/canvas/MultiSelectActions.jsx +120 -0
  165. package/src/react/components/canvas/SVGCanvas.jsx +145 -0
  166. package/src/react/components/canvas/SaveStatusIndicator.jsx +29 -0
  167. package/src/react/components/modals/CanvasPropertiesModal.jsx +358 -0
  168. package/src/react/components/modals/CommandPalette.jsx +307 -0
  169. package/src/react/components/modals/ExportImageModal.jsx +287 -0
  170. package/src/react/components/modals/GraphModal.jsx +174 -0
  171. package/src/react/components/modals/HelpModal.jsx +208 -0
  172. package/src/react/components/modals/LixScriptModal.jsx +130 -0
  173. package/src/react/components/modals/ShortcutsModal.jsx +125 -0
  174. package/src/react/components/sidebars/ArrowSidebar.jsx +173 -0
  175. package/src/react/components/sidebars/CircleSidebar.jsx +134 -0
  176. package/src/react/components/sidebars/FrameSidebar.jsx +263 -0
  177. package/src/react/components/sidebars/IconSidebar.jsx +279 -0
  178. package/src/react/components/sidebars/ImageSidebar.jsx +69 -0
  179. package/src/react/components/sidebars/LineSidebar.jsx +109 -0
  180. package/src/react/components/sidebars/PaintbrushSidebar.jsx +119 -0
  181. package/src/react/components/sidebars/RectangleSidebar.jsx +165 -0
  182. package/src/react/components/sidebars/ShapeSidebar.jsx +130 -0
  183. package/src/react/components/sidebars/TextSidebar.jsx +174 -0
  184. package/src/react/hooks/inertStores.js +58 -0
  185. package/src/react/hooks/useSketchEngine.js +76 -0
  186. package/src/react/hooks/useTranslation.js +92 -0
  187. package/src/react/index.js +41 -0
  188. package/src/react/store/useSketchStore.js +234 -0
  189. package/src/react/store/useUIStore.js +196 -0
  190. package/src/react/styles/arrowSidebar.css +263 -0
  191. package/src/react/styles/canvasStroke.css +47 -0
  192. package/src/react/styles/canvasTools.css +769 -0
  193. package/src/react/styles/circleSidebar.css +206 -0
  194. package/src/react/styles/fonts/fonts.css +29 -0
  195. package/src/react/styles/fonts/lixCode.ttf +0 -0
  196. package/src/react/styles/fonts/lixDefault.ttf +0 -0
  197. package/src/react/styles/fonts/lixDocs.ttf +0 -0
  198. package/src/react/styles/fonts/lixFancy.ttf +0 -0
  199. package/src/react/styles/fonts/lixFont.woff2 +0 -0
  200. package/src/react/styles/lineSidebar.css +207 -0
  201. package/src/react/styles/paintbrushSidebar.css +247 -0
  202. package/src/react/styles/squareSidebar.css +206 -0
  203. package/src/react/styles/writeText.css +149 -0
  204. package/src/react/styles.css +175 -0
  205. package/src/react/toolsEnum.js +20 -0
  206. package/src/react/utils/toast.js +18 -0
  207. package/src/shapes/Frame.js +7 -1
  208. package/src/shapes/Line.js +11 -5
  209. package/src/tools/arrowTool.js +30 -4
  210. package/src/tools/circleTool.js +33 -9
  211. package/src/tools/codeTool.js +5 -3
  212. package/src/tools/frameTool.js +47 -7
  213. package/src/tools/freehandTool.js +12 -4
  214. package/src/tools/iconTool.js +7 -1
  215. package/src/tools/imageTool.js +7 -1
  216. package/src/tools/lineTool.js +27 -3
  217. package/src/tools/rectangleTool.js +45 -8
  218. package/src/tools/textTool.js +23 -6
@@ -0,0 +1,1653 @@
1
+ "use client";
2
+ import {
3
+ calculateSnap,
4
+ cleanupAttachments,
5
+ clearSnapGuides
6
+ } from "./chunk-BZE4CEBG.js";
7
+ import {
8
+ pushDeleteAction,
9
+ pushFrameAttachmentAction,
10
+ pushTransformAction
11
+ } from "./chunk-L5OARZJ7.js";
12
+
13
+ // src/core/Selection.js
14
+ var isMultiSelecting = false;
15
+ var multiSelectionStart = { x: 0, y: 0 };
16
+ var multiSelectionRect = null;
17
+ var isDraggingMultiSelection = false;
18
+ function getSVGCoordsFromMouse(e) {
19
+ const viewBox = svg.viewBox.baseVal;
20
+ const rect = svg.getBoundingClientRect();
21
+ const mouseX = e.clientX - rect.left;
22
+ const mouseY = e.clientY - rect.top;
23
+ const svgX = viewBox.x + mouseX / rect.width * viewBox.width;
24
+ const svgY = viewBox.y + mouseY / rect.height * viewBox.height;
25
+ return { x: svgX, y: svgY };
26
+ }
27
+ function createMultiSelectionRect(startX, startY) {
28
+ multiSelectionRect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
29
+ multiSelectionRect.setAttribute("x", startX);
30
+ multiSelectionRect.setAttribute("y", startY);
31
+ multiSelectionRect.setAttribute("width", 0);
32
+ multiSelectionRect.setAttribute("height", 0);
33
+ multiSelectionRect.setAttribute("fill", "rgba(91, 87, 209, 0.1)");
34
+ multiSelectionRect.setAttribute("stroke", "#5B57D1");
35
+ multiSelectionRect.setAttribute("stroke-width", 1);
36
+ multiSelectionRect.setAttribute("stroke-dasharray", "4 2");
37
+ multiSelectionRect.setAttribute("style", "pointer-events: none;");
38
+ svg.appendChild(multiSelectionRect);
39
+ }
40
+ function updateMultiSelectionRect(currentX, currentY) {
41
+ if (!multiSelectionRect) return;
42
+ const x = Math.min(multiSelectionStart.x, currentX);
43
+ const y = Math.min(multiSelectionStart.y, currentY);
44
+ const width = Math.abs(currentX - multiSelectionStart.x);
45
+ const height = Math.abs(currentY - multiSelectionStart.y);
46
+ multiSelectionRect.setAttribute("x", x);
47
+ multiSelectionRect.setAttribute("y", y);
48
+ multiSelectionRect.setAttribute("width", width);
49
+ multiSelectionRect.setAttribute("height", height);
50
+ }
51
+ function removeMultiSelectionRect() {
52
+ if (multiSelectionRect && multiSelectionRect.parentNode) {
53
+ multiSelectionRect.parentNode.removeChild(multiSelectionRect);
54
+ }
55
+ multiSelectionRect = null;
56
+ clearDragSelectHighlights();
57
+ }
58
+ var dragSelectHighlights = [];
59
+ function highlightShapesInSelectionRect(currentX, currentY) {
60
+ clearDragSelectHighlights();
61
+ if (!multiSelectionStart) return;
62
+ const selBounds = {
63
+ x: Math.min(multiSelectionStart.x, currentX),
64
+ y: Math.min(multiSelectionStart.y, currentY),
65
+ width: Math.abs(currentX - multiSelectionStart.x),
66
+ height: Math.abs(currentY - multiSelectionStart.y)
67
+ };
68
+ if (selBounds.width < 5 && selBounds.height < 5) return;
69
+ if (typeof shapes !== "undefined") {
70
+ shapes.forEach((shape) => {
71
+ if (isShapeInSelectionRect(shape, selBounds)) {
72
+ if (shape.group && typeof shape.group.getBBox === "function") {
73
+ let bbox;
74
+ try {
75
+ bbox = shape.group.getBBox();
76
+ } catch {
77
+ return;
78
+ }
79
+ const overlay = document.createElementNS("http://www.w3.org/2000/svg", "rect");
80
+ overlay.setAttribute("x", bbox.x - 2);
81
+ overlay.setAttribute("y", bbox.y - 2);
82
+ overlay.setAttribute("width", bbox.width + 4);
83
+ overlay.setAttribute("height", bbox.height + 4);
84
+ overlay.setAttribute("fill", "rgba(91, 87, 209, 0.12)");
85
+ overlay.setAttribute("stroke", "#5B57D1");
86
+ overlay.setAttribute("stroke-width", "1");
87
+ overlay.setAttribute("stroke-opacity", "0.4");
88
+ overlay.setAttribute("rx", "3");
89
+ overlay.setAttribute("style", "pointer-events: none;");
90
+ overlay.setAttribute("class", "drag-select-highlight");
91
+ shape.group.appendChild(overlay);
92
+ dragSelectHighlights.push(overlay);
93
+ }
94
+ }
95
+ });
96
+ }
97
+ }
98
+ function clearDragSelectHighlights() {
99
+ dragSelectHighlights.forEach((el) => {
100
+ if (el.parentNode) el.parentNode.removeChild(el);
101
+ });
102
+ dragSelectHighlights = [];
103
+ }
104
+ function isShapeInSelectionRect(shape, selectionBounds) {
105
+ let shapeBounds;
106
+ switch (shape.shapeName) {
107
+ case "rectangle":
108
+ shapeBounds = {
109
+ x: shape.x,
110
+ y: shape.y,
111
+ width: shape.width,
112
+ height: shape.height
113
+ };
114
+ break;
115
+ case "circle":
116
+ shapeBounds = {
117
+ x: shape.x - shape.rx,
118
+ y: shape.y - shape.ry,
119
+ width: shape.rx * 2,
120
+ height: shape.ry * 2
121
+ };
122
+ break;
123
+ case "line":
124
+ shapeBounds = {
125
+ x: Math.min(shape.startPoint.x, shape.endPoint.x),
126
+ y: Math.min(shape.startPoint.y, shape.endPoint.y),
127
+ width: Math.abs(shape.endPoint.x - shape.startPoint.x),
128
+ height: Math.abs(shape.endPoint.y - shape.startPoint.y)
129
+ };
130
+ break;
131
+ case "arrow":
132
+ const minX = Math.min(shape.startPoint.x, shape.endPoint.x);
133
+ const minY = Math.min(shape.startPoint.y, shape.endPoint.y);
134
+ const maxX = Math.max(shape.startPoint.x, shape.endPoint.x);
135
+ const maxY = Math.max(shape.startPoint.y, shape.endPoint.y);
136
+ shapeBounds = {
137
+ x: minX,
138
+ y: minY,
139
+ width: maxX - minX,
140
+ height: maxY - minY
141
+ };
142
+ break;
143
+ case "freehandStroke":
144
+ shapeBounds = {
145
+ x: shape.x,
146
+ y: shape.y,
147
+ width: shape.width,
148
+ height: shape.height
149
+ };
150
+ break;
151
+ case "text":
152
+ case "code":
153
+ const textOrCodeEl = shape.group ? shape.group.querySelector("text") : null;
154
+ if (textOrCodeEl && shape.group.style.display !== "none") {
155
+ try {
156
+ const bbox = textOrCodeEl.getBBox();
157
+ const transform = shape.group.transform.baseVal.consolidate();
158
+ const matrix = transform ? transform.matrix : { e: 0, f: 0 };
159
+ shapeBounds = {
160
+ x: bbox.x + matrix.e,
161
+ y: bbox.y + matrix.f,
162
+ width: bbox.width,
163
+ height: bbox.height
164
+ };
165
+ } catch {
166
+ shapeBounds = { x: 0, y: 0, width: 0, height: 0 };
167
+ }
168
+ } else {
169
+ shapeBounds = { x: 0, y: 0, width: 0, height: 0 };
170
+ }
171
+ break;
172
+ case "image":
173
+ if (shape.element) {
174
+ shapeBounds = {
175
+ x: parseFloat(shape.element.getAttribute("x")),
176
+ y: parseFloat(shape.element.getAttribute("y")),
177
+ width: parseFloat(shape.element.getAttribute("width")),
178
+ height: parseFloat(shape.element.getAttribute("height"))
179
+ };
180
+ } else {
181
+ shapeBounds = { x: shape.x, y: shape.y, width: shape.width, height: shape.height };
182
+ }
183
+ break;
184
+ case "frame":
185
+ shapeBounds = {
186
+ x: shape.x,
187
+ y: shape.y,
188
+ width: shape.width,
189
+ height: shape.height
190
+ };
191
+ break;
192
+ case "icon":
193
+ shapeBounds = {
194
+ x: shape.x,
195
+ y: shape.y,
196
+ width: shape.width,
197
+ height: shape.height
198
+ };
199
+ break;
200
+ default:
201
+ shapeBounds = {
202
+ x: shape.x || 0,
203
+ y: shape.y || 0,
204
+ width: shape.width || 0,
205
+ height: shape.height || 0
206
+ };
207
+ }
208
+ return !(selectionBounds.x > shapeBounds.x + shapeBounds.width || selectionBounds.x + selectionBounds.width < shapeBounds.x || selectionBounds.y > shapeBounds.y + shapeBounds.height || selectionBounds.y + selectionBounds.height < shapeBounds.y);
209
+ }
210
+ var MultiSelection = class {
211
+ constructor() {
212
+ this.selectedShapes = /* @__PURE__ */ new Set();
213
+ this.group = null;
214
+ this.anchors = [];
215
+ this.outline = null;
216
+ this.rotationAnchor = null;
217
+ this.rotationLine = null;
218
+ this.selectionPadding = 12;
219
+ this.bounds = null;
220
+ this.initialPositions = /* @__PURE__ */ new Map();
221
+ this.isDragging = false;
222
+ this.isResizing = false;
223
+ this.isRotating = false;
224
+ this.resizingAnchorIndex = null;
225
+ this.dragStart = { x: 0, y: 0 };
226
+ this.rotationCenter = { x: 0, y: 0 };
227
+ this.startRotationMouseAngle = 0;
228
+ this._undoSnapshots = /* @__PURE__ */ new Map();
229
+ this.initialRotation = 0;
230
+ }
231
+ // Capture current state of all selected shapes for undo
232
+ _snapshotForUndo() {
233
+ this._undoSnapshots.clear();
234
+ this.selectedShapes.forEach((shape) => {
235
+ this._undoSnapshots.set(shape, this._captureShapeState(shape));
236
+ });
237
+ }
238
+ _captureShapeState(shape) {
239
+ switch (shape.shapeName) {
240
+ case "line":
241
+ case "arrow":
242
+ return {
243
+ startPoint: { ...shape.startPoint },
244
+ endPoint: { ...shape.endPoint },
245
+ x: shape.x,
246
+ y: shape.y,
247
+ width: shape.width || 0,
248
+ height: shape.height || 0,
249
+ rotation: shape.rotation || 0
250
+ };
251
+ case "circle":
252
+ return { x: shape.x, y: shape.y, rx: shape.rx, ry: shape.ry, rotation: shape.rotation || 0, width: shape.width, height: shape.height };
253
+ case "freehandStroke":
254
+ return { x: shape.x, y: shape.y, width: shape.width, height: shape.height, rotation: shape.rotation || 0, points: JSON.parse(JSON.stringify(shape.points)) };
255
+ default:
256
+ return { x: shape.x || 0, y: shape.y || 0, width: shape.width || 0, height: shape.height || 0, rotation: shape.rotation || 0 };
257
+ }
258
+ }
259
+ // Push undo actions for all shapes that changed
260
+ _pushUndoForAll() {
261
+ this.selectedShapes.forEach((shape) => {
262
+ const oldState = this._undoSnapshots.get(shape);
263
+ if (!oldState) return;
264
+ const newState = this._captureShapeState(shape);
265
+ const changed = Object.keys(oldState).some((key) => {
266
+ if (key === "points" || key === "startPoint" || key === "endPoint") return JSON.stringify(oldState[key]) !== JSON.stringify(newState[key]);
267
+ return oldState[key] !== newState[key];
268
+ });
269
+ if (changed) {
270
+ pushTransformAction(shape, oldState, newState);
271
+ }
272
+ });
273
+ this._undoSnapshots.clear();
274
+ }
275
+ addShape(shape) {
276
+ this.selectedShapes.add(shape);
277
+ shape.isSelected = true;
278
+ this.updateControls();
279
+ }
280
+ removeShape(shape) {
281
+ this.selectedShapes.delete(shape);
282
+ shape.isSelected = false;
283
+ if (this.selectedShapes.size === 0) {
284
+ this.clearSelection();
285
+ } else {
286
+ this.updateControls();
287
+ }
288
+ }
289
+ clearSelection() {
290
+ this.selectedShapes.forEach((shape) => {
291
+ shape.isSelected = false;
292
+ if (typeof shape.removeSelection === "function") {
293
+ shape.removeSelection();
294
+ }
295
+ });
296
+ this.selectedShapes.clear();
297
+ this.removeControls();
298
+ if (typeof currentShape !== "undefined") {
299
+ currentShape = null;
300
+ }
301
+ if (typeof disableAllSideBars === "function") {
302
+ disableAllSideBars();
303
+ }
304
+ }
305
+ selectShapesInRect(selectionBounds) {
306
+ this.clearSelection();
307
+ if (typeof shapes !== "undefined") {
308
+ shapes.forEach((shape) => {
309
+ if (isShapeInSelectionRect(shape, selectionBounds)) {
310
+ this.addShape(shape);
311
+ }
312
+ });
313
+ }
314
+ if (this.selectedShapes.size === 1) {
315
+ if (typeof currentShape !== "undefined") {
316
+ currentShape = Array.from(this.selectedShapes)[0];
317
+ }
318
+ } else {
319
+ if (typeof currentShape !== "undefined") {
320
+ currentShape = null;
321
+ }
322
+ }
323
+ }
324
+ getBounds() {
325
+ if (this.selectedShapes.size === 0) return null;
326
+ let minX = Infinity, minY = Infinity;
327
+ let maxX = -Infinity, maxY = -Infinity;
328
+ this.selectedShapes.forEach((shape) => {
329
+ const shapeBounds = this.getShapeBounds(shape);
330
+ minX = Math.min(minX, shapeBounds.x);
331
+ minY = Math.min(minY, shapeBounds.y);
332
+ maxX = Math.max(maxX, shapeBounds.x + shapeBounds.width);
333
+ maxY = Math.max(maxY, shapeBounds.y + shapeBounds.height);
334
+ });
335
+ return {
336
+ x: minX,
337
+ y: minY,
338
+ width: maxX - minX,
339
+ height: maxY - minY
340
+ };
341
+ }
342
+ getShapeBounds(shape) {
343
+ switch (shape.shapeName) {
344
+ case "rectangle":
345
+ case "frame":
346
+ return {
347
+ x: shape.x,
348
+ y: shape.y,
349
+ width: shape.width,
350
+ height: shape.height
351
+ };
352
+ case "circle":
353
+ return {
354
+ x: shape.x - shape.rx,
355
+ y: shape.y - shape.ry,
356
+ width: shape.rx * 2,
357
+ height: shape.ry * 2
358
+ };
359
+ case "line":
360
+ case "arrow":
361
+ return {
362
+ x: Math.min(shape.startPoint.x, shape.endPoint.x),
363
+ y: Math.min(shape.startPoint.y, shape.endPoint.y),
364
+ width: Math.abs(shape.endPoint.x - shape.startPoint.x),
365
+ height: Math.abs(shape.endPoint.y - shape.startPoint.y)
366
+ };
367
+ case "freehandStroke":
368
+ return {
369
+ x: shape.x,
370
+ y: shape.y,
371
+ width: shape.width,
372
+ height: shape.height
373
+ };
374
+ case "text":
375
+ const textElement = shape.group ? shape.group.querySelector("text") : null;
376
+ if (textElement) {
377
+ const bbox = textElement.getBBox();
378
+ const transform = shape.group.transform.baseVal.consolidate();
379
+ const matrix = transform ? transform.matrix : { e: 0, f: 0 };
380
+ return {
381
+ x: bbox.x + matrix.e,
382
+ y: bbox.y + matrix.f,
383
+ width: bbox.width,
384
+ height: bbox.height
385
+ };
386
+ }
387
+ return { x: 0, y: 0, width: 0, height: 0 };
388
+ case "image":
389
+ if (shape.element) {
390
+ return {
391
+ x: parseFloat(shape.element.getAttribute("x")),
392
+ y: parseFloat(shape.element.getAttribute("y")),
393
+ width: parseFloat(shape.element.getAttribute("width")),
394
+ height: parseFloat(shape.element.getAttribute("height"))
395
+ };
396
+ }
397
+ return { x: shape.x, y: shape.y, width: shape.width, height: shape.height };
398
+ case "icon":
399
+ return {
400
+ x: shape.x,
401
+ y: shape.y,
402
+ width: shape.width,
403
+ height: shape.height
404
+ };
405
+ default:
406
+ return {
407
+ x: shape.x || 0,
408
+ y: shape.y || 0,
409
+ width: shape.width || 0,
410
+ height: shape.height || 0
411
+ };
412
+ }
413
+ }
414
+ updateControls() {
415
+ this.removeControls();
416
+ if (this.selectedShapes.size === 0) return;
417
+ this.bounds = this.getBounds();
418
+ if (!this.bounds) return;
419
+ this.createControls();
420
+ }
421
+ createControls() {
422
+ this.createRotatedControls(0);
423
+ }
424
+ createOutline(x, y, width, height) {
425
+ const zoom = window.currentZoom || 1;
426
+ const outlinePoints = [
427
+ [x, y],
428
+ [x + width, y],
429
+ [x + width, y + height],
430
+ [x, y + height],
431
+ [x, y]
432
+ ];
433
+ this.outline = document.createElementNS("http://www.w3.org/2000/svg", "polyline");
434
+ this.outline.setAttribute("points", outlinePoints.map((p) => p.join(",")).join(" "));
435
+ this.outline.setAttribute("fill", "none");
436
+ this.outline.setAttribute("stroke", "#5B57D1");
437
+ this.outline.setAttribute("stroke-width", 2);
438
+ this.outline.setAttribute("stroke-dasharray", `${8 / zoom} ${4 / zoom}`);
439
+ this.outline.setAttribute("vector-effect", "non-scaling-stroke");
440
+ this.outline.setAttribute("style", "pointer-events: none;");
441
+ this.group.appendChild(this.outline);
442
+ }
443
+ createResizeAnchors(x, y, width, height) {
444
+ const zoom = window.currentZoom || 1;
445
+ const anchorSize = 12 / zoom;
446
+ const anchorPositions = [
447
+ { x, y, index: 0 },
448
+ { x: x + width, y, index: 1 },
449
+ { x, y: y + height, index: 2 },
450
+ { x: x + width, y: y + height, index: 3 },
451
+ { x: x + width / 2, y, index: 4 },
452
+ { x: x + width / 2, y: y + height, index: 5 },
453
+ { x, y: y + height / 2, index: 6 },
454
+ { x: x + width, y: y + height / 2, index: 7 }
455
+ ];
456
+ this.anchors = [];
457
+ anchorPositions.forEach((pos) => {
458
+ const anchor = document.createElementNS("http://www.w3.org/2000/svg", "rect");
459
+ anchor.setAttribute("x", pos.x - anchorSize / 2);
460
+ anchor.setAttribute("y", pos.y - anchorSize / 2);
461
+ anchor.setAttribute("width", anchorSize);
462
+ anchor.setAttribute("height", anchorSize);
463
+ anchor.setAttribute("class", "multi-selection-anchor");
464
+ anchor.setAttribute("data-index", pos.index);
465
+ anchor.setAttribute("fill", "#121212");
466
+ anchor.setAttribute("stroke", "#5B57D1");
467
+ anchor.setAttribute("stroke-width", 2);
468
+ anchor.setAttribute("vector-effect", "non-scaling-stroke");
469
+ anchor.setAttribute("style", "pointer-events: all; cursor: pointer;");
470
+ const cursors = ["nw-resize", "ne-resize", "sw-resize", "se-resize", "n-resize", "s-resize", "w-resize", "e-resize"];
471
+ anchor.style.cursor = cursors[pos.index];
472
+ anchor.addEventListener("pointerdown", (e) => this.startResize(e, pos.index));
473
+ this.group.appendChild(anchor);
474
+ this.anchors.push(anchor);
475
+ });
476
+ }
477
+ createRotationAnchor(x, y, width, height) {
478
+ const zoom = window.currentZoom || 1;
479
+ const rotationAnchorPos = { x: x + width / 2, y: y - 30 / zoom };
480
+ this.rotationAnchor = document.createElementNS("http://www.w3.org/2000/svg", "circle");
481
+ this.rotationAnchor.setAttribute("cx", rotationAnchorPos.x);
482
+ this.rotationAnchor.setAttribute("cy", rotationAnchorPos.y);
483
+ this.rotationAnchor.setAttribute("r", 8 / zoom);
484
+ this.rotationAnchor.setAttribute("class", "multi-selection-rotation-anchor");
485
+ this.rotationAnchor.setAttribute("fill", "#121212");
486
+ this.rotationAnchor.setAttribute("stroke", "#5B57D1");
487
+ this.rotationAnchor.setAttribute("stroke-width", 2);
488
+ this.rotationAnchor.setAttribute("vector-effect", "non-scaling-stroke");
489
+ this.rotationAnchor.setAttribute("style", "pointer-events: all; cursor: grab;");
490
+ this.rotationAnchor.addEventListener("pointerdown", (e) => this.startRotation(e));
491
+ this.rotationLine = document.createElementNS("http://www.w3.org/2000/svg", "line");
492
+ this.rotationLine.setAttribute("x1", rotationAnchorPos.x);
493
+ this.rotationLine.setAttribute("y1", rotationAnchorPos.y);
494
+ this.rotationLine.setAttribute("x2", x + width / 2);
495
+ this.rotationLine.setAttribute("y2", y);
496
+ this.rotationLine.setAttribute("stroke", "#5B57D1");
497
+ this.rotationLine.setAttribute("stroke-width", 1);
498
+ this.rotationLine.setAttribute("stroke-dasharray", `${3 / zoom} ${3 / zoom}`);
499
+ this.rotationLine.setAttribute("vector-effect", "non-scaling-stroke");
500
+ this.rotationLine.setAttribute("style", "pointer-events: none;");
501
+ this.group.appendChild(this.rotationLine);
502
+ this.group.appendChild(this.rotationAnchor);
503
+ }
504
+ removeControls() {
505
+ if (this.group && this.group.parentNode) {
506
+ this.group.parentNode.removeChild(this.group);
507
+ }
508
+ this.group = null;
509
+ this.anchors = [];
510
+ this.outline = null;
511
+ this.rotationAnchor = null;
512
+ this.rotationLine = null;
513
+ }
514
+ isPointInBounds(x, y) {
515
+ if (!this.bounds) return false;
516
+ const padding = this.selectionPadding;
517
+ return x >= this.bounds.x - padding && x <= this.bounds.x + this.bounds.width + padding && y >= this.bounds.y - padding && y <= this.bounds.y + this.bounds.height + padding;
518
+ }
519
+ move(dx, dy) {
520
+ this.selectedShapes.forEach((shape) => {
521
+ if (shape.parentFrame && this.selectedShapes.has(shape.parentFrame)) {
522
+ return;
523
+ }
524
+ if (typeof shape.move === "function") {
525
+ shape.move(dx, dy);
526
+ }
527
+ if (typeof shape.updateAttachedArrows === "function") {
528
+ shape.updateAttachedArrows();
529
+ }
530
+ });
531
+ this.updateControls();
532
+ }
533
+ startRotation(e) {
534
+ e.stopPropagation();
535
+ e.preventDefault();
536
+ this.isRotating = true;
537
+ const currentBounds = this.getBounds();
538
+ this.rotationCenter = {
539
+ x: currentBounds.x + currentBounds.width / 2,
540
+ y: currentBounds.y + currentBounds.height / 2
541
+ };
542
+ const { x: mouseX, y: mouseY } = getSVGCoordsFromMouse(e);
543
+ this.startRotationMouseAngle = Math.atan2(mouseY - this.rotationCenter.y, mouseX - this.rotationCenter.x) * 180 / Math.PI;
544
+ this.storeInitialPositions();
545
+ this._snapshotForUndo();
546
+ const onMouseMove = (event) => {
547
+ if (this.isRotating) {
548
+ this.handleRotation(event);
549
+ }
550
+ };
551
+ const onMouseUp = () => {
552
+ this.isRotating = false;
553
+ this.initialPositions.clear();
554
+ this._pushUndoForAll();
555
+ if (typeof svg !== "undefined") {
556
+ svg.removeEventListener("pointermove", onMouseMove);
557
+ svg.removeEventListener("pointerup", onMouseUp);
558
+ svg.style.cursor = "default";
559
+ }
560
+ };
561
+ if (typeof svg !== "undefined") {
562
+ svg.addEventListener("pointermove", onMouseMove);
563
+ svg.addEventListener("pointerup", onMouseUp);
564
+ svg.style.cursor = "grabbing";
565
+ }
566
+ }
567
+ storeInitialPositions() {
568
+ this.initialPositions.clear();
569
+ const initialBounds = this.getBounds();
570
+ this.initialPositions.set("initialBounds", {
571
+ x: initialBounds.x,
572
+ y: initialBounds.y,
573
+ width: initialBounds.width,
574
+ height: initialBounds.height
575
+ });
576
+ this.selectedShapes.forEach((shape) => {
577
+ let shapeData;
578
+ let shapeCenterX, shapeCenterY;
579
+ switch (shape.shapeName) {
580
+ case "rectangle":
581
+ case "frame":
582
+ shapeCenterX = shape.x + shape.width / 2;
583
+ shapeCenterY = shape.y + shape.height / 2;
584
+ shapeData = {
585
+ x: shape.x,
586
+ y: shape.y,
587
+ width: shape.width || 0,
588
+ height: shape.height || 0,
589
+ rotation: shape.rotation || 0,
590
+ centerX: shapeCenterX,
591
+ centerY: shapeCenterY,
592
+ relativeX: shapeCenterX - this.rotationCenter.x,
593
+ relativeY: shapeCenterY - this.rotationCenter.y
594
+ };
595
+ break;
596
+ case "icon":
597
+ shapeCenterX = shape.x + shape.width / 2;
598
+ shapeCenterY = shape.y + shape.height / 2;
599
+ shapeData = {
600
+ x: shape.x,
601
+ y: shape.y,
602
+ width: shape.width || 0,
603
+ height: shape.height || 0,
604
+ rotation: shape.rotation || 0,
605
+ centerX: shapeCenterX,
606
+ centerY: shapeCenterY,
607
+ relativeX: shapeCenterX - this.rotationCenter.x,
608
+ relativeY: shapeCenterY - this.rotationCenter.y
609
+ };
610
+ break;
611
+ case "circle":
612
+ shapeCenterX = shape.x;
613
+ shapeCenterY = shape.y;
614
+ shapeData = {
615
+ x: shape.x,
616
+ y: shape.y,
617
+ rx: shape.rx || 0,
618
+ ry: shape.ry || 0,
619
+ rotation: shape.rotation || 0,
620
+ centerX: shapeCenterX,
621
+ centerY: shapeCenterY,
622
+ relativeX: shapeCenterX - this.rotationCenter.x,
623
+ relativeY: shapeCenterY - this.rotationCenter.y
624
+ };
625
+ break;
626
+ case "line":
627
+ case "arrow":
628
+ shapeData = {
629
+ startPoint: { ...shape.startPoint },
630
+ endPoint: { ...shape.endPoint },
631
+ relativeStartX: shape.startPoint.x - this.rotationCenter.x,
632
+ relativeStartY: shape.startPoint.y - this.rotationCenter.y,
633
+ relativeEndX: shape.endPoint.x - this.rotationCenter.x,
634
+ relativeEndY: shape.endPoint.y - this.rotationCenter.y
635
+ };
636
+ break;
637
+ case "freehandStroke":
638
+ if (!shape.points || !Array.isArray(shape.points) || shape.points.length === 0) {
639
+ console.warn("FreehandStroke has invalid points array");
640
+ return;
641
+ }
642
+ const validPoints = shape.points.filter(
643
+ (point) => Array.isArray(point) && point.length >= 2 && typeof point[0] === "number" && typeof point[1] === "number" && !isNaN(point[0]) && !isNaN(point[1])
644
+ );
645
+ if (validPoints.length === 0) {
646
+ console.warn("FreehandStroke has no valid points");
647
+ return;
648
+ }
649
+ if (typeof shape.updateBoundingBox === "function") {
650
+ shape.updateBoundingBox();
651
+ }
652
+ const boundingBox = this.getShapeBounds(shape);
653
+ shapeData = {
654
+ x: boundingBox.x || 0,
655
+ y: boundingBox.y || 0,
656
+ width: boundingBox.width || 0,
657
+ height: boundingBox.height || 0,
658
+ points: validPoints.map((point) => [point[0], point[1], point[2] || 0.5]),
659
+ relativePoints: validPoints.map((point) => [
660
+ point[0] - this.rotationCenter.x,
661
+ point[1] - this.rotationCenter.y,
662
+ point[2] || 0.5
663
+ ]),
664
+ boundingBox: { ...boundingBox }
665
+ };
666
+ break;
667
+ case "text":
668
+ const textBounds = this.getShapeBounds(shape);
669
+ shapeCenterX = textBounds.x + textBounds.width / 2;
670
+ shapeCenterY = textBounds.y + textBounds.height / 2;
671
+ const currentTransform = shape.group.transform.baseVal.consolidate();
672
+ const currentX = currentTransform ? currentTransform.matrix.e : 0;
673
+ const currentY = currentTransform ? currentTransform.matrix.f : 0;
674
+ shapeData = {
675
+ x: currentX,
676
+ y: currentY,
677
+ width: shape.width || textBounds.width,
678
+ height: shape.height || textBounds.height,
679
+ rotation: shape.rotation || 0,
680
+ centerX: shapeCenterX,
681
+ centerY: shapeCenterY,
682
+ relativeX: shapeCenterX - this.rotationCenter.x,
683
+ relativeY: shapeCenterY - this.rotationCenter.y,
684
+ visualBounds: textBounds,
685
+ transformX: currentX,
686
+ transformY: currentY
687
+ };
688
+ break;
689
+ case "image":
690
+ shapeCenterX = shape.x + (shape.width || 0) / 2;
691
+ shapeCenterY = shape.y + (shape.height || 0) / 2;
692
+ shapeData = {
693
+ x: shape.x || 0,
694
+ y: shape.y || 0,
695
+ width: shape.width || 0,
696
+ height: shape.height || 0,
697
+ rotation: shape.rotation || 0,
698
+ centerX: shapeCenterX,
699
+ centerY: shapeCenterY,
700
+ relativeX: shapeCenterX - this.rotationCenter.x,
701
+ relativeY: shapeCenterY - this.rotationCenter.y
702
+ };
703
+ break;
704
+ default:
705
+ shapeCenterX = (shape.x || 0) + (shape.width || 0) / 2;
706
+ shapeCenterY = (shape.y || 0) + (shape.height || 0) / 2;
707
+ shapeData = {
708
+ x: shape.x || 0,
709
+ y: shape.y || 0,
710
+ width: shape.width || 0,
711
+ height: shape.height || 0,
712
+ rotation: shape.rotation || 0,
713
+ centerX: shapeCenterX,
714
+ centerY: shapeCenterY,
715
+ relativeX: shapeCenterX - this.rotationCenter.x,
716
+ relativeY: shapeCenterY - this.rotationCenter.y
717
+ };
718
+ }
719
+ if (shapeData) {
720
+ this.initialPositions.set(shape, shapeData);
721
+ }
722
+ });
723
+ }
724
+ handleRotation(e) {
725
+ const { x: mouseX, y: mouseY } = getSVGCoordsFromMouse(e);
726
+ const currentMouseAngle = Math.atan2(mouseY - this.rotationCenter.y, mouseX - this.rotationCenter.x) * 180 / Math.PI;
727
+ const angleDiff = currentMouseAngle - this.startRotationMouseAngle;
728
+ const angleRad = angleDiff * Math.PI / 180;
729
+ const cosAngle = Math.cos(angleRad);
730
+ const sinAngle = Math.sin(angleRad);
731
+ this.selectedShapes.forEach((shape) => {
732
+ const initialData = this.initialPositions.get(shape);
733
+ if (!initialData) return;
734
+ if (typeof shape.removeSelection === "function") {
735
+ shape.removeSelection();
736
+ }
737
+ switch (shape.shapeName) {
738
+ case "rectangle":
739
+ case "frame":
740
+ case "image":
741
+ case "icon":
742
+ const newCenterX = this.rotationCenter.x + (initialData.relativeX * cosAngle - initialData.relativeY * sinAngle);
743
+ const newCenterY = this.rotationCenter.y + (initialData.relativeX * sinAngle + initialData.relativeY * cosAngle);
744
+ shape.x = newCenterX - initialData.width / 2;
745
+ shape.y = newCenterY - initialData.height / 2;
746
+ shape.rotation = (initialData.rotation || 0) + angleDiff;
747
+ if (typeof shape.rotate === "function") {
748
+ shape.rotate(shape.rotation);
749
+ }
750
+ if (typeof shape.draw === "function") {
751
+ shape.draw();
752
+ }
753
+ break;
754
+ case "text":
755
+ const newTextCenterX = this.rotationCenter.x + (initialData.relativeX * cosAngle - initialData.relativeY * sinAngle);
756
+ const newTextCenterY = this.rotationCenter.y + (initialData.relativeX * sinAngle + initialData.relativeY * cosAngle);
757
+ const textElement = shape.group.querySelector("text");
758
+ if (textElement) {
759
+ const initialBbox = initialData.visualBounds;
760
+ const newTransformX = newTextCenterX - initialBbox.width / 2;
761
+ const newTransformY = newTextCenterY - initialBbox.height / 2;
762
+ const newRotation = (initialData.rotation || 0) + angleDiff;
763
+ const textCenterX = initialBbox.width / 2;
764
+ const textCenterY = initialBbox.height / 2;
765
+ shape.group.setAttribute(
766
+ "transform",
767
+ `translate(${newTransformX}, ${newTransformY}) rotate(${newRotation}, ${textCenterX}, ${textCenterY})`
768
+ );
769
+ shape.x = newTransformX;
770
+ shape.y = newTransformY;
771
+ shape.rotation = newRotation;
772
+ }
773
+ if (typeof shape.draw === "function") {
774
+ shape.draw();
775
+ }
776
+ break;
777
+ case "circle":
778
+ const newCircleCenterX = this.rotationCenter.x + (initialData.relativeX * cosAngle - initialData.relativeY * sinAngle);
779
+ const newCircleCenterY = this.rotationCenter.y + (initialData.relativeX * sinAngle + initialData.relativeY * cosAngle);
780
+ shape.x = newCircleCenterX;
781
+ shape.y = newCircleCenterY;
782
+ shape.rotation = (initialData.rotation || 0) + angleDiff;
783
+ if (typeof shape.rotate === "function") {
784
+ shape.rotate(shape.rotation);
785
+ }
786
+ if (typeof shape.draw === "function") {
787
+ shape.draw();
788
+ }
789
+ break;
790
+ case "line":
791
+ case "arrow":
792
+ const newStartX = this.rotationCenter.x + (initialData.relativeStartX * cosAngle - initialData.relativeStartY * sinAngle);
793
+ const newStartY = this.rotationCenter.y + (initialData.relativeStartX * sinAngle + initialData.relativeStartY * cosAngle);
794
+ const newEndX = this.rotationCenter.x + (initialData.relativeEndX * cosAngle - initialData.relativeEndY * sinAngle);
795
+ const newEndY = this.rotationCenter.y + (initialData.relativeEndX * sinAngle + initialData.relativeEndY * cosAngle);
796
+ shape.startPoint.x = newStartX;
797
+ shape.startPoint.y = newStartY;
798
+ shape.endPoint.x = newEndX;
799
+ shape.endPoint.y = newEndY;
800
+ if (shape.shapeName === "arrow" && shape.arrowCurved && typeof shape.initializeCurveControlPoints === "function") {
801
+ shape.initializeCurveControlPoints();
802
+ }
803
+ if (typeof shape.draw === "function") {
804
+ shape.draw();
805
+ }
806
+ break;
807
+ case "freehandStroke":
808
+ if (!initialData.relativePoints || !Array.isArray(initialData.relativePoints)) {
809
+ console.warn("FreehandStroke missing valid relativePoints");
810
+ break;
811
+ }
812
+ const rotatedPoints = initialData.relativePoints.map((relPoint) => {
813
+ if (!Array.isArray(relPoint) || relPoint.length < 2 || typeof relPoint[0] !== "number" || typeof relPoint[1] !== "number" || isNaN(relPoint[0]) || isNaN(relPoint[1])) {
814
+ console.warn("Invalid relative point:", relPoint);
815
+ return [0, 0, 0.5];
816
+ }
817
+ const newX = this.rotationCenter.x + (relPoint[0] * cosAngle - relPoint[1] * sinAngle);
818
+ const newY = this.rotationCenter.y + (relPoint[0] * sinAngle + relPoint[1] * cosAngle);
819
+ if (isNaN(newX) || isNaN(newY)) {
820
+ console.warn("Generated NaN coordinates:", { newX, newY, relPoint, rotationCenter: this.rotationCenter });
821
+ return [0, 0, 0.5];
822
+ }
823
+ return [newX, newY, relPoint[2] || 0.5];
824
+ });
825
+ shape.points = rotatedPoints.filter(
826
+ (point) => Array.isArray(point) && point.length >= 2 && typeof point[0] === "number" && typeof point[1] === "number" && !isNaN(point[0]) && !isNaN(point[1])
827
+ );
828
+ if (shape.points.length === 0) {
829
+ console.warn("No valid points after rotation");
830
+ shape.points = [[0, 0, 0.5]];
831
+ }
832
+ if (typeof shape.updateBoundingBox === "function") {
833
+ shape.updateBoundingBox();
834
+ }
835
+ if (typeof shape.draw === "function") {
836
+ shape.draw();
837
+ }
838
+ break;
839
+ }
840
+ if (typeof shape.updateAttachedArrows === "function") {
841
+ shape.updateAttachedArrows();
842
+ }
843
+ });
844
+ this.updateControlsAfterRotation();
845
+ }
846
+ updateControlsAfterRotation() {
847
+ this.removeControls();
848
+ if (this.selectedShapes.size === 0) return;
849
+ this.bounds = this.getBounds();
850
+ if (!this.bounds) {
851
+ return;
852
+ }
853
+ this.createControls();
854
+ }
855
+ createRotatedControls(angleDiff = 0) {
856
+ this.group = document.createElementNS("http://www.w3.org/2000/svg", "g");
857
+ this.group.setAttribute("id", "multi-selection-controls");
858
+ if (angleDiff !== 0) {
859
+ const centerX = this.bounds.x + this.bounds.width / 2;
860
+ const centerY = this.bounds.y + this.bounds.height / 2;
861
+ this.group.setAttribute("transform", `rotate(${angleDiff} ${centerX} ${centerY})`);
862
+ }
863
+ if (typeof svg !== "undefined") {
864
+ svg.appendChild(this.group);
865
+ }
866
+ if (this.selectedShapes.size > 1) {
867
+ this.selectedShapes.forEach((shape) => {
868
+ const b = this.getShapeBounds(shape);
869
+ if (!b || b.width === 0 && b.height === 0) return;
870
+ const rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
871
+ rect.setAttribute("x", b.x - 3);
872
+ rect.setAttribute("y", b.y - 3);
873
+ rect.setAttribute("width", b.width + 6);
874
+ rect.setAttribute("height", b.height + 6);
875
+ rect.setAttribute("fill", "none");
876
+ rect.setAttribute("stroke", "#5B57D1");
877
+ rect.setAttribute("stroke-width", 1);
878
+ rect.setAttribute("stroke-opacity", 0.35);
879
+ rect.setAttribute("stroke-dasharray", "4 3");
880
+ rect.setAttribute("rx", 3);
881
+ rect.setAttribute("style", "pointer-events: none;");
882
+ this.group.appendChild(rect);
883
+ });
884
+ }
885
+ const expandedX = this.bounds.x - this.selectionPadding;
886
+ const expandedY = this.bounds.y - this.selectionPadding;
887
+ const expandedWidth = this.bounds.width + 2 * this.selectionPadding;
888
+ const expandedHeight = this.bounds.height + 2 * this.selectionPadding;
889
+ this.createOutline(expandedX, expandedY, expandedWidth, expandedHeight);
890
+ this.createResizeAnchors(expandedX, expandedY, expandedWidth, expandedHeight);
891
+ this.createRotationAnchor(expandedX, expandedY, expandedWidth, expandedHeight);
892
+ }
893
+ startResize(e, anchorIndex) {
894
+ e.stopPropagation();
895
+ e.preventDefault();
896
+ this.isResizing = true;
897
+ this.resizingAnchorIndex = anchorIndex;
898
+ this._snapshotForUndo();
899
+ this.initialPositions.clear();
900
+ const initialBounds = this.getBounds();
901
+ this.selectedShapes.forEach((shape) => {
902
+ let shapeData;
903
+ shape.removeSelection();
904
+ switch (shape.shapeName) {
905
+ case "rectangle":
906
+ case "icon":
907
+ shapeData = {
908
+ x: shape.x,
909
+ y: shape.y,
910
+ width: shape.width,
911
+ height: shape.height,
912
+ rotation: shape.rotation
913
+ };
914
+ break;
915
+ case "circle":
916
+ shapeData = {
917
+ x: shape.x,
918
+ y: shape.y,
919
+ rx: shape.rx,
920
+ ry: shape.ry,
921
+ rotation: shape.rotation
922
+ };
923
+ break;
924
+ case "line":
925
+ case "arrow":
926
+ shapeData = {
927
+ startPoint: { ...shape.startPoint },
928
+ endPoint: { ...shape.endPoint }
929
+ };
930
+ break;
931
+ default:
932
+ shapeData = {
933
+ x: shape.x || 0,
934
+ y: shape.y || 0,
935
+ width: shape.width || 0,
936
+ height: shape.height || 0
937
+ };
938
+ }
939
+ this.initialPositions.set(shape, shapeData);
940
+ });
941
+ this.initialPositions.set("bounds", initialBounds);
942
+ const onMouseMove = (event) => {
943
+ if (this.isResizing) {
944
+ this.handleResize(event);
945
+ }
946
+ };
947
+ const onMouseUp = () => {
948
+ this.isResizing = false;
949
+ this.resizingAnchorIndex = null;
950
+ this.initialPositions.clear();
951
+ this._pushUndoForAll();
952
+ if (typeof svg !== "undefined") {
953
+ svg.removeEventListener("pointermove", onMouseMove);
954
+ svg.removeEventListener("pointerup", onMouseUp);
955
+ svg.style.cursor = "default";
956
+ }
957
+ };
958
+ if (typeof svg !== "undefined") {
959
+ svg.addEventListener("pointermove", onMouseMove);
960
+ svg.addEventListener("pointerup", onMouseUp);
961
+ }
962
+ }
963
+ handleResize(e) {
964
+ const { x: mouseX, y: mouseY } = getSVGCoordsFromMouse(e);
965
+ const initialBounds = this.initialPositions.get("bounds");
966
+ if (!initialBounds) return;
967
+ let scaleX = 1, scaleY = 1;
968
+ let newBounds = { ...initialBounds };
969
+ switch (this.resizingAnchorIndex) {
970
+ case 0:
971
+ scaleX = (initialBounds.x + initialBounds.width - mouseX) / initialBounds.width;
972
+ scaleY = (initialBounds.y + initialBounds.height - mouseY) / initialBounds.height;
973
+ newBounds.x = mouseX;
974
+ newBounds.y = mouseY;
975
+ newBounds.width = initialBounds.x + initialBounds.width - mouseX;
976
+ newBounds.height = initialBounds.y + initialBounds.height - mouseY;
977
+ break;
978
+ case 1:
979
+ scaleX = (mouseX - initialBounds.x) / initialBounds.width;
980
+ scaleY = (initialBounds.y + initialBounds.height - mouseY) / initialBounds.height;
981
+ newBounds.y = mouseY;
982
+ newBounds.width = mouseX - initialBounds.x;
983
+ newBounds.height = initialBounds.y + initialBounds.height - mouseY;
984
+ break;
985
+ case 2:
986
+ scaleX = (initialBounds.x + initialBounds.width - mouseX) / initialBounds.width;
987
+ scaleY = (mouseY - initialBounds.y) / initialBounds.height;
988
+ newBounds.x = mouseX;
989
+ newBounds.width = initialBounds.x + initialBounds.width - mouseX;
990
+ newBounds.height = mouseY - initialBounds.y;
991
+ break;
992
+ case 3:
993
+ scaleX = (mouseX - initialBounds.x) / initialBounds.width;
994
+ scaleY = (mouseY - initialBounds.y) / initialBounds.height;
995
+ newBounds.width = mouseX - initialBounds.x;
996
+ newBounds.height = mouseY - initialBounds.y;
997
+ break;
998
+ case 4:
999
+ scaleY = (initialBounds.y + initialBounds.height - mouseY) / initialBounds.height;
1000
+ newBounds.y = mouseY;
1001
+ newBounds.height = initialBounds.y + initialBounds.height - mouseY;
1002
+ break;
1003
+ case 5:
1004
+ scaleY = (mouseY - initialBounds.y) / initialBounds.height;
1005
+ newBounds.height = mouseY - initialBounds.y;
1006
+ break;
1007
+ case 6:
1008
+ scaleX = (initialBounds.x + initialBounds.width - mouseX) / initialBounds.width;
1009
+ newBounds.x = mouseX;
1010
+ newBounds.width = initialBounds.x + initialBounds.width - mouseX;
1011
+ break;
1012
+ case 7:
1013
+ scaleX = (mouseX - initialBounds.x) / initialBounds.width;
1014
+ newBounds.width = mouseX - initialBounds.x;
1015
+ break;
1016
+ }
1017
+ scaleX = Math.max(0.1, Math.abs(scaleX));
1018
+ scaleY = Math.max(0.1, Math.abs(scaleY));
1019
+ this.selectedShapes.forEach((shape) => {
1020
+ const initialData = this.initialPositions.get(shape);
1021
+ if (!initialData) return;
1022
+ switch (shape.shapeName) {
1023
+ case "rectangle":
1024
+ case "icon":
1025
+ const relX = (initialData.x - initialBounds.x) / initialBounds.width;
1026
+ const relY = (initialData.y - initialBounds.y) / initialBounds.height;
1027
+ const relW = initialData.width / initialBounds.width;
1028
+ const relH = initialData.height / initialBounds.height;
1029
+ shape.x = newBounds.x + relX * newBounds.width;
1030
+ shape.y = newBounds.y + relY * newBounds.height;
1031
+ shape.width = relW * newBounds.width;
1032
+ shape.height = relH * newBounds.height;
1033
+ shape._skipAnchors = true;
1034
+ shape.draw();
1035
+ shape._skipAnchors = false;
1036
+ break;
1037
+ case "circle":
1038
+ const relXCircle = (initialData.x - initialBounds.x) / initialBounds.width;
1039
+ const relYCircle = (initialData.y - initialBounds.y) / initialBounds.height;
1040
+ shape.x = newBounds.x + relXCircle * newBounds.width;
1041
+ shape.y = newBounds.y + relYCircle * newBounds.height;
1042
+ shape.rx = initialData.rx * scaleX;
1043
+ shape.ry = initialData.ry * scaleY;
1044
+ shape._skipAnchors = true;
1045
+ shape.draw();
1046
+ shape._skipAnchors = false;
1047
+ break;
1048
+ case "line":
1049
+ case "arrow":
1050
+ const relStartX = (initialData.startPoint.x - initialBounds.x) / initialBounds.width;
1051
+ const relStartY = (initialData.startPoint.y - initialBounds.y) / initialBounds.height;
1052
+ const relEndX = (initialData.endPoint.x - initialBounds.x) / initialBounds.width;
1053
+ const relEndY = (initialData.endPoint.y - initialBounds.y) / initialBounds.height;
1054
+ shape.startPoint.x = newBounds.x + relStartX * newBounds.width;
1055
+ shape.startPoint.y = newBounds.y + relStartY * newBounds.height;
1056
+ shape.endPoint.x = newBounds.x + relEndX * newBounds.width;
1057
+ shape.endPoint.y = newBounds.y + relEndY * newBounds.height;
1058
+ if (shape.shapeName === "arrow" && shape.arrowCurved) {
1059
+ if (typeof shape.initializeCurveControlPoints === "function") {
1060
+ shape.initializeCurveControlPoints();
1061
+ }
1062
+ }
1063
+ shape.draw();
1064
+ break;
1065
+ default:
1066
+ const relXDefault = (initialData.x - initialBounds.x) / initialBounds.width;
1067
+ const relYDefault = (initialData.y - initialBounds.y) / initialBounds.height;
1068
+ const relWDefault = initialData.width / initialBounds.width;
1069
+ const relHDefault = initialData.height / initialBounds.height;
1070
+ shape.x = newBounds.x + relXDefault * newBounds.width;
1071
+ shape.y = newBounds.y + relYDefault * newBounds.height;
1072
+ if (shape.width !== void 0) shape.width = relWDefault * newBounds.width;
1073
+ if (shape.height !== void 0) shape.height = relHDefault * newBounds.height;
1074
+ if (typeof shape.draw === "function") {
1075
+ shape._skipAnchors = true;
1076
+ shape.draw();
1077
+ shape._skipAnchors = false;
1078
+ }
1079
+ }
1080
+ if (typeof shape.updateAttachedArrows === "function") {
1081
+ shape.updateAttachedArrows();
1082
+ }
1083
+ });
1084
+ this.updateControls();
1085
+ }
1086
+ startDrag(e) {
1087
+ this.isDragging = true;
1088
+ isDraggingMultiSelection = true;
1089
+ const { x, y } = getSVGCoordsFromMouse(e);
1090
+ this.dragStart = { x, y };
1091
+ this._snapshotForUndo();
1092
+ this.initialPositions.clear();
1093
+ this.selectedShapes.forEach((shape) => {
1094
+ let shapeData;
1095
+ shape.removeSelection();
1096
+ switch (shape.shapeName) {
1097
+ case "rectangle":
1098
+ case "icon":
1099
+ shapeData = {
1100
+ x: shape.x,
1101
+ y: shape.y,
1102
+ width: shape.width,
1103
+ height: shape.height,
1104
+ rotation: shape.rotation
1105
+ };
1106
+ break;
1107
+ case "circle":
1108
+ shapeData = {
1109
+ x: shape.x,
1110
+ y: shape.y,
1111
+ rx: shape.rx,
1112
+ ry: shape.ry,
1113
+ rotation: shape.rotation
1114
+ };
1115
+ break;
1116
+ case "line":
1117
+ case "arrow":
1118
+ shapeData = {
1119
+ startPoint: { ...shape.startPoint },
1120
+ endPoint: { ...shape.endPoint }
1121
+ };
1122
+ break;
1123
+ default:
1124
+ shapeData = {
1125
+ x: shape.x || 0,
1126
+ y: shape.y || 0,
1127
+ width: shape.width || 0,
1128
+ height: shape.height || 0,
1129
+ rotation: shape.rotation || 0
1130
+ };
1131
+ }
1132
+ this.initialPositions.set(shape, shapeData);
1133
+ });
1134
+ if (typeof svg !== "undefined") {
1135
+ svg.style.cursor = "move";
1136
+ }
1137
+ }
1138
+ handleDrag(e) {
1139
+ if (!this.isDragging) return;
1140
+ isDraggingMultiSelection = true;
1141
+ const { x, y } = getSVGCoordsFromMouse(e);
1142
+ let dx = x - this.dragStart.x;
1143
+ let dy = y - this.dragStart.y;
1144
+ if (e.shiftKey) {
1145
+ if (Math.abs(dx) > Math.abs(dy)) {
1146
+ dy = 0;
1147
+ } else {
1148
+ dx = 0;
1149
+ }
1150
+ }
1151
+ this.move(dx, dy);
1152
+ if (window.__sketchStoreApi && window.__sketchStoreApi.getState().snapToObjects) {
1153
+ const bounds = this.getBounds();
1154
+ if (bounds) {
1155
+ const virtualShape = {
1156
+ shapeName: "rectangle",
1157
+ x: bounds.x,
1158
+ y: bounds.y,
1159
+ width: bounds.width,
1160
+ height: bounds.height
1161
+ };
1162
+ const snap = calculateSnap(virtualShape, e.shiftKey, e.clientX, e.clientY);
1163
+ if (snap.dx || snap.dy) {
1164
+ this.move(snap.dx, snap.dy);
1165
+ }
1166
+ }
1167
+ } else {
1168
+ clearSnapGuides();
1169
+ }
1170
+ this.dragStart = { x, y };
1171
+ }
1172
+ endDrag() {
1173
+ if (!this.isDragging) return;
1174
+ isDraggingMultiSelection = false;
1175
+ this.isDragging = false;
1176
+ clearSnapGuides();
1177
+ this.selectedShapes.forEach((shape) => {
1178
+ if (typeof shape.finalizeMove === "function") {
1179
+ shape.finalizeMove();
1180
+ }
1181
+ shape.isSelected = true;
1182
+ });
1183
+ if (typeof shapes !== "undefined" && Array.isArray(shapes)) {
1184
+ this.selectedShapes.forEach((shape) => {
1185
+ if (!shape || shape.shapeName === "frame") return;
1186
+ let target = null;
1187
+ for (let i = shapes.length - 1; i >= 0; i--) {
1188
+ const f = shapes[i];
1189
+ if (f.shapeName !== "frame") continue;
1190
+ if (this.selectedShapes.has(f)) continue;
1191
+ if (typeof f.isShapeInFrame === "function" && f.isShapeInFrame(shape)) {
1192
+ target = f;
1193
+ break;
1194
+ }
1195
+ }
1196
+ const current = shape.parentFrame || null;
1197
+ if (target === current) return;
1198
+ if (current && typeof current.removeShapeFromFrame === "function") {
1199
+ current.removeShapeFromFrame(shape);
1200
+ }
1201
+ if (target && typeof target.addShapeToFrame === "function") {
1202
+ target.addShapeToFrame(shape);
1203
+ if (typeof pushFrameAttachmentAction === "function") {
1204
+ pushFrameAttachmentAction(target, shape, "attach", current);
1205
+ }
1206
+ } else if (current && typeof pushFrameAttachmentAction === "function") {
1207
+ pushFrameAttachmentAction(current, shape, "detach", current);
1208
+ }
1209
+ });
1210
+ }
1211
+ this.initialPositions.clear();
1212
+ this._pushUndoForAll();
1213
+ this.updateControls();
1214
+ if (typeof svg !== "undefined") {
1215
+ svg.style.cursor = "default";
1216
+ }
1217
+ }
1218
+ };
1219
+ var multiSelection = new MultiSelection();
1220
+ function selectShapesInRect(selectionBounds) {
1221
+ multiSelection.selectShapesInRect(selectionBounds);
1222
+ }
1223
+ function clearAllSelections() {
1224
+ multiSelection.clearSelection();
1225
+ }
1226
+ function createMultiSelectionControls() {
1227
+ multiSelection.updateControls();
1228
+ }
1229
+ function removeMultiSelectionControls() {
1230
+ multiSelection.removeControls();
1231
+ }
1232
+ function isPointInMultiSelection(x, y) {
1233
+ return multiSelection.isPointInBounds(x, y);
1234
+ }
1235
+ function moveSelectedShapes(dx, dy) {
1236
+ multiSelection.move(dx, dy);
1237
+ }
1238
+ function handleMultiSelectionMouseDown(e) {
1239
+ if (!e.target) return false;
1240
+ const { x, y } = getSVGCoordsFromMouse(e);
1241
+ if (multiSelection.selectedShapes.size > 1) {
1242
+ const anchor = e.target.closest(".multi-selection-anchor");
1243
+ if (anchor) {
1244
+ const anchorIndex = parseInt(anchor.getAttribute("data-index"));
1245
+ multiSelection.startResize(e, anchorIndex);
1246
+ return true;
1247
+ }
1248
+ if (e.target.closest(".multi-selection-rotation-anchor")) {
1249
+ multiSelection.startRotation(e);
1250
+ return true;
1251
+ }
1252
+ let clickedOnSelectedShape = null;
1253
+ for (const shape of multiSelection.selectedShapes) {
1254
+ if (shape.contains && shape.contains(x, y)) {
1255
+ clickedOnSelectedShape = shape;
1256
+ break;
1257
+ }
1258
+ }
1259
+ if (clickedOnSelectedShape) {
1260
+ if (e.ctrlKey || e.metaKey) {
1261
+ multiSelection.removeShape(clickedOnSelectedShape);
1262
+ return true;
1263
+ }
1264
+ multiSelection.startDrag(e);
1265
+ return true;
1266
+ }
1267
+ if (multiSelection.isPointInBounds(x, y)) {
1268
+ multiSelection.startDrag(e);
1269
+ return true;
1270
+ }
1271
+ }
1272
+ if (e.target.closest(".anchor") || e.target.closest(".rotate-anchor") || e.target.closest(".rotation-anchor")) {
1273
+ return false;
1274
+ }
1275
+ let clickedOnShape = false;
1276
+ let clickedShape = null;
1277
+ if (typeof shapes !== "undefined") {
1278
+ let fallbackFrame = null;
1279
+ for (let i = shapes.length - 1; i >= 0; i--) {
1280
+ const shape = shapes[i];
1281
+ if (shape.contains && shape.contains(x, y)) {
1282
+ if (shape.shapeName === "frame") {
1283
+ if (!fallbackFrame) {
1284
+ fallbackFrame = shape;
1285
+ } else {
1286
+ const area = (shape.width || 0) * (shape.height || 0);
1287
+ const existingArea = (fallbackFrame.width || 0) * (fallbackFrame.height || 0);
1288
+ if (area < existingArea) {
1289
+ fallbackFrame = shape;
1290
+ }
1291
+ }
1292
+ } else {
1293
+ clickedOnShape = true;
1294
+ clickedShape = shape;
1295
+ break;
1296
+ }
1297
+ }
1298
+ }
1299
+ if (!clickedOnShape && fallbackFrame) {
1300
+ clickedOnShape = true;
1301
+ clickedShape = fallbackFrame;
1302
+ }
1303
+ }
1304
+ if (clickedOnShape && clickedShape) {
1305
+ if (e.ctrlKey || e.metaKey) {
1306
+ if (multiSelection.selectedShapes.has(clickedShape)) {
1307
+ multiSelection.removeShape(clickedShape);
1308
+ } else {
1309
+ if (currentShape && currentShape !== clickedShape && !multiSelection.selectedShapes.has(currentShape)) {
1310
+ multiSelection.addShape(currentShape);
1311
+ if (typeof currentShape.removeSelection === "function") currentShape.removeSelection();
1312
+ }
1313
+ multiSelection.addShape(clickedShape);
1314
+ }
1315
+ currentShape = null;
1316
+ return true;
1317
+ }
1318
+ if (multiSelection.selectedShapes.size > 1 && multiSelection.selectedShapes.has(clickedShape)) {
1319
+ multiSelection.startDrag(e);
1320
+ return true;
1321
+ }
1322
+ if (currentShape === clickedShape) {
1323
+ if (multiSelection.selectedShapes.size > 0) {
1324
+ multiSelection.clearSelection();
1325
+ }
1326
+ return false;
1327
+ }
1328
+ if (currentShape && typeof currentShape.removeSelection === "function") {
1329
+ currentShape.removeSelection();
1330
+ }
1331
+ multiSelection.clearSelection();
1332
+ if (clickedShape.groupId && typeof shapes !== "undefined") {
1333
+ const mates = shapes.filter((s) => s.groupId === clickedShape.groupId);
1334
+ if (mates.length > 1) {
1335
+ for (const m of mates) multiSelection.addShape(m);
1336
+ currentShape = null;
1337
+ multiSelection.startDrag(e);
1338
+ return true;
1339
+ }
1340
+ }
1341
+ currentShape = clickedShape;
1342
+ if (typeof clickedShape.addAnchors === "function") {
1343
+ clickedShape.addAnchors();
1344
+ clickedShape.isSelected = true;
1345
+ } else if (typeof clickedShape.createSelection === "function") {
1346
+ clickedShape.createSelection();
1347
+ clickedShape.isSelected = true;
1348
+ } else if (typeof clickedShape.selectShape === "function") {
1349
+ clickedShape.selectShape();
1350
+ clickedShape.isSelected = true;
1351
+ } else if (typeof clickedShape.selectFrame === "function") {
1352
+ clickedShape.selectFrame();
1353
+ }
1354
+ if (typeof clickedShape.updateSidebar === "function") {
1355
+ clickedShape.updateSidebar();
1356
+ }
1357
+ return false;
1358
+ }
1359
+ if (!clickedOnShape) {
1360
+ if (typeof currentShape !== "undefined" && currentShape && typeof currentShape.removeSelection === "function") {
1361
+ currentShape.removeSelection();
1362
+ currentShape = null;
1363
+ }
1364
+ if (window.__deselectTextElement) {
1365
+ window.__deselectTextElement();
1366
+ }
1367
+ if (typeof disableAllSideBars === "function") {
1368
+ disableAllSideBars();
1369
+ }
1370
+ multiSelectionStart = { x, y };
1371
+ isMultiSelecting = true;
1372
+ if (typeof svg !== "undefined") {
1373
+ svg.style.userSelect = "none";
1374
+ svg.style.webkitUserSelect = "none";
1375
+ }
1376
+ document.body.style.userSelect = "none";
1377
+ document.body.style.webkitUserSelect = "none";
1378
+ createMultiSelectionRect(x, y);
1379
+ clearAllSelections();
1380
+ return true;
1381
+ }
1382
+ return false;
1383
+ }
1384
+ function handleMultiSelectionMouseMove(e) {
1385
+ const { x, y } = getSVGCoordsFromMouse(e);
1386
+ if (multiSelection.isDragging) {
1387
+ multiSelection.handleDrag(e);
1388
+ return true;
1389
+ }
1390
+ if (multiSelection.isResizing) {
1391
+ multiSelection.handleResize(e);
1392
+ return true;
1393
+ }
1394
+ if (multiSelection.isRotating) {
1395
+ multiSelection.handleRotation(e);
1396
+ return true;
1397
+ }
1398
+ if (isMultiSelecting) {
1399
+ updateMultiSelectionRect(x, y);
1400
+ highlightShapesInSelectionRect(x, y);
1401
+ return true;
1402
+ }
1403
+ if (multiSelection.selectedShapes.size > 1 && multiSelection.isPointInBounds(x, y)) {
1404
+ if (typeof svg !== "undefined") {
1405
+ svg.style.cursor = "move";
1406
+ }
1407
+ return true;
1408
+ }
1409
+ if (typeof shapes !== "undefined" && Array.isArray(shapes)) {
1410
+ const overShape = shapes.some((shape) => {
1411
+ if (typeof shape.contains === "function") {
1412
+ return shape.contains(x, y);
1413
+ }
1414
+ return false;
1415
+ });
1416
+ if (overShape && typeof svg !== "undefined") {
1417
+ svg.style.cursor = "move";
1418
+ return false;
1419
+ }
1420
+ }
1421
+ if (typeof svg !== "undefined" && svg.style.cursor === "move") {
1422
+ svg.style.cursor = "default";
1423
+ }
1424
+ return false;
1425
+ }
1426
+ function handleMultiSelectionMouseUp(e) {
1427
+ if (multiSelection.isDragging) {
1428
+ multiSelection.endDrag();
1429
+ return true;
1430
+ }
1431
+ if (multiSelection.isResizing) {
1432
+ multiSelection.isResizing = false;
1433
+ multiSelection.resizingAnchorIndex = null;
1434
+ multiSelection.initialPositions.clear();
1435
+ multiSelection._pushUndoForAll();
1436
+ multiSelection.updateControls();
1437
+ return true;
1438
+ }
1439
+ if (multiSelection.isRotating) {
1440
+ multiSelection.isRotating = false;
1441
+ multiSelection.initialPositions.clear();
1442
+ multiSelection._pushUndoForAll();
1443
+ return true;
1444
+ }
1445
+ if (isMultiSelecting) {
1446
+ const { x, y } = getSVGCoordsFromMouse(e);
1447
+ const selectionBounds = {
1448
+ x: Math.min(multiSelectionStart.x, x),
1449
+ y: Math.min(multiSelectionStart.y, y),
1450
+ width: Math.abs(x - multiSelectionStart.x),
1451
+ height: Math.abs(y - multiSelectionStart.y)
1452
+ };
1453
+ if (selectionBounds.width > 5 && selectionBounds.height > 5) {
1454
+ let shapesInBounds = [];
1455
+ if (typeof shapes !== "undefined") {
1456
+ shapes.forEach((shape) => {
1457
+ if (isShapeInSelectionRect(shape, selectionBounds)) {
1458
+ shapesInBounds.push(shape);
1459
+ }
1460
+ });
1461
+ }
1462
+ if (shapesInBounds.length === 1) {
1463
+ const selectedShape = shapesInBounds[0];
1464
+ multiSelection.clearSelection();
1465
+ if (typeof currentShape !== "undefined") {
1466
+ currentShape = selectedShape;
1467
+ }
1468
+ if (typeof selectedShape.addAnchors === "function") {
1469
+ selectedShape.addAnchors();
1470
+ selectedShape.isSelected = true;
1471
+ } else if (typeof selectedShape.createSelection === "function") {
1472
+ selectedShape.createSelection();
1473
+ selectedShape.isSelected = true;
1474
+ } else if (typeof selectedShape.selectShape === "function") {
1475
+ selectedShape.selectShape();
1476
+ selectedShape.isSelected = true;
1477
+ }
1478
+ if (typeof selectedShape.updateSidebar === "function") {
1479
+ selectedShape.updateSidebar();
1480
+ }
1481
+ } else if (shapesInBounds.length > 1) {
1482
+ multiSelection.selectShapesInRect(selectionBounds);
1483
+ }
1484
+ }
1485
+ removeMultiSelectionRect();
1486
+ isMultiSelecting = false;
1487
+ isDraggingMultiSelection = false;
1488
+ document.body.style.userSelect = "";
1489
+ document.body.style.webkitUserSelect = "";
1490
+ if (typeof svg !== "undefined") {
1491
+ svg.style.userSelect = "";
1492
+ svg.style.webkitUserSelect = "";
1493
+ }
1494
+ return true;
1495
+ }
1496
+ isMultiSelecting = false;
1497
+ isDraggingMultiSelection = false;
1498
+ document.body.style.userSelect = "";
1499
+ document.body.style.webkitUserSelect = "";
1500
+ if (typeof svg !== "undefined") {
1501
+ svg.style.userSelect = "";
1502
+ svg.style.webkitUserSelect = "";
1503
+ }
1504
+ return false;
1505
+ }
1506
+ window.addEventListener("pointerup", () => {
1507
+ if (isMultiSelecting) {
1508
+ removeMultiSelectionRect();
1509
+ isMultiSelecting = false;
1510
+ isDraggingMultiSelection = false;
1511
+ document.body.style.userSelect = "";
1512
+ document.body.style.webkitUserSelect = "";
1513
+ }
1514
+ });
1515
+ document.addEventListener("keydown", (e) => {
1516
+ if ((e.key === "Delete" || e.key === "Backspace") && multiSelection.selectedShapes.size > 0) {
1517
+ const tag = e.target.tagName.toLowerCase();
1518
+ if (tag === "input" || tag === "textarea" || e.target.isContentEditable) return;
1519
+ e.preventDefault();
1520
+ deleteSelectedShapes();
1521
+ }
1522
+ });
1523
+ function deleteSelectedShapes() {
1524
+ if (multiSelection.selectedShapes.size === 0) return;
1525
+ const shapesToDelete = Array.from(multiSelection.selectedShapes);
1526
+ multiSelection.clearSelection();
1527
+ const releasedChildren = [];
1528
+ shapesToDelete.forEach((shape) => {
1529
+ const isFrame = shape.shapeName === "frame";
1530
+ const isDiagramFrame = isFrame && !!shape._diagramType;
1531
+ let childSnapshot = null;
1532
+ if (isFrame && !isDiagramFrame) {
1533
+ childSnapshot = [...shape.containedShapes || []];
1534
+ for (const child of childSnapshot) {
1535
+ if (!child || child === shape) continue;
1536
+ const el = child.group || child.element;
1537
+ if (el) {
1538
+ if (shape.clipGroup && el.parentNode === shape.clipGroup) {
1539
+ shape.clipGroup.removeChild(el);
1540
+ }
1541
+ if (window.svg && el.parentNode !== window.svg) window.svg.appendChild(el);
1542
+ }
1543
+ child.parentFrame = null;
1544
+ delete child.isBeingMovedByFrame;
1545
+ releasedChildren.push(child);
1546
+ }
1547
+ shape.containedShapes = [];
1548
+ if (shape.group && shape.group.parentNode) shape.group.parentNode.removeChild(shape.group);
1549
+ if (shape.clipGroup && shape.clipGroup.parentNode) shape.clipGroup.parentNode.removeChild(shape.clipGroup);
1550
+ if (shape.clipPath && shape.clipPath.parentNode) shape.clipPath.parentNode.removeChild(shape.clipPath);
1551
+ } else {
1552
+ if (shape.group && shape.group.parentNode) {
1553
+ shape.group.parentNode.removeChild(shape.group);
1554
+ } else if (shape.element && shape.element.parentNode) {
1555
+ shape.element.parentNode.removeChild(shape.element);
1556
+ }
1557
+ if (shape.parentFrame && typeof shape.parentFrame.removeShapeFromFrame === "function") {
1558
+ shape.parentFrame.removeShapeFromFrame(shape);
1559
+ }
1560
+ cleanupAttachments(shape);
1561
+ if (isDiagramFrame && typeof shape.destroy === "function") {
1562
+ shape.destroy();
1563
+ }
1564
+ }
1565
+ const idx = shapes.indexOf(shape);
1566
+ if (idx !== -1) shapes.splice(idx, 1);
1567
+ try {
1568
+ if (childSnapshot) {
1569
+ pushDeleteAction(shape, { childSnapshot });
1570
+ } else {
1571
+ pushDeleteAction(shape);
1572
+ }
1573
+ } catch (err) {
1574
+ console.warn("[deleteSelectedShapes] pushDeleteAction failed:", err);
1575
+ }
1576
+ });
1577
+ if (releasedChildren.length > 0) {
1578
+ for (const child of releasedChildren) {
1579
+ if (shapes.indexOf(child) === -1) continue;
1580
+ multiSelection.addShape(child);
1581
+ }
1582
+ if (window.__sketchStoreApi && typeof window.__sketchStoreApi.setActiveTool === "function") {
1583
+ window.__sketchStoreApi.setActiveTool("select");
1584
+ }
1585
+ }
1586
+ }
1587
+ function frameSelectedShapes() {
1588
+ if (multiSelection.selectedShapes.size < 2) return;
1589
+ const bounds = multiSelection.getBounds();
1590
+ if (!bounds) return;
1591
+ const padding = 20;
1592
+ const fx = bounds.x - padding;
1593
+ const fy = bounds.y - padding;
1594
+ const fw = bounds.width + padding * 2;
1595
+ const fh = bounds.height + padding * 2;
1596
+ const FrameClass = window.Frame;
1597
+ if (!FrameClass) {
1598
+ console.warn("[Selection] Frame class not available");
1599
+ return;
1600
+ }
1601
+ const frame = new FrameClass(fx, fy, fw, fh);
1602
+ shapes.push(frame);
1603
+ if (window.historyStack) {
1604
+ window.historyStack.push({
1605
+ type: window.ACTION_CREATE || "create",
1606
+ shape: frame,
1607
+ shapeName: "frame"
1608
+ });
1609
+ }
1610
+ const shapesToFrame = Array.from(multiSelection.selectedShapes);
1611
+ multiSelection.clearSelection();
1612
+ for (const shape of shapesToFrame) {
1613
+ frame.addShapeToFrame(shape);
1614
+ }
1615
+ const frameEl = frame.group;
1616
+ if (frameEl && frameEl.parentNode) {
1617
+ const frameIdx = shapes.indexOf(frame);
1618
+ if (frameIdx > 0) {
1619
+ shapes.splice(frameIdx, 1);
1620
+ let earliest = shapes.length;
1621
+ for (const s of shapesToFrame) {
1622
+ const idx = shapes.indexOf(s);
1623
+ if (idx >= 0 && idx < earliest) earliest = idx;
1624
+ }
1625
+ shapes.splice(earliest, 0, frame);
1626
+ }
1627
+ }
1628
+ if (typeof frame.selectFrame === "function") {
1629
+ frame.selectFrame();
1630
+ }
1631
+ currentShape = frame;
1632
+ }
1633
+ window.clearAllSelections = clearAllSelections;
1634
+ window.multiSelection = multiSelection;
1635
+ window.deleteSelectedShapes = deleteSelectedShapes;
1636
+ window.frameSelectedShapes = frameSelectedShapes;
1637
+
1638
+ export {
1639
+ isMultiSelecting,
1640
+ isDraggingMultiSelection,
1641
+ removeMultiSelectionRect,
1642
+ multiSelection,
1643
+ selectShapesInRect,
1644
+ clearAllSelections,
1645
+ createMultiSelectionControls,
1646
+ removeMultiSelectionControls,
1647
+ isPointInMultiSelection,
1648
+ moveSelectedShapes,
1649
+ handleMultiSelectionMouseDown,
1650
+ handleMultiSelectionMouseMove,
1651
+ handleMultiSelectionMouseUp
1652
+ };
1653
+ //# sourceMappingURL=chunk-Q2GA4MBZ.js.map