feat: restore automation controls with AutomationHeader component

- Import and use AutomationHeader component in automation bar
- Add parameter selection dropdown (Volume, Pan, Effect parameters)
- Add automation mode controls (Read, Write, Touch, Latch)
- Add lane height controls (increase/decrease buttons)
- Add show/hide toggle button
- Build available parameters dynamically from track effects
- All controls properly wired to update track automation state

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-19 13:13:12 +01:00
parent 0babc469cc
commit 14a9c6e163

View File

@@ -10,6 +10,7 @@ import type { Track as TrackType } from '@/types/track';
import { DEFAULT_TRACK_HEIGHT, COLLAPSED_TRACK_HEIGHT, MIN_TRACK_HEIGHT } from '@/types/track'; import { DEFAULT_TRACK_HEIGHT, COLLAPSED_TRACK_HEIGHT, MIN_TRACK_HEIGHT } from '@/types/track';
import { createEffect, type EffectType, EFFECT_NAMES } from '@/lib/audio/effects/chain'; import { createEffect, type EffectType, EFFECT_NAMES } from '@/lib/audio/effects/chain';
import { AutomationLane } from '@/components/automation/AutomationLane'; import { AutomationLane } from '@/components/automation/AutomationLane';
import { AutomationHeader } from '@/components/automation/AutomationHeader';
import type { AutomationPoint as AutomationPointType } from '@/types/automation'; import type { AutomationPoint as AutomationPointType } from '@/types/automation';
import { createAutomationPoint } from '@/lib/audio/automation/utils'; import { createAutomationPoint } from '@/lib/audio/automation/utils';
import { EffectDevice } from '@/components/effects/EffectDevice'; import { EffectDevice } from '@/components/effects/EffectDevice';
@@ -303,16 +304,44 @@ export function TrackList({
</div> </div>
{/* Automation Bar - Collapsible - Fixed height when expanded */} {/* Automation Bar - Collapsible - Fixed height when expanded */}
{!track.collapsed && ( {!track.collapsed && (() => {
const selectedParam = track.automation.selectedParameterId || 'volume';
const currentLane = track.automation.lanes.find(
l => l.parameterId === selectedParam
);
// Build available parameters list
const availableParameters: Array<{ id: string; name: string }> = [
{ id: 'volume', name: 'Volume' },
{ id: 'pan', name: 'Pan' },
];
// Add effect parameters
track.effectChain.effects.forEach((effect) => {
if (effect.parameters) {
Object.keys(effect.parameters).forEach((paramKey) => {
const parameterId = `effect.${effect.id}.${paramKey}`;
const paramName = `${effect.name} - ${paramKey.charAt(0).toUpperCase() + paramKey.slice(1)}`;
availableParameters.push({ id: parameterId, name: paramName });
});
}
});
return (
<div className="flex-shrink-0 bg-card/90 backdrop-blur-sm"> <div className="flex-shrink-0 bg-card/90 backdrop-blur-sm">
{/* Automation Header - Clickable to toggle */} <AutomationHeader
<div parameterName={currentLane?.parameterName || 'Volume'}
className="flex items-center gap-2 px-3 py-1.5 cursor-pointer hover:bg-accent/30 transition-colors" visible={currentLane?.visible ?? true}
onClick={() => { mode={currentLane?.mode || 'read'}
const selectedParam = track.automation.selectedParameterId || 'volume'; color={currentLane?.color}
const currentLane = track.automation.lanes.find( availableParameters={availableParameters}
l => l.parameterId === selectedParam selectedParameterId={selectedParam}
); onParameterChange={(parameterId) => {
onUpdateTrack(track.id, {
automation: { ...track.automation, selectedParameterId: parameterId },
});
}}
onToggleVisible={() => {
if (currentLane) { if (currentLane) {
const updatedLanes = track.automation.lanes.map((l) => const updatedLanes = track.automation.lanes.map((l) =>
l.id === currentLane.id ? { ...l, visible: !l.visible } : l l.id === currentLane.id ? { ...l, visible: !l.visible } : l
@@ -322,17 +351,28 @@ export function TrackList({
}); });
} }
}} }}
> onModeChange={(mode) => {
{track.automation.lanes.find(l => l.parameterId === (track.automation.selectedParameterId || 'volume'))?.visible ? ( if (currentLane) {
<ChevronDown className="h-3 w-3 text-muted-foreground" /> const updatedLanes = track.automation.lanes.map((l) =>
) : ( l.id === currentLane.id ? { ...l, mode } : l
<ChevronRight className="h-3 w-3 text-muted-foreground" /> );
)} onUpdateTrack(track.id, {
<span className="text-xs font-medium">Automation</span> automation: { ...track.automation, lanes: updatedLanes },
<span className="text-xs text-muted-foreground"> });
{track.automation.selectedParameterId || 'Volume'} }
</span> }}
</div> onHeightChange={(delta) => {
if (currentLane) {
const newHeight = Math.max(60, Math.min(200, currentLane.height + delta));
const updatedLanes = track.automation.lanes.map((l) =>
l.id === currentLane.id ? { ...l, height: newHeight } : l
);
onUpdateTrack(track.id, {
automation: { ...track.automation, lanes: updatedLanes },
});
}
}}
/>
{/* Automation Lane Content - Collapsible */} {/* Automation Lane Content - Collapsible */}
{track.automation.lanes {track.automation.lanes
@@ -391,7 +431,8 @@ export function TrackList({
/> />
))} ))}
</div> </div>
)} );
})()}
{/* Effects Bar - Collapsible - Fixed height when expanded */} {/* Effects Bar - Collapsible - Fixed height when expanded */}
{!track.collapsed && ( {!track.collapsed && (