feat: add keep alive

This commit is contained in:
2026-07-26 17:56:25 +02:00
parent a157b5f198
commit 31f645ebb2
21 changed files with 1207 additions and 263 deletions

View File

@ -3,13 +3,37 @@ import { invoke } from '@tauri-apps/api/core';
import { InputChannel, Channel, OutputChannel } from '../../../types/dsp408State';
import { DSP } from '../../../types/types';
import { NotificationType } from '../../../types/types';
import { useState } from 'react';
import Button from '../../Button/Button';
import ChannelGroup from '../ChannelGroup/ChannelGroup';
import { LinearGraph } from '../LinearGraph/LinearGraph';
import { useEffect, useRef, useState } from 'react';
import './GainSection.css';
const DESIGN_WIDTH = 90;
const DESIGN_HEIGHT = 380;
const PANEL_DESIGN_WIDTH = 90;
const PANEL_DESIGN_HEIGHT = 400;
function ChannelGroupScaled({ title, children }: { title: string; children: React.ReactNode }) {
const wrapperRef = useRef<HTMLDivElement>(null);
const [scale, setScale] = useState(1);
useEffect(() => {
const el = wrapperRef.current;
if (!el) return;
const observer = new ResizeObserver((entries) => {
const { height } = entries[0].contentRect;
if (height === 0) return;
setScale(height / PANEL_DESIGN_HEIGHT); // height is the only free axis here
});
observer.observe(el);
return () => observer.disconnect();
}, []);
return (
<div ref={wrapperRef} style={{ height: '100%', '--panel-scale': scale } as React.CSSProperties}>
<ChannelGroup title={title}>{children}</ChannelGroup>
</div>
);
}
function GainPanel({
dsp,
@ -78,6 +102,8 @@ function GainPanel({
const [muted, setMuted] = useState(false);
const [inverted, setInverted] = useState(false);
const viewportRef = useRef<HTMLDivElement>(null);
const [scale, setScale] = useState(1);
const toggleMute = async () => {
const next = !muted;
@ -91,9 +117,28 @@ function GainPanel({
await setInverse(next);
};
useEffect(() => {
const el = viewportRef.current;
if (!el) return;
const observer = new ResizeObserver((entries) => {
const { width, height } = entries[0].contentRect;
if (width === 0 || height === 0) return;
setScale(Math.min(width / PANEL_DESIGN_WIDTH, height / PANEL_DESIGN_HEIGHT));
});
observer.observe(el);
return () => observer.disconnect();
}, []);
return (
<div className="gain-panel-frame">
<div className="gain-panel" style={{ width: DESIGN_WIDTH, height: DESIGN_HEIGHT }}>
<div className="gain-panel-viewport" ref={viewportRef}>
<div
className="gain-panel"
style={{
width: PANEL_DESIGN_WIDTH,
height: PANEL_DESIGN_HEIGHT,
transform: `translate(-50%, -50%) scale(${scale})`,
}}
>
<div className="gain-panel__title">{title}</div>
<VerticalSlider
@ -104,12 +149,12 @@ function GainPanel({
switchStep={0.1}
unit=" dB"
onChange={setGain}
style={{ width: 70, height: 260, flex: '0 0 auto' }} // pin to design size, disable its own auto-shrink
/>
<Button variant="toggle" className={muted ? 'active-danger' : ''} onClick={toggleMute}>
{muted ? 'Muted' : 'Mute'}
</Button>
<Button
variant="toggle"
className={inverted ? 'active-warning' : ''}
@ -148,21 +193,48 @@ function GainSection({
];
return (
<div className="gain-section">
<div className="gain-panels">
<ChannelGroup title="Input">
{inputs.map(({ channel, title }) => (
<GainPanel key={title} dsp={dsp} notify={notify} channel={channel} title={title} />
))}
</ChannelGroup>
<ChannelGroup title="Output">
{outputs.map(({ channel, title }) => (
<GainPanel key={title} dsp={dsp} notify={notify} channel={channel} title={title} />
))}
</ChannelGroup>
<>
<div className="linear-graph-section">
<LinearGraph
xMin={20}
xMax={20000}
yMin={-24}
yMax={24}
yStep={6}
xLabel="Hz"
yLabel="dB"
draw={({ ctx, toPx }) => {
// placeholder flat 0 dB response — swap for real EQ/filter data.
// NOTE: this maps x linearly; for a real frequency response you'll
// likely want a log-frequency x-axis instead (see chat notes).
ctx.strokeStyle = '#4ade80';
ctx.lineWidth = 2;
const [x0, y0] = toPx(20, 0);
const [x1, y1] = toPx(20000, 0);
ctx.beginPath();
ctx.moveTo(x0, y0);
ctx.lineTo(x1, y1);
ctx.stroke();
}}
/>
</div>
</div>
<div className="gain-section">
<div className="gain-panels">
<ChannelGroup title="Input">
{inputs.map(({ channel, title }) => (
<GainPanel key={title} dsp={dsp} notify={notify} channel={channel} title={title} />
))}
</ChannelGroup>
<ChannelGroup title="Output">
{outputs.map(({ channel, title }) => (
<GainPanel key={title} dsp={dsp} notify={notify} channel={channel} title={title} />
))}
</ChannelGroup>
</div>
</div>
</>
);
}