fix: Ctrl+S in piano roll + ruler range selection via drag

This commit is contained in:
2026-07-25 21:04:06 +07:00
parent 57fd110fe3
commit 025aba3265
+44 -3
View File
@@ -4590,6 +4590,10 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
React.useEffect(() => { if (!draggedNoteRef.current) setNotes(st.notes || []); }, [st.notes]);
const brushVelocityRef = React.useRef(0.8);
const [selectedNoteIds, setSelectedNoteIds] = React.useState([]);
const [loopStartBeat, setLoopStartBeat] = React.useState(null);
const [loopEndBeat, setLoopEndBeat] = React.useState(null);
const [isLooping, setIsLooping] = React.useState(false);
const rulerDragRef = React.useRef(null);
React.useEffect(() => {
const handler = (e) => {
if ((e.ctrlKey || e.metaKey) && e.key === 'a') {
@@ -4638,6 +4642,10 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
} else if ((e.ctrlKey || e.metaKey) && (e.key === 'y' || (e.key === 'z' && e.shiftKey))) {
e.preventDefault();
handleRedo();
} else if ((e.ctrlKey || e.metaKey) && e.key === 's') {
e.preventDefault();
onSaveNotes(st.id, st.trackId, st.target_id, notes);
showToast('Đã lưu MIDI notes', 'info');
} else if (e.key === 'Delete' || e.key === 'Backspace') {
if (selectedNoteIds.length > 0 && e.target.tagName !== 'INPUT' && e.target.tagName !== 'TEXTAREA') {
e.preventDefault();
@@ -4650,7 +4658,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
};
window.addEventListener('keydown', handler);
return () => window.removeEventListener('keydown', handler);
}, [handleUndo, handleRedo, notes, selectedNoteIds]);
}, [handleUndo, handleRedo, notes, selectedNoteIds, onSaveNotes, showToast]);
React.useEffect(() => {
onUpdateNotes(st.id, notes);
@@ -5715,14 +5723,47 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
if (clickTime >= 0) {
setSubTabs(prev => prev.map(s => s.id === st.id ? { ...s, currentTime: clickTime } : s));
}
}
if (e.shiftKey) {
const beatSnap = Math.round(clickBeat / 4) * 4;
if (loopStartBeat === null) {
setLoopStartBeat(Math.max(0, beatSnap - 4));
setLoopEndBeat(Math.max(4, beatSnap));
} else {
setLoopEndBeat(Math.max(loopStartBeat + 4, beatSnap));
}
return;
}
rulerDragRef.current = { startX: e.clientX, startBeat: clickBeat, scrollLeft: e.currentTarget.scrollLeft };
},
onMouseMove: e => {
if (!rulerDragRef.current) return;
const rect = e.currentTarget.getBoundingClientRect();
const x = e.clientX - rect.left + rulerDragRef.current.scrollLeft;
const beat = Math.max(0, x / pixelsPerBeat);
if (Math.abs(e.clientX - rulerDragRef.current.startX) > 5) {
const sBeat = Math.max(0, Math.min(rulerDragRef.current.startBeat, beat));
const eBeat = Math.max(sBeat + 1, Math.max(rulerDragRef.current.startBeat, beat));
setLoopStartBeat(sBeat);
setLoopEndBeat(eBeat);
setIsLooping(true);
}
},
onMouseUp: () => { rulerDragRef.current = null; },
onMouseLeave: () => { rulerDragRef.current = null; }
}, /*#__PURE__*/React.createElement("div", {
style: {
width: `${viewWidth}px`,
height: '100%'
},
className: "relative h-full font-mono text-[9px] text-zinc-500 font-bold"
}, renderBarLabels()))), /*#__PURE__*/React.createElement("div", {
}, renderBarLabels(), loopStartBeat !== null && loopEndBeat !== null && loopEndBeat > loopStartBeat && /*#__PURE__*/React.createElement("div", {
style: {
left: `${loopStartBeat * pixelsPerBeat}px`,
width: `${(loopEndBeat - loopStartBeat) * pixelsPerBeat}px`,
top: 0, bottom: 0
},
className: "absolute bg-emerald-500/15 border-l border-r border-emerald-400 pointer-events-none"
}))), /*#__PURE__*/React.createElement("div", {
className: "flex-1 flex overflow-hidden min-h-0 relative"
}, /*#__PURE__*/React.createElement("div", {
ref: keybedRef,