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>
This commit is contained in:
237
components/layout/Toolbar.tsx
Normal file
237
components/layout/Toolbar.tsx
Normal file
@@ -0,0 +1,237 @@
|
||||
'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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user