16 KiB
phase, plan, type, wave, depends_on, files_modified, autonomous, requirements, must_haves
| phase | plan | type | wave | depends_on | files_modified | autonomous | requirements | must_haves | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| quick-17 | 1 | execute | 1 |
|
true |
|
|
Purpose: Consistent navigation UX across playlists, genres, and artists — all use the subpage pattern.
Output: New playlist-details component, simplified playlist-view, updated navigation routing.
<execution_context> @/home/caleb/.config/Claude/get-shit-done/workflows/execute-plan.md @/home/caleb/.config/Claude/get-shit-done/templates/summary.md </execution_context>
@frontend/src/components/genre-details/genre-details.ts (reference pattern — header + back button + content) @frontend/src/components/genres-view/genres-view.ts (reference pattern — click navigates to details) @frontend/src/components/playlist-view/playlist-view.ts (source — refactor this) @frontend/index.ts (navigation routing — add playlist-details case) @frontend/wailsjs/go/playlist/Service.d.ts (available backend APIs) @frontend/src/store/playlist-store.ts (playlist data store) @frontend/src/store/search-store.ts (search store — update SEARCHABLE_VIEWS if needed)From frontend/src/components/genre-details/genre-details.ts:
// Header: back-button → avatar → genre-info (title + track count)
// Content: <track-list .externalTracks=${this.tracks}>
// Back navigation: dispatches CustomEvent('navigate', { view: 'genres' })
From frontend/index.ts — navigation handler:
// Genre details routing (line 73-82):
case 'genre-details': {
const { genreName } = (e as CustomEvent).detail;
const genreEl = document.createElement('genre-details');
genreEl.setAttribute('genre-name', genreName);
mainContent.innerHTML = '';
mainContent.appendChild(genreEl);
break;
}
From frontend/wailsjs/go/playlist/Service.d.ts:
export function GetPlaylistTracks(arg1:number):Promise<Array<playlist.Track>>;
export function GetAllPlaylistsWithTracks():Promise<Array<playlist.WithTracks>>;
export function AddTracksToPlaylist(arg1:number,arg2:Array<string>):Promise<void>;
export function RemoveTracksFromPlaylist(arg1:number,arg2:Array<number>):Promise<void>;
export function RenamePlaylist(arg1:number,arg2:string):Promise<void>;
export function DeletePlaylist(arg1:number):Promise<void>;
export function RemovePhantomTracks(arg1:number,arg2:Array<string>):Promise<void>;
export function FindDuplicateTracksInPlaylist(arg1:number,arg2:Array<string>):Promise<playlist.DuplicateCheckResult>;
From frontend/src/components/playlist-view/playlist-view.ts:
// PlaylistEntry = { summary: playlist.Summary, expanded: boolean, tracks: playlist.Track[] }
// SelectionHost + ContextMenuHost interfaces
// Uses: PlayerController, PlaylistController, SearchController, SelectionController, ContextMenuController, FavoritesController
// Track interactions: click (select), dblclick (play), contextmenu, drag source, phantom handling
// Playlist-level: context menu (rename, delete, set-default), drag target, multi-select, sort
From frontend/wailsjs/go/models.ts:
// playlist.Summary: { ID: number, Name: string, CreatedAt: string, UpdatedAt: string }
// playlist.Track: { ID, Position, FilePath, Title, Artist, Album, CoverArtPath, CoverArtSmall, CoverArtMedium, CoverArtLarge, Duration, Phantom }
// playlist.WithTracks: { Summary: Summary, Tracks: Track[] }
Component structure:
-
Properties:
playlistId(Number attribute),playlistName(String attribute). -
Header (same layout as genre-details):
- Back button (dispatches
navigatewith{ view: 'playlists' }) - Playlist avatar: 80×80 rounded square with
listicon (wa-icon name="list") centered inside, using the same gradient background as genre-details'.genre-avatar - Playlist info: h1 title + track count subtitle
- Back button (dispatches
-
Content area: Render playlist tracks using the same track-item rendering pattern currently in
playlist-view'srenderPlaylistBodymethod. This includes:- "Play All" button at the top
- Track list with
<track-info>for normal tracks and phantom row rendering for phantom tracks - All track interactions: click (selection), dblclick (play from playlist), right-click context menu, drag source
- Phantom track interactions: click (select), right-click context menu, locate button, remove button
- Active track highlighting (uses PlayerController)
- Track selection (uses SelectionController — this component is the SelectionHost)
-
Context menus: Move the track-level context menu (play, add to queue, play next, remove from playlist, add to playlist submenu, favorites toggle, track details) and the playlist submenu from
playlist-viewinto this component. Include<track-details>,<phantom-resolver>, and<duplicate-tracks-dialog>elements. -
Data loading:
- On connectedCallback, call
GetPlaylistTracks(this.playlistId)to load tracks - Listen for
Events.PlaylistTracksChangedandEvents.PlaylistDeletedto refresh/navigate back - Implement a
refreshTracks()method that re-fetches tracks after mutations
- On connectedCallback, call
-
Drag target: Support dropping tracks onto this playlist (from queue, track-list, or other playlists). Use the same
AddTracksToPlaylist+FindDuplicateTracksInPlaylistflow currently inplaylist-view'sonPlaylistDrop. -
Drag source: Support dragging tracks from this playlist to queue or other destinations. Same pattern as
playlist-view'sonTrackDragStart/onTrackDragEnd. -
Search integration: Wire up SearchController. When search term is active, filter visible tracks (same logic as
playlist-view'sgetVisibleTracks). Addplaylist-detailstoSEARCHABLE_VIEWSinsearch-store.ts. -
Styles: Use
designTokensmixin. Copy the relevant styles fromgenre-detailsfor the header section (.back-button, avatar, title, track-count). Copy track-item styles (.track-item,.track-item.active,.track-item.selected,.phantom-row, etc.) and context menu styles fromplaylist-view. Include.play-all-buttonstyles. -
Select-all support: Listen for
shortcut:select-allevent (same as playlist-view does).
In frontend/index.ts:
- Add
import '@components/playlist-details/playlist-details.ts';at the top with the other imports - Add a
case 'playlist-details':block in the navigation switch, following the same pattern asgenre-details:case 'playlist-details': { const { playlistId, playlistName } = (e as CustomEvent).detail; const el = document.createElement('playlist-details'); el.setAttribute('playlist-id', String(playlistId)); el.setAttribute('playlist-name', playlistName); mainContent.innerHTML = ''; mainContent.appendChild(el); break; }
npx tsc --noEmit passes. The new component file exists with @customElement('playlist-details'), has back button, header, track rendering, context menus, drag support, and the index.ts routing case exists.
playlist-details component renders a header with back button + playlist icon + title + track count, displays playlist tracks with all interactions (select, play, context menu, drag, phantom handling), and navigating to playlist-details view works via index.ts routing.
Remove from playlist-view:
- The
expandedfield fromPlaylistEntryinterface (no longer needed — set type to just{ summary: playlist.Summary; tracks: playlist.Track[] }, keep tracks for count display and drag-drop track resolution) - The
renderPlaylistBodymethod entirely - The chevron icon in
renderPlaylistItem(no more expand/collapse) - All track-level interaction handlers:
handleTrackClick,handleTrackDblClick,handleTrackContextMenu,onTrackDragStart,onTrackDragEnd,ensureSelectionScope,getSelectedTrackIDs,getSelectedFilePaths,removeSelectedTracks,removeSelectedPhantoms,handlePhantomClick,handlePhantomContextMenu,openPhantomResolver,removePhantomTrack - The
SelectionController(no more track selection in the list view) andSelectionHostimplementation (getItemKey,getItemCount,onSelectionChanged) - The
ContextMenuControllerandContextMenuHostimplementation, and all track-level context menu rendering (the#context-menupopup,#playlist-submenupopup) - Remove
activePlaylistIndexstate - The
<track-details>,<phantom-resolver>, and<duplicate-tracks-dialog>elements from the render method (they move to playlist-details) - The
isActiveTrack,isPhantomSelection,resolvePlaylistCoverArt,openTrackDetailsmethods - The
getVisibleTracksandfilteredEntriessearch-related track filtering (search on the list page can just filter playlist names) - Remove
SelectionHostandContextMenuHostfrom the class declaration - Remove imports that are no longer needed:
SelectionController, track-info, track-details, phantom-resolver, duplicate-tracks-dialog, etc. - Remove the
clearSelectionHandlerandhandleSelectAllhandlers since track selection is gone - Remove the search-triggered auto-expand logic in
updated()(the part that setsexpanded: truebased on track matches)
Keep in playlist-view:
- Playlist list rendering (the grid of playlist items with name + track count)
- Playlist-level context menu (rename, delete, set-default) — the
#playlist-context-menupopup - Playlist-level selection (Ctrl/Shift+Click for multi-select of playlists for bulk delete)
- Create playlist functionality (new playlist button, create form)
- Import playlist button
- Sort toolbar (sort by name, created, modified, tracks)
- Drag-and-drop TARGET: dropping tracks onto a playlist item to add them (keep
onPlaylistDragOver,onPlaylistDragLeave,onPlaylistDrop). Also keep empty zone drop and new-button drop. - Search filtering (but simplified to just filter by playlist name, not tracks)
- Scroll position persistence
- PlaylistController for data loading
Modify handlePlaylistHeaderClick:
- Remove the expand/collapse toggle behavior for plain clicks
- Instead, plain click dispatches navigation:
this.dispatchEvent( new CustomEvent('navigate', { bubbles: true, composed: true, detail: { view: 'playlist-details', playlistId: entry.summary.ID, playlistName: entry.summary.Name, }, }), ); - Keep Ctrl+Click and Shift+Click for playlist multi-selection (same as before)
Modify renderPlaylistItem:
- Remove the chevron icon
- Remove the
${entry.expanded ? this.renderPlaylistBody(...) : nothing}conditional - The playlist-header row now just shows: playlist-icon (if favorites) + playlist-name + track-count
- Keep the right-click context menu handler on the header
- Keep the drag-over styling for drop targets
Simplify filteredEntries:
- Only filter by playlist name (remove the track title/artist matching since tracks aren't shown inline)
Remove styles that are no longer needed:
.chevron,.chevron.expanded.playlist-body.playlist-actions,.play-all-button.track-itemand all its variants (.active,.selected,.phantom).phantom-row,.phantom-caution,.phantom-path,.phantom-actions,.phantom-icon-btn.tracks-empty- Context menu styles for track-level menus
Update loadPlaylists/refreshPlaylists:
- Remove
expandedfrom the mapped entries
Remove the contextMenuStyles import if no longer needed (check if playlist-level context menu uses it — it does, so keep it).
Note: The PlaylistController, PlayerController, SearchController, FavoritesController may still be needed. Keep FavoritesController for the favorites icon display. Remove PlayerController since active track highlighting is gone from the list. Keep SearchController for name-based search. Keep PlaylistController for data.
npx tsc --noEmit passes. Verify playlist-view no longer has any expanded, renderPlaylistBody, handleTrackClick, SelectionController, or chevron references.
Playlist-view shows a clean list of playlists without inline track expansion. Plain-clicking a playlist navigates to playlist-details. Ctrl/Shift+Click still multi-selects. Right-click context menu still works for rename/delete/set-default. Drag-drop onto playlists still works. Create/import still works. Sort still works. Search filters by playlist name only.
<success_criteria>
- Playlist navigation matches genre/artist pattern: list view → click → detail subpage → back button
- No inline track expansion/collapse in playlist-view
- All existing track interactions work in playlist-details (play, select, context menu, drag, phantom handling)
- All existing playlist management works in playlist-view (create, import, rename, delete, sort, drag-drop target)
- TypeScript compiles without errors </success_criteria>