From 8ec350558146c50f27f2800a4b5e8ace49b68caa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastian=20Kr=C3=BCger?= Date: Wed, 19 Nov 2025 00:22:52 +0100 Subject: [PATCH] feat: enhance track controls and improve master fader layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Integrated all R/S/A/E buttons into TrackControls component - Removed duplicate button sections from Track component - Updated CircularKnob pan visualization to show no arc at center position - Arc now extends from center to value for directional indication - Moved MasterControls to dedicated right sidebar - Removed master controls from PlaybackControls footer - Optimized TrackControls spacing (gap-1.5, smaller buttons) - Cleaner separation between transport and master control sections 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- components/editor/AudioEditor.tsx | 40 +++++---- components/editor/PlaybackControls.tsx | 31 ------- components/tracks/Track.tsx | 97 +++++----------------- components/tracks/TrackControls.tsx | 110 +++++++++++++++++++++---- components/ui/CircularKnob.tsx | 50 ++++++++--- 5 files changed, 178 insertions(+), 150 deletions(-) diff --git a/components/editor/AudioEditor.tsx b/components/editor/AudioEditor.tsx index db63373..8bb5423 100644 --- a/components/editor/AudioEditor.tsx +++ b/components/editor/AudioEditor.tsx @@ -3,6 +3,7 @@ import * as React from 'react'; import { Music, Plus, Upload, Trash2, Settings, Download } from 'lucide-react'; import { PlaybackControls } from './PlaybackControls'; +import { MasterControls } from '@/components/controls/MasterControls'; import { ThemeToggle } from '@/components/layout/ThemeToggle'; import { CommandPalette } from '@/components/ui/CommandPalette'; import { GlobalSettingsDialog } from '@/components/settings/GlobalSettingsDialog'; @@ -1034,6 +1035,30 @@ export function AudioEditor() { /> + + {/* Right Sidebar - Master Controls */} + {/* Transport Controls */} @@ -1044,22 +1069,11 @@ export function AudioEditor() { currentTime={currentTime} duration={duration} volume={masterVolume} - pan={masterPan} onPlay={play} onPause={pause} onStop={stop} onSeek={seek} onVolumeChange={setMasterVolume} - onPanChange={setMasterPan} - onMuteToggle={() => { - if (isMasterMuted) { - setMasterVolume(0.8); - setIsMasterMuted(false); - } else { - setMasterVolume(0); - setIsMasterMuted(true); - } - }} currentTimeFormatted={formatDuration(currentTime)} durationFormatted={formatDuration(duration)} isRecording={recordingState.isRecording} @@ -1073,10 +1087,6 @@ export function AudioEditor() { onPunchOutTimeChange={setPunchOutTime} overdubEnabled={overdubEnabled} onOverdubEnabledChange={setOverdubEnabled} - masterPeakLevel={masterPeakLevel} - masterRmsLevel={masterRmsLevel} - masterIsClipping={masterIsClipping} - onResetClip={resetClipIndicator} /> diff --git a/components/editor/PlaybackControls.tsx b/components/editor/PlaybackControls.tsx index 5fd2ca0..167f744 100644 --- a/components/editor/PlaybackControls.tsx +++ b/components/editor/PlaybackControls.tsx @@ -3,7 +3,6 @@ import * as React from 'react'; import { Play, Pause, Square, SkipBack, Circle, AlignVerticalJustifyStart, AlignVerticalJustifyEnd, Layers } from 'lucide-react'; import { Button } from '@/components/ui/Button'; -import { MasterControls } from '@/components/controls/MasterControls'; import { cn } from '@/lib/utils/cn'; export interface PlaybackControlsProps { @@ -12,14 +11,11 @@ export interface PlaybackControlsProps { currentTime: number; duration: number; volume: number; - pan?: number; onPlay: () => void; onPause: () => void; onStop: () => void; onSeek: (time: number, autoPlay?: boolean) => void; onVolumeChange: (volume: number) => void; - onPanChange?: (pan: number) => void; - onMuteToggle?: () => void; disabled?: boolean; className?: string; currentTimeFormatted?: string; @@ -35,10 +31,6 @@ export interface PlaybackControlsProps { onPunchOutTimeChange?: (time: number) => void; overdubEnabled?: boolean; onOverdubEnabledChange?: (enabled: boolean) => void; - masterPeakLevel?: number; - masterRmsLevel?: number; - masterIsClipping?: boolean; - onResetClip?: () => void; } export function PlaybackControls({ @@ -47,14 +39,11 @@ export function PlaybackControls({ currentTime, duration, volume, - pan = 0, onPlay, onPause, onStop, onSeek, onVolumeChange, - onPanChange, - onMuteToggle, disabled = false, className, currentTimeFormatted, @@ -70,10 +59,6 @@ export function PlaybackControls({ onPunchOutTimeChange, overdubEnabled = false, onOverdubEnabledChange, - masterPeakLevel = 0, - masterRmsLevel = 0, - masterIsClipping = false, - onResetClip, }: PlaybackControlsProps) { const handlePlayPause = () => { if (isPlaying) { @@ -265,22 +250,6 @@ export function PlaybackControls({ )} - - {/* Master Controls */} - {onPanChange && onMuteToggle && ( - - )} ); diff --git a/components/tracks/Track.tsx b/components/tracks/Track.tsx index 0a3b107..5156aec 100644 --- a/components/tracks/Track.tsx +++ b/components/tracks/Track.tsx @@ -646,97 +646,40 @@ export function Track({ {/* Track Controls - Only show when not collapsed */} {!track.collapsed && (
- {/* Integrated Track Controls (Pan + Fader + Mute) */} + {/* Integrated Track Controls (Pan + Fader + Buttons) */} { + onUpdateTrack(track.id, { + automation: { + ...track.automation, + showAutomation: !track.automation?.showAutomation, + }, + }); + }} + onEffectsClick={() => { + onUpdateTrack(track.id, { + showEffects: !track.showEffects, + }); + }} onVolumeTouchStart={handleVolumeTouchStart} onVolumeTouchEnd={handleVolumeTouchEnd} onPanTouchStart={handlePanTouchStart} onPanTouchEnd={handlePanTouchEnd} /> - - {/* Inline Button Row - Below controls */} -
- {/* R/S/A inline row with icons */} -
- {/* Record Arm */} - {onToggleRecordEnable && ( - - )} - - {/* Solo Button */} - - - {/* Automation Toggle */} - - - {/* Effects Toggle */} - -
-
)} diff --git a/components/tracks/TrackControls.tsx b/components/tracks/TrackControls.tsx index e9a88d8..45e5fd8 100644 --- a/components/tracks/TrackControls.tsx +++ b/components/tracks/TrackControls.tsx @@ -1,7 +1,7 @@ 'use client'; import * as React from 'react'; -import { Volume2, VolumeX } from 'lucide-react'; +import { Circle, Headphones, Waveform, MoreHorizontal } from 'lucide-react'; import { CircularKnob } from '@/components/ui/CircularKnob'; import { TrackFader } from './TrackFader'; import { cn } from '@/lib/utils/cn'; @@ -12,9 +12,17 @@ export interface TrackControlsProps { peakLevel: number; rmsLevel: number; isMuted?: boolean; + isSolo?: boolean; + isRecordEnabled?: boolean; + showAutomation?: boolean; + isRecording?: boolean; onVolumeChange: (volume: number) => void; onPanChange: (pan: number) => void; onMuteToggle: () => void; + onSoloToggle?: () => void; + onRecordToggle?: () => void; + onAutomationToggle?: () => void; + onEffectsClick?: () => void; onVolumeTouchStart?: () => void; onVolumeTouchEnd?: () => void; onPanTouchStart?: () => void; @@ -28,9 +36,17 @@ export function TrackControls({ peakLevel, rmsLevel, isMuted = false, + isSolo = false, + isRecordEnabled = false, + showAutomation = false, + isRecording = false, onVolumeChange, onPanChange, onMuteToggle, + onSoloToggle, + onRecordToggle, + onAutomationToggle, + onEffectsClick, onVolumeTouchStart, onVolumeTouchEnd, onPanTouchStart, @@ -38,7 +54,7 @@ export function TrackControls({ className, }: TrackControlsProps) { return ( -
+
{/* Pan Control */} - {/* Mute Button */} - )} - title={isMuted ? 'Unmute' : 'Mute'} - > - M - + + {/* Solo Button */} + {onSoloToggle && ( + + )} + + {/* Mute Button */} + +
+ + {/* Control Buttons Row 2: A/E */} +
+ {/* Automation Toggle */} + {onAutomationToggle && ( + + )} + + {/* Effects Button */} + {onEffectsClick && ( + + )} +
); } diff --git a/components/ui/CircularKnob.tsx b/components/ui/CircularKnob.tsx index d85800d..bfa4cbf 100644 --- a/components/ui/CircularKnob.tsx +++ b/components/ui/CircularKnob.tsx @@ -115,6 +115,28 @@ export function CircularKnob({ ? `L${Math.abs(Math.round(value * 100))}` : `R${Math.round(value * 100)}`; + // Calculate arc parameters for center-based rendering + const isNearCenter = Math.abs(value) < 0.01; + const centerPercentage = 0.5; // Center position (50%) + + // Arc goes from center to current value + let arcStartPercentage: number; + let arcLength: number; + + if (value < -0.01) { + // Left side: arc from value to center + arcStartPercentage = percentage; + arcLength = centerPercentage - percentage; + } else if (value > 0.01) { + // Right side: arc from center to value + arcStartPercentage = centerPercentage; + arcLength = percentage - centerPercentage; + } else { + // Center: no arc + arcStartPercentage = centerPercentage; + arcLength = 0; + } + return (
{label && ( @@ -147,19 +169,21 @@ export function CircularKnob({ className="text-muted/30" /> - {/* Value arc */} - + {/* Value arc - only show when not centered */} + {!isNearCenter && ( + + )} {/* Knob body */}