Nicholai d3158e4c6a feat(timeline-mixer): WIP timeline and mixer components
work in progress implementation of:
- mixer with channel strips, faders, pan knobs, level meters
- timeline with ruler, playhead, sections, keyframe tracks
- pattern and progression pickers for drums/chords
- automation lanes and mute tracks
- loop bracket for loop region selection
- export modal placeholder

known issues:
- drum pattern changes don't update audio engine
- timeline keyframes not connected to scheduler
- some UI bugs remain

this is a checkpoint commit for further iteration
2026-01-20 18:22:10 -07:00

79 lines
2.1 KiB
TypeScript

'use client';
import { Drum, Music, Cloud, Volume2 } from 'lucide-react';
import { ChannelStrip } from './ChannelStrip';
import { LayerName, MeterLevels } from '@/types/audio';
interface MixerProps {
volumes: Record<LayerName | 'master', number>;
pans: Record<LayerName, number>;
muted: Record<LayerName, boolean>;
soloed: Record<LayerName, boolean>;
levels: MeterLevels;
onVolumeChange: (layer: LayerName | 'master', volume: number) => void;
onPanChange: (layer: LayerName, pan: number) => void;
onMuteToggle: (layer: LayerName) => void;
onSoloToggle: (layer: LayerName) => void;
}
const layerConfig: {
name: LayerName;
label: string;
icon: React.ReactNode;
}[] = [
{ name: 'drums', label: 'Drums', icon: <Drum className="h-3 w-3" /> },
{ name: 'chords', label: 'Chords', icon: <Music className="h-3 w-3" /> },
{ name: 'ambient', label: 'Ambient', icon: <Cloud className="h-3 w-3" /> },
];
export function Mixer({
volumes,
pans,
muted,
soloed,
levels,
onVolumeChange,
onPanChange,
onMuteToggle,
onSoloToggle,
}: MixerProps) {
return (
<div className="flex gap-2 overflow-x-auto pb-2">
{layerConfig.map(({ name, label, icon }) => (
<ChannelStrip
key={name}
name={label}
icon={icon}
volume={volumes[name]}
pan={pans[name]}
muted={muted[name]}
soloed={soloed[name]}
level={levels[name]}
onVolumeChange={(v) => onVolumeChange(name, v)}
onPanChange={(p) => onPanChange(name, p)}
onMuteToggle={() => onMuteToggle(name)}
onSoloToggle={() => onSoloToggle(name)}
/>
))}
<div className="w-px bg-border/50 mx-1" />
<ChannelStrip
name="Master"
icon={<Volume2 className="h-3 w-3" />}
volume={volumes.master}
pan={0}
muted={false}
soloed={false}
level={levels.master}
onVolumeChange={(v) => onVolumeChange('master', v)}
onPanChange={() => {}}
onMuteToggle={() => {}}
onSoloToggle={() => {}}
showPan={false}
showSolo={false}
/>
</div>
);
}