@elixpo/lixsketch 5.6.4 → 5.6.6

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 (160) hide show
  1. package/dist/mcp/index.js +5 -1
  2. package/dist/mcp/index.js.map +2 -2
  3. package/dist/mcp/stdio.js +5 -1
  4. package/dist/mcp/stdio.js.map +2 -2
  5. package/dist/react/{AIRenderer-SRF7LU5K.js → AIRenderer-R5M2CIDO.js} +13 -6
  6. package/dist/react/AIRenderer-R5M2CIDO.js.map +7 -0
  7. package/dist/react/Circle-HMM5PKJ2.js +10 -0
  8. package/dist/react/{CopyPaste-YUDZ6DHY.js → CopyPaste-P6G35CXO.js} +11 -10
  9. package/dist/react/{CopyPaste-YUDZ6DHY.js.map → CopyPaste-P6G35CXO.js.map} +1 -1
  10. package/dist/react/{EventDispatcher-7LAK7GQ7.js → EventDispatcher-M5B4EWHJ.js} +216 -63
  11. package/dist/react/EventDispatcher-M5B4EWHJ.js.map +7 -0
  12. package/dist/react/Frame-QEUEYITT.js +11 -0
  13. package/dist/react/{FreehandStroke-U6GYZOM4.js → FreehandStroke-WXIJKD5I.js} +2 -2
  14. package/dist/react/{GraphEngine-MM6ALA2N.js → GraphEngine-XH3RPYO4.js} +9 -2
  15. package/dist/react/GraphEngine-XH3RPYO4.js.map +7 -0
  16. package/dist/react/IconShape-NHJE4YBY.js +10 -0
  17. package/dist/react/ImageShape-33VCMSDP.js +10 -0
  18. package/dist/react/Line-A6FE232Q.js +10 -0
  19. package/dist/react/{LixScriptParser-JYWJX375.js → LixScriptParser-YS3ZOTGS.js} +6 -2
  20. package/dist/react/LixScriptParser-YS3ZOTGS.js.map +7 -0
  21. package/dist/react/{MermaidFlowchartRenderer-PQEC4TMI.js → MermaidFlowchartRenderer-EL3Q2MNW.js} +6 -2
  22. package/dist/react/MermaidFlowchartRenderer-EL3Q2MNW.js.map +7 -0
  23. package/dist/react/MermaidSequenceParser-DBYXW23P.js +8 -0
  24. package/dist/react/{MermaidSequenceRenderer-KCUHO7UI.js → MermaidSequenceRenderer-K4KHHEVC.js} +10 -3
  25. package/dist/react/MermaidSequenceRenderer-K4KHHEVC.js.map +7 -0
  26. package/dist/react/{MermaidStructuredRenderer-W7UKN7BM.js → MermaidStructuredRenderer-6LN3WISP.js} +7 -2
  27. package/dist/react/MermaidStructuredRenderer-6LN3WISP.js.map +7 -0
  28. package/dist/react/Rectangle-4U4CD2ER.js +10 -0
  29. package/dist/react/{SceneSerializer-SG6SJN5X.js → SceneSerializer-JEYTYXZQ.js} +120 -46
  30. package/dist/react/SceneSerializer-JEYTYXZQ.js.map +7 -0
  31. package/dist/react/{Selection-2TSH3J6P.js → Selection-23MOLHEI.js} +4 -4
  32. package/dist/react/{SketchEngine-UD2XV2KT.js → SketchEngine-OPJON7TD.js} +26 -26
  33. package/dist/react/{UndoRedo-IN4SXDG6.js → UndoRedo-QVNRI5OB.js} +8 -2
  34. package/dist/react/{arrowTool-OYDWJGQK.js → arrowTool-VH3PJPDM.js} +3 -3
  35. package/dist/react/{chunk-LSHZYN77.js → chunk-2DZHO67H.js} +2 -2
  36. package/dist/react/{chunk-XOWNCYE4.js → chunk-5NHJJJBZ.js} +3 -3
  37. package/dist/react/chunk-6AIFXYV6.js +31 -0
  38. package/dist/react/chunk-6AIFXYV6.js.map +7 -0
  39. package/dist/react/{chunk-K5RY4EV7.js → chunk-6GPWSPJF.js} +9 -5
  40. package/dist/react/{chunk-K5RY4EV7.js.map → chunk-6GPWSPJF.js.map} +2 -2
  41. package/dist/react/{chunk-ASBC5G35.js → chunk-B6CYUZC4.js} +2 -2
  42. package/dist/react/{chunk-OP56XPWF.js → chunk-DNFPTVIP.js} +2 -2
  43. package/dist/react/{chunk-XYUFEIZR.js → chunk-DWEFPKDO.js} +2 -2
  44. package/dist/react/{chunk-GQUZPON4.js → chunk-F56XUE5S.js} +4 -4
  45. package/dist/react/{chunk-7CM5XQDN.js → chunk-G3DJQY65.js} +20 -36
  46. package/dist/react/chunk-G3DJQY65.js.map +7 -0
  47. package/dist/react/{chunk-LJR7EVW2.js → chunk-JEGLUNBC.js} +3 -3
  48. package/dist/react/{chunk-6GPEWDRB.js → chunk-M7GAB24F.js} +52 -6
  49. package/dist/react/chunk-M7GAB24F.js.map +7 -0
  50. package/dist/react/{chunk-VQVEPJSA.js → chunk-MHV5P6TS.js} +2 -1
  51. package/dist/react/chunk-MHV5P6TS.js.map +7 -0
  52. package/dist/react/{chunk-PXFPPO3R.js → chunk-NH3NVSWT.js} +18 -2
  53. package/dist/react/chunk-NH3NVSWT.js.map +7 -0
  54. package/dist/react/{chunk-XFL4R5ZF.js → chunk-OK4ROGMM.js} +16 -35
  55. package/dist/react/chunk-OK4ROGMM.js.map +7 -0
  56. package/dist/react/{chunk-APTOPAOY.js → chunk-SSFEN5CY.js} +3 -3
  57. package/dist/react/{chunk-RGMDWLRJ.js → chunk-UEL2G6D2.js} +3 -3
  58. package/dist/react/{chunk-M6TMN76K.js → chunk-ULPIHUVC.js} +3 -3
  59. package/dist/react/{chunk-FETTJLCV.js → chunk-VZPH7OAM.js} +14 -22
  60. package/dist/react/chunk-VZPH7OAM.js.map +7 -0
  61. package/dist/react/{chunk-TKXKJNTX.js → chunk-W7XUY4GR.js} +16 -19
  62. package/dist/react/chunk-W7XUY4GR.js.map +7 -0
  63. package/dist/react/{chunk-EOURRJ55.js → chunk-XSE4HQ2D.js} +2 -2
  64. package/dist/react/{chunk-K5BG5MHY.js → chunk-ZNZAICWY.js} +36 -60
  65. package/dist/react/chunk-ZNZAICWY.js.map +7 -0
  66. package/dist/react/{circleTool-LDKGZALF.js → circleTool-2UODOBCV.js} +4 -4
  67. package/dist/react/{codeTool-4YZO5W7J.js → codeTool-F4RV7SAI.js} +4 -4
  68. package/dist/react/{frameTool-MXNJEXQQ.js → frameTool-NTGA5BI6.js} +4 -4
  69. package/dist/react/{freehandTool-26D3FOOX.js → freehandTool-L55EUB5C.js} +4 -4
  70. package/dist/react/{iconTool-46O5VEUX.js → iconTool-KQQBLGR3.js} +4 -4
  71. package/dist/react/{imageTool-P2RWZRHO.js → imageTool-LDJ45AD7.js} +4 -4
  72. package/dist/react/index.js +148 -37
  73. package/dist/react/index.js.map +4 -4
  74. package/dist/react/{lineTool-ZMR27U5L.js → lineTool-CSAFD7PW.js} +4 -4
  75. package/dist/react/{rectangleTool-EJKK6UTX.js → rectangleTool-YPD2SIIZ.js} +4 -4
  76. package/dist/react/{textTool-S4XXZHZH.js → textTool-VQHMWEFJ.js} +5 -5
  77. package/package.json +5 -2
  78. package/src/core/AIRenderer.js +8 -2
  79. package/src/core/CanvasSpace.js +28 -0
  80. package/src/core/GraphRenderer.js +8 -3
  81. package/src/core/LixScriptParser.js +7 -1
  82. package/src/core/MermaidFlowchartRenderer.js +5 -1
  83. package/src/core/MermaidSequenceParser.js +1 -0
  84. package/src/core/MermaidSequenceRenderer.js +8 -3
  85. package/src/core/MermaidStructuredRenderer.js +6 -1
  86. package/src/core/SceneSerializer.js +121 -38
  87. package/src/core/Selection.js +12 -2
  88. package/src/core/UndoRedo.js +59 -5
  89. package/src/react/components/AppMenu.jsx +20 -19
  90. package/src/react/components/canvas/CanvasRulers.jsx +8 -13
  91. package/src/react/components/canvas/SVGCanvas.jsx +2 -1
  92. package/src/react/components/canvas/rulerUnits.js +28 -0
  93. package/src/react/components/sidebars/PaintBucketSidebar.jsx +1 -1
  94. package/src/react/store/useSketchStore.js +24 -3
  95. package/src/react/store/useUIStore.js +24 -1
  96. package/src/react/utils/canvasBackgrounds.js +29 -0
  97. package/src/shapes/Circle.js +12 -19
  98. package/src/shapes/Frame.js +38 -69
  99. package/src/shapes/FreehandStroke.js +24 -1
  100. package/src/shapes/Rectangle.js +10 -22
  101. package/src/tools/circleTool.js +3 -27
  102. package/src/tools/paintBucketTool.js +72 -15
  103. package/src/tools/rectangleTool.js +7 -23
  104. package/src/tools/shapeRecognition.js +270 -0
  105. package/src/tools/shapeRecognitionTool.js +36 -183
  106. package/dist/react/AIRenderer-SRF7LU5K.js.map +0 -7
  107. package/dist/react/Circle-6POZCUZD.js +0 -9
  108. package/dist/react/EventDispatcher-7LAK7GQ7.js.map +0 -7
  109. package/dist/react/Frame-7D2MXCFF.js +0 -11
  110. package/dist/react/GraphEngine-MM6ALA2N.js.map +0 -7
  111. package/dist/react/IconShape-ZADX7Y33.js +0 -10
  112. package/dist/react/ImageShape-UJQN2HI7.js +0 -10
  113. package/dist/react/Line-APEULMHE.js +0 -10
  114. package/dist/react/LixScriptParser-JYWJX375.js.map +0 -7
  115. package/dist/react/MermaidFlowchartRenderer-PQEC4TMI.js.map +0 -7
  116. package/dist/react/MermaidSequenceParser-UKG2NSZY.js +0 -8
  117. package/dist/react/MermaidSequenceRenderer-KCUHO7UI.js.map +0 -7
  118. package/dist/react/MermaidStructuredRenderer-W7UKN7BM.js.map +0 -7
  119. package/dist/react/Rectangle-S7XUKWYD.js +0 -9
  120. package/dist/react/SceneSerializer-SG6SJN5X.js.map +0 -7
  121. package/dist/react/chunk-6GPEWDRB.js.map +0 -7
  122. package/dist/react/chunk-7CM5XQDN.js.map +0 -7
  123. package/dist/react/chunk-FETTJLCV.js.map +0 -7
  124. package/dist/react/chunk-K5BG5MHY.js.map +0 -7
  125. package/dist/react/chunk-PXFPPO3R.js.map +0 -7
  126. package/dist/react/chunk-TKXKJNTX.js.map +0 -7
  127. package/dist/react/chunk-VQVEPJSA.js.map +0 -7
  128. package/dist/react/chunk-XFL4R5ZF.js.map +0 -7
  129. /package/dist/react/{Circle-6POZCUZD.js.map → Circle-HMM5PKJ2.js.map} +0 -0
  130. /package/dist/react/{Frame-7D2MXCFF.js.map → Frame-QEUEYITT.js.map} +0 -0
  131. /package/dist/react/{FreehandStroke-U6GYZOM4.js.map → FreehandStroke-WXIJKD5I.js.map} +0 -0
  132. /package/dist/react/{IconShape-ZADX7Y33.js.map → IconShape-NHJE4YBY.js.map} +0 -0
  133. /package/dist/react/{ImageShape-UJQN2HI7.js.map → ImageShape-33VCMSDP.js.map} +0 -0
  134. /package/dist/react/{Line-APEULMHE.js.map → Line-A6FE232Q.js.map} +0 -0
  135. /package/dist/react/{MermaidSequenceParser-UKG2NSZY.js.map → MermaidSequenceParser-DBYXW23P.js.map} +0 -0
  136. /package/dist/react/{Rectangle-S7XUKWYD.js.map → Rectangle-4U4CD2ER.js.map} +0 -0
  137. /package/dist/react/{Selection-2TSH3J6P.js.map → Selection-23MOLHEI.js.map} +0 -0
  138. /package/dist/react/{SketchEngine-UD2XV2KT.js.map → SketchEngine-OPJON7TD.js.map} +0 -0
  139. /package/dist/react/{UndoRedo-IN4SXDG6.js.map → UndoRedo-QVNRI5OB.js.map} +0 -0
  140. /package/dist/react/{arrowTool-OYDWJGQK.js.map → arrowTool-VH3PJPDM.js.map} +0 -0
  141. /package/dist/react/{chunk-LSHZYN77.js.map → chunk-2DZHO67H.js.map} +0 -0
  142. /package/dist/react/{chunk-XOWNCYE4.js.map → chunk-5NHJJJBZ.js.map} +0 -0
  143. /package/dist/react/{chunk-ASBC5G35.js.map → chunk-B6CYUZC4.js.map} +0 -0
  144. /package/dist/react/{chunk-OP56XPWF.js.map → chunk-DNFPTVIP.js.map} +0 -0
  145. /package/dist/react/{chunk-XYUFEIZR.js.map → chunk-DWEFPKDO.js.map} +0 -0
  146. /package/dist/react/{chunk-GQUZPON4.js.map → chunk-F56XUE5S.js.map} +0 -0
  147. /package/dist/react/{chunk-LJR7EVW2.js.map → chunk-JEGLUNBC.js.map} +0 -0
  148. /package/dist/react/{chunk-APTOPAOY.js.map → chunk-SSFEN5CY.js.map} +0 -0
  149. /package/dist/react/{chunk-RGMDWLRJ.js.map → chunk-UEL2G6D2.js.map} +0 -0
  150. /package/dist/react/{chunk-M6TMN76K.js.map → chunk-ULPIHUVC.js.map} +0 -0
  151. /package/dist/react/{chunk-EOURRJ55.js.map → chunk-XSE4HQ2D.js.map} +0 -0
  152. /package/dist/react/{circleTool-LDKGZALF.js.map → circleTool-2UODOBCV.js.map} +0 -0
  153. /package/dist/react/{codeTool-4YZO5W7J.js.map → codeTool-F4RV7SAI.js.map} +0 -0
  154. /package/dist/react/{frameTool-MXNJEXQQ.js.map → frameTool-NTGA5BI6.js.map} +0 -0
  155. /package/dist/react/{freehandTool-26D3FOOX.js.map → freehandTool-L55EUB5C.js.map} +0 -0
  156. /package/dist/react/{iconTool-46O5VEUX.js.map → iconTool-KQQBLGR3.js.map} +0 -0
  157. /package/dist/react/{imageTool-P2RWZRHO.js.map → imageTool-LDJ45AD7.js.map} +0 -0
  158. /package/dist/react/{lineTool-ZMR27U5L.js.map → lineTool-CSAFD7PW.js.map} +0 -0
  159. /package/dist/react/{rectangleTool-EJKK6UTX.js.map → rectangleTool-YPD2SIIZ.js.map} +0 -0
  160. /package/dist/react/{textTool-S4XXZHZH.js.map → textTool-VQHMWEFJ.js.map} +0 -0
