feat: add cross-component playhead scroll synchronization

This commit is contained in:
Xiaohan-Tian
2026-05-02 00:25:41 -07:00
parent 6b8a470022
commit da8b9496df
7 changed files with 94 additions and 11 deletions
@@ -14,7 +14,7 @@ const PianoGridHeader: React.FC<PianoGridHeaderProps> = ({
timeSignature = { numerator: 4, denominator: 4 } // Default to 4/4 if not provided
}) => {
// Get store access for playhead position updates
const { setPlayheadPosition } = useProjectStore();
const { setPlayheadPosition, requestMainContentScroll } = useProjectStore();
// Refs for drag functionality
const isDraggingRef = useRef(false);
@@ -122,8 +122,9 @@ const PianoGridHeader: React.FC<PianoGridHeaderProps> = ({
console.log(`Current bar: ${currentBarNumber} (beat ${currentPlayheadPosition})`);
console.log(`Destination bar: ${destinationBarNumber} (beat ${newPosition})`);
}
setPlayheadPosition(newPosition);
requestMainContentScroll(newPosition);
}
};
+29 -1
View File
@@ -41,7 +41,7 @@ const PianoRoll: React.FC<PianoRollProps> = ({
}) => {
const isSpectrogram = mode === 'spectrogram';
const isHybrid = mode === 'hybrid';
const { maxBars, tracks, updateTrack, timeSignature, showChatBox, showInstrumentSelection, keySignature, selectedMode, setSelectedMode, playheadPosition, isPlaying, autoScrollEnabled, bpm } = useProjectStore();
const { maxBars, tracks, updateTrack, timeSignature, showChatBox, showInstrumentSelection, keySignature, selectedMode, setSelectedMode, playheadPosition, isPlaying, autoScrollEnabled, bpm, pianoRollScrollRequest } = useProjectStore();
// Tool state for piano roll
const [activeTool, setActiveTool] = useState<'pointer' | 'pencil'>('pointer');
@@ -689,6 +689,34 @@ const PianoRoll: React.FC<PianoRollProps> = ({
container.scrollLeft = clampedScrollLeft;
}, [playheadPosition, isPlaying, autoScrollEnabled]);
// Handle scroll requests from main content bar numbers clicks
useEffect(() => {
if (pianoRollScrollRequest === null) return;
const container = pianoRollContentRef.current;
if (!container) return;
const beatWidth = parseInt(
getComputedStyle(document.documentElement).getPropertyValue('--region-grid-beat-width')
) || 40;
const playheadPixel = pianoRollScrollRequest * beatWidth;
// Center the playhead in the visible grid area (excluding the 60px sticky piano keys panel)
const keysWidth = parseInt(
getComputedStyle(document.documentElement).getPropertyValue('--region-piano-key-width')
) || 60;
const targetScrollLeft = playheadPixel - (container.clientWidth - keysWidth) / 2;
const clampedScrollLeft = Math.max(
0,
Math.min(targetScrollLeft, container.scrollWidth - container.clientWidth)
);
container.scrollLeft = clampedScrollLeft;
// Clear the request after handling
useProjectStore.setState({ pianoRollScrollRequest: null });
}, [pianoRollScrollRequest]);
// Update --region-grid-beat-width when zoom changes; reset on unmount
useEffect(() => {
document.documentElement.style.setProperty('--region-grid-beat-width', `${40 * pianoRollZoom}px`);