feat: mixer VU meter, fader scaling, track name color

- Master strip: add fader placeholder + VU meter bar
- MixerStrip: items-end -> items-stretch so fader + VU fill height
- VU meter: bar height maps volumeDb, color green/amber/red
- Track name uses track.color instead of fixed text-zinc-400
This commit is contained in:
2026-07-29 09:54:18 +07:00
parent 2187440681
commit b00f0cff95
3 changed files with 33 additions and 16 deletions
+25 -14
View File
@@ -373,10 +373,12 @@ const VolumeKnob = ({
};
const MixerStrip = ({ track, index, onUpdateTrack }) => {
const dbLabel = (track.volumeDb == null || track.volumeDb <= -50) ? '-inf' : (track.volumeDb > 0 ? '+' : '') + (track.volumeDb || 0).toFixed(1) + 'dB';
const panLabel = track.pan === 0 ? 'C' : (track.pan < 0 ? 'L' + Math.round(Math.abs(track.pan)) : 'R' + Math.round(track.pan));
const isMuted = track.muted;
const isSoloed = track.solo;
const panDeg = (track.pan || 0) * 1.35;
const vol = track.volumeDb != null ? track.volumeDb : 0;
var pct = Math.max(0, Math.min(100, (vol + 60) / 72 * 100));
var vuColor = pct >= 80 ? '#ef4444' : pct >= 50 ? '#eab308' : '#22c55e';
var trackColor = track.color || '#06b6d4';
return React.createElement("div", {
className: "flex flex-col items-stretch w-14 shrink-0 bg-[#2b2b2b] border border-black/70 overflow-hidden rounded-sm"
},
@@ -397,24 +399,28 @@ const MixerStrip = ({ track, index, onUpdateTrack }) => {
className: "w-5 h-5 flex items-center justify-center rounded-sm text-[10px] font-mono font-bold border transition " + (isSoloed ? 'bg-yellow-400 text-black border-yellow-300' : 'bg-[#3a3a3a] text-zinc-400 border-black/60 hover:text-zinc-100')
}, "S")),
React.createElement("div", {
className: "flex-1 flex items-end justify-center gap-1 px-1 py-1 min-h-0"
className: "flex-1 flex items-stretch justify-center gap-1 px-1 py-1 min-h-0"
}, React.createElement("input", {
type: "range",
min: "-60",
max: "12",
step: "0.5",
value: track.volumeDb != null ? track.volumeDb : 0,
value: vol,
onChange: e => { if (onUpdateTrack) onUpdateTrack(track.id, { volumeDb: parseFloat(e.target.value) }); },
className: "w-3 bg-[#161616] rounded appearance-none cursor-pointer accent-orange-500 border border-black/60",
style: { writingMode: 'vertical-lr', direction: 'rtl' }
}), React.createElement("div", {
className: "w-2.5 rounded-sm bg-[#0d0d0d] border border-black/60"
})),
className: "w-2.5 rounded-sm relative overflow-hidden bg-[#0d0d0d] border border-black/60"
}, React.createElement("div", {
className: "absolute bottom-0 w-full transition-all duration-75",
style: { height: pct + '%', backgroundColor: vuColor }
}))),
React.createElement("div", {
className: "text-center text-[9px] font-mono font-bold py-0.5 " + ((track.volumeDb || 0) > 0 ? 'text-orange-400' : 'text-zinc-300') + " bg-[#1c1c1c] border-t border-black/50 shrink-0"
className: "text-center text-[9px] font-mono font-bold py-0.5 " + (vol > 0 ? 'text-orange-400' : 'text-zinc-300') + " bg-[#1c1c1c] border-t border-black/50 shrink-0"
}, dbLabel),
React.createElement("div", {
className: "text-[8px] font-mono text-zinc-400 truncate w-full text-center px-1 py-0.5 bg-[#222] border-t border-black/60 shrink-0"
className: "text-[8px] font-mono truncate w-full text-center px-1 py-0.5 bg-[#222] border-t border-black/60 shrink-0",
style: { color: trackColor }
}, track.name));
};
const WaveformLane = ({
@@ -17816,15 +17822,20 @@ const App = () => {
})))), /*#__PURE__*/React.createElement("div", {
className: "flex-1 flex overflow-x-auto p-1.5 gap-1.5 items-stretch"
}, /*#__PURE__*/React.createElement("div", {
className: "flex flex-col items-center justify-between w-14 shrink-0 bg-[#2b2b2b] border border-black/70 overflow-hidden rounded-sm"
className: "flex flex-col items-stretch w-14 shrink-0 bg-[#2b2b2b] border border-black/70 overflow-hidden rounded-sm"
}, /*#__PURE__*/React.createElement("div", {
className: "text-[9px] font-bold text-zinc-300 uppercase tracking-wider w-full text-center py-0.5 bg-[#222] border-b border-black/60"
className: "text-[9px] font-bold text-zinc-300 uppercase tracking-wider w-full text-center py-0.5 bg-[#222] border-b border-black/60 shrink-0"
}, "MASTER"), /*#__PURE__*/React.createElement("div", {
className: "flex-1 flex items-end justify-center w-full px-1 py-1"
className: "flex-1 flex items-stretch justify-center gap-1 px-1 py-1 min-h-0"
}, /*#__PURE__*/React.createElement("div", {
className: "w-2.5 rounded-sm bg-[#0d0d0d] border border-black/60"
})), /*#__PURE__*/React.createElement("div", {
className: "text-[8px] font-mono font-bold text-zinc-500 w-full text-center py-0.5 bg-[#222] border-t border-black/60"
className: "w-3 bg-[#161616] rounded border border-black/60"
}), /*#__PURE__*/React.createElement("div", {
className: "w-2.5 rounded-sm relative overflow-hidden bg-[#0d0d0d] border border-black/60"
}, /*#__PURE__*/React.createElement("div", {
className: "absolute bottom-0 w-full bg-white/40 transition-all duration-75",
style: { height: '50%' }
}))), /*#__PURE__*/React.createElement("div", {
className: "text-[8px] font-mono font-bold text-zinc-500 w-full text-center py-0.5 bg-[#222] border-t border-black/60 shrink-0"
}, "MAIN OUT")), activeTracks.length > 0 && /*#__PURE__*/React.createElement("div", {
className: "w-px bg-zinc-700 shrink-0 self-stretch mx-0.5"
}), activeTracks.map(function(track, idx) {