refactor: improve UX with 2-tab sidebar layout (Option A)

Changed from confusing 3-tab layout to cleaner 2-tab design inspired by
professional DAWs like Ableton Live:

**Tracks Tab:**
- Track management actions (Add/Import/Clear)
- Track count with selected track indicator
- Contextual help text
- Selected track's effects shown below (when track is selected)
- Clear visual separation with border-top

**Master Tab:**
- Master channel description
- Master effects chain
- Preset management for master effects
- Clear that these apply to final mix

Benefits:
- Clear separation: track operations vs master operations
- Contextual: selecting a track shows its effects in same tab
- Less cognitive load than 3 tabs
- Follows DAW conventions (track strip vs master strip)
- Selected track name shown in status area

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-18 07:41:16 +01:00
parent f640f2f9d4
commit a8f2391400

View File

@@ -75,7 +75,7 @@ export function SidePanel({
className, className,
}: SidePanelProps) { }: SidePanelProps) {
const [isCollapsed, setIsCollapsed] = React.useState(false); const [isCollapsed, setIsCollapsed] = React.useState(false);
const [activeTab, setActiveTab] = React.useState<'tracks' | 'trackFx' | 'masterFx'>('tracks'); const [activeTab, setActiveTab] = React.useState<'tracks' | 'master'>('tracks');
const [presetDialogOpen, setPresetDialogOpen] = React.useState(false); const [presetDialogOpen, setPresetDialogOpen] = React.useState(false);
const selectedTrack = tracks.find((t) => t.id === selectedTrackId); const selectedTrack = tracks.find((t) => t.id === selectedTrackId);
@@ -107,27 +107,21 @@ export function SidePanel({
<div className="flex items-center gap-1"> <div className="flex items-center gap-1">
<Button <Button
variant={activeTab === 'tracks' ? 'secondary' : 'ghost'} variant={activeTab === 'tracks' ? 'secondary' : 'ghost'}
size="icon-sm" size="sm"
onClick={() => setActiveTab('tracks')} onClick={() => setActiveTab('tracks')}
title="Tracks" title="Tracks"
> >
<Music2 className="h-4 w-4" /> <Music2 className="h-4 w-4 mr-1.5" />
Tracks
</Button> </Button>
<Button <Button
variant={activeTab === 'trackFx' ? 'secondary' : 'ghost'} variant={activeTab === 'master' ? 'secondary' : 'ghost'}
size="icon-sm" size="sm"
onClick={() => setActiveTab('trackFx')} onClick={() => setActiveTab('master')}
title="Track Effects" title="Master"
> >
<Link2 className="h-4 w-4" /> <Link2 className="h-4 w-4 mr-1.5 text-primary" />
</Button> Master
<Button
variant={activeTab === 'masterFx' ? 'secondary' : 'ghost'}
size="icon-sm"
onClick={() => setActiveTab('masterFx')}
title="Master Effects"
>
<Link2 className="h-4 w-4 text-primary" />
</Button> </Button>
</div> </div>
<Button <Button
@@ -147,7 +141,7 @@ export function SidePanel({
{/* Track Actions */} {/* Track Actions */}
<div className="space-y-2"> <div className="space-y-2">
<h3 className="text-xs font-semibold text-muted-foreground uppercase"> <h3 className="text-xs font-semibold text-muted-foreground uppercase">
Multi-Track Editor Track Management
</h3> </h3>
<div className="flex gap-2"> <div className="flex gap-2">
<Button <Button
@@ -182,17 +176,25 @@ export function SidePanel({
)} )}
</div> </div>
{/* Track List - Simplified */} {/* Track List Summary */}
{tracks.length > 0 ? ( {tracks.length > 0 ? (
<div className="space-y-2"> <div className="space-y-2">
<div className="flex items-center justify-between">
<h3 className="text-xs font-semibold text-muted-foreground uppercase"> <h3 className="text-xs font-semibold text-muted-foreground uppercase">
Tracks ({tracks.length}) Tracks ({tracks.length})
</h3> </h3>
{selectedTrack && (
<span className="text-xs text-primary">
{String(selectedTrack.name || 'Untitled Track')} selected
</span>
)}
</div>
<div className="text-xs text-muted-foreground"> <div className="text-xs text-muted-foreground">
<p className="mb-2"> <p>
Track controls are located on the left side of each track in the timeline. {selectedTrack
? 'Track controls are on the left of each track. Effects for the selected track are shown below.'
: 'Click a track\'s waveform to select it and edit its effects below.'}
</p> </p>
<p>Click a track to select it and apply effects from the Effect Chain tab.</p>
</div> </div>
</div> </div>
) : ( ) : (
@@ -203,19 +205,14 @@ export function SidePanel({
</p> </p>
</div> </div>
)} )}
</>
)}
{activeTab === 'trackFx' && ( {/* Selected Track Effects */}
<div className="space-y-2"> {selectedTrack && (
<div className="space-y-2 pt-3 border-t border-border">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h3 className="text-xs font-semibold text-muted-foreground uppercase"> <h3 className="text-xs font-semibold text-muted-foreground uppercase">
Track Effects Track Effects
{selectedTrack && (
<span className="text-primary ml-2">({String(selectedTrack.name || 'Untitled Track')})</span>
)}
</h3> </h3>
<div className="flex gap-1">
{trackEffectChain && trackEffectChain.effects.length > 0 && ( {trackEffectChain && trackEffectChain.effects.length > 0 && (
<Button <Button
variant="ghost" variant="ghost"
@@ -227,28 +224,33 @@ export function SidePanel({
</Button> </Button>
)} )}
</div> </div>
</div>
{!selectedTrack ? (
<div className="text-center py-8">
<Link2 className="h-12 w-12 mx-auto text-muted-foreground/50 mb-2" />
<p className="text-sm text-muted-foreground">
Select a track to apply effects
</p>
</div>
) : (
<EffectRack <EffectRack
chain={trackEffectChain!} chain={trackEffectChain!}
onToggleEffect={onToggleTrackEffect} onToggleEffect={onToggleTrackEffect}
onRemoveEffect={onRemoveTrackEffect} onRemoveEffect={onRemoveTrackEffect}
onReorderEffects={onReorderTrackEffects} onReorderEffects={onReorderTrackEffects}
/> />
)}
</div> </div>
)} )}
</>
)}
{activeTab === 'masterFx' && ( {activeTab === 'master' && (
<>
{/* Master Channel Info */}
<div className="space-y-2"> <div className="space-y-2">
<h3 className="text-xs font-semibold text-muted-foreground uppercase">
Master Channel
</h3>
<div className="text-xs text-muted-foreground">
<p>
Master effects are applied to the final mix of all tracks.
</p>
</div>
</div>
{/* Master Effects */}
<div className="space-y-2 pt-3 border-t border-border">
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
<h3 className="text-xs font-semibold text-muted-foreground uppercase"> <h3 className="text-xs font-semibold text-muted-foreground uppercase">
Master Effects Master Effects
@@ -293,6 +295,7 @@ export function SidePanel({
onImportPreset={(preset) => onSaveMasterPreset(preset)} onImportPreset={(preset) => onSaveMasterPreset(preset)}
/> />
</div> </div>
</>
)} )}
</div> </div>
</div> </div>