diff --git a/src/App.tsx b/src/App.tsx
index 33e4110..36bd543 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -5,11 +5,11 @@ import './styles/App.css';
import { DSP, DSPStatus, AppPage } from './types/types';
import TopBar from './components/Topbar/Topbar';
import DSPPage from './pages/DspPage/DspPage';
-import AddDSPForm from './pages/AddDspPage';
+import AddDSPForm from './pages/AddDspPage/AddDspPage';
import { useNotifications } from './hooks/useNotifications';
import Notifications from './components/Notifications/Notifications';
import { DSPState } from './types/dsp408State';
-import CreditsPage from './pages/CreditsPage';
+import CreditsPage from './pages/CreditsPage/CreditsPage';
import HomePage from './pages/HomePage/HomePage';
function App() {
diff --git a/src/components/Button/Button.css b/src/components/Button/Button.css
index d9daa3a..c7a59c9 100644
--- a/src/components/Button/Button.css
+++ b/src/components/Button/Button.css
@@ -1,47 +1,36 @@
.btn {
- width: 100%;
- height: 100%;
-
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
+ min-height: var(--card-control-height);
+
padding: 0 var(--space-4);
+ border: var(--border-width) solid transparent;
border-radius: var(--radius-sm);
font-family: var(--font-ui);
- font-size: 13px;
- font-weight: 500;
+ font-size: var(--font-md);
+ font-weight: var(--font-weight-medium);
cursor: pointer;
- border: 1px solid transparent;
}
.btn:disabled {
- opacity: 0.45;
+ opacity: var(--disabled-opacity);
cursor: not-allowed;
}
.btn-primary {
background: var(--accent-brand);
- color: #0d0f12;
-}
-
-.btn-ghost {
- background: transparent;
- border-color: var(--border-hairline);
- color: var(--text-muted);
-}
-
-.btn-toggle {
- background: transparent;
- border-color: var(--border-hairline);
- color: var(--text-muted);
+ color: var(--bg-void);
}
+.btn-ghost,
+.btn-toggle,
.btn-outline {
background: transparent;
border-color: var(--border-hairline);
diff --git a/src/components/Card/Card.css b/src/components/Card/Card.css
index 51ea730..034ba6d 100644
--- a/src/components/Card/Card.css
+++ b/src/components/Card/Card.css
@@ -1,8 +1,13 @@
.card {
+ width: min(100%, var(--card-width));
+
background: var(--bg-panel);
+
border: var(--border-width) solid var(--border-hairline);
border-radius: var(--radius-lg);
+
box-shadow: var(--shadow-md);
+
padding: var(--space-6);
color: var(--text-primary);
@@ -12,5 +17,6 @@
display: flex;
flex-direction: column;
- width: clamp(280px, 33.333%, 480px);
+ min-width: 0;
+ box-sizing: border-box;
}
diff --git a/src/components/Sidebar/Sidebar.css b/src/components/Sidebar/Sidebar.css
index 3808bd5..975734c 100644
--- a/src/components/Sidebar/Sidebar.css
+++ b/src/components/Sidebar/Sidebar.css
@@ -132,7 +132,7 @@
align-items: center;
font-size: var(--font-size-sm, 13px);
- font-weight: 600;
+ font-weight: var(--font-weight-semibold);
color: var(--text-primary);
white-space: nowrap;
diff --git a/src/components/dsp408/ChannelGroup/ChannelGroup.css b/src/components/dsp408/ChannelGroup/ChannelGroup.css
index 2df995f..a12cb6c 100644
--- a/src/components/dsp408/ChannelGroup/ChannelGroup.css
+++ b/src/components/dsp408/ChannelGroup/ChannelGroup.css
@@ -27,7 +27,7 @@
font-family: var(--font-ui);
font-size: var(--tab-close-font);
- font-weight: 600;
+ font-weight: var(--font-weight-semibold);
color: var(--text-muted);
white-space: nowrap;
diff --git a/src/components/dsp408/ConnectionSection/ConnectionSection.css b/src/components/dsp408/ConnectionSection/ConnectionSection.css
index 687e444..cde4110 100644
--- a/src/components/dsp408/ConnectionSection/ConnectionSection.css
+++ b/src/components/dsp408/ConnectionSection/ConnectionSection.css
@@ -1,4 +1,4 @@
-.connection-panel-frame {
+/* .connection-panel-frame {
width: 33.333%;
height: 33.333%;
box-sizing: border-box;
@@ -27,7 +27,7 @@
align-items: flex-start;
justify-content: space-between;
gap: 12px;
- min-width: 0; /* required so children can actually shrink/ellipsize instead of forcing overflow */
+ min-width: 0;
}
.panel-header-text {
@@ -41,7 +41,7 @@
text-overflow: ellipsis;
margin: 0;
font-size: 18px;
- font-weight: 600;
+ font-weight: var(--font-weight-semibold);
}
.mono {
@@ -54,19 +54,11 @@
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 */
+ position: absolute;
+ inset: 0;
display: flex;
justify-content: center;
align-items: center;
-}
+} */
diff --git a/src/components/dsp408/GainSection/GainSection.css b/src/components/dsp408/GainSection/GainSection.css
index ef15ee5..b81bb87 100644
--- a/src/components/dsp408/GainSection/GainSection.css
+++ b/src/components/dsp408/GainSection/GainSection.css
@@ -74,7 +74,7 @@
font-family: var(--font-ui);
font-size: 12px;
- font-weight: 600;
+ font-weight: var(--font-weight-semibold);
padding-bottom: 6px;
diff --git a/src/components/dsp408/GainSection/GainSection.tsx b/src/components/dsp408/GainSection/GainSection.tsx
index 378e940..faa10db 100644
--- a/src/components/dsp408/GainSection/GainSection.tsx
+++ b/src/components/dsp408/GainSection/GainSection.tsx
@@ -92,15 +92,7 @@ function GainPanel({
};
return (
-
+
{title}
diff --git a/src/components/dsp408/GateSection/GateSection.css b/src/components/dsp408/GateSection/GateSection.css
new file mode 100644
index 0000000..b880108
--- /dev/null
+++ b/src/components/dsp408/GateSection/GateSection.css
@@ -0,0 +1,118 @@
+.gate-section {
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 0;
+
+ height: var(--gain-section-height);
+ min-height: 0;
+
+ display: flex;
+ align-items: flex-end;
+ justify-content: center;
+
+ box-sizing: border-box;
+ padding: 0 var(--space-4);
+
+ background: var(--bg-panel);
+
+ z-index: 20;
+
+ border-top: 1px solid var(--border-hairline);
+}
+
+.gate-panels {
+ display: flex;
+ flex-direction: row;
+ align-items: stretch;
+ justify-content: safe center;
+ gap: var(--space-4);
+ width: 100%;
+ height: 100%;
+ min-width: 0;
+ min-height: 0;
+ overflow-x: auto;
+ overflow-y: hidden;
+}
+
+.gate-panel-frame {
+ flex: 0 0 auto;
+ height: calc(100% - 8px);
+ aspect-ratio: var(--panel-w) / var(--panel-h);
+ margin: 4px 0;
+ box-sizing: border-box;
+}
+
+.gate-panels::-webkit-scrollbar {
+ height: 6px;
+}
+.gate-panels::-webkit-scrollbar-thumb {
+ background: var(--border-hairline);
+ border-radius: var(--radius-pill);
+}
+.gate-panels::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+.gate-panel {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: flex-start;
+ border: 1px solid var(--border-hairline);
+ border-radius: var(--radius-md);
+ box-sizing: border-box;
+ padding: 8px;
+ background: var(--bg-void);
+}
+
+.gate-panel__title {
+ width: 100%;
+
+ text-align: center;
+
+ font-family: var(--font-ui);
+ font-size: 12px;
+ font-weight: var(--font-weight-semibold);
+
+ padding-bottom: 6px;
+
+ border-bottom: 1px solid var(--border-hairline);
+}
+
+.gate-panel__sliders {
+ display: flex;
+ flex-direction: row;
+ align-items: stretch;
+ justify-content: center;
+ gap: var(--space-2);
+ width: 100%;
+ flex: 1;
+ min-height: 0;
+}
+
+.gate-panel__slider-col {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ flex: 1 1 0;
+ min-width: 0;
+}
+
+.gate-panel__slider-label {
+ font-family: var(--font-ui);
+ font-size: 10px;
+ font-weight: var(--font-weight-medium);
+ text-align: center;
+ padding-top: 4px;
+ color: var(--text-secondary);
+}
+
+/* GateSection.css */
+.gate-panels .channel-group__items {
+ display: grid;
+ grid-template-columns: repeat(2, auto);
+ grid-template-rows: repeat(2, 1fr);
+ gap: var(--space-2);
+ align-items: stretch;
+}
diff --git a/src/components/topbar/Topbar.css b/src/components/topbar/Topbar.css
index 42e05b9..725d353 100644
--- a/src/components/topbar/Topbar.css
+++ b/src/components/topbar/Topbar.css
@@ -7,7 +7,7 @@
-webkit-app-region: drag;
padding-inline: var(--space-3);
gap: var(--space-3);
- box-sizing: border-box
+ box-sizing: border-box;
}
.logo-slot {
@@ -37,7 +37,6 @@
-webkit-app-region: no-drag;
border-left: 1px solid var(--border-hairline);
-
scrollbar-width: thin;
scrollbar-color: var(--border-hairline) transparent;
scroll-behavior: smooth;
@@ -114,7 +113,7 @@
color: var(--text-muted);
font-family: var(--font-ui);
- font-weight: 500;
+ font-weight: var(--font-weight-medium);
flex-shrink: 0;
diff --git a/src/pages/AddDspPage/AddDspPage.css b/src/pages/AddDspPage/AddDspPage.css
new file mode 100644
index 0000000..9d60079
--- /dev/null
+++ b/src/pages/AddDspPage/AddDspPage.css
@@ -0,0 +1,69 @@
+.add-dsp-form__header {
+ margin-bottom: var(--space-5);
+}
+
+.add-dsp-form__title {
+ margin: 0;
+ font-size: var(--font-lg);
+ font-weight: var(--font-weight-semibold);
+ color: var(--text-primary);
+}
+
+.add-dsp-form__hint {
+ margin: var(--space-2) 0 0;
+ font-size: var(--font-sm);
+ color: var(--text-muted);
+}
+
+.add-dsp-form__field {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-2);
+ margin-bottom: var(--space-4);
+ flex: 1;
+ min-width: 0;
+}
+
+.add-dsp-form__row {
+ display: flex;
+ gap: var(--space-3);
+}
+
+.add-dsp-form__field label {
+ font-size: var(--font-sm);
+ color: var(--text-muted);
+}
+
+.add-dsp-form__field input,
+.add-dsp-form__field select {
+ height: var(--card-control-height);
+ padding: 0 var(--space-3);
+
+ background: var(--bg-panel);
+
+ border: var(--border-width) solid var(--border-hairline);
+ border-radius: var(--radius-sm);
+
+ color: var(--text-primary);
+ font-family: var(--font-ui);
+ font-size: var(--font-sm);
+
+ outline: none;
+ transition: border-color var(--dur-base) var(--ease-standard);
+}
+
+.add-dsp-form__field input.mono {
+ font-family: var(--font-mono);
+}
+
+.add-dsp-form__field input:focus,
+.add-dsp-form__field select:focus {
+ border-color: var(--accent-brand);
+}
+
+.add-dsp-form__actions {
+ display: flex;
+ justify-content: flex-end;
+ gap: var(--space-3);
+ margin-top: var(--space-2);
+}
diff --git a/src/pages/AddDspPage.tsx b/src/pages/AddDspPage/AddDspPage.tsx
similarity index 77%
rename from src/pages/AddDspPage.tsx
rename to src/pages/AddDspPage/AddDspPage.tsx
index 516a37f..afa0cb7 100644
--- a/src/pages/AddDspPage.tsx
+++ b/src/pages/AddDspPage/AddDspPage.tsx
@@ -1,6 +1,9 @@
import { useState } from 'react';
-import { DSP, DSP_TYPES } from '../types/types';
-import Button from '../components/Button/Button';
+import { DSP, DSP_TYPES } from '../../types/types';
+import Button from '../../components/Button/Button';
+import Card from '../../components/Card/Card';
+
+import './AddDspPage.css';
function AddDSPForm({
onCancel,
@@ -28,13 +31,13 @@ function AddDSPForm({
}
return (
-
-
-
Add DSP
-
Register a new device to control from this app.
+
+
+
Add DSP
+
Register a new device to control from this app.
-
+
-
-
+
+
-
+
-
-
+
+
-
+
-
+
@@ -116,7 +119,7 @@ function AddDSPForm({
Add DSP
-
+
);
}
diff --git a/src/pages/CreditsPage.tsx b/src/pages/CreditsPage.tsx
deleted file mode 100644
index 2ee3ec8..0000000
--- a/src/pages/CreditsPage.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-function CreditsPage() {
- return (
-
-
-
-
Credits
-
Thank you to everyone supporting the project
-
-
-
-
-
-
- Developer
- - Your Name
-
-
-
-
- Design
- - Open Source Community
-
-
-
-
- Libraries
- - React · Tauri · TypeScript
-
-
-
-
-
-
-
- );
-}
-
-export default CreditsPage;
diff --git a/src/pages/CreditsPage/CreditsPage.css b/src/pages/CreditsPage/CreditsPage.css
new file mode 100644
index 0000000..0b34e92
--- /dev/null
+++ b/src/pages/CreditsPage/CreditsPage.css
@@ -0,0 +1,26 @@
+.credits-page__header {
+ margin-bottom: var(--space-5);
+}
+
+.credits-page__title {
+ margin: 0;
+
+ font-size: var(--font-2xl);
+ font-weight: var(--font-weight-semibold);
+
+ color: var(--text-primary);
+}
+
+.credits-page__subtitle {
+ margin: var(--space-2) 0 0;
+
+ font-size: var(--font-sm);
+ color: var(--text-muted);
+}
+
+.credits-page__actions {
+ display: flex;
+ justify-content: flex-end;
+
+ margin-top: var(--space-2);
+}
diff --git a/src/pages/CreditsPage/CreditsPage.tsx b/src/pages/CreditsPage/CreditsPage.tsx
new file mode 100644
index 0000000..a03590d
--- /dev/null
+++ b/src/pages/CreditsPage/CreditsPage.tsx
@@ -0,0 +1,45 @@
+import Card from '../../components/Card/Card';
+import Button from '../../components/Button/Button';
+
+import './CreditsPage.css';
+
+function CreditsPage() {
+ return (
+
+
+
Credits
+
+ Thanks to the people, projects, and communities that made this application possible.
+
+
+
+
+
+
- Developer
+ - Lucas
+
+
+
+
- Framework
+ - React + TypeScript
+
+
+
+
- Desktop Runtime
+ - Tauri
+
+
+
+
- Special Thanks
+
+
+
+
+
+
+
+
+ );
+}
+
+export default CreditsPage;
diff --git a/src/pages/HomePage/HomePage.css b/src/pages/HomePage/HomePage.css
index bad807d..b40e4c5 100644
--- a/src/pages/HomePage/HomePage.css
+++ b/src/pages/HomePage/HomePage.css
@@ -1,35 +1,14 @@
.home-page {
width: 100%;
height: 100%;
- position: relative;
display: flex;
align-items: center;
justify-content: center;
-}
-.status-block {
- display: flex;
- align-items: center;
- gap: var(--space-2);
- flex-shrink: 0;
-}
+ padding: var(--space-6);
-.status-label {
- font-weight: 500;
- transition: color var(--dur-base) var(--ease-standard);
-}
-
-.status-label--connected {
- color: var(--accent-live);
-}
-
-.status-label--connecting {
- color: var(--accent-connecting);
-}
-
-.status-label--disconnected {
- color: var(--text-muted);
+ box-sizing: border-box;
}
.specs {
@@ -50,18 +29,30 @@
display: flex;
align-items: center;
justify-content: space-between;
+
+ gap: var(--space-4);
}
.spec dt {
color: var(--text-muted);
+ font-size: var(--font-md);
}
.spec dd {
margin: 0;
+
color: var(--text-primary);
+ font-weight: var(--font-weight-medium);
+ font-size: var(--font-md);
}
-.panel-actions {
+.home-panel-actions {
display: flex;
justify-content: flex-end;
}
+
+.card-content {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-6);
+}
diff --git a/src/pages/HomePage/HomePage.tsx b/src/pages/HomePage/HomePage.tsx
index ea01516..22f7db8 100644
--- a/src/pages/HomePage/HomePage.tsx
+++ b/src/pages/HomePage/HomePage.tsx
@@ -2,39 +2,41 @@ import Button from '../../components/Button/Button';
import Card from '../../components/Card/Card';
import './HomePage.css';
-function HomePage({
- dspCount,
- connectedCount,
- onAddDSP,
-}: {
+interface HomePageProps {
dspCount: number;
connectedCount: number;
onAddDSP: () => void;
-}) {
+}
+
+function HomePage({ dspCount, connectedCount, onAddDSP }: HomePageProps) {
return (
-
-
-
-
- Connected
- -
- {connectedCount} DSP{connectedCount !== 1 ? 's' : ''}
-
-
+
+
+
+
+
+
- Connected
+ -
+ {connectedCount} DSP{connectedCount !== 1 ? 's' : ''}
+
+
-
-
- Devices
- -
- {dspCount} DSP{dspCount !== 1 ? 's' : ''} configured
-
-
-
+
+
- Devices
+ -
+ {dspCount} DSP{dspCount !== 1 ? 's' : ''} configured
+
+
+
-
-
-
-
+
+
+
+
+
+
);
}
diff --git a/src/styles/App.css b/src/styles/App.css
index 1f38199..d1dad87 100644
--- a/src/styles/App.css
+++ b/src/styles/App.css
@@ -58,105 +58,15 @@ button {
transform var(--dur-base) var(--ease-standard);
}
-/* ---------- Stage / main area ---------- */
-
.stage {
flex: 1;
display: flex;
align-items: center;
justify-content: center;
- padding: 40px var(--space-6);
background: var(--bg-panel);
overflow-y: auto;
}
-.empty {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 14px;
- color: var(--text-muted);
- text-align: center;
-}
-
-.empty-mark {
- font-size: 28px;
- color: var(--border-hairline);
-}
-
-.empty p {
- margin: 0;
- font-size: 14px;
-}
-
-/* ---------- Form ---------- */
-
-.form-header {
- margin-bottom: var(--space-5);
-}
-
-.form-hint {
- margin: 6px 0 0;
- font-size: 13px;
- color: var(--text-muted);
-}
-
-.field {
- display: flex;
- flex-direction: column;
- gap: 6px;
- margin-bottom: var(--space-4);
- flex: 1;
-}
-
-.field--grow {
- flex: 2;
-}
-
-.field--narrow {
- flex: 1;
-}
-
-.field-row {
- display: flex;
- gap: var(--space-3);
-}
-
-.field label {
- font-size: 12px;
- color: var(--text-muted);
-}
-
-.field input,
-.field select {
- height: 36px;
- padding: 0 10px;
- background: var(--bg-panel);
- border: 1px solid var(--border-hairline);
- border-radius: var(--radius-sm);
- color: var(--text-primary);
- font-family: var(--font-ui);
- font-size: 13px;
- outline: none;
- transition: border-color var(--dur-base) var(--ease-standard);
-}
-
-.field input.mono {
- font-family: var(--font-mono);
-}
-
-.field input:focus,
-.field select:focus {
- border-color: var(--accent-brand);
-}
-
-.form-actions {
- display: flex;
- justify-content: flex-end;
- gap: 10px;
- margin-top: var(--space-2);
-}
-
/* Global scrollbar */
::-webkit-scrollbar {
width: 4px;
diff --git a/src/styles/variables.css b/src/styles/variables.css
index 657028b..c30487a 100644
--- a/src/styles/variables.css
+++ b/src/styles/variables.css
@@ -23,6 +23,11 @@
--font-xl: 1.25rem;
--font-2xl: 1.5rem;
+ /* ---------- Typography ---------- */
+ --font-weight-normal: 400;
+ --font-weight-medium: 500;
+ --font-weight-semibold: 600;
+
/* ---------- Spacing Scale ---------- */
--space-0: 0;
--space-1: 0.25rem;
@@ -45,16 +50,21 @@
--radius-lg: 10px;
--radius-pill: 999px;
-
-
/* ---------- Header ---------- */
--header-height: 50px;
--font-size-header: 1rem;
--tab-height: calc(var(--header-height) * 0.75);
+ /* ---------- Card ---------- */
+ --card-width: 500px;
+ --card-control-height: 36px;
+
+ /* ---------- Button ---------- */
+ --disabled-opacity: 0.45;
+
/* ---------- SideBar ---------- */
- --sidebar-width-collapsed: calc(var(--header-height) * 0.85 + 2 * var(--space-3));
+ --sidebar-width-collapsed: calc(var(--header-height) * 0.85 + 2 * var(--space-3));
--gain-section-height: 40vh;
@@ -88,14 +98,10 @@
--ease-out: cubic-bezier(0, 0, 0.2, 1);
--ease-in: cubic-bezier(0.4, 0, 1, 1);
-
-
/* ---------- Shadow ---------- */
--shadow-sm: 0 2px 6px rgba(0, 0, 0, 0.2);
--shadow-md: 0 4px 12px rgba(0, 0, 0, 0.25);
-
-
/* ---------- Animation ---------- */
--transition-fast: 120ms;
--transition-base: 180ms;