diff --git a/components/layout/SidePanel.tsx b/components/layout/SidePanel.tsx index 0220c3f..9d3abd5 100644 --- a/components/layout/SidePanel.tsx +++ b/components/layout/SidePanel.tsx @@ -9,13 +9,10 @@ import { Trash2, Link2, FolderOpen, - Volume2, Music2, } from 'lucide-react'; import { Button } from '@/components/ui/Button'; -import { Slider } from '@/components/ui/Slider'; import { cn } from '@/lib/utils/cn'; -import { formatDuration } from '@/lib/audio/decoder'; import type { Track } from '@/types/track'; import type { EffectChain, EffectPreset } from '@/lib/audio/effects/chain'; import { EffectRack } from '@/components/effects/EffectRack'; @@ -165,122 +162,17 @@ export function SidePanel({ )} - {/* Track List */} + {/* Track List - Simplified */} {tracks.length > 0 ? (

Tracks ({tracks.length})

-
- {tracks.map((track) => { - const isSelected = selectedTrackId === track.id; - return ( -
onSelectTrack(isSelected ? null : track.id)} - > -
-
-
- {String(track.name || 'Untitled Track')} -
- {track.audioBuffer && ( -
- {formatDuration(track.audioBuffer.duration)} -
- )} -
- -
- - {/* Track Controls - Always visible */} -
e.stopPropagation()}> - {/* Volume */} -
-
- - - {Math.round(track.volume * 100)}% - -
- onUpdateTrack(track.id, { volume: value })} - min={0} - max={1} - step={0.01} - /> -
- - {/* Pan */} -
-
- - - {track.pan === 0 - ? 'C' - : track.pan < 0 - ? `L${Math.round(Math.abs(track.pan) * 100)}` - : `R${Math.round(track.pan * 100)}`} - -
- onUpdateTrack(track.id, { pan: value })} - min={-1} - max={1} - step={0.01} - /> -
- - {/* Solo / Mute */} -
- - -
-
-
- ); - })} +
+

+ Track controls are located on the left side of each track in the timeline. +

+

Click a track to select it and apply effects from the Effect Chain tab.

) : ( diff --git a/components/tracks/Track.tsx b/components/tracks/Track.tsx index 4071103..d6093d7 100644 --- a/components/tracks/Track.tsx +++ b/components/tracks/Track.tsx @@ -1,8 +1,10 @@ 'use client'; import * as React from 'react'; +import { Volume2, VolumeX, Headphones, Trash2, ChevronDown, ChevronRight } from 'lucide-react'; import type { Track as TrackType } from '@/types/track'; -import { TrackHeader } from './TrackHeader'; +import { Button } from '@/components/ui/Button'; +import { Slider } from '@/components/ui/Slider'; import { cn } from '@/lib/utils/cn'; export interface TrackProps { @@ -40,6 +42,39 @@ export function Track({ }: TrackProps) { const canvasRef = React.useRef(null); const containerRef = React.useRef(null); + const [isEditingName, setIsEditingName] = React.useState(false); + const [nameInput, setNameInput] = React.useState(String(track.name || 'Untitled Track')); + const inputRef = React.useRef(null); + + const handleNameClick = () => { + setIsEditingName(true); + setNameInput(String(track.name || 'Untitled Track')); + }; + + const handleNameBlur = () => { + setIsEditingName(false); + if (nameInput.trim()) { + onNameChange(nameInput.trim()); + } else { + setNameInput(String(track.name || 'Untitled Track')); + } + }; + + const handleNameKeyDown = (e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + inputRef.current?.blur(); + } else if (e.key === 'Escape') { + setNameInput(String(track.name || 'Untitled Track')); + setIsEditingName(false); + } + }; + + React.useEffect(() => { + if (isEditingName && inputRef.current) { + inputRef.current.focus(); + inputRef.current.select(); + } + }, [isEditingName]); // Draw waveform React.useEffect(() => { @@ -123,59 +158,173 @@ export function Track({ onSeek(clickTime); }; - if (track.collapsed) { - return ( -
- -
- ); - } + const trackHeight = track.collapsed ? 48 : track.height; return (
- -
- {track.audioBuffer ? ( - e.stopPropagation()} + > + {/* Track Name & Collapse Toggle */} +
+ + +
- ) : ( -
- No audio loaded + +
+ {isEditingName ? ( + setNameInput(e.target.value)} + onBlur={handleNameBlur} + onKeyDown={handleNameKeyDown} + className="w-full px-2 py-1 text-sm font-medium bg-background border border-border rounded" + /> + ) : ( +
+ {String(track.name || 'Untitled Track')} +
+ )}
+ + +
+ + {/* Track Controls - Only show when not collapsed */} + {!track.collapsed && ( + <> + {/* Volume */} +
+
+ + + {Math.round(track.volume * 100)}% + +
+ +
+ + {/* Pan */} +
+
+ + + {track.pan === 0 + ? 'C' + : track.pan < 0 + ? `L${Math.round(Math.abs(track.pan) * 100)}` + : `R${Math.round(track.pan * 100)}`} + +
+ +
+ + {/* Solo / Mute Buttons */} +
+ + +
+ + )} +
+ + {/* Right: Waveform Area (Flexible Width) */} +
+ {!track.collapsed && ( + <> + {track.audioBuffer ? ( + + ) : ( +
+ No audio loaded +
+ )} + )}