@@ -4,32 +4,32 @@ import {
4
4
  handleTextMouseDown,
5
5
  handleTextMouseMove,
6
6
  handleTextMouseUp
7
- } from "./chunk-GQUZPON4.js";
7
+ } from "./chunk-F56XUE5S.js";
8
8
  import {
9
9
  handleCodeMouseDown,
10
10
  handleCodeMouseMove,
11
11
  handleCodeMouseUp
12
- } from "./chunk-M6TMN76K.js";
12
+ } from "./chunk-ULPIHUVC.js";
13
13
  import {
14
14
  handleMouseDownImage,
15
15
  handleMouseMoveImage,
16
16
  handleMouseUpImage
17
- } from "./chunk-LJR7EVW2.js";
17
+ } from "./chunk-JEGLUNBC.js";
18
18
  import {
19
19
  handleMouseDownIcon,
20
20
  handleMouseMoveIcon,
21
21
  handleMouseUpIcon
22
- } from "./chunk-APTOPAOY.js";
22
+ } from "./chunk-SSFEN5CY.js";
23
23
  import {
24
24
  handleMouseDown as handleMouseDown4,
25
25
  handleMouseMove as handleMouseMove4,
26
26
  handleMouseUp as handleMouseUp4
27
- } from "./chunk-LSHZYN77.js";
27
+ } from "./chunk-2DZHO67H.js";
28
28
  import {
29
29
  handleMouseDown as handleMouseDown5,
30
30
  handleMouseMove as handleMouseMove5,
31
31
  handleMouseUp as handleMouseUp5
32
- } from "./chunk-XOWNCYE4.js";
32
+ } from "./chunk-5NHJJJBZ.js";
33
33
  import {
34
34
  handleMultiSelectionMouseDown,
35
35
  handleMultiSelectionMouseMove,
@@ -37,25 +37,25 @@ import {
37
37
  isMultiSelecting,
38
38
  multiSelection,
39
39
  removeMultiSelectionRect
40
- } from "./chunk-K5RY4EV7.js";
40
+ } from "./chunk-6GPWSPJF.js";
41
41
  import {
42
42
  Frame
43
- } from "./chunk-K5BG5MHY.js";
43
+ } from "./chunk-ZNZAICWY.js";
44
44
  import {
45
45
  handleMouseDownRect,
46
46
  handleMouseMoveRect,
47
47
  handleMouseUpRect
48
- } from "./chunk-7CM5XQDN.js";
48
+ } from "./chunk-G3DJQY65.js";
49
49
  import {
50
50
  handleMouseDown as handleMouseDown2,
51
51
  handleMouseMove as handleMouseMove2,
52
52
  handleMouseUp as handleMouseUp2
53
- } from "./chunk-XFL4R5ZF.js";
53
+ } from "./chunk-OK4ROGMM.js";
54
54
  import {
55
55
  handleMouseDown as handleMouseDown3,
56
56
  handleMouseMove as handleMouseMove3,
57
57
  handleMouseUp as handleMouseUp3
58
- } from "./chunk-RGMDWLRJ.js";
58
+ } from "./chunk-UEL2G6D2.js";
59
59
  import {
60
60
  normalizeWebEmbedUrl
61
61
  } from "./chunk-QZI4UIE5.js";
