@elixpo/lixsketch 5.4.1 → 5.4.3

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.
@@ -51,6 +51,7 @@ let rotationStartTransform = null;
51
51
  let initialHandlePosRelGroup = null;
52
52
  let initialGroupTx = 0;
53
53
  let initialGroupTy = 0;
54
+ let initialInverseScreenCTM = null;
54
55
 
55
56
  // Frame attachment variables
56
57
  let draggedShapeInitialFrameText = null;
@@ -301,10 +302,10 @@ function makeTextEditable(textElement, groupElement) {
301
302
  const handleClickOutside = (event) => {
302
303
  if (!input.contains(event.target)) {
303
304
  renderText(input, textElement, true);
304
- document.removeEventListener('mousedown', handleClickOutside, true);
305
+ document.removeEventListener('pointerdown', handleClickOutside, true);
305
306
  }
306
307
  };
307
- document.addEventListener('mousedown', handleClickOutside, true);
308
+ document.addEventListener('pointerdown', handleClickOutside, true);
308
309
  input.handleClickOutside = handleClickOutside;
309
310
 
310
311
  // Set text cursor on the element during edit mode
@@ -323,7 +324,7 @@ function renderText(input, textElement, deleteIfEmpty = false) {
323
324
  const gElement = input.textGroup;
324
325
 
325
326
  if (input.handleClickOutside) {
326
- document.removeEventListener('mousedown', input.handleClickOutside, true);
327
+ document.removeEventListener('pointerdown', input.handleClickOutside, true);
327
328
  }
328
329
 
329
330
  document.body.removeChild(input);
@@ -402,7 +403,8 @@ function renderText(input, textElement, deleteIfEmpty = false) {
402
403
  // After rendering text, switch to selection tool and auto-select
403
404
  if (gElement.parentNode) {
404
405
  switchToSelectionTool();
405
- selectElement(gElement);
406
+ // Defer selection so the tool switch (async React state) completes first
407
+ requestAnimationFrame(() => selectElement(gElement));
406
408
  }
407
409
  }
408
410
 
@@ -472,7 +474,7 @@ function createSelectionFeedback(groupElement) {
472
474
  groupElement.appendChild(handleRect);
473
475
  resizeHandles[handle.name] = handleRect;
474
476
 
475
- handleRect.addEventListener('mousedown', (e) => {
477
+ handleRect.addEventListener('pointerdown', (e) => {
476
478
  if (window.isSelectionToolActive) {
477
479
  e.stopPropagation();
478
480
  startResize(e, handle.name);
@@ -496,7 +498,7 @@ function createSelectionFeedback(groupElement) {
496
498
 
497
499
  resizeHandles.rotate = rotationAnchor;
498
500
 
499
- rotationAnchor.addEventListener('mousedown', (e) => {
501
+ rotationAnchor.addEventListener('pointerdown', (e) => {
500
502
  if (window.isSelectionToolActive) {
501
503
  e.stopPropagation();
502
504
  startRotation(e);
@@ -610,8 +612,8 @@ function startRotation(event) {
610
612
 
611
613
  svg.style.cursor = 'grabbing';
612
614
 
613
- window.addEventListener('mousemove', handleMouseMove);
614
- window.addEventListener('mouseup', handleMouseUp);
615
+ window.addEventListener('pointermove', handleMouseMove);
616
+ window.addEventListener('pointerup', handleMouseUp);
615
617
  }
616
618
 
617
619
  function removeSelectionFeedback(element) {
@@ -685,8 +687,8 @@ function deselectElement() {
685
687
  rotationStartTransform = null;
686
688
  svg.style.cursor = 'default';
687
689
 
688
- window.removeEventListener('mousemove', handleMouseMove);
689
- window.removeEventListener('mouseup', handleMouseUp);
690
+ window.removeEventListener('pointermove', handleMouseMove);
691
+ window.removeEventListener('pointerup', handleMouseUp);
690
692
  }
691
693
  }
692
694
 
@@ -728,8 +730,8 @@ function startDrag(event) {
728
730
 
729
731
  svg.style.cursor = 'grabbing';
730
732
 
731
- svg.addEventListener('mousemove', handleMouseMove);
732
- svg.addEventListener('mouseup', handleMouseUp);
733
+ svg.addEventListener('pointermove', handleMouseMove);
734
+ svg.addEventListener('pointerup', handleMouseUp);
733
735
  }
734
736
 
735
737
  function startResize(event, anchor) {
@@ -753,6 +755,10 @@ function startResize(event, anchor) {
753
755
 
754
756
  startPoint = getSVGCoordinates(event, selectedElement);
755
757
 
758
+ // Freeze the group's screen CTM at resize start so mouse→local mapping stays stable
759
+ const groupScreenCTM = selectedElement.getScreenCTM();
760
+ initialInverseScreenCTM = groupScreenCTM ? groupScreenCTM.inverse() : null;
761
+
756
762
  const currentTransform = selectedElement.transform.baseVal.consolidate();
757
763
  initialGroupTx = currentTransform ? currentTransform.matrix.e : 0;
758
764
  initialGroupTy = currentTransform ? currentTransform.matrix.f : 0;
@@ -771,8 +777,8 @@ function startResize(event, anchor) {
771
777
 
772
778
  svg.style.cursor = resizeHandles[anchor]?.style.cursor || 'default';
773
779
 
774
- svg.addEventListener('mousemove', handleMouseMove);
775
- svg.addEventListener('mouseup', handleMouseUp);
780
+ svg.addEventListener('pointermove', handleMouseMove);
781
+ svg.addEventListener('pointerup', handleMouseUp);
776
782
  }
777
783
 
778
784
 
@@ -828,7 +834,16 @@ const handleMouseMove = (event) => {
828
834
  const textElement = selectedElement.querySelector('text');
829
835
  if (!textElement || !startBBox || startFontSize === null || !startPoint || !initialHandlePosRelGroup) return;
830
836
 
831
- const currentPoint = getSVGCoordinates(event, selectedElement);
837
+ // Use the frozen initial CTM so the mapping doesn't shift as we change the group transform
838
+ let currentPoint;
839
+ if (initialInverseScreenCTM) {
840
+ const pt = svg.createSVGPoint();
841
+ pt.x = event.clientX;
842
+ pt.y = event.clientY;
843
+ currentPoint = pt.matrixTransform(initialInverseScreenCTM);
844
+ } else {
845
+ currentPoint = getSVGCoordinates(event, selectedElement);
846
+ }
832
847
 
833
848
  const startX = startBBox.x;
834
849
  const startY = startBBox.y;
@@ -1138,10 +1153,10 @@ const handleMouseUp = (event) => {
1138
1153
  setTimeout(updateSelectionFeedback, 0);
1139
1154
  }
1140
1155
 
1141
- svg.removeEventListener('mousemove', handleMouseMove);
1142
- svg.removeEventListener('mouseup', handleMouseUp);
1143
- window.removeEventListener('mousemove', handleMouseMove);
1144
- window.removeEventListener('mouseup', handleMouseUp);
1156
+ svg.removeEventListener('pointermove', handleMouseMove);
1157
+ svg.removeEventListener('pointerup', handleMouseUp);
1158
+ window.removeEventListener('pointermove', handleMouseMove);
1159
+ window.removeEventListener('pointerup', handleMouseUp);
1145
1160
  };
1146
1161
 
1147
1162
  function extractRotationFromTransform(element) {
@@ -1155,6 +1170,7 @@ function extractRotationFromTransform(element) {
1155
1170
 
1156
1171
  // EXPORTED EVENT HANDLERS
1157
1172
  const handleTextMouseDown = function (e) {
1173
+ if (!e.target) return;
1158
1174
  const activeEditor = document.querySelector("textarea.svg-text-editor");
1159
1175
  if (activeEditor && activeEditor.contains(e.target)) {
1160
1176
  return;
@@ -1249,14 +1265,14 @@ const handleTextMouseMove = function (e) {
1249
1265
 
1250
1266
  // Handle cursor changes for text tool
1251
1267
  if (isTextToolActive) {
1252
- const targetGroup = e.target.closest('g[data-type="text-group"]');
1268
+ const targetGroup = e.target?.closest?.('g[data-type="text-group"]');
1253
1269
  if (targetGroup) {
1254
1270
  svg.style.cursor = 'pointer';
1255
1271
  } else {
1256
1272
  svg.style.cursor = 'crosshair';
1257
1273
  }
1258
1274
  } else if (isSelectionToolActive) {
1259
- const targetGroup = e.target.closest('g[data-type="text-group"]');
1275
+ const targetGroup = e.target?.closest?.('g[data-type="text-group"]');
1260
1276
  if (targetGroup) {
1261
1277
  svg.style.cursor = 'default';
1262
1278
  }
@@ -1783,8 +1799,9 @@ window.updateSelectedTextStyle = function(changes) {
1783
1799
  }
1784
1800
  };
1785
1801
 
1786
- // Expose deselectElement for external callers (Selection.js blank canvas click)
1802
+ // Expose select/deselect for external callers (Selection.js, TextShape.selectShape)
1787
1803
  window.__deselectTextElement = deselectElement;
1804
+ window.__selectTextElement = selectElement;
1788
1805
 
1789
1806
  // React sidebar bridge — text ↔ code conversion
1790
1807
  window.__convertTextToCode = function() {
@@ -1820,4 +1837,4 @@ window.__setCodeLanguage = function(lang) {
1820
1837
  }
1821
1838
  };
1822
1839
 
1823
- export { handleTextMouseDown, handleTextMouseMove, handleTextMouseUp, updateCodeToggleForShape, deselectElement as deselectTextElement };
1840
+ export { handleTextMouseDown, handleTextMouseMove, handleTextMouseUp, updateCodeToggleForShape, deselectElement as deselectTextElement, enterEditMode };