@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,1105 @@
1
+ "use client";
2
+
3
+ // src/core/AIRenderer.js
4
+ var NODE_W = 160;
5
+ var NODE_H = 60;
6
+ var H_SPACING = 260;
7
+ var V_SPACING = 180;
8
+ var NS = "http://www.w3.org/2000/svg";
9
+ var ALIGN_THRESHOLD = 30;
10
+ function parseMermaid(src) {
11
+ const lines = src.trim().split("\n").map((l) => l.trim()).filter((l) => l && !l.startsWith("%%"));
12
+ if (lines.length === 0) return null;
13
+ const headerMatch = lines[0].match(/^(graph|flowchart)\s+(TD|TB|LR|RL|BT)/i);
14
+ const direction = headerMatch ? headerMatch[2].toUpperCase() : "TD";
15
+ const isHorizontal = direction === "LR" || direction === "RL";
16
+ const startIdx = headerMatch ? 1 : 0;
17
+ const nodesMap = /* @__PURE__ */ new Map();
18
+ const edges = [];
19
+ const classDefs = /* @__PURE__ */ new Map();
20
+ const classAssigns = [];
21
+ function cleanLabel(label) {
22
+ return label.replace(/<br\s*\/?>/gi, "\n").replace(/"/g, "");
23
+ }
24
+ function parseNodeRef(raw) {
25
+ raw = raw.trim();
26
+ if (!raw) return null;
27
+ let id, label, type;
28
+ let m = raw.match(/^(\w+)\(\((.+?)\)\)$/);
29
+ if (m) {
30
+ id = m[1];
31
+ label = m[2];
32
+ type = "circle";
33
+ }
34
+ if (!m) {
35
+ m = raw.match(/^(\w+)\{(.+?)\}$/);
36
+ if (m) {
37
+ id = m[1];
38
+ label = m[2];
39
+ type = "diamond";
40
+ }
41
+ }
42
+ if (!m) {
43
+ m = raw.match(/^(\w+)>(.+?)\]$/);
44
+ if (m) {
45
+ id = m[1];
46
+ label = m[2];
47
+ type = "asymmetric";
48
+ }
49
+ }
50
+ if (!m) {
51
+ m = raw.match(/^(\w+)\((.+?)\)$/);
52
+ if (m) {
53
+ id = m[1];
54
+ label = m[2];
55
+ type = "roundrect";
56
+ }
57
+ }
58
+ if (!m) {
59
+ m = raw.match(/^(\w+)\[(.+?)\]$/);
60
+ if (m) {
61
+ id = m[1];
62
+ label = m[2];
63
+ type = "rectangle";
64
+ }
65
+ }
66
+ if (!m) {
67
+ id = raw;
68
+ label = raw;
69
+ type = "rectangle";
70
+ }
71
+ label = cleanLabel(label);
72
+ if (!nodesMap.has(id)) {
73
+ nodesMap.set(id, { id, type, label });
74
+ } else if (label !== id) {
75
+ nodesMap.get(id).label = label;
76
+ nodesMap.get(id).type = type;
77
+ }
78
+ return id;
79
+ }
80
+ const subgraphs = [];
81
+ let currentSubgraph = null;
82
+ for (let i = startIdx; i < lines.length; i++) {
83
+ let addToSubgraph = function(fromId, toId) {
84
+ if (currentSubgraph) {
85
+ if (fromId && !currentSubgraph.nodeIds.includes(fromId)) currentSubgraph.nodeIds.push(fromId);
86
+ if (toId && !currentSubgraph.nodeIds.includes(toId)) currentSubgraph.nodeIds.push(toId);
87
+ }
88
+ };
89
+ const line = lines[i].replace(/;$/, "").trim();
90
+ if (!line) continue;
91
+ const classDefMatch = line.match(/^classDef\s+(\w+)\s+(.+)$/i);
92
+ if (classDefMatch) {
93
+ const name = classDefMatch[1];
94
+ const propsStr = classDefMatch[2].replace(/;$/, "");
95
+ const props = {};
96
+ for (const part of propsStr.split(",")) {
97
+ const [key, val] = part.split(":").map((s) => s.trim());
98
+ if (key === "fill") props.fill = val;
99
+ else if (key === "stroke") props.stroke = val;
100
+ else if (key === "stroke-width") props.strokeWidth = parseFloat(val);
101
+ }
102
+ classDefs.set(name, props);
103
+ continue;
104
+ }
105
+ const classMatch = line.match(/^class\s+(.+?)\s+(\w+)$/i);
106
+ if (classMatch) {
107
+ const nodeIds2 = classMatch[1].split(",").map((s) => s.trim());
108
+ classAssigns.push({ nodeIds: nodeIds2, className: classMatch[2] });
109
+ continue;
110
+ }
111
+ const sgMatch = line.match(/^subgraph\s+(\w+)(?:\s*\[?"?(.+?)"?\]?)?$/i);
112
+ if (sgMatch) {
113
+ currentSubgraph = {
114
+ id: sgMatch[1],
115
+ label: sgMatch[2] || sgMatch[1],
116
+ nodeIds: []
117
+ };
118
+ continue;
119
+ }
120
+ if (line.toLowerCase() === "end" && currentSubgraph) {
121
+ subgraphs.push(currentSubgraph);
122
+ currentSubgraph = null;
123
+ continue;
124
+ }
125
+ let match = line.match(/^(.+?)\s*--\s*(.+?)\s*-->\s*(.+)$/);
126
+ if (match) {
127
+ const fromId = parseNodeRef(match[1].trim());
128
+ const toId = parseNodeRef(match[3].trim());
129
+ if (fromId && toId) edges.push({ from: fromId, to: toId, label: cleanLabel(match[2].trim()), directed: true, style: "normal" });
130
+ addToSubgraph(fromId, toId);
131
+ continue;
132
+ }
133
+ match = line.match(/^(.+?)\s*-\.->?\s*(?:\|([^|]*)\|)?\s*(.+)$/);
134
+ if (match) {
135
+ const fromId = parseNodeRef(match[1].trim());
136
+ const toId = parseNodeRef(match[3].trim());
137
+ const edgeLabel = match[2] ? cleanLabel(match[2].trim()) : void 0;
138
+ if (fromId && toId) edges.push({ from: fromId, to: toId, label: edgeLabel, directed: true, style: "dotted" });
139
+ addToSubgraph(fromId, toId);
140
+ continue;
141
+ }
142
+ match = line.match(/^(.+?)\s*==>\s*(?:\|([^|]*)\|)?\s*(.+)$/);
143
+ if (match) {
144
+ const fromId = parseNodeRef(match[1].trim());
145
+ const toId = parseNodeRef(match[3].trim());
146
+ const edgeLabel = match[2] ? cleanLabel(match[2].trim()) : void 0;
147
+ if (fromId && toId) edges.push({ from: fromId, to: toId, label: edgeLabel, directed: true, style: "thick" });
148
+ addToSubgraph(fromId, toId);
149
+ continue;
150
+ }
151
+ match = line.match(/^(.+?)\s*(-{3,})\s*(?:\|([^|]*)\|)?\s*(.+)$/);
152
+ if (match && !match[2].includes(">")) {
153
+ const fromId = parseNodeRef(match[1].trim());
154
+ const toId = parseNodeRef(match[4].trim());
155
+ const edgeLabel = match[3] ? cleanLabel(match[3].trim()) : void 0;
156
+ if (fromId && toId) edges.push({ from: fromId, to: toId, label: edgeLabel, directed: false, style: "normal" });
157
+ addToSubgraph(fromId, toId);
158
+ continue;
159
+ }
160
+ match = line.match(/^(.+?)\s*(-{1,2}>|-->)\s*(?:\|([^|]*)\|)?\s*(.+)$/);
161
+ if (match) {
162
+ const fromId = parseNodeRef(match[1].trim());
163
+ const toId = parseNodeRef(match[4].trim());
164
+ const edgeLabel = match[3] ? cleanLabel(match[3].trim()) : void 0;
165
+ if (fromId && toId) edges.push({ from: fromId, to: toId, label: edgeLabel, directed: true, style: "normal" });
166
+ addToSubgraph(fromId, toId);
167
+ continue;
168
+ }
169
+ const nodeId = parseNodeRef(line);
170
+ if (nodeId && currentSubgraph) {
171
+ if (!currentSubgraph.nodeIds.includes(nodeId)) currentSubgraph.nodeIds.push(nodeId);
172
+ }
173
+ }
174
+ if (nodesMap.size === 0) return null;
175
+ for (const assign of classAssigns) {
176
+ const style = classDefs.get(assign.className);
177
+ if (!style) continue;
178
+ for (const nid of assign.nodeIds) {
179
+ const node = nodesMap.get(nid);
180
+ if (node) {
181
+ if (style.fill) node.fill = style.fill;
182
+ if (style.stroke) node.stroke = style.stroke;
183
+ if (style.strokeWidth) node.strokeWidth = style.strokeWidth;
184
+ }
185
+ }
186
+ }
187
+ const nodeIds = Array.from(nodesMap.keys());
188
+ const children = /* @__PURE__ */ new Map();
189
+ const parents = /* @__PURE__ */ new Map();
190
+ nodeIds.forEach((id) => {
191
+ children.set(id, []);
192
+ parents.set(id, []);
193
+ });
194
+ edges.forEach((e) => {
195
+ if (children.has(e.from)) children.get(e.from).push(e.to);
196
+ if (parents.has(e.to)) parents.get(e.to).push(e.from);
197
+ });
198
+ const layers = /* @__PURE__ */ new Map();
199
+ const roots = nodeIds.filter((id) => parents.get(id).length === 0);
200
+ if (roots.length === 0) roots.push(nodeIds[0]);
201
+ const queue = roots.map((id) => ({ id, layer: 0 }));
202
+ const visited = /* @__PURE__ */ new Set();
203
+ while (queue.length > 0) {
204
+ const { id, layer } = queue.shift();
205
+ if (visited.has(id)) {
206
+ if (layer > (layers.get(id) || 0)) layers.set(id, layer);
207
+ continue;
208
+ }
209
+ visited.add(id);
210
+ layers.set(id, Math.max(layer, layers.get(id) || 0));
211
+ for (const child of children.get(id) || []) queue.push({ id: child, layer: layer + 1 });
212
+ }
213
+ nodeIds.forEach((id) => {
214
+ if (!visited.has(id)) layers.set(id, 0);
215
+ });
216
+ const layerGroups = /* @__PURE__ */ new Map();
217
+ layers.forEach((layer, id) => {
218
+ if (!layerGroups.has(layer)) layerGroups.set(layer, []);
219
+ layerGroups.get(layer).push(id);
220
+ });
221
+ const nodes = [];
222
+ Array.from(layerGroups.keys()).sort((a, b) => a - b).forEach((layerIdx, li) => {
223
+ const group = layerGroups.get(layerIdx);
224
+ const startOffset = -(group.length * H_SPACING) / 2 + H_SPACING / 2;
225
+ group.forEach((id, gi) => {
226
+ const nd = nodesMap.get(id);
227
+ const x = isHorizontal ? li * H_SPACING : startOffset + gi * H_SPACING;
228
+ const y = isHorizontal ? startOffset + gi * V_SPACING : li * V_SPACING;
229
+ const labelLines = (nd.label || "").split("\n");
230
+ const maxLineLen = Math.max(...labelLines.map((l) => l.length));
231
+ const nw = Math.max(NODE_W, maxLineLen * 10 + 40);
232
+ const nh = Math.max(NODE_H, labelLines.length * 20 + 20);
233
+ nodes.push({
234
+ id: nd.id,
235
+ type: nd.type,
236
+ label: nd.label,
237
+ x,
238
+ y,
239
+ width: nw,
240
+ height: nh,
241
+ fill: nd.fill,
242
+ stroke: nd.stroke,
243
+ strokeWidth: nd.strokeWidth
244
+ });
245
+ });
246
+ });
247
+ return {
248
+ title: "Mermaid Diagram",
249
+ direction,
250
+ nodes,
251
+ edges: edges.map((e) => ({
252
+ from: e.from,
253
+ to: e.to,
254
+ label: e.label,
255
+ directed: e.directed !== false,
256
+ style: e.style || "normal"
257
+ })),
258
+ subgraphs: subgraphs.length > 0 ? subgraphs.map((sg) => ({
259
+ id: sg.id,
260
+ label: sg.label,
261
+ nodes: sg.nodeIds
262
+ })) : void 0
263
+ };
264
+ }
265
+ function renderAIDiagram(diagram) {
266
+ if (!diagram?.nodes?.length) {
267
+ console.error("[AIRenderer] Invalid diagram");
268
+ return false;
269
+ }
270
+ const nodes = diagram.nodes;
271
+ const edges = diagram.edges || [];
272
+ const title = diagram.title || "AI Diagram";
273
+ if (!window.svg || !window.Frame || !window.Rectangle) {
274
+ console.error("[AIRenderer] Engine not initialized");
275
+ return false;
276
+ }
277
+ const vb = window.currentViewBox || { x: 0, y: 0, width: window.innerWidth, height: window.innerHeight };
278
+ const vcx = vb.x + vb.width / 2;
279
+ const vcy = vb.y + vb.height / 2;
280
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
281
+ nodes.forEach((n) => {
282
+ minX = Math.min(minX, n.x);
283
+ minY = Math.min(minY, n.y);
284
+ maxX = Math.max(maxX, n.x + (n.width || NODE_W));
285
+ maxY = Math.max(maxY, n.y + (n.height || NODE_H));
286
+ });
287
+ const dw = maxX - minX, dh = maxY - minY;
288
+ const ox = vcx - dw / 2 - minX;
289
+ const oy = vcy - dh / 2 - minY;
290
+ const groupId = `aidiag-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
291
+ const frame = {
292
+ addShapeToFrame(s) {
293
+ if (s) s.groupId = groupId;
294
+ },
295
+ selectFrame() {
296
+ },
297
+ removeSelection() {
298
+ },
299
+ shapeName: "frame"
300
+ };
301
+ const nodeMap = /* @__PURE__ */ new Map();
302
+ for (const node of nodes) {
303
+ const nx = node.x + ox, ny = node.y + oy;
304
+ const nw = node.width || NODE_W, nh = node.height || NODE_H;
305
+ const cx = nx + nw / 2, cy = ny + nh / 2;
306
+ let shape = null;
307
+ const nodeOpts = {
308
+ stroke: node.stroke || "#e0e0e0",
309
+ strokeWidth: node.strokeWidth ?? 1.5,
310
+ fill: node.fill || "transparent",
311
+ fillStyle: node.fillStyle || "none",
312
+ roughness: node.roughness ?? 1,
313
+ strokeDasharray: node.strokeDasharray || "",
314
+ // Shading support
315
+ shadeColor: node.shadeColor || null,
316
+ shadeOpacity: node.shadeOpacity !== void 0 ? node.shadeOpacity : 0.15,
317
+ shadeDirection: node.shadeDirection || "bottom",
318
+ // Label styling
319
+ labelColor: node.labelColor || void 0,
320
+ labelFontSize: node.labelFontSize || void 0
321
+ };
322
+ try {
323
+ if (node.type === "icon" && node.iconKeyword) {
324
+ shape = new window.Rectangle(nx, ny, nw, nh, {
325
+ ...nodeOpts,
326
+ stroke: nodeOpts.stroke,
327
+ strokeDasharray: "4 3"
328
+ });
329
+ fetchAndPlaceIcon(node.iconKeyword, nx, ny, nw, nh, shape, frame);
330
+ } else if (node.type === "circle" && window.Circle) {
331
+ shape = new window.Circle(cx, cy, nw / 2, nh / 2, nodeOpts);
332
+ } else if (node.type === "diamond" && window.Rectangle) {
333
+ const sz = Math.max(nw, nh) * 0.7;
334
+ shape = new window.Rectangle(cx - sz / 2, cy - sz / 2, sz, sz, nodeOpts);
335
+ shape.rotation = 45;
336
+ shape.draw();
337
+ } else if (node.type === "roundrect" && window.Rectangle) {
338
+ shape = new window.Rectangle(nx, ny, nw, nh, { ...nodeOpts, cornerRadius: Math.min(nw, nh) * 0.2 });
339
+ } else if (window.Rectangle) {
340
+ shape = new window.Rectangle(nx, ny, nw, nh, nodeOpts);
341
+ }
342
+ } catch (err) {
343
+ console.warn("[AIRenderer] Node creation failed:", node.id, err);
344
+ continue;
345
+ }
346
+ if (!shape) continue;
347
+ if (node.rotation && shape.rotation !== void 0 && node.type !== "diamond") {
348
+ shape.rotation = node.rotation;
349
+ }
350
+ window.shapes.push(shape);
351
+ if (window.pushCreateAction) window.pushCreateAction(shape);
352
+ if (frame.addShapeToFrame) frame.addShapeToFrame(shape);
353
+ nodeMap.set(node.id, { shape, x: nx, y: ny, width: nw, height: nh, centerX: cx, centerY: cy });
354
+ if (node.label) {
355
+ let labelColor = node.labelColor || node.stroke || "#e0e0e0";
356
+ if (isColorTooDark(labelColor)) {
357
+ labelColor = "#e0e0e0";
358
+ }
359
+ const labelFontSize = node.labelFontSize || 14;
360
+ if (node.type === "icon") {
361
+ const labelY = cy + nh / 2 + 18;
362
+ createLabel(node.label, cx, labelY, labelFontSize, labelColor, frame);
363
+ } else if (shape && typeof shape.setLabel === "function") {
364
+ shape.setLabel(node.label, labelColor, labelFontSize);
365
+ } else {
366
+ createLabel(node.label, cx, cy, labelFontSize, labelColor, frame);
367
+ }
368
+ }
369
+ }
370
+ const fanOut = /* @__PURE__ */ new Map();
371
+ const fanIdx = /* @__PURE__ */ new Map();
372
+ edges.forEach((e) => {
373
+ fanOut.set(e.from, (fanOut.get(e.from) || 0) + 1);
374
+ fanIdx.set(e, fanOut.get(e.from) - 1);
375
+ });
376
+ const allNodeBounds = [];
377
+ nodeMap.forEach((n) => {
378
+ allNodeBounds.push({ x: n.x, y: n.y, width: n.width, height: n.height });
379
+ });
380
+ for (const edge of edges) {
381
+ const from = nodeMap.get(edge.from), to = nodeMap.get(edge.to);
382
+ if (!from || !to) continue;
383
+ const count = fanOut.get(edge.from) || 1;
384
+ const idx = fanIdx.get(edge);
385
+ const isDirected = edge.directed !== false;
386
+ const edgeStyle = chooseEdgeStyle(from, to, count, idx, allNodeBounds, nodeMap, edges);
387
+ const sp = getSpreadEdgePoint(from, to, count, idx);
388
+ const ep = getEdgePoint(to, from);
389
+ const adx = ep.x - sp.x, ady = ep.y - sp.y;
390
+ const alen = Math.sqrt(adx * adx + ady * ady) || 1;
391
+ const nudge = 6;
392
+ const spN = { x: sp.x + adx / alen * nudge, y: sp.y + ady / alen * nudge };
393
+ const epN = { x: ep.x - adx / alen * nudge, y: ep.y - ady / alen * nudge };
394
+ let connector = null;
395
+ const edgeStroke = edge.stroke || "#e0e0e0";
396
+ const edgeStrokeWidth = edge.strokeWidth ?? 1.5;
397
+ const edgeLineStyle = edge.lineStyle || "solid";
398
+ const dashMap = { solid: "", dashed: "5 3", dotted: "2 2" };
399
+ const dashValue = dashMap[edgeLineStyle] || "";
400
+ if (isDirected && window.Arrow) {
401
+ try {
402
+ const opts = {
403
+ stroke: edgeStroke,
404
+ strokeWidth: edgeStrokeWidth,
405
+ roughness: 1,
406
+ arrowOutlineStyle: edgeLineStyle,
407
+ arrowHeadStyle: edge.arrowHeadStyle || "default"
408
+ };
409
+ if (edgeStyle.type === "curved") {
410
+ opts.arrowCurved = "curved";
411
+ opts.arrowCurveAmount = edgeStyle.curveAmount;
412
+ } else if (edgeStyle.type === "elbow") {
413
+ opts.arrowCurved = "elbow";
414
+ }
415
+ connector = new window.Arrow(spN, epN, opts);
416
+ window.shapes.push(connector);
417
+ if (window.pushCreateAction) window.pushCreateAction(connector);
418
+ if (frame.addShapeToFrame) frame.addShapeToFrame(connector);
419
+ autoAttach(connector, from.shape, true, sp);
420
+ autoAttach(connector, to.shape, false, ep);
421
+ } catch (err) {
422
+ console.warn("[AIRenderer] Arrow creation failed:", edge, err);
423
+ }
424
+ } else if (!isDirected && window.Line) {
425
+ try {
426
+ const opts = {
427
+ stroke: edgeStroke,
428
+ strokeWidth: edgeStrokeWidth,
429
+ roughness: 1,
430
+ strokeDasharray: dashValue
431
+ };
432
+ connector = new window.Line(spN, epN, opts);
433
+ if (edgeStyle.type === "curved") {
434
+ connector.isCurved = true;
435
+ if (typeof connector.initializeCurveControlPoint === "function") {
436
+ connector.initializeCurveControlPoint();
437
+ }
438
+ if (typeof connector.draw === "function") connector.draw();
439
+ }
440
+ window.shapes.push(connector);
441
+ if (window.pushCreateAction) window.pushCreateAction(connector);
442
+ if (frame.addShapeToFrame) frame.addShapeToFrame(connector);
443
+ } catch (err) {
444
+ console.warn("[AIRenderer] Line creation failed:", edge, err);
445
+ }
446
+ } else if (window.Arrow) {
447
+ try {
448
+ connector = new window.Arrow(spN, epN, {
449
+ stroke: edgeStroke,
450
+ strokeWidth: edgeStrokeWidth,
451
+ roughness: 1
452
+ });
453
+ window.shapes.push(connector);
454
+ if (window.pushCreateAction) window.pushCreateAction(connector);
455
+ if (frame.addShapeToFrame) frame.addShapeToFrame(connector);
456
+ } catch (err) {
457
+ console.warn("[AIRenderer] Fallback arrow creation failed:", edge, err);
458
+ }
459
+ }
460
+ if (edge.label && connector) {
461
+ const edgeLabelColor = edgeStroke === "#e0e0e0" ? "#a0a0b0" : edgeStroke;
462
+ if (typeof connector.setLabel === "function") {
463
+ connector.setLabel(edge.label, edgeLabelColor, 11);
464
+ } else {
465
+ const mx = (spN.x + epN.x) / 2;
466
+ const my = (spN.y + epN.y) / 2 - 18;
467
+ createLabel(edge.label, mx, my, 11, edgeLabelColor, frame);
468
+ }
469
+ }
470
+ }
471
+ const subgraphs = diagram.subgraphs || [];
472
+ for (const sg of subgraphs) {
473
+ if (!sg.nodes || sg.nodes.length === 0) continue;
474
+ let sgMinX = Infinity, sgMinY = Infinity, sgMaxX = -Infinity, sgMaxY = -Infinity;
475
+ let hasNodes = false;
476
+ for (const nid of sg.nodes) {
477
+ const n = nodeMap.get(nid);
478
+ if (!n) continue;
479
+ hasNodes = true;
480
+ sgMinX = Math.min(sgMinX, n.x);
481
+ sgMinY = Math.min(sgMinY, n.y);
482
+ sgMaxX = Math.max(sgMaxX, n.x + n.width);
483
+ sgMaxY = Math.max(sgMaxY, n.y + n.height);
484
+ }
485
+ if (!hasNodes) continue;
486
+ const sgPad = 30;
487
+ try {
488
+ const subFrame = new window.Frame(
489
+ sgMinX - sgPad,
490
+ sgMinY - sgPad - 20,
491
+ sgMaxX - sgMinX + sgPad * 2,
492
+ sgMaxY - sgMinY + sgPad * 2 + 20,
493
+ {
494
+ stroke: sg.stroke || "#555",
495
+ strokeWidth: 1,
496
+ fill: "transparent",
497
+ opacity: 0.6,
498
+ frameName: sg.label || sg.id
499
+ }
500
+ );
501
+ window.shapes.push(subFrame);
502
+ if (window.pushCreateAction) window.pushCreateAction(subFrame);
503
+ if (frame.addShapeToFrame) frame.addShapeToFrame(subFrame);
504
+ for (const nid of sg.nodes) {
505
+ const n = nodeMap.get(nid);
506
+ if (n && n.shape && subFrame.addShapeToFrame) {
507
+ subFrame.addShapeToFrame(n.shape);
508
+ }
509
+ }
510
+ } catch (err) {
511
+ console.warn("[AIRenderer] Subgraph frame failed:", sg.id, err);
512
+ }
513
+ }
514
+ const first = nodeMap.values().next().value;
515
+ if (first && first.shape) {
516
+ window.currentShape = first.shape;
517
+ if (typeof first.shape.selectShape === "function") first.shape.selectShape();
518
+ }
519
+ console.log(`[AIRenderer] Done: ${nodes.length} nodes, ${edges.length} edges, ${subgraphs.length} subgraphs \u2192 "${title}" (groupId=${groupId})`);
520
+ return true;
521
+ }
522
+ function chooseEdgeStyle(from, to, fanCount, fanIdx, allNodeBounds, nodeMap, edges) {
523
+ const dx = Math.abs(from.centerX - to.centerX);
524
+ const dy = Math.abs(from.centerY - to.centerY);
525
+ if (fanCount > 1) {
526
+ const curveAmount = 40 + (fanIdx - (fanCount - 1) / 2) * 35;
527
+ return { type: "curved", curveAmount };
528
+ }
529
+ const isHAligned = dx < ALIGN_THRESHOLD;
530
+ const isVAligned = dy < ALIGN_THRESHOLD;
531
+ if (isHAligned || isVAligned) {
532
+ const sp = getEdgePoint(from, to);
533
+ const ep = getEdgePoint(to, from);
534
+ const blocked = wouldCrossNode(sp, ep, from, to, allNodeBounds);
535
+ if (blocked) {
536
+ return { type: "curved", curveAmount: 40 };
537
+ }
538
+ return { type: "straight" };
539
+ }
540
+ const isGridLike = dx > ALIGN_THRESHOLD * 2 && dy > ALIGN_THRESHOLD * 2;
541
+ if (isGridLike) {
542
+ const elbowMidX = to.centerX;
543
+ const elbowMidY = from.centerY;
544
+ const elbowMid = { x: elbowMidX, y: elbowMidY };
545
+ const seg1Blocked = wouldCrossNode(
546
+ { x: from.centerX, y: from.centerY },
547
+ elbowMid,
548
+ from,
549
+ to,
550
+ allNodeBounds
551
+ );
552
+ const seg2Blocked = wouldCrossNode(
553
+ elbowMid,
554
+ { x: to.centerX, y: to.centerY },
555
+ from,
556
+ to,
557
+ allNodeBounds
558
+ );
559
+ if (!seg1Blocked && !seg2Blocked) {
560
+ return { type: "elbow" };
561
+ }
562
+ }
563
+ return { type: "curved", curveAmount: 25 };
564
+ }
565
+ function wouldCrossNode(p1, p2, fromNode, toNode, allNodeBounds) {
566
+ const margin = 10;
567
+ for (const bounds of allNodeBounds) {
568
+ if (bounds.x === fromNode.x && bounds.y === fromNode.y) continue;
569
+ if (bounds.x === toNode.x && bounds.y === toNode.y) continue;
570
+ const bx = bounds.x - margin;
571
+ const by = bounds.y - margin;
572
+ const bw = bounds.width + margin * 2;
573
+ const bh = bounds.height + margin * 2;
574
+ if (lineIntersectsRect(p1.x, p1.y, p2.x, p2.y, bx, by, bw, bh)) {
575
+ return true;
576
+ }
577
+ }
578
+ return false;
579
+ }
580
+ function lineIntersectsRect(x1, y1, x2, y2, rx, ry, rw, rh) {
581
+ const left = rx, right = rx + rw, top = ry, bottom = ry + rh;
582
+ if (Math.max(x1, x2) < left || Math.min(x1, x2) > right) return false;
583
+ if (Math.max(y1, y2) < top || Math.min(y1, y2) > bottom) return false;
584
+ if (x1 >= left && x1 <= right && y1 >= top && y1 <= bottom) return true;
585
+ if (x2 >= left && x2 <= right && y2 >= top && y2 <= bottom) return true;
586
+ return segmentsIntersect(x1, y1, x2, y2, left, top, right, top) || segmentsIntersect(x1, y1, x2, y2, right, top, right, bottom) || segmentsIntersect(x1, y1, x2, y2, left, bottom, right, bottom) || segmentsIntersect(x1, y1, x2, y2, left, top, left, bottom);
587
+ }
588
+ function segmentsIntersect(ax1, ay1, ax2, ay2, bx1, by1, bx2, by2) {
589
+ const d1 = cross(bx1, by1, bx2, by2, ax1, ay1);
590
+ const d2 = cross(bx1, by1, bx2, by2, ax2, ay2);
591
+ const d3 = cross(ax1, ay1, ax2, ay2, bx1, by1);
592
+ const d4 = cross(ax1, ay1, ax2, ay2, bx2, by2);
593
+ if ((d1 > 0 && d2 < 0 || d1 < 0 && d2 > 0) && (d3 > 0 && d4 < 0 || d3 < 0 && d4 > 0)) {
594
+ return true;
595
+ }
596
+ return false;
597
+ }
598
+ function cross(ax, ay, bx, by, cx, cy) {
599
+ return (bx - ax) * (cy - ay) - (by - ay) * (cx - ax);
600
+ }
601
+ function autoAttach(arrow, shape, isStart, contactPoint) {
602
+ if (!arrow || !shape) return;
603
+ if (typeof arrow.attachToShape !== "function") return;
604
+ try {
605
+ let side, offset;
606
+ if (shape.shapeName === "rectangle" || shape.shapeName === "frame") {
607
+ const sx = shape.x, sy = shape.y, sw = shape.width, sh = shape.height;
608
+ const cx = sx + sw / 2, cy = sy + sh / 2;
609
+ const distTop = Math.abs(contactPoint.y - sy);
610
+ const distBottom = Math.abs(contactPoint.y - (sy + sh));
611
+ const distLeft = Math.abs(contactPoint.x - sx);
612
+ const distRight = Math.abs(contactPoint.x - (sx + sw));
613
+ const minDist = Math.min(distTop, distBottom, distLeft, distRight);
614
+ if (minDist === distTop) {
615
+ side = "top";
616
+ offset = { x: contactPoint.x - sx, y: 0, side: "top" };
617
+ } else if (minDist === distBottom) {
618
+ side = "bottom";
619
+ offset = { x: contactPoint.x - sx, y: sh, side: "bottom" };
620
+ } else if (minDist === distLeft) {
621
+ side = "left";
622
+ offset = { x: 0, y: contactPoint.y - sy, side: "left" };
623
+ } else {
624
+ side = "right";
625
+ offset = { x: sw, y: contactPoint.y - sy, side: "right" };
626
+ }
627
+ const attachment = { side, point: contactPoint, offset };
628
+ arrow.attachToShape(isStart, shape, attachment);
629
+ } else if (shape.shapeName === "circle") {
630
+ const angle = Math.atan2(
631
+ contactPoint.y - shape.y,
632
+ contactPoint.x - shape.x
633
+ );
634
+ const attachment = {
635
+ side: "perimeter",
636
+ point: contactPoint,
637
+ offset: { angle, side: "perimeter" }
638
+ };
639
+ arrow.attachToShape(isStart, shape, attachment);
640
+ }
641
+ } catch (err) {
642
+ console.warn("[AIRenderer] Auto-attach failed:", err);
643
+ }
644
+ }
645
+ async function fetchAndPlaceIcon(keyword, x, y, w, h, placeholderShape, frame) {
646
+ try {
647
+ const res = await fetch(`/api/icons/search?q=${encodeURIComponent(keyword)}&inline=1`);
648
+ if (!res.ok) return;
649
+ const data = await res.json();
650
+ const results = data.results;
651
+ if (!results || results.length === 0 || !results[0].svg) return;
652
+ const svgContent = results[0].svg;
653
+ if (window.svg && window.IconShape) {
654
+ const svg = window.svg;
655
+ const parser = new DOMParser();
656
+ const doc = parser.parseFromString(svgContent, "image/svg+xml");
657
+ const svgEl = doc.querySelector("svg");
658
+ if (!svgEl) return;
659
+ const vbWidth = parseFloat(svgEl.getAttribute("width") || svgEl.viewBox?.baseVal?.width || 24);
660
+ const vbHeight = parseFloat(svgEl.getAttribute("height") || svgEl.viewBox?.baseVal?.height || 24);
661
+ const scale = Math.min(w / vbWidth, h / vbHeight);
662
+ const g = document.createElementNS(NS, "g");
663
+ g.setAttribute("type", "icon");
664
+ g.setAttribute("x", x);
665
+ g.setAttribute("y", y);
666
+ g.setAttribute("width", w);
667
+ g.setAttribute("height", h);
668
+ g.setAttribute("data-shape-x", x);
669
+ g.setAttribute("data-shape-y", y);
670
+ g.setAttribute("data-shape-width", w);
671
+ g.setAttribute("data-shape-height", h);
672
+ g.setAttribute("data-shape-rotation", "0");
673
+ g.setAttribute("data-viewbox-width", vbWidth);
674
+ g.setAttribute("data-viewbox-height", vbHeight);
675
+ g.setAttribute("transform", `translate(${x}, ${y}) scale(${scale})`);
676
+ const children = svgEl.querySelectorAll("path, circle, rect, polygon, polyline, line, ellipse");
677
+ children.forEach((child) => {
678
+ const clone = child.cloneNode(true);
679
+ if (!clone.getAttribute("fill") || clone.getAttribute("fill") === "none" || clone.getAttribute("fill") === "black" || clone.getAttribute("fill") === "#000" || clone.getAttribute("fill") === "#000000") {
680
+ clone.setAttribute("fill", "#ffffff");
681
+ }
682
+ if (clone.getAttribute("stroke") === "black" || clone.getAttribute("stroke") === "#000" || clone.getAttribute("stroke") === "#000000") {
683
+ clone.setAttribute("stroke", "#ffffff");
684
+ }
685
+ g.appendChild(clone);
686
+ });
687
+ const hitRect = document.createElementNS(NS, "rect");
688
+ hitRect.setAttribute("x", "0");
689
+ hitRect.setAttribute("y", "0");
690
+ hitRect.setAttribute("width", vbWidth);
691
+ hitRect.setAttribute("height", vbHeight);
692
+ hitRect.setAttribute("fill", "transparent");
693
+ hitRect.setAttribute("stroke", "none");
694
+ g.insertBefore(hitRect, g.firstChild);
695
+ svg.appendChild(g);
696
+ const iconShape = new window.IconShape(g);
697
+ window.shapes.push(iconShape);
698
+ if (window.pushCreateAction) window.pushCreateAction(iconShape);
699
+ if (frame?.addShapeToFrame) frame.addShapeToFrame(iconShape);
700
+ if (placeholderShape) {
701
+ const idx = window.shapes.indexOf(placeholderShape);
702
+ if (idx !== -1) window.shapes.splice(idx, 1);
703
+ if (placeholderShape.group && placeholderShape.group.parentNode) {
704
+ placeholderShape.group.parentNode.removeChild(placeholderShape.group);
705
+ }
706
+ }
707
+ }
708
+ } catch (err) {
709
+ console.warn("[AIRenderer] Icon fetch failed for keyword:", keyword, err);
710
+ }
711
+ }
712
+ function createLabel(text, x, y, fontSize, fill, frame) {
713
+ const svg = window.svg;
714
+ if (!svg || !window.TextShape) return null;
715
+ try {
716
+ const g = document.createElementNS(NS, "g");
717
+ g.setAttribute("data-type", "text-group");
718
+ g.setAttribute("transform", `translate(${x}, ${y})`);
719
+ g.setAttribute("data-x", x);
720
+ g.setAttribute("data-y", y);
721
+ const t = document.createElementNS(NS, "text");
722
+ t.setAttribute("x", 0);
723
+ t.setAttribute("y", 0);
724
+ t.setAttribute("text-anchor", "middle");
725
+ t.setAttribute("dominant-baseline", "central");
726
+ t.setAttribute("fill", fill);
727
+ t.setAttribute("font-size", fontSize);
728
+ t.setAttribute("font-family", "lixFont, sans-serif");
729
+ t.setAttribute("data-initial-font", "lixFont");
730
+ t.setAttribute("data-initial-color", fill);
731
+ t.setAttribute("data-initial-size", fontSize + "px");
732
+ t.textContent = text;
733
+ g.appendChild(t);
734
+ svg.appendChild(g);
735
+ const shape = new window.TextShape(g);
736
+ window.shapes.push(shape);
737
+ if (window.pushCreateAction) window.pushCreateAction(shape);
738
+ if (frame?.addShapeToFrame) frame.addShapeToFrame(shape);
739
+ return shape;
740
+ } catch (err) {
741
+ console.warn("[AIRenderer] Label creation failed:", err);
742
+ return null;
743
+ }
744
+ }
745
+ function getSpreadEdgePoint(node, targetNode, count, idx) {
746
+ if (count <= 1) return getEdgePoint(node, targetNode);
747
+ const dx = targetNode.centerX - node.centerX;
748
+ const dy = targetNode.centerY - node.centerY;
749
+ const hw = node.width / 2;
750
+ const hh = node.height / 2;
751
+ const spread = 0.6;
752
+ const t = count === 1 ? 0.5 : idx / (count - 1);
753
+ const offset = (t - 0.5) * spread;
754
+ if (Math.abs(dx) < 1e-3 || Math.abs(dy) * hw > Math.abs(dx) * hh) {
755
+ const px2 = node.centerX + offset * node.width;
756
+ const py2 = dy > 0 ? node.y + node.height : node.y;
757
+ return { x: px2, y: py2 };
758
+ }
759
+ const px = dx > 0 ? node.x + node.width : node.x;
760
+ const py = node.centerY + offset * node.height;
761
+ return { x: px, y: py };
762
+ }
763
+ function getEdgePoint(node, targetNode) {
764
+ const dx = targetNode.centerX - node.centerX;
765
+ const dy = targetNode.centerY - node.centerY;
766
+ const hw = node.width / 2;
767
+ const hh = node.height / 2;
768
+ if (Math.abs(dx) < 1e-3 || Math.abs(dy) * hw > Math.abs(dx) * hh) {
769
+ if (dy > 0) return { x: node.centerX, y: node.y + node.height };
770
+ return { x: node.centerX, y: node.y };
771
+ }
772
+ if (dx > 0) return { x: node.x + node.width, y: node.centerY };
773
+ return { x: node.x, y: node.centerY };
774
+ }
775
+ function generatePreviewSVG(diagram, width = 500, height = 350) {
776
+ if (!diagram?.nodes?.length) return "";
777
+ const nodes = diagram.nodes;
778
+ const edges = diagram.edges || [];
779
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
780
+ nodes.forEach((n) => {
781
+ minX = Math.min(minX, n.x);
782
+ minY = Math.min(minY, n.y);
783
+ maxX = Math.max(maxX, n.x + (n.width || NODE_W));
784
+ maxY = Math.max(maxY, n.y + (n.height || NODE_H));
785
+ });
786
+ const dw = maxX - minX || 1;
787
+ const dh = maxY - minY || 1;
788
+ const pad = 40;
789
+ const scale = Math.min((width - pad * 2) / dw, (height - pad * 2) / dh, 1.5);
790
+ const offX = (width - dw * scale) / 2 - minX * scale;
791
+ const offY = (height - dh * scale) / 2 - minY * scale;
792
+ const nodeById = /* @__PURE__ */ new Map();
793
+ nodes.forEach((n) => {
794
+ const nx = n.x * scale + offX;
795
+ const ny = n.y * scale + offY;
796
+ const nw = (n.width || NODE_W) * scale;
797
+ const nh = (n.height || NODE_H) * scale;
798
+ nodeById.set(n.id, { x: nx, y: ny, w: nw, h: nh, cx: nx + nw / 2, cy: ny + nh / 2 });
799
+ });
800
+ let svgContent = "";
801
+ edges.forEach((e) => {
802
+ const f = nodeById.get(e.from);
803
+ const t = nodeById.get(e.to);
804
+ if (!f || !t) return;
805
+ const directed = e.directed !== false;
806
+ const eColor = e.stroke || "#666";
807
+ const markerId = directed ? `url(#preview-arrow-${eColor.replace("#", "")})` : "";
808
+ if (directed && !svgContent.includes(`id="preview-arrow-${eColor.replace("#", "")}"`)) {
809
+ svgContent = `<marker id="preview-arrow-${eColor.replace("#", "")}" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6" fill="none" stroke="${eColor}" stroke-width="1" /></marker>` + svgContent;
810
+ }
811
+ const eDash = e.lineStyle === "dashed" ? ' stroke-dasharray="5 3"' : e.lineStyle === "dotted" ? ' stroke-dasharray="2 2"' : "";
812
+ const dx = t.cx - f.cx;
813
+ const dy = t.cy - f.cy;
814
+ const dist = Math.sqrt(dx * dx + dy * dy);
815
+ let mx = (f.cx + t.cx) / 2;
816
+ let my = (f.cy + t.cy) / 2;
817
+ if (dist > 0 && Math.abs(dy) > 10 && Math.abs(dx) > 10) {
818
+ const perpX = -dy / dist;
819
+ const perpY = dx / dist;
820
+ const curveAmt = dist * 0.15;
821
+ const cpx = mx + perpX * curveAmt;
822
+ const cpy = my + perpY * curveAmt;
823
+ mx = 0.25 * f.cx + 0.5 * cpx + 0.25 * t.cx;
824
+ my = 0.25 * f.cy + 0.5 * cpy + 0.25 * t.cy;
825
+ svgContent += `<path d="M ${f.cx} ${f.cy} Q ${cpx} ${cpy} ${t.cx} ${t.cy}" fill="none" stroke="${eColor}" stroke-width="1.5"${eDash} marker-end="${markerId}" />`;
826
+ } else {
827
+ svgContent += `<line x1="${f.cx}" y1="${f.cy}" x2="${t.cx}" y2="${t.cy}" stroke="${eColor}" stroke-width="1.5"${eDash} marker-end="${markerId}" />`;
828
+ }
829
+ if (e.label) {
830
+ svgContent += `<text x="${mx}" y="${my - 8}" text-anchor="middle" fill="${eColor === "#666" ? "#888" : eColor}" font-size="9" font-family="lixFont, sans-serif">${escapeXml(e.label)}</text>`;
831
+ }
832
+ });
833
+ nodes.forEach((n) => {
834
+ const d = nodeById.get(n.id);
835
+ if (!d) return;
836
+ const nStroke = n.stroke || "#9090c0";
837
+ const nFill = n.fill || "transparent";
838
+ const nDash = n.strokeDasharray ? ` stroke-dasharray="${n.strokeDasharray}"` : "";
839
+ if (n.type === "circle") {
840
+ svgContent += `<ellipse cx="${d.cx}" cy="${d.cy}" rx="${d.w / 2}" ry="${d.h / 2}" fill="${nFill}" stroke="${nStroke}" stroke-width="1.5"${nDash} />`;
841
+ } else if (n.type === "diamond") {
842
+ const sz = Math.min(d.w, d.h) * 0.7;
843
+ svgContent += `<rect x="${d.cx - sz / 2}" y="${d.cy - sz / 2}" width="${sz}" height="${sz}" fill="${nFill}" stroke="${nStroke}" stroke-width="1.5"${nDash} transform="rotate(45, ${d.cx}, ${d.cy})" />`;
844
+ } else if (n.type === "roundrect") {
845
+ svgContent += `<rect x="${d.x}" y="${d.y}" width="${d.w}" height="${d.h}" rx="${Math.min(d.w, d.h) * 0.2}" fill="${nFill}" stroke="${nStroke}" stroke-width="1.5"${nDash} />`;
846
+ } else {
847
+ svgContent += `<rect x="${d.x}" y="${d.y}" width="${d.w}" height="${d.h}" rx="4" fill="${nFill}" stroke="${nStroke}" stroke-width="1.5"${nDash} />`;
848
+ }
849
+ if (n.label) {
850
+ const fontSize = Math.max(8, Math.min(12, 11 * scale));
851
+ let labelFill = nStroke === "#9090c0" ? "#d0d0d0" : nStroke;
852
+ if (isColorTooDark(labelFill)) labelFill = "#d0d0d0";
853
+ const labelY = n.type === "icon" ? d.cy + d.h / 2 + 12 * scale : d.cy;
854
+ svgContent += `<text x="${d.cx}" y="${labelY}" text-anchor="middle" dominant-baseline="central" fill="${labelFill}" font-size="${fontSize}" font-family="lixFont, sans-serif">${escapeXml(n.label)}</text>`;
855
+ }
856
+ });
857
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
858
+ <defs>${svgContent.match(/<marker[^]*?<\/marker>/g)?.join("") || ""}</defs>
859
+ ${svgContent.replace(/<marker[^]*?<\/marker>/g, "")}
860
+ </svg>`;
861
+ }
862
+ function escapeXml(str) {
863
+ return String(str).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
864
+ }
865
+ function isColorTooDark(hex) {
866
+ if (!hex || hex === "transparent" || hex === "none") return false;
867
+ const c = hex.replace("#", "");
868
+ if (c.length < 6) return false;
869
+ const r = parseInt(c.substring(0, 2), 16);
870
+ const g = parseInt(c.substring(2, 4), 16);
871
+ const b = parseInt(c.substring(4, 6), 16);
872
+ const lum = 0.299 * r + 0.587 * g + 0.114 * b;
873
+ return lum < 80;
874
+ }
875
+ function generateFramePreviewSVG(frame, width = 500, height = 350) {
876
+ if (!frame || !frame.clipGroup) return "";
877
+ const fx = frame.x, fy = frame.y, fw = frame.width, fh = frame.height;
878
+ if (fw <= 0 || fh <= 0) return "";
879
+ const pad = 30;
880
+ const scale = Math.min((width - pad * 2) / fw, (height - pad * 2) / fh, 1.5);
881
+ const offX = (width - fw * scale) / 2 - fx * scale;
882
+ const offY = (height - fh * scale) / 2 - fy * scale;
883
+ let svgContent = "";
884
+ svgContent += `<rect x="${fx * scale + offX}" y="${fy * scale + offY}" width="${fw * scale}" height="${fh * scale}" rx="6" fill="transparent" stroke="#555" stroke-width="1.5" stroke-dasharray="6 3" />`;
885
+ if (frame.frameName) {
886
+ svgContent += `<text x="${fx * scale + offX + 10}" y="${fy * scale + offY + 16}" fill="#888" font-size="11" font-family="lixFont, sans-serif">${escapeXml(frame.frameName)}</text>`;
887
+ }
888
+ if (frame.containedShapes) {
889
+ frame.containedShapes.forEach((shape) => {
890
+ if (!shape) return;
891
+ if (shape.shapeName === "rectangle") {
892
+ const sx = shape.x * scale + offX;
893
+ const sy = shape.y * scale + offY;
894
+ const sw = shape.width * scale;
895
+ const sh = shape.height * scale;
896
+ const stroke = shape.options?.stroke || "#e0e0e0";
897
+ const fill = shape.options?.fill || "transparent";
898
+ if (shape.rotation === 45) {
899
+ const sz = Math.min(sw, sh) * 0.7;
900
+ const rcx = sx + sw / 2, rcy = sy + sh / 2;
901
+ svgContent += `<rect x="${rcx - sz / 2}" y="${rcy - sz / 2}" width="${sz}" height="${sz}" fill="${fill}" stroke="${stroke}" stroke-width="1.5" transform="rotate(45, ${rcx}, ${rcy})" />`;
902
+ if (shape.label) {
903
+ let lf = shape.labelColor || stroke;
904
+ if (isColorTooDark(lf)) lf = "#d0d0d0";
905
+ const fs = Math.max(8, (shape.labelFontSize || 14) * scale);
906
+ svgContent += `<text x="${rcx}" y="${rcy}" text-anchor="middle" dominant-baseline="central" fill="${lf}" font-size="${fs}" font-family="lixFont, sans-serif">${escapeXml(shape.label)}</text>`;
907
+ }
908
+ } else {
909
+ svgContent += `<rect x="${sx}" y="${sy}" width="${sw}" height="${sh}" rx="4" fill="${fill}" stroke="${stroke}" stroke-width="1.5" />`;
910
+ if (shape.label) {
911
+ let lf = shape.labelColor || stroke;
912
+ if (isColorTooDark(lf)) lf = "#d0d0d0";
913
+ const fs = Math.max(8, (shape.labelFontSize || 14) * scale);
914
+ svgContent += `<text x="${sx + sw / 2}" y="${sy + sh / 2}" text-anchor="middle" dominant-baseline="central" fill="${lf}" font-size="${fs}" font-family="lixFont, sans-serif">${escapeXml(shape.label)}</text>`;
915
+ }
916
+ }
917
+ } else if (shape.shapeName === "circle") {
918
+ const ccx = shape.x * scale + offX;
919
+ const ccy = shape.y * scale + offY;
920
+ const crx = (shape.rx || 30) * scale;
921
+ const cry = (shape.ry || 30) * scale;
922
+ const stroke = shape.options?.stroke || "#e0e0e0";
923
+ const fill = shape.options?.fill || "transparent";
924
+ svgContent += `<ellipse cx="${ccx}" cy="${ccy}" rx="${crx}" ry="${cry}" fill="${fill}" stroke="${stroke}" stroke-width="1.5" />`;
925
+ if (shape.label) {
926
+ let lf = shape.labelColor || stroke;
927
+ if (isColorTooDark(lf)) lf = "#d0d0d0";
928
+ const fs = Math.max(8, (shape.labelFontSize || 14) * scale);
929
+ svgContent += `<text x="${ccx}" y="${ccy}" text-anchor="middle" dominant-baseline="central" fill="${lf}" font-size="${fs}" font-family="lixFont, sans-serif">${escapeXml(shape.label)}</text>`;
930
+ }
931
+ } else if (shape.shapeName === "text") {
932
+ const textEl = shape.group?.querySelector("text");
933
+ if (textEl) {
934
+ const tx = parseFloat(shape.group.getAttribute("data-x") || "0");
935
+ const ty = parseFloat(shape.group.getAttribute("data-y") || "0");
936
+ const fill = textEl.getAttribute("fill") || "#e0e0e0";
937
+ const fontSize = Math.max(8, (parseFloat(textEl.getAttribute("font-size")) || 14) * scale);
938
+ const text = textEl.textContent || "";
939
+ let labelFill = fill;
940
+ if (isColorTooDark(labelFill)) labelFill = "#d0d0d0";
941
+ svgContent += `<text x="${tx * scale + offX}" y="${ty * scale + offY}" text-anchor="middle" dominant-baseline="central" fill="${labelFill}" font-size="${fontSize}" font-family="lixFont, sans-serif">${escapeXml(text)}</text>`;
942
+ }
943
+ } else if (shape.shapeName === "arrow") {
944
+ const sp = shape.startPoint, ep = shape.endPoint;
945
+ if (sp && ep) {
946
+ const stroke = shape.options?.stroke || "#e0e0e0";
947
+ const sx1 = sp.x * scale + offX, sy1 = sp.y * scale + offY;
948
+ const sx2 = ep.x * scale + offX, sy2 = ep.y * scale + offY;
949
+ if (shape.arrowCurved === "curved" && shape.controlPoint1 && shape.controlPoint2) {
950
+ const cp1x = shape.controlPoint1.x * scale + offX;
951
+ const cp1y = shape.controlPoint1.y * scale + offY;
952
+ const cp2x = shape.controlPoint2.x * scale + offX;
953
+ const cp2y = shape.controlPoint2.y * scale + offY;
954
+ svgContent += `<path d="M ${sx1} ${sy1} C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${sx2} ${sy2}" fill="none" stroke="${stroke}" stroke-width="1.5" marker-end="url(#frame-preview-arrow)" />`;
955
+ } else {
956
+ svgContent += `<line x1="${sx1}" y1="${sy1}" x2="${sx2}" y2="${sy2}" stroke="${stroke}" stroke-width="1.5" marker-end="url(#frame-preview-arrow)" />`;
957
+ }
958
+ if (shape.label) {
959
+ const amx = (sx1 + sx2) / 2, amy = (sy1 + sy2) / 2 - 8;
960
+ const afs = Math.max(7, (shape.labelFontSize || 11) * scale);
961
+ svgContent += `<text x="${amx}" y="${amy}" text-anchor="middle" fill="${shape.labelColor || stroke}" font-size="${afs}" font-family="lixFont, sans-serif">${escapeXml(shape.label)}</text>`;
962
+ }
963
+ }
964
+ } else if (shape.shapeName === "line") {
965
+ const sp = shape.startPoint, ep = shape.endPoint;
966
+ if (sp && ep) {
967
+ const stroke = shape.options?.stroke || "#e0e0e0";
968
+ const sx1 = sp.x * scale + offX, sy1 = sp.y * scale + offY;
969
+ const sx2 = ep.x * scale + offX, sy2 = ep.y * scale + offY;
970
+ if (shape.isCurved && shape.controlPoint) {
971
+ const cpx = shape.controlPoint.x * scale + offX;
972
+ const cpy = shape.controlPoint.y * scale + offY;
973
+ svgContent += `<path d="M ${sx1} ${sy1} Q ${cpx} ${cpy} ${sx2} ${sy2}" fill="none" stroke="${stroke}" stroke-width="1.5" />`;
974
+ } else {
975
+ svgContent += `<line x1="${sx1}" y1="${sy1}" x2="${sx2}" y2="${sy2}" stroke="${stroke}" stroke-width="1.5" />`;
976
+ }
977
+ if (shape.label) {
978
+ const lmx = (sx1 + sx2) / 2, lmy = (sy1 + sy2) / 2 - 8;
979
+ const lfs = Math.max(7, (shape.labelFontSize || 11) * scale);
980
+ svgContent += `<text x="${lmx}" y="${lmy}" text-anchor="middle" fill="${shape.labelColor || stroke}" font-size="${lfs}" font-family="lixFont, sans-serif">${escapeXml(shape.label)}</text>`;
981
+ }
982
+ }
983
+ } else if (shape.shapeName === "icon") {
984
+ const ix = parseFloat(shape.group?.getAttribute("data-shape-x") || "0");
985
+ const iy = parseFloat(shape.group?.getAttribute("data-shape-y") || "0");
986
+ const iw = parseFloat(shape.group?.getAttribute("data-shape-width") || "40");
987
+ const ih = parseFloat(shape.group?.getAttribute("data-shape-height") || "40");
988
+ const pix = ix * scale + offX;
989
+ const piy = iy * scale + offY;
990
+ const piw = iw * scale;
991
+ const pih = ih * scale;
992
+ const iconPaths = shape.group?.querySelectorAll("path, circle, rect, polygon, polyline, line, ellipse");
993
+ if (iconPaths && iconPaths.length > 0) {
994
+ const vbW = parseFloat(shape.group?.getAttribute("data-viewbox-width") || iw);
995
+ const vbH = parseFloat(shape.group?.getAttribute("data-viewbox-height") || ih);
996
+ const iconScale = Math.min(piw / vbW, pih / vbH);
997
+ svgContent += `<g transform="translate(${pix}, ${piy}) scale(${iconScale})">`;
998
+ iconPaths.forEach((p) => {
999
+ if (p.tagName === "rect" && p.getAttribute("fill") === "transparent" && p.getAttribute("stroke") === "none") return;
1000
+ const clone = p.cloneNode(true);
1001
+ svgContent += clone.outerHTML;
1002
+ });
1003
+ svgContent += `</g>`;
1004
+ } else {
1005
+ svgContent += `<rect x="${pix}" y="${piy}" width="${piw}" height="${pih}" rx="4" fill="transparent" stroke="#9090c0" stroke-width="1" stroke-dasharray="3 2" />`;
1006
+ svgContent += `<text x="${pix + piw / 2}" y="${piy + pih / 2}" text-anchor="middle" dominant-baseline="central" fill="#9090c0" font-size="9" font-family="lixFont">icon</text>`;
1007
+ }
1008
+ } else if (shape.shapeName === "frame") {
1009
+ const sx = shape.x * scale + offX;
1010
+ const sy = shape.y * scale + offY;
1011
+ const sw = shape.width * scale;
1012
+ const sh = shape.height * scale;
1013
+ svgContent += `<rect x="${sx}" y="${sy}" width="${sw}" height="${sh}" rx="4" fill="transparent" stroke="${shape.options?.stroke || "#555"}" stroke-width="1" stroke-dasharray="4 2" opacity="0.6" />`;
1014
+ if (shape.frameName) {
1015
+ svgContent += `<text x="${sx + 6}" y="${sy + 12}" fill="#888" font-size="9" font-family="lixFont">${escapeXml(shape.frameName)}</text>`;
1016
+ }
1017
+ }
1018
+ });
1019
+ }
1020
+ return `<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">
1021
+ <defs><marker id="frame-preview-arrow" markerWidth="8" markerHeight="6" refX="8" refY="3" orient="auto"><path d="M0,0 L8,3 L0,6" fill="none" stroke="#e0e0e0" stroke-width="1" /></marker></defs>
1022
+ ${svgContent}
1023
+ </svg>`;
1024
+ }
1025
+ function initAIRenderer() {
1026
+ window.__autoAttach = autoAttach;
1027
+ let _seqParser = null;
1028
+ let _seqPreview = null;
1029
+ let _seqCanvas = null;
1030
+ let _fcPreview = null;
1031
+ let _fcCanvas = null;
1032
+ async function loadSequenceRenderer() {
1033
+ if (_seqParser) return;
1034
+ const mod = await import("./MermaidSequenceParser-UKG2NSZY.js");
1035
+ const rend = await import("./MermaidSequenceRenderer-HPLHY5II.js");
1036
+ _seqParser = mod.parseSequenceDiagram;
1037
+ _seqPreview = rend.renderSequencePreviewSVG;
1038
+ _seqCanvas = rend.renderSequenceOnCanvas;
1039
+ }
1040
+ async function loadFlowchartRenderer() {
1041
+ if (_fcPreview) return;
1042
+ const mod = await import("./MermaidFlowchartRenderer-4QBAZ5MB.js");
1043
+ _fcPreview = mod.renderFlowchartPreviewSVG;
1044
+ _fcCanvas = mod.renderFlowchartOnCanvas;
1045
+ }
1046
+ function isSequenceDiagram(src) {
1047
+ return src.trim().split("\n")[0].trim().toLowerCase() === "sequencediagram";
1048
+ }
1049
+ window.__aiRenderer = renderAIDiagram;
1050
+ window.__aiPreview = generatePreviewSVG;
1051
+ window.__aiFramePreview = generateFramePreviewSVG;
1052
+ window.__mermaidParser = (src) => {
1053
+ if (isSequenceDiagram(src)) {
1054
+ try {
1055
+ if (_seqParser) return _seqParser(src);
1056
+ loadSequenceRenderer();
1057
+ return { _pendingSequence: true, src };
1058
+ } catch {
1059
+ return null;
1060
+ }
1061
+ }
1062
+ return parseMermaid(src);
1063
+ };
1064
+ window.__mermaidPreview = async (src) => {
1065
+ if (isSequenceDiagram(src)) {
1066
+ await loadSequenceRenderer();
1067
+ const diagram2 = _seqParser(src);
1068
+ if (!diagram2) return "";
1069
+ return _seqPreview(diagram2);
1070
+ }
1071
+ await loadFlowchartRenderer();
1072
+ const diagram = parseMermaid(src);
1073
+ if (!diagram) return "";
1074
+ return _fcPreview(diagram);
1075
+ };
1076
+ window.__mermaidRenderer = async (src) => {
1077
+ if (isSequenceDiagram(src)) {
1078
+ await loadSequenceRenderer();
1079
+ const diagram2 = _seqParser(src);
1080
+ if (!diagram2) {
1081
+ console.error("[AIRenderer] Sequence parse failed");
1082
+ return false;
1083
+ }
1084
+ return _seqCanvas(diagram2);
1085
+ }
1086
+ await loadFlowchartRenderer();
1087
+ const diagram = parseMermaid(src);
1088
+ if (!diagram) {
1089
+ console.error("[AIRenderer] Mermaid parse failed");
1090
+ return false;
1091
+ }
1092
+ return _fcCanvas(diagram);
1093
+ };
1094
+ loadSequenceRenderer();
1095
+ loadFlowchartRenderer();
1096
+ }
1097
+ export {
1098
+ autoAttach,
1099
+ generateFramePreviewSVG,
1100
+ generatePreviewSVG,
1101
+ initAIRenderer,
1102
+ parseMermaid,
1103
+ renderAIDiagram
1104
+ };
1105
+ //# sourceMappingURL=AIRenderer-M4LMJ66J.js.map