feat: Snap to Scale toggle + remove tool buttons + no menu on erase sweep

- Added snapToScale state + toggle switch in piano roll toolbar
- snapPitchToScale only applies when snapToScale is true
- Removed Select/Pen/Eraser tool buttons
- Right-click drag erase sweep sets swallowContextMenuRef=true
  so context menu won't appear during sweep
This commit is contained in:
2026-07-25 12:39:32 +07:00
parent 221561d58a
commit f5bc0a8810
2 changed files with 18 additions and 12 deletions
+16 -10
View File
@@ -5068,12 +5068,12 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
mode: 'draw',
idx: -1,
startOffsetBeat: start,
startOffsetPitch: snapPitchToScale(pitch, selectedScale),
startOffsetPitch: snapToScale ? snapPitchToScale(pitch, selectedScale) : pitch,
drawNoteId: noteId,
drawDuration: initialDur,
visitedPitches: [snapPitchToScale(pitch, selectedScale)],
visitedPitches: snapToScale ? [snapPitchToScale(pitch, selectedScale)] : [pitch],
initialBeat: start,
initialPitch: snapPitchToScale(pitch, selectedScale)
initialPitch: snapToScale ? snapPitchToScale(pitch, selectedScale) : pitch
});
// Play the note with SoundFont
if (window.SonicSF) {
@@ -5118,6 +5118,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
const rc = rightClickDragRef.current;
if (rc.active && (Math.abs(e.clientX - rc.startX) > 5 || Math.abs(e.clientY - rc.startY) > 5)) {
rc.active = false;
swallowContextMenuRef.current = true;
notesBeforeDragRef.current = JSON.parse(JSON.stringify(notes));
setDraggedNote({ mode: 'erase_sweep', visitedPitches: [] });
return;
@@ -5155,7 +5156,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
return { ...n, duration_beats: newDur };
}));
}
const snappedPitch = snapPitchToScale(pitch, selectedScale);
const snappedPitch = snapToScale ? snapPitchToScale(pitch, selectedScale) : pitch;
if (!visited.includes(snappedPitch)) {
const newPitches = [...visited, snappedPitch];
const totalSpan = Math.max(0.125, beat - draggedNote.initialBeat);
@@ -5461,6 +5462,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
}
};
const [selectedScale, setSelectedScale] = React.useState(null);
const [snapToScale, setSnapToScale] = React.useState(true);
const [scaleMenuPos, setScaleMenuPos] = React.useState(null);
const scaleMenuOriginRef = React.useRef(null);
const [aiPrompt, setAiPrompt] = React.useState('');
@@ -5616,12 +5618,16 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
"data-lucide": "music",
className: "w-3.5 h-3.5"
}), st.label), /*#__PURE__*/React.createElement("div", {
className: "flex bg-zinc-800 p-0.5 rounded border border-zinc-700 text-xs"
}, ['select', 'pen', 'eraser'].map(tool => /*#__PURE__*/React.createElement("button", {
key: tool,
onClick: () => setActiveRollTool(tool),
className: `px-2.5 py-1 rounded capitalize ${activeRollTool === tool ? 'bg-yellow-600 text-white font-bold' : 'text-zinc-400 hover:text-zinc-200'}`
}, tool))), /*#__PURE__*/React.createElement("div", {
className: "flex items-center gap-1 text-xs"
}, /*#__PURE__*/React.createElement("span", {
className: "text-zinc-500 font-semibold"
}, "Snap to Scale"), /*#__PURE__*/React.createElement("button", {
onClick: () => setSnapToScale(!snapToScale),
className: `w-7 h-4 rounded-full transition-colors relative ${snapToScale ? 'bg-yellow-600' : 'bg-zinc-700'}`,
style: { padding: 0 }
}, /*#__PURE__*/React.createElement("div", {
className: `w-3 h-3 rounded-full bg-white absolute top-0.5 transition-transform ${snapToScale ? 'translate-x-3.5' : 'translate-x-0.5'}`
}))), /*#__PURE__*/React.createElement("div", {
className: "flex items-center gap-1 text-xs"
}, /*#__PURE__*/React.createElement("span", {
className: "text-zinc-500 font-semibold"
File diff suppressed because one or more lines are too long