'use client'; import * as React from 'react'; import { ChevronLeft, ChevronRight, Upload, Plus, Trash2, Link2, FolderOpen, Volume2, Music2, } from 'lucide-react'; import { Button } from '@/components/ui/Button'; import { Slider } from '@/components/ui/Slider'; import { cn } from '@/lib/utils/cn'; import { formatDuration } from '@/lib/audio/decoder'; import type { Track } from '@/types/track'; import type { EffectChain, EffectPreset } from '@/lib/audio/effects/chain'; import { EffectRack } from '@/components/effects/EffectRack'; import { PresetManager } from '@/components/effects/PresetManager'; export interface SidePanelProps { tracks: Track[]; selectedTrackId: string | null; onSelectTrack: (trackId: string | null) => void; onAddTrack: () => void; onImportTracks: () => void; onUpdateTrack: (trackId: string, updates: Partial) => void; onRemoveTrack: (trackId: string) => void; onClearTracks: () => void; // Effect chain effectChain: EffectChain; effectPresets: EffectPreset[]; onToggleEffect: (effectId: string) => void; onRemoveEffect: (effectId: string) => void; onReorderEffects: (fromIndex: number, toIndex: number) => void; onSavePreset: (preset: EffectPreset) => void; onLoadPreset: (preset: EffectPreset) => void; onDeletePreset: (presetId: string) => void; onClearChain: () => void; className?: string; } export function SidePanel({ tracks, selectedTrackId, onSelectTrack, onAddTrack, onImportTracks, onUpdateTrack, onRemoveTrack, onClearTracks, effectChain, effectPresets, onToggleEffect, onRemoveEffect, onReorderEffects, onSavePreset, onLoadPreset, onDeletePreset, onClearChain, className, }: SidePanelProps) { const [isCollapsed, setIsCollapsed] = React.useState(false); const [activeTab, setActiveTab] = React.useState<'tracks' | 'chain'>('tracks'); const [presetDialogOpen, setPresetDialogOpen] = React.useState(false); const selectedTrack = tracks.find((t) => t.id === selectedTrackId); if (isCollapsed) { return (
); } return (
{/* Header */}
{/* Content */}
{activeTab === 'tracks' && ( <> {/* Track Actions */}

Multi-Track Editor

{tracks.length > 0 && ( )}
{/* Track List */} {tracks.length > 0 ? (

Tracks ({tracks.length})

{tracks.map((track) => { const isSelected = selectedTrackId === track.id; return (
onSelectTrack(isSelected ? null : track.id)} >
{String(track.name || 'Untitled Track')}
{track.audioBuffer && (
{formatDuration(track.audioBuffer.duration)}
)}
{/* Track Controls - Always visible */}
e.stopPropagation()}> {/* Volume */}
{Math.round(track.volume * 100)}%
onUpdateTrack(track.id, { volume: value })} min={0} max={1} step={0.01} />
{/* Pan */}
{track.pan === 0 ? 'C' : track.pan < 0 ? `L${Math.round(Math.abs(track.pan) * 100)}` : `R${Math.round(track.pan * 100)}`}
onUpdateTrack(track.id, { pan: value })} min={-1} max={1} step={0.01} />
{/* Solo / Mute */}
); })}
) : (

No tracks yet. Add or import audio files to get started.

)} )} {activeTab === 'chain' && (

Effect Chain {selectedTrack && ( ({String(selectedTrack.name || 'Untitled Track')}) )}

{effectChain.effects.length > 0 && ( )}
{!selectedTrack ? (

Select a track to apply effects

) : ( <> setPresetDialogOpen(false)} currentChain={effectChain} presets={effectPresets} onSavePreset={onSavePreset} onLoadPreset={onLoadPreset} onDeletePreset={onDeletePreset} onExportPreset={() => {}} onImportPreset={(preset) => onSavePreset(preset)} /> )}
)}
); }