'use client'; import * as React from 'react'; import { Music } from 'lucide-react'; import { PlaybackControls } from './PlaybackControls'; import { SidePanel } from '@/components/layout/SidePanel'; import { ThemeToggle } from '@/components/layout/ThemeToggle'; import { CommandPalette } from '@/components/ui/CommandPalette'; import type { CommandAction } from '@/components/ui/CommandPalette'; import { useMultiTrack } from '@/lib/hooks/useMultiTrack'; import { useMultiTrackPlayer } from '@/lib/hooks/useMultiTrackPlayer'; import { useEffectChain } from '@/lib/hooks/useEffectChain'; import { useToast } from '@/components/ui/Toast'; import { TrackList } from '@/components/tracks/TrackList'; import { ImportTrackDialog } from '@/components/tracks/ImportTrackDialog'; import { formatDuration } from '@/lib/audio/decoder'; export function AudioEditor() { const [importDialogOpen, setImportDialogOpen] = React.useState(false); const [selectedTrackId, setSelectedTrackId] = React.useState(null); const [zoom, setZoom] = React.useState(1); const { addToast } = useToast(); // Multi-track hooks const { tracks, addTrack, addTrackFromBuffer, removeTrack, updateTrack, clearTracks, } = useMultiTrack(); const { isPlaying, currentTime, duration, play, pause, stop, seek, togglePlayPause, } = useMultiTrackPlayer(tracks); // Effect chain (for selected track) const { chain: effectChain, presets: effectPresets, toggleEffectEnabled, removeEffect, reorder: reorderEffects, clearChain, savePreset, loadPresetToChain, deletePreset, } = useEffectChain(); // Multi-track handlers const handleImportTracks = () => { setImportDialogOpen(true); }; const handleImportTrack = (buffer: AudioBuffer, name: string) => { addTrackFromBuffer(buffer, name); }; const handleClearTracks = () => { clearTracks(); setSelectedTrackId(null); addToast({ title: 'Tracks Cleared', description: 'All tracks have been removed', variant: 'info', duration: 2000, }); }; const handleRemoveTrack = (trackId: string) => { removeTrack(trackId); if (selectedTrackId === trackId) { setSelectedTrackId(null); } }; // Zoom controls const handleZoomIn = () => { setZoom((prev) => Math.min(20, prev + 1)); }; const handleZoomOut = () => { setZoom((prev) => Math.max(1, prev - 1)); }; const handleFitToView = () => { setZoom(1); }; // Keyboard shortcuts React.useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { // Spacebar for play/pause - only if not typing in an input if (e.code === 'Space' && !(e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement)) { e.preventDefault(); togglePlayPause(); } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [togglePlayPause]); // Find selected track const selectedTrack = tracks.find((t) => t.id === selectedTrackId); // Command palette actions const commandActions: CommandAction[] = React.useMemo(() => { const actions: CommandAction[] = [ // Playback { id: 'play', label: 'Play', description: 'Start playback', shortcut: 'Space', category: 'playback', action: play, }, { id: 'pause', label: 'Pause', description: 'Pause playback', shortcut: 'Space', category: 'playback', action: pause, }, { id: 'stop', label: 'Stop', description: 'Stop playback', category: 'playback', action: stop, }, // View { id: 'zoom-in', label: 'Zoom In', description: 'Zoom in on waveforms', category: 'view', action: handleZoomIn, }, { id: 'zoom-out', label: 'Zoom Out', description: 'Zoom out on waveforms', category: 'view', action: handleZoomOut, }, { id: 'fit-to-view', label: 'Fit to View', description: 'Reset zoom to fit all tracks', category: 'view', action: handleFitToView, }, // Tracks { id: 'add-track', label: 'Add Empty Track', description: 'Create a new empty track', category: 'tracks', action: () => addTrack(), }, { id: 'import-tracks', label: 'Import Audio Files', description: 'Import multiple audio files as tracks', category: 'tracks', action: handleImportTracks, }, { id: 'clear-tracks', label: 'Clear All Tracks', description: 'Remove all tracks', category: 'tracks', action: handleClearTracks, }, ]; return actions; }, [play, pause, stop, handleZoomIn, handleZoomOut, handleFitToView, handleImportTracks, handleClearTracks, addTrack]); // Keyboard shortcuts React.useEffect(() => { const handleKeyDown = (e: KeyboardEvent) => { // Prevent shortcuts if typing in an input const isTyping = e.target instanceof HTMLInputElement || e.target instanceof HTMLTextAreaElement; // Spacebar: Play/Pause (always, unless typing in an input) if (e.code === 'Space' && !isTyping) { e.preventDefault(); togglePlayPause(); return; } if (isTyping) return; // Escape: Clear selection if (e.key === 'Escape') { e.preventDefault(); setSelectedTrackId(null); } }; window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown); }, [togglePlayPause]); return ( <> {/* Compact Header */}
{/* Left: Logo */}

Audio UI

{/* Right: Command Palette + Theme Toggle */}
{/* Main content area */}
{/* Side Panel */} {/* Main canvas area */}
{/* Multi-Track View */}
{/* Multi-Track Playback Controls */}
{}} currentTimeFormatted={formatDuration(currentTime)} durationFormatted={formatDuration(duration)} />
{/* Import Track Dialog */} setImportDialogOpen(false)} onImportTrack={handleImportTrack} /> ); }