feat: integrate multi-track functionality into main AudioEditor
Added comprehensive multi-track support to the main application: - Added "Tracks" tab to SidePanel with track management controls - Integrated useMultiTrack and useMultiTrackPlayer hooks into AudioEditor - Added view mode switching between waveform and tracks views - Implemented "Convert to Track" to convert current audio buffer to track - Added TrackList view with multi-track playback controls - Wired up ImportTrackDialog for importing multiple audio files Users can now: - Click "Tracks" tab in side panel to access multi-track mode - Convert current audio to a track - Import multiple audio files as tracks - View and manage tracks in dedicated TrackList view - Play multiple tracks simultaneously with individual controls 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -14,6 +14,8 @@ import {
|
||||
Link2,
|
||||
FolderOpen,
|
||||
Trash2,
|
||||
Layers,
|
||||
Plus,
|
||||
} from 'lucide-react';
|
||||
import { Button } from '@/components/ui/Button';
|
||||
import { cn } from '@/lib/utils/cn';
|
||||
@@ -23,6 +25,7 @@ import type { HistoryState } from '@/lib/history/history-manager';
|
||||
import type { EffectChain, ChainEffect, EffectPreset } from '@/lib/audio/effects/chain';
|
||||
import { EffectRack } from '@/components/effects/EffectRack';
|
||||
import { PresetManager } from '@/components/effects/PresetManager';
|
||||
import type { Track } from '@/types/track';
|
||||
|
||||
export interface SidePanelProps {
|
||||
// File info
|
||||
@@ -69,6 +72,13 @@ export interface SidePanelProps {
|
||||
onDistortion: () => void;
|
||||
onBitcrusher: () => void;
|
||||
|
||||
// Multi-track
|
||||
tracks?: Track[];
|
||||
onAddTrack?: () => void;
|
||||
onImportTracks?: () => void;
|
||||
onConvertToTrack?: () => void;
|
||||
onClearTracks?: () => void;
|
||||
|
||||
className?: string;
|
||||
}
|
||||
|
||||
@@ -107,10 +117,15 @@ export function SidePanel({
|
||||
onTimeStretch,
|
||||
onDistortion,
|
||||
onBitcrusher,
|
||||
tracks,
|
||||
onAddTrack,
|
||||
onImportTracks,
|
||||
onConvertToTrack,
|
||||
onClearTracks,
|
||||
className,
|
||||
}: SidePanelProps) {
|
||||
const [isCollapsed, setIsCollapsed] = React.useState(false);
|
||||
const [activeTab, setActiveTab] = React.useState<'file' | 'chain' | 'history' | 'info' | 'effects'>('file');
|
||||
const [activeTab, setActiveTab] = React.useState<'file' | 'chain' | 'history' | 'info' | 'effects' | 'tracks'>('file');
|
||||
const [presetDialogOpen, setPresetDialogOpen] = React.useState(false);
|
||||
const fileInputRef = React.useRef<HTMLInputElement>(null);
|
||||
|
||||
@@ -174,6 +189,14 @@ export function SidePanel({
|
||||
>
|
||||
<Sparkles className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant={activeTab === 'tracks' ? 'secondary' : 'ghost'}
|
||||
size="icon-sm"
|
||||
onClick={() => setActiveTab('tracks')}
|
||||
title="Tracks"
|
||||
>
|
||||
<Layers className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
variant={activeTab === 'history' ? 'secondary' : 'ghost'}
|
||||
size="icon-sm"
|
||||
@@ -591,6 +614,78 @@ export function SidePanel({
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{activeTab === 'tracks' && (
|
||||
<div className="space-y-3">
|
||||
<div className="space-y-2">
|
||||
<h3 className="text-xs font-semibold text-muted-foreground uppercase">
|
||||
Multi-Track Mode
|
||||
</h3>
|
||||
{tracks && tracks.length > 0 ? (
|
||||
<div className="space-y-2">
|
||||
<div className="p-2 bg-secondary/30 rounded text-xs">
|
||||
<div className="text-foreground font-medium">
|
||||
{tracks.length} {tracks.length === 1 ? 'track' : 'tracks'} active
|
||||
</div>
|
||||
<div className="text-muted-foreground mt-1">
|
||||
Switch to Tracks view to manage
|
||||
</div>
|
||||
</div>
|
||||
{onClearTracks && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={onClearTracks}
|
||||
className="w-full"
|
||||
>
|
||||
<Trash2 className="h-3.5 w-3.5 mr-1.5 text-destructive" />
|
||||
Clear All Tracks
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-2">
|
||||
<div className="text-xs text-muted-foreground">
|
||||
Add tracks to work with multiple audio files simultaneously.
|
||||
</div>
|
||||
{audioBuffer && onConvertToTrack && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={onConvertToTrack}
|
||||
className="w-full"
|
||||
>
|
||||
<Plus className="h-3.5 w-3.5 mr-1.5" />
|
||||
Convert Current to Track
|
||||
</Button>
|
||||
)}
|
||||
{onAddTrack && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={onAddTrack}
|
||||
className="w-full"
|
||||
>
|
||||
<Plus className="h-3.5 w-3.5 mr-1.5" />
|
||||
Add Empty Track
|
||||
</Button>
|
||||
)}
|
||||
{onImportTracks && (
|
||||
<Button
|
||||
variant="outline"
|
||||
size="sm"
|
||||
onClick={onImportTracks}
|
||||
className="w-full"
|
||||
>
|
||||
<Upload className="h-3.5 w-3.5 mr-1.5" />
|
||||
Import Audio Files
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user