@elixpo/lixsketch 5.6.9 → 5.6.11
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.
- package/dist/react/{AIRenderer-R5M2CIDO.js → AIRenderer-JNEZYDYQ.js} +4 -4
- package/dist/react/Arrow-QMR77LGQ.js +9 -0
- package/dist/react/Circle-5WL5HZA5.js +11 -0
- package/dist/react/{CopyPaste-PPDS3AD6.js → CopyPaste-V7AY56H3.js} +74 -30
- package/dist/react/CopyPaste-V7AY56H3.js.map +7 -0
- package/dist/react/{EventDispatcher-UV7LFSAS.js → EventDispatcher-E4OJCMY6.js} +154 -57
- package/dist/react/{EventDispatcher-UV7LFSAS.js.map → EventDispatcher-E4OJCMY6.js.map} +3 -3
- package/dist/react/Frame-OZCDVPMI.js +11 -0
- package/dist/react/FreehandStroke-QSSYWBFK.js +9 -0
- package/dist/react/IconShape-HXUV4VRT.js +10 -0
- package/dist/react/ImageShape-JGLT2UGG.js +10 -0
- package/dist/react/Line-YONPLUYT.js +11 -0
- package/dist/react/{MermaidFlowchartRenderer-EL3Q2MNW.js → MermaidFlowchartRenderer-FSH43RUP.js} +26 -7
- package/dist/react/{MermaidFlowchartRenderer-EL3Q2MNW.js.map → MermaidFlowchartRenderer-FSH43RUP.js.map} +2 -2
- package/dist/react/{MermaidSequenceRenderer-K4KHHEVC.js → MermaidSequenceRenderer-FRPNOF2V.js} +11 -9
- package/dist/react/{MermaidSequenceRenderer-K4KHHEVC.js.map → MermaidSequenceRenderer-FRPNOF2V.js.map} +2 -2
- package/dist/react/{MermaidStructuredRenderer-6LN3WISP.js → MermaidStructuredRenderer-BZGQLRIH.js} +13 -6
- package/dist/react/{MermaidStructuredRenderer-6LN3WISP.js.map → MermaidStructuredRenderer-BZGQLRIH.js.map} +2 -2
- package/dist/react/Rectangle-7KMA3SC6.js +11 -0
- package/dist/react/{SceneSerializer-H6KMQCO2.js → SceneSerializer-7O72LEZP.js} +13 -12
- package/dist/react/{SceneSerializer-H6KMQCO2.js.map → SceneSerializer-7O72LEZP.js.map} +1 -1
- package/dist/react/{Selection-CFWRGVKH.js → Selection-S4FYSZIF.js} +5 -5
- package/dist/react/{SketchEngine-UFLZJQXS.js → SketchEngine-VTWURP2K.js} +26 -26
- package/dist/react/{UndoRedo-RXO7YHA2.js → UndoRedo-YLQJ6DXW.js} +8 -2
- package/dist/react/{ZoomPan-RR7KUYZT.js → ZoomPan-AEKWKUCH.js} +2 -2
- package/dist/react/{arrowTool-DO327FKU.js → arrowTool-C4RDWQEX.js} +3 -3
- package/dist/react/{chunk-X5ENBMGL.js → chunk-2U47AU6Y.js} +3 -3
- package/dist/react/{chunk-X5ENBMGL.js.map → chunk-2U47AU6Y.js.map} +1 -1
- package/dist/react/{chunk-RK3NVAER.js → chunk-75OPJOWN.js} +7 -7
- package/dist/react/chunk-75OPJOWN.js.map +7 -0
- package/dist/react/{chunk-DODPOWNX.js → chunk-CWE5IFG7.js} +5 -5
- package/dist/react/{chunk-DODPOWNX.js.map → chunk-CWE5IFG7.js.map} +1 -1
- package/dist/react/{chunk-O7KXLFZN.js → chunk-IS2MKWGH.js} +8 -7
- package/dist/react/chunk-IS2MKWGH.js.map +7 -0
- package/dist/react/{chunk-ZNZAICWY.js → chunk-JEHOEQ6Z.js} +13 -5
- package/dist/react/chunk-JEHOEQ6Z.js.map +7 -0
- package/dist/react/{chunk-BOZEDUAF.js → chunk-JYBJXTLM.js} +40 -15
- package/dist/react/chunk-JYBJXTLM.js.map +7 -0
- package/dist/react/{chunk-6LWUR7XB.js → chunk-LXEHYCKH.js} +4 -4
- package/dist/react/{chunk-6LWUR7XB.js.map → chunk-LXEHYCKH.js.map} +1 -1
- package/dist/react/chunk-M7OYYJC6.js +17 -0
- package/dist/react/chunk-M7OYYJC6.js.map +7 -0
- package/dist/react/chunk-MS4TIQ6Z.js +131 -0
- package/dist/react/chunk-MS4TIQ6Z.js.map +7 -0
- package/dist/react/{chunk-2ALS5JGS.js → chunk-OCJN32IL.js} +2 -2
- package/dist/react/{chunk-KZBKYHZO.js → chunk-P7VIYR6R.js} +8 -5
- package/dist/react/chunk-P7VIYR6R.js.map +7 -0
- package/dist/react/{chunk-KC26KJW4.js → chunk-PX2635H5.js} +32 -10
- package/dist/react/chunk-PX2635H5.js.map +7 -0
- package/dist/react/{chunk-37PF7FG2.js → chunk-SDG4U7TP.js} +7 -6
- package/dist/react/chunk-SDG4U7TP.js.map +7 -0
- package/dist/react/{chunk-CGMUXYPJ.js → chunk-STJERZ25.js} +8 -4
- package/dist/react/chunk-STJERZ25.js.map +7 -0
- package/dist/react/{chunk-VZPH7OAM.js → chunk-TCWCC4Z4.js} +7 -3
- package/dist/react/chunk-TCWCC4Z4.js.map +7 -0
- package/dist/react/{chunk-VZX55VQL.js → chunk-TFCBCWL5.js} +90 -33
- package/dist/react/chunk-TFCBCWL5.js.map +7 -0
- package/dist/react/{chunk-GQLXUZHQ.js → chunk-TWHJ6PJK.js} +5 -3
- package/dist/react/chunk-TWHJ6PJK.js.map +7 -0
- package/dist/react/{chunk-NH3NVSWT.js → chunk-UCOK4V5Z.js} +4 -3
- package/dist/react/{chunk-NH3NVSWT.js.map → chunk-UCOK4V5Z.js.map} +2 -2
- package/dist/react/{chunk-W7XUY4GR.js → chunk-VJDNKJBX.js} +56 -4
- package/dist/react/chunk-VJDNKJBX.js.map +7 -0
- package/dist/react/{chunk-BT2EIJD4.js → chunk-WCA3FGWE.js} +6 -3
- package/dist/react/chunk-WCA3FGWE.js.map +7 -0
- package/dist/react/{chunk-I7TVGJFF.js → chunk-Y3FJMYPX.js} +4 -4
- package/dist/react/{chunk-I7TVGJFF.js.map → chunk-Y3FJMYPX.js.map} +1 -1
- package/dist/react/{chunk-7DD2JRBC.js → chunk-YCGLGY4C.js} +2 -2
- package/dist/react/{circleTool-NK4RVDAR.js → circleTool-QQV2QEVF.js} +4 -4
- package/dist/react/{codeTool-3WF4HEOW.js → codeTool-PTCPSZ4L.js} +5 -5
- package/dist/react/{frameTool-VCNV2NG2.js → frameTool-7GQPLU4Y.js} +4 -4
- package/dist/react/{freehandTool-TBAOD46K.js → freehandTool-3RJXTVAK.js} +4 -4
- package/dist/react/{iconTool-65JCAKYF.js → iconTool-DW5YJX7P.js} +5 -5
- package/dist/react/{imageTool-UQZUBOK2.js → imageTool-7GZQKWYT.js} +5 -5
- package/dist/react/index.js +68 -63
- package/dist/react/index.js.map +2 -2
- package/dist/react/{lineTool-762KBYOM.js → lineTool-5CY7BHND.js} +4 -4
- package/dist/react/{rectangleTool-IQK3HZK6.js → rectangleTool-DK4QL4QW.js} +4 -4
- package/dist/react/styles.css +4 -0
- package/dist/react/{textTool-ITG2SBJK.js → textTool-GP5QQYF3.js} +6 -6
- package/package.json +1 -1
- package/src/core/CopyPaste.js +66 -18
- package/src/core/EventDispatcher.js +71 -47
- package/src/core/MermaidFlowchartRenderer.js +34 -8
- package/src/core/MermaidSequenceRenderer.js +10 -8
- package/src/core/MermaidStructuredRenderer.js +12 -5
- package/src/core/ScreenSpaceControls.js +102 -13
- package/src/core/Selection.js +26 -7
- package/src/core/SelectionGesture.js +50 -0
- package/src/core/UndoRedo.js +108 -35
- package/src/react/components/sidebars/CircleSidebar.jsx +11 -5
- package/src/react/components/sidebars/IconSidebar.jsx +10 -4
- package/src/react/components/sidebars/RectangleSidebar.jsx +11 -5
- package/src/react/components/sidebars/ShapeSidebar.jsx +29 -37
- package/src/react/components/sidebars/TextSidebar.jsx +10 -4
- package/src/react/store/useUIStore.js +8 -1
- package/src/react/styles.css +13 -0
- package/src/react/utils/canvasBackgrounds.js +11 -2
- package/src/shapes/Arrow.js +4 -3
- package/src/shapes/Circle.js +58 -2
- package/src/shapes/Frame.js +11 -4
- package/src/shapes/FreehandStroke.js +2 -1
- package/src/shapes/Line.js +47 -20
- package/src/shapes/Rectangle.js +3 -1
- package/src/tools/arrowTool.js +1 -1
- package/src/tools/circleTool.js +5 -2
- package/src/tools/codeTool.js +1 -1
- package/src/tools/frameTool.js +3 -2
- package/src/tools/freehandTool.js +1 -1
- package/src/tools/iconTool.js +4 -3
- package/src/tools/imageTool.js +3 -2
- package/src/tools/lineTool.js +5 -1
- package/src/tools/rectangleTool.js +1 -1
- package/src/tools/shapeRecognitionTool.js +1 -1
- package/src/tools/textTool.js +2 -2
- package/src/utils/themeColors.js +13 -0
- package/dist/react/Arrow-EF7ALH2Y.js +0 -8
- package/dist/react/Circle-HMM5PKJ2.js +0 -10
- package/dist/react/CopyPaste-PPDS3AD6.js.map +0 -7
- package/dist/react/Frame-DELCMYV6.js +0 -11
- package/dist/react/FreehandStroke-WXIJKD5I.js +0 -9
- package/dist/react/IconShape-PZHMZSLR.js +0 -10
- package/dist/react/ImageShape-ZMBZARRI.js +0 -10
- package/dist/react/Line-VPVPL6ET.js +0 -10
- package/dist/react/Rectangle-4U4CD2ER.js +0 -10
- package/dist/react/chunk-37PF7FG2.js.map +0 -7
- package/dist/react/chunk-BOZEDUAF.js.map +0 -7
- package/dist/react/chunk-BT2EIJD4.js.map +0 -7
- package/dist/react/chunk-CGMUXYPJ.js.map +0 -7
- package/dist/react/chunk-GQLXUZHQ.js.map +0 -7
- package/dist/react/chunk-KC26KJW4.js.map +0 -7
- package/dist/react/chunk-KZBKYHZO.js.map +0 -7
- package/dist/react/chunk-O7KXLFZN.js.map +0 -7
- package/dist/react/chunk-PAKA6HUX.js +0 -59
- package/dist/react/chunk-PAKA6HUX.js.map +0 -7
- package/dist/react/chunk-RK3NVAER.js.map +0 -7
- package/dist/react/chunk-VZPH7OAM.js.map +0 -7
- package/dist/react/chunk-VZX55VQL.js.map +0 -7
- package/dist/react/chunk-W7XUY4GR.js.map +0 -7
- package/dist/react/chunk-ZNZAICWY.js.map +0 -7
- /package/dist/react/{AIRenderer-R5M2CIDO.js.map → AIRenderer-JNEZYDYQ.js.map} +0 -0
- /package/dist/react/{Arrow-EF7ALH2Y.js.map → Arrow-QMR77LGQ.js.map} +0 -0
- /package/dist/react/{Circle-HMM5PKJ2.js.map → Circle-5WL5HZA5.js.map} +0 -0
- /package/dist/react/{Frame-DELCMYV6.js.map → Frame-OZCDVPMI.js.map} +0 -0
- /package/dist/react/{FreehandStroke-WXIJKD5I.js.map → FreehandStroke-QSSYWBFK.js.map} +0 -0
- /package/dist/react/{IconShape-PZHMZSLR.js.map → IconShape-HXUV4VRT.js.map} +0 -0
- /package/dist/react/{ImageShape-ZMBZARRI.js.map → ImageShape-JGLT2UGG.js.map} +0 -0
- /package/dist/react/{Line-VPVPL6ET.js.map → Line-YONPLUYT.js.map} +0 -0
- /package/dist/react/{Rectangle-4U4CD2ER.js.map → Rectangle-7KMA3SC6.js.map} +0 -0
- /package/dist/react/{Selection-CFWRGVKH.js.map → Selection-S4FYSZIF.js.map} +0 -0
- /package/dist/react/{SketchEngine-UFLZJQXS.js.map → SketchEngine-VTWURP2K.js.map} +0 -0
- /package/dist/react/{UndoRedo-RXO7YHA2.js.map → UndoRedo-YLQJ6DXW.js.map} +0 -0
- /package/dist/react/{ZoomPan-RR7KUYZT.js.map → ZoomPan-AEKWKUCH.js.map} +0 -0
- /package/dist/react/{arrowTool-DO327FKU.js.map → arrowTool-C4RDWQEX.js.map} +0 -0
- /package/dist/react/{chunk-2ALS5JGS.js.map → chunk-OCJN32IL.js.map} +0 -0
- /package/dist/react/{chunk-7DD2JRBC.js.map → chunk-YCGLGY4C.js.map} +0 -0
- /package/dist/react/{circleTool-NK4RVDAR.js.map → circleTool-QQV2QEVF.js.map} +0 -0
- /package/dist/react/{codeTool-3WF4HEOW.js.map → codeTool-PTCPSZ4L.js.map} +0 -0
- /package/dist/react/{frameTool-VCNV2NG2.js.map → frameTool-7GQPLU4Y.js.map} +0 -0
- /package/dist/react/{freehandTool-TBAOD46K.js.map → freehandTool-3RJXTVAK.js.map} +0 -0
- /package/dist/react/{iconTool-65JCAKYF.js.map → iconTool-DW5YJX7P.js.map} +0 -0
- /package/dist/react/{imageTool-UQZUBOK2.js.map → imageTool-7GZQKWYT.js.map} +0 -0
- /package/dist/react/{lineTool-762KBYOM.js.map → lineTool-5CY7BHND.js.map} +0 -0
- /package/dist/react/{rectangleTool-IQK3HZK6.js.map → rectangleTool-DK4QL4QW.js.map} +0 -0
- /package/dist/react/{textTool-ITG2SBJK.js.map → textTool-GP5QQYF3.js.map} +0 -0
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import {
|
|
3
|
+
resolveThemeLabelColor
|
|
4
|
+
} from "./chunk-M7OYYJC6.js";
|
|
2
5
|
import {
|
|
3
6
|
updateAttachedArrows
|
|
4
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-2U47AU6Y.js";
|
|
5
8
|
|
|
6
9
|
// src/shapes/Line.js
|
|
7
10
|
var rc = rough.svg(svg);
|
|
8
11
|
function getThemeStroke() {
|
|
9
12
|
if (typeof document === "undefined") return "#fff";
|
|
10
|
-
return document.body && document.body.classList.contains("theme-dark") ? "#fff" : "#
|
|
13
|
+
return document.body && document.body.classList.contains("theme-dark") ? "#fff" : "#000000";
|
|
11
14
|
}
|
|
12
15
|
var lineStrokeWidth = 2;
|
|
13
16
|
var hoveredFrameLine = null;
|
|
@@ -16,6 +19,9 @@ var Line = class {
|
|
|
16
19
|
this.startPoint = startPoint;
|
|
17
20
|
this.endPoint = endPoint;
|
|
18
21
|
this.options = { ...options };
|
|
22
|
+
if (!Number.isInteger(this.options.seed) || this.options.seed <= 0) {
|
|
23
|
+
this.options.seed = Math.floor(Math.random() * 2147483647) + 1;
|
|
24
|
+
}
|
|
19
25
|
this.group = document.createElementNS("http://www.w3.org/2000/svg", "g");
|
|
20
26
|
this.isSelected = false;
|
|
21
27
|
this.anchors = [];
|
|
@@ -28,11 +34,12 @@ var Line = class {
|
|
|
28
34
|
this.parentFrame = null;
|
|
29
35
|
this.label = options.label || "";
|
|
30
36
|
this.labelElement = null;
|
|
31
|
-
this.labelColor = options.labelColor
|
|
37
|
+
this.labelColor = resolveThemeLabelColor(options.labelColor);
|
|
32
38
|
this.labelFontSize = options.labelFontSize || 12;
|
|
33
39
|
this._isEditingLabel = false;
|
|
34
40
|
this._hitArea = null;
|
|
35
41
|
this._labelBg = null;
|
|
42
|
+
this._dragRenderFrame = null;
|
|
36
43
|
svg.appendChild(this.group);
|
|
37
44
|
this._setupLabelDblClick();
|
|
38
45
|
this.draw();
|
|
@@ -368,7 +375,7 @@ var Line = class {
|
|
|
368
375
|
});
|
|
369
376
|
this.anchors = [];
|
|
370
377
|
const anchorSize = 10 / (currentZoom || 1);
|
|
371
|
-
const anchorStrokeWidth = 2
|
|
378
|
+
const anchorStrokeWidth = 2;
|
|
372
379
|
const startAnchor = document.createElementNS("http://www.w3.org/2000/svg", "circle");
|
|
373
380
|
startAnchor.setAttribute("cx", this.startPoint.x);
|
|
374
381
|
startAnchor.setAttribute("cy", this.startPoint.y);
|
|
@@ -456,6 +463,33 @@ var Line = class {
|
|
|
456
463
|
y: 2 * newY - 0.5 * (this.startPoint.y + this.endPoint.y)
|
|
457
464
|
};
|
|
458
465
|
}
|
|
466
|
+
this.scheduleDragRender();
|
|
467
|
+
}
|
|
468
|
+
scheduleDragRender() {
|
|
469
|
+
if (this._dragRenderFrame !== null) return;
|
|
470
|
+
if (typeof requestAnimationFrame !== "function") {
|
|
471
|
+
this.renderDragGeometry();
|
|
472
|
+
return;
|
|
473
|
+
}
|
|
474
|
+
this._dragRenderFrame = requestAnimationFrame(() => {
|
|
475
|
+
this._dragRenderFrame = null;
|
|
476
|
+
this.renderDragGeometry();
|
|
477
|
+
});
|
|
478
|
+
}
|
|
479
|
+
renderDragGeometry() {
|
|
480
|
+
this.updateLineElement();
|
|
481
|
+
this.updateAnchorPositions();
|
|
482
|
+
if (this._hitArea) {
|
|
483
|
+
const path = this.isCurved && this.controlPoint ? `M ${this.startPoint.x} ${this.startPoint.y} Q ${this.controlPoint.x} ${this.controlPoint.y} ${this.endPoint.x} ${this.endPoint.y}` : `M ${this.startPoint.x} ${this.startPoint.y} L ${this.endPoint.x} ${this.endPoint.y}`;
|
|
484
|
+
this._hitArea.setAttribute("d", path);
|
|
485
|
+
}
|
|
486
|
+
this._updateLabelElement();
|
|
487
|
+
}
|
|
488
|
+
finalizeMove() {
|
|
489
|
+
if (this._dragRenderFrame !== null && typeof cancelAnimationFrame === "function") {
|
|
490
|
+
cancelAnimationFrame(this._dragRenderFrame);
|
|
491
|
+
}
|
|
492
|
+
this._dragRenderFrame = null;
|
|
459
493
|
this.draw();
|
|
460
494
|
}
|
|
461
495
|
updateLineElement() {
|
|
@@ -529,16 +563,7 @@ var Line = class {
|
|
|
529
563
|
this.controlPoint.x += dx;
|
|
530
564
|
this.controlPoint.y += dy;
|
|
531
565
|
}
|
|
532
|
-
this.
|
|
533
|
-
this.updateAnchorPositions();
|
|
534
|
-
if (this._hitArea) {
|
|
535
|
-
if (this.isCurved && this.controlPoint) {
|
|
536
|
-
this._hitArea.setAttribute("d", `M ${this.startPoint.x} ${this.startPoint.y} Q ${this.controlPoint.x} ${this.controlPoint.y} ${this.endPoint.x} ${this.endPoint.y}`);
|
|
537
|
-
} else {
|
|
538
|
-
this._hitArea.setAttribute("d", `M ${this.startPoint.x} ${this.startPoint.y} L ${this.endPoint.x} ${this.endPoint.y}`);
|
|
539
|
-
}
|
|
540
|
-
}
|
|
541
|
-
this._updateLabelElement();
|
|
566
|
+
this.scheduleDragRender();
|
|
542
567
|
if (!this.isBeingMovedByFrame) {
|
|
543
568
|
this.updateFrameContainment();
|
|
544
569
|
}
|
|
@@ -625,4 +650,4 @@ var Line = class {
|
|
|
625
650
|
export {
|
|
626
651
|
Line
|
|
627
652
|
};
|
|
628
|
-
//# sourceMappingURL=chunk-
|
|
653
|
+
//# sourceMappingURL=chunk-JYBJXTLM.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/shapes/Line.js"],
|
|
4
|
+
"sourcesContent": ["/* eslint-disable */\n// Line shape class - extracted from lineTool.js\n// Depends on globals: svg, shapes, rough, currentShape, currentZoom\nimport { updateAttachedArrows as updateArrowsForShape } from '../tools/arrowTool.js';\nimport { resolveThemeLabelColor } from '../utils/themeColors.js';\n\nconst rc = rough.svg(svg);\n// Issue #38 follow-up: read the default stroke from the active theme\n// instead of pinning to a single colour. White was invisible on the new\n// light canvas, black is invisible if the user toggles dark.\nfunction getThemeStroke() {\n if (typeof document === 'undefined') return '#fff';\n return document.body && document.body.classList.contains('theme-dark') ? '#fff' : '#000000';\n}\nconst lineStrokeWidth = 2;\nlet hoveredFrameLine = null;\n\nclass Line {\n constructor(startPoint, endPoint, options = {}) {\n this.startPoint = startPoint;\n this.endPoint = endPoint;\n this.options = { ...options };\n // RoughJS otherwise chooses a fresh random stroke on every redraw.\n // A stable per-line seed keeps the hand-drawn contour visually fixed\n // while its endpoints move.\n if (!Number.isInteger(this.options.seed) || this.options.seed <= 0) {\n this.options.seed = Math.floor(Math.random() * 0x7fffffff) + 1;\n }\n this.group = document.createElementNS('http://www.w3.org/2000/svg', 'g');\n this.isSelected = false;\n this.anchors = [];\n this.selectionOutline = null;\n this.shapeName = \"line\"; \n this.shapeID = `line-${String(Date.now()).slice(0, 8)}-${Math.floor(Math.random() * 10000)}`; \n this.group.setAttribute('id', this.shapeID);\n \n // Curve properties\n this.isCurved = false;\n this.controlPoint = null;\n \n // Frame attachment properties\n this.parentFrame = null;\n\n // Embedded label support\n this.label = options.label || '';\n this.labelElement = null;\n this.labelColor = resolveThemeLabelColor(options.labelColor);\n this.labelFontSize = options.labelFontSize || 12;\n this._isEditingLabel = false;\n this._hitArea = null;\n this._labelBg = null;\n this._dragRenderFrame = null;\n\n svg.appendChild(this.group);\n this._setupLabelDblClick();\n this.draw();\n }\n\n // Add position and dimension properties for frame compatibility\n get x() {\n return Math.min(this.startPoint.x, this.endPoint.x);\n }\n \n set x(value) {\n const currentX = this.x;\n const dx = value - currentX;\n this.startPoint.x += dx;\n this.endPoint.x += dx;\n if (this.controlPoint) {\n this.controlPoint.x += dx;\n }\n }\n \n get y() {\n return Math.min(this.startPoint.y, this.endPoint.y);\n }\n \n set y(value) {\n const currentY = this.y;\n const dy = value - currentY;\n this.startPoint.y += dy;\n this.endPoint.y += dy;\n if (this.controlPoint) {\n this.controlPoint.y += dy;\n }\n }\n \n get width() {\n return Math.abs(this.endPoint.x - this.startPoint.x);\n }\n \n set width(value) {\n const centerX = (this.startPoint.x + this.endPoint.x) / 2;\n this.startPoint.x = centerX - value / 2;\n this.endPoint.x = centerX + value / 2;\n }\n \n get height() {\n return Math.abs(this.endPoint.y - this.startPoint.y);\n }\n \n set height(value) {\n const centerY = (this.startPoint.y + this.endPoint.y) / 2;\n this.startPoint.y = centerY - value / 2;\n this.endPoint.y = centerY + value / 2;\n }\n\n initializeCurveControlPoint() {\n const midX = (this.startPoint.x + this.endPoint.x) / 2;\n const midY = (this.startPoint.y + this.endPoint.y) / 2;\n \n // Calculate perpendicular offset for curve\n const dx = this.endPoint.x - this.startPoint.x;\n const dy = this.endPoint.y - this.startPoint.y;\n const length = Math.sqrt(dx * dx + dy * dy);\n \n if (length === 0) {\n this.controlPoint = { x: midX, y: midY - 20 };\n return;\n }\n \n // Perpendicular vector\n const perpX = -dy / length;\n const perpY = dx / length;\n const curveOffset = 30; // Default curve amount\n \n this.controlPoint = {\n x: midX + perpX * curveOffset,\n y: midY + perpY * curveOffset\n };\n }\n\n draw() {\n // Clear existing elements but preserve label, hit area, and anchors during active resize\n const childrenToRemove = [];\n const preserveSet = this._skipAnchors ? new Set([...this.anchors, this.selectionOutline].filter(Boolean)) : null;\n for (let i = 0; i < this.group.children.length; i++) {\n const child = this.group.children[i];\n if (child !== this.labelElement && child !== this._hitArea && child !== this._labelBg) {\n if (preserveSet && preserveSet.has(child)) continue;\n childrenToRemove.push(child);\n }\n }\n childrenToRemove.forEach(child => this.group.removeChild(child));\n\n const rc = rough.svg(svg);\n let lineElement;\n\n if (this.isCurved && this.controlPoint) {\n const pathData = `M ${this.startPoint.x} ${this.startPoint.y} Q ${this.controlPoint.x} ${this.controlPoint.y} ${this.endPoint.x} ${this.endPoint.y}`;\n lineElement = document.createElementNS('http://www.w3.org/2000/svg', 'path');\n lineElement.setAttribute('d', pathData);\n lineElement.setAttribute('stroke', this.options.stroke || getThemeStroke());\n lineElement.setAttribute('stroke-width', this.options.strokeWidth || lineStrokeWidth);\n lineElement.setAttribute('fill', 'none');\n lineElement.setAttribute('stroke-linecap', 'round');\n if (this.options.strokeDasharray) {\n lineElement.setAttribute('stroke-dasharray', this.options.strokeDasharray);\n }\n } else if (this.isBeingDrawn || this.options.strokeDasharray) {\n // Use plain SVG for lines being drawn OR dashed/dotted lines\n // (RoughJS ignores strokeDasharray, so dashed styles must bypass it)\n lineElement = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n lineElement.setAttribute('x1', this.startPoint.x);\n lineElement.setAttribute('y1', this.startPoint.y);\n lineElement.setAttribute('x2', this.endPoint.x);\n lineElement.setAttribute('y2', this.endPoint.y);\n lineElement.setAttribute('stroke', this.options.stroke || getThemeStroke());\n lineElement.setAttribute('stroke-width', this.options.strokeWidth || lineStrokeWidth);\n lineElement.setAttribute('stroke-linecap', 'round');\n if (this.options.strokeDasharray) {\n lineElement.setAttribute('stroke-dasharray', this.options.strokeDasharray);\n }\n } else {\n lineElement = rc.line(\n this.startPoint.x, this.startPoint.y,\n this.endPoint.x, this.endPoint.y,\n this.options\n );\n }\n\n this.element = lineElement;\n this.group.appendChild(lineElement);\n\n // Hit area - thicker invisible path for dblclick detection\n if (!this._hitArea) {\n this._hitArea = document.createElementNS('http://www.w3.org/2000/svg', 'path');\n this._hitArea.setAttribute('fill', 'none');\n this._hitArea.setAttribute('stroke', 'transparent');\n this._hitArea.setAttribute('stroke-width', '20');\n this._hitArea.setAttribute('style', 'pointer-events: stroke;');\n this.group.appendChild(this._hitArea);\n }\n if (this.isCurved && this.controlPoint) {\n this._hitArea.setAttribute('d', `M ${this.startPoint.x} ${this.startPoint.y} Q ${this.controlPoint.x} ${this.controlPoint.y} ${this.endPoint.x} ${this.endPoint.y}`);\n } else {\n this._hitArea.setAttribute('d', `M ${this.startPoint.x} ${this.startPoint.y} L ${this.endPoint.x} ${this.endPoint.y}`);\n }\n\n // Update embedded label at midpoint\n this._updateLabelElement();\n\n if (this.isSelected) {\n if (this._skipAnchors) {\n this.updateSelectionControls();\n } else {\n this.addAnchors();\n }\n }\n }\n\n _getMidpoint() {\n if (this.isCurved && this.controlPoint) {\n // Quadratic bezier midpoint at t=0.5\n const t = 0.5;\n const mx = (1 - t) * (1 - t) * this.startPoint.x + 2 * (1 - t) * t * this.controlPoint.x + t * t * this.endPoint.x;\n const my = (1 - t) * (1 - t) * this.startPoint.y + 2 * (1 - t) * t * this.controlPoint.y + t * t * this.endPoint.y;\n return { x: mx, y: my };\n }\n return {\n x: (this.startPoint.x + this.endPoint.x) / 2,\n y: (this.startPoint.y + this.endPoint.y) / 2\n };\n }\n\n _updateLabelElement() {\n if (!this.label) {\n if (this.labelElement && this.labelElement.parentNode === this.group) {\n this.group.removeChild(this.labelElement);\n this.labelElement = null;\n }\n if (this._labelBg && this._labelBg.parentNode === this.group) {\n this.group.removeChild(this._labelBg);\n this._labelBg = null;\n }\n return;\n }\n\n if (!this.labelElement) {\n this.labelElement = document.createElementNS('http://www.w3.org/2000/svg', 'text');\n this.labelElement.setAttribute('class', 'shape-label');\n this.labelElement.setAttribute('pointer-events', 'none');\n }\n\n const mid = this._getMidpoint();\n this.labelElement.setAttribute('x', mid.x);\n this.labelElement.setAttribute('y', mid.y);\n this.labelElement.setAttribute('text-anchor', 'middle');\n this.labelElement.setAttribute('dominant-baseline', 'central');\n this.labelElement.setAttribute('fill', this.labelColor);\n this.labelElement.setAttribute('font-size', this.labelFontSize);\n this.labelElement.setAttribute('font-family', 'lixFont, sans-serif');\n this.labelElement.textContent = this.label;\n\n // Background knockout rect - hides the line behind the text\n const canvasBg = window.getComputedStyle(svg).backgroundColor || '#000';\n if (!this._labelBg) {\n this._labelBg = document.createElementNS('http://www.w3.org/2000/svg', 'rect');\n this._labelBg.setAttribute('pointer-events', 'none');\n }\n this._labelBg.setAttribute('fill', canvasBg);\n const hPadding = 4;\n const vPadding = 1;\n const charWidth = this.labelFontSize * 0.6;\n const bgW = this.label.length * charWidth + hPadding * 2;\n const bgH = this.labelFontSize + vPadding * 2;\n this._labelBg.setAttribute('x', mid.x - bgW / 2);\n this._labelBg.setAttribute('y', mid.y - bgH / 2);\n this._labelBg.setAttribute('width', bgW);\n this._labelBg.setAttribute('height', bgH);\n this._labelBg.setAttribute('rx', 2);\n\n // Re-append bg then text at end so they render ON TOP of the line path\n if (this._labelBg.parentNode === this.group) this.group.removeChild(this._labelBg);\n if (this.labelElement.parentNode === this.group) this.group.removeChild(this.labelElement);\n this.group.appendChild(this._labelBg);\n this.group.appendChild(this.labelElement);\n }\n\n _setupLabelDblClick() {\n this.group.addEventListener('dblclick', (e) => {\n e.stopPropagation();\n e.preventDefault();\n this.startLabelEdit();\n });\n }\n\n startLabelEdit() {\n if (this._isEditingLabel) return;\n this._isEditingLabel = true;\n\n if (this.labelElement) {\n this.labelElement.setAttribute('visibility', 'hidden');\n }\n if (this._labelBg) {\n this._labelBg.setAttribute('visibility', 'hidden');\n }\n\n // Get midpoint in screen coords via CTM\n const mid = this._getMidpoint();\n const ctm = this.group.getScreenCTM();\n if (!ctm) { this._isEditingLabel = false; return; }\n\n const pt = svg.createSVGPoint();\n pt.x = mid.x; pt.y = mid.y;\n const screenMid = pt.matrixTransform(ctm);\n\n const editW = 160;\n const editH = 28;\n\n // Create HTML overlay centered on the midpoint\n const overlay = document.createElement('div');\n overlay.className = 'shape-label-editor';\n overlay.style.cssText = `\n position: fixed; z-index: 10000;\n left: ${screenMid.x - editW / 2}px; top: ${screenMid.y - editH / 2}px;\n width: ${editW}px; height: ${editH}px;\n display: flex; align-items: center; justify-content: center;\n pointer-events: auto;\n `;\n\n const canvasBg = window.getComputedStyle(svg).backgroundColor || '#000';\n const input = document.createElement('div');\n input.setAttribute('contenteditable', 'true');\n input.style.cssText = `\n width: 100%; height: 100%;\n background: ${canvasBg}; border: none;\n outline: none; padding: 2px 6px;\n color: ${this.labelColor}; font-size: ${this.labelFontSize}px;\n font-family: lixFont, sans-serif; text-align: center;\n display: flex; align-items: center; justify-content: center;\n white-space: pre-wrap; word-break: break-word;\n cursor: text;\n `;\n if (this.label) {\n input.textContent = this.label;\n } else {\n input.innerHTML = ' ';\n }\n\n overlay.appendChild(input);\n document.body.appendChild(overlay);\n\n setTimeout(() => {\n input.focus();\n const sel = window.getSelection();\n const range = document.createRange();\n range.selectNodeContents(input);\n sel.removeAllRanges();\n sel.addRange(range);\n }, 10);\n\n const finishEdit = () => {\n const newText = input.textContent.trim().replace(/\\u00A0/g, '');\n this.label = newText;\n this._isEditingLabel = false;\n if (overlay.parentNode) overlay.parentNode.removeChild(overlay);\n if (this.labelElement) this.labelElement.removeAttribute('visibility');\n if (this._labelBg) this._labelBg.removeAttribute('visibility');\n this.draw();\n };\n\n input.addEventListener('blur', finishEdit);\n input.addEventListener('keydown', (e) => {\n e.stopPropagation();\n if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); input.blur(); }\n if (e.key === 'Escape') { input.textContent = this.label; input.blur(); }\n });\n input.addEventListener('pointerdown', (e) => e.stopPropagation());\n input.addEventListener('pointermove', (e) => e.stopPropagation());\n input.addEventListener('pointerup', (e) => e.stopPropagation());\n }\n\n setLabel(text, color, fontSize) {\n this.label = text || '';\n if (color) this.labelColor = color;\n if (fontSize) this.labelFontSize = fontSize;\n this.draw();\n }\n\n updateSelectionControls() {\n if (this.anchors.length === 0) return;\n\n // Update start anchor\n if (this.anchors[0]) {\n this.anchors[0].setAttribute('cx', this.startPoint.x);\n this.anchors[0].setAttribute('cy', this.startPoint.y);\n }\n\n // Update end anchor\n if (this.anchors[1]) {\n this.anchors[1].setAttribute('cx', this.endPoint.x);\n this.anchors[1].setAttribute('cy', this.endPoint.y);\n }\n\n // Update middle anchor\n if (this.anchors[2]) {\n const midX = (this.startPoint.x + this.endPoint.x) / 2;\n const midY = (this.startPoint.y + this.endPoint.y) / 2;\n let anchorMidX = midX, anchorMidY = midY;\n if (this.isCurved && this.controlPoint) {\n anchorMidX = 0.25 * this.startPoint.x + 0.5 * this.controlPoint.x + 0.25 * this.endPoint.x;\n anchorMidY = 0.25 * this.startPoint.y + 0.5 * this.controlPoint.y + 0.25 * this.endPoint.y;\n }\n this.anchors[2].setAttribute('cx', anchorMidX);\n this.anchors[2].setAttribute('cy', anchorMidY);\n }\n }\n\n selectLine() {\n this.isSelected = true;\n this.addAnchors();\n}\n\ndeselectLine() {\n this.isSelected = false;\n this.removeSelection();\n}\n\n// Update the complete removeSelection method\nremoveSelection() {\n // Remove anchors\n this.anchors.forEach(anchor => {\n if (anchor && anchor.parentNode === this.group) {\n this.group.removeChild(anchor);\n }\n });\n this.anchors = [];\n this.isSelected = false; // Only set to false when actually deselecting\n}\n\n addAnchors() {\n // Only remove existing anchors if they exist, don't call full removeSelection\n this.anchors.forEach(anchor => {\n if (anchor && anchor.parentNode === this.group) {\n this.group.removeChild(anchor);\n }\n });\n this.anchors = [];\n \n const anchorSize = 10 / (currentZoom || 1);\n const anchorStrokeWidth = 2;\n \n // Start point anchor\n const startAnchor = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n startAnchor.setAttribute('cx', this.startPoint.x);\n startAnchor.setAttribute('cy', this.startPoint.y);\n startAnchor.setAttribute('r', anchorSize / 2);\n startAnchor.setAttribute('fill', '#121212');\n startAnchor.setAttribute('stroke', '#5B57D1');\n startAnchor.setAttribute('stroke-width', anchorStrokeWidth);\n startAnchor.setAttribute('vector-effect', 'non-scaling-stroke');\n startAnchor.setAttribute('class', 'anchor line-anchor');\n startAnchor.style.cursor = 'grab';\n startAnchor.style.pointerEvents = 'all';\n startAnchor.dataset.index = 0;\n this.group.appendChild(startAnchor);\n this.anchors[0] = startAnchor;\n\n // End point anchor\n const endAnchor = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n endAnchor.setAttribute('cx', this.endPoint.x);\n endAnchor.setAttribute('cy', this.endPoint.y);\n endAnchor.setAttribute('r', anchorSize / 2);\n endAnchor.setAttribute('fill', '#121212');\n endAnchor.setAttribute('stroke', '#5B57D1');\n endAnchor.setAttribute('stroke-width', anchorStrokeWidth);\n endAnchor.setAttribute('vector-effect', 'non-scaling-stroke');\n endAnchor.setAttribute('class', 'anchor line-anchor');\n endAnchor.style.cursor = 'grab';\n endAnchor.style.pointerEvents = 'all';\n endAnchor.dataset.index = 1;\n this.group.appendChild(endAnchor);\n this.anchors[1] = endAnchor;\n\n // Middle anchor for curving - positioned ON the bezier curve at t=0.5\n const midX = (this.startPoint.x + this.endPoint.x) / 2;\n const midY = (this.startPoint.y + this.endPoint.y) / 2;\n let anchorMidX = midX, anchorMidY = midY;\n if (this.isCurved && this.controlPoint) {\n // Bezier point at t=0.5: 0.25*P0 + 0.5*CP + 0.25*P1\n anchorMidX = 0.25 * this.startPoint.x + 0.5 * this.controlPoint.x + 0.25 * this.endPoint.x;\n anchorMidY = 0.25 * this.startPoint.y + 0.5 * this.controlPoint.y + 0.25 * this.endPoint.y;\n }\n\n const middleAnchor = document.createElementNS('http://www.w3.org/2000/svg', 'circle');\n middleAnchor.setAttribute('cx', anchorMidX);\n middleAnchor.setAttribute('cy', anchorMidY);\n middleAnchor.setAttribute('r', anchorSize / 2);\n middleAnchor.setAttribute('fill', this.isCurved ? '#5B57D1' : '#121212');\n middleAnchor.setAttribute('stroke', '#5B57D1');\n middleAnchor.setAttribute('stroke-width', anchorStrokeWidth);\n middleAnchor.setAttribute('vector-effect', 'non-scaling-stroke');\n middleAnchor.setAttribute('class', 'anchor line-middle-anchor');\n middleAnchor.style.cursor = 'grab';\n middleAnchor.style.pointerEvents = 'all';\n middleAnchor.dataset.index = 2;\n this.group.appendChild(middleAnchor);\n this.anchors[2] = middleAnchor;\n\n // Show line sidebar\n disableAllSideBars();\n lineSideBar.classList.remove(\"hidden\");\n if (window.__showSidebarForShape) window.__showSidebarForShape('line');\n this.updateSidebar();\n }\n\n isNearAnchor(x, y) {\n if (!this.isSelected) return null;\n const buffer = 15; // Increased buffer for easier selection\n const anchorSize = 10 / (currentZoom || 1);\n\n // Check anchors\n for (let i = 0; i < this.anchors.length; i++) {\n const anchor = this.anchors[i];\n if (anchor) {\n const anchorX = parseFloat(anchor.getAttribute('cx'));\n const anchorY = parseFloat(anchor.getAttribute('cy'));\n const distance = Math.sqrt(Math.pow(x - anchorX, 2) + Math.pow(y - anchorY, 2));\n if (distance <= anchorSize / 2 + buffer) {\n return { type: 'anchor', index: i };\n }\n }\n }\n \n return null;\n }\n\n updatePosition(anchorIndex, newX, newY) {\n if (anchorIndex === 0) {\n this.startPoint.x = newX;\n this.startPoint.y = newY;\n } else if (anchorIndex === 1) {\n this.endPoint.x = newX;\n this.endPoint.y = newY;\n } else if (anchorIndex === 2) {\n // Middle anchor - dragged position is ON the curve (bezier t=0.5)\n if (!this.isCurved) {\n this.isCurved = true;\n }\n // Invert bezier formula: CP = 2*M - 0.5*(P0+P1)\n this.controlPoint = {\n x: 2 * newX - 0.5 * (this.startPoint.x + this.endPoint.x),\n y: 2 * newY - 0.5 * (this.startPoint.y + this.endPoint.y)\n };\n }\n\n this.scheduleDragRender();\n }\n\n scheduleDragRender() {\n if (this._dragRenderFrame !== null) return;\n if (typeof requestAnimationFrame !== 'function') {\n this.renderDragGeometry();\n return;\n }\n this._dragRenderFrame = requestAnimationFrame(() => {\n this._dragRenderFrame = null;\n this.renderDragGeometry();\n });\n }\n\n renderDragGeometry() {\n this.updateLineElement();\n this.updateAnchorPositions();\n if (this._hitArea) {\n const path = this.isCurved && this.controlPoint\n ? `M ${this.startPoint.x} ${this.startPoint.y} Q ${this.controlPoint.x} ${this.controlPoint.y} ${this.endPoint.x} ${this.endPoint.y}`\n : `M ${this.startPoint.x} ${this.startPoint.y} L ${this.endPoint.x} ${this.endPoint.y}`;\n this._hitArea.setAttribute('d', path);\n }\n this._updateLabelElement();\n }\n\n finalizeMove() {\n if (this._dragRenderFrame !== null && typeof cancelAnimationFrame === 'function') {\n cancelAnimationFrame(this._dragRenderFrame);\n }\n this._dragRenderFrame = null;\n this.draw();\n }\n\n updateLineElement() {\n // Update just the line element without rebuilding entire structure\n if (this.element && this.element.parentNode) {\n this.element.parentNode.removeChild(this.element);\n }\n\n const rc = rough.svg(svg);\n let lineElement;\n\n if (this.isCurved && this.controlPoint) {\n const pathData = `M ${this.startPoint.x} ${this.startPoint.y} Q ${this.controlPoint.x} ${this.controlPoint.y} ${this.endPoint.x} ${this.endPoint.y}`;\n lineElement = document.createElementNS('http://www.w3.org/2000/svg', 'path');\n lineElement.setAttribute('d', pathData);\n lineElement.setAttribute('stroke', this.options.stroke || getThemeStroke());\n lineElement.setAttribute('stroke-width', this.options.strokeWidth || lineStrokeWidth);\n lineElement.setAttribute('fill', 'none');\n lineElement.setAttribute('stroke-linecap', 'round');\n if (this.options.strokeDasharray) {\n lineElement.setAttribute('stroke-dasharray', this.options.strokeDasharray);\n }\n } else if (this.options.strokeDasharray) {\n // Dashed/dotted lines must use plain SVG (RoughJS ignores strokeDasharray)\n lineElement = document.createElementNS('http://www.w3.org/2000/svg', 'line');\n lineElement.setAttribute('x1', this.startPoint.x);\n lineElement.setAttribute('y1', this.startPoint.y);\n lineElement.setAttribute('x2', this.endPoint.x);\n lineElement.setAttribute('y2', this.endPoint.y);\n lineElement.setAttribute('stroke', this.options.stroke || getThemeStroke());\n lineElement.setAttribute('stroke-width', this.options.strokeWidth || lineStrokeWidth);\n lineElement.setAttribute('stroke-linecap', 'round');\n lineElement.setAttribute('stroke-dasharray', this.options.strokeDasharray);\n } else {\n lineElement = rc.line(\n this.startPoint.x, this.startPoint.y,\n this.endPoint.x, this.endPoint.y,\n this.options\n );\n }\n\n this.element = lineElement;\n this.group.insertBefore(lineElement, this.group.firstChild);\n }\n\n updateAnchorPositions() {\n // Update anchor positions without rebuilding them\n if (this.anchors[0]) {\n this.anchors[0].setAttribute('cx', this.startPoint.x);\n this.anchors[0].setAttribute('cy', this.startPoint.y);\n }\n if (this.anchors[1]) {\n this.anchors[1].setAttribute('cx', this.endPoint.x);\n this.anchors[1].setAttribute('cy', this.endPoint.y);\n }\n if (this.anchors[2]) {\n const midX = (this.startPoint.x + this.endPoint.x) / 2;\n const midY = (this.startPoint.y + this.endPoint.y) / 2;\n let anchorMidX = midX, anchorMidY = midY;\n if (this.isCurved && this.controlPoint) {\n // Bezier point at t=0.5: 0.25*P0 + 0.5*CP + 0.25*P1\n anchorMidX = 0.25 * this.startPoint.x + 0.5 * this.controlPoint.x + 0.25 * this.endPoint.x;\n anchorMidY = 0.25 * this.startPoint.y + 0.5 * this.controlPoint.y + 0.25 * this.endPoint.y;\n }\n this.anchors[2].setAttribute('cx', anchorMidX);\n this.anchors[2].setAttribute('cy', anchorMidY);\n this.anchors[2].setAttribute('fill', this.isCurved ? '#5B57D1' : '#121212');\n }\n }\n\n // Add move method for dragging the entire line\n move(dx, dy) {\n this.startPoint.x += dx;\n this.startPoint.y += dy;\n this.endPoint.x += dx;\n this.endPoint.y += dy;\n if (this.controlPoint) {\n this.controlPoint.x += dx;\n this.controlPoint.y += dy;\n }\n\n // Multiple pointer/snap updates in one browser frame collapse into a\n // single DOM + RoughJS update.\n this.scheduleDragRender();\n\n // Only update frame containment if not being moved by a parent frame\n if (!this.isBeingMovedByFrame) {\n this.updateFrameContainment();\n }\n\n this.updateAttachedArrows();\n }\n\n updateAttachedArrows() {\n updateArrowsForShape(this);\n }\n\n updateFrameContainment() {\n // Don't update if we're being moved by a frame\n if (this.isBeingMovedByFrame) return;\n \n let targetFrame = null;\n \n // Find which frame this shape is over\n shapes.forEach(shape => {\n if (shape.shapeName === 'frame' && shape.isShapeInFrame(this)) {\n targetFrame = shape;\n }\n });\n \n // If we have a parent frame and we're being dragged, temporarily remove clipping\n if (this.parentFrame) {\n this.parentFrame.temporarilyRemoveFromFrame(this);\n }\n \n // Update frame highlighting\n if (hoveredFrameLine && hoveredFrameLine !== targetFrame) {\n hoveredFrameLine.removeHighlight();\n }\n \n if (targetFrame && targetFrame !== hoveredFrameLine) {\n targetFrame.highlightFrame();\n }\n \n hoveredFrameLine = targetFrame;\n }\n\n contains(x, y) {\n const tolerance = 8 / (currentZoom || 1); // Slightly larger tolerance for easier selection\n \n if (this.isCurved && this.controlPoint) {\n return this.pointToQuadraticBezierDistance(x, y) <= tolerance;\n } else {\n return this.pointToLineDistance(x, y, this.startPoint.x, this.startPoint.y, this.endPoint.x, this.endPoint.y) <= tolerance;\n }\n }\n\n pointToQuadraticBezierDistance(x, y) {\n let minDistance = Infinity;\n const steps = 50; // Reduced steps for better performance\n\n for (let i = 0; i <= steps; i++) {\n const t = i / steps;\n const point = this.getQuadraticBezierPoint(t);\n const distance = Math.sqrt(Math.pow(x - point.x, 2) + Math.pow(y - point.y, 2));\n minDistance = Math.min(minDistance, distance);\n }\n\n return minDistance;\n }\n\n getQuadraticBezierPoint(t) {\n if (!this.controlPoint) return this.startPoint;\n \n const mt = 1 - t;\n return {\n x: mt * mt * this.startPoint.x + 2 * mt * t * this.controlPoint.x + t * t * this.endPoint.x,\n y: mt * mt * this.startPoint.y + 2 * mt * t * this.controlPoint.y + t * t * this.endPoint.y\n };\n }\n\n pointToLineDistance(x, y, x1, y1, x2, y2) {\n const A = x - x1;\n const B = y - y1;\n const C = x2 - x1;\n const D = y2 - y1;\n const dot = A * C + B * D;\n const lenSq = C * C + D * D;\n let param = -1;\n if (lenSq !== 0) param = dot / lenSq;\n let xx, yy;\n if (param < 0) {\n xx = x1;\n yy = y1;\n } else if (param > 1) {\n xx = x2;\n yy = y2;\n } else {\n xx = x1 + param * C;\n yy = y1 + param * D;\n }\n const dx = x - xx;\n const dy = y - yy;\n return Math.sqrt(dx * dx + dy * dy);\n }\n\n // No-op: React sidebar handles UI updates via Zustand store\n updateSidebar() {}\n}\n\nexport { Line };\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["rc"]
|
|
7
|
+
}
|
|
@@ -3,19 +3,19 @@ import {
|
|
|
3
3
|
calculateSnap,
|
|
4
4
|
clearSnapGuides,
|
|
5
5
|
updateAttachedArrows
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-2U47AU6Y.js";
|
|
7
7
|
import {
|
|
8
8
|
pushCreateAction,
|
|
9
9
|
pushDeleteAction,
|
|
10
10
|
pushFrameAttachmentAction,
|
|
11
11
|
pushOptionsChangeAction,
|
|
12
12
|
pushTransformAction
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-TFCBCWL5.js";
|
|
14
14
|
|
|
15
15
|
// src/tools/freehandTool.js
|
|
16
16
|
function getThemeStroke() {
|
|
17
17
|
if (typeof document === "undefined") return "#fff";
|
|
18
|
-
return document.body && document.body.classList.contains("theme-dark") ? "#fff" : "#
|
|
18
|
+
return document.body && document.body.classList.contains("theme-dark") ? "#fff" : "#000000";
|
|
19
19
|
}
|
|
20
20
|
window.__updateArrowsForShape = updateAttachedArrows;
|
|
21
21
|
var strokeColors = document.querySelectorAll(".strokeColors span");
|
|
@@ -561,4 +561,4 @@ export {
|
|
|
561
561
|
handleMouseMove,
|
|
562
562
|
handleMouseUp
|
|
563
563
|
};
|
|
564
|
-
//# sourceMappingURL=chunk-
|
|
564
|
+
//# sourceMappingURL=chunk-LXEHYCKH.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/tools/freehandTool.js"],
|
|
4
|
-
"sourcesContent": ["/* eslint-disable */\n// Freehand tool event handlers - extracted from canvasStroke.js\nimport { pushCreateAction, pushDeleteAction, pushOptionsChangeAction, pushTransformAction, pushFrameAttachmentAction } from '../core/UndoRedo.js';\nimport { updateAttachedArrows as updateArrowsForShape, cleanupAttachments } from './arrowTool.js';\nimport { calculateSnap, clearSnapGuides } from '../core/SnapGuides.js';\n\nfunction getThemeStroke() { if (typeof document === \"undefined\") return \"#fff\"; return document.body && document.body.classList.contains(\"theme-dark\") ? \"#fff\" : \"#1a1a2e\"; }\n\n// Expose updateArrowsForShape globally so FreehandStroke.updateAttachedArrows() can call it\n// (FreehandStroke.js is a separate module and cannot import freehandTool's local bindings)\nwindow.__updateArrowsForShape = updateArrowsForShape;\n\nconst strokeColors = document.querySelectorAll(\".strokeColors span\");\nconst strokeThicknesses = document.querySelectorAll(\".strokeThickness span\");\nconst strokeStyles = document.querySelectorAll(\".strokeStyleSpan\");\nconst strokeTapers = document.querySelectorAll(\".strokeTaperSpan\");\nconst strokeRoughnesses = document.querySelectorAll(\".strokeRoughnessSpan\");\nlet strokeColor = null;\nlet strokeThickness = 2;\nlet strokeStyleValue = \"solid\";\nlet strokeThinning = 0;\nlet strokeRoughnessValue = \"smooth\";\nlet points = [];\nlet isDrawingStroke = false;\nlet currentStroke = null;\nlet strokeOpacity = 1;\n\n\nlet isDraggingStroke = false;\nlet isResizingStroke = false;\nlet isRotatingStroke = false;\nlet dragOldPosStroke = null;\nlet resizingAnchorIndex = null;\nlet startRotationMouseAngle = null;\nlet startShapeRotation = null;\nlet startX, startY;\n\n// Frame attachment variables\nlet draggedShapeInitialFrameStroke = null;\nlet hoveredFrameStroke = null;\n\n// Enhanced mouse tracking with better point collection\nlet lastPoint = null;\nlet lastTime = 0;\nconst minDistance = 2; // Minimum distance between points\nconst maxDistance = 15; // Maximum distance for interpolation\nlet isdraggingOpacity = false;\n\n\nfunction getSvgCoordinates(event) {\n const rect = svg.getBoundingClientRect();\n const scaleX = currentViewBox.width / rect.width;\n const scaleY = currentViewBox.height / rect.height;\n\n const svgX = currentViewBox.x + (event.clientX - rect.left) * scaleX;\n const svgY = currentViewBox.y + (event.clientY - rect.top) * scaleY;\n\n return { x: svgX, y: svgY };\n}\n\nfunction getSvgPathFromStroke(stroke) {\n if (!stroke.length) return '';\n \n // Use more sophisticated curve fitting\n const pathData = [];\n pathData.push('M', stroke[0][0], stroke[0][1]);\n \n for (let i = 1; i < stroke.length - 1; i++) {\n const curr = stroke[i];\n const next = stroke[i + 1];\n \n // Calculate control points for smoother curves\n const cpX = curr[0] + (next[0] - curr[0]) * 0.5;\n const cpY = curr[1] + (next[1] - curr[1]) * 0.5;\n \n pathData.push('Q', curr[0], curr[1], cpX, cpY);\n }\n \n // Add final point\n if (stroke.length > 1) {\n const lastPoint = stroke[stroke.length - 1];\n pathData.push('L', lastPoint[0], lastPoint[1]);\n }\n \n return pathData.join(' ');\n}\n\n// Frame containment check for a freehand stroke during drag.\n// Lives here (not in FreehandStroke.js) because it needs isDraggingStroke & hoveredFrameStroke.\nfunction updateFrameContainmentForStroke(shape) {\n if (shape.isBeingMovedByFrame) return;\n\n let targetFrame = null;\n shapes.forEach(s => {\n if (s.shapeName === 'frame' && s.isShapeInFrame(shape)) {\n targetFrame = s;\n }\n });\n\n if (shape.parentFrame && isDraggingStroke) {\n shape.parentFrame.temporarilyRemoveFromFrame(shape);\n }\n\n if (hoveredFrameStroke && hoveredFrameStroke !== targetFrame) {\n hoveredFrameStroke.removeHighlight();\n }\n if (targetFrame && targetFrame !== hoveredFrameStroke) {\n targetFrame.highlightFrame();\n }\n hoveredFrameStroke = targetFrame;\n}\n\n// Delete functionality\nfunction deleteCurrentShape() {\n if (currentShape && currentShape.shapeName === 'freehandStroke') {\n const idx = shapes.indexOf(currentShape);\n if (idx !== -1) shapes.splice(idx, 1);\n if (currentShape.group.parentNode) {\n currentShape.group.parentNode.removeChild(currentShape.group);\n }\n pushDeleteAction(currentShape);\n currentShape = null;\n disableAllSideBars();\n }\n}\n\ndocument.addEventListener('keydown', (e) => {\n if (e.key === 'Delete' && currentShape && currentShape.shapeName === 'freehandStroke') {\n deleteCurrentShape();\n }\n});\n\n// Event handlers\nfunction handleMouseDown(e) {\n const { x, y } = getSvgCoordinates(e);\n \n if (isPaintToolActive) {\n isDrawingStroke = true;\n const pressure = e.pressure || 0.5;\n points = [[x, y, pressure]];\n lastPoint = [x, y, pressure];\n lastTime = Date.now();\n\n currentStroke = new FreehandStroke(points, {\n stroke: strokeColor ?? getThemeStroke(),\n strokeWidth: strokeThickness\n });\n\n shapes.push(currentStroke);\n currentShape = currentStroke;\n } else if (isSelectionToolActive) {\n let clickedOnShape = false;\n \n // Check if clicking on current selected stroke\n if (currentShape && currentShape.shapeName === 'freehandStroke' && currentShape.isSelected) {\n const anchorInfo = currentShape.isNearAnchor(x, y);\n if (anchorInfo) {\n if (anchorInfo.type === 'resize') {\n isResizingStroke = true;\n resizingAnchorIndex = anchorInfo.index;\n dragOldPosStroke = cloneStrokeData(currentShape);\n } else if (anchorInfo.type === 'rotate') {\n isRotatingStroke = true;\n const centerX = currentShape.boundingBox.x + currentShape.boundingBox.width / 2;\n const centerY = currentShape.boundingBox.y + currentShape.boundingBox.height / 2;\n startRotationMouseAngle = Math.atan2(y - centerY, x - centerX) * 180 / Math.PI;\n startShapeRotation = currentShape.rotation;\n dragOldPosStroke = cloneStrokeData(currentShape);\n }\n clickedOnShape = true;\n } else if (currentShape.contains(x, y)) {\n isDraggingStroke = true;\n dragOldPosStroke = cloneStrokeData(currentShape);\n \n // Store initial frame state\n draggedShapeInitialFrameStroke = currentShape.parentFrame || null;\n \n // Temporarily remove from frame clipping if dragging\n if (currentShape.parentFrame) {\n currentShape.parentFrame.temporarilyRemoveFromFrame(currentShape);\n }\n \n startX = x;\n startY = y;\n clickedOnShape = true;\n }\n }\n\n // If not clicking on selected shape, check for other shapes\n if (!clickedOnShape) {\n let shapeToSelect = null;\n for (let i = shapes.length - 1; i >= 0; i--) {\n const shape = shapes[i];\n if (shape instanceof FreehandStroke && shape.contains(x, y)) {\n shapeToSelect = shape;\n break;\n }\n }\n\n if (currentShape && currentShape !== shapeToSelect) {\n currentShape.deselectStroke();\n currentShape = null;\n }\n\n if (shapeToSelect) {\n currentShape = shapeToSelect;\n currentShape.selectStroke(); // This will show the sidebar\n \n const anchorInfo = currentShape.isNearAnchor(x, y);\n if (anchorInfo) {\n if (anchorInfo.type === 'resize') {\n isResizingStroke = true;\n resizingAnchorIndex = anchorInfo.index;\n dragOldPosStroke = cloneStrokeData(currentShape);\n } else if (anchorInfo.type === 'rotate') {\n isRotatingStroke = true;\n const centerX = currentShape.boundingBox.x + currentShape.boundingBox.width / 2;\n const centerY = currentShape.boundingBox.y + currentShape.boundingBox.height / 2;\n startRotationMouseAngle = Math.atan2(y - centerY, x - centerX) * 180 / Math.PI;\n startShapeRotation = currentShape.rotation;\n dragOldPosStroke = cloneStrokeData(currentShape);\n }\n } else {\n isDraggingStroke = true;\n dragOldPosStroke = cloneStrokeData(currentShape);\n \n // Store initial frame state\n draggedShapeInitialFrameStroke = currentShape.parentFrame || null;\n \n // Temporarily remove from frame clipping if dragging\n if (currentShape.parentFrame) {\n currentShape.parentFrame.temporarilyRemoveFromFrame(currentShape);\n }\n \n startX = x;\n startY = y;\n }\n clickedOnShape = true;\n }\n }\n\n if (!clickedOnShape && currentShape) {\n currentShape.deselectStroke();\n currentShape = null;\n disableAllSideBars(); // Hide sidebar when deselecting\n }\n }\n}\n\nfunction handleMouseMove(e) {\n let { x, y } = getSvgCoordinates(e);\n const currentTime = Date.now();\n \n // Keep lastMousePos in screen coordinates for other functions\n const svgRect = svg.getBoundingClientRect();\n lastMousePos = {\n x: e.clientX - svgRect.left, \n y: e.clientY - svgRect.top\n };\n \n if (isDrawingStroke && isPaintToolActive) {\n const pressure = e.pressure || 0.5;\n\n // Shift key constrains to straight line from start point\n if (e.shiftKey && points.length > 0) {\n const startX = points[0][0], startY = points[0][1];\n const dx = x - startX, dy = y - startY;\n const angle = Math.atan2(dy, dx);\n const snapAngle = Math.round(angle / (Math.PI / 4)) * (Math.PI / 4);\n const dist = Math.sqrt(dx * dx + dy * dy);\n x = startX + dist * Math.cos(snapAngle);\n y = startY + dist * Math.sin(snapAngle);\n // Reset points to just start + current for straight line\n points = [points[0], [x, y, pressure]];\n lastPoint = [x, y, pressure];\n currentStroke.points = points;\n currentStroke.draw();\n return;\n }\n\n if (lastPoint) {\n const dx = x - lastPoint[0];\n const dy = y - lastPoint[1];\n const distance = Math.sqrt(dx * dx + dy * dy);\n \n // Only add point if it's far enough from the last one\n if (distance >= minDistance) {\n // If distance is too large, interpolate points\n if (distance > maxDistance) {\n const steps = Math.ceil(distance / maxDistance);\n for (let i = 1; i < steps; i++) {\n const t = i / steps;\n const interpX = lastPoint[0] + dx * t;\n const interpY = lastPoint[1] + dy * t;\n const interpPressure = lastPoint[2] + (pressure - lastPoint[2]) * t;\n points.push([interpX, interpY, interpPressure]);\n }\n }\n \n // Calculate velocity-based pressure\n const timeDelta = currentTime - lastTime;\n const velocity = distance / Math.max(timeDelta, 1);\n const velocityPressure = Math.min(1, Math.max(0.1, 1 - velocity * 0.02));\n const finalPressure = (pressure + velocityPressure) * 0.5;\n \n points.push([x, y, finalPressure]);\n currentStroke.points = points;\n currentStroke.draw();\n \n lastPoint = [x, y, finalPressure];\n lastTime = currentTime;\n }\n } else {\n lastPoint = [x, y, pressure];\n lastTime = currentTime;\n }\n \n // Check for frame containment while drawing (but don't apply clipping yet)\n shapes.forEach(frame => {\n if (frame.shapeName === 'frame') {\n if (frame.isShapeInFrame(currentStroke)) {\n frame.highlightFrame();\n hoveredFrameStroke = frame;\n } else if (hoveredFrameStroke === frame) {\n frame.removeHighlight();\n hoveredFrameStroke = null;\n }\n }\n });\n } else if (isDraggingStroke && currentShape && currentShape.isSelected) {\n const dx = x - startX;\n const dy = y - startY;\n currentShape.move(dx, dy);\n startX = x;\n startY = y;\n\n // Frame containment update (must live here where isDraggingStroke is in scope)\n if (!currentShape.isBeingMovedByFrame) {\n updateFrameContainmentForStroke(currentShape);\n }\n\n // Update arrows attached to this shape\n currentShape.updateAttachedArrows();\n\n // Snap guides\n if (window.__sketchStoreApi && window.__sketchStoreApi.getState().snapToObjects) {\n const snap = calculateSnap(currentShape, e.shiftKey, e.clientX, e.clientY);\n if (snap.dx || snap.dy) {\n currentShape.move(snap.dx, snap.dy);\n }\n } else {\n clearSnapGuides();\n }\n } else if (isResizingStroke && currentShape && currentShape.isSelected) {\n currentShape.updatePosition(resizingAnchorIndex, x, y);\n } else if (isRotatingStroke && currentShape && currentShape.isSelected) {\n const centerX = currentShape.boundingBox.x + currentShape.boundingBox.width / 2;\n const centerY = currentShape.boundingBox.y + currentShape.boundingBox.height / 2;\n const currentMouseAngle = Math.atan2(y - centerY, x - centerX) * 180 / Math.PI;\n const angleDiff = currentMouseAngle - startRotationMouseAngle;\n currentShape.rotate(startShapeRotation + angleDiff);\n currentShape.draw();\n }\n}\n\nfunction handleMouseUp(e) {\n if (isDrawingStroke) {\n isDrawingStroke = false;\n lastPoint = null;\n \n // Final smoothing pass after drawing is complete\n if (currentStroke && currentStroke.points.length >= 2) {\n currentStroke.draw(); // Redraw with final smoothing\n pushCreateAction(currentStroke);\n\n // Check for frame containment and track attachment\n const finalFrame = hoveredFrameStroke;\n if (finalFrame) {\n finalFrame.addShapeToFrame(currentStroke);\n // Track the attachment for undo\n pushFrameAttachmentAction(finalFrame, currentStroke, 'attach', null);\n }\n\n // Auto-select the drawn stroke and switch to selection tool\n const drawnShape = currentStroke;\n if (window.__sketchStoreApi) window.__sketchStoreApi.setActiveTool('select', { afterDraw: true });\n currentShape = drawnShape;\n currentShape.selectStroke();\n } else if (currentStroke) {\n // Remove strokes that are too small\n shapes.pop();\n if (currentStroke.group.parentNode) {\n currentStroke.group.parentNode.removeChild(currentStroke.group);\n }\n }\n\n // Clear frame highlighting\n if (hoveredFrameStroke) {\n hoveredFrameStroke.removeHighlight();\n hoveredFrameStroke = null;\n }\n\n currentStroke = null;\n }\n \n if ((isDraggingStroke || isResizingStroke || isRotatingStroke) && dragOldPosStroke && currentShape) {\n const newPos = cloneStrokeData(currentShape);\n const stateChanged = \n JSON.stringify(dragOldPosStroke.points) !== JSON.stringify(newPos.points) ||\n dragOldPosStroke.rotation !== newPos.rotation;\n \n const oldPos = {\n ...dragOldPosStroke,\n parentFrame: draggedShapeInitialFrameStroke\n };\n // Issue #34 bug #2: for drag, hoveredFrameStroke is the actual\n // destination \u2014 currentShape.parentFrame at this point is still\n // the OLD frame. Resize / rotate don't track hover, fall back to\n // the shape's own parent.\n const newPosForUndo = {\n ...newPos,\n parentFrame: isDraggingStroke\n ? (hoveredFrameStroke || null)\n : currentShape.parentFrame,\n };\n \n const frameChanged = oldPos.parentFrame !== newPosForUndo.parentFrame;\n \n if (stateChanged || frameChanged) {\n pushTransformAction(currentShape, oldPos, newPosForUndo);\n }\n \n // Handle frame containment changes after drag\n if (isDraggingStroke) {\n const finalFrame = hoveredFrameStroke;\n \n // If shape moved to a different frame\n if (draggedShapeInitialFrameStroke !== finalFrame) {\n // Remove from initial frame\n if (draggedShapeInitialFrameStroke) {\n draggedShapeInitialFrameStroke.removeShapeFromFrame(currentShape);\n }\n \n // Add to new frame\n if (finalFrame) {\n finalFrame.addShapeToFrame(currentShape);\n }\n \n // Track the frame change for undo\n if (frameChanged) {\n pushFrameAttachmentAction(finalFrame || draggedShapeInitialFrameStroke, currentShape, \n finalFrame ? 'attach' : 'detach', draggedShapeInitialFrameStroke);\n }\n } else if (draggedShapeInitialFrameStroke) {\n // Shape stayed in same frame, restore clipping\n draggedShapeInitialFrameStroke.restoreToFrame(currentShape);\n }\n }\n \n dragOldPosStroke = null;\n draggedShapeInitialFrameStroke = null;\n }\n \n // Clear frame highlighting\n if (hoveredFrameStroke) {\n hoveredFrameStroke.removeHighlight();\n hoveredFrameStroke = null;\n }\n \n // Bake accumulated move offset into point coordinates\n if (currentShape && typeof currentShape.finalizeMove === 'function') {\n currentShape.finalizeMove();\n }\n clearSnapGuides();\n isDraggingStroke = false;\n isResizingStroke = false;\n isRotatingStroke = false;\n resizingAnchorIndex = null;\n startRotationMouseAngle = null;\n startShapeRotation = null;\n svg.style.cursor = 'default';\n}\n\n// Color and thickness selection\nstrokeColors.forEach(span => {\n span.addEventListener(\"click\", (event) => {\n strokeColors.forEach(el => el.classList.remove(\"selected\"));\n span.classList.add(\"selected\");\n \n if (currentShape instanceof FreehandStroke && currentShape.isSelected) {\n const oldOptions = {...currentShape.options};\n currentShape.options.stroke = span.getAttribute(\"data-id\");\n currentShape.draw();\n pushOptionsChangeAction(currentShape, oldOptions);\n } else {\n strokeColor = span.getAttribute(\"data-id\");\n }\n });\n});\n\nstrokeThicknesses.forEach(span => {\n span.addEventListener(\"click\", (event) => {\n strokeThicknesses.forEach(el => el.classList.remove(\"selected\"));\n span.classList.add(\"selected\");\n \n if (currentShape instanceof FreehandStroke && currentShape.isSelected) {\n const oldOptions = {...currentShape.options};\n currentShape.options.strokeWidth = parseInt(span.getAttribute(\"data-id\"));\n currentShape.draw();\n pushOptionsChangeAction(currentShape, oldOptions);\n } else {\n strokeThickness = parseInt(span.getAttribute(\"data-id\"));\n }\n });\n});\n\n\nstrokeStyles.forEach(span => {\n span.addEventListener(\"click\", () => {\n strokeStyles.forEach(el => el.classList.remove(\"selected\"));\n span.classList.add(\"selected\");\n const val = span.getAttribute(\"data-id\");\n if (currentShape instanceof FreehandStroke && currentShape.isSelected) {\n const oldOptions = {...currentShape.options};\n currentShape.options.strokeStyle = val;\n currentShape.draw();\n pushOptionsChangeAction(currentShape, oldOptions);\n } else {\n strokeStyleValue = val;\n }\n });\n});\n\nstrokeTapers.forEach(span => {\n span.addEventListener(\"click\", () => {\n strokeTapers.forEach(el => el.classList.remove(\"selected\"));\n span.classList.add(\"selected\");\n const val = parseFloat(span.getAttribute(\"data-id\"));\n if (currentShape instanceof FreehandStroke && currentShape.isSelected) {\n const oldOptions = {...currentShape.options};\n currentShape.options.thinning = val;\n currentShape.draw();\n pushOptionsChangeAction(currentShape, oldOptions);\n } else {\n strokeThinning = val;\n }\n });\n});\n\nstrokeRoughnesses.forEach(span => {\n span.addEventListener(\"click\", () => {\n strokeRoughnesses.forEach(el => el.classList.remove(\"selected\"));\n span.classList.add(\"selected\");\n const val = span.getAttribute(\"data-id\");\n if (currentShape instanceof FreehandStroke && currentShape.isSelected) {\n const oldOptions = {...currentShape.options};\n currentShape.options.roughness = val;\n currentShape.draw();\n pushOptionsChangeAction(currentShape, oldOptions);\n } else {\n strokeRoughnessValue = val;\n }\n });\n});\n\ndocument.getElementById(\"strokeOpacity\")?.addEventListener(\"mousedown\", (event) => {\n isdraggingOpacity = true;\n});\n\ndocument.getElementById(\"strokeOpacity\")?.addEventListener(\"mousemove\", (event) => {\n if(isdraggingOpacity) \n {\n const slider = document.getElementById(\"strokeOpacity\");\n const rect = slider.getBoundingClientRect();\n const mouseX = event.clientX - rect.left;\n const percent = Math.max(0, Math.min(100, (mouseX / rect.width) * 100));\n document.getElementById(\"opacityContainerValue\").textContent = percent.toFixed(0);\n const opacity = percent / 100; \n if (currentShape instanceof FreehandStroke && currentShape.isSelected) {\n const oldOptions = {...currentShape.options};\n currentShape.options.strokeOpacity = opacity; \n currentShape.draw();\n pushOptionsChangeAction(currentShape, oldOptions);\n } else {\n strokeOpacity = opacity; \n }\n }\n});\n\ndocument.getElementById(\"strokeOpacity\")?.addEventListener(\"mouseup\", (event) => {\n isdraggingOpacity = false;\n});\n\nfunction cloneOptions(options) {\n return JSON.parse(JSON.stringify(options));\n}\n\nfunction cloneStrokeData(stroke) {\n return {\n points: JSON.parse(JSON.stringify(stroke.points)),\n rotation: stroke.rotation,\n options: cloneOptions(stroke.options)\n };\n}\n\n// Event listeners\n// svg.addEventListener('pointerdown', handleMouseDown);\n// svg.addEventListener('pointermove', handleMouseMove);\n// svg.addEventListener('pointerup', handleMouseUp);\n\n// Bridge freehand tool settings to React sidebar\nwindow.freehandToolSettings = {\n get strokeColor() { return strokeColor; },\n set strokeColor(v) { strokeColor = v; },\n get strokeWidth() { return strokeThickness; },\n set strokeWidth(v) { strokeThickness = v; },\n get strokeStyle() { return strokeStyleValue; },\n set strokeStyle(v) { strokeStyleValue = v; },\n get thinning() { return strokeThinning; },\n set thinning(v) { strokeThinning = v; },\n get roughness() { return strokeRoughnessValue; },\n set roughness(v) { strokeRoughnessValue = v; },\n get opacity() { return strokeOpacity; },\n set opacity(v) { strokeOpacity = v; },\n};\nwindow.updateSelectedFreehandStyle = function(changes) {\n if (currentShape && currentShape.shapeName === 'freehandStroke' && currentShape.isSelected) {\n if (changes.stroke !== undefined) { strokeColor = changes.stroke; currentShape.options.stroke = changes.stroke; }\n if (changes.strokeWidth !== undefined) { strokeThickness = changes.strokeWidth; currentShape.options.strokeWidth = changes.strokeWidth; }\n if (changes.thinning !== undefined) { strokeThinning = changes.thinning; currentShape.options.thinning = changes.thinning; }\n if (changes.roughness !== undefined) { strokeRoughnessValue = changes.roughness; currentShape.options.roughness = changes.roughness; }\n if (changes.opacity !== undefined) { strokeOpacity = changes.opacity; currentShape.options.strokeOpacity = changes.opacity; }\n currentShape.draw();\n } else {\n if (changes.stroke !== undefined) strokeColor = changes.stroke;\n if (changes.strokeWidth !== undefined) strokeThickness = changes.strokeWidth;\n if (changes.thinning !== undefined) strokeThinning = changes.thinning;\n if (changes.roughness !== undefined) strokeRoughnessValue = changes.roughness;\n if (changes.opacity !== undefined) strokeOpacity = changes.opacity;\n }\n};\n\n// Safety net: if mouseup fires outside the SVG canvas (e.g. on toolbar/overlay),\n// ensure we stop drawing so the stroke doesn't continue when pointer re-enters.\nwindow.addEventListener('pointerup', () => {\n if (isDrawingStroke) {\n isDrawingStroke = false;\n lastPoint = null;\n if (currentStroke && currentStroke.points && currentStroke.points.length >= 2) {\n currentStroke.draw();\n }\n currentStroke = null;\n }\n});\n\n// Also listen for visibility change (e.g. alt-tab while drawing)\ndocument.addEventListener('visibilitychange', () => {\n if (document.hidden && isDrawingStroke) {\n isDrawingStroke = false;\n lastPoint = null;\n currentStroke = null;\n }\n});\n\nexport\n{\n handleMouseDown as handleFreehandMouseDown,\n handleMouseMove as handleFreehandMouseMove,\n handleMouseUp as handleFreehandMouseUp,\n}\n"],
|
|
4
|
+
"sourcesContent": ["/* eslint-disable */\n// Freehand tool event handlers - extracted from canvasStroke.js\nimport { pushCreateAction, pushDeleteAction, pushOptionsChangeAction, pushTransformAction, pushFrameAttachmentAction } from '../core/UndoRedo.js';\nimport { updateAttachedArrows as updateArrowsForShape, cleanupAttachments } from './arrowTool.js';\nimport { calculateSnap, clearSnapGuides } from '../core/SnapGuides.js';\n\nfunction getThemeStroke() { if (typeof document === \"undefined\") return \"#fff\"; return document.body && document.body.classList.contains(\"theme-dark\") ? \"#fff\" : \"#000000\"; }\n\n// Expose updateArrowsForShape globally so FreehandStroke.updateAttachedArrows() can call it\n// (FreehandStroke.js is a separate module and cannot import freehandTool's local bindings)\nwindow.__updateArrowsForShape = updateArrowsForShape;\n\nconst strokeColors = document.querySelectorAll(\".strokeColors span\");\nconst strokeThicknesses = document.querySelectorAll(\".strokeThickness span\");\nconst strokeStyles = document.querySelectorAll(\".strokeStyleSpan\");\nconst strokeTapers = document.querySelectorAll(\".strokeTaperSpan\");\nconst strokeRoughnesses = document.querySelectorAll(\".strokeRoughnessSpan\");\nlet strokeColor = null;\nlet strokeThickness = 2;\nlet strokeStyleValue = \"solid\";\nlet strokeThinning = 0;\nlet strokeRoughnessValue = \"smooth\";\nlet points = [];\nlet isDrawingStroke = false;\nlet currentStroke = null;\nlet strokeOpacity = 1;\n\n\nlet isDraggingStroke = false;\nlet isResizingStroke = false;\nlet isRotatingStroke = false;\nlet dragOldPosStroke = null;\nlet resizingAnchorIndex = null;\nlet startRotationMouseAngle = null;\nlet startShapeRotation = null;\nlet startX, startY;\n\n// Frame attachment variables\nlet draggedShapeInitialFrameStroke = null;\nlet hoveredFrameStroke = null;\n\n// Enhanced mouse tracking with better point collection\nlet lastPoint = null;\nlet lastTime = 0;\nconst minDistance = 2; // Minimum distance between points\nconst maxDistance = 15; // Maximum distance for interpolation\nlet isdraggingOpacity = false;\n\n\nfunction getSvgCoordinates(event) {\n const rect = svg.getBoundingClientRect();\n const scaleX = currentViewBox.width / rect.width;\n const scaleY = currentViewBox.height / rect.height;\n\n const svgX = currentViewBox.x + (event.clientX - rect.left) * scaleX;\n const svgY = currentViewBox.y + (event.clientY - rect.top) * scaleY;\n\n return { x: svgX, y: svgY };\n}\n\nfunction getSvgPathFromStroke(stroke) {\n if (!stroke.length) return '';\n \n // Use more sophisticated curve fitting\n const pathData = [];\n pathData.push('M', stroke[0][0], stroke[0][1]);\n \n for (let i = 1; i < stroke.length - 1; i++) {\n const curr = stroke[i];\n const next = stroke[i + 1];\n \n // Calculate control points for smoother curves\n const cpX = curr[0] + (next[0] - curr[0]) * 0.5;\n const cpY = curr[1] + (next[1] - curr[1]) * 0.5;\n \n pathData.push('Q', curr[0], curr[1], cpX, cpY);\n }\n \n // Add final point\n if (stroke.length > 1) {\n const lastPoint = stroke[stroke.length - 1];\n pathData.push('L', lastPoint[0], lastPoint[1]);\n }\n \n return pathData.join(' ');\n}\n\n// Frame containment check for a freehand stroke during drag.\n// Lives here (not in FreehandStroke.js) because it needs isDraggingStroke & hoveredFrameStroke.\nfunction updateFrameContainmentForStroke(shape) {\n if (shape.isBeingMovedByFrame) return;\n\n let targetFrame = null;\n shapes.forEach(s => {\n if (s.shapeName === 'frame' && s.isShapeInFrame(shape)) {\n targetFrame = s;\n }\n });\n\n if (shape.parentFrame && isDraggingStroke) {\n shape.parentFrame.temporarilyRemoveFromFrame(shape);\n }\n\n if (hoveredFrameStroke && hoveredFrameStroke !== targetFrame) {\n hoveredFrameStroke.removeHighlight();\n }\n if (targetFrame && targetFrame !== hoveredFrameStroke) {\n targetFrame.highlightFrame();\n }\n hoveredFrameStroke = targetFrame;\n}\n\n// Delete functionality\nfunction deleteCurrentShape() {\n if (currentShape && currentShape.shapeName === 'freehandStroke') {\n const idx = shapes.indexOf(currentShape);\n if (idx !== -1) shapes.splice(idx, 1);\n if (currentShape.group.parentNode) {\n currentShape.group.parentNode.removeChild(currentShape.group);\n }\n pushDeleteAction(currentShape);\n currentShape = null;\n disableAllSideBars();\n }\n}\n\ndocument.addEventListener('keydown', (e) => {\n if (e.key === 'Delete' && currentShape && currentShape.shapeName === 'freehandStroke') {\n deleteCurrentShape();\n }\n});\n\n// Event handlers\nfunction handleMouseDown(e) {\n const { x, y } = getSvgCoordinates(e);\n \n if (isPaintToolActive) {\n isDrawingStroke = true;\n const pressure = e.pressure || 0.5;\n points = [[x, y, pressure]];\n lastPoint = [x, y, pressure];\n lastTime = Date.now();\n\n currentStroke = new FreehandStroke(points, {\n stroke: strokeColor ?? getThemeStroke(),\n strokeWidth: strokeThickness\n });\n\n shapes.push(currentStroke);\n currentShape = currentStroke;\n } else if (isSelectionToolActive) {\n let clickedOnShape = false;\n \n // Check if clicking on current selected stroke\n if (currentShape && currentShape.shapeName === 'freehandStroke' && currentShape.isSelected) {\n const anchorInfo = currentShape.isNearAnchor(x, y);\n if (anchorInfo) {\n if (anchorInfo.type === 'resize') {\n isResizingStroke = true;\n resizingAnchorIndex = anchorInfo.index;\n dragOldPosStroke = cloneStrokeData(currentShape);\n } else if (anchorInfo.type === 'rotate') {\n isRotatingStroke = true;\n const centerX = currentShape.boundingBox.x + currentShape.boundingBox.width / 2;\n const centerY = currentShape.boundingBox.y + currentShape.boundingBox.height / 2;\n startRotationMouseAngle = Math.atan2(y - centerY, x - centerX) * 180 / Math.PI;\n startShapeRotation = currentShape.rotation;\n dragOldPosStroke = cloneStrokeData(currentShape);\n }\n clickedOnShape = true;\n } else if (currentShape.contains(x, y)) {\n isDraggingStroke = true;\n dragOldPosStroke = cloneStrokeData(currentShape);\n \n // Store initial frame state\n draggedShapeInitialFrameStroke = currentShape.parentFrame || null;\n \n // Temporarily remove from frame clipping if dragging\n if (currentShape.parentFrame) {\n currentShape.parentFrame.temporarilyRemoveFromFrame(currentShape);\n }\n \n startX = x;\n startY = y;\n clickedOnShape = true;\n }\n }\n\n // If not clicking on selected shape, check for other shapes\n if (!clickedOnShape) {\n let shapeToSelect = null;\n for (let i = shapes.length - 1; i >= 0; i--) {\n const shape = shapes[i];\n if (shape instanceof FreehandStroke && shape.contains(x, y)) {\n shapeToSelect = shape;\n break;\n }\n }\n\n if (currentShape && currentShape !== shapeToSelect) {\n currentShape.deselectStroke();\n currentShape = null;\n }\n\n if (shapeToSelect) {\n currentShape = shapeToSelect;\n currentShape.selectStroke(); // This will show the sidebar\n \n const anchorInfo = currentShape.isNearAnchor(x, y);\n if (anchorInfo) {\n if (anchorInfo.type === 'resize') {\n isResizingStroke = true;\n resizingAnchorIndex = anchorInfo.index;\n dragOldPosStroke = cloneStrokeData(currentShape);\n } else if (anchorInfo.type === 'rotate') {\n isRotatingStroke = true;\n const centerX = currentShape.boundingBox.x + currentShape.boundingBox.width / 2;\n const centerY = currentShape.boundingBox.y + currentShape.boundingBox.height / 2;\n startRotationMouseAngle = Math.atan2(y - centerY, x - centerX) * 180 / Math.PI;\n startShapeRotation = currentShape.rotation;\n dragOldPosStroke = cloneStrokeData(currentShape);\n }\n } else {\n isDraggingStroke = true;\n dragOldPosStroke = cloneStrokeData(currentShape);\n \n // Store initial frame state\n draggedShapeInitialFrameStroke = currentShape.parentFrame || null;\n \n // Temporarily remove from frame clipping if dragging\n if (currentShape.parentFrame) {\n currentShape.parentFrame.temporarilyRemoveFromFrame(currentShape);\n }\n \n startX = x;\n startY = y;\n }\n clickedOnShape = true;\n }\n }\n\n if (!clickedOnShape && currentShape) {\n currentShape.deselectStroke();\n currentShape = null;\n disableAllSideBars(); // Hide sidebar when deselecting\n }\n }\n}\n\nfunction handleMouseMove(e) {\n let { x, y } = getSvgCoordinates(e);\n const currentTime = Date.now();\n \n // Keep lastMousePos in screen coordinates for other functions\n const svgRect = svg.getBoundingClientRect();\n lastMousePos = {\n x: e.clientX - svgRect.left, \n y: e.clientY - svgRect.top\n };\n \n if (isDrawingStroke && isPaintToolActive) {\n const pressure = e.pressure || 0.5;\n\n // Shift key constrains to straight line from start point\n if (e.shiftKey && points.length > 0) {\n const startX = points[0][0], startY = points[0][1];\n const dx = x - startX, dy = y - startY;\n const angle = Math.atan2(dy, dx);\n const snapAngle = Math.round(angle / (Math.PI / 4)) * (Math.PI / 4);\n const dist = Math.sqrt(dx * dx + dy * dy);\n x = startX + dist * Math.cos(snapAngle);\n y = startY + dist * Math.sin(snapAngle);\n // Reset points to just start + current for straight line\n points = [points[0], [x, y, pressure]];\n lastPoint = [x, y, pressure];\n currentStroke.points = points;\n currentStroke.draw();\n return;\n }\n\n if (lastPoint) {\n const dx = x - lastPoint[0];\n const dy = y - lastPoint[1];\n const distance = Math.sqrt(dx * dx + dy * dy);\n \n // Only add point if it's far enough from the last one\n if (distance >= minDistance) {\n // If distance is too large, interpolate points\n if (distance > maxDistance) {\n const steps = Math.ceil(distance / maxDistance);\n for (let i = 1; i < steps; i++) {\n const t = i / steps;\n const interpX = lastPoint[0] + dx * t;\n const interpY = lastPoint[1] + dy * t;\n const interpPressure = lastPoint[2] + (pressure - lastPoint[2]) * t;\n points.push([interpX, interpY, interpPressure]);\n }\n }\n \n // Calculate velocity-based pressure\n const timeDelta = currentTime - lastTime;\n const velocity = distance / Math.max(timeDelta, 1);\n const velocityPressure = Math.min(1, Math.max(0.1, 1 - velocity * 0.02));\n const finalPressure = (pressure + velocityPressure) * 0.5;\n \n points.push([x, y, finalPressure]);\n currentStroke.points = points;\n currentStroke.draw();\n \n lastPoint = [x, y, finalPressure];\n lastTime = currentTime;\n }\n } else {\n lastPoint = [x, y, pressure];\n lastTime = currentTime;\n }\n \n // Check for frame containment while drawing (but don't apply clipping yet)\n shapes.forEach(frame => {\n if (frame.shapeName === 'frame') {\n if (frame.isShapeInFrame(currentStroke)) {\n frame.highlightFrame();\n hoveredFrameStroke = frame;\n } else if (hoveredFrameStroke === frame) {\n frame.removeHighlight();\n hoveredFrameStroke = null;\n }\n }\n });\n } else if (isDraggingStroke && currentShape && currentShape.isSelected) {\n const dx = x - startX;\n const dy = y - startY;\n currentShape.move(dx, dy);\n startX = x;\n startY = y;\n\n // Frame containment update (must live here where isDraggingStroke is in scope)\n if (!currentShape.isBeingMovedByFrame) {\n updateFrameContainmentForStroke(currentShape);\n }\n\n // Update arrows attached to this shape\n currentShape.updateAttachedArrows();\n\n // Snap guides\n if (window.__sketchStoreApi && window.__sketchStoreApi.getState().snapToObjects) {\n const snap = calculateSnap(currentShape, e.shiftKey, e.clientX, e.clientY);\n if (snap.dx || snap.dy) {\n currentShape.move(snap.dx, snap.dy);\n }\n } else {\n clearSnapGuides();\n }\n } else if (isResizingStroke && currentShape && currentShape.isSelected) {\n currentShape.updatePosition(resizingAnchorIndex, x, y);\n } else if (isRotatingStroke && currentShape && currentShape.isSelected) {\n const centerX = currentShape.boundingBox.x + currentShape.boundingBox.width / 2;\n const centerY = currentShape.boundingBox.y + currentShape.boundingBox.height / 2;\n const currentMouseAngle = Math.atan2(y - centerY, x - centerX) * 180 / Math.PI;\n const angleDiff = currentMouseAngle - startRotationMouseAngle;\n currentShape.rotate(startShapeRotation + angleDiff);\n currentShape.draw();\n }\n}\n\nfunction handleMouseUp(e) {\n if (isDrawingStroke) {\n isDrawingStroke = false;\n lastPoint = null;\n \n // Final smoothing pass after drawing is complete\n if (currentStroke && currentStroke.points.length >= 2) {\n currentStroke.draw(); // Redraw with final smoothing\n pushCreateAction(currentStroke);\n\n // Check for frame containment and track attachment\n const finalFrame = hoveredFrameStroke;\n if (finalFrame) {\n finalFrame.addShapeToFrame(currentStroke);\n // Track the attachment for undo\n pushFrameAttachmentAction(finalFrame, currentStroke, 'attach', null);\n }\n\n // Auto-select the drawn stroke and switch to selection tool\n const drawnShape = currentStroke;\n if (window.__sketchStoreApi) window.__sketchStoreApi.setActiveTool('select', { afterDraw: true });\n currentShape = drawnShape;\n currentShape.selectStroke();\n } else if (currentStroke) {\n // Remove strokes that are too small\n shapes.pop();\n if (currentStroke.group.parentNode) {\n currentStroke.group.parentNode.removeChild(currentStroke.group);\n }\n }\n\n // Clear frame highlighting\n if (hoveredFrameStroke) {\n hoveredFrameStroke.removeHighlight();\n hoveredFrameStroke = null;\n }\n\n currentStroke = null;\n }\n \n if ((isDraggingStroke || isResizingStroke || isRotatingStroke) && dragOldPosStroke && currentShape) {\n const newPos = cloneStrokeData(currentShape);\n const stateChanged = \n JSON.stringify(dragOldPosStroke.points) !== JSON.stringify(newPos.points) ||\n dragOldPosStroke.rotation !== newPos.rotation;\n \n const oldPos = {\n ...dragOldPosStroke,\n parentFrame: draggedShapeInitialFrameStroke\n };\n // Issue #34 bug #2: for drag, hoveredFrameStroke is the actual\n // destination \u2014 currentShape.parentFrame at this point is still\n // the OLD frame. Resize / rotate don't track hover, fall back to\n // the shape's own parent.\n const newPosForUndo = {\n ...newPos,\n parentFrame: isDraggingStroke\n ? (hoveredFrameStroke || null)\n : currentShape.parentFrame,\n };\n \n const frameChanged = oldPos.parentFrame !== newPosForUndo.parentFrame;\n \n if (stateChanged || frameChanged) {\n pushTransformAction(currentShape, oldPos, newPosForUndo);\n }\n \n // Handle frame containment changes after drag\n if (isDraggingStroke) {\n const finalFrame = hoveredFrameStroke;\n \n // If shape moved to a different frame\n if (draggedShapeInitialFrameStroke !== finalFrame) {\n // Remove from initial frame\n if (draggedShapeInitialFrameStroke) {\n draggedShapeInitialFrameStroke.removeShapeFromFrame(currentShape);\n }\n \n // Add to new frame\n if (finalFrame) {\n finalFrame.addShapeToFrame(currentShape);\n }\n \n // Track the frame change for undo\n if (frameChanged) {\n pushFrameAttachmentAction(finalFrame || draggedShapeInitialFrameStroke, currentShape, \n finalFrame ? 'attach' : 'detach', draggedShapeInitialFrameStroke);\n }\n } else if (draggedShapeInitialFrameStroke) {\n // Shape stayed in same frame, restore clipping\n draggedShapeInitialFrameStroke.restoreToFrame(currentShape);\n }\n }\n \n dragOldPosStroke = null;\n draggedShapeInitialFrameStroke = null;\n }\n \n // Clear frame highlighting\n if (hoveredFrameStroke) {\n hoveredFrameStroke.removeHighlight();\n hoveredFrameStroke = null;\n }\n \n // Bake accumulated move offset into point coordinates\n if (currentShape && typeof currentShape.finalizeMove === 'function') {\n currentShape.finalizeMove();\n }\n clearSnapGuides();\n isDraggingStroke = false;\n isResizingStroke = false;\n isRotatingStroke = false;\n resizingAnchorIndex = null;\n startRotationMouseAngle = null;\n startShapeRotation = null;\n svg.style.cursor = 'default';\n}\n\n// Color and thickness selection\nstrokeColors.forEach(span => {\n span.addEventListener(\"click\", (event) => {\n strokeColors.forEach(el => el.classList.remove(\"selected\"));\n span.classList.add(\"selected\");\n \n if (currentShape instanceof FreehandStroke && currentShape.isSelected) {\n const oldOptions = {...currentShape.options};\n currentShape.options.stroke = span.getAttribute(\"data-id\");\n currentShape.draw();\n pushOptionsChangeAction(currentShape, oldOptions);\n } else {\n strokeColor = span.getAttribute(\"data-id\");\n }\n });\n});\n\nstrokeThicknesses.forEach(span => {\n span.addEventListener(\"click\", (event) => {\n strokeThicknesses.forEach(el => el.classList.remove(\"selected\"));\n span.classList.add(\"selected\");\n \n if (currentShape instanceof FreehandStroke && currentShape.isSelected) {\n const oldOptions = {...currentShape.options};\n currentShape.options.strokeWidth = parseInt(span.getAttribute(\"data-id\"));\n currentShape.draw();\n pushOptionsChangeAction(currentShape, oldOptions);\n } else {\n strokeThickness = parseInt(span.getAttribute(\"data-id\"));\n }\n });\n});\n\n\nstrokeStyles.forEach(span => {\n span.addEventListener(\"click\", () => {\n strokeStyles.forEach(el => el.classList.remove(\"selected\"));\n span.classList.add(\"selected\");\n const val = span.getAttribute(\"data-id\");\n if (currentShape instanceof FreehandStroke && currentShape.isSelected) {\n const oldOptions = {...currentShape.options};\n currentShape.options.strokeStyle = val;\n currentShape.draw();\n pushOptionsChangeAction(currentShape, oldOptions);\n } else {\n strokeStyleValue = val;\n }\n });\n});\n\nstrokeTapers.forEach(span => {\n span.addEventListener(\"click\", () => {\n strokeTapers.forEach(el => el.classList.remove(\"selected\"));\n span.classList.add(\"selected\");\n const val = parseFloat(span.getAttribute(\"data-id\"));\n if (currentShape instanceof FreehandStroke && currentShape.isSelected) {\n const oldOptions = {...currentShape.options};\n currentShape.options.thinning = val;\n currentShape.draw();\n pushOptionsChangeAction(currentShape, oldOptions);\n } else {\n strokeThinning = val;\n }\n });\n});\n\nstrokeRoughnesses.forEach(span => {\n span.addEventListener(\"click\", () => {\n strokeRoughnesses.forEach(el => el.classList.remove(\"selected\"));\n span.classList.add(\"selected\");\n const val = span.getAttribute(\"data-id\");\n if (currentShape instanceof FreehandStroke && currentShape.isSelected) {\n const oldOptions = {...currentShape.options};\n currentShape.options.roughness = val;\n currentShape.draw();\n pushOptionsChangeAction(currentShape, oldOptions);\n } else {\n strokeRoughnessValue = val;\n }\n });\n});\n\ndocument.getElementById(\"strokeOpacity\")?.addEventListener(\"mousedown\", (event) => {\n isdraggingOpacity = true;\n});\n\ndocument.getElementById(\"strokeOpacity\")?.addEventListener(\"mousemove\", (event) => {\n if(isdraggingOpacity) \n {\n const slider = document.getElementById(\"strokeOpacity\");\n const rect = slider.getBoundingClientRect();\n const mouseX = event.clientX - rect.left;\n const percent = Math.max(0, Math.min(100, (mouseX / rect.width) * 100));\n document.getElementById(\"opacityContainerValue\").textContent = percent.toFixed(0);\n const opacity = percent / 100; \n if (currentShape instanceof FreehandStroke && currentShape.isSelected) {\n const oldOptions = {...currentShape.options};\n currentShape.options.strokeOpacity = opacity; \n currentShape.draw();\n pushOptionsChangeAction(currentShape, oldOptions);\n } else {\n strokeOpacity = opacity; \n }\n }\n});\n\ndocument.getElementById(\"strokeOpacity\")?.addEventListener(\"mouseup\", (event) => {\n isdraggingOpacity = false;\n});\n\nfunction cloneOptions(options) {\n return JSON.parse(JSON.stringify(options));\n}\n\nfunction cloneStrokeData(stroke) {\n return {\n points: JSON.parse(JSON.stringify(stroke.points)),\n rotation: stroke.rotation,\n options: cloneOptions(stroke.options)\n };\n}\n\n// Event listeners\n// svg.addEventListener('pointerdown', handleMouseDown);\n// svg.addEventListener('pointermove', handleMouseMove);\n// svg.addEventListener('pointerup', handleMouseUp);\n\n// Bridge freehand tool settings to React sidebar\nwindow.freehandToolSettings = {\n get strokeColor() { return strokeColor; },\n set strokeColor(v) { strokeColor = v; },\n get strokeWidth() { return strokeThickness; },\n set strokeWidth(v) { strokeThickness = v; },\n get strokeStyle() { return strokeStyleValue; },\n set strokeStyle(v) { strokeStyleValue = v; },\n get thinning() { return strokeThinning; },\n set thinning(v) { strokeThinning = v; },\n get roughness() { return strokeRoughnessValue; },\n set roughness(v) { strokeRoughnessValue = v; },\n get opacity() { return strokeOpacity; },\n set opacity(v) { strokeOpacity = v; },\n};\nwindow.updateSelectedFreehandStyle = function(changes) {\n if (currentShape && currentShape.shapeName === 'freehandStroke' && currentShape.isSelected) {\n if (changes.stroke !== undefined) { strokeColor = changes.stroke; currentShape.options.stroke = changes.stroke; }\n if (changes.strokeWidth !== undefined) { strokeThickness = changes.strokeWidth; currentShape.options.strokeWidth = changes.strokeWidth; }\n if (changes.thinning !== undefined) { strokeThinning = changes.thinning; currentShape.options.thinning = changes.thinning; }\n if (changes.roughness !== undefined) { strokeRoughnessValue = changes.roughness; currentShape.options.roughness = changes.roughness; }\n if (changes.opacity !== undefined) { strokeOpacity = changes.opacity; currentShape.options.strokeOpacity = changes.opacity; }\n currentShape.draw();\n } else {\n if (changes.stroke !== undefined) strokeColor = changes.stroke;\n if (changes.strokeWidth !== undefined) strokeThickness = changes.strokeWidth;\n if (changes.thinning !== undefined) strokeThinning = changes.thinning;\n if (changes.roughness !== undefined) strokeRoughnessValue = changes.roughness;\n if (changes.opacity !== undefined) strokeOpacity = changes.opacity;\n }\n};\n\n// Safety net: if mouseup fires outside the SVG canvas (e.g. on toolbar/overlay),\n// ensure we stop drawing so the stroke doesn't continue when pointer re-enters.\nwindow.addEventListener('pointerup', () => {\n if (isDrawingStroke) {\n isDrawingStroke = false;\n lastPoint = null;\n if (currentStroke && currentStroke.points && currentStroke.points.length >= 2) {\n currentStroke.draw();\n }\n currentStroke = null;\n }\n});\n\n// Also listen for visibility change (e.g. alt-tab while drawing)\ndocument.addEventListener('visibilitychange', () => {\n if (document.hidden && isDrawingStroke) {\n isDrawingStroke = false;\n lastPoint = null;\n currentStroke = null;\n }\n});\n\nexport\n{\n handleMouseDown as handleFreehandMouseDown,\n handleMouseMove as handleFreehandMouseMove,\n handleMouseUp as handleFreehandMouseUp,\n}\n"],
|
|
5
5
|
"mappings": "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",
|
|
6
6
|
"names": ["startX", "startY"]
|
|
7
7
|
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/utils/themeColors.js
|
|
4
|
+
var LEGACY_LABEL_COLORS = /* @__PURE__ */ new Set(["#e0e0e0", "#e8e3f3"]);
|
|
5
|
+
function getThemeForeground() {
|
|
6
|
+
if (typeof document === "undefined") return "#ffffff";
|
|
7
|
+
return document.body?.classList.contains("theme-dark") ? "#ffffff" : "#000000";
|
|
8
|
+
}
|
|
9
|
+
function resolveThemeLabelColor(color) {
|
|
10
|
+
if (!color) return getThemeForeground();
|
|
11
|
+
return LEGACY_LABEL_COLORS.has(String(color).trim().toLowerCase()) ? getThemeForeground() : color;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export {
|
|
15
|
+
resolveThemeLabelColor
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=chunk-M7OYYJC6.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/utils/themeColors.js"],
|
|
4
|
+
"sourcesContent": ["const LEGACY_LABEL_COLORS = new Set(['#e0e0e0', '#e8e3f3'])\n\nexport function getThemeForeground() {\n if (typeof document === 'undefined') return '#ffffff'\n return document.body?.classList.contains('theme-dark') ? '#ffffff' : '#000000'\n}\n\nexport function resolveThemeLabelColor(color) {\n if (!color) return getThemeForeground()\n return LEGACY_LABEL_COLORS.has(String(color).trim().toLowerCase())\n ? getThemeForeground()\n : color\n}\n"],
|
|
5
|
+
"mappings": ";;;AAAA,IAAM,sBAAsB,oBAAI,IAAI,CAAC,WAAW,SAAS,CAAC;AAEnD,SAAS,qBAAqB;AACnC,MAAI,OAAO,aAAa,YAAa,QAAO;AAC5C,SAAO,SAAS,MAAM,UAAU,SAAS,YAAY,IAAI,YAAY;AACvE;AAEO,SAAS,uBAAuB,OAAO;AAC5C,MAAI,CAAC,MAAO,QAAO,mBAAmB;AACtC,SAAO,oBAAoB,IAAI,OAAO,KAAK,EAAE,KAAK,EAAE,YAAY,CAAC,IAC7D,mBAAmB,IACnB;AACN;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/core/ScreenSpaceControls.js
|
|
4
|
+
var ROTATION_SELECTOR = '[data-screen-space-rotation-anchor="true"]';
|
|
5
|
+
var OUTLINE_SELECTOR = ".selection-outline, .selection-box, .multi-selection-outline";
|
|
6
|
+
var HANDLE_SELECTOR = ".anchor, .resize-anchor, .resize-handle, .multi-selection-anchor";
|
|
7
|
+
var SELECTION_CHROME = Object.freeze({
|
|
8
|
+
outlineWidth: 1.5,
|
|
9
|
+
outlineDash: "4 2",
|
|
10
|
+
handleSize: 10,
|
|
11
|
+
handleStrokeWidth: 2,
|
|
12
|
+
rotationRadius: 8,
|
|
13
|
+
rotationGap: 30
|
|
14
|
+
});
|
|
15
|
+
function getZoom() {
|
|
16
|
+
return Math.max(1e-3, Number(globalThis.window?.currentZoom) || 1);
|
|
17
|
+
}
|
|
18
|
+
function registerRotationAnchor(anchor, options = {}) {
|
|
19
|
+
if (!anchor) return;
|
|
20
|
+
const zoom = getZoom();
|
|
21
|
+
const radius = SELECTION_CHROME.rotationRadius;
|
|
22
|
+
const gap = SELECTION_CHROME.rotationGap;
|
|
23
|
+
const edgeY = Number(options.edgeY);
|
|
24
|
+
anchor.setAttribute("data-screen-space-rotation-anchor", "true");
|
|
25
|
+
anchor.setAttribute("data-screen-radius", String(radius));
|
|
26
|
+
anchor.setAttribute("data-screen-gap", String(gap));
|
|
27
|
+
anchor.setAttribute("data-applied-zoom", String(zoom));
|
|
28
|
+
anchor.setAttribute("r", String(radius / zoom));
|
|
29
|
+
anchor.setAttribute("stroke", "#5B57D1");
|
|
30
|
+
anchor.setAttribute("stroke-width", String(SELECTION_CHROME.handleStrokeWidth));
|
|
31
|
+
anchor.setAttribute("vector-effect", "non-scaling-stroke");
|
|
32
|
+
if (Number.isFinite(edgeY)) anchor.setAttribute("cy", String(edgeY - gap / zoom));
|
|
33
|
+
anchor.__rotationConnector = options.line || null;
|
|
34
|
+
anchor.__rotationConnectorEnd = options.lineEnd || "2";
|
|
35
|
+
syncRotationConnector(anchor);
|
|
36
|
+
}
|
|
37
|
+
function syncRotationConnector(anchor) {
|
|
38
|
+
const line = anchor.__rotationConnector;
|
|
39
|
+
if (!line) return;
|
|
40
|
+
const end = anchor.__rotationConnectorEnd === "1" ? "1" : "2";
|
|
41
|
+
line.setAttribute(`x${end}`, anchor.getAttribute("cx"));
|
|
42
|
+
line.setAttribute(`y${end}`, anchor.getAttribute("cy"));
|
|
43
|
+
line.setAttribute("stroke", "#5B57D1");
|
|
44
|
+
line.setAttribute("stroke-width", "1");
|
|
45
|
+
line.setAttribute("stroke-dasharray", SELECTION_CHROME.outlineDash);
|
|
46
|
+
line.setAttribute("vector-effect", "non-scaling-stroke");
|
|
47
|
+
}
|
|
48
|
+
function resizeHandleInScreenSpace(handle, zoom) {
|
|
49
|
+
const size = SELECTION_CHROME.handleSize / zoom;
|
|
50
|
+
const tagName = handle.tagName?.toLowerCase();
|
|
51
|
+
if (tagName === "rect") {
|
|
52
|
+
const oldWidth = Number(handle.getAttribute("width")) || size;
|
|
53
|
+
const oldHeight = Number(handle.getAttribute("height")) || size;
|
|
54
|
+
const centerX = (Number(handle.getAttribute("x")) || 0) + oldWidth / 2;
|
|
55
|
+
const centerY = (Number(handle.getAttribute("y")) || 0) + oldHeight / 2;
|
|
56
|
+
handle.setAttribute("x", String(centerX - size / 2));
|
|
57
|
+
handle.setAttribute("y", String(centerY - size / 2));
|
|
58
|
+
handle.setAttribute("width", String(size));
|
|
59
|
+
handle.setAttribute("height", String(size));
|
|
60
|
+
} else if (tagName === "circle" && !handle.matches(ROTATION_SELECTOR)) {
|
|
61
|
+
handle.setAttribute("r", String(size / 2));
|
|
62
|
+
}
|
|
63
|
+
handle.setAttribute("stroke", "#5B57D1");
|
|
64
|
+
handle.setAttribute("stroke-width", String(SELECTION_CHROME.handleStrokeWidth));
|
|
65
|
+
handle.setAttribute("vector-effect", "non-scaling-stroke");
|
|
66
|
+
}
|
|
67
|
+
function syncSelectionChromeToZoom(root = document) {
|
|
68
|
+
if (!root?.querySelectorAll) return;
|
|
69
|
+
const zoom = getZoom();
|
|
70
|
+
root.querySelectorAll(OUTLINE_SELECTOR).forEach((outline) => {
|
|
71
|
+
outline.setAttribute("fill", "none");
|
|
72
|
+
outline.setAttribute("stroke", "#5B57D1");
|
|
73
|
+
outline.setAttribute("stroke-width", String(SELECTION_CHROME.outlineWidth));
|
|
74
|
+
outline.setAttribute("stroke-dasharray", SELECTION_CHROME.outlineDash);
|
|
75
|
+
outline.setAttribute("vector-effect", "non-scaling-stroke");
|
|
76
|
+
});
|
|
77
|
+
root.querySelectorAll(HANDLE_SELECTOR).forEach((handle) => {
|
|
78
|
+
resizeHandleInScreenSpace(handle, zoom);
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
function syncRotationAnchorsToZoom() {
|
|
82
|
+
if (typeof document === "undefined") return;
|
|
83
|
+
const zoom = getZoom();
|
|
84
|
+
document.querySelectorAll(ROTATION_SELECTOR).forEach((anchor) => {
|
|
85
|
+
const previousZoom = Math.max(1e-3, Number(anchor.getAttribute("data-applied-zoom")) || zoom);
|
|
86
|
+
const radius = SELECTION_CHROME.rotationRadius;
|
|
87
|
+
const gap = SELECTION_CHROME.rotationGap;
|
|
88
|
+
if (Math.abs(previousZoom - zoom) >= 1e-6) {
|
|
89
|
+
const currentY = Number(anchor.getAttribute("cy")) || 0;
|
|
90
|
+
const edgeY = currentY + gap / previousZoom;
|
|
91
|
+
anchor.setAttribute("cy", String(edgeY - gap / zoom));
|
|
92
|
+
anchor.setAttribute("data-applied-zoom", String(zoom));
|
|
93
|
+
}
|
|
94
|
+
anchor.setAttribute("r", String(radius / zoom));
|
|
95
|
+
anchor.setAttribute("stroke", "#5B57D1");
|
|
96
|
+
anchor.setAttribute("stroke-width", String(SELECTION_CHROME.handleStrokeWidth));
|
|
97
|
+
anchor.setAttribute("vector-effect", "non-scaling-stroke");
|
|
98
|
+
syncRotationConnector(anchor);
|
|
99
|
+
});
|
|
100
|
+
syncSelectionChromeToZoom(document);
|
|
101
|
+
}
|
|
102
|
+
function installScreenSpaceControlSync(svgElement) {
|
|
103
|
+
if (!svgElement || typeof MutationObserver === "undefined") return;
|
|
104
|
+
window.__screenSpaceControlObserver?.disconnect?.();
|
|
105
|
+
let scheduled = false;
|
|
106
|
+
const scheduleSync = () => {
|
|
107
|
+
if (scheduled) return;
|
|
108
|
+
scheduled = true;
|
|
109
|
+
const run = () => {
|
|
110
|
+
scheduled = false;
|
|
111
|
+
syncRotationAnchorsToZoom();
|
|
112
|
+
};
|
|
113
|
+
if (typeof requestAnimationFrame === "function") requestAnimationFrame(run);
|
|
114
|
+
else queueMicrotask(run);
|
|
115
|
+
};
|
|
116
|
+
const observer = new MutationObserver(scheduleSync);
|
|
117
|
+
observer.observe(svgElement, {
|
|
118
|
+
attributes: true,
|
|
119
|
+
attributeFilter: ["viewBox"],
|
|
120
|
+
childList: true,
|
|
121
|
+
subtree: true
|
|
122
|
+
});
|
|
123
|
+
window.__screenSpaceControlObserver = observer;
|
|
124
|
+
scheduleSync();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export {
|
|
128
|
+
registerRotationAnchor,
|
|
129
|
+
installScreenSpaceControlSync
|
|
130
|
+
};
|
|
131
|
+
//# sourceMappingURL=chunk-MS4TIQ6Z.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/core/ScreenSpaceControls.js"],
|
|
4
|
+
"sourcesContent": ["/* eslint-disable */\n\nconst ROTATION_SELECTOR = '[data-screen-space-rotation-anchor=\"true\"]';\nconst OUTLINE_SELECTOR = '.selection-outline, .selection-box, .multi-selection-outline';\nconst HANDLE_SELECTOR = '.anchor, .resize-anchor, .resize-handle, .multi-selection-anchor';\n\nexport const SELECTION_CHROME = Object.freeze({\n outlineWidth: 1.5,\n outlineDash: '4 2',\n handleSize: 10,\n handleStrokeWidth: 2,\n rotationRadius: 8,\n rotationGap: 30,\n});\n\nexport function getSelectionChromeMetrics(zoom = 1) {\n const normalizedZoom = Math.max(0.001, Number(zoom) || 1);\n return {\n outlineWidth: SELECTION_CHROME.outlineWidth,\n outlineDash: SELECTION_CHROME.outlineDash,\n handleSize: SELECTION_CHROME.handleSize / normalizedZoom,\n rotationRadius: SELECTION_CHROME.rotationRadius / normalizedZoom,\n rotationGap: SELECTION_CHROME.rotationGap / normalizedZoom,\n };\n}\n\nfunction getZoom() {\n return Math.max(0.001, Number(globalThis.window?.currentZoom) || 1);\n}\n\nexport function registerRotationAnchor(anchor, options = {}) {\n if (!anchor) return;\n const zoom = getZoom();\n const radius = SELECTION_CHROME.rotationRadius;\n const gap = SELECTION_CHROME.rotationGap;\n const edgeY = Number(options.edgeY);\n\n anchor.setAttribute('data-screen-space-rotation-anchor', 'true');\n anchor.setAttribute('data-screen-radius', String(radius));\n anchor.setAttribute('data-screen-gap', String(gap));\n anchor.setAttribute('data-applied-zoom', String(zoom));\n anchor.setAttribute('r', String(radius / zoom));\n anchor.setAttribute('stroke', '#5B57D1');\n anchor.setAttribute('stroke-width', String(SELECTION_CHROME.handleStrokeWidth));\n anchor.setAttribute('vector-effect', 'non-scaling-stroke');\n if (Number.isFinite(edgeY)) anchor.setAttribute('cy', String(edgeY - gap / zoom));\n\n anchor.__rotationConnector = options.line || null;\n anchor.__rotationConnectorEnd = options.lineEnd || '2';\n syncRotationConnector(anchor);\n}\n\nfunction syncRotationConnector(anchor) {\n const line = anchor.__rotationConnector;\n if (!line) return;\n const end = anchor.__rotationConnectorEnd === '1' ? '1' : '2';\n line.setAttribute(`x${end}`, anchor.getAttribute('cx'));\n line.setAttribute(`y${end}`, anchor.getAttribute('cy'));\n line.setAttribute('stroke', '#5B57D1');\n line.setAttribute('stroke-width', '1');\n line.setAttribute('stroke-dasharray', SELECTION_CHROME.outlineDash);\n line.setAttribute('vector-effect', 'non-scaling-stroke');\n}\n\nfunction resizeHandleInScreenSpace(handle, zoom) {\n const size = SELECTION_CHROME.handleSize / zoom;\n const tagName = handle.tagName?.toLowerCase();\n\n if (tagName === 'rect') {\n const oldWidth = Number(handle.getAttribute('width')) || size;\n const oldHeight = Number(handle.getAttribute('height')) || size;\n const centerX = (Number(handle.getAttribute('x')) || 0) + oldWidth / 2;\n const centerY = (Number(handle.getAttribute('y')) || 0) + oldHeight / 2;\n handle.setAttribute('x', String(centerX - size / 2));\n handle.setAttribute('y', String(centerY - size / 2));\n handle.setAttribute('width', String(size));\n handle.setAttribute('height', String(size));\n } else if (tagName === 'circle' && !handle.matches(ROTATION_SELECTOR)) {\n handle.setAttribute('r', String(size / 2));\n }\n\n handle.setAttribute('stroke', '#5B57D1');\n handle.setAttribute('stroke-width', String(SELECTION_CHROME.handleStrokeWidth));\n handle.setAttribute('vector-effect', 'non-scaling-stroke');\n}\n\nexport function syncSelectionChromeToZoom(root = document) {\n if (!root?.querySelectorAll) return;\n const zoom = getZoom();\n\n root.querySelectorAll(OUTLINE_SELECTOR).forEach((outline) => {\n outline.setAttribute('fill', 'none');\n outline.setAttribute('stroke', '#5B57D1');\n outline.setAttribute('stroke-width', String(SELECTION_CHROME.outlineWidth));\n outline.setAttribute('stroke-dasharray', SELECTION_CHROME.outlineDash);\n outline.setAttribute('vector-effect', 'non-scaling-stroke');\n });\n\n root.querySelectorAll(HANDLE_SELECTOR).forEach((handle) => {\n resizeHandleInScreenSpace(handle, zoom);\n });\n}\n\nexport function syncRotationAnchorsToZoom() {\n if (typeof document === 'undefined') return;\n const zoom = getZoom();\n document.querySelectorAll(ROTATION_SELECTOR).forEach((anchor) => {\n const previousZoom = Math.max(0.001, Number(anchor.getAttribute('data-applied-zoom')) || zoom);\n const radius = SELECTION_CHROME.rotationRadius;\n const gap = SELECTION_CHROME.rotationGap;\n if (Math.abs(previousZoom - zoom) >= 0.000001) {\n const currentY = Number(anchor.getAttribute('cy')) || 0;\n const edgeY = currentY + gap / previousZoom;\n anchor.setAttribute('cy', String(edgeY - gap / zoom));\n anchor.setAttribute('data-applied-zoom', String(zoom));\n }\n anchor.setAttribute('r', String(radius / zoom));\n anchor.setAttribute('stroke', '#5B57D1');\n anchor.setAttribute('stroke-width', String(SELECTION_CHROME.handleStrokeWidth));\n anchor.setAttribute('vector-effect', 'non-scaling-stroke');\n syncRotationConnector(anchor);\n });\n syncSelectionChromeToZoom(document);\n}\n\nexport function installScreenSpaceControlSync(svgElement) {\n if (!svgElement || typeof MutationObserver === 'undefined') return;\n window.__screenSpaceControlObserver?.disconnect?.();\n let scheduled = false;\n const scheduleSync = () => {\n if (scheduled) return;\n scheduled = true;\n const run = () => {\n scheduled = false;\n syncRotationAnchorsToZoom();\n };\n if (typeof requestAnimationFrame === 'function') requestAnimationFrame(run);\n else queueMicrotask(run);\n };\n const observer = new MutationObserver(scheduleSync);\n observer.observe(svgElement, {\n attributes: true,\n attributeFilter: ['viewBox'],\n childList: true,\n subtree: true,\n });\n window.__screenSpaceControlObserver = observer;\n scheduleSync();\n}\n"],
|
|
5
|
+
"mappings": ";;;AAEA,IAAM,oBAAoB;AAC1B,IAAM,mBAAmB;AACzB,IAAM,kBAAkB;AAEjB,IAAM,mBAAmB,OAAO,OAAO;AAAA,EAC1C,cAAc;AAAA,EACd,aAAa;AAAA,EACb,YAAY;AAAA,EACZ,mBAAmB;AAAA,EACnB,gBAAgB;AAAA,EAChB,aAAa;AACjB,CAAC;AAaD,SAAS,UAAU;AACf,SAAO,KAAK,IAAI,MAAO,OAAO,WAAW,QAAQ,WAAW,KAAK,CAAC;AACtE;AAEO,SAAS,uBAAuB,QAAQ,UAAU,CAAC,GAAG;AACzD,MAAI,CAAC,OAAQ;AACb,QAAM,OAAO,QAAQ;AACrB,QAAM,SAAS,iBAAiB;AAChC,QAAM,MAAM,iBAAiB;AAC7B,QAAM,QAAQ,OAAO,QAAQ,KAAK;AAElC,SAAO,aAAa,qCAAqC,MAAM;AAC/D,SAAO,aAAa,sBAAsB,OAAO,MAAM,CAAC;AACxD,SAAO,aAAa,mBAAmB,OAAO,GAAG,CAAC;AAClD,SAAO,aAAa,qBAAqB,OAAO,IAAI,CAAC;AACrD,SAAO,aAAa,KAAK,OAAO,SAAS,IAAI,CAAC;AAC9C,SAAO,aAAa,UAAU,SAAS;AACvC,SAAO,aAAa,gBAAgB,OAAO,iBAAiB,iBAAiB,CAAC;AAC9E,SAAO,aAAa,iBAAiB,oBAAoB;AACzD,MAAI,OAAO,SAAS,KAAK,EAAG,QAAO,aAAa,MAAM,OAAO,QAAQ,MAAM,IAAI,CAAC;AAEhF,SAAO,sBAAsB,QAAQ,QAAQ;AAC7C,SAAO,yBAAyB,QAAQ,WAAW;AACnD,wBAAsB,MAAM;AAChC;AAEA,SAAS,sBAAsB,QAAQ;AACnC,QAAM,OAAO,OAAO;AACpB,MAAI,CAAC,KAAM;AACX,QAAM,MAAM,OAAO,2BAA2B,MAAM,MAAM;AAC1D,OAAK,aAAa,IAAI,GAAG,IAAI,OAAO,aAAa,IAAI,CAAC;AACtD,OAAK,aAAa,IAAI,GAAG,IAAI,OAAO,aAAa,IAAI,CAAC;AACtD,OAAK,aAAa,UAAU,SAAS;AACrC,OAAK,aAAa,gBAAgB,GAAG;AACrC,OAAK,aAAa,oBAAoB,iBAAiB,WAAW;AAClE,OAAK,aAAa,iBAAiB,oBAAoB;AAC3D;AAEA,SAAS,0BAA0B,QAAQ,MAAM;AAC7C,QAAM,OAAO,iBAAiB,aAAa;AAC3C,QAAM,UAAU,OAAO,SAAS,YAAY;AAE5C,MAAI,YAAY,QAAQ;AACpB,UAAM,WAAW,OAAO,OAAO,aAAa,OAAO,CAAC,KAAK;AACzD,UAAM,YAAY,OAAO,OAAO,aAAa,QAAQ,CAAC,KAAK;AAC3D,UAAM,WAAW,OAAO,OAAO,aAAa,GAAG,CAAC,KAAK,KAAK,WAAW;AACrE,UAAM,WAAW,OAAO,OAAO,aAAa,GAAG,CAAC,KAAK,KAAK,YAAY;AACtE,WAAO,aAAa,KAAK,OAAO,UAAU,OAAO,CAAC,CAAC;AACnD,WAAO,aAAa,KAAK,OAAO,UAAU,OAAO,CAAC,CAAC;AACnD,WAAO,aAAa,SAAS,OAAO,IAAI,CAAC;AACzC,WAAO,aAAa,UAAU,OAAO,IAAI,CAAC;AAAA,EAC9C,WAAW,YAAY,YAAY,CAAC,OAAO,QAAQ,iBAAiB,GAAG;AACnE,WAAO,aAAa,KAAK,OAAO,OAAO,CAAC,CAAC;AAAA,EAC7C;AAEA,SAAO,aAAa,UAAU,SAAS;AACvC,SAAO,aAAa,gBAAgB,OAAO,iBAAiB,iBAAiB,CAAC;AAC9E,SAAO,aAAa,iBAAiB,oBAAoB;AAC7D;AAEO,SAAS,0BAA0B,OAAO,UAAU;AACvD,MAAI,CAAC,MAAM,iBAAkB;AAC7B,QAAM,OAAO,QAAQ;AAErB,OAAK,iBAAiB,gBAAgB,EAAE,QAAQ,CAAC,YAAY;AACzD,YAAQ,aAAa,QAAQ,MAAM;AACnC,YAAQ,aAAa,UAAU,SAAS;AACxC,YAAQ,aAAa,gBAAgB,OAAO,iBAAiB,YAAY,CAAC;AAC1E,YAAQ,aAAa,oBAAoB,iBAAiB,WAAW;AACrE,YAAQ,aAAa,iBAAiB,oBAAoB;AAAA,EAC9D,CAAC;AAED,OAAK,iBAAiB,eAAe,EAAE,QAAQ,CAAC,WAAW;AACvD,8BAA0B,QAAQ,IAAI;AAAA,EAC1C,CAAC;AACL;AAEO,SAAS,4BAA4B;AACxC,MAAI,OAAO,aAAa,YAAa;AACrC,QAAM,OAAO,QAAQ;AACrB,WAAS,iBAAiB,iBAAiB,EAAE,QAAQ,CAAC,WAAW;AAC7D,UAAM,eAAe,KAAK,IAAI,MAAO,OAAO,OAAO,aAAa,mBAAmB,CAAC,KAAK,IAAI;AAC7F,UAAM,SAAS,iBAAiB;AAChC,UAAM,MAAM,iBAAiB;AAC7B,QAAI,KAAK,IAAI,eAAe,IAAI,KAAK,MAAU;AAC3C,YAAM,WAAW,OAAO,OAAO,aAAa,IAAI,CAAC,KAAK;AACtD,YAAM,QAAQ,WAAW,MAAM;AAC/B,aAAO,aAAa,MAAM,OAAO,QAAQ,MAAM,IAAI,CAAC;AACpD,aAAO,aAAa,qBAAqB,OAAO,IAAI,CAAC;AAAA,IACzD;AACA,WAAO,aAAa,KAAK,OAAO,SAAS,IAAI,CAAC;AAC9C,WAAO,aAAa,UAAU,SAAS;AACvC,WAAO,aAAa,gBAAgB,OAAO,iBAAiB,iBAAiB,CAAC;AAC9E,WAAO,aAAa,iBAAiB,oBAAoB;AACzD,0BAAsB,MAAM;AAAA,EAChC,CAAC;AACD,4BAA0B,QAAQ;AACtC;AAEO,SAAS,8BAA8B,YAAY;AACtD,MAAI,CAAC,cAAc,OAAO,qBAAqB,YAAa;AAC5D,SAAO,8BAA8B,aAAa;AAClD,MAAI,YAAY;AAChB,QAAM,eAAe,MAAM;AACvB,QAAI,UAAW;AACf,gBAAY;AACZ,UAAM,MAAM,MAAM;AACd,kBAAY;AACZ,gCAA0B;AAAA,IAC9B;AACA,QAAI,OAAO,0BAA0B,WAAY,uBAAsB,GAAG;AAAA,QACrE,gBAAe,GAAG;AAAA,EAC3B;AACA,QAAM,WAAW,IAAI,iBAAiB,YAAY;AAClD,WAAS,QAAQ,YAAY;AAAA,IACzB,YAAY;AAAA,IACZ,iBAAiB,CAAC,SAAS;AAAA,IAC3B,WAAW;AAAA,IACX,SAAS;AAAA,EACb,CAAC;AACD,SAAO,+BAA+B;AACtC,eAAa;AACjB;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|