feat: sidebar allows recall, rename and display custom names

This commit is contained in:
2026-07-28 12:39:13 +02:00
parent da322c9df1
commit 54fda168cc
9 changed files with 632 additions and 253 deletions

View File

@ -1,8 +1,6 @@
import { invoke } from '@tauri-apps/api/core';
import { useState } from 'react'; import { useState } from 'react';
import './App.css'; import './App.css';
import { DSP, DSPStatus, AppPage } from './types/types'; import { DSP, AppPage } from './types/types';
import TopBar from './components/Topbar/Topbar'; import TopBar from './components/Topbar/Topbar';
import DSPPage from './pages/DspPage/DspPage'; import DSPPage from './pages/DspPage/DspPage';
import AddDSPForm from './pages/AddDspPage/AddDspPage'; import AddDSPForm from './pages/AddDspPage/AddDspPage';
@ -10,8 +8,7 @@ import { useNotifications } from './hooks/useNotifications';
import Notifications from './components/Notifications/Notifications'; import Notifications from './components/Notifications/Notifications';
import CreditsPage from './pages/CreditsPage/CreditsPage'; import CreditsPage from './pages/CreditsPage/CreditsPage';
import HomePage from './pages/HomePage/HomePage'; import HomePage from './pages/HomePage/HomePage';
import { getDSP408State, connectDSP408, disconnectDSP408 } from './api/dsp408'; import { useDSP408 } from './hooks/useDSP408';
import { useDSPPolling } from './hooks/useDSPPolling';
function App() { function App() {
const [dsps, setDsps] = useState<DSP[]>([]); const [dsps, setDsps] = useState<DSP[]>([]);
@ -21,92 +18,14 @@ function App() {
const activeDsp = dsps.find((d) => d.id === selected) ?? null; const activeDsp = dsps.find((d) => d.id === selected) ?? null;
const connectedDspCount = dsps.filter((dsp) => dsp.status === 'connected').length; const connectedDspCount = dsps.filter((dsp) => dsp.status === 'connected').length;
useDSPPolling(dsps, selected, setDsps); const dsp408 = useDSP408(
dsps,
function setStatus(id: number, status: DSPStatus) { setDsps,
setDsps((prev) => prev.map((d) => (d.id === id ? { ...d, status } : d))); selected,
} setSelected,
setPage,
async function addDSP(dsp: Omit<DSP, 'id' | 'status' | 'state' | 'meters'>) { notify
try { );
const id = await invoke<number>('create_dsp408', {
ip: dsp.ip,
port: dsp.port,
deviceId: Number(dsp.deviceId),
});
const newDsp: DSP = {
id,
...dsp,
status: 'disconnected',
state: null,
meters: null,
};
setDsps((prev) => [...prev, newDsp]);
setSelected(id);
setPage('view-dsp');
notify('Added successfully', 'success', dsp.name);
} catch (err) {
notify(`Connection failed: ${String(err)}`, 'error', dsp.name);
}
}
async function removeDSP(dsp: DSP) {
try {
let id = dsp.id;
await invoke('remove_dsp408', {
id,
});
setDsps((prev) => {
const next = prev.filter((d) => d.id !== id);
if (selected === id) {
setSelected(next.length ? next[0].id : null);
}
return next;
});
notify('Removed successfully', 'success', dsp?.name);
} catch (err) {
notify(`Remove failed: ${String(err)}`, 'error', dsp?.name);
}
}
async function connectDSP(dsp: DSP) {
setStatus(dsp.id, 'connecting');
try {
await connectDSP408(dsp.id);
setStatus(dsp.id, 'connected');
const state = await getDSP408State(dsp.id);
setDsps((prev) => prev.map((d) => (d.id === dsp.id ? { ...d, state } : d)));
notify('Connected', 'success', dsp.name);
} catch (err) {
notify(`Connection failed: ${String(err)}`, 'error', dsp.name);
setStatus(dsp.id, 'disconnected');
}
}
async function disconnectDSP(dsp: DSP) {
try {
await disconnectDSP408(dsp.id);
setStatus(dsp.id, 'disconnected');
notify('Disconnected', 'success', dsp.name);
} catch (err) {
notify(`Disconnect failed: ${String(err)}`, 'error', dsp.name);
}
}
return ( return (
<div className="app"> <div className="app">
@ -128,7 +47,7 @@ function App() {
setPage('view-dsp'); setPage('view-dsp');
}} }}
onRemove={removeDSP} onRemove={dsp408.removeDSP}
onAddClick={() => { onAddClick={() => {
setPage('add-dsp'); setPage('add-dsp');
@ -139,13 +58,11 @@ function App() {
{page === 'credits' ? ( {page === 'credits' ? (
<CreditsPage /> <CreditsPage />
) : page === 'add-dsp' ? ( ) : page === 'add-dsp' ? (
<AddDSPForm onCancel={() => setPage('home')} onAdd={addDSP} /> <AddDSPForm onCancel={() => setPage('home')} onAdd={dsp408.addDSP} />
) : activeDsp ? ( ) : activeDsp ? (
<DSPPage <DSPPage
dsp={activeDsp} dsp={activeDsp}
onConnect={() => connectDSP(activeDsp)} dsp408={dsp408.forDSP(activeDsp)}
onDisconnect={() => disconnectDSP(activeDsp)}
notify={notify}
/> />
) : ( ) : (
<HomePage <HomePage

View File

@ -16,10 +16,16 @@ export const disconnectDSP408 = (id: number) => invoke('disconnect_dsp408', { id
export const getDSP408State = (id: number) => invoke<DSPState>('get_dsp_state', { id }); export const getDSP408State = (id: number) => invoke<DSPState>('get_dsp_state', { id });
export const recallPreset = (id: number, presetIndex: number) => export const recallPresetDSP408 = (id: number, presetIndex: number) =>
invoke<boolean>('recall_preset', { invoke<boolean>('recall_preset', {
id, id,
presetIndex, presetIndex,
}); });
export const DSP408SetCurrentPresetName = (id: number, name: string) =>
invoke<boolean>('set_current_preset_name', {
id,
name,
});
export const getDSP408MeterLevels = (id: number) => invoke<Meters>('get_meter_levels', { id }); export const getDSP408MeterLevels = (id: number) => invoke<Meters>('get_meter_levels', { id });

View File

@ -50,6 +50,7 @@ function AppCombobox({
onAppRename?: (name: string) => void; onAppRename?: (name: string) => void;
}) { }) {
const currentOption = appOptions.find((o) => o.id === selectedAppId) ?? appOptions[0]; const currentOption = appOptions.find((o) => o.id === selectedAppId) ?? appOptions[0];
const canRename = currentOption?.id !== 'f00';
const [open, setOpen] = useState(false); const [open, setOpen] = useState(false);
const [editing, setEditing] = useState(false); const [editing, setEditing] = useState(false);
@ -91,8 +92,12 @@ function AppCombobox({
function startEditing(e: React.MouseEvent) { function startEditing(e: React.MouseEvent) {
e.stopPropagation(); e.stopPropagation();
if (!canRename) return;
setEditing(true); setEditing(true);
setOpen(false); setOpen(false);
requestAnimationFrame(() => { requestAnimationFrame(() => {
inputRef.current?.focus(); inputRef.current?.focus();
inputRef.current?.select(); inputRef.current?.select();
@ -100,17 +105,30 @@ function AppCombobox({
} }
function commitRename() { function commitRename() {
if (!canRename) {
setInputValue(currentOption?.name ?? appName);
setEditing(false);
return;
}
const trimmed = inputValue.trim(); const trimmed = inputValue.trim();
setEditing(false); setEditing(false);
if (!trimmed) { if (!trimmed) {
setInputValue(currentOption?.name ?? appName); setInputValue(currentOption?.name ?? appName);
return; return;
} }
const matched = appOptions.find((o) => o.name === trimmed); const matched = appOptions.find((o) => o.name === trimmed);
if (matched) { if (matched) {
if (matched.id !== selectedAppId) onAppChange?.(matched.id); if (matched.id !== selectedAppId) {
onAppChange?.(matched.id);
}
return; return;
} }
if (trimmed !== currentOption?.name) { if (trimmed !== currentOption?.name) {
onAppRename?.(trimmed); onAppRename?.(trimmed);
} }
@ -153,7 +171,8 @@ function AppCombobox({
ref={inputRef} ref={inputRef}
className="sidebar-app-input" className="sidebar-app-input"
value={inputValue} value={inputValue}
onChange={(e) => setInputValue(e.target.value)} maxLength={14}
onChange={(e) => setInputValue(e.target.value.slice(0, 14))}
onBlur={commitRename} onBlur={commitRename}
onKeyDown={handleKeyDown} onKeyDown={handleKeyDown}
aria-label="Rename app" aria-label="Rename app"

View File

@ -57,7 +57,7 @@ function ConnectionPanel({
<div className="connection-panel__spec"> <div className="connection-panel__spec">
<dt>Device ID</dt> <dt>Device ID</dt>
<dd className="connection-panel__mono" title={dsp.deviceId || undefined}> <dd className="connection-panel__mono" title={String(dsp.deviceId) || undefined}>
{dsp.deviceId || '—'} {dsp.deviceId || '—'}
</dd> </dd>
</div> </div>

553
src/hooks/useDSP408.ts Normal file
View File

@ -0,0 +1,553 @@
// hooks/useDSP408.ts
import { useEffect, useRef } from 'react';
import { invoke } from '@tauri-apps/api/core';
import { DSP, DSPStatus, AppPage } from '../types/types';
import {
createDSP408,
removeDSP408,
connectDSP408,
disconnectDSP408,
getDSP408State,
recallPresetDSP408,
DSP408SetCurrentPresetName,
getDSP408MeterLevels,
} from '../api/dsp408';
type NotifyFunction = (
message: string,
type: 'success' | 'error',
dspName?: string
) => void;
type SetDsps = React.Dispatch<React.SetStateAction<DSP[]>>;
// ============================================================
// Helpers
// ============================================================
function updateDSP(
setDsps: SetDsps,
dspId: number,
updater: (dsp: DSP) => DSP
) {
setDsps((prev) =>
prev.map((dsp) => (dsp.id === dspId ? updater(dsp) : dsp))
);
}
export type DSP408Device = {
connect: () => Promise<void>;
disconnect: () => Promise<void>;
recallPreset: (index: number) => Promise<boolean>;
setCurrentPresetName: (name: string) => Promise<boolean>;
};
// ============================================================
// Polling
// ============================================================
function useDSPPolling(
dsps: DSP[],
selected: number | null,
setDsps: React.Dispatch<React.SetStateAction<DSP[]>>
) {
const dspsRef = useRef(dsps);
const selectedRef = useRef(selected);
useEffect(() => {
dspsRef.current = dsps;
}, [dsps]);
useEffect(() => {
selectedRef.current = selected;
}, [selected]);
useEffect(() => {
let stopped = false;
let activeTimeout: ReturnType<typeof setTimeout> | undefined;
let inactiveTimeout: ReturnType<typeof setTimeout> | undefined;
const pollDSP = async (dsp: DSP) => {
// Don't poll paused DSPs
if (dsp.pollingPaused) {
return;
}
try {
const meters = await getDSP408MeterLevels(dsp.id);
if (stopped) return;
setDsps((prev) => prev.map((d) => (d.id === dsp.id ? { ...d, meters } : d)));
} catch (err) {
if (!stopped) {
console.error(`Meter polling failed for ${dsp.name}:`, err);
}
}
};
const activeLoop = async () => {
if (stopped) return;
const activeId = selectedRef.current;
if (activeId !== null) {
const dsp = dspsRef.current.find(
(d) => d.id === activeId && d.status === 'connected' && !d.pollingPaused
);
if (dsp) {
await pollDSP(dsp);
}
}
if (!stopped) {
activeTimeout = setTimeout(activeLoop, 500);
}
};
const inactiveLoop = async () => {
if (stopped) return;
const activeId = selectedRef.current;
const inactiveDsps = dspsRef.current.filter(
(dsp) => dsp.status === 'connected' && dsp.id !== activeId && !dsp.pollingPaused
);
await Promise.all(inactiveDsps.map((dsp) => pollDSP(dsp)));
if (!stopped) {
inactiveTimeout = setTimeout(inactiveLoop, 5000);
}
};
// Start polling
activeLoop();
inactiveLoop();
return () => {
stopped = true;
if (activeTimeout !== undefined) {
clearTimeout(activeTimeout);
}
if (inactiveTimeout !== undefined) {
clearTimeout(inactiveTimeout);
}
};
}, [setDsps]);
}
// ============================================================
// Main hook
// ============================================================
export function useDSP408(
dsps: DSP[],
setDsps: SetDsps,
selected: number | null,
setSelected: React.Dispatch<React.SetStateAction<number | null>>,
setPage: React.Dispatch<React.SetStateAction<AppPage>>,
notify: NotifyFunction
) {
// ----------------------------------------------------------
// Polling control
// ----------------------------------------------------------
const setPollingPaused = (id: number, paused: boolean) => {
updateDSP(setDsps, id, (dsp) => ({
...dsp,
pollingPaused: paused,
}));
};
// Polling is part of the hook.
useDSPPolling(dsps, selected, setDsps);
// ----------------------------------------------------------
// Generic operation wrapper
// ----------------------------------------------------------
async function withPollingPaused<T>(
dsp: DSP,
operation: () => Promise<T>
): Promise<T> {
setPollingPaused(dsp.id, true);
try {
return await operation();
} finally {
setPollingPaused(dsp.id, false);
}
}
// ==========================================================
// Connection
// ==========================================================
async function connectDSP(dsp: DSP) {
setPollingPaused(dsp.id, true);
updateDSP(setDsps, dsp.id, (d) => ({
...d,
status: 'connecting',
}));
try {
await connectDSP408(dsp.id);
const state = await getDSP408State(dsp.id);
updateDSP(setDsps, dsp.id, (d) => ({
...d,
status: 'connected',
state,
}));
notify('Connected', 'success', dsp.name);
} catch (err) {
updateDSP(setDsps, dsp.id, (d) => ({
...d,
status: 'disconnected',
}));
notify(
`Connection failed: ${String(err)}`,
'error',
dsp.name
);
} finally {
setPollingPaused(dsp.id, false);
}
}
async function disconnectDSP(dsp: DSP) {
setPollingPaused(dsp.id, true);
try {
await disconnectDSP408(dsp.id);
updateDSP(setDsps, dsp.id, (d) => ({
...d,
status: 'disconnected',
}));
notify('Disconnected', 'success', dsp.name);
} catch (err) {
// If disconnect failed, it is probably still connected.
setPollingPaused(dsp.id, false);
notify(
`Disconnect failed: ${String(err)}`,
'error',
dsp.name
);
}
}
// ==========================================================
// Add / Remove
// ==========================================================
async function addDSP(
dsp: Omit<
DSP,
'id' | 'status' | 'state' | 'meters' | 'pollingPaused'
>
) {
try {
const id = await createDSP408(
dsp.ip,
dsp.port,
dsp.deviceId
);
const newDsp: DSP = {
id,
...dsp,
status: 'disconnected',
state: null,
meters: null,
pollingPaused: true,
};
setDsps((prev) => [...prev, newDsp]);
setSelected(id);
setPage('view-dsp');
notify('Added successfully', 'success', dsp.name);
} catch (err) {
notify(
`Connection failed: ${String(err)}`,
'error',
dsp.name
);
}
}
async function removeDSP(dsp: DSP) {
setPollingPaused(dsp.id, true);
try {
await removeDSP408(dsp.id);
setDsps((prev) => {
const next = prev.filter(
(d) => d.id !== dsp.id
);
if (selected === dsp.id) {
setSelected(
next.length ? next[0].id : null
);
}
return next;
});
notify(
'Removed successfully',
'success',
dsp.name
);
} catch (err) {
setPollingPaused(dsp.id, false);
notify(
`Remove failed: ${String(err)}`,
'error',
dsp.name
);
}
}
// ==========================================================
// Presets
// ==========================================================
async function recallPreset(
dsp: DSP,
presetIndex: number
) {
return withPollingPaused(dsp, async () => {
try {
const success = await recallPresetDSP408(
dsp.id,
presetIndex
);
if (!success) {
notify(
'Failed to recall preset',
'error',
dsp.name
);
return false;
}
updateDSP(setDsps, dsp.id, (d) => {
if (!d.state) return d;
return {
...d,
state: {
...d.state,
presets: {
...d.state.presets,
current_index: presetIndex - 1,
},
},
};
});
notify(
`Preset ${presetIndex} recalled`,
'success',
dsp.name
);
return true;
} catch (err) {
notify(
`Preset recall failed: ${String(err)}`,
'error',
dsp.name
);
return false;
}
});
}
async function setCurrentPresetName(
dsp: DSP,
name: string
) {
return withPollingPaused(dsp, async () => {
try {
const paddedName = name
.slice(0, 14)
.padEnd(14, ' ');
const success =
await DSP408SetCurrentPresetName(
dsp.id,
paddedName
);
if (!success) {
notify(
'Failed to rename preset',
'error',
dsp.name
);
return false;
}
updateDSP(setDsps, dsp.id, (d) => {
if (!d.state) return d;
const presets = d.state.presets;
const index = presets.current_index;
return {
...d,
state: {
...d.state,
presets: {
...presets,
names: presets.names.map(
(presetName, i) =>
i === index
? paddedName
: presetName
),
modified: presets.modified.map(
(modified, i) =>
i === index
? true
: modified
),
},
},
};
});
notify(
'Preset renamed',
'success',
dsp.name
);
return true;
} catch (err) {
notify(
`Preset rename failed: ${String(err)}`,
'error',
dsp.name
);
return false;
}
});
}
// ==========================================================
// Gain
// ==========================================================
async function setGain(
dsp: DSP,
channel: number,
gain: number
) {
return withPollingPaused(dsp, async () => {
try {
const success = await invoke<boolean>(
'set_gain',
{
id: dsp.id,
channel,
gain,
}
);
if (!success) {
notify(
'Failed to set gain',
'error',
dsp.name
);
return false;
}
updateDSP(setDsps, dsp.id, (d) => {
if (!d.state) return d;
return {
...d,
state: {
...d.state,
// Update gain state here
},
};
});
notify(
'Gain updated',
'success',
dsp.name
);
return true;
} catch (err) {
notify(
`Gain update failed: ${String(err)}`,
'error',
dsp.name
);
return false;
}
});
}
// ==========================================================
// Return API
// ==========================================================
function forDSP(dsp: DSP): DSP408Device {
return {
connect: () => connectDSP(dsp),
disconnect: () => disconnectDSP(dsp),
recallPreset: (index: number) => recallPreset(dsp, index),
setCurrentPresetName: (name: string) =>
setCurrentPresetName(dsp, name),
// setGain: (channel, gain) =>
// setGain(dsp, channel, gain),
};
}
return {
forDSP,
addDSP,
removeDSP,
connectDSP,
disconnectDSP,
recallPreset,
setCurrentPresetName,
setGain,
};
}

View File

@ -1,82 +0,0 @@
import { useEffect, useRef } from 'react';
import { DSP } from '../types/types';
import { getDSP408MeterLevels } from '../api/dsp408';
export function useDSPPolling(
dsps: DSP[],
selected: number | null,
setDsps: React.Dispatch<React.SetStateAction<DSP[]>>
) {
const dspsRef = useRef(dsps);
const selectedRef = useRef(selected);
// Keep refs up to date without restarting the polling loops
useEffect(() => {
dspsRef.current = dsps;
}, [dsps]);
useEffect(() => {
selectedRef.current = selected;
}, [selected]);
useEffect(() => {
let stopped = false;
const pollDSP = async (dsp: DSP) => {
try {
const meters = await getDSP408MeterLevels(dsp.id);
if (stopped) return;
setDsps((prev) => prev.map((d) => (d.id === dsp.id ? { ...d, meters } : d)));
} catch (err) {
if (!stopped) {
console.error(`Meter polling failed for ${dsp.name}:`, err);
}
}
};
const activeLoop = async () => {
if (stopped) return;
const activeId = selectedRef.current;
if (activeId !== null) {
const dsp = dspsRef.current.find((d) => d.id === activeId && d.status === 'connected');
if (dsp) {
await pollDSP(dsp);
}
}
if (!stopped) {
setTimeout(activeLoop, 500);
}
};
const inactiveLoop = async () => {
if (stopped) return;
const activeId = selectedRef.current;
const inactiveDsps = dspsRef.current.filter(
(dsp) => dsp.status === 'connected' && dsp.id !== activeId
);
// Poll inactive connected DSPs
await Promise.all(inactiveDsps.map((dsp) => pollDSP(dsp)));
if (!stopped) {
setTimeout(inactiveLoop, 5000);
}
};
// Start both loops
activeLoop();
inactiveLoop();
return () => {
stopped = true;
};
}, [setDsps]);
}

View File

@ -10,18 +10,22 @@ function AddDSPForm({
onAdd, onAdd,
}: { }: {
onCancel: () => void; onCancel: () => void;
onAdd: (d: Omit<DSP, 'status' | 'id' | 'state' | 'meters'>) => void; onAdd: (d: Omit<DSP, 'status' | 'id' | 'state' | 'meters' | 'pollingPaused'>) => void;
}) { }) {
const [form, setForm] = useState({ const [form, setForm] = useState({
name: '', name: '',
type: DSP_TYPES[0].name, type: DSP_TYPES[0].name,
ip: '192.168.1.100', ip: '192.168.1.100',
port: DSP_TYPES[0].defaultPort, port: DSP_TYPES[0].defaultPort,
deviceId: '1', deviceId: 1,
}); });
const canSubmit = const canSubmit =
form.name.trim().length > 0 && form.ip.trim().length > 0 && form.deviceId.length > 0; form.name.trim().length > 0 &&
form.ip.trim().length > 0 &&
Number.isInteger(form.deviceId) &&
form.deviceId >= 1 &&
form.deviceId <= 255;
function submit() { function submit() {
if (!canSubmit) return; if (!canSubmit) return;
@ -82,7 +86,7 @@ function AddDSPForm({
step={1} step={1}
placeholder="1" placeholder="1"
value={form.deviceId} value={form.deviceId}
onChange={(e) => setForm({ ...form, deviceId: e.target.value })} onChange={(e) => setForm({ ...form, deviceId: Number(e.target.value) })}
/> />
</div> </div>
</div> </div>

View File

@ -4,27 +4,25 @@ import Sidebar from '../../components/Sidebar/Sidebar';
import { InfoIcon, KnobGainIcon } from '../../assets/icons/AudioIcons'; import { InfoIcon, KnobGainIcon } from '../../assets/icons/AudioIcons';
import ConnectionPanel from '../../components/dsp408/ConnectionSection/ConnectionSection'; import ConnectionPanel from '../../components/dsp408/ConnectionSection/ConnectionSection';
import GainSection from '../../components/dsp408/GainSection/GainSection'; import GainSection from '../../components/dsp408/GainSection/GainSection';
import { NotificationType } from '../../types/types'; import { DSP408Device } from '../../hooks/useDSP408';
import './DspPage.css'; import './DspPage.css';
import GateSection from '../../components/dsp408/GateSection/GateSection'; import GateSection from '../../components/dsp408/GateSection/GateSection';
import { invoke } from '@tauri-apps/api/core';
import LoadingOverlay from '../../components/LoadingOverlay/LoadingOverlay'; import LoadingOverlay from '../../components/LoadingOverlay/LoadingOverlay';
import { InputChannel, OutputChannel } from '../../types/dsp408State';
function DSPPage({ function DSPPage({
dsp, dsp,
onConnect, dsp408,
onDisconnect,
notify,
}: { }: {
dsp: DSP; dsp: DSP;
onConnect: () => void; dsp408: DSP408Device;
onDisconnect: () => void;
notify: (message: string, type?: NotificationType, dspName?: string) => void;
}) { }) {
const [activeSidebar, setActiveSidebar] = useState('overview'); const [activeSidebar, setActiveSidebar] = useState('overview');
const [sidebarExpanded, setSidebarExpanded] = useState(false); const [sidebarExpanded, setSidebarExpanded] = useState(false);
const [selectedPreset, setSelectedPreset] = useState(dsp.state?.presets.current_index ?? 0); const [selectedPreset, setSelectedPreset] = useState(dsp.state?.presets.current_index ?? 0);
const [presetLoading, setPresetLoading] = useState(false); const [presetLoading, setPresetLoading] = useState(false);
const inputStates = dsp.state?.current_config.input_states;
const outputStates = dsp.state?.current_config.output_states;
const sidebarItems = [ const sidebarItems = [
{ {
id: 'overview', id: 'overview',
@ -68,62 +66,63 @@ function DSPPage({
}, },
{ {
id: 'ina', id: 'ina',
label: 'In A', label: `In A (${inputStates?.[InputChannel.InA]?.name})`,
icon: <KnobGainIcon />, icon: <KnobGainIcon />,
}, },
{ {
id: 'inb', id: 'inb',
label: 'In B', label: `In B (${inputStates?.[InputChannel.InB]?.name})`,
icon: <KnobGainIcon />, icon: <KnobGainIcon />,
}, },
{ {
id: 'inc', id: 'inc',
label: 'In C', label: `In C (${inputStates?.[InputChannel.InC]?.name})`,
icon: <KnobGainIcon />, icon: <KnobGainIcon />,
}, },
{ {
id: 'ind', id: 'ind',
label: 'In D (TESTTEST)', label: `In D (${inputStates?.[InputChannel.InD]?.name})`,
icon: <KnobGainIcon />, icon: <KnobGainIcon />,
}, },
{ {
id: 'out1', id: 'out1',
label: 'Out 1', label: `Out 1 (${outputStates?.[OutputChannel.Out1]?.name})`,
icon: <KnobGainIcon />, icon: <KnobGainIcon />,
}, },
{ {
id: 'out2', id: 'out2',
label: 'Out 2', label: `Out 2 (${outputStates?.[OutputChannel.Out2]?.name})`,
icon: <KnobGainIcon />, icon: <KnobGainIcon />,
}, },
{ {
id: 'out3', id: 'out3',
label: 'Out 3', label: `Out 3 (${outputStates?.[OutputChannel.Out3]?.name})`,
icon: <KnobGainIcon />, icon: <KnobGainIcon />,
}, },
{ {
id: 'out4', id: 'out4',
label: 'Out 4', label: `Out 4 (${outputStates?.[OutputChannel.Out4]?.name})`,
icon: <KnobGainIcon />, icon: <KnobGainIcon />,
}, },
{ {
id: 'out5', id: 'out5',
label: 'Out 5', label: `Out 5 (${outputStates?.[OutputChannel.Out5]?.name})`,
icon: <KnobGainIcon />, icon: <KnobGainIcon />,
}, },
{ {
id: 'out6', id: 'out6',
label: 'Out 6', label: `Out 6 (${outputStates?.[OutputChannel.Out6]?.name})`,
icon: <KnobGainIcon />, icon: <KnobGainIcon />,
}, },
{ {
id: 'out7', id: 'out7',
label: 'Out 7', label: `Out 7 (${outputStates?.[OutputChannel.Out7]?.name})`,
icon: <KnobGainIcon />, icon: <KnobGainIcon />,
}, },
{ {
id: 'out8', id: 'out8',
label: 'Out 8', label: `Out 8 (${outputStates?.[OutputChannel.Out8]?.name})`,
icon: <KnobGainIcon />, icon: <KnobGainIcon />,
}, },
{ {
@ -154,52 +153,14 @@ function DSPPage({
})), })),
]; ];
const recallPreset = async (presetIndex: number) => { // const { recallPreset, setCurrentPresetName } = useDSPPresets(dsp, setDsps, notify);
try {
const success = await invoke<boolean>('recall_preset', {
id: dsp.id,
presetIndex,
});
if (success) {
notify(`Preset ${presetIndex} recalled`, 'success', dsp.name);
} else {
notify('Failed to recall preset', 'error', dsp.name);
}
return success;
} catch (err) {
notify(`Preset recall failed: ${String(err)}`, 'error', dsp.name);
return false;
}
};
const setCurrentPresetName = async (name: string) => {
try {
const success = await invoke<boolean>('set_current_preset_name', {
id: dsp.id,
name,
});
if (success) {
notify(`Preset renamed`, 'success', dsp.name);
} else {
notify('Failed to rename preset', 'error', dsp.name);
}
return success;
} catch (err) {
notify(`Preset rename failed: ${String(err)}`, 'error', dsp.name);
return false;
}
};
const onAppChange = async (id: string) => { const onAppChange = async (id: string) => {
const presetIndex = presets.findIndex((preset) => preset.id === id); const presetIndex = presets.findIndex((preset) => preset.id === id);
if (presetIndex === -1) return; if (presetIndex === -1) return;
setPresetLoading(true); setPresetLoading(true);
const success = await recallPreset(presetIndex); const success = await dsp408.recallPreset(presetIndex);
if (success) { if (success) {
setSelectedPreset(presetIndex); setSelectedPreset(presetIndex);
} }
@ -209,13 +170,13 @@ function DSPPage({
const renderTab = () => { const renderTab = () => {
switch (activeSidebar) { switch (activeSidebar) {
case 'overview': case 'overview':
return <ConnectionPanel dsp={dsp} onConnect={onConnect} onDisconnect={onDisconnect} />; return <ConnectionPanel dsp={dsp} onConnect={dsp408.connect} onDisconnect={dsp408.disconnect} />;
case 'gain': // case 'gain':
return <GainSection dsp={dsp} notify={notify} />; // return <GainSection dsp={dsp} notify={notify} />;
case 'gate': // case 'gate':
return <GateSection dsp={dsp} notify={notify} />; // return <GateSection dsp={dsp} notify={notify} />;
default: default:
return null; return null;
@ -232,7 +193,7 @@ function DSPPage({
appOptions={presets} appOptions={presets}
selectedAppId={presets[selectedPreset]?.id} selectedAppId={presets[selectedPreset]?.id}
onAppChange={onAppChange} onAppChange={onAppChange}
onAppRename={setCurrentPresetName} onAppRename={dsp408.setCurrentPresetName}
expanded={sidebarExpanded} expanded={sidebarExpanded}
onToggle={() => setSidebarExpanded((v) => !v)} onToggle={() => setSidebarExpanded((v) => !v)}
/> />

View File

@ -8,10 +8,11 @@ export type DSP = {
type: string; type: string;
ip: string; ip: string;
port: number; port: number;
deviceId: string; deviceId: number;
status: DSPStatus; status: DSPStatus;
state: DSPState | null; state: DSPState | null;
meters: Meters | null; meters: Meters | null;
pollingPaused: boolean;
}; };
export const DSP_TYPES = [ export const DSP_TYPES = [