@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,739 @@
1
+ "use client";
2
+ import {
3
+ CodeShape
4
+ } from "./chunk-7TZSIR6V.js";
5
+ import {
6
+ ImageShape
7
+ } from "./chunk-CB2RANCB.js";
8
+ import {
9
+ IconShape
10
+ } from "./chunk-JQ3IRFDP.js";
11
+ import {
12
+ Frame
13
+ } from "./chunk-BU4D2WYQ.js";
14
+ import {
15
+ FreehandStroke
16
+ } from "./chunk-YKJUBFHE.js";
17
+ import {
18
+ Rectangle
19
+ } from "./chunk-T6ZVV5ZO.js";
20
+ import {
21
+ Circle
22
+ } from "./chunk-AJAZC37E.js";
23
+ import {
24
+ Arrow
25
+ } from "./chunk-XISEAGBV.js";
26
+ import {
27
+ Line
28
+ } from "./chunk-6PFXZKL2.js";
29
+ import "./chunk-BZE4CEBG.js";
30
+ import "./chunk-L5OARZJ7.js";
31
+ import {
32
+ TextShape
33
+ } from "./chunk-QXFBG7OM.js";
34
+
35
+ // src/core/SceneSerializer.js
36
+ var FORMAT_VERSION = 1;
37
+ var _sessionID = null;
38
+ function getSessionID() {
39
+ if (!_sessionID) {
40
+ _sessionID = `lx-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
41
+ }
42
+ return _sessionID;
43
+ }
44
+ function resetSessionID() {
45
+ _sessionID = null;
46
+ }
47
+ function cloneOptions(options) {
48
+ return JSON.parse(JSON.stringify(options));
49
+ }
50
+ function serializeShape(shape) {
51
+ const base = {
52
+ shapeID: shape.shapeID,
53
+ parentFrame: shape.parentFrame ? shape.parentFrame.shapeID : null,
54
+ // Group membership — null when the shape isn't part of a group.
55
+ // All shapes sharing a non-null groupId move/resize/rotate as a
56
+ // unit (see Selection.handleMultiSelectionMouseDown).
57
+ groupId: shape.groupId || null
58
+ };
59
+ switch (shape.shapeName) {
60
+ case "rectangle":
61
+ return {
62
+ ...base,
63
+ type: "rectangle",
64
+ x: shape.x,
65
+ y: shape.y,
66
+ width: shape.width,
67
+ height: shape.height,
68
+ rotation: shape.rotation,
69
+ options: cloneOptions(shape.options)
70
+ };
71
+ case "circle":
72
+ return {
73
+ ...base,
74
+ type: "circle",
75
+ x: shape.x,
76
+ y: shape.y,
77
+ rx: shape.rx,
78
+ ry: shape.ry,
79
+ rotation: shape.rotation,
80
+ options: cloneOptions(shape.options)
81
+ };
82
+ case "line":
83
+ return {
84
+ ...base,
85
+ type: "line",
86
+ startPoint: { x: shape.startPoint.x, y: shape.startPoint.y },
87
+ endPoint: { x: shape.endPoint.x, y: shape.endPoint.y },
88
+ controlPoint: shape.controlPoint ? { x: shape.controlPoint.x, y: shape.controlPoint.y } : null,
89
+ isCurved: shape.isCurved || false,
90
+ options: cloneOptions(shape.options)
91
+ };
92
+ case "arrow": {
93
+ const data = {
94
+ ...base,
95
+ type: "arrow",
96
+ startPoint: { x: shape.startPoint.x, y: shape.startPoint.y },
97
+ endPoint: { x: shape.endPoint.x, y: shape.endPoint.y },
98
+ options: cloneOptions(shape.options),
99
+ arrowOutlineStyle: shape.arrowOutlineStyle,
100
+ arrowHeadStyle: shape.arrowHeadStyle,
101
+ arrowCurved: shape.arrowCurved,
102
+ arrowCurveAmount: shape.arrowCurveAmount
103
+ };
104
+ if (shape.controlPoint1) data.controlPoint1 = { x: shape.controlPoint1.x, y: shape.controlPoint1.y };
105
+ if (shape.controlPoint2) data.controlPoint2 = { x: shape.controlPoint2.x, y: shape.controlPoint2.y };
106
+ if (shape.startAttachment) data.startAttachmentID = shape.startAttachment.shapeID;
107
+ if (shape.endAttachment) data.endAttachmentID = shape.endAttachment.shapeID;
108
+ return data;
109
+ }
110
+ case "freehandStroke":
111
+ return {
112
+ ...base,
113
+ type: "freehandStroke",
114
+ points: JSON.parse(JSON.stringify(shape.points)),
115
+ rotation: shape.rotation,
116
+ options: cloneOptions(shape.options)
117
+ };
118
+ case "frame":
119
+ return {
120
+ ...base,
121
+ type: "frame",
122
+ x: shape.x,
123
+ y: shape.y,
124
+ width: shape.width,
125
+ height: shape.height,
126
+ rotation: shape.rotation,
127
+ frameName: shape.frameName,
128
+ fillStyle: shape.fillStyle || "transparent",
129
+ fillColor: shape.fillColor || "#1e1e28",
130
+ gridSize: shape.gridSize || 20,
131
+ gridColor: shape.gridColor || "rgba(255,255,255,0.06)",
132
+ options: cloneOptions(shape.options),
133
+ // Issue #24 bug #10: filter out null / undefined / shapeID-less
134
+ // children. A stale reference would serialize as `null` and the
135
+ // load path would skip it (logging a misleading "missing shape"
136
+ // warning) — but also previously skewed the order tracking.
137
+ containedShapeIDs: shape.containedShapes ? Array.from(shape.containedShapes).filter((s) => s && s.shapeID).map((s) => s.shapeID) : []
138
+ };
139
+ case "text": {
140
+ const group = shape.group;
141
+ return {
142
+ ...base,
143
+ type: "text",
144
+ x: shape.x,
145
+ y: shape.y,
146
+ rotation: shape.rotation,
147
+ groupHTML: group.cloneNode(true).outerHTML
148
+ };
149
+ }
150
+ case "code": {
151
+ const group = shape.group;
152
+ return {
153
+ ...base,
154
+ type: "code",
155
+ x: shape.x,
156
+ y: shape.y,
157
+ rotation: shape.rotation,
158
+ groupHTML: group.cloneNode(true).outerHTML
159
+ };
160
+ }
161
+ case "image": {
162
+ const el = shape.element;
163
+ return {
164
+ ...base,
165
+ type: "image",
166
+ x: shape.x,
167
+ y: shape.y,
168
+ width: shape.width,
169
+ height: shape.height,
170
+ rotation: shape.rotation,
171
+ href: el.getAttribute("href") || el.getAttributeNS("http://www.w3.org/1999/xlink", "href") || ""
172
+ };
173
+ }
174
+ case "icon": {
175
+ const el = shape.element;
176
+ return {
177
+ ...base,
178
+ type: "icon",
179
+ x: shape.x,
180
+ y: shape.y,
181
+ width: shape.width,
182
+ height: shape.height,
183
+ rotation: shape.rotation,
184
+ elementHTML: el.cloneNode(true).outerHTML,
185
+ viewboxWidth: parseFloat(el.getAttribute("data-viewbox-width")) || 24,
186
+ viewboxHeight: parseFloat(el.getAttribute("data-viewbox-height")) || 24
187
+ };
188
+ }
189
+ default:
190
+ console.warn("[SceneSerializer] Unknown shape type:", shape.shapeName);
191
+ return null;
192
+ }
193
+ }
194
+ function deserializeShape(data) {
195
+ const svgEl = window.svg;
196
+ if (!svgEl) return null;
197
+ const ns = "http://www.w3.org/2000/svg";
198
+ switch (data.type) {
199
+ case "rectangle": {
200
+ const shape = new Rectangle(data.x, data.y, data.width, data.height, data.options || {});
201
+ if (data.rotation) shape.rotation = data.rotation;
202
+ if (data.shapeID) shape.shapeID = data.shapeID;
203
+ return shape;
204
+ }
205
+ case "circle": {
206
+ const shape = new Circle(data.x, data.y, data.rx, data.ry, data.options || {});
207
+ if (data.rotation) shape.rotation = data.rotation;
208
+ if (data.shapeID) shape.shapeID = data.shapeID;
209
+ return shape;
210
+ }
211
+ case "line": {
212
+ const shape = new Line(data.startPoint, data.endPoint, data.options || {});
213
+ if (data.controlPoint) shape.controlPoint = data.controlPoint;
214
+ if (data.isCurved) shape.isCurved = data.isCurved;
215
+ if (data.shapeID) shape.shapeID = data.shapeID;
216
+ return shape;
217
+ }
218
+ case "arrow": {
219
+ const shape = new Arrow(data.startPoint, data.endPoint, data.options || {});
220
+ if (data.controlPoint1) shape.controlPoint1 = data.controlPoint1;
221
+ if (data.controlPoint2) shape.controlPoint2 = data.controlPoint2;
222
+ if (data.arrowOutlineStyle) shape.arrowOutlineStyle = data.arrowOutlineStyle;
223
+ if (data.arrowHeadStyle) shape.arrowHeadStyle = data.arrowHeadStyle;
224
+ if (data.arrowCurved) shape.arrowCurved = data.arrowCurved;
225
+ if (data.arrowCurveAmount) shape.arrowCurveAmount = data.arrowCurveAmount;
226
+ if (data.shapeID) shape.shapeID = data.shapeID;
227
+ return shape;
228
+ }
229
+ case "freehandStroke": {
230
+ const shape = new FreehandStroke(data.points, data.options || {});
231
+ if (data.rotation) shape.rotation = data.rotation;
232
+ if (data.shapeID) shape.shapeID = data.shapeID;
233
+ return shape;
234
+ }
235
+ case "frame": {
236
+ const frameOpts = { ...data.options || {}, frameName: data.frameName || "Frame" };
237
+ if (data.fillStyle) frameOpts.fillStyle = data.fillStyle;
238
+ if (data.fillColor) frameOpts.fillColor = data.fillColor;
239
+ if (data.gridSize) frameOpts.gridSize = data.gridSize;
240
+ if (data.gridColor) frameOpts.gridColor = data.gridColor;
241
+ const shape = new Frame(data.x, data.y, data.width, data.height, frameOpts);
242
+ if (data.rotation) shape.rotation = data.rotation;
243
+ if (data.shapeID) shape.shapeID = data.shapeID;
244
+ shape.draw();
245
+ return shape;
246
+ }
247
+ case "text": {
248
+ if (!data.groupHTML) return null;
249
+ const parser = new DOMParser();
250
+ const doc = parser.parseFromString(`<svg xmlns="${ns}">${data.groupHTML}</svg>`, "image/svg+xml");
251
+ const group = doc.querySelector("g");
252
+ if (!group) return null;
253
+ const imported = svgEl.ownerDocument.importNode(group, true);
254
+ svgEl.appendChild(imported);
255
+ const shape = new TextShape(imported);
256
+ if (data.shapeID) shape.shapeID = data.shapeID;
257
+ return shape;
258
+ }
259
+ case "code": {
260
+ if (!data.groupHTML) return null;
261
+ const parser = new DOMParser();
262
+ const doc = parser.parseFromString(`<svg xmlns="${ns}">${data.groupHTML}</svg>`, "image/svg+xml");
263
+ const group = doc.querySelector("g");
264
+ if (!group) return null;
265
+ const imported = svgEl.ownerDocument.importNode(group, true);
266
+ svgEl.appendChild(imported);
267
+ const shape = new CodeShape(imported);
268
+ if (data.shapeID) shape.shapeID = data.shapeID;
269
+ return shape;
270
+ }
271
+ case "image": {
272
+ const imgEl = document.createElementNS(ns, "image");
273
+ imgEl.setAttribute("x", data.x);
274
+ imgEl.setAttribute("y", data.y);
275
+ imgEl.setAttribute("width", data.width);
276
+ imgEl.setAttribute("height", data.height);
277
+ imgEl.setAttribute("href", data.href);
278
+ imgEl.setAttribute("preserveAspectRatio", "none");
279
+ svgEl.appendChild(imgEl);
280
+ const shape = new ImageShape(imgEl);
281
+ if (data.rotation) shape.rotation = data.rotation;
282
+ if (data.shapeID) shape.shapeID = data.shapeID;
283
+ return shape;
284
+ }
285
+ case "icon": {
286
+ if (!data.elementHTML) return null;
287
+ const parser = new DOMParser();
288
+ const doc = parser.parseFromString(`<svg xmlns="${ns}">${data.elementHTML}</svg>`, "image/svg+xml");
289
+ const iconGroup = doc.querySelector("g");
290
+ if (!iconGroup) return null;
291
+ const imported = svgEl.ownerDocument.importNode(iconGroup, true);
292
+ svgEl.appendChild(imported);
293
+ const shape = new IconShape(imported);
294
+ if (data.shapeID) shape.shapeID = data.shapeID;
295
+ return shape;
296
+ }
297
+ default:
298
+ console.warn("[SceneSerializer] Unknown type:", data.type);
299
+ return null;
300
+ }
301
+ }
302
+ function saveScene(workspaceName = "Untitled") {
303
+ const allShapes = window.shapes || [];
304
+ const serialized = [];
305
+ const childrenByFrame = /* @__PURE__ */ new Map();
306
+ for (const s of allShapes) {
307
+ if (s && s.parentFrame && s.shapeID && s !== s.parentFrame) {
308
+ const arr = childrenByFrame.get(s.parentFrame.shapeID) || [];
309
+ arr.push(s.shapeID);
310
+ childrenByFrame.set(s.parentFrame.shapeID, arr);
311
+ }
312
+ }
313
+ for (const shape of allShapes) {
314
+ const data = serializeShape(shape);
315
+ if (!data) continue;
316
+ if (data.type === "frame" && shape.shapeID && childrenByFrame.has(shape.shapeID)) {
317
+ const merged = new Set(data.containedShapeIDs || []);
318
+ for (const id of childrenByFrame.get(shape.shapeID)) merged.add(id);
319
+ data.containedShapeIDs = Array.from(merged);
320
+ }
321
+ serialized.push(data);
322
+ }
323
+ const scene = {
324
+ format: "lixsketch",
325
+ version: FORMAT_VERSION,
326
+ sessionID: getSessionID(),
327
+ name: workspaceName,
328
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
329
+ viewport: window.currentViewBox ? { ...window.currentViewBox } : null,
330
+ zoom: window.currentZoom || 1,
331
+ shapes: serialized
332
+ };
333
+ return scene;
334
+ }
335
+ function loadScene(sceneData) {
336
+ if (!sceneData || sceneData.format !== "lixsketch") {
337
+ console.error("[SceneSerializer] Invalid scene format");
338
+ return false;
339
+ }
340
+ const svgEl = window.svg;
341
+ if (!svgEl) return false;
342
+ const existingShapes = window.shapes || [];
343
+ existingShapes.forEach((shape) => {
344
+ if (shape.shapeName === "frame") {
345
+ if (shape.clipGroup && shape.clipGroup.parentNode) {
346
+ shape.clipGroup.parentNode.removeChild(shape.clipGroup);
347
+ }
348
+ if (shape.clipPath && shape.clipPath.parentNode) {
349
+ shape.clipPath.parentNode.removeChild(shape.clipPath);
350
+ }
351
+ }
352
+ if (shape.group && shape.group.parentNode) {
353
+ shape.group.parentNode.removeChild(shape.group);
354
+ } else if (shape.element && shape.element.parentNode) {
355
+ shape.element.parentNode.removeChild(shape.element);
356
+ }
357
+ });
358
+ if (window.shapes) window.shapes.length = 0;
359
+ else window.shapes = [];
360
+ window.currentShape = null;
361
+ if (window.historyStack) window.historyStack.length = 0;
362
+ else window.historyStack = [];
363
+ if (window.redoStack) window.redoStack.length = 0;
364
+ else window.redoStack = [];
365
+ const idMap = /* @__PURE__ */ new Map();
366
+ const frameData = sceneData.shapes.filter((s) => s.type === "frame");
367
+ const otherData = sceneData.shapes.filter((s) => s.type !== "frame");
368
+ for (const data of frameData) {
369
+ const shape = deserializeShape(data);
370
+ if (shape) {
371
+ if (data.groupId) shape.groupId = data.groupId;
372
+ window.shapes.push(shape);
373
+ if (data.shapeID) idMap.set(data.shapeID, shape);
374
+ }
375
+ }
376
+ for (const data of otherData) {
377
+ const shape = deserializeShape(data);
378
+ if (shape) {
379
+ if (data.groupId) shape.groupId = data.groupId;
380
+ window.shapes.push(shape);
381
+ if (data.shapeID) idMap.set(data.shapeID, shape);
382
+ }
383
+ }
384
+ for (const data of frameData) {
385
+ const frame = idMap.get(data.shapeID);
386
+ if (frame && data.containedShapeIDs && data.containedShapeIDs.length > 0) {
387
+ for (const childID of data.containedShapeIDs) {
388
+ const child = idMap.get(childID);
389
+ if (!child) {
390
+ console.warn(`[SceneSerializer] Frame "${data.frameName}" references missing shape: ${childID}`);
391
+ continue;
392
+ }
393
+ try {
394
+ if (typeof frame.addShapeToFrame === "function") {
395
+ frame.addShapeToFrame(child);
396
+ } else {
397
+ frame.containedShapes.push(child);
398
+ child.parentFrame = frame;
399
+ }
400
+ } catch (err) {
401
+ console.warn(`[SceneSerializer] Failed to restore containment for ${childID} in frame ${data.shapeID}:`, err);
402
+ if (!frame.containedShapes.includes(child)) {
403
+ frame.containedShapes.push(child);
404
+ }
405
+ child.parentFrame = frame;
406
+ }
407
+ }
408
+ }
409
+ }
410
+ for (const data of sceneData.shapes) {
411
+ if (data.type === "arrow") {
412
+ const arrow = idMap.get(data.shapeID);
413
+ if (!arrow) continue;
414
+ if (data.startAttachmentID) {
415
+ const target = idMap.get(data.startAttachmentID);
416
+ if (target && arrow.setStartAttachment) arrow.setStartAttachment(target);
417
+ }
418
+ if (data.endAttachmentID) {
419
+ const target = idMap.get(data.endAttachmentID);
420
+ if (target && arrow.setEndAttachment) arrow.setEndAttachment(target);
421
+ }
422
+ }
423
+ }
424
+ for (const data of frameData) {
425
+ const frame = idMap.get(data.shapeID);
426
+ if (!frame) continue;
427
+ delete frame.isBeingMovedByFrame;
428
+ delete frame.isDraggedOutTemporarily;
429
+ if (frame.clipGroup && frame.clipGroup.parentNode !== svgEl) {
430
+ svgEl.appendChild(frame.clipGroup);
431
+ }
432
+ for (const child of frame.containedShapes || []) {
433
+ if (!child) continue;
434
+ delete child.isBeingMovedByFrame;
435
+ delete child.isDraggedOutTemporarily;
436
+ const el = child.group || child.element;
437
+ if (el && el.parentNode !== frame.clipGroup) {
438
+ if (el.parentNode) el.parentNode.removeChild(el);
439
+ frame.clipGroup.appendChild(el);
440
+ }
441
+ if (child.shapeName === "frame" && child.clipGroup && child.clipGroup.parentNode !== frame.clipGroup) {
442
+ if (child.clipGroup.parentNode) child.clipGroup.parentNode.removeChild(child.clipGroup);
443
+ frame.clipGroup.appendChild(child.clipGroup);
444
+ }
445
+ child.parentFrame = frame;
446
+ }
447
+ if (typeof frame.updateClipPath === "function") frame.updateClipPath();
448
+ if (typeof frame.draw === "function") {
449
+ try {
450
+ frame.draw();
451
+ } catch (err) {
452
+ console.warn("[SceneSerializer] frame redraw failed:", err);
453
+ }
454
+ }
455
+ }
456
+ if (sceneData.viewport && svgEl) {
457
+ const vb = sceneData.viewport;
458
+ window.currentViewBox = { ...vb };
459
+ svgEl.setAttribute("viewBox", `${vb.x} ${vb.y} ${vb.width} ${vb.height}`);
460
+ }
461
+ if (sceneData.zoom) {
462
+ window.currentZoom = sceneData.zoom;
463
+ }
464
+ console.log(`[SceneSerializer] Loaded ${window.shapes.length} shapes from "${sceneData.name}"`);
465
+ if (window.__sketchEngine && typeof window.__sketchEngine.setActiveTool === "function") {
466
+ const store = window.__sketchStoreApi;
467
+ const currentTool = store ? store.getState().activeTool : "select";
468
+ window.__sketchEngine.setActiveTool(currentTool);
469
+ } else {
470
+ window.isSelectionToolActive = true;
471
+ }
472
+ return true;
473
+ }
474
+ function downloadScene(workspaceName = "Untitled") {
475
+ const scene = saveScene(workspaceName);
476
+ const json = JSON.stringify(scene, null, 2);
477
+ const blob = new Blob([json], { type: "application/json" });
478
+ const url = URL.createObjectURL(blob);
479
+ const a = document.createElement("a");
480
+ a.href = url;
481
+ a.download = `${workspaceName.replace(/[^a-zA-Z0-9_-]/g, "_")}.lixjson`;
482
+ document.body.appendChild(a);
483
+ a.click();
484
+ document.body.removeChild(a);
485
+ URL.revokeObjectURL(url);
486
+ }
487
+ function validateScene(data) {
488
+ if (!data || typeof data !== "object") return { valid: false, error: "Not a valid JSON object" };
489
+ if (data.format !== "lixsketch") return { valid: false, error: "Not a LixSketch scene file (missing format field)" };
490
+ if (!data.version || data.version > FORMAT_VERSION) return { valid: false, error: `Unsupported version: ${data.version}` };
491
+ if (!Array.isArray(data.shapes)) return { valid: false, error: "Invalid scene: missing shapes array" };
492
+ return {
493
+ valid: true,
494
+ name: data.name || "Untitled",
495
+ shapeCount: data.shapes.length,
496
+ sessionID: data.sessionID || null,
497
+ createdAt: data.createdAt || null
498
+ };
499
+ }
500
+ function uploadScene() {
501
+ return new Promise((resolve, reject) => {
502
+ const input = document.createElement("input");
503
+ input.type = "file";
504
+ input.accept = ".lixjson,.json";
505
+ input.onchange = (e) => {
506
+ const file = e.target.files[0];
507
+ if (!file) return resolve(false);
508
+ const reader = new FileReader();
509
+ reader.onload = (ev) => {
510
+ try {
511
+ const data = JSON.parse(ev.target.result);
512
+ const validation = validateScene(data);
513
+ if (!validation.valid) {
514
+ console.error("[SceneSerializer] Invalid file:", validation.error);
515
+ resolve({ success: false, error: validation.error });
516
+ return;
517
+ }
518
+ resetSessionID();
519
+ const result = loadScene(data);
520
+ resolve({ success: result, validation });
521
+ } catch (err) {
522
+ console.error("[SceneSerializer] Failed to parse file:", err);
523
+ resolve({ success: false, error: "Failed to parse JSON file" });
524
+ }
525
+ };
526
+ reader.readAsText(file);
527
+ };
528
+ input.click();
529
+ });
530
+ }
531
+ function exportAsPNG() {
532
+ const svgEl = window.svg;
533
+ if (!svgEl) return;
534
+ const clone = svgEl.cloneNode(true);
535
+ const selectionEls = clone.querySelectorAll("[data-selection], .selection-handle, .resize-handle, .rotation-handle");
536
+ selectionEls.forEach((el) => el.remove());
537
+ const svgData = new XMLSerializer().serializeToString(clone);
538
+ const canvas = document.createElement("canvas");
539
+ const vb = svgEl.viewBox.baseVal;
540
+ canvas.width = vb.width * 2;
541
+ canvas.height = vb.height * 2;
542
+ const ctx = canvas.getContext("2d");
543
+ ctx.scale(2, 2);
544
+ const img = new Image();
545
+ const svgBlob = new Blob([svgData], { type: "image/svg+xml;charset=utf-8" });
546
+ const url = URL.createObjectURL(svgBlob);
547
+ img.onload = () => {
548
+ ctx.fillStyle = "#121212";
549
+ ctx.fillRect(0, 0, vb.width, vb.height);
550
+ ctx.drawImage(img, 0, 0, vb.width, vb.height);
551
+ URL.revokeObjectURL(url);
552
+ canvas.toBlob((blob) => {
553
+ const a = document.createElement("a");
554
+ a.href = URL.createObjectURL(blob);
555
+ a.download = "lixsketch-export.png";
556
+ document.body.appendChild(a);
557
+ a.click();
558
+ document.body.removeChild(a);
559
+ }, "image/png");
560
+ };
561
+ img.src = url;
562
+ }
563
+ function copyAsPNG() {
564
+ const svgEl = window.svg;
565
+ if (!svgEl) return;
566
+ const clone = svgEl.cloneNode(true);
567
+ const selectionEls = clone.querySelectorAll("[data-selection], .selection-handle, .resize-handle, .rotation-handle");
568
+ selectionEls.forEach((el) => el.remove());
569
+ const svgData = new XMLSerializer().serializeToString(clone);
570
+ const canvas = document.createElement("canvas");
571
+ const vb = svgEl.viewBox.baseVal;
572
+ canvas.width = vb.width * 2;
573
+ canvas.height = vb.height * 2;
574
+ const ctx = canvas.getContext("2d");
575
+ ctx.scale(2, 2);
576
+ const img = new Image();
577
+ const svgBlob = new Blob([svgData], { type: "image/svg+xml;charset=utf-8" });
578
+ const url = URL.createObjectURL(svgBlob);
579
+ img.onload = () => {
580
+ ctx.fillStyle = "#121212";
581
+ ctx.fillRect(0, 0, vb.width, vb.height);
582
+ ctx.drawImage(img, 0, 0, vb.width, vb.height);
583
+ URL.revokeObjectURL(url);
584
+ canvas.toBlob((blob) => {
585
+ if (!blob) return;
586
+ navigator.clipboard.write([
587
+ new ClipboardItem({ "image/png": blob })
588
+ ]).catch((err) => console.warn("[SceneSerializer] Clipboard write failed:", err));
589
+ }, "image/png");
590
+ };
591
+ img.src = url;
592
+ }
593
+ function copyAsSVG() {
594
+ const svgEl = window.svg;
595
+ if (!svgEl) return;
596
+ const clone = svgEl.cloneNode(true);
597
+ const selectionEls = clone.querySelectorAll("[data-selection], .selection-handle, .resize-handle, .rotation-handle");
598
+ selectionEls.forEach((el) => el.remove());
599
+ const svgData = new XMLSerializer().serializeToString(clone);
600
+ navigator.clipboard.writeText(svgData).catch((err) => console.warn("[SceneSerializer] Clipboard write failed:", err));
601
+ }
602
+ function exportAsPDF() {
603
+ const svgEl = window.svg;
604
+ if (!svgEl) return;
605
+ const clone = svgEl.cloneNode(true);
606
+ const selectionEls = clone.querySelectorAll("[data-selection], .selection-handle, .resize-handle, .rotation-handle");
607
+ selectionEls.forEach((el) => el.remove());
608
+ const svgData = new XMLSerializer().serializeToString(clone);
609
+ const printWindow = window.open("", "_blank");
610
+ printWindow.document.write(`
611
+ <!DOCTYPE html>
612
+ <html>
613
+ <head><title>LixSketch Export</title>
614
+ <style>
615
+ body { margin: 0; background: #121212; display: flex; align-items: center; justify-content: center; min-height: 100vh; }
616
+ svg { max-width: 100vw; max-height: 100vh; }
617
+ @media print { body { background: white; } }
618
+ </style>
619
+ </head>
620
+ <body>${svgData}</body>
621
+ </html>
622
+ `);
623
+ printWindow.document.close();
624
+ printWindow.focus();
625
+ setTimeout(() => printWindow.print(), 500);
626
+ }
627
+ function resetCanvas() {
628
+ const svgEl = window.svg;
629
+ if (!svgEl) return;
630
+ const existingShapes = window.shapes || [];
631
+ existingShapes.forEach((shape) => {
632
+ if (shape.shapeName === "frame") {
633
+ if (shape.clipGroup && shape.clipGroup.parentNode) {
634
+ shape.clipGroup.parentNode.removeChild(shape.clipGroup);
635
+ }
636
+ if (shape.clipPath && shape.clipPath.parentNode) {
637
+ shape.clipPath.parentNode.removeChild(shape.clipPath);
638
+ }
639
+ }
640
+ if (shape.group && shape.group.parentNode) {
641
+ shape.group.parentNode.removeChild(shape.group);
642
+ } else if (shape.element && shape.element.parentNode) {
643
+ shape.element.parentNode.removeChild(shape.element);
644
+ }
645
+ });
646
+ svgEl.querySelectorAll(
647
+ ".selection-outline, .resize-anchor, .rotation-anchor, [data-selection]"
648
+ ).forEach((el) => el.remove());
649
+ if (window.shapes) window.shapes.length = 0;
650
+ else window.shapes = [];
651
+ window.currentShape = null;
652
+ if (window.historyStack) window.historyStack.length = 0;
653
+ else window.historyStack = [];
654
+ if (window.redoStack) window.redoStack.length = 0;
655
+ else window.redoStack = [];
656
+ if (typeof window.clearAllSelections === "function") {
657
+ window.clearAllSelections();
658
+ }
659
+ if (typeof window.disableAllSideBars === "function") {
660
+ window.disableAllSideBars();
661
+ }
662
+ try {
663
+ localStorage.removeItem("lixsketch-autosave");
664
+ localStorage.removeItem("lixsketch-autosave-meta");
665
+ const sid = window.__sessionID;
666
+ if (sid) {
667
+ localStorage.removeItem(`lixsketch-autosave-${sid}`);
668
+ localStorage.removeItem(`lixsketch-autosave-meta-${sid}`);
669
+ }
670
+ } catch (_) {
671
+ }
672
+ console.log("[SceneSerializer] Canvas reset");
673
+ }
674
+ function findTextOnCanvas(query) {
675
+ const allShapes = window.shapes || [];
676
+ if (!query || query.trim() === "") return [];
677
+ const lowerQuery = query.toLowerCase();
678
+ const results = [];
679
+ for (const shape of allShapes) {
680
+ let textContent = "";
681
+ if (shape.shapeName === "text" || shape.shapeName === "code") {
682
+ const group = shape.group;
683
+ if (group) {
684
+ const textEls = group.querySelectorAll("text, tspan");
685
+ textEls.forEach((el) => {
686
+ if (el.textContent) textContent += el.textContent + " ";
687
+ });
688
+ const foreignEls = group.querySelectorAll("foreignObject *");
689
+ foreignEls.forEach((el) => {
690
+ if (el.textContent) textContent += el.textContent + " ";
691
+ });
692
+ }
693
+ } else if (shape.shapeName === "frame") {
694
+ textContent = shape.frameName || "";
695
+ }
696
+ textContent = textContent.trim();
697
+ if (textContent && textContent.toLowerCase().includes(lowerQuery)) {
698
+ results.push({
699
+ shape,
700
+ text: textContent,
701
+ type: shape.shapeName,
702
+ x: shape.x || 0,
703
+ y: shape.y || 0
704
+ });
705
+ }
706
+ }
707
+ return results;
708
+ }
709
+ function initSceneSerializer() {
710
+ window.__sceneSerializer = {
711
+ save: saveScene,
712
+ load: loadScene,
713
+ download: downloadScene,
714
+ upload: uploadScene,
715
+ exportPNG: exportAsPNG,
716
+ exportPDF: exportAsPDF,
717
+ copyAsPNG,
718
+ copyAsSVG,
719
+ resetCanvas,
720
+ findText: findTextOnCanvas
721
+ };
722
+ }
723
+ export {
724
+ copyAsPNG,
725
+ copyAsSVG,
726
+ downloadScene,
727
+ exportAsPDF,
728
+ exportAsPNG,
729
+ findTextOnCanvas,
730
+ getSessionID,
731
+ initSceneSerializer,
732
+ loadScene,
733
+ resetCanvas,
734
+ resetSessionID,
735
+ saveScene,
736
+ uploadScene,
737
+ validateScene
738
+ };
739
+ //# sourceMappingURL=SceneSerializer-4U4B24JM.js.map