Files
audio-ui/components/layout/Toolbar.tsx
Sebastian Krüger ee48f9475f feat: add advanced audio effects and improve UI
Phase 6.5 Advanced Effects:
- Add Pitch Shifter with semitones and cents adjustment
- Add Time Stretch with pitch preservation using overlap-add
- Add Distortion with soft/hard/tube types and tone control
- Add Bitcrusher with bit depth and sample rate reduction
- Add AdvancedParameterDialog with real-time waveform visualization
- Add 4 professional presets per effect type

Improvements:
- Fix undefined parameter errors by adding nullish coalescing operators
- Add global custom scrollbar styling with color-mix transparency
- Add custom-scrollbar utility class for side panel
- Improve theme-aware scrollbar appearance in light/dark modes
- Fix parameter initialization when switching effect types

Integration:
- All advanced effects support undo/redo via EffectCommand
- Effects accessible via command palette and side panel
- Selection-based processing support
- Toast notifications for all effects

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-17 20:03:40 +01:00

238 lines
4.9 KiB
TypeScript

'use client';
import * as React from 'react';
import {
Play,
Pause,
Square,
SkipBack,
Scissors,
Copy,
Clipboard,
Trash2,
CropIcon,
Undo2,
Redo2,
ZoomIn,
ZoomOut,
Maximize2,
} from 'lucide-react';
import { Button } from '@/components/ui/Button';
import { cn } from '@/lib/utils/cn';
export interface ToolbarProps {
// Playback
isPlaying: boolean;
isPaused: boolean;
onPlay: () => void;
onPause: () => void;
onStop: () => void;
// Edit
hasSelection: boolean;
hasClipboard: boolean;
onCut: () => void;
onCopy: () => void;
onPaste: () => void;
onDelete: () => void;
onTrim: () => void;
// History
canUndo: boolean;
canRedo: boolean;
onUndo: () => void;
onRedo: () => void;
// Zoom
onZoomIn: () => void;
onZoomOut: () => void;
onFitToView: () => void;
disabled?: boolean;
className?: string;
}
export function Toolbar({
isPlaying,
isPaused,
onPlay,
onPause,
onStop,
hasSelection,
hasClipboard,
onCut,
onCopy,
onPaste,
onDelete,
onTrim,
canUndo,
canRedo,
onUndo,
onRedo,
onZoomIn,
onZoomOut,
onFitToView,
disabled = false,
className,
}: ToolbarProps) {
const handlePlayPause = () => {
if (isPlaying) {
onPause();
} else {
onPlay();
}
};
return (
<div
className={cn(
'flex items-center gap-1 px-2 py-1.5 bg-card border-b border-border',
className
)}
>
{/* Transport Controls */}
<div className="flex items-center gap-0.5 pr-2 border-r border-border">
<Button
variant="ghost"
size="icon-sm"
onClick={onStop}
disabled={disabled || (!isPlaying && !isPaused)}
title="Stop"
>
<SkipBack className="h-4 w-4" />
</Button>
<Button
variant={isPlaying ? 'default' : 'ghost'}
size="icon-sm"
onClick={handlePlayPause}
disabled={disabled}
title={isPlaying ? 'Pause (Space)' : 'Play (Space)'}
>
{isPlaying ? (
<Pause className="h-4 w-4" />
) : (
<Play className="h-4 w-4 ml-0.5" />
)}
</Button>
<Button
variant="ghost"
size="icon-sm"
onClick={onStop}
disabled={disabled || (!isPlaying && !isPaused)}
title="Stop"
>
<Square className="h-4 w-4" />
</Button>
</div>
{/* Edit Tools */}
<div className="flex items-center gap-0.5 px-2 border-r border-border">
<Button
variant="ghost"
size="icon-sm"
onClick={onCut}
disabled={!hasSelection}
title="Cut (Ctrl+X)"
>
<Scissors className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon-sm"
onClick={onCopy}
disabled={!hasSelection}
title="Copy (Ctrl+C)"
>
<Copy className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon-sm"
onClick={onPaste}
disabled={!hasClipboard}
title="Paste (Ctrl+V)"
>
<Clipboard className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon-sm"
onClick={onDelete}
disabled={!hasSelection}
title="Delete (Del)"
>
<Trash2 className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon-sm"
onClick={onTrim}
disabled={!hasSelection}
title="Trim to Selection"
>
<CropIcon className="h-4 w-4" />
</Button>
</div>
{/* History */}
<div className="flex items-center gap-0.5 px-2 border-r border-border">
<Button
variant="ghost"
size="icon-sm"
onClick={onUndo}
disabled={!canUndo}
title="Undo (Ctrl+Z)"
>
<Undo2 className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon-sm"
onClick={onRedo}
disabled={!canRedo}
title="Redo (Ctrl+Y)"
>
<Redo2 className="h-4 w-4" />
</Button>
</div>
{/* Zoom Controls */}
<div className="flex items-center gap-0.5 px-2">
<Button
variant="ghost"
size="icon-sm"
onClick={onZoomOut}
title="Zoom Out"
>
<ZoomOut className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon-sm"
onClick={onZoomIn}
title="Zoom In"
>
<ZoomIn className="h-4 w-4" />
</Button>
<Button
variant="ghost"
size="icon-sm"
onClick={onFitToView}
title="Fit to View"
>
<Maximize2 className="h-4 w-4" />
</Button>
</div>
</div>
);
}