@@ -65,35 +65,16 @@ import {
65
65
  handleMouseDown,
66
66
  handleMouseMove,
67
67
  handleMouseUp
68
- } from "./chunk-XYUFEIZR.js";
68
+ } from "./chunk-DWEFPKDO.js";
69
69
  import {
70
70
  pushCreateAction,
71
71
  pushFrameAttachmentAction,
72
72
  pushOptionsChangeAction
73
- } from "./chunk-6GPEWDRB.js";
73
+ } from "./chunk-M7GAB24F.js";
74
74
 
75
- // src/tools/shapeRecognitionTool.js
76
- var SVG_NS = "http://www.w3.org/2000/svg";
77
- var MAX_POINTS = 96;
78
- var MIN_SCREEN_SAMPLE_PX = 4;
79
- var MIN_SHAPE_SIZE = 8;
80
- var drawing = false;
81
- var points = [];
82
- var previewPath = null;
83
- var previewFrame = 0;
84
- var latestPrediction = null;
85
- function getThemeStroke() {
86
- return document.body?.classList.contains("theme-dark") ? "#f7f4ff" : "#211a33";
87
- }
88
- function getSVGPoint(event) {
89
- const viewBox = svg.viewBox.baseVal;
90
- const rect = svg.getBoundingClientRect();
91
- return {
92
- x: viewBox.x + (event.clientX - rect.left) / rect.width * viewBox.width,
93
- y: viewBox.y + (event.clientY - rect.top) / rect.height * viewBox.height,
94
- pressure: event.pressure || 0.5
95
- };
96
- }
75
+ // src/tools/shapeRecognition.js
76
+ var MIN_SHAPE_SIZE2 = 8;
77
+ var MAX_GESTURE_POINTS = 96;
97
78
  function distance(a, b) {
98
79
  return Math.hypot(b.x - a.x, b.y - a.y);
99
80
  }
