diff --git a/components/tracks/TrackList.tsx b/components/tracks/TrackList.tsx index 931f247..f926307 100644 --- a/components/tracks/TrackList.tsx +++ b/components/tracks/TrackList.tsx @@ -1,7 +1,7 @@ 'use client'; import * as React from 'react'; -import { Plus, Upload, ChevronDown } from 'lucide-react'; +import { Plus, Upload, ChevronDown, ChevronRight, X } from 'lucide-react'; import { Button } from '@/components/ui/Button'; import { Track } from './Track'; import { TrackExtensions } from './TrackExtensions'; @@ -292,143 +292,206 @@ export function TrackList({ renderWaveformOnly={true} /> - {/* Automation Lane Overlay */} + {/* Automation Bar - Integrated at top */} {!track.collapsed && track.automation?.showAutomation && ( -
-
-
- {/* Header */} -
-
- Automation - - {track.automation.selectedParameterId || 'Volume'} - -
- -
- - {/* Automation Lane */} -
- {track.automation.lanes - .filter((lane) => lane.parameterId === track.automation.selectedParameterId) - .map((lane) => ( - { - const newPoint = createAutomationPoint(time, value); - const updatedLanes = track.automation.lanes.map((l) => - l.id === lane.id - ? { ...l, points: [...l.points, newPoint].sort((a, b) => a.time - b.time) } - : l - ); - onUpdateTrack(track.id, { - automation: { ...track.automation, lanes: updatedLanes }, - }); - }} - onUpdatePoint={(pointId, updates) => { - const updatedLanes = track.automation.lanes.map((l) => - l.id === lane.id - ? { - ...l, - points: l.points.map((p) => - p.id === pointId ? { ...p, ...updates } : p - ), - } - : l - ); - onUpdateTrack(track.id, { - automation: { ...track.automation, lanes: updatedLanes }, - }); - }} - onRemovePoint={(pointId) => { - const updatedLanes = track.automation.lanes.map((l) => - l.id === lane.id - ? { ...l, points: l.points.filter((p) => p.id !== pointId) } - : l - ); - onUpdateTrack(track.id, { - automation: { ...track.automation, lanes: updatedLanes }, - }); - }} - onUpdateLane={(updates) => { - const updatedLanes = track.automation.lanes.map((l) => - l.id === lane.id ? { ...l, ...updates } : l - ); - onUpdateTrack(track.id, { - automation: { ...track.automation, lanes: updatedLanes }, - }); - }} - onSeek={onSeek} - /> - ))} -
+
+ {/* Automation Header - Always visible */} +
{ + const currentLane = track.automation.lanes.find( + l => l.parameterId === track.automation.selectedParameterId + ); + if (currentLane) { + const updatedLanes = track.automation.lanes.map((l) => + l.id === currentLane.id ? { ...l, visible: !l.visible } : l + ); + onUpdateTrack(track.id, { + automation: { ...track.automation, lanes: updatedLanes }, + }); + } + }} + > +
+ {track.automation.lanes.find(l => l.parameterId === track.automation.selectedParameterId)?.visible ? ( + + ) : ( + + )} + Automation + + {track.automation.selectedParameterId || 'Volume'} +
+
+ + {/* Automation Lane Content - Collapsible */} + {track.automation.lanes + .filter((lane) => lane.parameterId === track.automation.selectedParameterId && lane.visible) + .map((lane) => ( +
+ { + const newPoint = createAutomationPoint(time, value); + const updatedLanes = track.automation.lanes.map((l) => + l.id === lane.id + ? { ...l, points: [...l.points, newPoint].sort((a, b) => a.time - b.time) } + : l + ); + onUpdateTrack(track.id, { + automation: { ...track.automation, lanes: updatedLanes }, + }); + }} + onUpdatePoint={(pointId, updates) => { + const updatedLanes = track.automation.lanes.map((l) => + l.id === lane.id + ? { + ...l, + points: l.points.map((p) => + p.id === pointId ? { ...p, ...updates } : p + ), + } + : l + ); + onUpdateTrack(track.id, { + automation: { ...track.automation, lanes: updatedLanes }, + }); + }} + onRemovePoint={(pointId) => { + const updatedLanes = track.automation.lanes.map((l) => + l.id === lane.id + ? { ...l, points: l.points.filter((p) => p.id !== pointId) } + : l + ); + onUpdateTrack(track.id, { + automation: { ...track.automation, lanes: updatedLanes }, + }); + }} + onUpdateLane={(updates) => { + const updatedLanes = track.automation.lanes.map((l) => + l.id === lane.id ? { ...l, ...updates } : l + ); + onUpdateTrack(track.id, { + automation: { ...track.automation, lanes: updatedLanes }, + }); + }} + onSeek={onSeek} + /> +
+ ))}
)} - {/* Effects Overlay */} + {/* Effects Bar - Integrated below automation or at top */} {!track.collapsed && track.showEffects && ( -
-
- { - const updatedChain = { - ...track.effectChain, - effects: track.effectChain.effects.map((e) => - e.id === effectId ? { ...e, enabled: !e.enabled } : e - ), - }; - onUpdateTrack(track.id, { effectChain: updatedChain }); +
l.parameterId === track.automation.selectedParameterId)?.visible + ? '164px' // 32px header + 132px automation lane + : track.automation?.showAutomation + ? '32px' // Just header + : '0px' + }} + > + {/* Effects Header - Always visible */} +
{ + onUpdateTrack(track.id, { effectsExpanded: !track.effectsExpanded }); + }} + > +
+ {track.effectsExpanded ? ( + + ) : ( + + )} + Effects + + ({track.effectChain.effects.length}) + +
+
+ + {/* Effects Content - Collapsible */} + {track.effectsExpanded && ( +
+ { + const updatedChain = { + ...track.effectChain, + effects: track.effectChain.effects.map((e) => + e.id === effectId ? { ...e, enabled: !e.enabled } : e + ), + }; + onUpdateTrack(track.id, { effectChain: updatedChain }); + }} + onRemoveEffect={(effectId) => { + const updatedChain = { + ...track.effectChain, + effects: track.effectChain.effects.filter((e) => e.id !== effectId), + }; + onUpdateTrack(track.id, { effectChain: updatedChain }); + }} + onUpdateEffect={(effectId, parameters) => { + const updatedChain = { + ...track.effectChain, + effects: track.effectChain.effects.map((e) => + e.id === effectId ? { ...e, parameters } : e + ), + }; + onUpdateTrack(track.id, { effectChain: updatedChain }); + }} + onAddEffect={(effectType) => { + const newEffect = createEffect( + effectType, + EFFECT_NAMES[effectType] + ); + const updatedChain = { + ...track.effectChain, + effects: [...track.effectChain.effects, newEffect], + }; + onUpdateTrack(track.id, { effectChain: updatedChain }); + }} + /> +
+ )}
)}
diff --git a/types/track.ts b/types/track.ts index a5bc952..d2460d8 100644 --- a/types/track.ts +++ b/types/track.ts @@ -34,6 +34,7 @@ export interface Track { collapsed: boolean; selected: boolean; showEffects: boolean; // Show/hide per-track effects panel + effectsExpanded?: boolean; // Whether effects bar is expanded (when showEffects is true) // Selection (for editing operations) selection: Selection | null;