From 198411b4eda63170d18fd3b32e81503fbef7a2b2 Mon Sep 17 00:00:00 2001 From: ChuxiJ Date: Fri, 27 Mar 2026 01:29:31 +0800 Subject: [PATCH] fix: use sample track for external audio file drops on timeline When dropping an external audio file on the empty timeline area, it was creating a Quick Sampler (piano roll) track instead of a sample track. The audio was invisible because Quick Sampler doesn't render waveform clips on the timeline. Now defaults to importAudioFile() which creates a proper sample track with visible waveform. Alt+Drop still creates Quick Sampler for users who want that workflow. Fixes both the main timeline drop zone and the empty track slot drop. Closes #916 Co-Authored-By: Claude Opus 4.6 (1M context) --- src/components/timeline/Timeline.tsx | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/src/components/timeline/Timeline.tsx b/src/components/timeline/Timeline.tsx index 265d1188..66081b04 100644 --- a/src/components/timeline/Timeline.tsx +++ b/src/components/timeline/Timeline.tsx @@ -266,7 +266,7 @@ export function Timeline() { const zoomAnchorRef = useRef<{ time: number; viewportX: number } | null>(null); const zoomFrameTimeRef = useRef(null); const handledTimelineZoomRequestIdRef = useRef(null); - const { importMultipleFiles, importLoopToTrack, importAssetToTrack, importAudioFileAsNewQuickSampler, importAssetAsQuickSampler } = useAudioImport(); + const { importAudioFile, importMultipleFiles, importLoopToTrack, importAssetToTrack, importAudioFileAsNewQuickSampler, importAssetAsQuickSampler } = useAudioImport(); const isTrackListCollapsed = trackListDisplayMode === 'collapsed'; const handleDragOver = useCallback((e: React.DragEvent) => { @@ -295,18 +295,23 @@ export function Timeline() { return; } - // Audio files -> Quick Sampler, MIDI files -> piano roll tracks + // Audio files -> sample track (Alt+Drop -> Quick Sampler), MIDI files -> piano roll tracks + const wantsQuickSampler = e.altKey; const files = e.dataTransfer.files; if (files.length > 0) { for (const file of Array.from(files)) { if (file.type.startsWith('audio/') || /\.(wav|mp3|ogg|flac|aac|m4a|webm)$/i.test(file.name)) { - await importAudioFileAsNewQuickSampler(file); + if (wantsQuickSampler) { + await importAudioFileAsNewQuickSampler(file); + } else { + await importAudioFile(file); + } } else if (/\.(mid|midi)$/i.test(file.name)) { await importMultipleFiles([file]); } } } - }, [addTrack, importMultipleFiles, importLoopToTrack, importAssetToTrack, importAudioFileAsNewQuickSampler, importAssetAsQuickSampler]); + }, [addTrack, importAudioFile, importMultipleFiles, importLoopToTrack, importAssetToTrack, importAudioFileAsNewQuickSampler, importAssetAsQuickSampler]); const handleTrackHeaderDragStart = useCallback((trackId: string) => { draggedTrackIdRef.current = trackId; @@ -1260,7 +1265,7 @@ function EmptyTrackRow({ slotIndex }: { slotIndex: number }) { const addTrack = useProjectStore((s) => s.addTrack); const virtualId = getArrangementEmptyTrackId(slotIndex); const isSelected = selectedTrackIds.has(virtualId); - const { importLoopToTrack, importAssetToTrack, importAssetAsQuickSampler, importAudioFileAsNewQuickSampler } = useAudioImport(); + const { importAudioFile, importLoopToTrack, importAssetToTrack, importAssetAsQuickSampler, importAudioFileAsNewQuickSampler } = useAudioImport(); const laneRef = useRef(null); const [dropGhost, setDropGhost] = useState<{ left: number; width: number; name: string } | null>(null); @@ -1360,15 +1365,20 @@ function EmptyTrackRow({ slotIndex }: { slotIndex: number }) { return; } + const wantsQuickSampler = e.altKey; const files = e.dataTransfer.files; if (files.length > 0) { for (const file of Array.from(files)) { if (file.type.startsWith('audio/') || /\.(wav|mp3|ogg|flac|aac|m4a|webm)$/i.test(file.name)) { - await importAudioFileAsNewQuickSampler(file); + if (wantsQuickSampler) { + await importAudioFileAsNewQuickSampler(file); + } else { + await importAudioFile(file); + } } } } - }, [hasProject, pixelsPerSecond, addTrack, importLoopToTrack, importAssetToTrack, importAssetAsQuickSampler, importAudioFileAsNewQuickSampler, bpm, tempoMap]); + }, [hasProject, pixelsPerSecond, addTrack, importAudioFile, importLoopToTrack, importAssetToTrack, importAssetAsQuickSampler, importAudioFileAsNewQuickSampler, bpm, tempoMap]); return (