@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,575 @@
1
+ "use client";
2
+
3
+ // src/shapes/FreehandStroke.js
4
+ import { getStroke } from "perfect-freehand";
5
+ function getSvgPathFromStroke(stroke) {
6
+ if (!stroke.length) return "";
7
+ const pathData = [];
8
+ pathData.push("M", stroke[0][0], stroke[0][1]);
9
+ for (let i = 1; i < stroke.length - 1; i++) {
10
+ const curr = stroke[i];
11
+ const next = stroke[i + 1];
12
+ const cpX = curr[0] + (next[0] - curr[0]) * 0.5;
13
+ const cpY = curr[1] + (next[1] - curr[1]) * 0.5;
14
+ pathData.push("Q", curr[0], curr[1], cpX, cpY);
15
+ }
16
+ if (stroke.length > 1) {
17
+ const lastPoint = stroke[stroke.length - 1];
18
+ pathData.push("L", lastPoint[0], lastPoint[1]);
19
+ }
20
+ return pathData.join(" ");
21
+ }
22
+ var FreehandStroke = class {
23
+ constructor(points = [], options = {}) {
24
+ this.points = points;
25
+ this.rawPoints = [];
26
+ this.options = {
27
+ stroke: "#fff",
28
+ strokeWidth: 3,
29
+ fill: "none",
30
+ strokeLinecap: "round",
31
+ strokeLinejoin: "round",
32
+ strokeOpacity: 1,
33
+ thinning: 0.5,
34
+ roughness: 0,
35
+ strokeStyle: "solid",
36
+ ...options
37
+ };
38
+ this.element = null;
39
+ this.isSelected = false;
40
+ this.rotation = 0;
41
+ this.group = document.createElementNS("http://www.w3.org/2000/svg", "g");
42
+ this.anchors = [];
43
+ this.rotationAnchor = null;
44
+ this.rotationLine = null;
45
+ this.selectionPadding = 8;
46
+ this.selectionOutline = null;
47
+ this.boundingBox = { x: 0, y: 0, width: 0, height: 0 };
48
+ this.shapeName = "freehandStroke";
49
+ this._moveOffsetX = 0;
50
+ this._moveOffsetY = 0;
51
+ this.parentFrame = null;
52
+ svg.appendChild(this.group);
53
+ this.draw();
54
+ }
55
+ // Add position and dimension properties for frame compatibility
56
+ // Getters include pending move offset so callers see the visual position
57
+ get x() {
58
+ return this.boundingBox.x + (this._moveOffsetX || 0);
59
+ }
60
+ set x(value) {
61
+ const dx = value - this.boundingBox.x - (this._moveOffsetX || 0);
62
+ this.points = this.points.map((point) => [point[0] + dx, point[1], point[2] || 0.5]);
63
+ this.boundingBox.x = value - (this._moveOffsetX || 0);
64
+ }
65
+ get y() {
66
+ return this.boundingBox.y + (this._moveOffsetY || 0);
67
+ }
68
+ set y(value) {
69
+ const dy = value - this.boundingBox.y - (this._moveOffsetY || 0);
70
+ this.points = this.points.map((point) => [point[0], point[1] + dy, point[2] || 0.5]);
71
+ this.boundingBox.y = value - (this._moveOffsetY || 0);
72
+ }
73
+ get width() {
74
+ return this.boundingBox.width;
75
+ }
76
+ set width(value) {
77
+ if (this.boundingBox.width === 0) return;
78
+ const scaleX = value / this.boundingBox.width;
79
+ const centerX = this.boundingBox.x + this.boundingBox.width / 2;
80
+ this.points = this.points.map((point) => [
81
+ centerX + (point[0] - centerX) * scaleX,
82
+ point[1],
83
+ point[2] || 0.5
84
+ ]);
85
+ this.boundingBox.width = value;
86
+ }
87
+ get height() {
88
+ return this.boundingBox.height;
89
+ }
90
+ set height(value) {
91
+ if (this.boundingBox.height === 0) return;
92
+ const scaleY = value / this.boundingBox.height;
93
+ const centerY = this.boundingBox.y + this.boundingBox.height / 2;
94
+ this.points = this.points.map((point) => [
95
+ point[0],
96
+ centerY + (point[1] - centerY) * scaleY,
97
+ point[2] || 0.5
98
+ ]);
99
+ this.boundingBox.height = value;
100
+ }
101
+ // Enhanced smoothing algorithm
102
+ smoothPoints(points, factor = 0.8) {
103
+ if (points.length < 3) return points;
104
+ const smoothed = [points[0]];
105
+ for (let i = 1; i < points.length - 1; i++) {
106
+ const prev = points[i - 1];
107
+ const curr = points[i];
108
+ const next = points[i + 1];
109
+ const smoothedX = curr[0] * (1 - factor) + (prev[0] + next[0]) * factor * 0.5;
110
+ const smoothedY = curr[1] * (1 - factor) + (prev[1] + next[1]) * factor * 0.5;
111
+ const pressure = curr[2] || 0.5;
112
+ smoothed.push([smoothedX, smoothedY, pressure]);
113
+ }
114
+ smoothed.push(points[points.length - 1]);
115
+ return smoothed;
116
+ }
117
+ // Interpolate points for smoother curves
118
+ interpolatePoints(points, steps = 2) {
119
+ if (points.length < 2) return points;
120
+ const interpolated = [points[0]];
121
+ for (let i = 0; i < points.length - 1; i++) {
122
+ const curr = points[i];
123
+ const next = points[i + 1];
124
+ for (let j = 1; j <= steps; j++) {
125
+ const t = j / (steps + 1);
126
+ const x = curr[0] + (next[0] - curr[0]) * t;
127
+ const y = curr[1] + (next[1] - curr[1]) * t;
128
+ const pressure = curr[2] + (next[2] - curr[2]) * t;
129
+ interpolated.push([x, y, pressure]);
130
+ }
131
+ if (i < points.length - 2) {
132
+ interpolated.push(next);
133
+ }
134
+ }
135
+ interpolated.push(points[points.length - 1]);
136
+ return interpolated;
137
+ }
138
+ // Enhanced path generation with better curve fitting
139
+ getPathData() {
140
+ if (this.points.length < 2) return "";
141
+ const isRough = this.options.roughness === "rough";
142
+ const isMedium = this.options.roughness === "medium";
143
+ let pts = this.points;
144
+ if (isRough || isMedium) {
145
+ const jitter = isRough ? 3.5 : 1.5;
146
+ pts = pts.map((p, i) => {
147
+ if (i === 0 || i === pts.length - 1) return p;
148
+ return [
149
+ p[0] + (Math.random() - 0.5) * jitter,
150
+ p[1] + (Math.random() - 0.5) * jitter,
151
+ p[2] || 0.5
152
+ ];
153
+ });
154
+ }
155
+ let smoothedPoints = this.interpolatePoints(pts, 1);
156
+ if (!isRough) {
157
+ smoothedPoints = this.smoothPoints(smoothedPoints, 0.6);
158
+ smoothedPoints = this.smoothPoints(smoothedPoints, 0.4);
159
+ }
160
+ const thinning = this.options.thinning !== void 0 ? this.options.thinning : 0;
161
+ const smoothing = isRough ? 0.3 : isMedium ? 0.6 : 0.9;
162
+ const streamline = isRough ? 0.1 : isMedium ? 0.25 : 0.4;
163
+ const stroke = getStroke(smoothedPoints, {
164
+ size: this.options.strokeWidth,
165
+ thinning,
166
+ smoothing,
167
+ streamline,
168
+ easing: (t) => Math.sin(t * Math.PI * 0.5),
169
+ start: {
170
+ taper: thinning > 0 ? this.options.strokeWidth * 2 : 0,
171
+ easing: (t) => t * t,
172
+ cap: thinning === 0
173
+ },
174
+ end: {
175
+ taper: thinning > 0 ? this.options.strokeWidth * 2 : 0,
176
+ easing: (t) => --t * t * t + 1,
177
+ cap: thinning === 0
178
+ },
179
+ simulatePressure: thinning > 0
180
+ });
181
+ return getSvgPathFromStroke(stroke);
182
+ }
183
+ // Simple centerline path used for dashed/dotted overlays
184
+ getCenterlinePathData() {
185
+ if (this.points.length < 2) return "";
186
+ const d = [`M ${this.points[0][0]} ${this.points[0][1]}`];
187
+ for (let i = 1; i < this.points.length - 1; i++) {
188
+ const curr = this.points[i];
189
+ const next = this.points[i + 1];
190
+ const cpX = curr[0] + (next[0] - curr[0]) * 0.5;
191
+ const cpY = curr[1] + (next[1] - curr[1]) * 0.5;
192
+ d.push(`Q ${curr[0]} ${curr[1]} ${cpX} ${cpY}`);
193
+ }
194
+ const last = this.points[this.points.length - 1];
195
+ d.push(`L ${last[0]} ${last[1]}`);
196
+ return d.join(" ");
197
+ }
198
+ // Calculate the bounding box of the stroke
199
+ calculateBoundingBox() {
200
+ if (this.points.length === 0) {
201
+ return { x: 0, y: 0, width: 0, height: 0 };
202
+ }
203
+ let minX = Infinity, minY = Infinity;
204
+ let maxX = -Infinity, maxY = -Infinity;
205
+ this.points.forEach((point) => {
206
+ minX = Math.min(minX, point[0]);
207
+ minY = Math.min(minY, point[1]);
208
+ maxX = Math.max(maxX, point[0]);
209
+ maxY = Math.max(maxY, point[1]);
210
+ });
211
+ if (minX === Infinity || minY === Infinity) {
212
+ return { x: 0, y: 0, width: 0, height: 0 };
213
+ }
214
+ const padding = this.options.strokeWidth / 2;
215
+ return {
216
+ x: minX - padding,
217
+ y: minY - padding,
218
+ width: Math.max(0, maxX - minX + padding * 2),
219
+ height: Math.max(0, maxY - minY + padding * 2)
220
+ };
221
+ }
222
+ draw() {
223
+ while (this.group.firstChild) {
224
+ this.group.removeChild(this.group.firstChild);
225
+ }
226
+ if (this.selectionOutline && this.selectionOutline.parentNode === this.group) {
227
+ this.group.removeChild(this.selectionOutline);
228
+ this.selectionOutline = null;
229
+ }
230
+ const path = document.createElementNS("http://www.w3.org/2000/svg", "path");
231
+ const pathData = this.getPathData();
232
+ path.setAttribute("d", pathData);
233
+ path.setAttribute("fill", this.options.stroke);
234
+ path.setAttribute("fill-opacity", this.options.strokeOpacity);
235
+ path.setAttribute("stroke", "none");
236
+ if (this.options.strokeStyle === "dashed" || this.options.strokeStyle === "dotted") {
237
+ const dashArray = this.options.strokeStyle === "dashed" ? `${this.options.strokeWidth * 3} ${this.options.strokeWidth * 2}` : `${this.options.strokeWidth * 0.5} ${this.options.strokeWidth * 2}`;
238
+ const overlay = document.createElementNS("http://www.w3.org/2000/svg", "path");
239
+ overlay.setAttribute("d", this.getCenterlinePathData());
240
+ overlay.setAttribute("fill", "none");
241
+ overlay.setAttribute("stroke", this.options.stroke);
242
+ overlay.setAttribute("stroke-width", this.options.strokeWidth);
243
+ overlay.setAttribute("stroke-linecap", "round");
244
+ overlay.setAttribute("stroke-linejoin", "round");
245
+ overlay.setAttribute("stroke-dasharray", dashArray);
246
+ overlay.setAttribute("stroke-opacity", this.options.strokeOpacity);
247
+ this.group.appendChild(overlay);
248
+ }
249
+ this.element = path;
250
+ this.group.appendChild(path);
251
+ this.boundingBox = this.calculateBoundingBox();
252
+ const centerX = this.boundingBox.x + this.boundingBox.width / 2;
253
+ const centerY = this.boundingBox.y + this.boundingBox.height / 2;
254
+ if (!isNaN(centerX) && !isNaN(centerY)) {
255
+ this.group.setAttribute("transform", `rotate(${this.rotation} ${centerX} ${centerY})`);
256
+ }
257
+ if (this.isSelected) {
258
+ this.addAnchors();
259
+ }
260
+ }
261
+ move(dx, dy) {
262
+ this._moveOffsetX = (this._moveOffsetX || 0) + dx;
263
+ this._moveOffsetY = (this._moveOffsetY || 0) + dy;
264
+ const centerX = this.boundingBox.x + this.boundingBox.width / 2;
265
+ const centerY = this.boundingBox.y + this.boundingBox.height / 2;
266
+ const rot = this.rotation ? `rotate(${this.rotation} ${centerX} ${centerY})` : "";
267
+ this.group.setAttribute("transform", `translate(${this._moveOffsetX}, ${this._moveOffsetY}) ${rot}`);
268
+ }
269
+ // Call after drag ends to bake the offset into actual point coordinates
270
+ finalizeMove() {
271
+ if (this._moveOffsetX || this._moveOffsetY) {
272
+ const ox = this._moveOffsetX || 0;
273
+ const oy = this._moveOffsetY || 0;
274
+ this.points = this.points.map((point) => [point[0] + ox, point[1] + oy, point[2] || 0.5]);
275
+ this._moveOffsetX = 0;
276
+ this._moveOffsetY = 0;
277
+ this.draw();
278
+ }
279
+ }
280
+ updateAttachedArrows() {
281
+ if (typeof window.__updateArrowsForShape === "function") {
282
+ window.__updateArrowsForShape(this);
283
+ }
284
+ }
285
+ selectStroke() {
286
+ this.isSelected = true;
287
+ this.draw();
288
+ disableAllSideBars();
289
+ paintBrushSideBar.classList.remove("hidden");
290
+ if (window.__showSidebarForShape) window.__showSidebarForShape("freehandStroke");
291
+ this.updateSidebar();
292
+ }
293
+ deselectStroke() {
294
+ this.isSelected = false;
295
+ this.anchors = [];
296
+ this.rotationAnchor = null;
297
+ this.draw();
298
+ }
299
+ removeSelection() {
300
+ this.anchors.forEach((anchor) => {
301
+ if (anchor.parentNode === this.group) {
302
+ this.group.removeChild(anchor);
303
+ }
304
+ });
305
+ if (this.rotationAnchor && this.rotationAnchor.parentNode === this.group) {
306
+ this.group.removeChild(this.rotationAnchor);
307
+ }
308
+ if (this.rotationLine && this.rotationLine.parentNode === this.group) {
309
+ this.group.removeChild(this.rotationLine);
310
+ }
311
+ if (this.selectionOutline && this.selectionOutline.parentNode === this.group) {
312
+ this.group.removeChild(this.selectionOutline);
313
+ }
314
+ this.anchors = [];
315
+ this.rotationAnchor = null;
316
+ this.rotationLine = null;
317
+ this.selectionOutline = null;
318
+ this.isSelected = false;
319
+ }
320
+ // No-op: React sidebar handles UI updates via Zustand store
321
+ updateSidebar() {
322
+ }
323
+ contains(x, y) {
324
+ const ox = this._moveOffsetX || 0;
325
+ const oy = this._moveOffsetY || 0;
326
+ const bbX = this.boundingBox.x + ox;
327
+ const bbY = this.boundingBox.y + oy;
328
+ const centerX = bbX + this.boundingBox.width / 2;
329
+ const centerY = bbY + this.boundingBox.height / 2;
330
+ const dx = x - centerX;
331
+ const dy = y - centerY;
332
+ const angleRad = -this.rotation * Math.PI / 180;
333
+ const rotatedX = dx * Math.cos(angleRad) - dy * Math.sin(angleRad) + centerX;
334
+ const rotatedY = dx * Math.sin(angleRad) + dy * Math.cos(angleRad) + centerY;
335
+ return rotatedX >= bbX && rotatedX <= bbX + this.boundingBox.width && rotatedY >= bbY && rotatedY <= bbY + this.boundingBox.height;
336
+ }
337
+ isNearAnchor(x, y) {
338
+ if (!this.isSelected) return null;
339
+ const buffer = 10;
340
+ const anchorSize = 10 / currentZoom;
341
+ const ox = this._moveOffsetX || 0;
342
+ const oy = this._moveOffsetY || 0;
343
+ const centerX = this.boundingBox.x + ox + this.boundingBox.width / 2;
344
+ const centerY = this.boundingBox.y + oy + this.boundingBox.height / 2;
345
+ const angleRad = -this.rotation * Math.PI / 180;
346
+ const dx = x - centerX;
347
+ const dy = y - centerY;
348
+ const localX = dx * Math.cos(angleRad) - dy * Math.sin(angleRad) + centerX;
349
+ const localY = dx * Math.sin(angleRad) + dy * Math.cos(angleRad) + centerY;
350
+ const expandedX = this.boundingBox.x + ox - this.selectionPadding;
351
+ const expandedY = this.boundingBox.y + oy - this.selectionPadding;
352
+ const expandedWidth = this.boundingBox.width + 2 * this.selectionPadding;
353
+ const expandedHeight = this.boundingBox.height + 2 * this.selectionPadding;
354
+ const anchorPositions = [
355
+ { x: expandedX, y: expandedY },
356
+ // top-left
357
+ { x: expandedX + expandedWidth, y: expandedY },
358
+ // top-right
359
+ { x: expandedX, y: expandedY + expandedHeight },
360
+ // bottom-left
361
+ { x: expandedX + expandedWidth, y: expandedY + expandedHeight },
362
+ // bottom-right
363
+ { x: expandedX + expandedWidth / 2, y: expandedY },
364
+ // top-center
365
+ { x: expandedX + expandedWidth / 2, y: expandedY + expandedHeight },
366
+ // bottom-center
367
+ { x: expandedX, y: expandedY + expandedHeight / 2 },
368
+ // left-center
369
+ { x: expandedX + expandedWidth, y: expandedY + expandedHeight / 2 }
370
+ // right-center
371
+ ];
372
+ for (let i = 0; i < anchorPositions.length; i++) {
373
+ const anchor = anchorPositions[i];
374
+ const distance = Math.sqrt(Math.pow(localX - anchor.x, 2) + Math.pow(localY - anchor.y, 2));
375
+ if (distance <= anchorSize / 2 + buffer) {
376
+ return { type: "resize", index: i };
377
+ }
378
+ }
379
+ if (this.rotationAnchor) {
380
+ const rotationX = parseFloat(this.rotationAnchor.getAttribute("cx"));
381
+ const rotationY = parseFloat(this.rotationAnchor.getAttribute("cy"));
382
+ const distance = Math.sqrt(Math.pow(x - rotationX, 2) + Math.pow(y - rotationY, 2));
383
+ if (distance <= anchorSize / 2 + buffer) {
384
+ return { type: "rotate" };
385
+ }
386
+ }
387
+ return null;
388
+ }
389
+ rotate(angle) {
390
+ this.rotation = angle;
391
+ if (this.isSelected && this.anchors.length > 0) {
392
+ const cursors = ["nw-resize", "ne-resize", "sw-resize", "se-resize", "n-resize", "s-resize", "w-resize", "e-resize"];
393
+ this.anchors.forEach((anchor, i) => {
394
+ if (anchor) {
395
+ const originalCursor = cursors[i];
396
+ const rotatedCursor = this.getRotatedCursor(originalCursor, this.rotation);
397
+ anchor.style.cursor = rotatedCursor;
398
+ }
399
+ });
400
+ }
401
+ }
402
+ addAnchors() {
403
+ const anchorSize = 10 / currentZoom;
404
+ const anchorStrokeWidth = 2 / currentZoom;
405
+ const expandedX = this.boundingBox.x - this.selectionPadding;
406
+ const expandedY = this.boundingBox.y - this.selectionPadding;
407
+ const expandedWidth = this.boundingBox.width + 2 * this.selectionPadding;
408
+ const expandedHeight = this.boundingBox.height + 2 * this.selectionPadding;
409
+ const positions = [
410
+ { x: expandedX, y: expandedY, cursor: "nw-resize" },
411
+ // top-left
412
+ { x: expandedX + expandedWidth, y: expandedY, cursor: "ne-resize" },
413
+ // top-right
414
+ { x: expandedX, y: expandedY + expandedHeight, cursor: "sw-resize" },
415
+ // bottom-left
416
+ { x: expandedX + expandedWidth, y: expandedY + expandedHeight, cursor: "se-resize" },
417
+ // bottom-right
418
+ { x: expandedX + expandedWidth / 2, y: expandedY, cursor: "n-resize" },
419
+ // top-center
420
+ { x: expandedX + expandedWidth / 2, y: expandedY + expandedHeight, cursor: "s-resize" },
421
+ // bottom-center
422
+ { x: expandedX, y: expandedY + expandedHeight / 2, cursor: "w-resize" },
423
+ // left-center
424
+ { x: expandedX + expandedWidth, y: expandedY + expandedHeight / 2, cursor: "e-resize" }
425
+ // right-center
426
+ ];
427
+ positions.forEach((pos, i) => {
428
+ const anchor = document.createElementNS("http://www.w3.org/2000/svg", "rect");
429
+ anchor.setAttribute("x", pos.x - anchorSize / 2);
430
+ anchor.setAttribute("y", pos.y - anchorSize / 2);
431
+ anchor.setAttribute("width", anchorSize);
432
+ anchor.setAttribute("height", anchorSize);
433
+ anchor.setAttribute("class", "anchor");
434
+ anchor.setAttribute("data-index", i);
435
+ anchor.setAttribute("fill", "#121212");
436
+ anchor.setAttribute("stroke", "#5B57D1");
437
+ anchor.setAttribute("stroke-width", anchorStrokeWidth);
438
+ anchor.setAttribute("vector-effect", "non-scaling-stroke");
439
+ const rotatedCursor = this.getRotatedCursor(pos.cursor, this.rotation);
440
+ anchor.style.cursor = rotatedCursor;
441
+ anchor.style.pointerEvents = "all";
442
+ this.group.appendChild(anchor);
443
+ this.anchors[i] = anchor;
444
+ });
445
+ const rotationAnchorDistance = 30 / currentZoom;
446
+ const rotationX = expandedX + expandedWidth / 2;
447
+ const rotationY = expandedY - rotationAnchorDistance;
448
+ const rotationAnchor = document.createElementNS("http://www.w3.org/2000/svg", "circle");
449
+ rotationAnchor.setAttribute("cx", rotationX);
450
+ rotationAnchor.setAttribute("cy", rotationY);
451
+ rotationAnchor.setAttribute("r", anchorSize / 2);
452
+ rotationAnchor.setAttribute("fill", "#121212");
453
+ rotationAnchor.setAttribute("stroke", "#5B57D1");
454
+ rotationAnchor.setAttribute("stroke-width", anchorStrokeWidth);
455
+ rotationAnchor.setAttribute("vector-effect", "non-scaling-stroke");
456
+ rotationAnchor.setAttribute("class", "rotation-anchor");
457
+ rotationAnchor.style.cursor = "grab";
458
+ rotationAnchor.style.pointerEvents = "all";
459
+ this.group.appendChild(rotationAnchor);
460
+ this.rotationAnchor = rotationAnchor;
461
+ const outlinePoints = [
462
+ [expandedX, expandedY],
463
+ [expandedX + expandedWidth, expandedY],
464
+ [expandedX + expandedWidth, expandedY + expandedHeight],
465
+ [expandedX, expandedY + expandedHeight],
466
+ [expandedX, expandedY]
467
+ ];
468
+ const outline = document.createElementNS("http://www.w3.org/2000/svg", "polyline");
469
+ outline.setAttribute("points", outlinePoints.map((p) => p.join(",")).join(" "));
470
+ outline.setAttribute("fill", "none");
471
+ outline.setAttribute("stroke", "#5B57D1");
472
+ outline.setAttribute("stroke-width", 1.5);
473
+ outline.setAttribute("vector-effect", "non-scaling-stroke");
474
+ outline.setAttribute("stroke-dasharray", "4 2");
475
+ outline.setAttribute("style", "pointer-events: none;");
476
+ this.group.appendChild(outline);
477
+ this.selectionOutline = outline;
478
+ const rotationLine = document.createElementNS("http://www.w3.org/2000/svg", "line");
479
+ rotationLine.setAttribute("x1", rotationX);
480
+ rotationLine.setAttribute("y1", rotationY);
481
+ rotationLine.setAttribute("x2", expandedX + expandedWidth / 2);
482
+ rotationLine.setAttribute("y2", expandedY);
483
+ rotationLine.setAttribute("stroke", "#5B57D1");
484
+ rotationLine.setAttribute("stroke-width", 1);
485
+ rotationLine.setAttribute("vector-effect", "non-scaling-stroke");
486
+ rotationLine.setAttribute("stroke-dasharray", "2 2");
487
+ rotationLine.setAttribute("style", "pointer-events: none;");
488
+ this.group.appendChild(rotationLine);
489
+ this.rotationLine = rotationLine;
490
+ disableAllSideBars();
491
+ paintBrushSideBar.classList.remove("hidden");
492
+ if (window.__showSidebarForShape) window.__showSidebarForShape("freehandStroke");
493
+ this.updateSidebar();
494
+ }
495
+ getRotatedCursor(direction, angle) {
496
+ const normalizedAngle = (angle % 360 + 360) % 360;
497
+ const cursors = ["n-resize", "ne-resize", "e-resize", "se-resize", "s-resize", "sw-resize", "w-resize", "nw-resize"];
498
+ const directionMap = {
499
+ "n-resize": 0,
500
+ "ne-resize": 1,
501
+ "e-resize": 2,
502
+ "se-resize": 3,
503
+ "s-resize": 4,
504
+ "sw-resize": 5,
505
+ "w-resize": 6,
506
+ "nw-resize": 7
507
+ };
508
+ if (!(direction in directionMap)) return direction;
509
+ const rotationSteps = Math.round(normalizedAngle / 45);
510
+ const currentIndex = directionMap[direction];
511
+ const newIndex = (currentIndex + rotationSteps) % 8;
512
+ return cursors[newIndex];
513
+ }
514
+ updatePosition(anchorIndex, newX, newY) {
515
+ const centerX = this.boundingBox.x + this.boundingBox.width / 2;
516
+ const centerY = this.boundingBox.y + this.boundingBox.height / 2;
517
+ const angleRad = -this.rotation * Math.PI / 180;
518
+ const dx = newX - centerX;
519
+ const dy = newY - centerY;
520
+ const localX = dx * Math.cos(angleRad) - dy * Math.sin(angleRad) + centerX;
521
+ const localY = dx * Math.sin(angleRad) + dy * Math.cos(angleRad) + centerY;
522
+ const expandedX = this.boundingBox.x - this.selectionPadding;
523
+ const expandedY = this.boundingBox.y - this.selectionPadding;
524
+ const expandedWidth = this.boundingBox.width + 2 * this.selectionPadding;
525
+ const expandedHeight = this.boundingBox.height + 2 * this.selectionPadding;
526
+ let scaleX = 1, scaleY = 1;
527
+ switch (anchorIndex) {
528
+ case 0:
529
+ scaleX = (centerX - localX) / (centerX - expandedX);
530
+ scaleY = (centerY - localY) / (centerY - expandedY);
531
+ break;
532
+ case 1:
533
+ scaleX = (localX - centerX) / (expandedX + expandedWidth - centerX);
534
+ scaleY = (centerY - localY) / (centerY - expandedY);
535
+ break;
536
+ case 2:
537
+ scaleX = (centerX - localX) / (centerX - expandedX);
538
+ scaleY = (localY - centerY) / (expandedY + expandedHeight - centerY);
539
+ break;
540
+ case 3:
541
+ scaleX = (localX - centerX) / (expandedX + expandedWidth - centerX);
542
+ scaleY = (localY - centerY) / (expandedY + expandedHeight - centerY);
543
+ break;
544
+ case 4:
545
+ scaleY = (centerY - localY) / (centerY - expandedY);
546
+ break;
547
+ case 5:
548
+ scaleY = (localY - centerY) / (expandedY + expandedHeight - centerY);
549
+ break;
550
+ case 6:
551
+ scaleX = (centerX - localX) / (centerX - expandedX);
552
+ break;
553
+ case 7:
554
+ scaleX = (localX - centerX) / (expandedX + expandedWidth - centerX);
555
+ break;
556
+ }
557
+ scaleX = Math.max(0.1, Math.abs(scaleX));
558
+ scaleY = Math.max(0.1, Math.abs(scaleY));
559
+ this.points = this.points.map((point) => {
560
+ const relX = point[0] - centerX;
561
+ const relY = point[1] - centerY;
562
+ return [
563
+ centerX + relX * scaleX,
564
+ centerY + relY * scaleY,
565
+ point[2] || 0.5
566
+ ];
567
+ });
568
+ this.draw();
569
+ }
570
+ };
571
+
572
+ export {
573
+ FreehandStroke
574
+ };
575
+ //# sourceMappingURL=chunk-YKJUBFHE.js.map