@elixpo/lixsketch 5.4.7 → 5.5.0

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 +5342 -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 +475 -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,1600 @@
1
+ "use client";
2
+ import {
3
+ cleanupAttachments,
4
+ updateAttachedArrows
5
+ } from "./chunk-BZE4CEBG.js";
6
+ import {
7
+ pushCreateAction,
8
+ pushDeleteActionWithAttachments,
9
+ pushFrameAttachmentAction,
10
+ pushOptionsChangeAction,
11
+ pushTransformAction,
12
+ setTextReferences,
13
+ updateSelectedElement
14
+ } from "./chunk-L5OARZJ7.js";
15
+
16
+ // src/tools/codeTool.js
17
+ function getThemeStroke() {
18
+ if (typeof document === "undefined") return "#fff";
19
+ return document.body && document.body.classList.contains("theme-dark") ? "#fff" : "#1a1a2e";
20
+ }
21
+ var codeTextSize = "25px";
22
+ var codeTextFont = "lixCode";
23
+ var codeTextColor = null;
24
+ var codeTextAlign = "left";
25
+ var codeLanguage = "auto";
26
+ var codeTextColorOptions = document.querySelectorAll(".textColorSpan");
27
+ var codeTextFontOptions = document.querySelectorAll(".textFontSpan");
28
+ var codeTextSizeOptions = document.querySelectorAll(".textSizeSpan");
29
+ var codeTextAlignOptions = document.querySelectorAll(".textAlignSpan");
30
+ var selectedCodeBlock = null;
31
+ var codeSelectionBox = null;
32
+ var codeResizeHandles = {};
33
+ var codeDragOffsetX;
34
+ var codeDragOffsetY;
35
+ var isCodeDragging = false;
36
+ var isCodeResizing = false;
37
+ var currentCodeResizeHandle = null;
38
+ var startCodeBBox = null;
39
+ var startCodeFontSize = null;
40
+ var startCodePoint = null;
41
+ var isCodeRotating = false;
42
+ var codeRotationStartAngle = 0;
43
+ var codeRotationStartTransform = null;
44
+ var initialCodeHandlePosRelGroup = null;
45
+ var initialCodeGroupTx = 0;
46
+ var initialCodeGroupTy = 0;
47
+ var draggedCodeInitialFrame = null;
48
+ var hoveredCodeFrame = null;
49
+ setTextReferences(selectedCodeBlock, updateCodeSelectionFeedback, svg);
50
+ function wrapCodeElement(groupElement) {
51
+ const codeShape = new CodeShape(groupElement);
52
+ groupElement.addEventListener("dblclick", (e) => {
53
+ e.stopPropagation();
54
+ const codeElement = groupElement.querySelector("text");
55
+ if (codeElement) {
56
+ makeCodeEditable(codeElement, groupElement, e);
57
+ }
58
+ });
59
+ return codeShape;
60
+ }
61
+ function getSVGCoordinates(event, element = svg) {
62
+ if (!svg || !svg.createSVGPoint) {
63
+ console.error("SVG element or createSVGPoint method not available.");
64
+ return { x: 0, y: 0 };
65
+ }
66
+ let pt = svg.createSVGPoint();
67
+ pt.x = event.clientX;
68
+ pt.y = event.clientY;
69
+ try {
70
+ let screenCTM = element && typeof element.getScreenCTM === "function" && element.getScreenCTM() || svg.getScreenCTM();
71
+ if (!screenCTM) {
72
+ console.error("Could not get Screen CTM.");
73
+ return { x: event.clientX, y: event.clientY };
74
+ }
75
+ let svgPoint = pt.matrixTransform(screenCTM.inverse());
76
+ return {
77
+ x: svgPoint.x,
78
+ y: svgPoint.y
79
+ };
80
+ } catch (error) {
81
+ console.error("Error getting SVG coordinates:", error);
82
+ return { x: event.clientX, y: event.clientY };
83
+ }
84
+ }
85
+ function addCodeBlock(event) {
86
+ let { x, y } = getSVGCoordinates(event);
87
+ let gElement = document.createElementNS("http://www.w3.org/2000/svg", "g");
88
+ gElement.setAttribute("data-type", "code-group");
89
+ gElement.setAttribute("transform", `translate(${x}, ${y})`);
90
+ let backgroundRect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
91
+ backgroundRect.setAttribute("class", "code-background");
92
+ backgroundRect.setAttribute("x", -10);
93
+ backgroundRect.setAttribute("y", -10);
94
+ backgroundRect.setAttribute("width", 300);
95
+ backgroundRect.setAttribute("height", 60);
96
+ backgroundRect.setAttribute("fill", "#161b22");
97
+ backgroundRect.setAttribute("stroke", "#30363d");
98
+ backgroundRect.setAttribute("stroke-width", "1");
99
+ backgroundRect.setAttribute("rx", "6");
100
+ backgroundRect.setAttribute("ry", "6");
101
+ gElement.appendChild(backgroundRect);
102
+ let codeElement = document.createElementNS("http://www.w3.org/2000/svg", "text");
103
+ let textAlignElement = "start";
104
+ if (codeTextAlign === "center") textAlignElement = "middle";
105
+ else if (codeTextAlign === "right") textAlignElement = "end";
106
+ codeElement.setAttribute("x", 0);
107
+ codeElement.setAttribute("y", 0);
108
+ codeElement.setAttribute("fill", codeTextColor ?? getThemeStroke());
109
+ codeElement.setAttribute("font-size", codeTextSize);
110
+ codeElement.setAttribute("font-family", codeTextFont);
111
+ codeElement.setAttribute("text-anchor", textAlignElement);
112
+ codeElement.setAttribute("cursor", "text");
113
+ codeElement.setAttribute("white-space", "pre");
114
+ codeElement.setAttribute("dominant-baseline", "hanging");
115
+ codeElement.textContent = "";
116
+ gElement.setAttribute("data-x", x);
117
+ gElement.setAttribute("data-y", y);
118
+ codeElement.setAttribute("data-initial-size", codeTextSize);
119
+ codeElement.setAttribute("data-initial-font", codeTextFont);
120
+ codeElement.setAttribute("data-initial-color", codeTextColor ?? getThemeStroke());
121
+ codeElement.setAttribute("data-initial-align", codeTextAlign);
122
+ codeElement.setAttribute("data-language", codeLanguage);
123
+ codeElement.setAttribute("data-type", "code");
124
+ gElement.appendChild(codeElement);
125
+ svg.appendChild(gElement);
126
+ const shapeID = `code-${String(Date.now()).slice(0, 8)}-${Math.floor(Math.random() * 1e4)}`;
127
+ gElement.setAttribute("id", shapeID);
128
+ codeElement.setAttribute("id", `${shapeID}-code`);
129
+ const codeShape = wrapCodeElement(gElement);
130
+ if (typeof shapes !== "undefined" && Array.isArray(shapes)) {
131
+ shapes.push(codeShape);
132
+ }
133
+ pushCreateAction({
134
+ type: "code",
135
+ element: codeShape,
136
+ shapeName: "code"
137
+ });
138
+ makeCodeEditable(codeElement, gElement);
139
+ }
140
+ function updateSyntaxHighlighting(editor) {
141
+ if (window.hljs) {
142
+ window.hljs.highlightElement(editor);
143
+ }
144
+ }
145
+ function adjustCodeEditorSize(editor) {
146
+ const foreignObject = editor.closest("foreignObject");
147
+ const codeContainer = editor.closest(".svg-code-container");
148
+ if (!foreignObject || !codeContainer) return;
149
+ const svgRect = svg.getBoundingClientRect();
150
+ editor.style.overflow = "visible";
151
+ editor.style.height = "auto";
152
+ editor.style.width = "auto";
153
+ editor.style.whiteSpace = "pre";
154
+ let minContentWidth = editor.scrollWidth + (parseFloat(codeContainer.style.paddingLeft) || 0) + (parseFloat(codeContainer.style.paddingRight) || 0);
155
+ let minContentHeight = editor.scrollHeight + (parseFloat(codeContainer.style.paddingTop) || 0) + (parseFloat(codeContainer.style.paddingBottom) || 0);
156
+ const editorRect = editor.getBoundingClientRect();
157
+ const maxWidth = svgRect.width - (editorRect.left - svgRect.left);
158
+ const maxHeight = svgRect.height - (editorRect.top - svgRect.top);
159
+ let newWidth = Math.min(Math.max(minContentWidth, 50), maxWidth);
160
+ let newHeight = Math.min(Math.max(minContentHeight, 30), maxHeight);
161
+ foreignObject.setAttribute("width", newWidth);
162
+ foreignObject.setAttribute("height", newHeight);
163
+ editor.style.width = `${newWidth - (parseFloat(codeContainer.style.paddingLeft) || 0) - (parseFloat(codeContainer.style.paddingRight) || 0)}px`;
164
+ editor.style.height = `${newHeight - (parseFloat(codeContainer.style.paddingTop) || 0) - (parseFloat(codeContainer.style.paddingBottom) || 0)}px`;
165
+ if (editor.scrollHeight > editor.clientHeight || editor.scrollWidth > editor.clientWidth) {
166
+ editor.style.overflow = "auto";
167
+ } else {
168
+ editor.style.overflow = "hidden";
169
+ }
170
+ if (selectedCodeBlock && selectedCodeBlock.contains(foreignObject)) {
171
+ updateCodeSelectionFeedback();
172
+ }
173
+ }
174
+ function makeCodeEditable(codeElement, groupElement, clickEvent = null) {
175
+ if (document.querySelector(".svg-code-editor")) {
176
+ return;
177
+ }
178
+ if (selectedCodeBlock) {
179
+ deselectCodeBlock();
180
+ }
181
+ removeCodeSelectionFeedback();
182
+ let editorContainer = document.createElement("div");
183
+ editorContainer.className = "svg-code-container";
184
+ editorContainer.style.position = "absolute";
185
+ editorContainer.style.zIndex = "10000";
186
+ editorContainer.style.backgroundColor = "#161b22";
187
+ editorContainer.style.border = "1px solid #30363d";
188
+ editorContainer.style.borderRadius = "6px";
189
+ editorContainer.style.padding = "12px";
190
+ editorContainer.style.fontFamily = "monospace";
191
+ editorContainer.style.overflow = "hidden";
192
+ editorContainer.style.minWidth = "200px";
193
+ editorContainer.style.minHeight = "40px";
194
+ let input = document.createElement("div");
195
+ input.className = "svg-code-editor";
196
+ input.contentEditable = true;
197
+ input.style.outline = "none";
198
+ input.style.minHeight = "30px";
199
+ input.style.maxHeight = "400px";
200
+ input.style.overflowY = "auto";
201
+ input.style.whiteSpace = "pre";
202
+ input.style.fontFamily = "lixCode, Consolas, 'Courier New', monospace";
203
+ input.style.fontSize = codeElement.getAttribute("font-size") || codeTextSize;
204
+ input.style.color = "#c9d1d9";
205
+ input.style.lineHeight = "1.4";
206
+ input.style.tabSize = "4";
207
+ input.style.background = "transparent";
208
+ let codeContent = extractTextFromCodeElement(codeElement);
209
+ input.textContent = codeContent;
210
+ editorContainer.appendChild(input);
211
+ const svgRect = svg.getBoundingClientRect();
212
+ let left = svgRect.left, top = svgRect.top;
213
+ if (clickEvent) {
214
+ left += clickEvent.clientX - svgRect.left;
215
+ top += clickEvent.clientY - svgRect.top;
216
+ } else {
217
+ let groupTransformMatrix = svg.createSVGMatrix();
218
+ if (groupElement && groupElement.transform && groupElement.transform.baseVal) {
219
+ const transformList = groupElement.transform.baseVal;
220
+ if (transformList.numberOfItems > 0) {
221
+ const consolidatedTransform = transformList.consolidate();
222
+ if (consolidatedTransform) {
223
+ groupTransformMatrix = consolidatedTransform.matrix;
224
+ }
225
+ }
226
+ }
227
+ const codeBBox = codeElement.getBBox();
228
+ let pt = svg.createSVGPoint();
229
+ pt.x = codeBBox.x - 8;
230
+ pt.y = codeBBox.y - 8;
231
+ let screenPt = pt.matrixTransform(groupTransformMatrix.multiply(svg.getScreenCTM()));
232
+ left = screenPt.x + svgRect.left;
233
+ top = screenPt.y + svgRect.top;
234
+ }
235
+ editorContainer.style.left = `${left}px`;
236
+ editorContainer.style.top = `${top}px`;
237
+ document.body.appendChild(editorContainer);
238
+ const adjustSize = () => {
239
+ const maxWidth = svgRect.width - (left - svgRect.left);
240
+ const maxHeight = svgRect.height - (top - svgRect.top);
241
+ let newWidth = Math.max(300, Math.min(input.scrollWidth + 20, maxWidth));
242
+ let newHeight = Math.max(40, Math.min(input.scrollHeight + 20, maxHeight));
243
+ editorContainer.style.width = newWidth + "px";
244
+ editorContainer.style.height = newHeight + "px";
245
+ if (input.scrollHeight > input.clientHeight) {
246
+ input.style.overflowY = "auto";
247
+ } else {
248
+ input.style.overflowY = "hidden";
249
+ }
250
+ };
251
+ adjustSize();
252
+ setTimeout(() => {
253
+ input.focus();
254
+ const range = document.createRange();
255
+ range.selectNodeContents(input);
256
+ const selection = window.getSelection();
257
+ selection.removeAllRanges();
258
+ selection.addRange(range);
259
+ applySyntaxHighlightingImproved(input);
260
+ }, 50);
261
+ let highlightingTimeout = null;
262
+ let isHighlighting = false;
263
+ let lastContent = input.textContent;
264
+ input.addEventListener("input", function(e) {
265
+ if (isHighlighting) return;
266
+ if (highlightingTimeout) {
267
+ clearTimeout(highlightingTimeout);
268
+ highlightingTimeout = null;
269
+ }
270
+ const currentContent = input.textContent || input.innerText || "";
271
+ if (currentContent === lastContent) return;
272
+ highlightingTimeout = setTimeout(() => {
273
+ const contentAtTimeout = input.textContent || input.innerText || "";
274
+ if (contentAtTimeout !== lastContent && !isHighlighting) {
275
+ applySyntaxHighlightingImproved(input);
276
+ lastContent = contentAtTimeout;
277
+ adjustSize();
278
+ }
279
+ highlightingTimeout = null;
280
+ }, 300);
281
+ });
282
+ input.addEventListener("keydown", function(e) {
283
+ if (e.key === "Tab") {
284
+ e.preventDefault();
285
+ document.execCommand("insertText", false, " ");
286
+ } else if (e.key === "Enter" && e.ctrlKey) {
287
+ e.preventDefault();
288
+ renderCodeFromEditor(input, codeElement, true);
289
+ } else if (e.key === "Escape") {
290
+ e.preventDefault();
291
+ renderCodeFromEditor(input, codeElement, true);
292
+ }
293
+ });
294
+ input.originalCodeElement = codeElement;
295
+ input.codeGroup = groupElement;
296
+ input.isHighlighting = () => isHighlighting;
297
+ input.setHighlighting = (state) => {
298
+ isHighlighting = state;
299
+ };
300
+ input.clearHighlightTimeout = () => {
301
+ if (highlightingTimeout) {
302
+ clearTimeout(highlightingTimeout);
303
+ highlightingTimeout = null;
304
+ }
305
+ };
306
+ const handleClickOutside = (event) => {
307
+ if (!editorContainer.contains(event.target)) {
308
+ input.clearHighlightTimeout();
309
+ renderCodeFromEditor(input, codeElement, true);
310
+ document.removeEventListener("pointerdown", handleClickOutside, true);
311
+ }
312
+ };
313
+ document.addEventListener("pointerdown", handleClickOutside, true);
314
+ input.handleClickOutside = handleClickOutside;
315
+ groupElement.style.display = "none";
316
+ }
317
+ function applySyntaxHighlightingImproved(editor) {
318
+ if (!window.hljs) {
319
+ return;
320
+ }
321
+ if (editor.isHighlighting && editor.isHighlighting()) {
322
+ return;
323
+ }
324
+ if (editor.setHighlighting) {
325
+ editor.setHighlighting(true);
326
+ }
327
+ try {
328
+ const selection = window.getSelection();
329
+ let cursorOffset = 0;
330
+ let cursorNode = null;
331
+ let cursorNodeOffset = 0;
332
+ if (selection.rangeCount > 0) {
333
+ const range = selection.getRangeAt(0);
334
+ cursorNode = range.startContainer;
335
+ cursorNodeOffset = range.startOffset;
336
+ cursorOffset = getCursorOffset(editor, cursorNode, cursorNodeOffset);
337
+ }
338
+ const code = editor.textContent || editor.innerText || "";
339
+ if (!code.trim()) {
340
+ return;
341
+ }
342
+ const result = window.hljs.highlightAuto(code);
343
+ const newHTML = result.value;
344
+ if (editor.innerHTML !== newHTML) {
345
+ editor.innerHTML = newHTML;
346
+ restoreCursorPositionImproved(editor, cursorOffset);
347
+ }
348
+ if (result.language) {
349
+ editor.setAttribute("data-language", result.language);
350
+ }
351
+ } catch (error) {
352
+ } finally {
353
+ if (editor.setHighlighting) {
354
+ editor.setHighlighting(false);
355
+ }
356
+ }
357
+ }
358
+ function restoreCursorPositionImproved(editor, targetOffset) {
359
+ if (targetOffset < 0) return;
360
+ try {
361
+ const selection = window.getSelection();
362
+ const range = document.createRange();
363
+ let currentOffset = 0;
364
+ let targetNode = null;
365
+ let targetNodeOffset = 0;
366
+ const walker = document.createTreeWalker(
367
+ editor,
368
+ NodeFilter.SHOW_TEXT,
369
+ null,
370
+ false
371
+ );
372
+ let node;
373
+ while (node = walker.nextNode()) {
374
+ const nodeLength = node.textContent.length;
375
+ if (currentOffset + nodeLength >= targetOffset) {
376
+ targetNode = node;
377
+ targetNodeOffset = Math.max(0, Math.min(targetOffset - currentOffset, nodeLength));
378
+ break;
379
+ }
380
+ currentOffset += nodeLength;
381
+ }
382
+ if (targetNode) {
383
+ range.setStart(targetNode, targetNodeOffset);
384
+ range.collapse(true);
385
+ selection.removeAllRanges();
386
+ selection.addRange(range);
387
+ } else {
388
+ range.selectNodeContents(editor);
389
+ range.collapse(false);
390
+ selection.removeAllRanges();
391
+ selection.addRange(range);
392
+ }
393
+ } catch (error) {
394
+ try {
395
+ editor.focus();
396
+ } catch (focusError) {
397
+ }
398
+ }
399
+ }
400
+ function getCursorOffset(container, node, offset) {
401
+ let cursorOffset = 0;
402
+ if (!node || !container.contains(node)) {
403
+ return 0;
404
+ }
405
+ const walker = document.createTreeWalker(
406
+ container,
407
+ NodeFilter.SHOW_TEXT,
408
+ null,
409
+ false
410
+ );
411
+ let currentNode;
412
+ while (currentNode = walker.nextNode()) {
413
+ if (currentNode === node) {
414
+ return cursorOffset + Math.max(0, Math.min(offset, currentNode.textContent.length));
415
+ }
416
+ cursorOffset += currentNode.textContent.length;
417
+ }
418
+ return cursorOffset;
419
+ }
420
+ function extractTextFromCodeElement(codeElement) {
421
+ if (!codeElement) return "";
422
+ let codeContent = "";
423
+ const childNodes = codeElement.childNodes;
424
+ if (childNodes.length === 0) {
425
+ return codeElement.textContent || "";
426
+ }
427
+ for (let i = 0; i < childNodes.length; i++) {
428
+ const node = childNodes[i];
429
+ if (node.nodeType === Node.TEXT_NODE) {
430
+ codeContent += node.textContent;
431
+ } else if (node.tagName === "tspan") {
432
+ const tspanText = node.textContent || "";
433
+ codeContent += tspanText;
434
+ if (i < childNodes.length - 1) {
435
+ const nextNode = childNodes[i + 1];
436
+ if (nextNode && nextNode.tagName === "tspan") {
437
+ codeContent += "\n";
438
+ }
439
+ }
440
+ }
441
+ }
442
+ return codeContent.replace(/\u00A0/g, " ").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
443
+ }
444
+ function renderCodeFromEditor(input, codeElement, deleteIfEmpty = false) {
445
+ const editorContainer = input.closest(".svg-code-container");
446
+ if (!editorContainer || !document.body.contains(editorContainer)) {
447
+ return;
448
+ }
449
+ const code = input.textContent || input.innerText || "";
450
+ const gElement = input.codeGroup;
451
+ if (input.handleClickOutside) {
452
+ document.removeEventListener("pointerdown", input.handleClickOutside, true);
453
+ }
454
+ if (input.clearHighlightTimeout) {
455
+ input.clearHighlightTimeout();
456
+ }
457
+ document.body.removeChild(editorContainer);
458
+ if (!gElement || !codeElement) {
459
+ return;
460
+ }
461
+ if (!gElement.parentNode) {
462
+ if (selectedCodeBlock === gElement) {
463
+ deselectCodeBlock();
464
+ }
465
+ return;
466
+ }
467
+ if (deleteIfEmpty && code.trim() === "") {
468
+ let codeShape = null;
469
+ if (typeof shapes !== "undefined" && Array.isArray(shapes)) {
470
+ codeShape = shapes.find((shape) => shape.shapeName === "code" && shape.group === gElement);
471
+ if (codeShape) {
472
+ const idx = shapes.indexOf(codeShape);
473
+ if (idx !== -1) shapes.splice(idx, 1);
474
+ }
475
+ }
476
+ pushDeleteActionWithAttachments({
477
+ type: "code",
478
+ element: codeShape || gElement,
479
+ shapeName: "code"
480
+ });
481
+ if (typeof cleanupAttachments === "function") {
482
+ cleanupAttachments(gElement);
483
+ }
484
+ svg.removeChild(gElement);
485
+ if (selectedCodeBlock === gElement) {
486
+ selectedCodeBlock = null;
487
+ removeCodeSelectionFeedback();
488
+ }
489
+ } else {
490
+ while (codeElement.firstChild) {
491
+ codeElement.removeChild(codeElement.firstChild);
492
+ }
493
+ codeElement.textContent = "";
494
+ const storedLang = codeElement.getAttribute("data-language") || "auto";
495
+ const highlightedCode = applySyntaxHighlightingToSVG(code, storedLang);
496
+ createHighlightedSVGText(highlightedCode, codeElement);
497
+ updateCodeBackground(gElement, codeElement);
498
+ gElement.style.display = "block";
499
+ if (typeof updateAttachedArrows === "function") {
500
+ updateAttachedArrows(gElement);
501
+ }
502
+ if (selectedCodeBlock === gElement) {
503
+ setTimeout(updateCodeSelectionFeedback, 0);
504
+ }
505
+ }
506
+ if (gElement.parentNode) {
507
+ if (window.__sketchStoreApi) {
508
+ window.__sketchStoreApi.setActiveTool("select", { afterDraw: true });
509
+ } else {
510
+ window.isSelectionToolActive = true;
511
+ }
512
+ selectCodeBlock(gElement);
513
+ }
514
+ }
515
+ function createHighlightedSVGText(highlightResult, parentElement) {
516
+ if (!highlightResult || !highlightResult.value) {
517
+ return;
518
+ }
519
+ const lines = highlightResult.value.split("\n");
520
+ const x = parentElement.getAttribute("x") || 0;
521
+ lines.forEach((line, index) => {
522
+ const tspan = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
523
+ tspan.setAttribute("x", x);
524
+ tspan.setAttribute("dy", index === 0 ? "0" : "1.2em");
525
+ if (line.trim()) {
526
+ createHighlightedTspans(line, tspan, x);
527
+ if (tspan.childNodes.length === 0) {
528
+ tspan.textContent = line;
529
+ }
530
+ } else {
531
+ tspan.textContent = " ";
532
+ }
533
+ parentElement.appendChild(tspan);
534
+ });
535
+ }
536
+ function applySyntaxHighlightingToSVG(code, language) {
537
+ if (!window.hljs) {
538
+ return { value: code, language: null };
539
+ }
540
+ const lang = language || codeLanguage;
541
+ if (lang && lang !== "auto") {
542
+ try {
543
+ return window.hljs.highlight(code, { language: lang });
544
+ } catch (e) {
545
+ }
546
+ }
547
+ return window.hljs.highlightAuto(code);
548
+ }
549
+ function renderCode(input, codeElement, deleteIfEmpty = false) {
550
+ if (!input || !document.body.contains(input)) {
551
+ return;
552
+ }
553
+ const code = input.value || "";
554
+ const gElement = input.codeGroup;
555
+ if (input.handleClickOutside) {
556
+ document.removeEventListener("pointerdown", input.handleClickOutside, true);
557
+ }
558
+ document.body.removeChild(input);
559
+ if (!gElement || !codeElement) {
560
+ return;
561
+ }
562
+ if (!gElement.parentNode) {
563
+ if (selectedCodeBlock === gElement) {
564
+ deselectCodeBlock();
565
+ }
566
+ return;
567
+ }
568
+ if (deleteIfEmpty && code.trim() === "") {
569
+ let codeShape = null;
570
+ if (typeof shapes !== "undefined" && Array.isArray(shapes)) {
571
+ codeShape = shapes.find((shape) => shape.shapeName === "code" && shape.group === gElement);
572
+ if (codeShape) {
573
+ const idx = shapes.indexOf(codeShape);
574
+ if (idx !== -1) shapes.splice(idx, 1);
575
+ }
576
+ }
577
+ pushDeleteActionWithAttachments({
578
+ type: "code",
579
+ element: codeShape || gElement,
580
+ shapeName: "code"
581
+ });
582
+ if (typeof cleanupAttachments === "function") {
583
+ cleanupAttachments(gElement);
584
+ }
585
+ svg.removeChild(gElement);
586
+ if (selectedCodeBlock === gElement) {
587
+ selectedCodeBlock = null;
588
+ removeCodeSelectionFeedback();
589
+ }
590
+ } else {
591
+ while (codeElement.firstChild) {
592
+ codeElement.removeChild(codeElement.firstChild);
593
+ }
594
+ const lines = code.split("\n");
595
+ const x = codeElement.getAttribute("x") || 0;
596
+ lines.forEach((line, index) => {
597
+ if (window.hljs && line.trim()) {
598
+ const result = window.hljs.highlightAuto(line);
599
+ createHighlightedTspans(result.value, codeElement, x, index === 0 ? "0" : "1.2em");
600
+ } else {
601
+ let tspan = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
602
+ tspan.setAttribute("x", x);
603
+ tspan.setAttribute("dy", index === 0 ? "0" : "1.2em");
604
+ tspan.textContent = line.replace(/\u00A0/g, " ") || " ";
605
+ codeElement.appendChild(tspan);
606
+ }
607
+ });
608
+ updateCodeBackground(gElement, codeElement);
609
+ gElement.style.display = "block";
610
+ updateAttachedArrows(gElement);
611
+ if (selectedCodeBlock === gElement) {
612
+ setTimeout(updateCodeSelectionFeedback, 0);
613
+ }
614
+ }
615
+ if (gElement.parentNode) {
616
+ if (window.__sketchStoreApi) {
617
+ window.__sketchStoreApi.setActiveTool("select", { afterDraw: true });
618
+ } else {
619
+ window.isSelectionToolActive = true;
620
+ }
621
+ selectCodeBlock(gElement);
622
+ }
623
+ }
624
+ function createHighlightedTspans(highlightedHtml, parentTspan, x) {
625
+ if (!highlightedHtml || !parentTspan) return;
626
+ const tempDiv = document.createElement("div");
627
+ tempDiv.innerHTML = highlightedHtml;
628
+ processHighlightedNodes(tempDiv, parentTspan);
629
+ }
630
+ function processHighlightedNodes(node, parentTspan) {
631
+ if (!node || !parentTspan) return;
632
+ for (let child of node.childNodes) {
633
+ if (child.nodeType === Node.TEXT_NODE) {
634
+ if (child.textContent) {
635
+ if (parentTspan.textContent) {
636
+ parentTspan.textContent += child.textContent;
637
+ } else {
638
+ parentTspan.textContent = child.textContent;
639
+ }
640
+ }
641
+ } else if (child.nodeType === Node.ELEMENT_NODE) {
642
+ let styledTspan = document.createElementNS("http://www.w3.org/2000/svg", "tspan");
643
+ styledTspan.textContent = child.textContent || "";
644
+ const className = child.className || "";
645
+ applyHighlightColor(styledTspan, className);
646
+ parentTspan.appendChild(styledTspan);
647
+ }
648
+ }
649
+ }
650
+ function applyHighlightColor(tspan, className) {
651
+ if (className.includes("hljs-keyword") || className.includes("hljs-built_in")) {
652
+ tspan.setAttribute("fill", "#ff7b72");
653
+ } else if (className.includes("hljs-string") || className.includes("hljs-template-string")) {
654
+ tspan.setAttribute("fill", "#a5d6ff");
655
+ } else if (className.includes("hljs-comment")) {
656
+ tspan.setAttribute("fill", "#8b949e");
657
+ tspan.setAttribute("font-style", "italic");
658
+ } else if (className.includes("hljs-number") || className.includes("hljs-literal")) {
659
+ tspan.setAttribute("fill", "#79c0ff");
660
+ } else if (className.includes("hljs-function") || className.includes("hljs-title")) {
661
+ tspan.setAttribute("fill", "#d2a8ff");
662
+ } else if (className.includes("hljs-variable") || className.includes("hljs-name")) {
663
+ tspan.setAttribute("fill", "#ffa657");
664
+ } else if (className.includes("hljs-type") || className.includes("hljs-class")) {
665
+ tspan.setAttribute("fill", "#f0883e");
666
+ } else if (className.includes("hljs-operator") || className.includes("hljs-punctuation")) {
667
+ tspan.setAttribute("fill", "#c9d1d9");
668
+ } else if (className.includes("hljs-property") || className.includes("hljs-attribute")) {
669
+ tspan.setAttribute("fill", "#79c0ff");
670
+ } else if (className.includes("hljs-tag")) {
671
+ tspan.setAttribute("fill", "#7ee787");
672
+ } else if (className.includes("hljs-meta") || className.includes("hljs-doctag")) {
673
+ tspan.setAttribute("fill", "#8b949e");
674
+ } else if (className.includes("hljs-regexp")) {
675
+ tspan.setAttribute("fill", "#a5d6ff");
676
+ } else {
677
+ tspan.setAttribute("fill", "#c9d1d9");
678
+ }
679
+ }
680
+ function createCodeSelectionFeedback(groupElement) {
681
+ if (!groupElement) return;
682
+ removeCodeSelectionFeedback();
683
+ const backgroundRect = groupElement.querySelector(".code-background");
684
+ if (!backgroundRect) {
685
+ return;
686
+ }
687
+ const x = parseFloat(backgroundRect.getAttribute("x"));
688
+ const y = parseFloat(backgroundRect.getAttribute("y"));
689
+ const width = parseFloat(backgroundRect.getAttribute("width"));
690
+ const height = parseFloat(backgroundRect.getAttribute("height"));
691
+ const zoom = window.currentZoom || 1;
692
+ const padding = 8 / zoom;
693
+ const handleSize = 10 / zoom;
694
+ const handleOffset = handleSize / 2;
695
+ const selX = x - padding;
696
+ const selY = y - padding;
697
+ const selWidth = width + 2 * padding;
698
+ const selHeight = height + 2 * padding;
699
+ const outlinePoints = [
700
+ [selX, selY],
701
+ [selX + selWidth, selY],
702
+ [selX + selWidth, selY + selHeight],
703
+ [selX, selY + selHeight],
704
+ [selX, selY]
705
+ ];
706
+ const pointsAttr = outlinePoints.map((p) => p.join(",")).join(" ");
707
+ codeSelectionBox = document.createElementNS("http://www.w3.org/2000/svg", "polyline");
708
+ codeSelectionBox.setAttribute("class", "selection-box");
709
+ codeSelectionBox.setAttribute("points", pointsAttr);
710
+ codeSelectionBox.setAttribute("fill", "none");
711
+ codeSelectionBox.setAttribute("stroke", "#5B57D1");
712
+ codeSelectionBox.setAttribute("stroke-width", "1.5");
713
+ codeSelectionBox.setAttribute("stroke-dasharray", "4 2");
714
+ codeSelectionBox.setAttribute("vector-effect", "non-scaling-stroke");
715
+ codeSelectionBox.setAttribute("pointer-events", "none");
716
+ groupElement.appendChild(codeSelectionBox);
717
+ const handlesData = [
718
+ { name: "nw", x: selX, y: selY, cursor: "nwse-resize" },
719
+ { name: "ne", x: selX + selWidth, y: selY, cursor: "nesw-resize" },
720
+ { name: "sw", x: selX, y: selY + selHeight, cursor: "nesw-resize" },
721
+ { name: "se", x: selX + selWidth, y: selY + selHeight, cursor: "nwse-resize" }
722
+ ];
723
+ codeResizeHandles = {};
724
+ handlesData.forEach((handle) => {
725
+ const handleRect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
726
+ handleRect.setAttribute("class", `resize-handle resize-handle-${handle.name}`);
727
+ handleRect.setAttribute("x", handle.x - handleOffset);
728
+ handleRect.setAttribute("y", handle.y - handleOffset);
729
+ handleRect.setAttribute("width", handleSize);
730
+ handleRect.setAttribute("height", handleSize);
731
+ handleRect.setAttribute("fill", "#121212");
732
+ handleRect.setAttribute("stroke", "#5B57D1");
733
+ handleRect.setAttribute("stroke-width", 2);
734
+ handleRect.setAttribute("vector-effect", "non-scaling-stroke");
735
+ handleRect.style.cursor = handle.cursor;
736
+ handleRect.setAttribute("data-anchor", handle.name);
737
+ groupElement.appendChild(handleRect);
738
+ codeResizeHandles[handle.name] = handleRect;
739
+ handleRect.addEventListener("pointerdown", (e) => {
740
+ if (window.isSelectionToolActive) {
741
+ e.stopPropagation();
742
+ startCodeResize(e, handle.name);
743
+ }
744
+ });
745
+ });
746
+ const rotationAnchorPos = { x: selX + selWidth / 2, y: selY - 30 };
747
+ const rotationAnchor = document.createElementNS("http://www.w3.org/2000/svg", "circle");
748
+ rotationAnchor.setAttribute("cx", rotationAnchorPos.x);
749
+ rotationAnchor.setAttribute("cy", rotationAnchorPos.y);
750
+ rotationAnchor.setAttribute("r", 8);
751
+ rotationAnchor.setAttribute("class", "rotate-anchor");
752
+ rotationAnchor.setAttribute("fill", "#121212");
753
+ rotationAnchor.setAttribute("stroke", "#5B57D1");
754
+ rotationAnchor.setAttribute("stroke-width", 2);
755
+ rotationAnchor.setAttribute("vector-effect", "non-scaling-stroke");
756
+ rotationAnchor.style.cursor = "grab";
757
+ rotationAnchor.setAttribute("pointer-events", "all");
758
+ groupElement.appendChild(rotationAnchor);
759
+ codeResizeHandles.rotate = rotationAnchor;
760
+ rotationAnchor.addEventListener("pointerdown", (e) => {
761
+ if (window.isSelectionToolActive) {
762
+ e.stopPropagation();
763
+ startCodeRotation(e);
764
+ }
765
+ });
766
+ }
767
+ function updateCodeBackground(groupElement, codeElement) {
768
+ const backgroundRect = groupElement.querySelector(".code-background");
769
+ if (!backgroundRect || !codeElement) return;
770
+ const textBBox = codeElement.getBBox();
771
+ const padding = 10;
772
+ backgroundRect.setAttribute("x", textBBox.x - padding);
773
+ backgroundRect.setAttribute("y", textBBox.y - padding);
774
+ backgroundRect.setAttribute("width", textBBox.width + padding * 2);
775
+ backgroundRect.setAttribute("height", textBBox.height + padding * 2);
776
+ }
777
+ function updateCodeSelectionFeedback() {
778
+ if (!selectedCodeBlock || !codeSelectionBox) return;
779
+ const codeElement = selectedCodeBlock.querySelector("text");
780
+ if (!codeElement) return;
781
+ const wasHidden = selectedCodeBlock.style.display === "none";
782
+ if (wasHidden) selectedCodeBlock.style.display = "block";
783
+ const bbox = codeElement.getBBox();
784
+ if (wasHidden) selectedCodeBlock.style.display = "none";
785
+ if (bbox.width === 0 && bbox.height === 0 && codeElement.textContent.trim() !== "") {
786
+ }
787
+ const zoom = window.currentZoom || 1;
788
+ const padding = 8 / zoom;
789
+ const handleSize = 10 / zoom;
790
+ const handleOffset = handleSize / 2;
791
+ const selX = bbox.x - padding;
792
+ const selY = bbox.y - padding;
793
+ const selWidth = Math.max(bbox.width + 2 * padding, handleSize);
794
+ const selHeight = Math.max(bbox.height + 2 * padding, handleSize);
795
+ const outlinePoints = [
796
+ [selX, selY],
797
+ [selX + selWidth, selY],
798
+ [selX + selWidth, selY + selHeight],
799
+ [selX, selY + selHeight],
800
+ [selX, selY]
801
+ ];
802
+ const pointsAttr = outlinePoints.map((p) => p.join(",")).join(" ");
803
+ codeSelectionBox.setAttribute("points", pointsAttr);
804
+ const handlesData = [
805
+ { name: "nw", x: selX, y: selY },
806
+ { name: "ne", x: selX + selWidth, y: selY },
807
+ { name: "sw", x: selX, y: selY + selHeight },
808
+ { name: "se", x: selX + selWidth, y: selY + selHeight }
809
+ ];
810
+ handlesData.forEach((handle) => {
811
+ const handleRect = codeResizeHandles[handle.name];
812
+ if (handleRect) {
813
+ handleRect.setAttribute("x", handle.x - handleOffset);
814
+ handleRect.setAttribute("y", handle.y - handleOffset);
815
+ }
816
+ });
817
+ const rotationAnchor = codeResizeHandles.rotate;
818
+ if (rotationAnchor) {
819
+ const rotationAnchorPos = { x: selX + selWidth / 2, y: selY - 30 };
820
+ rotationAnchor.setAttribute("cx", rotationAnchorPos.x);
821
+ rotationAnchor.setAttribute("cy", rotationAnchorPos.y);
822
+ }
823
+ }
824
+ function startCodeRotation(event) {
825
+ if (!selectedCodeBlock || event.button !== 0) return;
826
+ event.preventDefault();
827
+ event.stopPropagation();
828
+ isCodeRotating = true;
829
+ isCodeDragging = false;
830
+ isCodeResizing = false;
831
+ const codeElement = selectedCodeBlock.querySelector("text");
832
+ if (!codeElement) return;
833
+ const bbox = codeElement.getBBox();
834
+ const centerX = bbox.x + bbox.width / 2;
835
+ const centerY = bbox.y + bbox.height / 2;
836
+ const mousePos = getSVGCoordinates(event);
837
+ let centerPoint = svg.createSVGPoint();
838
+ centerPoint.x = centerX;
839
+ centerPoint.y = centerY;
840
+ const groupTransform = selectedCodeBlock.transform.baseVal.consolidate();
841
+ if (groupTransform) {
842
+ centerPoint = centerPoint.matrixTransform(groupTransform.matrix);
843
+ }
844
+ codeRotationStartAngle = Math.atan2(mousePos.y - centerPoint.y, mousePos.x - centerPoint.x) * 180 / Math.PI;
845
+ const currentTransform = selectedCodeBlock.transform.baseVal.consolidate();
846
+ codeRotationStartTransform = currentTransform ? currentTransform.matrix : svg.createSVGMatrix();
847
+ svg.style.cursor = "grabbing";
848
+ window.addEventListener("pointermove", handleCodeMouseMove);
849
+ window.addEventListener("pointerup", handleCodeMouseUp);
850
+ }
851
+ function removeCodeSelectionFeedback() {
852
+ if (selectedCodeBlock) {
853
+ selectedCodeBlock.querySelectorAll(".selection-box, .resize-handle, .rotate-anchor").forEach((el) => el.remove());
854
+ }
855
+ codeSelectionBox = null;
856
+ codeResizeHandles = {};
857
+ }
858
+ function selectCodeBlock(groupElement) {
859
+ if (!groupElement || !groupElement.parentNode) return;
860
+ if (groupElement === selectedCodeBlock) return;
861
+ deselectCodeBlock();
862
+ selectedCodeBlock = groupElement;
863
+ selectedCodeBlock.classList.add("selected");
864
+ createCodeSelectionFeedback(selectedCodeBlock);
865
+ updateSelectedElement(selectedCodeBlock);
866
+ const toggleSpans = document.querySelectorAll(".textCodeSpan");
867
+ toggleSpans.forEach((el) => el.classList.remove("selected"));
868
+ toggleSpans.forEach((el) => {
869
+ if (el.getAttribute("data-id") === "true") el.classList.add("selected");
870
+ });
871
+ if (window.__showSidebarForShape) window.__showSidebarForShape("code");
872
+ }
873
+ function deselectCodeBlock() {
874
+ const activeEditor = document.querySelector(".svg-code-editor[contenteditable='true']");
875
+ if (activeEditor) {
876
+ let groupElement = activeEditor.originalGroup;
877
+ if (groupElement) {
878
+ renderCode(activeEditor, true);
879
+ } else if (document.body.contains(activeEditor)) {
880
+ activeEditor.remove();
881
+ }
882
+ }
883
+ if (selectedCodeBlock) {
884
+ removeCodeSelectionFeedback();
885
+ selectedCodeBlock.classList.remove("selected");
886
+ selectedCodeBlock = null;
887
+ updateSelectedElement(null);
888
+ }
889
+ if (isCodeRotating) {
890
+ isCodeRotating = false;
891
+ codeRotationStartAngle = 0;
892
+ codeRotationStartTransform = null;
893
+ svg.style.cursor = "default";
894
+ window.removeEventListener("pointermove", handleCodeMouseMove);
895
+ window.removeEventListener("pointerup", handleCodeMouseUp);
896
+ }
897
+ }
898
+ function startCodeDrag(event) {
899
+ if (!selectedCodeBlock || event.button !== 0) return;
900
+ if (event.target.closest(".resize-handle")) {
901
+ return;
902
+ }
903
+ isCodeDragging = true;
904
+ isCodeResizing = false;
905
+ event.preventDefault();
906
+ const currentTransform = selectedCodeBlock.transform.baseVal.consolidate();
907
+ const initialTranslateX = currentTransform ? currentTransform.matrix.e : 0;
908
+ const initialTranslateY = currentTransform ? currentTransform.matrix.f : 0;
909
+ startCodePoint = getSVGCoordinates(event);
910
+ codeDragOffsetX = startCodePoint.x - initialTranslateX;
911
+ codeDragOffsetY = startCodePoint.y - initialTranslateY;
912
+ let codeShape = null;
913
+ if (typeof shapes !== "undefined" && Array.isArray(shapes)) {
914
+ codeShape = shapes.find((shape) => shape.shapeName === "code" && shape.group === selectedCodeBlock);
915
+ }
916
+ if (codeShape) {
917
+ draggedCodeInitialFrame = codeShape.parentFrame || null;
918
+ if (codeShape.parentFrame) {
919
+ codeShape.parentFrame.temporarilyRemoveFromFrame(codeShape);
920
+ }
921
+ }
922
+ svg.style.cursor = "grabbing";
923
+ window.addEventListener("pointermove", handleCodeMouseMove);
924
+ window.addEventListener("pointerup", handleCodeMouseUp);
925
+ }
926
+ function startCodeResize(event, anchor) {
927
+ if (!selectedCodeBlock || event.button !== 0) return;
928
+ event.preventDefault();
929
+ event.stopPropagation();
930
+ isCodeResizing = true;
931
+ isCodeDragging = false;
932
+ currentCodeResizeHandle = anchor;
933
+ const codeElement = selectedCodeBlock.querySelector("text");
934
+ if (!codeElement) {
935
+ isCodeResizing = false;
936
+ return;
937
+ }
938
+ startCodeBBox = codeElement.getBBox();
939
+ startCodeFontSize = parseFloat(codeElement.getAttribute("font-size") || 25);
940
+ if (isNaN(startCodeFontSize)) startCodeFontSize = 25;
941
+ startCodePoint = getSVGCoordinates(event, selectedCodeBlock);
942
+ const currentTransform = selectedCodeBlock.transform.baseVal.consolidate();
943
+ initialCodeGroupTx = currentTransform ? currentTransform.matrix.e : 0;
944
+ initialCodeGroupTy = currentTransform ? currentTransform.matrix.f : 0;
945
+ const padding = 3;
946
+ const startX = startCodeBBox.x - padding;
947
+ const startY = startCodeBBox.y - padding;
948
+ const startWidth = startCodeBBox.width + 2 * padding;
949
+ const startHeight = startCodeBBox.height + 2 * padding;
950
+ let hx = startX;
951
+ let hy = startY;
952
+ if (anchor.includes("e")) {
953
+ hx = startX + startWidth;
954
+ }
955
+ if (anchor.includes("s")) {
956
+ hy = startY + startHeight;
957
+ }
958
+ initialCodeHandlePosRelGroup = { x: hx, y: hy };
959
+ svg.style.cursor = codeResizeHandles[anchor]?.style.cursor || "default";
960
+ window.addEventListener("pointermove", handleCodeMouseMove);
961
+ window.addEventListener("pointerup", handleCodeMouseUp);
962
+ }
963
+ var handleCodeMouseMove = (event) => {
964
+ if (!selectedCodeBlock) return;
965
+ event.preventDefault();
966
+ const svgRect = svg.getBoundingClientRect();
967
+ lastMousePos = {
968
+ x: event.clientX - svgRect.left,
969
+ y: event.clientY - svgRect.top
970
+ };
971
+ if (isCodeDragging) {
972
+ const currentPoint = getSVGCoordinates(event);
973
+ const newTranslateX = currentPoint.x - codeDragOffsetX;
974
+ const newTranslateY = currentPoint.y - codeDragOffsetY;
975
+ const currentTransform = selectedCodeBlock.transform.baseVal.consolidate();
976
+ if (currentTransform) {
977
+ const matrix = currentTransform.matrix;
978
+ const angle = Math.atan2(matrix.b, matrix.a) * 180 / Math.PI;
979
+ const codeElement = selectedCodeBlock.querySelector("text");
980
+ if (codeElement) {
981
+ const bbox = codeElement.getBBox();
982
+ const centerX = bbox.x + bbox.width / 2;
983
+ const centerY = bbox.y + bbox.height / 2;
984
+ selectedCodeBlock.setAttribute(
985
+ "transform",
986
+ `translate(${newTranslateX}, ${newTranslateY}) rotate(${angle}, ${centerX}, ${centerY})`
987
+ );
988
+ } else {
989
+ selectedCodeBlock.setAttribute("transform", `translate(${newTranslateX}, ${newTranslateY})`);
990
+ }
991
+ } else {
992
+ selectedCodeBlock.setAttribute("transform", `translate(${newTranslateX}, ${newTranslateY})`);
993
+ }
994
+ if (typeof shapes !== "undefined" && Array.isArray(shapes)) {
995
+ const codeShape = shapes.find((shape) => shape.shapeName === "code" && shape.group === selectedCodeBlock);
996
+ if (codeShape) {
997
+ codeShape.updateFrameContainment();
998
+ }
999
+ }
1000
+ updateAttachedArrows(selectedCodeBlock);
1001
+ } else if (isCodeResizing) {
1002
+ const codeElement = selectedCodeBlock.querySelector("text");
1003
+ if (!codeElement || !startCodeBBox || startCodeFontSize === null || !startCodePoint || !initialCodeHandlePosRelGroup) return;
1004
+ const currentPoint = getSVGCoordinates(event, selectedCodeBlock);
1005
+ const startX = startCodeBBox.x;
1006
+ const startY = startCodeBBox.y;
1007
+ const startWidth = startCodeBBox.width;
1008
+ const startHeight = startCodeBBox.height;
1009
+ let anchorX, anchorY;
1010
+ switch (currentCodeResizeHandle) {
1011
+ case "nw":
1012
+ anchorX = startX + startWidth;
1013
+ anchorY = startY + startHeight;
1014
+ break;
1015
+ case "ne":
1016
+ anchorX = startX;
1017
+ anchorY = startY + startHeight;
1018
+ break;
1019
+ case "sw":
1020
+ anchorX = startX + startWidth;
1021
+ anchorY = startY;
1022
+ break;
1023
+ case "se":
1024
+ anchorX = startX;
1025
+ anchorY = startY;
1026
+ break;
1027
+ }
1028
+ const newWidth = Math.abs(currentPoint.x - anchorX);
1029
+ const newHeight = Math.abs(currentPoint.y - anchorY);
1030
+ const chosenScale = newHeight / startHeight;
1031
+ const minScale = 0.1;
1032
+ const maxScale = 10;
1033
+ const clampedScale = Math.max(minScale, Math.min(chosenScale, maxScale));
1034
+ const newFontSize = startCodeFontSize * clampedScale;
1035
+ const minFontSize = 5;
1036
+ const finalFontSize = Math.max(newFontSize, minFontSize);
1037
+ codeElement.setAttribute("font-size", `${finalFontSize}px`);
1038
+ const currentBBox = codeElement.getBBox();
1039
+ let newAnchorX, newAnchorY;
1040
+ switch (currentCodeResizeHandle) {
1041
+ case "nw":
1042
+ newAnchorX = currentBBox.x + currentBBox.width;
1043
+ newAnchorY = currentBBox.y + currentBBox.height;
1044
+ break;
1045
+ case "ne":
1046
+ newAnchorX = currentBBox.x;
1047
+ newAnchorY = currentBBox.y + currentBBox.height;
1048
+ break;
1049
+ case "sw":
1050
+ newAnchorX = currentBBox.x + currentBBox.width;
1051
+ newAnchorY = currentBBox.y;
1052
+ break;
1053
+ case "se":
1054
+ newAnchorX = currentBBox.x;
1055
+ newAnchorY = currentBBox.y;
1056
+ break;
1057
+ }
1058
+ const deltaX = anchorX - newAnchorX;
1059
+ const deltaY = anchorY - newAnchorY;
1060
+ const currentTransform = selectedCodeBlock.transform.baseVal.consolidate();
1061
+ if (currentTransform) {
1062
+ const matrix = currentTransform.matrix;
1063
+ const angle = Math.atan2(matrix.b, matrix.a) * 180 / Math.PI;
1064
+ const newGroupTx = initialCodeGroupTx + deltaX;
1065
+ const newGroupTy = initialCodeGroupTy + deltaY;
1066
+ const centerX = currentBBox.x + currentBBox.width / 2;
1067
+ const centerY = currentBBox.y + currentBBox.height / 2;
1068
+ selectedCodeBlock.setAttribute(
1069
+ "transform",
1070
+ `translate(${newGroupTx}, ${newGroupTy}) rotate(${angle}, ${centerX}, ${centerY})`
1071
+ );
1072
+ } else {
1073
+ const newGroupTx = initialCodeGroupTx + deltaX;
1074
+ const newGroupTy = initialCodeGroupTy + deltaY;
1075
+ selectedCodeBlock.setAttribute("transform", `translate(${newGroupTx}, ${newGroupTy})`);
1076
+ }
1077
+ updateCodeBackground(selectedCodeBlock, codeElement);
1078
+ updateAttachedArrows(selectedCodeBlock);
1079
+ clearTimeout(selectedCodeBlock.updateFeedbackTimeout);
1080
+ selectedCodeBlock.updateFeedbackTimeout = setTimeout(() => {
1081
+ updateCodeSelectionFeedback();
1082
+ delete selectedCodeBlock.updateFeedbackTimeout;
1083
+ }, 0);
1084
+ } else if (isCodeRotating) {
1085
+ const codeElement = selectedCodeBlock.querySelector("text");
1086
+ if (!codeElement) return;
1087
+ const bbox = codeElement.getBBox();
1088
+ const centerX = bbox.x + bbox.width / 2;
1089
+ const centerY = bbox.y + bbox.height / 2;
1090
+ const mousePos = getSVGCoordinates(event);
1091
+ let centerPoint = svg.createSVGPoint();
1092
+ centerPoint.x = centerX;
1093
+ centerPoint.y = centerY;
1094
+ const groupTransform = selectedCodeBlock.transform.baseVal.consolidate();
1095
+ if (groupTransform) {
1096
+ centerPoint = centerPoint.matrixTransform(groupTransform.matrix);
1097
+ }
1098
+ const currentAngle = Math.atan2(mousePos.y - centerPoint.y, mousePos.x - centerPoint.x) * 180 / Math.PI;
1099
+ const rotationDiff = currentAngle - codeRotationStartAngle;
1100
+ const newTransform = `translate(${codeRotationStartTransform.e}, ${codeRotationStartTransform.f}) rotate(${rotationDiff}, ${centerX}, ${centerY})`;
1101
+ selectedCodeBlock.setAttribute("transform", newTransform);
1102
+ updateAttachedArrows(selectedCodeBlock);
1103
+ updateCodeSelectionFeedback();
1104
+ }
1105
+ if ((isCodeToolActive || isTextToolActive && isTextInCodeMode) && !isCodeDragging && !isCodeResizing && !isCodeRotating) {
1106
+ svg.style.cursor = "text";
1107
+ const { x, y } = getSVGCoordinates(event);
1108
+ const tempCodeBounds = {
1109
+ x: x - 275,
1110
+ y: y - 30,
1111
+ width: 550,
1112
+ height: 60
1113
+ };
1114
+ if (typeof shapes !== "undefined" && Array.isArray(shapes)) {
1115
+ shapes.forEach((frame) => {
1116
+ if (frame.shapeName === "frame") {
1117
+ if (frame.isShapeInFrame(tempCodeBounds)) {
1118
+ frame.highlightFrame();
1119
+ hoveredCodeFrame = frame;
1120
+ } else if (hoveredCodeFrame === frame) {
1121
+ frame.removeHighlight();
1122
+ hoveredCodeFrame = null;
1123
+ }
1124
+ }
1125
+ });
1126
+ }
1127
+ } else if (isSelectionToolActive && !isCodeDragging && !isCodeResizing && !isCodeRotating) {
1128
+ const targetGroup = event.target.closest('g[data-type="code-group"]');
1129
+ if (targetGroup) {
1130
+ svg.style.cursor = "move";
1131
+ } else {
1132
+ svg.style.cursor = "default";
1133
+ }
1134
+ }
1135
+ };
1136
+ var handleCodeMouseUp = (event) => {
1137
+ if (event.button !== 0) return;
1138
+ if (isCodeDragging && selectedCodeBlock) {
1139
+ const currentTransform = selectedCodeBlock.transform.baseVal.consolidate();
1140
+ if (currentTransform) {
1141
+ const finalTranslateX = currentTransform.matrix.e;
1142
+ const finalTranslateY = currentTransform.matrix.f;
1143
+ const initialX = parseFloat(selectedCodeBlock.getAttribute("data-x")) || 0;
1144
+ const initialY = parseFloat(selectedCodeBlock.getAttribute("data-y")) || 0;
1145
+ let codeShape = null;
1146
+ if (typeof shapes !== "undefined" && Array.isArray(shapes)) {
1147
+ codeShape = shapes.find((shape) => shape.shapeName === "code" && shape.group === selectedCodeBlock);
1148
+ }
1149
+ const oldPosWithFrame = {
1150
+ x: initialX,
1151
+ y: initialY,
1152
+ rotation: extractRotationFromTransform(selectedCodeBlock) || 0,
1153
+ parentFrame: draggedCodeInitialFrame
1154
+ };
1155
+ const newPosWithFrame = {
1156
+ x: finalTranslateX,
1157
+ y: finalTranslateY,
1158
+ rotation: extractRotationFromTransform(selectedCodeBlock) || 0,
1159
+ parentFrame: codeShape ? codeShape.parentFrame : null
1160
+ };
1161
+ const stateChanged = initialX !== finalTranslateX || initialY !== finalTranslateY;
1162
+ const frameChanged = oldPosWithFrame.parentFrame !== newPosWithFrame.parentFrame;
1163
+ if (stateChanged || frameChanged) {
1164
+ pushTransformAction(
1165
+ {
1166
+ type: "code",
1167
+ element: selectedCodeBlock,
1168
+ shapeName: "code"
1169
+ },
1170
+ oldPosWithFrame,
1171
+ newPosWithFrame
1172
+ );
1173
+ }
1174
+ if (codeShape) {
1175
+ const finalFrame = hoveredCodeFrame;
1176
+ if (draggedCodeInitialFrame !== finalFrame) {
1177
+ if (draggedCodeInitialFrame) {
1178
+ draggedCodeInitialFrame.removeShapeFromFrame(codeShape);
1179
+ }
1180
+ if (finalFrame) {
1181
+ finalFrame.addShapeToFrame(codeShape);
1182
+ }
1183
+ if (frameChanged) {
1184
+ pushFrameAttachmentAction(
1185
+ finalFrame || draggedCodeInitialFrame,
1186
+ codeShape,
1187
+ finalFrame ? "attach" : "detach",
1188
+ draggedCodeInitialFrame
1189
+ );
1190
+ }
1191
+ } else if (draggedCodeInitialFrame) {
1192
+ draggedCodeInitialFrame.restoreToFrame(codeShape);
1193
+ }
1194
+ }
1195
+ selectedCodeBlock.setAttribute("data-x", finalTranslateX);
1196
+ selectedCodeBlock.setAttribute("data-y", finalTranslateY);
1197
+ }
1198
+ draggedCodeInitialFrame = null;
1199
+ } else if (isCodeResizing && selectedCodeBlock) {
1200
+ const codeElement = selectedCodeBlock.querySelector("text");
1201
+ if (codeElement) {
1202
+ const finalFontSize = codeElement.getAttribute("font-size");
1203
+ const initialFontSize = startCodeFontSize;
1204
+ const currentTransform = selectedCodeBlock.transform.baseVal.consolidate();
1205
+ if (currentTransform && initialFontSize !== parseFloat(finalFontSize)) {
1206
+ const finalTranslateX = currentTransform.matrix.e;
1207
+ const finalTranslateY = currentTransform.matrix.f;
1208
+ pushTransformAction(
1209
+ {
1210
+ type: "code",
1211
+ element: selectedCodeBlock,
1212
+ shapeName: "code"
1213
+ },
1214
+ {
1215
+ x: initialCodeGroupTx,
1216
+ y: initialCodeGroupTy,
1217
+ fontSize: initialFontSize,
1218
+ rotation: extractRotationFromTransform(selectedCodeBlock) || 0
1219
+ },
1220
+ {
1221
+ x: finalTranslateX,
1222
+ y: finalTranslateY,
1223
+ fontSize: parseFloat(finalFontSize),
1224
+ rotation: extractRotationFromTransform(selectedCodeBlock) || 0
1225
+ }
1226
+ );
1227
+ selectedCodeBlock.setAttribute("data-x", finalTranslateX);
1228
+ selectedCodeBlock.setAttribute("data-y", finalTranslateY);
1229
+ }
1230
+ clearTimeout(selectedCodeBlock.updateFeedbackTimeout);
1231
+ updateCodeSelectionFeedback();
1232
+ }
1233
+ } else if (isCodeRotating && selectedCodeBlock) {
1234
+ const currentTransform = selectedCodeBlock.transform.baseVal.consolidate();
1235
+ if (currentTransform && codeRotationStartTransform) {
1236
+ const initialRotation = Math.atan2(codeRotationStartTransform.b, codeRotationStartTransform.a) * 180 / Math.PI;
1237
+ const finalRotation = extractRotationFromTransform(selectedCodeBlock) || 0;
1238
+ if (Math.abs(initialRotation - finalRotation) > 1) {
1239
+ pushTransformAction(
1240
+ {
1241
+ type: "code",
1242
+ element: selectedCodeBlock,
1243
+ shapeName: "code"
1244
+ },
1245
+ {
1246
+ x: codeRotationStartTransform.e,
1247
+ y: codeRotationStartTransform.f,
1248
+ rotation: initialRotation
1249
+ },
1250
+ {
1251
+ x: currentTransform.matrix.e,
1252
+ y: currentTransform.matrix.f,
1253
+ rotation: finalRotation
1254
+ }
1255
+ );
1256
+ }
1257
+ }
1258
+ updateCodeSelectionFeedback();
1259
+ }
1260
+ if (hoveredCodeFrame) {
1261
+ hoveredCodeFrame.removeHighlight();
1262
+ hoveredCodeFrame = null;
1263
+ }
1264
+ if (isSelectionToolActive) {
1265
+ const targetGroup = event.target.closest('g[data-type="code-group"]');
1266
+ const isResizeHandle = event.target.closest(".resize-handle");
1267
+ const isRotateAnchor = event.target.closest(".rotate-anchor");
1268
+ if (!targetGroup && !isResizeHandle && !isRotateAnchor && selectedCodeBlock) {
1269
+ deselectCodeBlock();
1270
+ }
1271
+ }
1272
+ isCodeDragging = false;
1273
+ isCodeResizing = false;
1274
+ isCodeRotating = false;
1275
+ currentCodeResizeHandle = null;
1276
+ startCodePoint = null;
1277
+ startCodeBBox = null;
1278
+ startCodeFontSize = null;
1279
+ codeDragOffsetX = void 0;
1280
+ codeDragOffsetY = void 0;
1281
+ initialCodeHandlePosRelGroup = null;
1282
+ initialCodeGroupTx = 0;
1283
+ initialCodeGroupTy = 0;
1284
+ codeRotationStartAngle = 0;
1285
+ codeRotationStartTransform = null;
1286
+ svg.style.cursor = "default";
1287
+ svg.removeEventListener("pointermove", handleCodeMouseMove);
1288
+ svg.removeEventListener("pointerup", handleCodeMouseUp);
1289
+ };
1290
+ function extractRotationFromTransform(element) {
1291
+ const currentTransform = element.transform.baseVal.consolidate();
1292
+ if (currentTransform) {
1293
+ const matrix = currentTransform.matrix;
1294
+ return Math.atan2(matrix.b, matrix.a) * 180 / Math.PI;
1295
+ }
1296
+ return 0;
1297
+ }
1298
+ var handleCodeMouseDown = function(e) {
1299
+ if (!e.target) return;
1300
+ const activeContentEditor = document.querySelector(".svg-code-editor[contenteditable='true']");
1301
+ if (activeContentEditor) {
1302
+ const editorContainer = activeContentEditor.closest(".svg-code-container");
1303
+ if (editorContainer && editorContainer.contains(e.target)) {
1304
+ return;
1305
+ }
1306
+ let codeElement = activeContentEditor.originalCodeElement;
1307
+ if (codeElement) {
1308
+ renderCodeFromEditor(activeContentEditor, codeElement, true);
1309
+ } else if (editorContainer && document.body.contains(editorContainer)) {
1310
+ document.body.removeChild(editorContainer);
1311
+ }
1312
+ }
1313
+ const activeEditor = document.querySelector("textarea.svg-code-editor");
1314
+ if (activeEditor && activeEditor.contains(e.target)) {
1315
+ return;
1316
+ }
1317
+ if (activeEditor && !activeEditor.contains(e.target)) {
1318
+ let codeElement = activeEditor.originalCodeElement;
1319
+ if (codeElement) {
1320
+ renderCode(activeEditor, codeElement, true);
1321
+ } else if (document.body.contains(activeEditor)) {
1322
+ document.body.removeChild(activeEditor);
1323
+ }
1324
+ }
1325
+ const targetGroup = e.target.closest('g[data-type="code-group"]');
1326
+ if (isSelectionToolActive && e.button === 0) {
1327
+ if (targetGroup) {
1328
+ if (e.target.closest(".resize-handle") || e.target.closest(".rotate-anchor")) {
1329
+ return;
1330
+ }
1331
+ if (targetGroup === selectedCodeBlock) {
1332
+ startCodeDrag(e);
1333
+ } else {
1334
+ selectCodeBlock(targetGroup);
1335
+ startCodeDrag(e);
1336
+ }
1337
+ } else {
1338
+ deselectCodeBlock();
1339
+ }
1340
+ } else if ((isCodeToolActive || isTextToolActive) && e.button === 0) {
1341
+ if (targetGroup) {
1342
+ const codeElement = targetGroup.querySelector("text");
1343
+ if (codeElement && (e.target.tagName === "text" || e.target.tagName === "tspan")) {
1344
+ makeCodeEditable(codeElement, targetGroup, e);
1345
+ e.stopPropagation();
1346
+ } else {
1347
+ deselectCodeBlock();
1348
+ addCodeBlock(e);
1349
+ }
1350
+ } else {
1351
+ deselectCodeBlock();
1352
+ addCodeBlock(e);
1353
+ }
1354
+ }
1355
+ };
1356
+ codeTextColorOptions.forEach((span) => {
1357
+ span.addEventListener("click", (event) => {
1358
+ event.stopPropagation();
1359
+ codeTextColorOptions.forEach((el) => el.classList.remove("selected"));
1360
+ span.classList.add("selected");
1361
+ const newColor = span.getAttribute("data-id");
1362
+ const oldColor = codeTextColor;
1363
+ codeTextColor = newColor;
1364
+ if (selectedCodeBlock) {
1365
+ const codeEditor = selectedCodeBlock.querySelector(".svg-code-editor");
1366
+ if (codeEditor) {
1367
+ const currentColor = codeEditor.style.color;
1368
+ if (currentColor !== newColor) {
1369
+ pushOptionsChangeAction(
1370
+ {
1371
+ type: "code",
1372
+ element: selectedCodeBlock,
1373
+ shapeName: "code"
1374
+ },
1375
+ {
1376
+ color: currentColor,
1377
+ font: codeEditor.style.fontFamily,
1378
+ size: codeEditor.style.fontSize,
1379
+ align: codeEditor.style.textAlign
1380
+ // Although mostly 'left'
1381
+ },
1382
+ {
1383
+ color: newColor,
1384
+ font: codeEditor.style.fontFamily,
1385
+ size: codeEditor.style.fontSize,
1386
+ align: codeEditor.style.textAlign
1387
+ }
1388
+ );
1389
+ }
1390
+ codeEditor.style.color = newColor;
1391
+ updateSyntaxHighlighting(codeEditor);
1392
+ }
1393
+ }
1394
+ });
1395
+ });
1396
+ codeTextFontOptions.forEach((span) => {
1397
+ span.addEventListener("click", (event) => {
1398
+ event.stopPropagation();
1399
+ codeTextFontOptions.forEach((el) => el.classList.remove("selected"));
1400
+ span.classList.add("selected");
1401
+ const newFont = span.getAttribute("data-id");
1402
+ const oldFont = codeTextFont;
1403
+ codeTextFont = newFont;
1404
+ if (selectedCodeBlock) {
1405
+ const codeEditor = selectedCodeBlock.querySelector(".svg-code-editor");
1406
+ if (codeEditor) {
1407
+ const currentFont = codeEditor.style.fontFamily;
1408
+ if (currentFont !== newFont) {
1409
+ pushOptionsChangeAction(
1410
+ {
1411
+ type: "code",
1412
+ element: selectedCodeBlock,
1413
+ shapeName: "code"
1414
+ },
1415
+ {
1416
+ color: codeEditor.style.color,
1417
+ font: currentFont,
1418
+ size: codeEditor.style.fontSize,
1419
+ align: codeEditor.style.textAlign
1420
+ },
1421
+ {
1422
+ color: codeEditor.style.color,
1423
+ font: newFont,
1424
+ size: codeEditor.style.fontSize,
1425
+ align: codeEditor.style.textAlign
1426
+ }
1427
+ );
1428
+ }
1429
+ codeEditor.style.fontFamily = newFont;
1430
+ updateSyntaxHighlighting(codeEditor);
1431
+ setTimeout(updateCodeSelectionFeedback, 0);
1432
+ }
1433
+ }
1434
+ });
1435
+ });
1436
+ codeTextSizeOptions.forEach((span) => {
1437
+ span.addEventListener("click", (event) => {
1438
+ event.stopPropagation();
1439
+ codeTextSizeOptions.forEach((el) => el.classList.remove("selected"));
1440
+ span.classList.add("selected");
1441
+ const newSize = span.getAttribute("data-id") + "px";
1442
+ const oldSize = codeTextSize;
1443
+ codeTextSize = newSize;
1444
+ if (selectedCodeBlock) {
1445
+ const codeEditor = selectedCodeBlock.querySelector(".svg-code-editor");
1446
+ if (codeEditor) {
1447
+ const currentSize = codeEditor.style.fontSize;
1448
+ if (currentSize !== newSize) {
1449
+ pushOptionsChangeAction(
1450
+ {
1451
+ type: "code",
1452
+ element: selectedCodeBlock,
1453
+ shapeName: "code"
1454
+ },
1455
+ {
1456
+ color: codeEditor.style.color,
1457
+ font: codeEditor.style.fontFamily,
1458
+ size: currentSize,
1459
+ align: codeEditor.style.textAlign
1460
+ },
1461
+ {
1462
+ color: codeEditor.style.color,
1463
+ font: codeEditor.style.fontFamily,
1464
+ size: newSize,
1465
+ align: codeEditor.style.textAlign
1466
+ }
1467
+ );
1468
+ }
1469
+ codeEditor.style.fontSize = newSize;
1470
+ adjustCodeEditorSize(codeEditor);
1471
+ updateSyntaxHighlighting(codeEditor);
1472
+ setTimeout(updateCodeSelectionFeedback, 0);
1473
+ }
1474
+ }
1475
+ });
1476
+ });
1477
+ codeTextAlignOptions.forEach((span) => {
1478
+ span.addEventListener("click", (event) => {
1479
+ event.stopPropagation();
1480
+ codeTextAlignOptions.forEach((el) => el.classList.remove("selected"));
1481
+ span.classList.add("selected");
1482
+ const newAlign = span.getAttribute("data-id");
1483
+ const oldAlign = codeTextAlign;
1484
+ codeTextAlign = newAlign;
1485
+ if (selectedCodeBlock) {
1486
+ const codeEditor = selectedCodeBlock.querySelector(".svg-code-editor");
1487
+ if (codeEditor) {
1488
+ const currentAlign = codeEditor.style.textAlign;
1489
+ if (currentAlign !== newAlign) {
1490
+ pushOptionsChangeAction(
1491
+ {
1492
+ type: "code",
1493
+ element: selectedCodeBlock,
1494
+ shapeName: "code"
1495
+ },
1496
+ {
1497
+ color: codeEditor.style.color,
1498
+ font: codeEditor.style.fontFamily,
1499
+ size: codeEditor.style.fontSize,
1500
+ align: currentAlign
1501
+ },
1502
+ {
1503
+ color: codeEditor.style.color,
1504
+ font: codeEditor.style.fontFamily,
1505
+ size: codeEditor.style.fontSize,
1506
+ align: newAlign
1507
+ }
1508
+ );
1509
+ }
1510
+ codeEditor.style.textAlign = newAlign;
1511
+ setTimeout(updateCodeSelectionFeedback, 0);
1512
+ }
1513
+ }
1514
+ });
1515
+ });
1516
+ var editorStyles = `
1517
+ .svg-code-container {
1518
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
1519
+ border-radius: 6px;
1520
+ }
1521
+
1522
+ .svg-code-editor {
1523
+ scrollbar-width: thin;
1524
+ scrollbar-color: #484f58 #161b22;
1525
+ }
1526
+
1527
+ .svg-code-editor::-webkit-scrollbar {
1528
+ width: 8px;
1529
+ height: 8px;
1530
+ }
1531
+
1532
+ .svg-code-editor::-webkit-scrollbar-track {
1533
+ background: #161b22;
1534
+ border-radius: 6px;
1535
+ }
1536
+
1537
+ .svg-code-editor::-webkit-scrollbar-thumb {
1538
+ background: #484f58;
1539
+ border-radius: 6px;
1540
+ }
1541
+
1542
+ .svg-code-editor::-webkit-scrollbar-thumb:hover {
1543
+ background: #6e7681;
1544
+ }
1545
+
1546
+ /* Language detection indicator */
1547
+ .svg-code-container::after {
1548
+ content: attr(data-language);
1549
+ position: absolute;
1550
+ top: -20px;
1551
+ right: 0;
1552
+ background: #007acc;
1553
+ color: white;
1554
+ padding: 2px 6px;
1555
+ border-radius: 3px;
1556
+ font-size: 10px;
1557
+ font-family: monospace;
1558
+ opacity: 0.8;
1559
+ }
1560
+ `;
1561
+ if (!document.getElementById("code-editor-styles")) {
1562
+ const styleSheet = document.createElement("style");
1563
+ styleSheet.id = "code-editor-styles";
1564
+ styleSheet.textContent = editorStyles;
1565
+ document.head.appendChild(styleSheet);
1566
+ }
1567
+ function setCodeLanguage(lang) {
1568
+ codeLanguage = lang;
1569
+ if (selectedCodeBlock) {
1570
+ const codeElement = selectedCodeBlock.querySelector("text");
1571
+ if (codeElement) {
1572
+ codeElement.setAttribute("data-language", lang);
1573
+ }
1574
+ }
1575
+ }
1576
+ function getCodeLanguage() {
1577
+ return codeLanguage;
1578
+ }
1579
+ function getSelectedCodeBlock() {
1580
+ return selectedCodeBlock;
1581
+ }
1582
+
1583
+ export {
1584
+ wrapCodeElement,
1585
+ addCodeBlock,
1586
+ makeCodeEditable,
1587
+ extractTextFromCodeElement,
1588
+ createHighlightedSVGText,
1589
+ applySyntaxHighlightingToSVG,
1590
+ updateCodeBackground,
1591
+ selectCodeBlock,
1592
+ deselectCodeBlock,
1593
+ handleCodeMouseMove,
1594
+ handleCodeMouseUp,
1595
+ handleCodeMouseDown,
1596
+ setCodeLanguage,
1597
+ getCodeLanguage,
1598
+ getSelectedCodeBlock
1599
+ };
1600
+ //# sourceMappingURL=chunk-LHRTXNVE.js.map