From 447504ea673c22878cb77b4f5044e220cfb587dc Mon Sep 17 00:00:00 2001 From: 3dtours Date: Tue, 28 Jul 2026 08:07:49 +0700 Subject: [PATCH] fix: Ctrl+Click on section/MIDI items toggled selection, locked item Ctrl+Click handler toggled selection + set pending drag. Once selected, drag system treated it as 'move selection' instead of 'copy'. Removed selection toggle - Ctrl+Click now only sets pending drag for copy operation. --- app/static/js/app.jsx | 11 ++--------- app/static/js/app.precompiled.js | 5 ++--- wiki.md | 6 ++++++ 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/app/static/js/app.jsx b/app/static/js/app.jsx index 3d65a48..a0fe7da 100644 --- a/app/static/js/app.jsx +++ b/app/static/js/app.jsx @@ -1083,15 +1083,8 @@ const WaveformLane = ({ e.preventDefault(); e.stopPropagation(); if (e.ctrlKey) { - // Ctrl+Click: toggle selection only, never start drag immediately - var preToggleSnapshot = selectedItemIds ? new Set(selectedItemIds) : new Set(); - if (selectedItemIds && selectedItemIds.has(hitItem.id)) { - if (onDeselectItem) onDeselectItem(hitItem.id); - } else if (onAddToSelection) { - onAddToSelection(hitItem.id); - } - // Store pre-toggle snapshot so drag copies the ORIGINAL group - if (onSetPendingDrag) onSetPendingDrag(track.id, hitItem.type, hitItem.id, time - hitItem.start, e.nativeEvent || e, preToggleSnapshot); + // Ctrl+Click: copy on drag, do NOT toggle selection + if (onSetPendingDrag) onSetPendingDrag(track.id, hitItem.type, hitItem.id, time - hitItem.start, e.nativeEvent || e, selectedItemIds ? new Set(selectedItemIds) : new Set()); } else { // No modifier: start drag immediately (single or multi move) if (onSectionItemDragStart) onSectionItemDragStart(track.id, hitItem.type, hitItem.id, time - hitItem.start, false); diff --git a/app/static/js/app.precompiled.js b/app/static/js/app.precompiled.js index 5bb0f16..53b3814 100644 --- a/app/static/js/app.precompiled.js +++ b/app/static/js/app.precompiled.js @@ -51,9 +51,8 @@ if(e.ctrlKey&&selectionMode){e.preventDefault();e.stopPropagation();if(onClearLo if(e.shiftKey){e.preventDefault();e.stopPropagation();const currentAnchor=getLocalAnchor?getLocalAnchor():null;const anchor=currentAnchor!==null&¤tAnchor!==undefined?currentAnchor:localSelectionStart!==null?localSelectionStart:currentTime;const selS=Math.min(anchor,time);const selE=Math.max(anchor,time);if(onSetSelectionMode)onSetSelectionMode('local');if(onSetLocalSelectionTrackId)onSetLocalSelectionTrackId(track.id);if(onSetLocalSelectionStart)onSetLocalSelectionStart(selS);if(onSetLocalSelectionEnd)onSetLocalSelectionEnd(selE);if(onSetSelectionStart)onSetSelectionStart(selS);if(onSetSelectionEnd)onSetSelectionEnd(selE);if(onSetCurrentTime)onSetCurrentTime(time);if(onSelectTrack)onSelectTrack(track.id);return;}// Check if dragging selection boundaries (local mode) const isLocal=selectionMode==='local'&&localSelectionTrackId===track.id;if(isLocal&&localSelLeft!==null&&localSelRight!==null){const leftPx=localSelLeft*zoom;const rightPx=localSelRight*zoom;const distToLeft=Math.abs(x-leftPx);const distToRight=Math.abs(x-rightPx);if(distToLeft<=5){e.preventDefault();e.stopPropagation();if(onSelectionEdgeDragStart)onSelectionEdgeDragStart(e,track.id,'left');return;}else if(distToRight<=5){e.preventDefault();e.stopPropagation();if(onSelectionEdgeDragStart)onSelectionEdgeDragStart(e,track.id,'right');return;}}// Check if time-stretching (Alt + Right Edge) const toleranceSec=8/zoom;const rightEdgeClip=clips.find(c=>{const duration=c.buffer.duration/(c.speed||1.0);return Math.abs(time-(c.startTime+duration))<=toleranceSec;});if(rightEdgeClip&&e.altKey){e.preventDefault();e.stopPropagation();if(onClipStretchStart){onClipStretchStart(track.id,rightEdgeClip.id,time);}return;}// Check section/MIDI item edge for resize, then body for drag -const secItems=track.sections||[];const midiItems=track.midiItems||[];const secTol=8/zoom;let hitItem=null;let hitEdge=null;for(const sec of secItems){if(Math.abs(time-sec.start)<=secTol){hitItem={type:'section',id:sec.id,start:sec.start,dur:sec.duration};hitEdge='left';break;}if(Math.abs(time-(sec.start+sec.duration))<=secTol){hitItem={type:'section',id:sec.id,start:sec.start,dur:sec.duration};hitEdge='right';break;}}if(!hitItem){for(const midi of midiItems){if(Math.abs(time-midi.startTime)<=secTol){hitItem={type:'midiItem',id:midi.id,start:midi.startTime,dur:midi.duration};hitEdge='left';break;}if(Math.abs(time-(midi.startTime+midi.duration))<=secTol){hitItem={type:'midiItem',id:midi.id,start:midi.startTime,dur:midi.duration};hitEdge='right';break;}}}if(hitItem&&hitEdge){e.preventDefault();e.stopPropagation();if(onSectionItemResizeStart)onSectionItemResizeStart(track.id,hitItem.type,hitItem.id,hitEdge,time);return;}if(!hitItem){for(const sec of secItems){if(time>=sec.start&&time=midi.startTime&&time=sec.start&&time=midi.startTime&&timetime>=c.startTime&&time