refactor: single automation lane with parameter dropdown and fixed-height effects panel

Phase 9 Automation Improvements:
- Replaced multiple automation lanes with single lane + parameter selector
- Added dropdown in automation header to switch between parameters:
  - Track parameters: Volume, Pan
  - Effect parameters: Dynamically generated from effect chain
- Lanes are created on-demand when parameter is selected
- Effects panel now has fixed height (280px) with scroll
  - Panel no longer resizes when effects are expanded
  - Maintains consistent UI layout

Technical changes:
- Track.automation.selectedParameterId: Tracks current parameter
- AutomationHeader: Added parameter dropdown props
- AutomationLane: Passes parameter selection to header
- Track.tsx: Single lane rendering with IIFE for parameter list
- Effects panel: h-[280px] with flex layout and overflow-y-auto

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-11-18 18:49:44 +01:00
parent d2709b8fc2
commit eb445bfa3a
4 changed files with 114 additions and 21 deletions

View File

@@ -17,6 +17,10 @@ export interface AutomationHeaderProps {
onHeightChange?: (delta: number) => void;
className?: string;
formatter?: (value: number) => string;
// Parameter selection
availableParameters?: Array<{ id: string; name: string }>;
selectedParameterId?: string;
onParameterChange?: (parameterId: string) => void;
}
const MODE_LABELS: Record<AutomationMode, string> = {
@@ -44,6 +48,9 @@ export function AutomationHeader({
onHeightChange,
className,
formatter,
availableParameters,
selectedParameterId,
onParameterChange,
}: AutomationHeaderProps) {
const modes: AutomationMode[] = ['read', 'write', 'touch', 'latch'];
const currentModeIndex = modes.indexOf(mode);
@@ -74,10 +81,24 @@ export function AutomationHeader({
/>
)}
{/* Parameter name */}
<span className="text-xs font-medium text-foreground flex-1 min-w-0 truncate">
{parameterName}
</span>
{/* Parameter name / selector */}
{availableParameters && availableParameters.length > 1 ? (
<select
value={selectedParameterId}
onChange={(e) => onParameterChange?.(e.target.value)}
className="text-xs font-medium text-foreground flex-1 min-w-0 bg-background/50 border border-border/30 rounded px-1.5 py-0.5 hover:bg-background/80 focus:outline-none focus:ring-1 focus:ring-primary"
>
{availableParameters.map((param) => (
<option key={param.id} value={param.id}>
{param.name}
</option>
))}
</select>
) : (
<span className="text-xs font-medium text-foreground flex-1 min-w-0 truncate">
{parameterName}
</span>
)}
{/* Current value display */}
{currentValue !== undefined && (

View File

@@ -16,6 +16,10 @@ export interface AutomationLaneProps {
onUpdatePoint?: (pointId: string, updates: Partial<AutomationPointType>) => void;
onRemovePoint?: (pointId: string) => void;
className?: string;
// Parameter selection
availableParameters?: Array<{ id: string; name: string }>;
selectedParameterId?: string;
onParameterChange?: (parameterId: string) => void;
}
export function AutomationLane({
@@ -28,6 +32,9 @@ export function AutomationLane({
onUpdatePoint,
onRemovePoint,
className,
availableParameters,
selectedParameterId,
onParameterChange,
}: AutomationLaneProps) {
const canvasRef = React.useRef<HTMLCanvasElement>(null);
const containerRef = React.useRef<HTMLDivElement>(null);
@@ -302,6 +309,9 @@ export function AutomationLane({
onUpdateLane?.({ height: newHeight });
}}
formatter={lane.valueRange.formatter}
availableParameters={availableParameters}
selectedParameterId={selectedParameterId}
onParameterChange={onParameterChange}
/>
{/* Lane canvas area */}