@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,1213 @@
1
+ "use client";
2
+
3
+ // src/shapes/Frame.js
4
+ function getSVGCoordsFromMouse(e) {
5
+ const viewBox = svg.viewBox.baseVal;
6
+ const rect = svg.getBoundingClientRect();
7
+ const mouseX = e.clientX - rect.left;
8
+ const mouseY = e.clientY - rect.top;
9
+ const svgX = viewBox.x + mouseX / rect.width * viewBox.width;
10
+ const svgY = viewBox.y + mouseY / rect.height * viewBox.height;
11
+ return { x: svgX, y: svgY };
12
+ }
13
+ var dragOldPosFrame = null;
14
+ var Frame = class {
15
+ constructor(x, y, width, height, options = {}) {
16
+ this.x = x;
17
+ this.y = y;
18
+ this.width = width;
19
+ this.height = height;
20
+ this.rotation = options.rotation || 0;
21
+ this.frameName = options.frameName || "Frame";
22
+ this.fillStyle = options.fillStyle || "transparent";
23
+ this.fillColor = options.fillColor || "#1e1e28";
24
+ this.gridSize = options.gridSize || 20;
25
+ this.gridColor = options.gridColor || "var(--lixsketch-grid-fine, rgba(255,255,255,0.06))";
26
+ this.options = {
27
+ stroke: options.stroke || "#555",
28
+ strokeWidth: options.strokeWidth || 1,
29
+ fill: options.fill || "transparent",
30
+ opacity: options.opacity || 1,
31
+ ...options
32
+ };
33
+ this.element = null;
34
+ this.group = document.createElementNS("http://www.w3.org/2000/svg", "g");
35
+ this.isSelected = false;
36
+ this.anchors = [];
37
+ this.shapeName = "frame";
38
+ this.shapeID = `frame-${String(Date.now()).slice(0, 8)}-${Math.floor(Math.random() * 1e4)}`;
39
+ this.group.setAttribute("id", this.shapeID);
40
+ this.clipGroup = document.createElementNS("http://www.w3.org/2000/svg", "g");
41
+ this.clipPath = document.createElementNS("http://www.w3.org/2000/svg", "clipPath");
42
+ this.clipRect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
43
+ this.clipId = `clip-${this.shapeID}`;
44
+ this.clipPath.setAttribute("id", this.clipId);
45
+ this.clipPath.appendChild(this.clipRect);
46
+ this.clipGroup.setAttribute("clip-path", `url(#${this.clipId})`);
47
+ let defs = svg.querySelector("defs");
48
+ if (!defs) {
49
+ defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
50
+ svg.appendChild(defs);
51
+ }
52
+ defs.appendChild(this.clipPath);
53
+ this.containedShapes = [];
54
+ svg.appendChild(this.group);
55
+ svg.appendChild(this.clipGroup);
56
+ this.draw();
57
+ this.updateClipPath();
58
+ }
59
+ draw() {
60
+ while (this.group.firstChild) {
61
+ this.group.removeChild(this.group.firstChild);
62
+ }
63
+ this.anchors = [];
64
+ let defs = svg.querySelector("defs");
65
+ if (!defs) {
66
+ defs = document.createElementNS("http://www.w3.org/2000/svg", "defs");
67
+ svg.appendChild(defs);
68
+ }
69
+ let fillValue = "transparent";
70
+ if (this.fillStyle === "solid") {
71
+ fillValue = this.fillColor;
72
+ } else if (this.fillStyle === "grid") {
73
+ const patternId = `frame-grid-${this.shapeID}`;
74
+ let pattern = defs.querySelector(`#${patternId}`);
75
+ if (!pattern) {
76
+ pattern = document.createElementNS("http://www.w3.org/2000/svg", "pattern");
77
+ pattern.setAttribute("id", patternId);
78
+ pattern.setAttribute("patternUnits", "userSpaceOnUse");
79
+ defs.appendChild(pattern);
80
+ }
81
+ pattern.setAttribute("x", this.x);
82
+ pattern.setAttribute("y", this.y);
83
+ pattern.setAttribute("width", this.gridSize);
84
+ pattern.setAttribute("height", this.gridSize);
85
+ while (pattern.firstChild) pattern.removeChild(pattern.firstChild);
86
+ const bgRect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
87
+ bgRect.setAttribute("width", this.gridSize);
88
+ bgRect.setAttribute("height", this.gridSize);
89
+ bgRect.setAttribute("fill", this.fillColor);
90
+ pattern.appendChild(bgRect);
91
+ const gridPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
92
+ gridPath.setAttribute("d", `M ${this.gridSize} 0 L 0 0 0 ${this.gridSize}`);
93
+ gridPath.setAttribute("fill", "none");
94
+ gridPath.setAttribute("stroke", this.gridColor);
95
+ gridPath.setAttribute("stroke-width", "0.5");
96
+ pattern.appendChild(gridPath);
97
+ fillValue = `url(#${patternId})`;
98
+ }
99
+ const frameRect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
100
+ frameRect.setAttribute("x", this.x);
101
+ frameRect.setAttribute("y", this.y);
102
+ frameRect.setAttribute("width", this.width);
103
+ frameRect.setAttribute("height", this.height);
104
+ frameRect.setAttribute("stroke", this.options.stroke);
105
+ frameRect.setAttribute("stroke-width", this.options.strokeWidth);
106
+ frameRect.setAttribute("fill", fillValue);
107
+ frameRect.setAttribute("opacity", this.options.opacity);
108
+ frameRect.setAttribute("stroke-dasharray", "5,5");
109
+ frameRect.classList.add("frame-rect");
110
+ if (this.rotation !== 0) {
111
+ const centerX = this.x + this.width / 2;
112
+ const centerY = this.y + this.height / 2;
113
+ frameRect.setAttribute("transform", `rotate(${this.rotation}, ${centerX}, ${centerY})`);
114
+ }
115
+ this.element = frameRect;
116
+ this.group.appendChild(this.element);
117
+ this.addFrameLabel();
118
+ if (this._frameImageURL) {
119
+ this.setImageFromURL(this._frameImageURL, this._frameImageFit || "cover");
120
+ }
121
+ if (this.isSelected) {
122
+ this.addAnchors();
123
+ }
124
+ this.updateClipPath();
125
+ }
126
+ updateAttachedArrows() {
127
+ if (typeof shapes !== "undefined" && Array.isArray(shapes)) {
128
+ shapes.forEach((shape) => {
129
+ if (shape.shapeName === "arrow") {
130
+ let needsUpdate = false;
131
+ if (shape.attachedToStart && shape.attachedToStart.shape === this) {
132
+ needsUpdate = true;
133
+ }
134
+ if (shape.attachedToEnd && shape.attachedToEnd.shape === this) {
135
+ needsUpdate = true;
136
+ }
137
+ if (needsUpdate) {
138
+ shape.updateAttachments();
139
+ }
140
+ }
141
+ });
142
+ }
143
+ }
144
+ updateClipPath() {
145
+ this.clipRect.setAttribute("x", this.x);
146
+ this.clipRect.setAttribute("y", this.y);
147
+ this.clipRect.setAttribute("width", this.width);
148
+ this.clipRect.setAttribute("height", this.height);
149
+ if (this.rotation !== 0) {
150
+ const centerX = this.x + this.width / 2;
151
+ const centerY = this.y + this.height / 2;
152
+ this.clipRect.setAttribute("transform", `rotate(${this.rotation}, ${centerX}, ${centerY})`);
153
+ } else {
154
+ this.clipRect.removeAttribute("transform");
155
+ }
156
+ }
157
+ addShapeToFrame(shape) {
158
+ if (shape && !this.containedShapes.includes(shape)) {
159
+ const oldFrame = shape.parentFrame;
160
+ if (typeof shapes !== "undefined" && Array.isArray(shapes)) {
161
+ shapes.forEach((otherFrame) => {
162
+ if (otherFrame.shapeName === "frame" && otherFrame !== this) {
163
+ otherFrame.removeShapeFromFrame(shape);
164
+ }
165
+ });
166
+ }
167
+ this.containedShapes.push(shape);
168
+ shape.parentFrame = this;
169
+ const el = shape.group || shape.element;
170
+ if (el && el.parentNode && !shape.isDraggedOutTemporarily) {
171
+ el.parentNode.removeChild(el);
172
+ this.clipGroup.appendChild(el);
173
+ }
174
+ if (shape.shapeName === "frame" && shape.clipGroup) {
175
+ if (shape.clipGroup.parentNode) {
176
+ shape.clipGroup.parentNode.removeChild(shape.clipGroup);
177
+ }
178
+ this.clipGroup.appendChild(shape.clipGroup);
179
+ }
180
+ }
181
+ }
182
+ removeShapeFromFrame(shape) {
183
+ const index = this.containedShapes.indexOf(shape);
184
+ if (index > -1) {
185
+ this.containedShapes.splice(index, 1);
186
+ if (shape.parentFrame === this) {
187
+ shape.parentFrame = null;
188
+ }
189
+ const el = shape.group || shape.element;
190
+ if (el && el.parentNode === this.clipGroup) {
191
+ this.clipGroup.removeChild(el);
192
+ svg.appendChild(el);
193
+ }
194
+ if (shape.shapeName === "frame" && shape.clipGroup && shape.clipGroup.parentNode === this.clipGroup) {
195
+ this.clipGroup.removeChild(shape.clipGroup);
196
+ svg.appendChild(shape.clipGroup);
197
+ }
198
+ delete shape.isDraggedOutTemporarily;
199
+ }
200
+ }
201
+ // Check if a point is inside the frame bounds
202
+ isPointInFrame(x, y) {
203
+ return x >= this.x && x <= this.x + this.width && y >= this.y && y <= this.y + this.height;
204
+ }
205
+ // Check if a shape overlaps with this frame
206
+ isShapeInFrame(shape) {
207
+ if (!shape || shape === this) return false;
208
+ const shapeX = shape.x || 0;
209
+ const shapeY = shape.y || 0;
210
+ const shapeWidth = shape.width || 0;
211
+ const shapeHeight = shape.height || 0;
212
+ const shapeCenterX = shapeX + shapeWidth / 2;
213
+ const shapeCenterY = shapeY + shapeHeight / 2;
214
+ return shapeCenterX >= this.x && shapeCenterX <= this.x + this.width && shapeCenterY >= this.y && shapeCenterY <= this.y + this.height;
215
+ }
216
+ updateContainedShapes(applyClipping = true) {
217
+ if (typeof shapes === "undefined" || !Array.isArray(shapes)) return;
218
+ shapes.forEach((shape) => {
219
+ if (shape !== this) {
220
+ if (shape.shapeName === "frame") {
221
+ const shapeArea = (shape.width || 0) * (shape.height || 0);
222
+ const thisArea = (this.width || 0) * (this.height || 0);
223
+ if (shapeArea >= thisArea) return;
224
+ }
225
+ const isInFrame = this.isShapeInFrame(shape);
226
+ const isAlreadyContained = this.containedShapes.includes(shape);
227
+ if (isInFrame && !isAlreadyContained) {
228
+ this.addShapeToFrame(shape);
229
+ if (!applyClipping) {
230
+ const el = shape.group || shape.element;
231
+ if (el && el.parentNode === this.clipGroup) {
232
+ this.clipGroup.removeChild(el);
233
+ svg.appendChild(el);
234
+ shape.isDraggedOutTemporarily = true;
235
+ }
236
+ }
237
+ } else if (!isInFrame && isAlreadyContained) {
238
+ this.removeShapeFromFrame(shape);
239
+ }
240
+ }
241
+ });
242
+ this.updateClipPath();
243
+ }
244
+ highlightFrame() {
245
+ if (this.element) {
246
+ this.element.setAttribute("stroke", "#5B57D1");
247
+ this.element.setAttribute("stroke-width", "3");
248
+ this.element.setAttribute("opacity", "0.7");
249
+ }
250
+ }
251
+ // Remove frame highlight
252
+ removeHighlight() {
253
+ if (this.element) {
254
+ this.element.setAttribute("stroke", this.options.stroke);
255
+ this.element.setAttribute("stroke-width", this.options.strokeWidth);
256
+ this.element.setAttribute("opacity", this.options.opacity);
257
+ }
258
+ }
259
+ move(dx, dy) {
260
+ this.x += dx;
261
+ this.y += dy;
262
+ this.containedShapes.forEach((shape) => {
263
+ if (shape) {
264
+ shape.isBeingMovedByFrame = true;
265
+ if (typeof shape.move === "function") {
266
+ shape.move(dx, dy);
267
+ } else {
268
+ shape.x += dx;
269
+ shape.y += dy;
270
+ }
271
+ if (typeof shape.draw === "function") {
272
+ shape.draw();
273
+ }
274
+ if (typeof shape.updateBoundingBox === "function") {
275
+ shape.updateBoundingBox();
276
+ }
277
+ delete shape.isBeingMovedByFrame;
278
+ }
279
+ });
280
+ this.updateAttachedArrows();
281
+ this.draw();
282
+ this.updateClipPath();
283
+ }
284
+ handleResizeLegacy(anchorIndex, currentPos, startPos, initialFrame) {
285
+ const dx = currentPos.x - startPos.x;
286
+ const dy = currentPos.y - startPos.y;
287
+ switch (anchorIndex) {
288
+ case 0:
289
+ this.x = initialFrame.x + dx;
290
+ this.y = initialFrame.y + dy;
291
+ this.width = Math.max(10, initialFrame.width - dx);
292
+ this.height = Math.max(10, initialFrame.height - dy);
293
+ break;
294
+ case 1:
295
+ this.y = initialFrame.y + dy;
296
+ this.height = Math.max(10, initialFrame.height - dy);
297
+ break;
298
+ case 2:
299
+ this.y = initialFrame.y + dy;
300
+ this.width = Math.max(10, initialFrame.width + dx);
301
+ this.height = Math.max(10, initialFrame.height - dy);
302
+ break;
303
+ case 3:
304
+ this.width = Math.max(10, initialFrame.width + dx);
305
+ break;
306
+ case 4:
307
+ this.width = Math.max(10, initialFrame.width + dx);
308
+ this.height = Math.max(10, initialFrame.height + dy);
309
+ break;
310
+ case 5:
311
+ this.height = Math.max(10, initialFrame.height + dy);
312
+ break;
313
+ case 6:
314
+ this.x = initialFrame.x + dx;
315
+ this.width = Math.max(10, initialFrame.width - dx);
316
+ this.height = Math.max(10, initialFrame.height + dy);
317
+ break;
318
+ case 7:
319
+ this.x = initialFrame.x + dx;
320
+ this.width = Math.max(10, initialFrame.width - dx);
321
+ break;
322
+ }
323
+ this.updateAttachedArrows();
324
+ this.updateContainedShapes();
325
+ }
326
+ destroy() {
327
+ const isDiagramFrame = !!this._diagramType;
328
+ [...this.containedShapes].forEach((shape) => {
329
+ if (isDiagramFrame) {
330
+ if (typeof window.cleanupAttachments === "function") {
331
+ window.cleanupAttachments(shape);
332
+ }
333
+ const el = shape.group || shape.element;
334
+ if (el && el.parentNode) {
335
+ el.parentNode.removeChild(el);
336
+ }
337
+ if (shape.shapeName === "frame") {
338
+ if (shape.clipGroup && shape.clipGroup.parentNode) {
339
+ shape.clipGroup.parentNode.removeChild(shape.clipGroup);
340
+ }
341
+ if (shape.clipPath && shape.clipPath.parentNode) {
342
+ shape.clipPath.parentNode.removeChild(shape.clipPath);
343
+ }
344
+ }
345
+ const idx = shapes.indexOf(shape);
346
+ if (idx > -1) {
347
+ shapes.splice(idx, 1);
348
+ }
349
+ } else {
350
+ const el = shape.group || shape.element;
351
+ if (el) {
352
+ if (el.parentNode === this.clipGroup) {
353
+ this.clipGroup.removeChild(el);
354
+ }
355
+ svg.appendChild(el);
356
+ }
357
+ if (shape.shapeName === "frame" && shape.clipGroup) {
358
+ if (shape.clipGroup.parentNode === this.clipGroup) {
359
+ this.clipGroup.removeChild(shape.clipGroup);
360
+ }
361
+ svg.appendChild(shape.clipGroup);
362
+ }
363
+ }
364
+ shape.parentFrame = null;
365
+ delete shape.isBeingMovedByFrame;
366
+ });
367
+ this.containedShapes = [];
368
+ if (this.clipPath && this.clipPath.parentNode) {
369
+ this.clipPath.parentNode.removeChild(this.clipPath);
370
+ }
371
+ if (this.clipGroup && this.clipGroup.parentNode) {
372
+ this.clipGroup.parentNode.removeChild(this.clipGroup);
373
+ }
374
+ if (this.group && this.group.parentNode) {
375
+ this.group.parentNode.removeChild(this.group);
376
+ }
377
+ const index = shapes.indexOf(this);
378
+ if (index > -1) {
379
+ shapes.splice(index, 1);
380
+ }
381
+ if (currentShape === this) {
382
+ currentShape = null;
383
+ }
384
+ }
385
+ addFrameLabel() {
386
+ const labelText = document.createElementNS("http://www.w3.org/2000/svg", "text");
387
+ labelText.setAttribute("x", this.x + 5);
388
+ labelText.setAttribute("y", this.y - 10);
389
+ labelText.setAttribute("font-size", `${16 / currentZoom}px`);
390
+ labelText.setAttribute("fill", this.options.stroke);
391
+ labelText.setAttribute("font-family", "lixFont");
392
+ labelText.textContent = this.frameName || "Frame";
393
+ labelText.style.cursor = "pointer";
394
+ labelText.style.userSelect = "none";
395
+ if (this.rotation !== 0) {
396
+ const centerX = this.x + this.width / 2;
397
+ const centerY = this.y + this.height / 2;
398
+ labelText.setAttribute("transform", `rotate(${this.rotation}, ${centerX}, ${centerY})`);
399
+ }
400
+ labelText.addEventListener("dblclick", (e) => {
401
+ e.stopPropagation();
402
+ this.startLabelEdit(labelText);
403
+ });
404
+ this.group.appendChild(labelText);
405
+ this.labelElement = labelText;
406
+ }
407
+ startLabelEdit(labelElement) {
408
+ const foreignObject = document.createElementNS("http://www.w3.org/2000/svg", "foreignObject");
409
+ foreignObject.setAttribute("x", this.x + 5);
410
+ foreignObject.setAttribute("y", this.y - 20);
411
+ foreignObject.setAttribute("width", Math.max(100, this.width - 10));
412
+ foreignObject.setAttribute("height", 20);
413
+ if (this.rotation !== 0) {
414
+ const centerX = this.x + this.width / 2;
415
+ const centerY = this.y + this.height / 2;
416
+ foreignObject.setAttribute("transform", `rotate(${this.rotation}, ${centerX}, ${centerY})`);
417
+ }
418
+ const input = document.createElement("input");
419
+ input.type = "text";
420
+ input.value = this.frameName || "Frame";
421
+ const oldName = this.frameName;
422
+ input.style.cssText = `
423
+ position: absolute;
424
+ width: 100%;
425
+ height: 20px;
426
+ border: 1px solid #5B57D1;
427
+ background: transparent;
428
+ color: ${this.options.stroke};
429
+ font-family: "lixFont";
430
+ font-size: ${15 / currentZoom}px;
431
+ padding: 2px 10px;
432
+ margin: 0;
433
+ outline: none;
434
+ border-radius: 2px;
435
+ box-sizing: border-box;
436
+ `;
437
+ foreignObject.appendChild(input);
438
+ this.group.appendChild(foreignObject);
439
+ labelElement.style.display = "none";
440
+ setTimeout(() => {
441
+ input.focus();
442
+ input.select();
443
+ }, 10);
444
+ const finishEdit = () => {
445
+ const newName = input.value.trim() || "Frame";
446
+ if (newName !== oldName) {
447
+ pushTransformAction(
448
+ this,
449
+ {
450
+ x: this.x,
451
+ y: this.y,
452
+ width: this.width,
453
+ height: this.height,
454
+ rotation: this.rotation,
455
+ frameName: oldName
456
+ },
457
+ {
458
+ x: this.x,
459
+ y: this.y,
460
+ width: this.width,
461
+ height: this.height,
462
+ rotation: this.rotation,
463
+ frameName: newName
464
+ }
465
+ );
466
+ }
467
+ this.frameName = newName;
468
+ this.group.removeChild(foreignObject);
469
+ labelElement.style.display = "block";
470
+ labelElement.textContent = this.frameName;
471
+ this.labelElement = labelElement;
472
+ };
473
+ input.addEventListener("blur", finishEdit);
474
+ input.addEventListener("keydown", (e) => {
475
+ if (e.key === "Enter") {
476
+ e.preventDefault();
477
+ finishEdit();
478
+ } else if (e.key === "Escape") {
479
+ e.preventDefault();
480
+ this.group.removeChild(foreignObject);
481
+ labelElement.style.display = "block";
482
+ }
483
+ });
484
+ input.addEventListener("pointerdown", (e) => e.stopPropagation());
485
+ input.addEventListener("pointermove", (e) => e.stopPropagation());
486
+ input.addEventListener("pointerup", (e) => e.stopPropagation());
487
+ }
488
+ selectFrame() {
489
+ this.isSelected = true;
490
+ this.draw();
491
+ this._showSidebar();
492
+ }
493
+ _showSidebar() {
494
+ if (window.__showSidebarForShape) {
495
+ window.__showSidebarForShape("frame");
496
+ }
497
+ const sidebar = document.getElementById("frameSideBar");
498
+ const renameInput = document.getElementById("frameRenameInput");
499
+ const resizeBtn = document.getElementById("frameResizeToFit");
500
+ if (!sidebar) return;
501
+ sidebar.classList.remove("hidden");
502
+ if (renameInput) {
503
+ renameInput.value = this.frameName || "Frame";
504
+ const newInput = renameInput.cloneNode(true);
505
+ renameInput.parentNode.replaceChild(newInput, renameInput);
506
+ newInput.addEventListener("input", () => {
507
+ const val = newInput.value.trim() || "Frame";
508
+ this.frameName = val;
509
+ if (this.labelElement) this.labelElement.textContent = val;
510
+ });
511
+ newInput.addEventListener("keydown", (e) => {
512
+ if (e.key === "Enter") newInput.blur();
513
+ });
514
+ newInput.addEventListener("pointerdown", (e) => e.stopPropagation());
515
+ }
516
+ if (resizeBtn) {
517
+ const newBtn = resizeBtn.cloneNode(true);
518
+ resizeBtn.parentNode.replaceChild(newBtn, resizeBtn);
519
+ newBtn.addEventListener("click", () => this.resizeToFit());
520
+ }
521
+ }
522
+ resizeToFit() {
523
+ if (this.containedShapes.length === 0) return;
524
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
525
+ this.containedShapes.forEach((shape) => {
526
+ if (shape.shapeName === "arrow" || shape.shapeName === "line") {
527
+ [shape.startPoint, shape.endPoint].filter(Boolean).forEach((p) => {
528
+ minX = Math.min(minX, p.x);
529
+ minY = Math.min(minY, p.y);
530
+ maxX = Math.max(maxX, p.x);
531
+ maxY = Math.max(maxY, p.y);
532
+ });
533
+ } else if (typeof shape.x === "number" && typeof shape.y === "number") {
534
+ const w = shape.width || 0;
535
+ const h = shape.height || 0;
536
+ minX = Math.min(minX, shape.x);
537
+ minY = Math.min(minY, shape.y);
538
+ maxX = Math.max(maxX, shape.x + w);
539
+ maxY = Math.max(maxY, shape.y + h);
540
+ }
541
+ });
542
+ if (!isFinite(minX)) return;
543
+ const padding = 20;
544
+ const oldState = { x: this.x, y: this.y, width: this.width, height: this.height, rotation: this.rotation };
545
+ this.x = minX - padding;
546
+ this.y = minY - padding;
547
+ this.width = maxX - minX + padding * 2;
548
+ this.height = maxY - minY + padding * 2;
549
+ pushTransformAction(this, oldState, { x: this.x, y: this.y, width: this.width, height: this.height, rotation: this.rotation });
550
+ this.draw();
551
+ this.updateClipPath();
552
+ this.updateContainedShapes();
553
+ }
554
+ removeSelection() {
555
+ this.anchors.forEach((anchor) => {
556
+ if (anchor.parentNode) {
557
+ anchor.parentNode.removeChild(anchor);
558
+ }
559
+ });
560
+ this.anchors = [];
561
+ this.isSelected = false;
562
+ this.draw();
563
+ const sidebar = document.getElementById("frameSideBar");
564
+ if (sidebar) sidebar.classList.add("hidden");
565
+ }
566
+ addAnchors() {
567
+ const anchorSize = 8 / currentZoom;
568
+ const anchorStrokeWidth = 2 / currentZoom;
569
+ const anchorPositions = [
570
+ { x: this.x, y: this.y, cursor: "nw-resize", type: "corner" },
571
+ // Top-left
572
+ { x: this.x + this.width / 2, y: this.y, cursor: "n-resize", type: "edge" },
573
+ // Top-middle
574
+ { x: this.x + this.width, y: this.y, cursor: "ne-resize", type: "corner" },
575
+ // Top-right
576
+ { x: this.x + this.width, y: this.y + this.height / 2, cursor: "e-resize", type: "edge" },
577
+ // Right-middle
578
+ { x: this.x + this.width, y: this.y + this.height, cursor: "se-resize", type: "corner" },
579
+ // Bottom-right
580
+ { x: this.x + this.width / 2, y: this.y + this.height, cursor: "s-resize", type: "edge" },
581
+ // Bottom-middle
582
+ { x: this.x, y: this.y + this.height, cursor: "sw-resize", type: "corner" },
583
+ // Bottom-left
584
+ { x: this.x, y: this.y + this.height / 2, cursor: "w-resize", type: "edge" }
585
+ // Left-middle
586
+ ];
587
+ const rotationHandleDistance = 30 / currentZoom;
588
+ const rotationHandle = {
589
+ x: this.x + this.width / 2,
590
+ y: this.y - rotationHandleDistance,
591
+ cursor: "grab",
592
+ type: "rotation"
593
+ };
594
+ anchorPositions.push(rotationHandle);
595
+ const outline = document.createElementNS("http://www.w3.org/2000/svg", "rect");
596
+ outline.setAttribute("x", this.x);
597
+ outline.setAttribute("y", this.y);
598
+ outline.setAttribute("width", this.width);
599
+ outline.setAttribute("height", this.height);
600
+ outline.setAttribute("fill", "none");
601
+ outline.setAttribute("stroke", "#5B57D1");
602
+ outline.setAttribute("stroke-width", 1.5);
603
+ outline.setAttribute("vector-effect", "non-scaling-stroke");
604
+ outline.setAttribute("stroke-dasharray", "4 2");
605
+ outline.setAttribute("style", "pointer-events: none;");
606
+ if (this.rotation !== 0) {
607
+ const centerX = this.x + this.width / 2;
608
+ const centerY = this.y + this.height / 2;
609
+ outline.setAttribute("transform", `rotate(${this.rotation}, ${centerX}, ${centerY})`);
610
+ }
611
+ this.group.appendChild(outline);
612
+ this.selectionOutline = outline;
613
+ anchorPositions.forEach((position, index) => {
614
+ let anchor;
615
+ if (position.type === "rotation") {
616
+ anchor = document.createElementNS("http://www.w3.org/2000/svg", "circle");
617
+ anchor.setAttribute("cx", position.x);
618
+ anchor.setAttribute("cy", position.y);
619
+ anchor.setAttribute("r", anchorSize);
620
+ anchor.setAttribute("fill", "#121212");
621
+ anchor.setAttribute("stroke", "#5B57D1");
622
+ anchor.setAttribute("stroke-width", anchorStrokeWidth);
623
+ anchor.setAttribute("vector-effect", "non-scaling-stroke");
624
+ const rotationLine = document.createElementNS("http://www.w3.org/2000/svg", "line");
625
+ rotationLine.setAttribute("x1", this.x + this.width / 2);
626
+ rotationLine.setAttribute("y1", this.y);
627
+ rotationLine.setAttribute("x2", position.x);
628
+ rotationLine.setAttribute("y2", position.y);
629
+ rotationLine.setAttribute("stroke", "#5B57D1");
630
+ rotationLine.setAttribute("stroke-width", anchorStrokeWidth);
631
+ rotationLine.setAttribute("vector-effect", "non-scaling-stroke");
632
+ if (this.rotation !== 0) {
633
+ const centerX = this.x + this.width / 2;
634
+ const centerY = this.y + this.height / 2;
635
+ rotationLine.setAttribute("transform", `rotate(${this.rotation}, ${centerX}, ${centerY})`);
636
+ }
637
+ this.group.appendChild(rotationLine);
638
+ } else {
639
+ anchor = document.createElementNS("http://www.w3.org/2000/svg", "rect");
640
+ anchor.setAttribute("x", position.x - anchorSize / 2);
641
+ anchor.setAttribute("y", position.y - anchorSize / 2);
642
+ anchor.setAttribute("width", anchorSize);
643
+ anchor.setAttribute("height", anchorSize);
644
+ anchor.setAttribute("fill", "#121212");
645
+ anchor.setAttribute("stroke", "#5B57D1");
646
+ anchor.setAttribute("stroke-width", anchorStrokeWidth);
647
+ anchor.setAttribute("vector-effect", "non-scaling-stroke");
648
+ }
649
+ if (this.rotation !== 0) {
650
+ const centerX = this.x + this.width / 2;
651
+ const centerY = this.y + this.height / 2;
652
+ anchor.setAttribute("transform", `rotate(${this.rotation}, ${centerX}, ${centerY})`);
653
+ }
654
+ anchor.style.cursor = position.cursor;
655
+ anchor.addEventListener("pointerdown", (e) => this.startAnchorDrag(e, index));
656
+ this.anchors.push(anchor);
657
+ this.group.appendChild(anchor);
658
+ });
659
+ }
660
+ startAnchorDrag(e, index) {
661
+ e.stopPropagation();
662
+ e.preventDefault();
663
+ dragOldPosFrame = {
664
+ x: this.x,
665
+ y: this.y,
666
+ width: this.width,
667
+ height: this.height,
668
+ rotation: this.rotation
669
+ };
670
+ const startMousePos = getSVGCoordsFromMouse(e);
671
+ const initialFrame = {
672
+ x: this.x,
673
+ y: this.y,
674
+ width: this.width,
675
+ height: this.height,
676
+ rotation: this.rotation
677
+ };
678
+ const onPointerMove = (event) => {
679
+ const currentMousePos = getSVGCoordsFromMouse(event);
680
+ if (index === 8) {
681
+ this.handleRotation(currentMousePos, initialFrame);
682
+ } else {
683
+ this.handleResize(index, currentMousePos, startMousePos, initialFrame);
684
+ }
685
+ this.draw();
686
+ this.updateContainedShapes();
687
+ };
688
+ const onPointerUp = () => {
689
+ if (dragOldPosFrame) {
690
+ const newPos = {
691
+ x: this.x,
692
+ y: this.y,
693
+ width: this.width,
694
+ height: this.height,
695
+ rotation: this.rotation
696
+ };
697
+ pushTransformAction(this, dragOldPosFrame, newPos);
698
+ dragOldPosFrame = null;
699
+ }
700
+ svg.removeEventListener("pointermove", onPointerMove);
701
+ svg.removeEventListener("pointerup", onPointerUp);
702
+ svg.style.cursor = "default";
703
+ };
704
+ svg.addEventListener("pointermove", onPointerMove);
705
+ svg.addEventListener("pointerup", onPointerUp);
706
+ }
707
+ handleRotation(mousePos, initialFrame) {
708
+ const centerX = initialFrame.x + initialFrame.width / 2;
709
+ const centerY = initialFrame.y + initialFrame.height / 2;
710
+ const angle = Math.atan2(mousePos.y - centerY, mousePos.x - centerX);
711
+ const newRotation = (angle * 180 / Math.PI + 90) % 360;
712
+ const angleDiff = newRotation - this.rotation;
713
+ const angleRad = angleDiff * Math.PI / 180;
714
+ const cosAngle = Math.cos(angleRad);
715
+ const sinAngle = Math.sin(angleRad);
716
+ const rotationCenter = { x: centerX, y: centerY };
717
+ this.containedShapes.forEach((shape) => {
718
+ if (!shape) return;
719
+ shape.isBeingMovedByFrame = true;
720
+ switch (shape.shapeName) {
721
+ case "rectangle":
722
+ case "frame":
723
+ case "image":
724
+ case "icon":
725
+ const shapeCenterX = shape.x + shape.width / 2;
726
+ const shapeCenterY = shape.y + shape.height / 2;
727
+ const relativeX = shapeCenterX - rotationCenter.x;
728
+ const relativeY = shapeCenterY - rotationCenter.y;
729
+ const newCenterX = rotationCenter.x + (relativeX * cosAngle - relativeY * sinAngle);
730
+ const newCenterY = rotationCenter.y + (relativeX * sinAngle + relativeY * cosAngle);
731
+ shape.x = newCenterX - shape.width / 2;
732
+ shape.y = newCenterY - shape.height / 2;
733
+ shape.rotation = (shape.rotation || 0) + angleDiff;
734
+ if (typeof shape.draw === "function") {
735
+ shape.draw();
736
+ }
737
+ break;
738
+ case "circle":
739
+ const circleCenterX = shape.x;
740
+ const circleCenterY = shape.y;
741
+ const relativeCircleX = circleCenterX - rotationCenter.x;
742
+ const relativeCircleY = circleCenterY - rotationCenter.y;
743
+ const newCircleCenterX = rotationCenter.x + (relativeCircleX * cosAngle - relativeCircleY * sinAngle);
744
+ const newCircleCenterY = rotationCenter.y + (relativeCircleX * sinAngle + relativeCircleY * cosAngle);
745
+ shape.x = newCircleCenterX;
746
+ shape.y = newCircleCenterY;
747
+ shape.rotation = (shape.rotation || 0) + angleDiff;
748
+ if (typeof shape.draw === "function") {
749
+ shape.draw();
750
+ }
751
+ break;
752
+ case "line":
753
+ case "arrow":
754
+ const relativeStartX = shape.startPoint.x - rotationCenter.x;
755
+ const relativeStartY = shape.startPoint.y - rotationCenter.y;
756
+ const relativeEndX = shape.endPoint.x - rotationCenter.x;
757
+ const relativeEndY = shape.endPoint.y - rotationCenter.y;
758
+ const newStartX = rotationCenter.x + (relativeStartX * cosAngle - relativeStartY * sinAngle);
759
+ const newStartY = rotationCenter.y + (relativeStartX * sinAngle + relativeStartY * cosAngle);
760
+ const newEndX = rotationCenter.x + (relativeEndX * cosAngle - relativeEndY * sinAngle);
761
+ const newEndY = rotationCenter.y + (relativeEndX * sinAngle + relativeEndY * cosAngle);
762
+ shape.startPoint.x = newStartX;
763
+ shape.startPoint.y = newStartY;
764
+ shape.endPoint.x = newEndX;
765
+ shape.endPoint.y = newEndY;
766
+ if (shape.shapeName === "arrow" && shape.arrowCurved && typeof shape.initializeCurveControlPoints === "function") {
767
+ shape.initializeCurveControlPoints();
768
+ }
769
+ if (typeof shape.draw === "function") {
770
+ shape.draw();
771
+ }
772
+ break;
773
+ case "text":
774
+ case "code":
775
+ const textOrCodeEl = shape.group ? shape.group.querySelector("text") || shape.group.querySelector("foreignObject") : null;
776
+ if (textOrCodeEl) {
777
+ const bbox = textOrCodeEl.getBBox();
778
+ const transform = shape.group.transform.baseVal.consolidate();
779
+ const matrix = transform ? transform.matrix : { e: 0, f: 0 };
780
+ const textCenterX = bbox.x + matrix.e + bbox.width / 2;
781
+ const textCenterY = bbox.y + matrix.f + bbox.height / 2;
782
+ const relativeTextX = textCenterX - rotationCenter.x;
783
+ const relativeTextY = textCenterY - rotationCenter.y;
784
+ const newTextCenterX = rotationCenter.x + (relativeTextX * cosAngle - relativeTextY * sinAngle);
785
+ const newTextCenterY = rotationCenter.y + (relativeTextX * sinAngle + relativeTextY * cosAngle);
786
+ const newTransformX = newTextCenterX - bbox.width / 2;
787
+ const newTransformY = newTextCenterY - bbox.height / 2;
788
+ const newShapeRotation = (shape.rotation || 0) + angleDiff;
789
+ const textCenterRelativeX = bbox.width / 2;
790
+ const textCenterRelativeY = bbox.height / 2;
791
+ shape.group.setAttribute(
792
+ "transform",
793
+ `translate(${newTransformX}, ${newTransformY}) rotate(${newShapeRotation}, ${textCenterRelativeX}, ${textCenterRelativeY})`
794
+ );
795
+ shape.x = newTransformX;
796
+ shape.y = newTransformY;
797
+ shape.rotation = newShapeRotation;
798
+ }
799
+ break;
800
+ case "freehandStroke":
801
+ if (shape.points && Array.isArray(shape.points)) {
802
+ shape.points = shape.points.map((point) => {
803
+ if (!Array.isArray(point) || point.length < 2) return point;
804
+ const relativePointX = point[0] - rotationCenter.x;
805
+ const relativePointY = point[1] - rotationCenter.y;
806
+ const newPointX = rotationCenter.x + (relativePointX * cosAngle - relativePointY * sinAngle);
807
+ const newPointY = rotationCenter.y + (relativePointX * sinAngle + relativePointY * cosAngle);
808
+ return [newPointX, newPointY, point[2] || 0.5];
809
+ });
810
+ if (typeof shape.updateBoundingBox === "function") {
811
+ shape.updateBoundingBox();
812
+ }
813
+ if (typeof shape.draw === "function") {
814
+ shape.draw();
815
+ }
816
+ }
817
+ break;
818
+ }
819
+ if (typeof shape.updateAttachedArrows === "function") {
820
+ shape.updateAttachedArrows();
821
+ }
822
+ delete shape.isBeingMovedByFrame;
823
+ });
824
+ this.rotation = newRotation;
825
+ this.updateAttachedArrows();
826
+ }
827
+ handleResize(anchorIndex, currentPos, startPos, initialFrame) {
828
+ const rawDx = currentPos.x - startPos.x;
829
+ const rawDy = currentPos.y - startPos.y;
830
+ const rad = (this.rotation || 0) * Math.PI / 180;
831
+ const cosR = Math.cos(rad);
832
+ const sinR = Math.sin(rad);
833
+ const dx = rawDx * cosR + rawDy * sinR;
834
+ const dy = -rawDx * sinR + rawDy * cosR;
835
+ const oldX = this.x, oldY = this.y, oldW = this.width, oldH = this.height;
836
+ let newW, newH;
837
+ let fixRelX, fixRelY;
838
+ switch (anchorIndex) {
839
+ case 0:
840
+ newW = Math.max(10, initialFrame.width - dx);
841
+ newH = Math.max(10, initialFrame.height - dy);
842
+ fixRelX = initialFrame.width;
843
+ fixRelY = initialFrame.height;
844
+ break;
845
+ case 1:
846
+ newW = initialFrame.width;
847
+ newH = Math.max(10, initialFrame.height - dy);
848
+ fixRelX = 0;
849
+ fixRelY = initialFrame.height;
850
+ break;
851
+ case 2:
852
+ newW = Math.max(10, initialFrame.width + dx);
853
+ newH = Math.max(10, initialFrame.height - dy);
854
+ fixRelX = 0;
855
+ fixRelY = initialFrame.height;
856
+ break;
857
+ case 3:
858
+ newW = Math.max(10, initialFrame.width + dx);
859
+ newH = initialFrame.height;
860
+ fixRelX = 0;
861
+ fixRelY = 0;
862
+ break;
863
+ case 4:
864
+ newW = Math.max(10, initialFrame.width + dx);
865
+ newH = Math.max(10, initialFrame.height + dy);
866
+ fixRelX = 0;
867
+ fixRelY = 0;
868
+ break;
869
+ case 5:
870
+ newW = initialFrame.width;
871
+ newH = Math.max(10, initialFrame.height + dy);
872
+ fixRelX = 0;
873
+ fixRelY = 0;
874
+ break;
875
+ case 6:
876
+ newW = Math.max(10, initialFrame.width - dx);
877
+ newH = Math.max(10, initialFrame.height + dy);
878
+ fixRelX = initialFrame.width;
879
+ fixRelY = 0;
880
+ break;
881
+ case 7:
882
+ newW = Math.max(10, initialFrame.width - dx);
883
+ newH = initialFrame.height;
884
+ fixRelX = initialFrame.width;
885
+ fixRelY = 0;
886
+ break;
887
+ }
888
+ if (this.rotation && this.rotation !== 0) {
889
+ const iCX = initialFrame.x + initialFrame.width / 2;
890
+ const iCY = initialFrame.y + initialFrame.height / 2;
891
+ const odx = initialFrame.x + fixRelX - iCX;
892
+ const ody = initialFrame.y + fixRelY - iCY;
893
+ const fixedWorldX = iCX + odx * cosR - ody * sinR;
894
+ const fixedWorldY = iCY + odx * sinR + ody * cosR;
895
+ const fixNewRelX = fixRelX === 0 ? 0 : newW;
896
+ const fixNewRelY = fixRelY === 0 ? 0 : newH;
897
+ const ncx = newW / 2;
898
+ const ncy = newH / 2;
899
+ const ndx = fixNewRelX - ncx;
900
+ const ndy = fixNewRelY - ncy;
901
+ const rotX = ncx + ndx * cosR - ndy * sinR;
902
+ const rotY = ncy + ndx * sinR + ndy * cosR;
903
+ this.x = fixedWorldX - rotX;
904
+ this.y = fixedWorldY - rotY;
905
+ } else {
906
+ switch (anchorIndex) {
907
+ case 0:
908
+ this.x = initialFrame.x + (initialFrame.width - newW);
909
+ this.y = initialFrame.y + (initialFrame.height - newH);
910
+ break;
911
+ case 1:
912
+ this.y = initialFrame.y + (initialFrame.height - newH);
913
+ break;
914
+ case 2:
915
+ this.y = initialFrame.y + (initialFrame.height - newH);
916
+ break;
917
+ case 3:
918
+ break;
919
+ case 4:
920
+ break;
921
+ case 5:
922
+ break;
923
+ case 6:
924
+ this.x = initialFrame.x + (initialFrame.width - newW);
925
+ break;
926
+ case 7:
927
+ this.x = initialFrame.x + (initialFrame.width - newW);
928
+ break;
929
+ }
930
+ }
931
+ this.width = newW;
932
+ this.height = newH;
933
+ if (oldW > 0 && oldH > 0) {
934
+ const scaleX = this.width / oldW;
935
+ const scaleY = this.height / oldH;
936
+ if (Math.abs(scaleX - 1) > 1e-3 || Math.abs(scaleY - 1) > 1e-3) {
937
+ this.containedShapes.forEach((shape) => {
938
+ if (!shape) return;
939
+ shape.isBeingMovedByFrame = true;
940
+ switch (shape.shapeName) {
941
+ case "rectangle":
942
+ case "image":
943
+ case "icon":
944
+ case "code": {
945
+ const relX = (shape.x - oldX) / oldW;
946
+ const relY = (shape.y - oldY) / oldH;
947
+ const relW = shape.width / oldW;
948
+ const relH = shape.height / oldH;
949
+ shape.x = this.x + relX * this.width;
950
+ shape.y = this.y + relY * this.height;
951
+ shape.width = relW * this.width;
952
+ shape.height = relH * this.height;
953
+ if (typeof shape.draw === "function") shape.draw();
954
+ break;
955
+ }
956
+ case "circle": {
957
+ const relCX = (shape.x - oldX) / oldW;
958
+ const relCY = (shape.y - oldY) / oldH;
959
+ shape.x = this.x + relCX * this.width;
960
+ shape.y = this.y + relCY * this.height;
961
+ shape.rx = shape.rx / oldW * this.width;
962
+ shape.ry = shape.ry / oldH * this.height;
963
+ shape.width = shape.rx * 2;
964
+ shape.height = shape.ry * 2;
965
+ if (typeof shape.draw === "function") shape.draw();
966
+ break;
967
+ }
968
+ case "arrow":
969
+ case "line": {
970
+ const relSX = (shape.startPoint.x - oldX) / oldW;
971
+ const relSY = (shape.startPoint.y - oldY) / oldH;
972
+ const relEX = (shape.endPoint.x - oldX) / oldW;
973
+ const relEY = (shape.endPoint.y - oldY) / oldH;
974
+ shape.startPoint.x = this.x + relSX * this.width;
975
+ shape.startPoint.y = this.y + relSY * this.height;
976
+ shape.endPoint.x = this.x + relEX * this.width;
977
+ shape.endPoint.y = this.y + relEY * this.height;
978
+ if (shape.controlPoint1) {
979
+ const relC1X = (shape.controlPoint1.x - oldX) / oldW;
980
+ const relC1Y = (shape.controlPoint1.y - oldY) / oldH;
981
+ shape.controlPoint1.x = this.x + relC1X * this.width;
982
+ shape.controlPoint1.y = this.y + relC1Y * this.height;
983
+ }
984
+ if (shape.controlPoint2) {
985
+ const relC2X = (shape.controlPoint2.x - oldX) / oldW;
986
+ const relC2Y = (shape.controlPoint2.y - oldY) / oldH;
987
+ shape.controlPoint2.x = this.x + relC2X * this.width;
988
+ shape.controlPoint2.y = this.y + relC2Y * this.height;
989
+ }
990
+ if (typeof shape.draw === "function") shape.draw();
991
+ break;
992
+ }
993
+ case "text": {
994
+ const tx = shape.x || 0;
995
+ const ty = shape.y || 0;
996
+ const relTX = (tx - oldX) / oldW;
997
+ const relTY = (ty - oldY) / oldH;
998
+ shape.x = this.x + relTX * this.width;
999
+ shape.y = this.y + relTY * this.height;
1000
+ break;
1001
+ }
1002
+ }
1003
+ if (typeof shape.updateAttachedArrows === "function") {
1004
+ shape.updateAttachedArrows();
1005
+ }
1006
+ delete shape.isBeingMovedByFrame;
1007
+ });
1008
+ }
1009
+ }
1010
+ this.updateContainedShapes();
1011
+ }
1012
+ temporarilyRemoveFromFrame(shape) {
1013
+ if (shape && shape.parentFrame === this) {
1014
+ const el = shape.group || shape.element;
1015
+ if (el && el.parentNode === this.clipGroup) {
1016
+ this.clipGroup.removeChild(el);
1017
+ svg.appendChild(el);
1018
+ }
1019
+ shape.isDraggedOutTemporarily = true;
1020
+ }
1021
+ }
1022
+ // Add a method to restore clipping for a shape after drag
1023
+ restoreToFrame(shape) {
1024
+ if (shape && shape.parentFrame === this && shape.isDraggedOutTemporarily) {
1025
+ const el = shape.group || shape.element;
1026
+ if (el && el.parentNode === svg) {
1027
+ svg.removeChild(el);
1028
+ this.clipGroup.appendChild(el);
1029
+ }
1030
+ delete shape.isDraggedOutTemporarily;
1031
+ }
1032
+ }
1033
+ contains(viewBoxX, viewBoxY) {
1034
+ return viewBoxX >= this.x && viewBoxX <= this.x + this.width && viewBoxY >= this.y && viewBoxY <= this.y + this.height;
1035
+ }
1036
+ // Arrow attachment support (similar to other shapes)
1037
+ getAttachmentPoint(point, tolerance = 20) {
1038
+ const rect = {
1039
+ left: this.x,
1040
+ right: this.x + this.width,
1041
+ top: this.y,
1042
+ bottom: this.y + this.height
1043
+ };
1044
+ const distances = {
1045
+ top: Math.abs(point.y - rect.top),
1046
+ bottom: Math.abs(point.y - rect.bottom),
1047
+ left: Math.abs(point.x - rect.left),
1048
+ right: Math.abs(point.x - rect.right)
1049
+ };
1050
+ let closestSide = null;
1051
+ let minDistance = tolerance;
1052
+ for (let side in distances) {
1053
+ if (distances[side] < minDistance && ((side === "top" || side === "bottom") && point.x >= rect.left && point.x <= rect.right) || (side === "left" || side === "right") && point.y >= rect.top && point.y <= rect.bottom) {
1054
+ minDistance = distances[side];
1055
+ closestSide = side;
1056
+ }
1057
+ }
1058
+ if (closestSide) {
1059
+ let attachPoint;
1060
+ const offset = { x: 0, y: 0 };
1061
+ switch (closestSide) {
1062
+ case "top":
1063
+ attachPoint = { x: point.x, y: rect.top };
1064
+ offset.x = point.x - (rect.left + this.width / 2);
1065
+ break;
1066
+ case "bottom":
1067
+ attachPoint = { x: point.x, y: rect.bottom };
1068
+ offset.x = point.x - (rect.left + this.width / 2);
1069
+ break;
1070
+ case "left":
1071
+ attachPoint = { x: rect.left, y: point.y };
1072
+ offset.y = point.y - (rect.top + this.height / 2);
1073
+ break;
1074
+ case "right":
1075
+ attachPoint = { x: rect.right, y: point.y };
1076
+ offset.y = point.y - (rect.top + this.height / 2);
1077
+ break;
1078
+ }
1079
+ return { point: attachPoint, side: closestSide, offset, shape: this };
1080
+ }
1081
+ return null;
1082
+ }
1083
+ updateStyle(newOptions) {
1084
+ Object.keys(newOptions).forEach((key) => newOptions[key] === void 0 && delete newOptions[key]);
1085
+ this.options = { ...this.options, ...newOptions };
1086
+ this.draw();
1087
+ }
1088
+ destroy() {
1089
+ const isDiagramFrame = !!this._diagramType;
1090
+ [...this.containedShapes].forEach((shape) => {
1091
+ if (isDiagramFrame) {
1092
+ if (typeof window.cleanupAttachments === "function") {
1093
+ window.cleanupAttachments(shape);
1094
+ }
1095
+ const el = shape.group || shape.element;
1096
+ if (el && el.parentNode) {
1097
+ el.parentNode.removeChild(el);
1098
+ }
1099
+ if (shape.shapeName === "frame") {
1100
+ if (shape.clipGroup && shape.clipGroup.parentNode) {
1101
+ shape.clipGroup.parentNode.removeChild(shape.clipGroup);
1102
+ }
1103
+ if (shape.clipPath && shape.clipPath.parentNode) {
1104
+ shape.clipPath.parentNode.removeChild(shape.clipPath);
1105
+ }
1106
+ }
1107
+ const idx = shapes.indexOf(shape);
1108
+ if (idx > -1) {
1109
+ shapes.splice(idx, 1);
1110
+ }
1111
+ } else {
1112
+ const el = shape.group || shape.element;
1113
+ if (el) {
1114
+ if (el.parentNode === this.clipGroup) {
1115
+ this.clipGroup.removeChild(el);
1116
+ }
1117
+ svg.appendChild(el);
1118
+ }
1119
+ if (shape.shapeName === "frame" && shape.clipGroup) {
1120
+ if (shape.clipGroup.parentNode === this.clipGroup) {
1121
+ this.clipGroup.removeChild(shape.clipGroup);
1122
+ }
1123
+ svg.appendChild(shape.clipGroup);
1124
+ }
1125
+ }
1126
+ shape.parentFrame = null;
1127
+ delete shape.isBeingMovedByFrame;
1128
+ });
1129
+ this.containedShapes = [];
1130
+ if (this.clipPath && this.clipPath.parentNode) {
1131
+ this.clipPath.parentNode.removeChild(this.clipPath);
1132
+ }
1133
+ if (this.clipGroup && this.clipGroup.parentNode) {
1134
+ this.clipGroup.parentNode.removeChild(this.clipGroup);
1135
+ }
1136
+ if (this.group && this.group.parentNode) {
1137
+ this.group.parentNode.removeChild(this.group);
1138
+ }
1139
+ const index = shapes.indexOf(this);
1140
+ if (index > -1) {
1141
+ shapes.splice(index, 1);
1142
+ }
1143
+ if (currentShape === this) {
1144
+ currentShape = null;
1145
+ }
1146
+ }
1147
+ /**
1148
+ * Set a background image for this frame from a URL.
1149
+ * The image will fit/cover the frame dimensions.
1150
+ * @param {string} url - Image URL
1151
+ * @param {string} fit - 'cover' (default) | 'contain' | 'fill'
1152
+ */
1153
+ setImageFromURL(url, fit = "cover") {
1154
+ if (!url) {
1155
+ if (this._frameImage && this._frameImage.parentNode) {
1156
+ this._frameImage.parentNode.removeChild(this._frameImage);
1157
+ this._frameImage = null;
1158
+ }
1159
+ this._frameImageURL = null;
1160
+ return;
1161
+ }
1162
+ this._frameImageURL = url;
1163
+ this._frameImageFit = fit;
1164
+ if (!this._frameImage) {
1165
+ this._frameImage = document.createElementNS("http://www.w3.org/2000/svg", "image");
1166
+ this._frameImage.setAttribute("pointer-events", "none");
1167
+ }
1168
+ this._frameImage.setAttribute(
1169
+ "preserveAspectRatio",
1170
+ fit === "cover" ? "xMidYMid slice" : fit === "contain" ? "xMidYMid meet" : fit === "center" ? "xMidYMid meet" : "none"
1171
+ );
1172
+ this._frameImage.setAttribute("href", url);
1173
+ this._frameImage.setAttribute("x", this.x);
1174
+ this._frameImage.setAttribute("y", this.y);
1175
+ this._frameImage.setAttribute("width", this.width);
1176
+ this._frameImage.setAttribute("height", this.height);
1177
+ if (this.rotation !== 0) {
1178
+ const centerX = this.x + this.width / 2;
1179
+ const centerY = this.y + this.height / 2;
1180
+ this._frameImage.setAttribute("transform", `rotate(${this.rotation}, ${centerX}, ${centerY})`);
1181
+ }
1182
+ if (this._frameImage.parentNode === this.group) {
1183
+ this.group.removeChild(this._frameImage);
1184
+ }
1185
+ if (this.element && this.element.nextSibling) {
1186
+ this.group.insertBefore(this._frameImage, this.element.nextSibling);
1187
+ } else {
1188
+ this.group.appendChild(this._frameImage);
1189
+ }
1190
+ }
1191
+ /**
1192
+ * Update frame image position/size (called during move/resize)
1193
+ */
1194
+ _updateFrameImage() {
1195
+ if (!this._frameImage) return;
1196
+ this._frameImage.setAttribute("x", this.x);
1197
+ this._frameImage.setAttribute("y", this.y);
1198
+ this._frameImage.setAttribute("width", this.width);
1199
+ this._frameImage.setAttribute("height", this.height);
1200
+ if (this.rotation !== 0) {
1201
+ const centerX = this.x + this.width / 2;
1202
+ const centerY = this.y + this.height / 2;
1203
+ this._frameImage.setAttribute("transform", `rotate(${this.rotation}, ${centerX}, ${centerY})`);
1204
+ } else {
1205
+ this._frameImage.removeAttribute("transform");
1206
+ }
1207
+ }
1208
+ };
1209
+
1210
+ export {
1211
+ Frame
1212
+ };
1213
+ //# sourceMappingURL=chunk-BU4D2WYQ.js.map