@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,21 @@
1
+ "use client";
2
+ import {
3
+ deselectElement,
4
+ enterEditMode,
5
+ handleTextMouseDown,
6
+ handleTextMouseMove,
7
+ handleTextMouseUp,
8
+ updateCodeToggleForShape
9
+ } from "./chunk-JJCJNLNN.js";
10
+ import "./chunk-LHRTXNVE.js";
11
+ import "./chunk-BZE4CEBG.js";
12
+ import "./chunk-L5OARZJ7.js";
13
+ export {
14
+ deselectElement as deselectTextElement,
15
+ enterEditMode,
16
+ handleTextMouseDown,
17
+ handleTextMouseMove,
18
+ handleTextMouseUp,
19
+ updateCodeToggleForShape
20
+ };
21
+ //# sourceMappingURL=textTool-M7S6F3VC.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
package/package.json CHANGED
@@ -1,17 +1,26 @@
1
1
  {
2
2
  "name": "@elixpo/lixsketch",
3
- "version": "5.4.7",
4
- "description": "Open-source SVG whiteboard engine with hand-drawn aesthetics — the core of LixSketch",
3
+ "version": "5.5.1",
4
+ "description": "Open-source SVG whiteboard engine + React canvas component with hand-drawn aesthetics — the core of LixSketch",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
7
7
  "exports": {
8
8
  ".": "./src/index.js",
9
+ "./react": {
10
+ "import": "./dist/react/index.js",
11
+ "default": "./dist/react/index.js"
12
+ },
13
+ "./react/styles": "./dist/react/styles.css",
9
14
  "./src/*": "./src/*",
10
15
  "./fonts": "./fonts/fonts.css",
11
16
  "./fonts/*": "./fonts/*"
12
17
  },
18
+ "sideEffects": [
19
+ "**/*.css"
20
+ ],
13
21
  "files": [
14
22
  "src/",
23
+ "dist/",
15
24
  "fonts/",
16
25
  "README.md"
17
26
  ],
@@ -25,7 +34,8 @@
25
34
  "sketch",
26
35
  "excalidraw-alternative",
27
36
  "infinite-canvas",
28
- "framework"
37
+ "framework",
38
+ "react"
29
39
  ],
30
40
  "author": {
31
41
  "name": "Ayushman Bhattacharya",
@@ -43,7 +53,31 @@
43
53
  "url": "https://github.com/elixpo/sketch.elixpo/issues"
44
54
  },
45
55
  "dependencies": {
46
- "roughjs": "^4.6.6",
47
- "perfect-freehand": "^1.2.3"
56
+ "boxicons": "^2.1.4",
57
+ "perfect-freehand": "^1.2.3",
58
+ "roughjs": "^4.6.6"
59
+ },
60
+ "peerDependencies": {
61
+ "react": ">=18.0.0",
62
+ "react-dom": ">=18.0.0",
63
+ "zustand": "^5.0.13"
64
+ },
65
+ "peerDependenciesMeta": {
66
+ "react": {
67
+ "optional": true
68
+ },
69
+ "react-dom": {
70
+ "optional": true
71
+ },
72
+ "zustand": {
73
+ "optional": true
74
+ }
75
+ },
76
+ "devDependencies": {
77
+ "esbuild": "^0.24.0"
78
+ },
79
+ "scripts": {
80
+ "build": "node build.mjs",
81
+ "prepublishOnly": "npm run build"
48
82
  }
49
83
  }
