From a5c528942477299c7680e6ebc3593c506f43888e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kr=C3=BCger?= Date: Tue, 18 Nov 2025 18:57:51 +0100 Subject: [PATCH] refactor: move automation controls to left sidebar, simplify layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Major automation UX improvements: - Moved all automation controls to left sidebar (180px, matching track controls) - Parameter dropdown selector - Automation mode button (R/W/T/L with color coding) - Height adjustment buttons (+/-) - Automation canvas now fills right side (matching waveform width exactly) - Removed AutomationHeader component (no longer needed) - Removed eye icon (automation visibility controlled by "A" button on track) Two-column layout consistency: - Left: 180px sidebar with all controls - Right: Flexible canvas area matching waveform width - Perfect vertical alignment between waveform and automation Simplified AutomationLane component: - Now only renders the canvas area with points - All controls handled in parent Track component - Cleaner, more maintainable code structure 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- components/automation/AutomationLane.tsx | 78 ++++++------------- components/tracks/Track.tsx | 97 +++++++++++++++++++++--- 2 files changed, 109 insertions(+), 66 deletions(-) diff --git a/components/automation/AutomationLane.tsx b/components/automation/AutomationLane.tsx index 8451024..20417b7 100644 --- a/components/automation/AutomationLane.tsx +++ b/components/automation/AutomationLane.tsx @@ -2,8 +2,7 @@ import * as React from 'react'; import { cn } from '@/lib/utils/cn'; -import type { AutomationLane as AutomationLaneType, AutomationPoint as AutomationPointType, AutomationMode } from '@/types/automation'; -import { AutomationHeader } from './AutomationHeader'; +import type { AutomationLane as AutomationLaneType, AutomationPoint as AutomationPointType } from '@/types/automation'; import { AutomationPoint } from './AutomationPoint'; export interface AutomationLaneProps { @@ -16,10 +15,6 @@ export interface AutomationLaneProps { onUpdatePoint?: (pointId: string, updates: Partial) => void; onRemovePoint?: (pointId: string) => void; className?: string; - // Parameter selection - availableParameters?: Array<{ id: string; name: string }>; - selectedParameterId?: string; - onParameterChange?: (parameterId: string) => void; } export function AutomationLane({ @@ -32,9 +27,6 @@ export function AutomationLane({ onUpdatePoint, onRemovePoint, className, - availableParameters, - selectedParameterId, - onParameterChange, }: AutomationLaneProps) { const canvasRef = React.useRef(null); const containerRef = React.useRef(null); @@ -294,54 +286,32 @@ export function AutomationLane({ if (!lane.visible) return null; return ( -
- {/* Header */} - onUpdateLane?.({ visible: !lane.visible })} - onModeChange={(mode: AutomationMode) => onUpdateLane?.({ mode })} - onHeightChange={(delta) => { - const newHeight = Math.max(60, Math.min(180, lane.height + delta)); - onUpdateLane?.({ height: newHeight }); - }} - formatter={lane.valueRange.formatter} - availableParameters={availableParameters} - selectedParameterId={selectedParameterId} - onParameterChange={onParameterChange} +
+ - {/* Lane canvas area */} -
- ( + - - {/* Automation points */} - {lane.points.map((point) => ( - - ))} -
+ ))}
); } diff --git a/components/tracks/Track.tsx b/components/tracks/Track.tsx index c745ba2..536ca1d 100644 --- a/components/tracks/Track.tsx +++ b/components/tracks/Track.tsx @@ -1,7 +1,7 @@ 'use client'; import * as React from 'react'; -import { Volume2, VolumeX, Headphones, Trash2, ChevronDown, ChevronRight, UnfoldHorizontal, Upload, Mic, Gauge, Circle, Sparkles } from 'lucide-react'; +import { Volume2, VolumeX, Headphones, Trash2, ChevronDown, ChevronRight, ChevronUp, UnfoldHorizontal, Upload, Mic, Gauge, Circle, Sparkles } from 'lucide-react'; import type { Track as TrackType } from '@/types/track'; import { COLLAPSED_TRACK_HEIGHT, MIN_TRACK_HEIGHT, MAX_TRACK_HEIGHT } from '@/types/track'; import { Button } from '@/components/ui/Button'; @@ -765,26 +765,99 @@ export function Track({ } } + const modes: Array<{ value: string; label: string; color: string }> = [ + { value: 'read', label: 'R', color: 'text-muted-foreground' }, + { value: 'write', label: 'W', color: 'text-red-500' }, + { value: 'touch', label: 'T', color: 'text-yellow-500' }, + { value: 'latch', label: 'L', color: 'text-orange-500' }, + ]; + const currentModeIndex = modes.findIndex(m => m.value === selectedLane?.mode); + return selectedLane ? (
- {/* Left: Sidebar spacer to align with track controls */} -
+ {/* Left: Automation Controls (matching track controls width) */} +
+ {/* Parameter selector dropdown */} + - {/* Right: Automation lane matching waveform width */} -
+ {/* Automation mode cycle button */} + + + {/* Height controls */} +
+ + +
+
+ + {/* Right: Automation Lane Canvas (matching waveform width) */} +
{ - onUpdateTrack(track.id, { - automation: { ...track.automation, selectedParameterId: parameterId }, - }); - }} onUpdateLane={(updates) => { const updatedLanes = track.automation.lanes.map((l) => l.id === selectedLane.id ? { ...l, ...updates } : l