diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json
index f4caccd..adf92c6 100644
--- a/src-tauri/tauri.conf.json
+++ b/src-tauri/tauri.conf.json
@@ -13,8 +13,10 @@
"windows": [
{
"title": "thomann-dsp-app",
- "width": 800,
- "height": 600
+ "width": 1200,
+ "height": 800,
+ "minWidth": 900,
+ "minHeight": 600
}
],
"security": {
diff --git a/src/components/Button/Button.css b/src/components/Button/Button.css
index b143f7b..d9daa3a 100644
--- a/src/components/Button/Button.css
+++ b/src/components/Button/Button.css
@@ -46,4 +46,4 @@
background: transparent;
border-color: var(--border-hairline);
color: var(--text-muted);
-}
\ No newline at end of file
+}
diff --git a/src/components/Button/Button.tsx b/src/components/Button/Button.tsx
index 5cd04f1..765c775 100644
--- a/src/components/Button/Button.tsx
+++ b/src/components/Button/Button.tsx
@@ -34,4 +34,4 @@ export default function Button({
{children}
);
-}
\ No newline at end of file
+}
diff --git a/src/components/Card/Card.css b/src/components/Card/Card.css
new file mode 100644
index 0000000..51ea730
--- /dev/null
+++ b/src/components/Card/Card.css
@@ -0,0 +1,16 @@
+.card {
+ 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);
+ font-family: var(--font-ui);
+ font-size: var(--font-size-sm);
+
+ display: flex;
+ flex-direction: column;
+
+ width: clamp(280px, 33.333%, 480px);
+}
diff --git a/src/components/Card/Card.tsx b/src/components/Card/Card.tsx
new file mode 100644
index 0000000..9a8fb32
--- /dev/null
+++ b/src/components/Card/Card.tsx
@@ -0,0 +1,12 @@
+import './Card.css';
+
+interface CardProps {
+ children: React.ReactNode;
+ className?: string;
+}
+
+function Card({ children, className = '' }: CardProps) {
+ return
-
-
-
- {dsp.name}
-
-
{dsp.type}
-
-
-
-
- {statusLabel}
-
+
+
+
+
+ {dsp.name}
+
+
{dsp.type}
-
-
-
- IP address
- -
- {dsp.ip}
-
-
-
-
-
- Port
- - {dsp.port}
-
-
-
-
- Device ID
- -
- {dsp.deviceId || '—'}
-
-
-
-
-
- {dsp.status === 'connected' ? (
-
- ) : (
-
- )}
+
+
+ {statusLabel}
+
+
+
+
- IP address
+ -
+ {dsp.ip}
+
+
+
+
+
- Port
+ - {dsp.port}
+
+
+
+
- Device ID
+ -
+ {dsp.deviceId || '—'}
+
+
+
+
+
+ {dsp.status === 'connected' ? (
+
+ ) : (
+
+ )}
+
+
);
diff --git a/src/components/topbar/Topbar.css b/src/components/topbar/Topbar.css
index 1c98d06..42e05b9 100644
--- a/src/components/topbar/Topbar.css
+++ b/src/components/topbar/Topbar.css
@@ -5,8 +5,9 @@
background: var(--bg-void);
border-bottom: 1px solid var(--border-hairline);
-webkit-app-region: drag;
- padding-inline: var(--space-2);
- gap: var(--space-2);
+ padding-inline: var(--space-3);
+ gap: var(--space-3);
+ box-sizing: border-box
}
.logo-slot {
@@ -34,13 +35,17 @@
overflow-y: hidden;
min-width: 0;
-webkit-app-region: no-drag;
+ border-left: 1px solid var(--border-hairline);
+
+
scrollbar-width: thin;
scrollbar-color: var(--border-hairline) transparent;
scroll-behavior: smooth;
scroll-padding-inline: var(--space-3);
scroll-snap-type: x proximity;
align-items: center;
- gap: var(--space-2);
+ gap: var(--space-4);
+ padding-left: var(--space-4);
container-type: inline-size;
container-name: tabstrip;
@@ -49,28 +54,13 @@
mask-image: linear-gradient(to right, transparent, black 0);
}
-.tabstrip::-webkit-scrollbar {
- height: 4px;
-}
-.tabstrip::-webkit-scrollbar-thumb {
- background: var(--border-hairline);
- border-radius: var(--radius-pill);
-}
-
.tabstrip-actions {
display: flex;
align-items: center;
justify-content: center;
height: var(--header-height);
flex: 0 0 auto;
- gap: var(--space-2);
-}
-
-.icon-crown,
-.icon-tab-add,
-.icon-tab-add-empty {
- aspect-ratio: 1/1;
- height: 70%;
+ gap: var(--space-3);
}
.tab-action {
@@ -91,8 +81,15 @@
color: var(--text-primary);
}
+.tab-add {
+ height: 70%;
+ border-radius: 50%;
+ color: var(--text-muted);
+ aspect-ratio: 1;
+}
+
.crown-action {
- height: 50%;
+ height: 70%;
aspect-ratio: 1;
border-radius: 50%;
color: #facc15;
@@ -103,28 +100,26 @@
color: #fde047;
}
-.tab-add {
- height: 50%;
- border-radius: 50%;
- color: var(--text-muted);
- aspect-ratio: 1;
+.icon-crown,
+.icon-tab-add,
+.icon-tab-add-empty {
+ aspect-ratio: 1/1;
+ height: 80%;
}
.tab-add-empty {
- height: 50%;
+ height: 70%;
border-radius: var(--radius-pill);
border: var(--border-width-active) dashed var(--border-hairline);
color: var(--text-muted);
font-family: var(--font-ui);
- font-size: var(--tab-add-empty-font);
font-weight: 500;
flex-shrink: 0;
- gap: var(--space-1);
-
- padding-inline: var(--space-1);
+ gap: var(--space-2);
+ padding-inline: var(--space-2);
}
.tab-add-empty:hover {
@@ -136,15 +131,14 @@
.tab {
display: flex;
align-items: center;
- gap: var(--space-2);
+ gap: var(--space-3);
height: var(--tab-height);
padding: 0 var(--space-3);
- border: var(--border-width) solid var(--border-hairline);
+ border: var(--border-width-active) solid transparent;
border-radius: var(--radius-lg);
background: transparent;
color: var(--text-muted);
font-family: var(--font-ui);
- font-size: var(--tab-font-size);
cursor: pointer;
box-sizing: border-box;
scroll-snap-align: start;
@@ -152,14 +146,17 @@
flex: 0 0 25%;
max-width: 25%;
min-width: calc(
- (var(--space-3) * 2) +
- (var(--tab-height) * 0.5) +
- (var(--space-2) * 2) +
- 4ch +
- var(--tab-close-size)
+ (var(--space-3) * 2) + (var(--tab-height) * 0.5) + (var(--space-2) * 2) + 4ch +
+ var(--tab-close-size)
);
}
+.tab:hover {
+ background: var(--bg-raised);
+ color: var(--text-primary);
+ border-color: var(--accent-brand);
+}
+
.tab span[title] {
overflow: hidden;
text-overflow: ellipsis;
@@ -168,7 +165,7 @@
}
.tab-led {
- height: 50%;
+ height: 40%;
aspect-ratio: 1 / 1;
flex: 0 0 auto;
@@ -182,13 +179,14 @@
}
.tab-close {
+ height: 50%;
+ aspect-ratio: 1 / 1;
+
display: flex;
align-items: center;
justify-content: center;
flex: 0 0 auto;
margin-left: auto;
- width: var(--tab-close-size);
- height: var(--tab-close-size);
padding: 0;
font-size: var(--tab-close-font);
line-height: 1;
@@ -196,12 +194,6 @@
border-radius: var(--radius-sm);
}
-.tab:hover {
- background: var(--bg-raised);
- border-color: var(--text-muted);
- color: var(--text-primary);
-}
-
.tab--active {
background: var(--bg-panel);
border-color: var(--accent-brand);
@@ -217,16 +209,14 @@
color: var(--text-primary);
}
-.tab-add:active {
- background: var(--border-hairline);
-}
-
-.tabstrip::-webkit-scrollbar {
- display: none;
-}
-
.tabstrip[data-fade-start='true'] {
- -webkit-mask-image: linear-gradient(to right, transparent, black 24px, black calc(100% - 24px), black);
+ -webkit-mask-image: linear-gradient(
+ to right,
+ transparent,
+ black 24px,
+ black calc(100% - 24px),
+ black
+ );
mask-image: linear-gradient(to right, transparent, black 24px, black calc(100% - 24px), black);
}
.tabstrip[data-fade-end='true'] {
@@ -234,18 +224,27 @@
mask-image: linear-gradient(to right, black, black calc(100% - 24px), transparent);
}
.tabstrip[data-fade-start='true'][data-fade-end='true'] {
- -webkit-mask-image: linear-gradient(to right, transparent, black 24px, black calc(100% - 24px), transparent);
- mask-image: linear-gradient(to right, transparent, black 24px, black calc(100% - 24px), transparent);
+ -webkit-mask-image: linear-gradient(
+ to right,
+ transparent,
+ black 24px,
+ black calc(100% - 24px),
+ transparent
+ );
+ mask-image: linear-gradient(
+ to right,
+ transparent,
+ black 24px,
+ black calc(100% - 24px),
+ transparent
+ );
}
.tab[data-step='6'],
.tab[data-step='8'],
.tab[data-step='10'] {
min-width: calc(
- (var(--space-3) * 2) +
- (var(--tab-height) * 0.5) +
- (var(--space-2) * 2) +
- 4ch +
- var(--tab-close-size)
+ (var(--space-3) * 2) + (var(--tab-height) * 0.5) + (var(--space-2) * 2) + 4ch +
+ var(--tab-close-size)
);
-}
\ No newline at end of file
+}
diff --git a/src/components/topbar/Topbar.tsx b/src/components/topbar/Topbar.tsx
index 6cd2d16..b989fbd 100644
--- a/src/components/topbar/Topbar.tsx
+++ b/src/components/topbar/Topbar.tsx
@@ -4,6 +4,7 @@ import AnimatedLogo from '../../assets/icons/AnimatedLogo';
import { PlusIcon } from '../../assets/icons/PlusIcon';
import { CrownIcon } from '../../assets/icons/CrownIcon';
import { Led } from '../Led/Led';
+import { CloseIcon } from '../../assets/icons/CloseIcon';
import './Topbar.css';
@@ -15,7 +16,12 @@ function getStep(count: number) {
}
function Tab({
- dsp, active, dimmed, step, onSelect, onRemove,
+ dsp,
+ active,
+ dimmed,
+ step,
+ onSelect,
+ onRemove,
}: {
dsp: DSP;
active: boolean;
@@ -43,7 +49,7 @@ function Tab({
onRemove();
}}
>
- x
+
);
@@ -81,19 +87,19 @@ function TopBar({
el.dataset.fadeStart = String(!atStart);
el.dataset.fadeEnd = String(!atEnd && el.scrollWidth > el.clientWidth);
};
-
+
useEffect(() => {
- const el = tabstripRef.current;
- if (!el) return;
- const onWheel = (e: WheelEvent) => {
- if (Math.abs(e.deltaY) > Math.abs(e.deltaX)) {
- el.scrollLeft += e.deltaY;
- e.preventDefault();
- }
- };
- el.addEventListener('wheel', onWheel, { passive: false });
- return () => el.removeEventListener('wheel', onWheel);
-}, []);
+ const el = tabstripRef.current;
+ if (!el) return;
+ const onWheel = (e: WheelEvent) => {
+ if (Math.abs(e.deltaY) > Math.abs(e.deltaX)) {
+ el.scrollLeft += e.deltaY;
+ e.preventDefault();
+ }
+ };
+ el.addEventListener('wheel', onWheel, { passive: false });
+ return () => el.removeEventListener('wheel', onWheel);
+ }, []);
useEffect(() => {
updateFade();
@@ -164,8 +170,8 @@ function TopBar({
setSelect(null);
}}
>
-
-
+
+
);
diff --git a/src/pages/HomePage/HomePage.css b/src/pages/HomePage/HomePage.css
index e69de29..bad807d 100644
--- a/src/pages/HomePage/HomePage.css
+++ b/src/pages/HomePage/HomePage.css
@@ -0,0 +1,67 @@
+.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;
+}
+
+.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);
+}
+
+.specs {
+ display: flex;
+ flex-direction: column;
+ gap: var(--space-3);
+
+ margin: 0 0 var(--space-6);
+ padding: var(--space-4);
+
+ background: var(--bg-panel);
+
+ border: 1px solid var(--border-hairline);
+ border-radius: var(--radius-md);
+}
+
+.spec {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.spec dt {
+ color: var(--text-muted);
+}
+
+.spec dd {
+ margin: 0;
+ color: var(--text-primary);
+}
+
+.panel-actions {
+ display: flex;
+ justify-content: flex-end;
+}
diff --git a/src/pages/HomePage/HomePage.tsx b/src/pages/HomePage/HomePage.tsx
index 9020a58..ea01516 100644
--- a/src/pages/HomePage/HomePage.tsx
+++ b/src/pages/HomePage/HomePage.tsx
@@ -1,4 +1,6 @@
import Button from '../../components/Button/Button';
+import Card from '../../components/Card/Card';
+import './HomePage.css';
function HomePage({
dspCount,
@@ -10,7 +12,7 @@ function HomePage({
onAddDSP: () => void;
}) {
return (
-