@@ -18,31 +18,55 @@
18
18
  * specific contexts (e.g. when the host's overlay is focused).
19
19
  */
20
20
 
21
- import { TOOLS } from './index.js';
22
-
23
- // Single-key tool shortcuts. Mirrors what the standalone product ships.
21
+ // NB: tool names are inlined as string literals below (rather than imported
22
+ // from `./index.js`) to avoid a module-init cycle. EngineShortcuts.js and
23
+ // index.js import each other; if SHORTCUT_MAP read TOOLS.PAN at the top
24
+ // level, it would evaluate before index.js had populated TOOLS, throwing
25
+ // "TOOLS is undefined" once esbuild splits this into a chunk.
26
+ //
27
+ // The values still match TOOLS.* exactly. If you change one, change both.
24
28
  export const SHORTCUT_MAP = {
25
- h: TOOLS.PAN,
26
- v: TOOLS.SELECT,
27
- 1: TOOLS.SELECT,
28
- r: TOOLS.RECTANGLE,
29
- 2: TOOLS.RECTANGLE,
30
- o: TOOLS.CIRCLE,
31
- 4: TOOLS.CIRCLE,
32
- a: TOOLS.ARROW,
33
- 5: TOOLS.ARROW,
34
- l: TOOLS.LINE,
35
- 6: TOOLS.LINE,
36
- p: TOOLS.FREEHAND,
37
- 7: TOOLS.FREEHAND,
38
- t: TOOLS.TEXT,
39
- 8: TOOLS.TEXT,
40
- 9: TOOLS.IMAGE,
41
- e: TOOLS.ERASER,
42
- 0: TOOLS.ERASER,
43
- i: TOOLS.ICON,
44
- f: TOOLS.FRAME,
45
- k: TOOLS.LASER,
29
+ h: 'pan',
30
+ v: 'select',
31
+ 1: 'select',
32
+ r: 'rectangle',
33
+ 2: 'rectangle',
34
+ o: 'circle',
35
+ 4: 'circle',
36
+ a: 'arrow',
37
+ 5: 'arrow',
38
+ l: 'line',
39
+ 6: 'line',
40
+ p: 'freehand',
41
+ 7: 'freehand',
42
+ t: 'text',
43
+ 8: 'text',
44
+ 9: 'image',
45
+ e: 'eraser',
46
+ 0: 'eraser',
47
+ i: 'icon',
48
+ f: 'frame',
49
+ k: 'laser',
50
+ };
51
+
52
+ // Mirror of TOOLS used inside this module's function bodies. Local copy
53
+ // avoids the same module-init cycle while still letting callers compare
54
+ // engine state against named constants instead of magic strings.
55
+ const TOOLS = {
56
+ SELECT: 'select',
57
+ PAN: 'pan',
58
+ RECTANGLE: 'rectangle',
59
+ CIRCLE: 'circle',
60
+ LINE: 'line',
61
+ ARROW: 'arrow',
62
+ FREEHAND: 'freehand',
63
+ TEXT: 'text',
64
+ CODE: 'code',
65
+ ERASER: 'eraser',
66
+ LASER: 'laser',
67
+ IMAGE: 'image',
68
+ FRAME: 'frame',
69
+ ICON: 'icon',
46
70
  };
47
71
 
48
72
  function isTypingTarget(target) {
@@ -165,6 +189,28 @@ export function installEngineShortcuts(engine, options = {}) {
165
189
  if (window.currentShape) {
166
190
  const shape = window.currentShape;
167
191
  const shapes = window.shapes;
192
+ const isFrame = shape.shapeName === 'frame';
193
+ const isDiagramFrame = isFrame && !!shape._diagramType;
194
+
195
+ // Issue #24 bug #7: regular frame → orphan children +
196
+ // snapshot for undo (mirrors deleteSelectedShapes).
197
+ let childSnapshot = null;
198
+ if (isFrame && !isDiagramFrame) {
199
+ childSnapshot = [...(shape.containedShapes || [])];
200
+ for (const child of childSnapshot) {
201
+ if (!child || child === shape) continue;
202
+ const el = child.group || child.element;
203
+ if (el) {
204
+ if (shape.clipGroup && el.parentNode === shape.clipGroup) {
205
+ shape.clipGroup.removeChild(el);
206
+ }
207
+ if (window.svg && el.parentNode !== window.svg) window.svg.appendChild(el);
208
+ }
209
+ child.parentFrame = null;
210
+ delete child.isBeingMovedByFrame;
211
+ }
212
+ }
213
+
168
214
  if (shapes) {
169
215
  const idx = shapes.indexOf(shape);
170
216
  if (idx !== -1) shapes.splice(idx, 1);
@@ -175,10 +221,20 @@ export function installEngineShortcuts(engine, options = {}) {
175
221
  if (shape.parentFrame && typeof shape.parentFrame.removeShapeFromFrame === 'function') {
176
222
  shape.parentFrame.removeShapeFromFrame(shape);
177
223
  }
178
- const el = shape.group || shape.element;
179
- if (el && el.parentNode) el.parentNode.removeChild(el);
224
+
225
+ if (isFrame && !isDiagramFrame) {
226
+ shape.containedShapes = [];
227
+ if (shape.group && shape.group.parentNode) shape.group.parentNode.removeChild(shape.group);
228
+ if (shape.clipGroup && shape.clipGroup.parentNode) shape.clipGroup.parentNode.removeChild(shape.clipGroup);
229
+ if (shape.clipPath && shape.clipPath.parentNode) shape.clipPath.parentNode.removeChild(shape.clipPath);
230
+ } else {
231
+ const el = shape.group || shape.element;
232
+ if (el && el.parentNode) el.parentNode.removeChild(el);
233
+ }
234
+
180
235
  if (typeof window.pushDeleteAction === 'function') {
181
- window.pushDeleteAction(shape);
236
+ if (childSnapshot) window.pushDeleteAction(shape, { childSnapshot });
237
+ else window.pushDeleteAction(shape);
182
238
  }
183
239
  window.currentShape = null;
184
240
  if (typeof window.disableAllSideBars === 'function') {
@@ -316,18 +316,22 @@ export function renderAIDiagram(diagram) {
316
316
  const ox = vcx - dw / 2 - minX;
317
317
  const oy = vcy - dh / 2 - minY;
318
318
 
319
- // Create frame
320
- let frame;
321
- try {
322
- frame = new window.Frame(vcx - dw / 2 - PADDING, vcy - dh / 2 - PADDING, dw + PADDING * 2, dh + PADDING * 2, {
323
- stroke: '#888', strokeWidth: 2, fill: 'transparent', opacity: 1, frameName: title,
324
- });
325
- window.shapes.push(frame);
326
- if (window.pushCreateAction) window.pushCreateAction(frame);
327
- } catch (err) {
328
- console.error('[AIRenderer] Frame creation failed:', err);
329
- return false;
330
- }
319
+ // Phase 5 (issue #22): no outer wrapper frame. Each generated shape
320
+ // (node, edge, subgraph container) gets the same `groupId` so the whole
321
+ // diagram behaves as one selectable group via Selection.js's existing
322
+ // group-expansion path. Drops the "solid background frame" symptom and
323
+ // makes the rendered content as interactive as user-drawn shapes.
324
+ const groupId = `aidiag-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 7)}`;
325
+ // Kept under the same name `frame` to minimise the diff inside this
326
+ // 600-line function — it's now a thin shim that catches `addShapeToFrame`
327
+ // calls and re-routes them to a groupId assignment. `selectFrame` /
328
+ // `removeSelection` no-op; selection is on the first node below.
329
+ const frame = {
330
+ addShapeToFrame(s) { if (s) s.groupId = groupId; },
331
+ selectFrame() {},
332
+ removeSelection() {},
333
+ shapeName: 'frame',
334
+ };
331
335
 
332
336
  const nodeMap = new Map();
333
337
 
@@ -593,12 +597,15 @@ export function renderAIDiagram(diagram) {
593
597
  }
594
598
  }
595
599
 
596
- // Auto-select the frame and show its sidebar
597
- window.currentShape = frame;
598
- if (frame.selectFrame) frame.selectFrame();
599
- if (window.__sketchStoreApi) window.__sketchStoreApi.setSelectedShapeSidebar('frame');
600
+ // Phase 5: select the first generated node group expansion in
601
+ // Selection.js will pick up the rest via the shared groupId.
602
+ const first = nodeMap.values().next().value;
603
+ if (first && first.shape) {
604
+ window.currentShape = first.shape;
605
+ if (typeof first.shape.selectShape === 'function') first.shape.selectShape();
606
+ }
600
607
 
601
- console.log(`[AIRenderer] Done: ${nodes.length} nodes, ${edges.length} edges, ${subgraphs.length} subgraphs → "${title}"`);
608
+ console.log(`[AIRenderer] Done: ${nodes.length} nodes, ${edges.length} edges, ${subgraphs.length} subgraphs → "${title}" (groupId=${groupId})`);
602
609
  return true;
603
610
  }
604
611
 
@@ -738,7 +745,7 @@ function cross(ax, ay, bx, by, cx, cy) {
738
745
  * Automatically attach an arrow endpoint to a shape.
739
746
  * This makes edges follow nodes when they're moved.
740
747
  */
741
- function autoAttach(arrow, shape, isStart, contactPoint) {
748
+ export function autoAttach(arrow, shape, isStart, contactPoint) {
742
749
  if (!arrow || !shape) return;
743
750
  if (typeof arrow.attachToShape !== 'function') return;
744
751
 
@@ -1306,6 +1313,11 @@ export function generateFramePreviewSVG(frame, width = 500, height = 350) {
1306
1313
  // ============================================================
1307
1314
 
1308
1315
  export function initAIRenderer() {
1316
+ // Expose the autoAttach helper so sibling renderers (Mermaid flowchart,
1317
+ // Graph, LixScript) can wire arrow endpoints into shapes without
1318
+ // import-cycling through this large module.
1319
+ window.__autoAttach = autoAttach;
1320
+
1309
1321
  // Lazy-load sequence renderer
1310
1322
  let _seqParser = null;
1311
1323
  let _seqPreview = null;
@@ -48,39 +48,22 @@ function renderGraphOnCanvas(equations, settings) {
48
48
  ? 'Graph: ' + eqLabels.join(', ')
49
49
  : 'Graph';
50
50
 
51
- // Create frame
52
- let frame;
53
- try {
54
- frame = new window.Frame(frameX, frameY, frameW, frameH, {
55
- stroke: '#4A90D9', strokeWidth: 2, fill: 'transparent', opacity: 1,
56
- frameName: title,
57
- });
58
- // Store graph data for re-editing
59
- frame._graphData = {
60
- equations: equations.map(eq => ({ expression: eq.expression, color: eq.color })),
61
- settings: { ...settings },
62
- };
63
- frame._frameType = 'graph';
64
-
65
- window.shapes.push(frame);
66
- if (window.pushCreateAction) window.pushCreateAction(frame);
67
- } catch (err) {
68
- console.error('[GraphEngine] Frame creation failed:', err);
69
- return false;
70
- }
71
-
72
- // Parse SVG markup and insert elements into the frame
51
+ // Phase 5 (issue #22): no wrapper frame. The graph is conceptually a
52
+ // single rendering — axes, grid, curves all interlocked — so we don't
53
+ // split it into per-curve shapes. Instead the graph becomes ONE
54
+ // first-class shape on the canvas: a `graphShape` that implements the
55
+ // shape API the engine relies on (contains / selectShape / etc.) so
56
+ // it can be selected, dragged, attached to by arrows, picked up by
57
+ // the rect-drag — exactly like a user-drawn shape.
73
58
  try {
74
59
  const parser = new DOMParser();
75
60
  const doc = parser.parseFromString(svgMarkup, 'image/svg+xml');
76
61
  const svgEl = doc.querySelector('svg');
77
62
  if (!svgEl) return false;
78
63
 
79
- // Create a group to hold all graph elements
80
64
  const graphGroup = document.createElementNS(NS, 'g');
81
65
  graphGroup.setAttribute('data-type', 'graph-group');
82
66
  graphGroup.setAttribute('transform', `translate(${frameX + 20}, ${frameY + 20})`);
83
- graphGroup.style.pointerEvents = 'none';
84
67
 
85
68
  // Copy defs (clip paths)
86
69
  const defs = svgEl.querySelector('defs');
@@ -90,46 +73,86 @@ function renderGraphOnCanvas(equations, settings) {
90
73
  window.svg.insertBefore(defsClone, window.svg.firstChild);
91
74
  }
92
75
 
93
- // Copy all child elements
94
76
  while (svgEl.childNodes.length > 0) {
95
77
  const child = svgEl.childNodes[0];
96
- if (child.nodeName === 'defs') {
97
- svgEl.removeChild(child);
98
- continue;
99
- }
78
+ if (child.nodeName === 'defs') { svgEl.removeChild(child); continue; }
100
79
  graphGroup.appendChild(child);
101
80
  }
102
-
103
81
  window.svg.appendChild(graphGroup);
104
82
 
105
- // Wrap as a simple shape-like object for the frame
106
83
  const graphShape = {
107
- shapeName: 'graphContent',
84
+ shapeName: 'graph', // first-class shapeName
85
+ shapeID: `graph-${Date.now().toString(36)}-${Math.floor(Math.random() * 10000)}`,
108
86
  group: graphGroup,
109
87
  element: graphGroup,
110
88
  x: frameX + 20,
111
89
  y: frameY + 20,
112
90
  width: GRAPH_WIDTH,
113
91
  height: GRAPH_HEIGHT,
92
+ rotation: 0,
93
+ isSelected: false,
94
+ _selectionRect: null,
95
+ _graphData: {
96
+ equations: equations.map(eq => ({ expression: eq.expression, color: eq.color })),
97
+ settings: { ...settings },
98
+ },
99
+
100
+ // ── Shape API the engine relies on ────────────────────────
101
+ contains(px, py) {
102
+ return px >= this.x && px <= this.x + this.width
103
+ && py >= this.y && py <= this.y + this.height;
104
+ },
114
105
  move(dx, dy) {
115
- this.x += dx;
116
- this.y += dy;
106
+ this.x += dx; this.y += dy;
117
107
  this.group.setAttribute('transform', `translate(${this.x}, ${this.y})`);
108
+ this._updateSelectionRect();
109
+ },
110
+ selectShape() {
111
+ this.isSelected = true;
112
+ if (this._selectionRect) return;
113
+ const r = document.createElementNS(NS, 'rect');
114
+ r.setAttribute('fill', 'none');
115
+ r.setAttribute('stroke', '#9b7bf7');
116
+ r.setAttribute('stroke-width', '1.5');
117
+ r.setAttribute('stroke-dasharray', '4 3');
118
+ r.setAttribute('pointer-events', 'none');
119
+ window.svg.appendChild(r);
120
+ this._selectionRect = r;
121
+ this._updateSelectionRect();
122
+ },
123
+ removeSelection() {
124
+ this.isSelected = false;
125
+ if (this._selectionRect && this._selectionRect.parentNode) {
126
+ this._selectionRect.parentNode.removeChild(this._selectionRect);
127
+ }
128
+ this._selectionRect = null;
129
+ },
130
+ _updateSelectionRect() {
131
+ if (!this._selectionRect) return;
132
+ const pad = 4;
133
+ this._selectionRect.setAttribute('x', this.x - pad);
134
+ this._selectionRect.setAttribute('y', this.y - pad);
135
+ this._selectionRect.setAttribute('width', this.width + pad * 2);
136
+ this._selectionRect.setAttribute('height', this.height + pad * 2);
137
+ },
138
+ updateAttachedArrows() {
139
+ if (typeof window.updateAttachedArrows === 'function') {
140
+ window.updateAttachedArrows(this);
141
+ }
118
142
  },
119
- updateAttachedArrows() {},
120
143
  };
121
144
 
122
145
  window.shapes.push(graphShape);
123
- if (frame.addShapeToFrame) frame.addShapeToFrame(graphShape);
146
+ if (window.pushCreateAction) window.pushCreateAction(graphShape);
147
+
148
+ // Auto-select so the user sees something landed.
149
+ window.currentShape = graphShape;
150
+ graphShape.selectShape();
124
151
  } catch (err) {
125
152
  console.error('[GraphEngine] SVG insertion failed:', err);
153
+ return false;
126
154
  }
127
155
 
128
- // Select the frame
129
- window.currentShape = frame;
130
- if (frame.selectFrame) frame.selectFrame();
131
- if (window.__sketchStoreApi) window.__sketchStoreApi.setSelectedShapeSidebar('frame');
132
-
133
156
  return true;
134
157
  }
135
158