From 50949f747b2188f83d8964bbf5e5f6cbec5b8bc0 Mon Sep 17 00:00:00 2001 From: Jonathan Date: Sun, 12 Jul 2026 14:34:46 +0200 Subject: [PATCH] feat(web): restyle Settings + read-only library path panel (Phase 3) Settings onto the Pressing Plant system: credentials with clear set/unset affordances, a Library section that shows the configured /music path with a "change MUSIC_DIR in docker-compose + rebuild" note (honest about the mount being non-remountable from the UI), restyled scan trigger/result, and the discovery section. All logic and aria-labels preserved. Completes the frontend redesign (all pages + shell on one system, both themes). Co-Authored-By: Claude Opus 4.8 (1M context) --- web/src/app/design.css | 20 +++ web/src/app/settings/page.tsx | 8 +- web/src/app/settings/settings-form.tsx | 188 +++++++++++++++++-------- 3 files changed, 152 insertions(+), 64 deletions(-) diff --git a/web/src/app/design.css b/web/src/app/design.css index 3d6b5ca..4150580 100644 --- a/web/src/app/design.css +++ b/web/src/app/design.css @@ -222,3 +222,23 @@ nav.contents .sep { flex: 1; } } .tracks li .tnum { color: var(--rule-2); min-width: 2em; } .tracks li .tname { color: var(--ink); } + +/* ── Phase 3: settings ────────────────────────────────── */ +.field-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 18px 24px; margin: 16px 0 8px; } +.field.wide { grid-column: 1 / -1; } +.subhead { + font-family: var(--mono); font-size: 0.66rem; letter-spacing: 0.16em; text-transform: uppercase; + color: var(--graphite); margin: 26px 0 2px; +} +.field .set { color: var(--accent); } +.info-panel { + background: var(--paper-2); border: 1px solid var(--rule); padding: 15px 18px; margin: 14px 0 16px; + font-family: var(--mono); font-size: 0.78rem; line-height: 1.8; color: var(--ink); +} +.info-panel .k { color: var(--graphite); text-transform: uppercase; letter-spacing: 0.12em; font-size: 0.6rem; } +.info-panel code { color: var(--accent); } +.saved-note { + font-family: var(--mono); font-size: 0.64rem; letter-spacing: 0.12em; text-transform: uppercase; color: var(--accent); +} +.save-row { display: flex; align-items: center; gap: 14px; margin-top: 8px; } +.settings-section { margin-bottom: 30px; } diff --git a/web/src/app/settings/page.tsx b/web/src/app/settings/page.tsx index a9d104c..a246823 100644 --- a/web/src/app/settings/page.tsx +++ b/web/src/app/settings/page.tsx @@ -1,11 +1,5 @@ import { SettingsForm } from "./settings-form"; export default function SettingsPage() { - return ( -
-

Settings

- -

← Back to queue

-
- ); + return ; } diff --git a/web/src/app/settings/settings-form.tsx b/web/src/app/settings/settings-form.tsx index b243def..56c4521 100644 --- a/web/src/app/settings/settings-form.tsx +++ b/web/src/app/settings/settings-form.tsx @@ -1,6 +1,8 @@ "use client"; import { useEffect, useState } from "react"; +import { PageHead } from "../_ui/page-head"; +import { SectionHeader } from "../_ui/section-header"; type Loaded = { qobuzEmail: string; @@ -11,6 +13,14 @@ type Loaded = { slskdApiKeySet: boolean; }; +const DISCOVERY_FIELDS: [string, string][] = [ + ["discover.intervalHours", "Interval (hours)"], + ["discover.maxSeeds", "Max seeds per sweep"], + ["discover.similarPerSeed", "Similar per seed"], + ["discover.albumsPerArtist", "Albums per artist"], + ["discover.minScore", "Min score"], +]; + export function SettingsForm() { const [qobuzEmail, setQobuzEmail] = useState(""); const [qobuzPassword, setQobuzPassword] = useState(""); @@ -101,67 +111,131 @@ export function SettingsForm() { setTimeout(() => setDiscoverSaved(false), 1500); } + const secretHint = (set: boolean) => (set ? · set : null); + return ( - <> -
-
- Qobuz — email/password - - -
-
- Qobuz — auth token (more reliable; overrides email/password) - - -
-
- Soulseek (slskd) - - -
- - {saved ? Saved. : null} +
+ + + {/* ── Credentials ─────────────────────────── */} + + + +
Qobuz — email / password
+
+ + +
+ +
Qobuz — auth token (more reliable; overrides email/password)
+
+ + +
+ +
Soulseek (slskd)
+
+ + +
+ +
+ + {saved ? Saved : null} +
-
-

Library

-

Scan /music and record what's already on disk as owned + monitored.

- - {scanResult ?

Last scan: {scanResult}

: null} + + {/* ── Library ─────────────────────────────── */} +
+ +
+
+ Music path   /music +
+
+ Mounted read/write into the worker. To point Lyra at a different folder, set{" "} + MUSIC_DIR in docker-compose.yml and rebuild — it can’t be remounted from here. +
+
+

Scan the library to record what’s already on disk as owned + monitored.

+
+ + {scanResult ? ( + + Last scan: {scanResult} + + ) : null} +
-
-

Discovery

- - {[ - ["discover.intervalHours", "Interval (hours)"], - ["discover.maxSeeds", "Max seeds per sweep"], - ["discover.similarPerSeed", "Similar per seed"], - ["discover.albumsPerArtist", "Albums per artist"], - ["discover.minScore", "Min score"], - ].map(([key, label]) => ( -

-

+ +
+
+ {DISCOVERY_FIELDS.map(([key, label]) => ( + -

- ))} - - {discoverSaved && Saved.} + ))} +
+
+ + {discoverSaved ? Saved : null} +
- +
); }