fix: insert noteB after noteA in split, not at end of array

This commit is contained in:
2026-07-31 08:15:56 +07:00
parent c60803ee2a
commit e1550c8ffe
2 changed files with 9 additions and 2 deletions
+8 -1
View File
@@ -6522,7 +6522,14 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, activeTracks, onClos
start_beat: splitBeat,
duration_beats: target.start_beat + target.duration_beats - splitBeat
};
setNotes(prev => prev.map(n => n.id === target.id ? noteA : n).concat([noteB]));
setNotes(prev => {
const idx = prev.findIndex(n => n.id === target.id);
if (idx === -1) return prev;
const result = [...prev];
result.splice(idx, 1, noteA);
result.splice(idx + 1, 0, noteB);
return result;
});
setSelectedNoteIds([noteA.id, noteB.id]);
showToast('Đã tách nốt!', 'info');
}
+1 -1
View File
@@ -194,7 +194,7 @@ const clickedNoteIdx=notes.findIndex(n=>{return pitch===n.pitch&&beat>=n.start_b
if(clickedNoteIdx!==-1&&!e.ctrlKey&&!e.shiftKey&&!e.altKey){if(window.SonicSF){const ctx=getAudioContext();var clTrk=activeTracks.find(function(t){return t.id===st.trackId;});var clCh=window.SonicPianoRoll?window.SonicPianoRoll.getTrackMidiChannel(clTrk,activeTracks):clTrk?clTrk.midiChannel:0;window.SonicSF.playNote(notes[clickedNoteIdx].pitch,100,300,ctx.currentTime,st.instrumentProgram,null,clCh,clTrk?clTrk.synth_engine:undefined);}}// Ctrl+click: toggle selection (multi-select)
if(e.ctrlKey&&!e.altKey&&!e.shiftKey){if(clickedNoteIdx!==-1){const clickedNote=notes[clickedNoteIdx];if(selectedNoteIds.includes(clickedNote.id)){setSelectedNoteIds(prev=>prev.filter(id=>id!==clickedNote.id));}else{setSelectedNoteIds(prev=>[...prev,clickedNote.id]);}return;}else{// Ctrl+click on empty space: start selection marquee
setSelectedNoteIds([]);const snapStart=getSnapBeat(beat,snapValue);setSelectionMarquee({startBeat:snapStart,startPitch:pitch,currentBeat:snapStart,currentPitch:pitch});return;}}// Ctrl+Shift+click on note → split at click position
if(e.ctrlKey&&e.shiftKey){if(clickedNoteIdx!==-1){const target=notes[clickedNoteIdx];const splitBeat=getSnapBeat(beat,snapValue);if(splitBeat>target.start_beat+0.03125&&splitBeat<target.start_beat+target.duration_beats-0.03125){pushToUndo(notes);const noteA={...JSON.parse(JSON.stringify(target)),id:'note_'+Date.now()+Math.random().toString(36).substr(2,8),duration_beats:splitBeat-target.start_beat};const noteB={...JSON.parse(JSON.stringify(target)),id:'note_'+Date.now()+Math.random().toString(36).substr(2,8),start_beat:splitBeat,duration_beats:target.start_beat+target.duration_beats-splitBeat};setNotes(prev=>prev.map(n=>n.id===target.id?noteA:n).concat([noteB]));setSelectedNoteIds([noteA.id,noteB.id]);showToast('Đã tách nốt!','info');}}else{// Ctrl+Shift+click on empty space → duplicate selected notes
if(e.ctrlKey&&e.shiftKey){if(clickedNoteIdx!==-1){const target=notes[clickedNoteIdx];const splitBeat=getSnapBeat(beat,snapValue);if(splitBeat>target.start_beat+0.03125&&splitBeat<target.start_beat+target.duration_beats-0.03125){pushToUndo(notes);const noteA={...JSON.parse(JSON.stringify(target)),id:'note_'+Date.now()+Math.random().toString(36).substr(2,8),duration_beats:splitBeat-target.start_beat};const noteB={...JSON.parse(JSON.stringify(target)),id:'note_'+Date.now()+Math.random().toString(36).substr(2,8),start_beat:splitBeat,duration_beats:target.start_beat+target.duration_beats-splitBeat};setNotes(prev=>{const idx=prev.findIndex(n=>n.id===target.id);if(idx===-1)return prev;const result=[...prev];result.splice(idx,1,noteA);result.splice(idx+1,0,noteB);return result;});setSelectedNoteIds([noteA.id,noteB.id]);showToast('Đã tách nốt!','info');}}else{// Ctrl+Shift+click on empty space → duplicate selected + clicked notes
pushToUndo(notes);const clones=notes.filter(n=>selectedNoteIds.includes(n.id)).map(n=>({...JSON.parse(JSON.stringify(n)),id:'note_'+Date.now()+Math.random().toString(36).substr(2,8)}));if(clones.length>0){setNotes(prev=>[...prev,...clones]);const cloneIds=clones.map(c=>c.id);setSelectedNoteIds(cloneIds);notesBeforeDragRef.current=JSON.parse(JSON.stringify(notes));const cloneOffsets=clones.map(n=>({id:n.id,originalStartBeat:n.start_beat,originalPitch:n.pitch}));setDraggedNote({mode:'move',idx:-1,startOffsetBeat:beat,startOffsetPitch:pitch,selectedNotesOffset:cloneOffsets});showToast('Đã nhân bản '+clones.length+' nốt!','info');}}return;}// Hovered resize edge (Alt+resize for scaling)
if(hoveredResizeIdx!==-1&&e.altKey){pushToUndo(notes);notesBeforeDragRef.current=JSON.parse(JSON.stringify(notes));const allSelected=[...new Set(selectedNoteIds.length>0?selectedNoteIds:[notes[hoveredResizeIdx].id])];const selectedNotes=notes.filter(n=>allSelected.includes(n.id));const firstStart=Math.min(...selectedNotes.map(n=>n.start_beat));const draggedNote=notes[hoveredResizeIdx];setDraggedNote({mode:'scale',idx:hoveredResizeIdx,originalEnd:draggedNote.start_beat+draggedNote.duration_beats,firstStart:firstStart,selectedNoteIds:allSelected});return;}// Hovered resize edge (normal resize)
if(hoveredResizeIdx!==-1){pushToUndo(notes);notesBeforeDragRef.current=JSON.parse(JSON.stringify(notes));setDraggedNote({mode:'resize',idx:hoveredResizeIdx,originalStart:notes[hoveredResizeIdx].start_beat});return;}if(clickedNoteIdx!==-1){// Click on existing note: drag-move