fix: snap to scale now works on initial click + use refs

- Initial click note pitch now snaps when snapToScale is on
- Added snapToScaleRef and selectedScaleRef for stale-closure safety
- All brush-draw snap paths (mousedown + mousemove) use refs
This commit is contained in:
2026-07-25 12:42:50 +07:00
parent f5bc0a8810
commit eaedfebdf0
2 changed files with 11 additions and 7 deletions
+9 -5
View File
@@ -5056,7 +5056,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
const noteId = 'note_' + Date.now() + Math.random().toString(36).substr(2, 5); const noteId = 'note_' + Date.now() + Math.random().toString(36).substr(2, 5);
const newNote = { const newNote = {
id: noteId, id: noteId,
pitch: pitch, pitch: snapToScaleRef.current ? snapPitchToScale(pitch, selectedScaleRef.current) : pitch,
start_beat: start, start_beat: start,
duration_beats: initialDur, duration_beats: initialDur,
velocity: brushVelocityRef.current, velocity: brushVelocityRef.current,
@@ -5068,12 +5068,12 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
mode: 'draw', mode: 'draw',
idx: -1, idx: -1,
startOffsetBeat: start, startOffsetBeat: start,
startOffsetPitch: snapToScale ? snapPitchToScale(pitch, selectedScale) : pitch, startOffsetPitch: snapToScaleRef.current ? snapPitchToScale(pitch, selectedScaleRef.current) : pitch,
drawNoteId: noteId, drawNoteId: noteId,
drawDuration: initialDur, drawDuration: initialDur,
visitedPitches: snapToScale ? [snapPitchToScale(pitch, selectedScale)] : [pitch], visitedPitches: snapToScaleRef.current ? [snapPitchToScale(pitch, selectedScaleRef.current)] : [pitch],
initialBeat: start, initialBeat: start,
initialPitch: snapToScale ? snapPitchToScale(pitch, selectedScale) : pitch initialPitch: snapToScaleRef.current ? snapPitchToScale(pitch, selectedScaleRef.current) : pitch
}); });
// Play the note with SoundFont // Play the note with SoundFont
if (window.SonicSF) { if (window.SonicSF) {
@@ -5156,7 +5156,7 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
return { ...n, duration_beats: newDur }; return { ...n, duration_beats: newDur };
})); }));
} }
const snappedPitch = snapToScale ? snapPitchToScale(pitch, selectedScale) : pitch; const snappedPitch = snapToScaleRef.current ? snapPitchToScale(pitch, selectedScaleRef.current) : pitch;
if (!visited.includes(snappedPitch)) { if (!visited.includes(snappedPitch)) {
const newPitches = [...visited, snappedPitch]; const newPitches = [...visited, snappedPitch];
const totalSpan = Math.max(0.125, beat - draggedNote.initialBeat); const totalSpan = Math.max(0.125, beat - draggedNote.initialBeat);
@@ -5462,7 +5462,11 @@ const PianoRollTabEditor = ({ st, zoom, bpm, viewportWidth, onClose, onUpdateNot
} }
}; };
const [selectedScale, setSelectedScale] = React.useState(null); const [selectedScale, setSelectedScale] = React.useState(null);
const selectedScaleRef = React.useRef(null);
selectedScaleRef.current = selectedScale;
const [snapToScale, setSnapToScale] = React.useState(true); const [snapToScale, setSnapToScale] = React.useState(true);
const snapToScaleRef = React.useRef(true);
snapToScaleRef.current = snapToScale;
const [scaleMenuPos, setScaleMenuPos] = React.useState(null); const [scaleMenuPos, setScaleMenuPos] = React.useState(null);
const scaleMenuOriginRef = React.useRef(null); const scaleMenuOriginRef = React.useRef(null);
const [aiPrompt, setAiPrompt] = React.useState(''); const [aiPrompt, setAiPrompt] = React.useState('');
File diff suppressed because one or more lines are too long