@@ -18,6 +18,7 @@ const MIN_ZOOM = 0.01;
1818const ZOOM_STEP = 0.01 ;
1919const TRANSPARENT_STYLE_COLOR = "#00000000" ;
2020const PREVIEW_HISTORY_LIMIT = 50 ;
21+ const PREVIEW_DRAG_START_THRESHOLD_PX = 5 ;
2122const SNAP_MODES = Object . freeze ( [ "grid" , "point" , "none" ] ) ;
2223const ANGLE_SNAP_STEPS = Object . freeze ( [ 15 , 30 , 45 , 90 ] ) ;
2324const POINT_SNAP_RADIUS = 1.5 ;
@@ -3325,9 +3326,31 @@ export class ToolStarterApp {
33253326 this . applySelectedPaletteColorToShape ( shapeIndex , isStrokeMode ? "stroke" : "fill" , options . source || ( options . dragStart ? "render surface click" : "render surface drag" ) ) ;
33263327 return ;
33273328 }
3329+ if ( this . preserveSelectedShapeClick ( shapeIndex , options . source || "render surface" ) ) {
3330+ return ;
3331+ }
33283332 this . selectShape ( shapeIndex , "render surface" ) ;
33293333 }
33303334
3335+ preserveSelectedShapeClick ( shapeIndex , sourceLabel ) {
3336+ const object = this . selectedObject ( ) ;
3337+ const normalizedIndex = normalizeShapeIndex ( shapeIndex ) ;
3338+ const shapes = sortedShapes ( object ) ;
3339+ if ( normalizedIndex < 0 || normalizedIndex >= shapes . length || ! this . selectedShapeIndexes . has ( normalizedIndex ) ) {
3340+ return false ;
3341+ }
3342+
3343+ const scrollState = this . captureLeftPanelScrollState ( ) ;
3344+ this . selectedShapeIndex = normalizedIndex ;
3345+ this . syncPaletteSelectionFromCurrentShape ( { logMissing : true } ) ;
3346+ this . setPaletteTarget ( "stroke" , false ) ;
3347+ this . renderPayload ( ) ;
3348+ this . restoreLeftPanelScrollState ( scrollState ) ;
3349+ const shape = this . selectedShape ( ) ;
3350+ this . statusLog . write ( `OK Preserved selected shape from ${ sourceLabel } : row ${ this . selectedShapeIndex } (${ shapeTool ( shape ) } ). Multi-select count: ${ this . selectedShapeIndexes . size } .` ) ;
3351+ return true ;
3352+ }
3353+
33313354 handleShapeContextMenu ( event , shape , shapeIndex ) {
33323355 event . preventDefault ( ) ;
33333356 if ( this . activeDrawing || this . previewPointerEdit ) {
@@ -5012,12 +5035,10 @@ export class ToolStarterApp {
50125035 this . selectedShapeIndexes = new Set ( targetIndexes ) ;
50135036 this . directSelectedShapeIndexes = directSelectedShapeIndexes . size ? directSelectedShapeIndexes : new Set ( targetIndexes ) ;
50145037 this . previewPointerEdit = {
5038+ ...this . previewPointerEditStartMetadata ( event ) ,
50155039 mode : "move-group" ,
50165040 groupId,
50175041 directSelectedShapeIndexes : new Set ( this . directSelectedShapeIndexes ) ,
5018- historyRecorded : false ,
5019- historySnapshot : this . cloneCurrentPayload ( ) ,
5020- lastDelta : { x : 0 , y : 0 } ,
50215042 originalTransforms : Object . fromEntries ( targetIndexes . map ( ( targetIndex ) => {
50225043 const effectiveShape = this . effectiveShapeForFrame ( objectShapes [ targetIndex ] , activeFrame , targetIndex ) ;
50235044 return [ targetIndex , this . ensureShapeTransform ( effectiveShape ) ] ;
@@ -5032,10 +5053,8 @@ export class ToolStarterApp {
50325053 return ;
50335054 }
50345055 this . previewPointerEdit = {
5056+ ...this . previewPointerEditStartMetadata ( event ) ,
50355057 mode : "move" ,
5036- historyRecorded : false ,
5037- historySnapshot : this . cloneCurrentPayload ( ) ,
5038- lastDelta : { x : 0 , y : 0 } ,
50395058 originalGeometry : JSON . parse ( JSON . stringify ( selected . geometry ) ) ,
50405059 originalTransform : { ...this . shapeTransform ( selected ) } ,
50415060 shapeIndex : normalizedIndex ,
@@ -5055,17 +5074,44 @@ export class ToolStarterApp {
50555074 event . preventDefault ( ) ;
50565075 event . stopPropagation ( ) ;
50575076 this . previewPointerEdit = {
5077+ ...this . previewPointerEditStartMetadata ( event ) ,
50585078 ...options ,
5059- historyRecorded : false ,
5060- historySnapshot : this . cloneCurrentPayload ( ) ,
5061- lastDelta : { x : 0 , y : 0 } ,
50625079 originalGeometry : JSON . parse ( JSON . stringify ( selected . geometry ) ) ,
50635080 originalTransform : { ...this . shapeTransform ( selected ) } ,
50645081 shapeIndex : normalizedIndex ,
50655082 start : this . snapCanvasPoint ( this . pointerPreviewPoint ( event ) , { excludeShapeIndex : normalizedIndex } )
50665083 } ;
50675084 }
50685085
5086+ previewPointerEditStartMetadata ( event ) {
5087+ return {
5088+ dragThresholdMet : false ,
5089+ historyRecorded : false ,
5090+ historySnapshot : this . cloneCurrentPayload ( ) ,
5091+ lastDelta : { x : 0 , y : 0 } ,
5092+ startClient : {
5093+ x : Number ( event . clientX ) || 0 ,
5094+ y : Number ( event . clientY ) || 0
5095+ }
5096+ } ;
5097+ }
5098+
5099+ previewPointerEditPastDragThreshold ( edit , event ) {
5100+ if ( edit . dragThresholdMet ) {
5101+ return true ;
5102+ }
5103+ if ( ! edit . startClient ) {
5104+ edit . dragThresholdMet = true ;
5105+ return true ;
5106+ }
5107+ const distance = Math . hypot ( ( Number ( event . clientX ) || 0 ) - edit . startClient . x , ( Number ( event . clientY ) || 0 ) - edit . startClient . y ) ;
5108+ if ( distance < PREVIEW_DRAG_START_THRESHOLD_PX ) {
5109+ return false ;
5110+ }
5111+ edit . dragThresholdMet = true ;
5112+ return true ;
5113+ }
5114+
50695115 previewPointerEditDelta ( edit , event ) {
50705116 const rawEnd = this . pointerPreviewPoint ( event ) ;
50715117 const end = this . snapCanvasPoint ( rawEnd , { excludeShapeIndex : edit . shapeIndex } ) ;
@@ -5080,6 +5126,9 @@ export class ToolStarterApp {
50805126 if ( ! edit || event . buttons !== 1 ) {
50815127 return ;
50825128 }
5129+ if ( ! this . previewPointerEditPastDragThreshold ( edit , event ) ) {
5130+ return ;
5131+ }
50835132 const delta = this . previewPointerEditDelta ( edit , event ) ;
50845133 if ( Math . abs ( delta . x - edit . lastDelta . x ) < 0.001 && Math . abs ( delta . y - edit . lastDelta . y ) < 0.001 ) {
50855134 return ;
@@ -5096,6 +5145,9 @@ export class ToolStarterApp {
50965145 return ;
50975146 }
50985147 this . previewPointerEdit = null ;
5148+ if ( ! edit . dragThresholdMet && ! this . previewPointerEditPastDragThreshold ( edit , event ) ) {
5149+ return ;
5150+ }
50995151 const delta = this . previewPointerEditDelta ( edit , event ) ;
51005152 if ( Math . abs ( delta . x ) < 0.001 && Math . abs ( delta . y ) < 0.001 ) {
51015153 return ;
0 commit comments