diff --git a/docs/superpowers/specs/2026-05-03-m7-track-actions-menu-design.md b/docs/superpowers/specs/2026-05-03-m7-track-actions-menu-design.md new file mode 100644 index 00000000..4141baff --- /dev/null +++ b/docs/superpowers/specs/2026-05-03-m7-track-actions-menu-design.md @@ -0,0 +1,240 @@ +# M7 — Track-level actions menu + +> **Status:** Draft for review · 2026-05-03 +> +> **Phase:** M7 task #372. Web-side polish slice. Replaces the single-entry `` (built as scaffolding for M5b's quarantine flag) with a real per-track action set. Lands before M7 #352 (Playlists CRUD) so the "Add to playlist…" entry has a menu to slot into. Mirrored on Flutter in a later slice once the web surface is settled. + +## 1. Goal + +Per-track kebab menu wherever `` or the `` track display is used, exposing the full set of actions a user can take on a track right now: queue manipulation, library state changes (Like, Hide), navigation (Go to album/artist), Lidarr-aware track removal, and a placeholder slot for "Add to playlist…" that #352 fills. + +## 2. Goals and non-goals + +### Goals + +- `` exposes nine entries, grouped four ways: + 1. **Queue:** Play next · Add to queue + 2. **Collection:** Like / Unlike · Add to playlist… (slot reserved; wired by #352) + 3. **Navigation:** Go to album · Go to artist + 4. **Lifecycle:** Flag this track… · Hide / Unhide · Remove from library *(admin-only)* +- "Like / Unlike" and "Hide / Unhide" entries duplicate functionality already on the row (heart and eye icon buttons). Reason: keyboard discoverability — once the kebab is focused, arrow-keys reach every action; mouse users still get one-tap heart/eye on the row. +- "Add to playlist…" reserves the menu slot but the submenu of playlists is wired in #352, not here. The slot renders as disabled with a tooltip "Coming with playlists" until #352 ships. +- "Go to artist" is single-target (`track.artistId`); compilation/feature artists aren't modeled in the schema today and that's a schema problem to solve before this entry needs a submenu. +- "Remove from library" is admin-only — gated client-side by `currentUser.is_admin`, server-side by the existing `auth.RequireAdmin()` middleware on the new `/api/admin/tracks/{id}` route. New backend endpoint deletes the track row, removes the file from disk (or via Lidarr for Lidarr-managed tracks), and cascades album → artist tidy-up if either becomes empty. +- Keyboard accessibility: kebab is `