feat: add EffectBrowser dialog for adding effects to tracks

Created a beautiful effect browser dialog inspired by Ableton Live:

**EffectBrowser Component:**
- Modal dialog with search functionality
- Effects organized by category:
  - Dynamics (Compressor, Limiter, Gate)
  - Filters (Lowpass, Highpass, Bandpass, etc.)
  - Time-Based (Delay, Reverb, Chorus, Flanger, Phaser)
  - Distortion (Distortion, Bitcrusher)
  - Pitch & Time (Pitch Shifter, Time Stretch)
  - Utility (Normalize, Fade In/Out, Reverse)
- Grid layout with hover effects
- Real-time search filtering
- Click effect to add to track

**Integration:**
- "+" button in track strip opens EffectBrowser dialog
- Selecting an effect adds it to the track's effect chain
- Effects appear immediately in the Devices section
- Full enable/disable and remove functionality

**UX Flow:**
1. Click "+" in track Devices section
2. Browse or search for effect
3. Click effect to add it
4. Effect appears in Devices list
5. Toggle on/off with ●/○
6. Remove with × button

Effects are now fully manageable in the UI! Next: Apply them to audio.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-18 08:02:21 +01:00
parent 25e843236d
commit cb396ddfd6
3 changed files with 151 additions and 4 deletions

View File

@@ -6,6 +6,8 @@ import type { Track as TrackType } from '@/types/track';
import { Button } from '@/components/ui/Button';
import { Slider } from '@/components/ui/Slider';
import { cn } from '@/lib/utils/cn';
import { EffectBrowser } from '@/components/effects/EffectBrowser';
import { createEffect, type EffectType } from '@/lib/audio/effects/chain';
export interface TrackProps {
track: TrackType;
@@ -25,6 +27,7 @@ export interface TrackProps {
onLoadAudio?: (buffer: AudioBuffer) => void;
onToggleEffect?: (effectId: string) => void;
onRemoveEffect?: (effectId: string) => void;
onAddEffect?: (effectType: EffectType) => void;
}
export function Track({
@@ -45,6 +48,7 @@ export function Track({
onLoadAudio,
onToggleEffect,
onRemoveEffect,
onAddEffect,
}: TrackProps) {
const canvasRef = React.useRef<HTMLCanvasElement>(null);
const containerRef = React.useRef<HTMLDivElement>(null);
@@ -52,6 +56,7 @@ export function Track({
const [isEditingName, setIsEditingName] = React.useState(false);
const [nameInput, setNameInput] = React.useState(String(track.name || 'Untitled Track'));
const [showDevices, setShowDevices] = React.useState(true);
const [effectBrowserOpen, setEffectBrowserOpen] = React.useState(false);
const inputRef = React.useRef<HTMLInputElement>(null);
const handleNameClick = () => {
@@ -401,10 +406,7 @@ export function Track({
<Button
variant="ghost"
size="icon-sm"
onClick={() => {
// TODO: Open effect browser/selector dialog
console.log('Add effect clicked for track:', track.id);
}}
onClick={() => setEffectBrowserOpen(true)}
title="Add effect"
className="h-5 w-5"
>
@@ -498,6 +500,17 @@ export function Track({
)
)}
</div>
{/* Effect Browser Dialog */}
<EffectBrowser
open={effectBrowserOpen}
onClose={() => setEffectBrowserOpen(false)}
onSelectEffect={(effectType) => {
if (onAddEffect) {
onAddEffect(effectType);
}
}}
/>
</div>
);
}