diff --git a/app/templates/index.html b/app/templates/index.html index 0fa7309..b769015 100644 --- a/app/templates/index.html +++ b/app/templates/index.html @@ -363,7 +363,7 @@ canvasRef.current.style.cursor = isOverClip ? 'cell' : 'not-allowed'; } else { // select tool - canvasRef.current.style.cursor = (isOverClip && e.altKey) ? 'grab' : 'crosshair'; + canvasRef.current.style.cursor = (isOverClip && (e.altKey || e.ctrlKey)) ? 'grab' : 'crosshair'; } }} onMouseDown={(e) => { @@ -401,7 +401,7 @@ e.preventDefault(); e.stopPropagation(); if (onClipDragStart) { - onClipDragStart(track.id, clickedClip.id, time - clickedClip.startTime); + onClipDragStart(track.id, clickedClip.id, time - clickedClip.startTime, e.ctrlKey); } } else { onPlayheadSet(time); @@ -409,12 +409,12 @@ return; } - // Check for click drag clip (no Ctrl key required if select tool is in fallback ctrl-mode) - if (clickedClip && e.ctrlKey) { + // Check for click drag clip (Alt to move, Ctrl to duplicate) + if (clickedClip && (e.altKey || e.ctrlKey)) { e.preventDefault(); e.stopPropagation(); if (onClipDragStart) { - onClipDragStart(track.id, clickedClip.id, time - clickedClip.startTime); + onClipDragStart(track.id, clickedClip.id, time - clickedClip.startTime, e.ctrlKey); } return; } @@ -1710,25 +1710,64 @@ const captureTrackSnapshotRef = useRef(null); captureTrackSnapshotRef.current = captureTrackSnapshot; - const handleClipDragStart = (trackId, clipId, clickOffset) => { + const handleClipDragStart = (trackId, clipId, clickOffset, isDuplicate = false) => { const track = tracks.find(t => t.id === trackId); if (!track) return; - const clips = track.clips && track.clips.length > 0 ? track.clips : (track.buffer ? [{ - id: 'default', + const existingClips = track.clips && track.clips.length > 0 ? track.clips : (track.buffer ? [{ + id: 'default_' + track.id, buffer: track.buffer, startTime: track.startTime || 0, name: track.name }] : []); - const clip = clips.find(c => c.id === clipId); + const clip = existingClips.find(c => c.id === clipId || (clipId === 'default' && c.id === 'default_' + track.id)); if (!clip) return; const beforeSnap = captureTrackSnapshot(trackId); + let targetClipId = clip.id; + + if (isDuplicate) { + const cloneId = clip.id + '_copy_' + Date.now(); + const clone = { + ...clip, + id: cloneId, + name: clip.name + ' (Copy)' + }; + + setTracks(prev => prev.map(t => { + if (t.id === trackId) { + const newClips = [...existingClips, clone]; + return { + ...t, + clips: newClips, + buffer: newClips[0].buffer, + startTime: newClips[0].startTime, + name: newClips[0].name + }; + } + return t; + })); + + targetClipId = cloneId; + } else { + if (!track.clips || track.clips.length === 0) { + setTracks(prev => prev.map(t => { + if (t.id === trackId) { + return { + ...t, + clips: existingClips + }; + } + return t; + })); + } + } + setDraggedClip({ trackId: trackId, - clipId: clipId, + clipId: targetClipId, clickOffset: clickOffset, buffer: clip.buffer, name: clip.name, @@ -2789,7 +2828,7 @@