Commit Graph

82 Commits

Author SHA1 Message Date
Jonathan e008ee8d13 fix(web): remove inert global autoMonitorFuture checkbox from Monitor tab
The worker never reads the global monitor.autoMonitorFuture Config key;
auto-monitor-future is governed solely by the per-artist
WatchedArtist.autoMonitorFuture toggle on the Artists page. Removes the
misleading no-op checkbox and its DEFAULTS/test entries.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 01:08:05 +02:00
Jonathan 44c1b1a62a fix(web): wanted POST converges on P2002 unique race instead of 500
Two concurrent Wants for the same release-group both pass the
findUnique existence check as absent; the loser's create() then hits
the rgMbid unique constraint and threw an uncaught P2002, 500ing the
request. Wrap the create in try/catch and fall back to the same
update-and-return-200 path on P2002, converging instead of erroring.
Non-P2002 errors are rethrown unchanged.
2026-07-13 00:40:29 +02:00
Jonathan 9c793f42d1 fix(web): escape Lucene quotes in searchReleaseGroup query
A `"` or `\` inside artist/album terminates the quoted Lucene phrase
and corrupts the MusicBrainz search query. Escape both terms before
interpolation.
2026-07-13 00:37:00 +02:00
Jonathan fd0bb6aaa0 feat(web): surface discover.listenBrainzUrl; swap maxSeeds->chunkSize in Discovery tab 2026-07-13 00:33:49 +02:00
Jonathan 98735b5353 feat(web): clear-credential buttons on Settings 2026-07-13 00:30:35 +02:00
Jonathan 011852d091 fix(web): close prototype-chain bypass in DELETE /api/config allowlist
FIELDS[field] walked the prototype chain, so field=__proto__ (or
constructor, hasOwnProperty, etc.) resolved to a truthy inherited value
whose .key was undefined. Prisma drops undefined filter values, so
deleteMany({ where: { key: undefined } }) collapsed to deleteMany({}),
wiping the entire Config table. Guard with an own-property check before
indexing FIELDS so only real allowlist entries pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 00:27:22 +02:00
Jonathan 789eb2c76a feat(web): DELETE /api/config?field= to clear a credential
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 00:23:21 +02:00
Jonathan e048c68f40 feat(web): Monitor settings tab 2026-07-13 00:20:18 +02:00
Jonathan 2a50c1f518 feat(web): monitor config API route
Implement GET and PATCH endpoints for monitor configuration, matching the existing discover config pattern. GET returns stored or default values; PATCH upserts known keys with allowlist filtering.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 00:16:43 +02:00
Jonathan 891793953f feat(web): artists page — add-artist modal + filter watched list
The MusicBrainz search+follow moves into a modal opened by an "Add artist"
button; the main view gets a filter that narrows the watched roster (with a
"N of N" count). Follow keeps the modal open (toast per follow) so several can
be added in a row.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 23:22:37 +02:00
Jonathan 606ea82b70 feat: real worker status, covers on Recently Pressed, library counter
- Worker stamps a throttled worker.heartbeat; GET /api/worker reports online
  from its updatedAt recency (DB-time); masthead shows a live listening/offline
  dot instead of a static decoration.
- /api/requests enriched with rgMbid; Recently Pressed shows cover thumbs.
- Library album count moved from the filter row into a section header.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 17:58:23 +02:00
Jonathan e624d12089 feat(web): rework preview page onto tabs + covers + album modal
The standalone /discover/artist/[mbid] preview page was an inconsistent
orphan (no art, no tabs, old inline TRACKS toggle). Rebuild it to mirror the
artist-detail page: category tabs, cover thumbnails, and the album modal for
tracks, with per-release Want. Extract shared categoryOf/TAB_ORDER util.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 17:58:23 +02:00
Jonathan 92d1345503 feat(web): favicon + toast feedback (polish)
Add a vinyl-record favicon (icon.svg) — fixes the favicon.ico 404 console
error. Lightweight toast system (ToastHost + toast()) fired on request, wanted
add, search, follow, and want actions so submits give explicit feedback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 15:44:24 +02:00
Jonathan c027945191 feat(web): cover art on Wanted + Discover album rows
Wanted API returns rgMbid/type/year; Wanted rows show a cover thumb and open
the album modal. Discover suggested-album rows get a cover thumb too. Completes
cover art across the app (artist detail, Library, modals, Wanted, Discover).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 15:37:02 +02:00
Jonathan 76eab9e703 feat(web): Settings tabs per service (Qobuz/Soulseek/Library/Discovery)
Split the long Settings page into tabs so each service stands alone, instead
of one long mixed scroll. Same handlers/aria-labels; credential save still
PUTs the full config.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 15:33:20 +02:00
Jonathan 1fbe58013f feat(web): discovery modals — artist + album, not preview navigation
Clicking a suggested (or find-similar) artist opens an ArtistModal: Follow +
a cover-art grid of their releases with per-album Want (reuses the preview
endpoint), plus a "Full page" link to the deep-linkable preview route. Clicking
a suggested album opens the AlbumModal (tracklist + Want). Lighter than jumping
to a full preview page.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 15:29:46 +02:00
Jonathan 4f6c5995f8 feat(web): Library page — owned-albums grid with cover art
New /library: GET /api/library lists LibraryItems enriched with rgMbid + year
from the matching MonitoredRelease. Client renders a cover-art grid with a live
filter; clicking an album opens the AlbumModal (tracklist). Added Library to the
nav. The album-only overview to see everything downloaded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 15:24:16 +02:00
Jonathan d05d092d53 feat(web): artist detail — category tabs, cover thumbs, album modal
Discography now has category tabs (All/Albums/Singles/EPs/Live/Comps/Other
with counts, defaults to Albums), cover-art thumbnails, and clicking an album
opens the AlbumModal with its tracklist. Detail GET gains ?all=true so the
client filters by tab (studio-only default still governs the artists-list
counts). Replaces the old flat date-sorted list + show-all toggle.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 15:20:06 +02:00
Jonathan 34235a2355 feat(web): CoverArt + reusable AlbumModal
CoverArt loads album art from the Cover Art Archive CDN by release-group MBID
(browser-side, cached, graceful placeholder fallback). AlbumModal shows cover +
tracklist (fetched from MB) with slots for a status chip and context actions.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 15:14:34 +02:00
Jonathan 3284059aba feat(web): reusable accessible Modal (portal, focus trap, scroll lock)
Pressing Plant styled modal — ESC + backdrop close, Tab focus trap, body
scroll lock, focus restore, reduced-motion. Foundation for album/artist modals.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 15:12:36 +02:00
Jonathan 3f29ac1930 feat(web): widen app container 900px -> 1120px
More horizontal room for lists, tiles, and the upcoming Library grid.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 15:11:19 +02:00
Jonathan 50949f747b 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) <noreply@anthropic.com>
2026-07-12 14:34:46 +02:00
Jonathan ead5f33bcc feat(web): restyle Artists, Discover, Wanted, discography + preview (Phase 2)
Convert the remaining pages onto the Pressing Plant system: PageHead
component, shared list-row / toggle / small+ghost button / tracklist classes,
and page wrappers trimmed now that the shell provides masthead + nav. All
logic and test hooks (aria-labels, button text) preserved. Verified in the
real app across both themes (Playwright): artists/discover/wanted/discography
render cohesively with real data.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 14:27:43 +02:00
Jonathan 7f1fe52a7c feat(web): rebuild home as "The Floor" on the Pressing Plant system
Home reads /api/requests + /api/wanted + /api/artists: summary tiles
(on the press / pressed / wanted / watching), the on-the-press job list
(state -> stripe + chip + stepped bar via describeJob; needs-attention shows
a literal recovery note), a request form (test hooks preserved), an empty
state, and a recently-pressed deadwax list. Verified in the real app (dark
desktop + mobile); light theme matches the approved comp.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 14:09:42 +02:00
Jonathan fcbe34b027 feat(web): app shell — masthead, contents nav, theme toggle
Root layout now wraps every page in the editorial column with a masthead
(wordmark + tagline + live worker dot) and a contents-bar nav with active
state via usePathname. Theme toggle stamps data-theme on <html> and persists
to localStorage. Pages no longer repeat the ad-hoc nav line.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 14:09:42 +02:00
Jonathan 656e755daf feat(web): Pressing Plant component library + describeJob
design.css component classes (masthead, contents nav, tiles, dept header,
job row + severity stripe, status chip, progress bar, pressed list, buttons,
fields). Presentational _ui components. describeJob maps worker Job.state to
literal labels + severity kind + stepped progress (unit-tested).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 14:09:30 +02:00
Jonathan e1d96aa603 feat(web): Pressing Plant design tokens + self-hosted Fraunces
globals.css defines the manila/spruce palette as CSS custom properties for
both themes (prefers-color-scheme + explicit data-theme override), paper
grain, base reset, focus-visible, reduced-motion. Fraunces vendored as woff2
and wired via next/font/local with a Palatino/Georgia fallback.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 14:09:30 +02:00
Jonathan 33a1ed9cc4 fix(artists-api): create artist + discography atomically
Wrap the WatchedArtist.create + MonitoredRelease.createMany in a
$transaction so a mid-write failure can't leave a followed artist with
no releases (a state the 409 duplicate-follow guard would then wedge).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 10:42:42 +02:00
Jonathan a07e791368 feat(artists): link live-search hits to the artist preview page
Each MB search result on /artists now links its name to
/discover/artist/[mbid] (the read-only disco + tracklist + own-state
preview), so you can preview before following there too — same wiring
the discovery feed already uses.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 10:42:29 +02:00
Jonathan 3f77bb1f4c fix(preview): guard want() on res.ok; docs: preview in README + artists-search TODO
- preview-client want() no longer flips the Monitored badge when the POST fails
  (deferred item #1 from the discovery-preview whole-branch review).
- README: discovery section notes the artist preview page.
- Plan Notes + project-state: TODO to reuse the preview page from the /artists
  live search (wire artists-client.tsx hits to /discover/artist/[mbid]).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 02:03:08 +02:00
Jonathan 3928662809 feat(preview): link discovery feed + Find-similar to the artist preview
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 01:49:40 +02:00
Jonathan e1033dab81 feat(preview): /discover/artist/[mbid] preview page (disco + tracks + own-state)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 01:46:13 +02:00
Jonathan 9f2b115721 feat(preview): POST /api/discover/want (want a release-group by metadata)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 01:42:49 +02:00
Jonathan 46164c9eab feat(preview): GET /api/discover/preview/[mbid] annotated discography
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 01:39:35 +02:00
Jonathan f3cb306a19 feat(preview): GET /api/mb/release-groups/[rgMbid]/tracks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 01:36:28 +02:00
Jonathan e9759b8ef2 feat(preview): MB getArtistName + browseReleaseGroupTracks
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 01:33:44 +02:00
Jonathan d2c581d8e7 fix(discovery): use a valid ListenBrainz similar-artists algorithm
The placeholder algorithm string (contribution_5_threshold_15_limit_50) is not a
permitted enum member of the LB Labs endpoint — every lookup 400'd, making discovery
inert. Verified against the live API 2026-07-12: the days_7500/contribution_3/
threshold_10/limit_100 variant returns rows shaped {artist_mbid,name,comment,score},
which the existing defensive parser already handles. Fixed in both the worker adapter
and the web seed-search client (kept in sync).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:54:33 +02:00
Jonathan b9ebd18c2c feat(discovery): Discovery settings section (enable + tuning knobs)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:47:18 +02:00
Jonathan 3de7461245 feat(discovery): /discover feed page + seed search UI + nav link
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:44:11 +02:00
Jonathan 10262f053a feat(discovery): GET/PATCH /api/discover/config
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:41:13 +02:00
Jonathan c7aeab5176 feat(discovery): POST /api/discover/[id] follow/want/dismiss actions
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:38:06 +02:00
Jonathan bf868829d8 feat(discovery): GET /api/discover/search interactive seed search
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:34:28 +02:00
Jonathan be59862dfc feat(discovery): POST/GET /api/discover/run trigger + status
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:32:01 +02:00
Jonathan f750b815d2 feat(discovery): GET /api/discover feed
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:29:28 +02:00
Jonathan 2dfc9ab277 feat(discovery): web ListenBrainz similar-artists client
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 00:26:29 +02:00
Jonathan 0ddc9db611 feat(discovery): DiscoverySuggestion schema + WatchedArtist.lastDiscoveredAt
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 23:58:45 +02:00
Jonathan d65230ddfb feat: artist page toggle to show all release types (default studio-only) 2026-07-11 18:07:38 +02:00
Jonathan 560fdaa4b0 feat: filter artist discography + counts to core releases unless showAllTypes 2026-07-11 18:05:46 +02:00
Jonathan f2dbef047c feat: WatchedArtist.showAllTypes toggle (default false)
Adds the per-artist release-type filter toggle: off (default) restricts
a discography view to core studio releases, on shows everything
(live, compilations, etc.).
2026-07-11 18:03:29 +02:00
Jonathan 28fc1ee1a8 fix: artists search marks already-followed artists instead of a Follow button 2026-07-11 17:50:53 +02:00