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
{children}
; +} + +export default Card; diff --git a/src/components/Led/Led.css b/src/components/Led/Led.css index 6fa6ecd..0990dc5 100644 --- a/src/components/Led/Led.css +++ b/src/components/Led/Led.css @@ -47,7 +47,8 @@ } @keyframes pulse { - 0%, 100% { + 0%, + 100% { transform: scale(1); opacity: 1; } diff --git a/src/components/Led/Led.tsx b/src/components/Led/Led.tsx index d9576f1..c3e6b81 100644 --- a/src/components/Led/Led.tsx +++ b/src/components/Led/Led.tsx @@ -1,17 +1,14 @@ -import "./Led.css" +import './Led.css'; type LedProps = { status?: 'connected' | 'connecting' | 'disconnected'; className?: string; }; -export function Led({ - status = 'disconnected', - className = '', -}: LedProps) { +export function Led({ status = 'disconnected', className = '' }: LedProps) { return ( ); -} \ No newline at end of file +} diff --git a/src/components/Sidebar/Sidebar.css b/src/components/Sidebar/Sidebar.css index 6363703..3808bd5 100644 --- a/src/components/Sidebar/Sidebar.css +++ b/src/components/Sidebar/Sidebar.css @@ -94,7 +94,7 @@ .sidebar-rail { position: fixed; - top: var(--header-height); + top: calc(var(--header-height)); left: 0; bottom: 0; diff --git a/src/components/dsp408/ConnectionSection/ConnectionSection.tsx b/src/components/dsp408/ConnectionSection/ConnectionSection.tsx index c8d9956..6ec2825 100644 --- a/src/components/dsp408/ConnectionSection/ConnectionSection.tsx +++ b/src/components/dsp408/ConnectionSection/ConnectionSection.tsx @@ -31,61 +31,54 @@ function ConnectionPanel({ className="connection-panel-frame" style={{ '--panel-w': DESIGN_WIDTH, '--panel-h': DESIGN_HEIGHT } as React.CSSProperties} > -
-
-
-

- {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 ( -
+
Connected
@@ -32,7 +34,7 @@ function HomePage({ Add a DSP
-
+ ); } diff --git a/src/styles/App.css b/src/styles/App.css index d0cf788..1f38199 100644 --- a/src/styles/App.css +++ b/src/styles/App.css @@ -16,6 +16,7 @@ html, body, #root { height: 100%; + font-size: var(--font-size); } body { @@ -69,15 +70,6 @@ button { overflow-y: auto; } -.card { - width: 100%; - max-width: 480px; - background: var(--bg-raised); - border: 1px solid var(--border-hairline); - border-radius: var(--radius-lg); - padding: 28px; -} - .empty { display: flex; flex-direction: column; @@ -97,63 +89,6 @@ button { font-size: 14px; } -/* ---------- DSP panel ---------- */ - -.status-block { - display: flex; - align-items: center; - gap: var(--space-2); - flex-shrink: 0; -} - -.status-label { - font-size: 12px; - 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; - font-size: 13px; -} - -.spec dt { - color: var(--text-muted); -} - -.spec dd { - margin: 0; - color: var(--text-primary); -} - -.panel-actions { - display: flex; - justify-content: flex-end; -} - /* ---------- Form ---------- */ .form-header { @@ -221,3 +156,22 @@ button { gap: 10px; margin-top: var(--space-2); } + +/* Global scrollbar */ +::-webkit-scrollbar { + width: 4px; + height: 4px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background: var(--border-hairline); + border-radius: var(--radius-pill); +} + +::-webkit-scrollbar-thumb:hover { + background: var(--text-muted); +} diff --git a/src/styles/variables.css b/src/styles/variables.css index 926005c..657028b 100644 --- a/src/styles/variables.css +++ b/src/styles/variables.css @@ -1,34 +1,7 @@ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=JetBrains+Mono:wght@400;500&display=swap'); :root { - --header-height: 7vh; - --gain-section-height: 40vh; - - --sidebar-width-collapsed: 6vh; - --sidebar-width-expanded: 22vh; - - --sidebar-icon-size: 2vh; - --sidebar-item-size: 4.5vh; - --sidebar-toggle-size: 3vh; - --sidebar-label-size: 1.9vh; - --sidebar-item-gap: 1vh; - --sidebar-item-padding: 1vh; - - --font-size-sm: 3vh; - - --tab-height: 4vh; - --tab-width: 18vh; - --tab-font-size: 1.3vh; - --tab-close-size: 1.8vh; - --tab-close-font: 1.5vh; - - /* --tab-add-size: 2.6vh; */ - --tab-add-empty-height: 3vh; - --tab-add-empty-font: 1.2vh; - - --crown-icon-size: 2.2vh; - --tab-add-icon-size: 1.6vh; - --logo-size: 18vh; + --font-size: 18px; /* ---------- Color ---------- */ --bg-void: #0d0f12; @@ -42,6 +15,67 @@ --accent-down: #ff5c5c; --accent-connecting: #f5a623; + /* ---------- Font ---------- */ + --font-xs: 0.75rem; + --font-sm: 0.875rem; + --font-md: 1rem; + --font-lg: 1.125rem; + --font-xl: 1.25rem; + --font-2xl: 1.5rem; + + /* ---------- Spacing Scale ---------- */ + --space-0: 0; + --space-1: 0.25rem; + --space-2: 0.5rem; + --space-3: 0.75rem; + --space-4: 1rem; + --space-5: 1.25rem; + --space-6: 1.5rem; + --space-8: 2rem; + --space-10: 2.5rem; + --space-12: 3rem; + + /* ---------- Border ---------- */ + --border-width: 1px; + --border-width-active: 2px; + + /* ---------- Radius ---------- */ + --radius-sm: 6px; + --radius-md: 8px; + --radius-lg: 10px; + --radius-pill: 999px; + + + + /* ---------- Header ---------- */ + --header-height: 50px; + --font-size-header: 1rem; + + --tab-height: calc(var(--header-height) * 0.75); + + /* ---------- SideBar ---------- */ + --sidebar-width-collapsed: calc(var(--header-height) * 0.85 + 2 * var(--space-3)); + + --gain-section-height: 40vh; + + --sidebar-width-expanded: 22vh; + + --sidebar-icon-size: 2vh; + --sidebar-item-size: 4.5vh; + --sidebar-toggle-size: 3vh; + --sidebar-label-size: 1.9vh; + --sidebar-item-gap: 1vh; + --sidebar-item-padding: 1vh; + + --font-size-sm: 0.875rem; + + /* --tab-add-size: 2.6vh; */ + --tab-add-empty-height: 3vh; + + --crown-icon-size: 2.2vh; + --tab-add-icon-size: 1.6vh; + --logo-size: 18vh; + /* ---------- Type ---------- */ --font-ui: 'Inter', -apple-system, 'Segoe UI', sans-serif; --font-mono: 'JetBrains Mono', 'SF Mono', Menlo, monospace; @@ -54,38 +88,18 @@ --ease-out: cubic-bezier(0, 0, 0.2, 1); --ease-in: cubic-bezier(0.4, 0, 1, 1); - /* ---------- Spacing scale ---------- */ - --space-1: 0.5vh; - --space-2: 1vh; - --space-3: 1.5vh; - --space-4: 2vh; - --space-5: 2.5vh; - --space-6: 3vh; - - /* ---------- Radius ---------- */ - --radius-sm: 6px; - --radius-md: 8px; - --radius-lg: 1vh; - --radius-pill: 999px; + /* ---------- Shadow ---------- */ --shadow-sm: 0 2px 6px rgba(0, 0, 0, 0.2); --shadow-md: 0 4px 12px rgba(0, 0, 0, 0.25); - /* ---------- Border ---------- */ - --border-width: 0.07vh; - --border-width-active: 0.1vh; + /* ---------- Animation ---------- */ --transition-fast: 120ms; --transition-base: 180ms; - --font-xs: 1.2vh; - --font-sm: 1.4vh; - --font-md: 1.6vh; - --font-lg: 1.9vh; - --font-xl: 2.2vh; - --font-button: 1.5vh; --font-label: 1.4vh; --font-title: 1.8vh;