@@ -119,8 +100,8 @@ function orientedBounds(sample, angle) {
119
100
  x: centerU * cos - centerV * sin,
120
101
  y: centerU * sin + centerV * cos
121
102
  },
122
- width: Math.max(MIN_SHAPE_SIZE, maxU - minU),
123
- height: Math.max(MIN_SHAPE_SIZE, maxV - minV),
103
+ width: Math.max(MIN_SHAPE_SIZE2, maxU - minU),
104
+ height: Math.max(MIN_SHAPE_SIZE2, maxV - minV),
124
105
  angle,
125
106
  minU,
126
107
  maxU,
@@ -164,7 +145,73 @@ function rectangleAngle(sample) {
164
145
  }
165
146
  return Math.atan2(y, x) / 4;
166
147
  }
148
+ function pointToSegmentDistance(point, start2, end) {
149
+ const dx = end.x - start2.x;
150
+ const dy = end.y - start2.y;
151
+ const lengthSquared = dx * dx + dy * dy;
152
+ if (lengthSquared === 0) return distance(point, start2);
153
+ const amount = Math.max(0, Math.min(
154
+ 1,
155
+ ((point.x - start2.x) * dx + (point.y - start2.y) * dy) / lengthSquared
156
+ ));
157
+ return Math.hypot(point.x - (start2.x + dx * amount), point.y - (start2.y + dy * amount));
158
+ }
159
+ function polygonError(sample, vertices, scale) {
160
+ let total = 0;
161
+ for (const point of sample) {
162
+ let nearest = Infinity;
163
+ for (let index = 0; index < vertices.length; index += 1) {
164
+ nearest = Math.min(nearest, pointToSegmentDistance(
165
+ point,
166
+ vertices[index],
167
+ vertices[(index + 1) % vertices.length]
168
+ ));
169
+ }
170
+ total += nearest;
171
+ }
172
+ return total / sample.length / Math.max(scale, MIN_SHAPE_SIZE2);
173
+ }
174
+ function polygonVertices(bounds, kind) {
175
+ const cos = Math.cos(bounds.angle);
176
+ const sin = Math.sin(bounds.angle);
177
+ const toWorld = (u, v) => ({
178
+ x: u * cos - v * sin,
179
+ y: u * sin + v * cos
180
+ });
181
+ const centerU = (bounds.minU + bounds.maxU) / 2;
182
+ const centerV = (bounds.minV + bounds.maxV) / 2;
183
+ const local = kind === "triangle" ? [[centerU, bounds.minV], [bounds.maxU, bounds.maxV], [bounds.minU, bounds.maxV]] : kind === "diamond" ? [[centerU, bounds.minV], [bounds.maxU, centerV], [centerU, bounds.maxV], [bounds.minU, centerV]] : [[bounds.minU, bounds.minV], [bounds.maxU, bounds.minV], [bounds.maxU, bounds.maxV], [bounds.minU, bounds.maxV]];
184
+ return local.map(([u, v]) => toWorld(u, v));
185
+ }
186
+ function trianglePrediction(sample, pathLength) {
187
+ const baseAngle = principalAngle(sample);
188
+ let best = null;
189
+ for (let turn = 0; turn < 4; turn += 1) {
190
+ const bounds = orientedBounds(sample, baseAngle + turn * Math.PI / 2);
191
+ const vertices = polygonVertices(bounds, "triangle");
192
+ const error = polygonError(sample, vertices, Math.hypot(bounds.width, bounds.height));
193
+ if (!best || error < best.error) best = { bounds, vertices, error };
194
+ }
195
+ if (!best || best.error > 0.045) return null;
196
+ return {
197
+ type: "triangle",
198
+ ...best.bounds,
199
+ vertices: best.vertices,
200
+ pathLength
201
+ };
202
+ }
203
+ function diamondPrediction(sample, pathLength) {
204
+ const bounds = orientedBounds(sample, principalAngle(sample));
205
+ const vertices = polygonVertices(bounds, "diamond");
206
+ const error = polygonError(sample, vertices, Math.hypot(bounds.width, bounds.height));
207
+ if (error > 0.035) return null;
208
+ return { type: "diamond", ...bounds, vertices, pathLength };
209
+ }
167
210
  function closedShapePrediction(sample, pathLength) {
211
+ const triangle = trianglePrediction(sample, pathLength);
212
+ if (triangle) return triangle;
213
+ const fittedDiamond = diamondPrediction(sample, pathLength);
214
+ if (fittedDiamond) return fittedDiamond;
168
215
  const rectBounds = orientedBounds(sample, rectangleAngle(sample));
169
216
  const ellipseBounds = orientedBounds(sample, principalAngle(sample));
170
217
  let rectangleError = 0;
@@ -197,14 +244,29 @@ function closedShapePrediction(sample, pathLength) {
197
244
  return { type: "freehand", points: sample.map((point) => ({ ...point })), pathLength };
198
245
  }
199
246
  const bounds = rectangle ? rectBounds : ellipseBounds;
247
+ const normalizedAngle = Math.abs(Math.atan2(Math.sin(rectBounds.angle * 2), Math.cos(rectBounds.angle * 2)) / 2);
248
+ const diamond = rectangle && normalizedAngle >= Math.PI / 7;
200
249
  return {
201
- type: rectangle ? "rectangle" : "circle",
250
+ type: diamond ? "diamond" : rectangle ? "rectangle" : "circle",
202
251
  ...bounds,
252
+ ...diamond ? { vertices: polygonVertices(rectBounds, "rectangle") } : {},
203
253
  pathLength
204
254
  };
205
255
  }
