diff --git a/src/App.tsx b/src/App.tsx
index 4bbbab7..632346c 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -4,7 +4,7 @@ import { useState } from 'react';
import './styles/App.css';
import { DSP, DSPStatus, AppPage } from './types/types';
import TopBar from './components/topbar/Topbar';
-import DSPPage from './pages/DspPage';
+import DSPPage from './pages/DspPage/DspPage';
import AddDSPForm from './pages/AddDspPage';
import { useNotifications } from './hooks/useNotifications';
import Notifications from './components/Notifications/Notifications';
diff --git a/src/components/Button/Button.css b/src/components/Button/Button.css
index f851af5..5dd8892 100644
--- a/src/components/Button/Button.css
+++ b/src/components/Button/Button.css
@@ -78,3 +78,14 @@
border-color: var(--accent-down);
color: var(--text-primary);
}
+
+.btn-outline {
+ background: transparent;
+ border-color: var(--border-hairline);
+ color: var(--text-muted);
+}
+
+.btn-outline:hover:not(:disabled) {
+ color: var(--text-primary);
+ border-color: var(--text-muted);
+}
diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx
index f4a75f7..f9d2186 100644
--- a/src/components/Button/Button.tsx
+++ b/src/components/Button/Button.tsx
@@ -70,17 +70,22 @@ export default function Button({
}, [fontSize, children, W]);
const button = (
-
- );
+
+);
if (!scale) return button;
diff --git a/src/components/dsp408/ConnectionPanel.tsx b/src/components/dsp408/ConnectionPanel.tsx
deleted file mode 100644
index da6fb89..0000000
--- a/src/components/dsp408/ConnectionPanel.tsx
+++ /dev/null
@@ -1,69 +0,0 @@
-import { DSP } from '../../types/types';
-
-function ConnectionPanel({
- dsp,
- onConnect,
- onDisconnect,
-}: {
- dsp: DSP;
- onConnect: () => void;
- onDisconnect: () => void;
-}) {
- const statusLabel =
- dsp.status === 'connected'
- ? 'Connected'
- : dsp.status === 'connecting'
- ? 'Connecting…'
- : 'Disconnected';
-
- return (
-
-
-
-
{dsp.name}
-
{dsp.type}
-
-
-
-
- {statusLabel}
-
-
-
-
-
-
- IP address
- - {dsp.ip}
-
-
-
-
- Port
- - {dsp.port}
-
-
-
-
- Device ID
- - {dsp.deviceId || '—'}
-
-
-
-
- {dsp.status === 'connected' ? (
-
- ) : (
-
- )}
-
-
- );
-}
-
-export default ConnectionPanel;
diff --git a/src/components/dsp408/ConnectionSection/ConnectionSection.css b/src/components/dsp408/ConnectionSection/ConnectionSection.css
new file mode 100644
index 0000000..161c510
--- /dev/null
+++ b/src/components/dsp408/ConnectionSection/ConnectionSection.css
@@ -0,0 +1,62 @@
+.connection-panel-frame {
+ width: 33.333%;
+ height: 33.333%;
+ box-sizing: border-box;
+}
+
+.panel {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ flex-direction: column;
+ box-sizing: border-box;
+ overflow: hidden;
+ position: relative;
+}
+
+
+.panel-header {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ gap: 12px;
+ min-width: 0; /* required so children can actually shrink/ellipsize instead of forcing overflow */
+}
+
+.panel-header-text {
+ min-width: 0;
+ flex: 1 1 auto;
+}
+
+.panel-title {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.mono {
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.specs {
+ flex-shrink: 0;
+}
+
+.panel-actions {
+ position: absolute;
+ right: 16px;
+ bottom: 16px;
+ margin: 0; /* drop margin-top: auto, no longer needed */
+ padding: 0; /* drop padding-top: 16px, spacing now handled by right/bottom offsets */
+}
+
+.connection-section {
+ position: absolute; /* was: fixed — relative to .dsp-content now */
+ inset: 0; /* replaces top:0; right:0; bottom:0; left:0 */
+
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
\ No newline at end of file
diff --git a/src/components/dsp408/ConnectionSection/ConnectionSection.tsx b/src/components/dsp408/ConnectionSection/ConnectionSection.tsx
new file mode 100644
index 0000000..2816d26
--- /dev/null
+++ b/src/components/dsp408/ConnectionSection/ConnectionSection.tsx
@@ -0,0 +1,97 @@
+// ConnectionPanel.tsx
+import { DSP } from '../../../types/types';
+import Button from '../../Button/Button';
+import AutoScale from '../../AutoScale/AutoScale';
+import './ConnectionSection.css';
+
+const DESIGN_WIDTH = 340;
+const DESIGN_HEIGHT = 280;
+
+const BTN_W = 100;
+const BTN_H = 40;
+
+function ConnectionPanel({
+ dsp,
+ onConnect,
+ onDisconnect,
+}: {
+ dsp: DSP;
+ onConnect: () => void;
+ onDisconnect: () => void;
+}) {
+ const statusLabel =
+ dsp.status === 'connected'
+ ? 'Connected'
+ : dsp.status === 'connecting'
+ ? 'Connecting…'
+ : 'Disconnected';
+
+ return (
+
+
+
+
+
+
+
+ {dsp.name}
+
+
{dsp.type}
+
+
+
+
+ {statusLabel}
+
+
+
+
+
+
- IP address
+ -
+ {dsp.ip}
+
+
+
+
+
- Port
+ - {dsp.port}
+
+
+
+
- Device ID
+ -
+ {dsp.deviceId || '—'}
+
+
+
+
+
+ {dsp.status === 'connected' ? (
+
+ ) : (
+
+ )}
+
+
+
+
+
+ );
+}
+
+export default ConnectionPanel;
\ No newline at end of file
diff --git a/src/components/dsp408/GainSection/GainSection.css b/src/components/dsp408/GainSection/GainSection.css
index 7370cd4..178314e 100644
--- a/src/components/dsp408/GainSection/GainSection.css
+++ b/src/components/dsp408/GainSection/GainSection.css
@@ -1,10 +1,10 @@
:root {
- --gain-section-height: 50vh; /* your fixed % of screen height */
+ --gain-section-height: 40vh;
}
.gain-section {
- position: fixed;
- left: var(--sidebar-width-collapsed);
+ position: absolute; /* was: fixed — now relative to .dsp-content, which is the fixed+inset parent */
+ left: 0; /* was: var(--sidebar-width-collapsed) — .dsp-content already excludes the sidebar */
right: 0;
bottom: 0;
@@ -18,17 +18,13 @@
box-sizing: border-box;
padding: 0 var(--space-4);
- background: var(--bg-void);
-
- transition: left var(--dur-base) var(--ease-standard);
+ background: var(--bg-panel);
z-index: 20;
+
+ border-top: 1px solid var(--border-hairline);
}
-/* keep it clear of the sidebar when expanded, without lifting React state */
-.app-root:has(.sidebar-rail--expanded) .gain-section {
- left: var(--sidebar-width-expanded);
-}
.gain-panels {
display: flex;
flex-direction: row;
@@ -72,6 +68,7 @@
border-radius: var(--radius-md);
box-sizing: border-box;
padding: 8px;
+ background: var(--bg-void);
}
.gain-panel__title {
diff --git a/src/pages/DspPage/DspPage.css b/src/pages/DspPage/DspPage.css
new file mode 100644
index 0000000..c698ea5
--- /dev/null
+++ b/src/pages/DspPage/DspPage.css
@@ -0,0 +1,13 @@
+.dsp-content {
+ position: fixed;
+ left: var(--sidebar-width-collapsed);
+ right: 0;
+ top: 0;
+ bottom: 0;
+ transition: left var(--dur-base) var(--ease-standard);
+ top: var(--header-height, 0);
+}
+
+.app-root:has(.sidebar-rail--expanded) .dsp-content {
+ left: var(--sidebar-width-expanded);
+}
diff --git a/src/pages/DspPage.tsx b/src/pages/DspPage/DspPage.tsx
similarity index 71%
rename from src/pages/DspPage.tsx
rename to src/pages/DspPage/DspPage.tsx
index 71a3f7c..70967b0 100644
--- a/src/pages/DspPage.tsx
+++ b/src/pages/DspPage/DspPage.tsx
@@ -1,10 +1,11 @@
import { useState } from 'react';
-import { DSP } from '../types/types';
-import Sidebar from '../components/Sidebar/Sidebar';
-import { InfoIcon, SlidersIcon } from '../assets/icons/AudioIcons';
-import ConnectionPanel from '../components/dsp408/ConnectionPanel';
-import GainSection from '../components/dsp408/GainSection/GainSection';
-import { NotificationType } from '../types/types';
+import { DSP } from '../../types/types';
+import Sidebar from '../../components/Sidebar/Sidebar';
+import { InfoIcon, SlidersIcon } from '../../assets/icons/AudioIcons';
+import ConnectionPanel from '../../components/dsp408/ConnectionSection/ConnectionSection';
+import GainSection from '../../components/dsp408/GainSection/GainSection';
+import { NotificationType } from '../../types/types';
+import './DspPage.css'
function DSPPage({
dsp,
@@ -47,7 +48,7 @@ function DSPPage({
};
return (
-