put the music library location in the url

/music?path=<rel> replaces the music:cwd channel. Each panel reads the param
itself through useMusicCwd(), so MusicBrowser, MusicDetail and FavoritesView
no longer tell each other where they are, and every drill-in is a <Link>:
library rows, folder rows, album/artist cards, both "up" affordances, the
favorites rows, and the dock's now-playing tile. Track rows stay buttons —
they play, which is a mutation.

A query param rather than a nested route because the location is only one of
the things this screen holds (the lyrics split and the favorites view are the
others), and a splat has to be a route's last segment.

MusicPlayerHost is mounted outside <Routes> and used to write the channel and
then navigate('/music') to make the write visible — the audit's only
navigate-with-a-side-effect. That collapses to one <Link>.

music:resync (a refresh signal) and music:favorites (a view of one panel) stay
channels, deliberately.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-07 12:08:04 +00:00
co-authored by Claude Opus 5
parent 374140d3a6
commit aef8619c6c
8 changed files with 164 additions and 122 deletions
+28 -3
View File
@@ -621,7 +621,9 @@ All the same bug: an app guessing "am I being closed?" from an unmount, or payin
### 5.8 Navigation — finish the refactor
- [ ] **Move the seven channel-driven apps' selection into the URL.** Split today: 11 URL-driven
- [x] **Move the seven channel-driven apps' selection into the URL.** _Six of the seven are done; the
seventh (Chat detail) is chat-owned and deliberately untouched — see the foot of this item._
Split when this was written: 11 URL-driven
(everything post-refactor), 7 channel-driven, 3 on bare `useState`. The channel-driven set: Music
(`music:cwd`), Soulseek sections, SystemMonitor (`monitor:scope`), Chat detail
(`chat:selected-session`), Email, Browser, and all five Settings pages (one fix point,
@@ -678,8 +680,31 @@ All the same bug: an app guessing "am I being closed?" from an unmount, or payin
Focus and Close buttons were nested inside the row `<button>` (invalid HTML, held together by two
`stopPropagation` calls — they are siblings of the anchor now), and "Set up in Integrations" was a
raw `<a href>` that reloaded the whole SPA.
Remaining here: Music. **Chat detail (`chat:selected-session`) is chat-owned —
leave it; it is written up in `COMMS/`.**
**Music is done** — the last of the seven that is mine, and the largest: the location touched four
files across two packages. It is `/music?path=<rel>`, `rel` relative to the `Music` root, and
`music:cwd` is deleted. A query param rather than the nested route the audit left open, for two
reasons: the location is only one of the things this screen holds (the lyrics split and the
favorites view are the others), and a splat has to be a route's last segment, which is the same
constraint `/chat` hit when it spelled its group `/chat/g/*`. `useMusicCwd()` returns the location
in the home-relative `Music/…` vocabulary the panels already spoke, so reading the URL cost each
panel one line and no rewrite.
What fell out of it is the point of the whole section. `MusicBrowser` published, `MusicDetail` and
`FavoritesView` subscribed, and `MusicPlayerHost` — mounted *outside* `<Routes>` — wrote the channel
and then called `navigate('/music')` to make the write visible, which is navigate-site 13 and the
only "navigate with a side-effect" in the audit's table. With the location in the URL that pair
collapses to one `<Link>`, and the host stops importing the library's channel at all. Every
drill-in is a link now: library rows, folder rows, album and artist cards, both "up" affordances,
the favorites rows, and the dock's now-playing tile. Track rows stay `<button>`s — they play, which
is a mutation, not a location. `MusicDetail`'s once-only auto-nav to the playing album navigates
with `replace`, on the same reasoning as Email's arrow keys: landing on `/music` and being moved to
the playing album is one arrival, so Back should leave the screen rather than undo a jump the user
never asked for. Two channels survived deliberately: `music:resync` (a refresh signal, never
selection) and `music:favorites` (a view of one panel). The favorites one needs a note — going Home
or opening a favorite has to close it *by hand*, because navigating to where you already are fires
no route change and the effect that clears it keys on the location.
Remaining here: **nothing that is mine.** Chat detail (`chat:selected-session`) is chat-owned —
leave it; it is written up in `COMMS/`. Soulseek's rooms and conversations are still `useState`,
noted in the audit and not scheduled.
### 5.9 The context has grown an app-config section — _(found 2026-08-07)_