P4: Production hardening — reconnect, auto-scene-load, service cleanup

This commit is contained in:
2026-06-23 13:27:06 -04:00
parent d19e0ea7a8
commit 84cad11745
21 changed files with 2458 additions and 363 deletions
@@ -0,0 +1,98 @@
/**
* FxInsertSlot — a single FX insert on a channel strip.
*
* Shows:
* - Plugin name
* - Bypass toggle button
* - Wet/dry mix slider
* - Remove button
*/
import React, { useCallback } from 'react';
import type { FxInsert } from '../../hooks/useFxInserts';
import './FxInsertPanel.css';
interface FxInsertSlotProps {
insert: FxInsert;
accentColor: string;
onToggleBypass: (id: string, currentBypassed: boolean) => void;
onWetDryChange: (id: string, wetDry: number) => void;
onRemove: (id: string) => void;
}
const FxInsertSlot: React.FC<FxInsertSlotProps> = ({
insert,
accentColor,
onToggleBypass,
onWetDryChange,
onRemove,
}) => {
const handleBypass = useCallback(() => {
onToggleBypass(insert.id, insert.bypassed);
}, [insert.id, insert.bypassed, onToggleBypass]);
const handleWetDry = useCallback(
(e: React.ChangeEvent<HTMLInputElement>) => {
onWetDryChange(insert.id, parseFloat(e.target.value));
},
[insert.id, onWetDryChange],
);
const handleRemove = useCallback(() => {
onRemove(insert.id);
}, [insert.id, onRemove]);
const wetDryPercent = Math.round(insert.wet_dry_mix * 100);
return (
<div
className={`fx-insert-slot ${insert.bypassed ? 'bypassed' : ''}`}
style={
{
'--fx-accent': accentColor,
} as React.CSSProperties
}
>
{/* Header: plugin name + bypass + remove */}
<div className="fx-insert-header">
<span
className={`fx-insert-bypass-btn ${insert.bypassed ? 'active' : ''}`}
onClick={handleBypass}
title={insert.bypassed ? 'Enable insert' : 'Bypass insert'}
>
{insert.bypassed ? 'OFF' : 'ON'}
</span>
<span className="fx-insert-name" title={insert.plugin_name}>
{insert.plugin_name}
</span>
<button
className="fx-insert-remove"
onClick={handleRemove}
title="Remove insert"
>
×
</button>
</div>
{/* Wet/dry mix slider */}
{!insert.bypassed && (
<div className="fx-insert-wetdry">
<label className="fx-insert-wetdry-label">Mix</label>
<input
type="range"
min="0"
max="1"
step="0.01"
value={insert.wet_dry_mix}
onChange={handleWetDry}
className="fx-insert-wetdry-slider"
title={`Wet/Dry: ${wetDryPercent}%`}
/>
<span className="fx-insert-wetdry-readout">{wetDryPercent}%</span>
</div>
)}
</div>
);
};
export { FxInsertSlot };
export default FxInsertSlot;