206
- function predictDrawnShape(sample) {
207
- if (!Array.isArray(sample) || sample.length < 2) return null;
256
+ function capGestureSample(sample, maxPoints = MAX_GESTURE_POINTS) {
257
+ if (!Array.isArray(sample) || sample.length <= maxPoints) return Array.isArray(sample) ? sample : [];
258
+ if (maxPoints <= 1) return sample.slice(0, Math.max(0, maxPoints));
259
+ const capped = [sample[0]];
260
+ const step = (sample.length - 1) / (maxPoints - 1);
261
+ for (let index = 1; index < maxPoints - 1; index += 1) {
262
+ capped.push(sample[Math.round(index * step)]);
263
+ }
264
+ capped.push(sample[sample.length - 1]);
265
+ return capped;
266
+ }
267
+ function predictDrawnShape(input) {
268
+ const sample = capGestureSample(input);
269
+ if (sample.length < 2) return null;
208
270
  const start2 = sample[0];
209
271
  let pathLength = 0;
210
272
  let farthestIndex = 1;
@@ -225,9 +287,9 @@ function predictDrawnShape(sample) {
225
287
  minY = Math.min(minY, sample[index].y);
226
288
  maxY = Math.max(maxY, sample[index].y);
227
289
  }
228
- const diagonal = Math.max(MIN_SHAPE_SIZE, Math.hypot(maxX - minX, maxY - minY));
290
+ const diagonal = Math.max(MIN_SHAPE_SIZE2, Math.hypot(maxX - minX, maxY - minY));
229
291
  const end = sample[sample.length - 1];
230
- const closed = sample.length >= 8 && distance(start2, end) <= diagonal * 0.28 && pathLength >= diagonal * 2.05;
292
+ const closed = sample.length >= 8 && distance(start2, end) <= diagonal * 0.28 && pathLength >= diagonal * 1.85;
231
293
  if (closed) return closedShapePrediction(sample, pathLength);
232
294
  const tip = sample[farthestIndex];
233
295
  let afterTipLength = 0;
@@ -248,11 +310,32 @@ function predictDrawnShape(sample) {
248
310
  type: arrow ? "arrow" : "line",
249
311
  start: { ...start2 },
250
312
  end: arrow ? { ...tip } : { ...end },
251
- width: Math.max(MIN_SHAPE_SIZE, maxX - minX),
252
- height: Math.max(MIN_SHAPE_SIZE, maxY - minY),
313
+ width: Math.max(MIN_SHAPE_SIZE2, maxX - minX),
314
+ height: Math.max(MIN_SHAPE_SIZE2, maxY - minY),
253
315
  pathLength
254
316
  };
255
317
  }
318
+
319
+ // src/tools/shapeRecognitionTool.js
320
+ var SVG_NS = "http://www.w3.org/2000/svg";
321
+ var MIN_SCREEN_SAMPLE_PX = 4;
322
+ var drawing = false;
323
+ var points = [];
324
+ var previewPath = null;
325
+ var previewFrame = 0;
326
+ var latestPrediction = null;
327
+ function getThemeStroke() {
328
+ return document.body?.classList.contains("theme-dark") ? "#f7f4ff" : "#211a33";
329
+ }
330
+ function getSVGPoint(event) {
331
+ const viewBox = svg.viewBox.baseVal;
332
+ const rect = svg.getBoundingClientRect();
333
+ return {
334
+ x: viewBox.x + (event.clientX - rect.left) / rect.width * viewBox.width,
335
+ y: viewBox.y + (event.clientY - rect.top) / rect.height * viewBox.height,
336
+ pressure: event.pressure || 0.5
337
+ };
338
+ }
256
339
  function rotatedPoint(center, x, y, angle) {
257
340
  const cos = Math.cos(angle);
258
341
  const sin = Math.sin(angle);
@@ -279,6 +362,12 @@ function pathForPrediction(prediction) {
279
362
  const rotation = prediction.angle * 180 / Math.PI;
280
363
  return `M ${start3.x} ${start3.y} A ${rx} ${ry} ${rotation} 1 0 ${opposite.x} ${opposite.y} A ${rx} ${ry} ${rotation} 1 0 ${start3.x} ${start3.y}`;
281
364
  }
365
+ if (prediction.type === "triangle" || prediction.type === "diamond") {
366
+ return prediction.vertices.reduce(
367
+ (path2, point, index) => `${path2}${index === 0 ? "M" : " L"} ${point.x} ${point.y}`,
368
+ ""
369
+ ) + " Z";
370
+ }
282
371
  if (prediction.type === "freehand") {
283
372
  if (!prediction.points.length) return "";
284
373
  return prediction.points.reduce((path2, point, index) => `${path2}${index === 0 ? "M" : " L"} ${point.x} ${point.y}`, "");
@@ -314,7 +403,7 @@ function renderPreview() {
314
403
  latestPrediction = predictDrawnShape(points);
315
404
  ensurePreview();
316
405
  previewPath.setAttribute("d", pathForPrediction(latestPrediction));
317
- previewPath.setAttribute("fill", latestPrediction?.type === "rectangle" || latestPrediction?.type === "circle" ? "rgba(148, 148, 158, 0.06)" : "none");
406
+ previewPath.setAttribute("fill", ["rectangle", "circle", "triangle", "diamond"].includes(latestPrediction?.type) ? "rgba(148, 148, 158, 0.06)" : "none");
318
407
  }
319
408
  function schedulePreview() {
320
409
  if (!previewFrame) previewFrame = requestAnimationFrame(renderPreview);
@@ -371,6 +460,23 @@ function createPredictedShape(prediction) {
371
460
  );
372
461
  shape.rotation = prediction.angle * 180 / Math.PI;
373
462
  shape.draw();
463
+ } else if (prediction.type === "triangle" || prediction.type === "diamond") {
464
+ const polygonPoints = [...prediction.vertices, prediction.vertices[0]];
465
+ shape = new window.FreehandStroke(
466
+ polygonPoints.map((point) => [point.x, point.y, 0.5]),
467
+ {
468
+ stroke: "transparent",
469
+ fill: "transparent",
470
+ fillStyle: "none",
471
+ outlineStroke: style.stroke,
472
+ outlineWidth: style.strokeWidth,
473
+ closedFill: true,
474
+ strokeWidth: style.strokeWidth,
475
+ strokeStyle: style.outline,
476
+ roughness: 0
477
+ }
478
+ );
479
+ shape.recognizedShapeType = prediction.type;
374
480
  } else if (prediction.type === "freehand") {
375
481
  const brush = window.freehandToolSettings || {};
376
482
  shape = new window.FreehandStroke(
@@ -419,7 +525,7 @@ function handleShapeRecognitionMove(event) {
419
525
  const last = points[points.length - 1];
420
526
  const threshold = MIN_SCREEN_SAMPLE_PX / (window.currentZoom || 1);
421
527
  if (distance(last, point) < threshold) return;
422
- if (points.length < MAX_POINTS) {
528
+ if (points.length < MAX_GESTURE_POINTS) {
423
529
  points.push(point);
424
530
  } else {
425
531
  points = [points[0], ...points.slice(2).filter((_, index) => index % 2 === 0), point];
@@ -460,7 +566,7 @@ function cancelShapeRecognition() {
460
566
  window.__cancelShapeRecognition = cancelShapeRecognition;
461
567
 
462
568
  // src/tools/paintBucketTool.js
463
- var CLOSED_SHAPES = /* @__PURE__ */ new Set(["rectangle", "circle"]);
569
+ var OPTION_FILL_SHAPES = /* @__PURE__ */ new Set(["rectangle", "circle"]);
464
570
  window.paintBucketSettings = window.paintBucketSettings || {
465
571
  fillColor: "#A98DEB",
466
572
  fillStyle: "solid"
@@ -473,37 +579,84 @@ function getSVGPoint2(event) {
473
579
  y: viewBox.y + (event.clientY - rect.top) / rect.height * viewBox.height
474
580
  };
475
581
  }
476
- function findClosedShapeAt(x, y) {
582
+ function isClosedFreehand(shape) {
583
+ if (shape?.shapeName !== "freehandStroke" || !Array.isArray(shape.points) || shape.points.length < 3) return false;
584
+ if (shape.options?.closedFill) return true;
585
+ const first = shape.points[0];
586
+ const last = shape.points[shape.points.length - 1];
587
+ let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
588
+ for (const point of shape.points) {
589
+ const x = Number(point[0]) || 0;
590
+ const y = Number(point[1]) || 0;
591
+ minX = Math.min(minX, x);
592
+ minY = Math.min(minY, y);
593
+ maxX = Math.max(maxX, x);
594
+ maxY = Math.max(maxY, y);
595
+ }
596
+ const diagonal = Math.hypot(maxX - minX, maxY - minY);
597
+ const closeDistance = Math.hypot(last[0] - first[0], last[1] - first[1]);
598
+ const tolerance = Math.max((shape.options?.strokeWidth || 2) * 2, Math.min(16, diagonal * 0.08));
599
+ return diagonal > 0 && closeDistance <= tolerance;
600
+ }
601
+ function isPaintBucketFillable(shape) {
602
+ return OPTION_FILL_SHAPES.has(shape?.shapeName) || shape?.shapeName === "frame" || isClosedFreehand(shape);
603
+ }
604
+ function findShapeAt(x, y) {
477
605
  for (let index = shapes.length - 1; index >= 0; index -= 1) {
478
606
  const shape = shapes[index];
479
- if (!CLOSED_SHAPES.has(shape?.shapeName) || typeof shape.contains !== "function") continue;
607
+ if (typeof shape?.contains !== "function") continue;
480
608
  if (shape.contains(x, y)) return shape;
481
609
  }
482
610
  return null;
483
611
  }
484
- function handlePaintBucketDown(event) {
485
- if (!window.isPaintBucketToolActive || event.button !== 0) return;
486
- const { x, y } = getSVGPoint2(event);
487
- const shape = findClosedShapeAt(x, y);
488
- if (!shape) {
489
- window.dispatchEvent(new CustomEvent("lixsketch:bucket-miss"));
490
- return;
491
- }
492
- const settings = window.paintBucketSettings;
612
+ function fillShape(shape, settings = window.paintBucketSettings) {
613
+ if (!isPaintBucketFillable(shape)) return false;
493
614
  const transparent = settings.fillStyle === "none" || settings.fillStyle === "transparent";
494
615
  const nextFill = transparent ? "transparent" : settings.fillColor;
495
616
  const nextStyle = transparent ? "none" : settings.fillStyle;
496
- if (shape.options.fill === nextFill && shape.options.fillStyle === nextStyle) return;
617
+ if (shape.shapeName === "frame") {
618
+ if (shape.fillColor === nextFill && shape.fillStyle === nextStyle) return false;
619
+ const oldOptions2 = { ...shape.options, fillColor: shape.fillColor, fillStyle: shape.fillStyle };
620
+ const newOptions2 = { ...shape.options, fillColor: nextFill, fillStyle: nextStyle };
621
+ pushOptionsChangeAction(shape, oldOptions2, newOptions2);
622
+ shape.fillColor = nextFill;
623
+ shape.fillStyle = nextStyle;
624
+ shape.options = { ...shape.options, fillColor: nextFill, fillStyle: nextStyle };
625
+ shape.draw();
626
+ return true;
627
+ }
628
+ const freehandOptions = shape.shapeName === "freehandStroke" ? {
629
+ closedFill: true,
630
+ outlineStroke: shape.options.outlineStroke || shape.options.stroke
631
+ } : {};
632
+ if (shape.options.fill === nextFill && shape.options.fillStyle === nextStyle && Object.entries(freehandOptions).every(([key, value]) => shape.options[key] === value)) return false;
497
633
  const oldOptions = { ...shape.options };
498
- const newOptions = { ...shape.options, fill: nextFill, fillStyle: nextStyle };
634
+ const newOptions = { ...shape.options, ...freehandOptions, fill: nextFill, fillStyle: nextStyle };
499
635
  pushOptionsChangeAction(shape, oldOptions, newOptions);
500
636
  shape.options = newOptions;
501
637
  shape.draw();
638
+ return true;
639
+ }
640
+ function handlePaintBucketDown(event) {
641
+ if (!window.isPaintBucketToolActive || event.button !== 0) return;
642
+ const { x, y } = getSVGPoint2(event);
643
+ const shape = findShapeAt(x, y);
644
+ if (!shape) {
645
+ window.dispatchEvent(new CustomEvent("lixsketch:bucket-miss"));
646
+ return;
647
+ }
648
+ if (!isPaintBucketFillable(shape)) {
649
+ window.dispatchEvent(new CustomEvent("lixsketch:bucket-not-fillable", {
650
+ detail: { shapeID: shape.shapeID, shapeType: shape.shapeName }
651
+ }));
652
+ return;
653
+ }
654
+ if (!fillShape(shape)) return;
502
655
  window.dispatchEvent(new CustomEvent("lixsketch:bucket-filled", { detail: { shapeID: shape.shapeID } }));
503
656
  }
504
657
 
505
658
  // src/tools/lassoTool.js
506
- var MAX_POINTS2 = 192;
659
+ var MAX_POINTS = 192;
507
660
  var MIN_SAMPLE_PX = 4;
508
661
  var points2 = [];
509
662
  var preview = null;
@@ -575,7 +728,7 @@ function handleLassoMove(e) {
575
728
  const p = coords(e), last = points2[points2.length - 1];
576
729
  const min = MIN_SAMPLE_PX / (window.currentZoom || 1);
577
730
  if (Math.hypot(p.x - last.x, p.y - last.y) < min) return;
578
- if (points2.length < MAX_POINTS2) points2.push(p);
731
+ if (points2.length < MAX_POINTS) points2.push(p);
579
732
  else points2[points2.length - 1] = p;
580
733
  preview?.setAttribute("d", `M ${points2.map((v) => `${v.x} ${v.y}`).join(" L ")} Z`);
581
734
  }
@@ -1094,4 +1247,4 @@ export {
1094
1247
  handleMainMouseUp,
1095
1248
  initEventDispatcher
1096
1249
  };
1097
- //# sourceMappingURL=EventDispatcher-7LAK7GQ7.js.map
1250
+ //# sourceMappingURL=EventDispatcher-M5B4EWHJ.js.map