fix: align automation lane with waveform, improve header layout

Automation lane improvements:
- Lane now aligns exactly with waveform width using two-column layout
- Added 180px left spacer to match track controls sidebar
- Playhead marker now aligns perfectly with waveform

Automation header improvements:
- Dropdown has fixed width (min-w-[120px] max-w-[200px]) instead of flex-1
- Eye icon (show/hide) is now positioned absolutely on the right
- Cleaner, more compact header layout

Visual consistency:
- Removed redundant border-b from AutomationLane (handled by parent)
- Automation lane and waveform now perfectly aligned vertically

🤖 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:53:08 +01:00
parent eb445bfa3a
commit 3cc4cb555a
3 changed files with 75 additions and 69 deletions

View File

@@ -69,7 +69,7 @@ export function AutomationHeader({
return (
<div
className={cn(
'flex items-center gap-2 px-2 py-1 bg-muted/50 border-b border-border/30 flex-shrink-0',
'relative flex items-center gap-2 px-2 py-1 bg-muted/50 border-b border-border/30 flex-shrink-0',
className
)}
>
@@ -86,7 +86,7 @@ export function AutomationHeader({
<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"
className="text-xs font-medium text-foreground w-auto min-w-[120px] max-w-[200px] 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}>
@@ -95,7 +95,7 @@ export function AutomationHeader({
))}
</select>
) : (
<span className="text-xs font-medium text-foreground flex-1 min-w-0 truncate">
<span className="text-xs font-medium text-foreground truncate">
{parameterName}
</span>
)}
@@ -142,13 +142,13 @@ export function AutomationHeader({
</div>
)}
{/* Show/hide toggle */}
{/* Show/hide toggle - Positioned absolutely on the right */}
<Button
variant="ghost"
size="icon-sm"
onClick={onToggleVisible}
title={visible ? 'Hide automation' : 'Show automation'}
className="h-5 w-5 flex-shrink-0"
className="absolute right-2 h-5 w-5 flex-shrink-0"
>
{visible ? (
<Eye className="h-3 w-3" />

View File

@@ -294,7 +294,7 @@ export function AutomationLane({
if (!lane.visible) return null;
return (
<div className={cn('flex flex-col border-b border-border/50', className)} style={{ height: lane.height + 30 }}>
<div className={cn('flex flex-col', className)} style={{ height: lane.height + 30 }}>
{/* Header */}
<AutomationHeader
parameterName={lane.parameterName}

View File

@@ -766,69 +766,75 @@ export function Track({
}
return selectedLane ? (
<div className="bg-background/30">
<AutomationLane
key={selectedLane.id}
lane={selectedLane}
duration={duration}
zoom={zoom}
currentTime={currentTime}
availableParameters={availableParameters}
selectedParameterId={selectedParameterId}
onParameterChange={(parameterId) => {
onUpdateTrack(track.id, {
automation: { ...track.automation, selectedParameterId: parameterId },
});
}}
onUpdateLane={(updates) => {
const updatedLanes = track.automation.lanes.map((l) =>
l.id === selectedLane.id ? { ...l, ...updates } : l
);
onUpdateTrack(track.id, {
automation: { ...track.automation, lanes: updatedLanes },
});
}}
onAddPoint={(time, value) => {
const newPoint = createAutomationPoint({
time,
value,
curve: 'linear',
});
const updatedLanes = track.automation.lanes.map((l) =>
l.id === selectedLane.id
? { ...l, points: [...l.points, newPoint] }
: l
);
onUpdateTrack(track.id, {
automation: { ...track.automation, lanes: updatedLanes },
});
}}
onUpdatePoint={(pointId, updates) => {
const updatedLanes = track.automation.lanes.map((l) =>
l.id === selectedLane.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 === selectedLane.id
? { ...l, points: l.points.filter((p) => p.id !== pointId) }
: l
);
onUpdateTrack(track.id, {
automation: { ...track.automation, lanes: updatedLanes },
});
}}
/>
<div className="flex border-b border-border">
{/* Left: Sidebar spacer to align with track controls */}
<div className="w-[180px] flex-shrink-0 bg-background/30" />
{/* Right: Automation lane matching waveform width */}
<div className="flex-1">
<AutomationLane
key={selectedLane.id}
lane={selectedLane}
duration={duration}
zoom={zoom}
currentTime={currentTime}
availableParameters={availableParameters}
selectedParameterId={selectedParameterId}
onParameterChange={(parameterId) => {
onUpdateTrack(track.id, {
automation: { ...track.automation, selectedParameterId: parameterId },
});
}}
onUpdateLane={(updates) => {
const updatedLanes = track.automation.lanes.map((l) =>
l.id === selectedLane.id ? { ...l, ...updates } : l
);
onUpdateTrack(track.id, {
automation: { ...track.automation, lanes: updatedLanes },
});
}}
onAddPoint={(time, value) => {
const newPoint = createAutomationPoint({
time,
value,
curve: 'linear',
});
const updatedLanes = track.automation.lanes.map((l) =>
l.id === selectedLane.id
? { ...l, points: [...l.points, newPoint] }
: l
);
onUpdateTrack(track.id, {
automation: { ...track.automation, lanes: updatedLanes },
});
}}
onUpdatePoint={(pointId, updates) => {
const updatedLanes = track.automation.lanes.map((l) =>
l.id === selectedLane.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 === selectedLane.id
? { ...l, points: l.points.filter((p) => p.id !== pointId) }
: l
);
onUpdateTrack(track.id, {
automation: { ...track.automation, lanes: updatedLanes },
});
}}
/>
</div>
</div>
) : null;
})()}