9.2 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 | 3 | execute | 1 |
|
true |
|
|
Purpose: Currently users can only delete playlists one at a time. This adds standard multi-select UX (matching the existing track-level and album-level multi-select patterns) so users can quickly clean up multiple playlists.
Output: Updated playlist-view.ts with playlist-level multi-select and batch delete.
<execution_context> @.planning/quick/3-add-multi-select-to-playlist-view-with-c/3-PLAN.md </execution_context>
@frontend/src/components/playlist-view/playlist-view.ts @frontend/src/utils/selection-controller.ts @frontend/src/utils/context-menu-controller.tsFrom selection-controller.ts:
export interface SelectionHost extends ReactiveControllerHost {
getItemKey(index: number): string | undefined;
getItemCount(): number;
onSelectionChanged?(): void;
}
export class SelectionController {
handleItemClick(e: MouseEvent, key: string, index: number): void;
handleContextMenu(key: string): void;
clear(): void;
isSelected(key: string): boolean;
get hasSelection(): boolean;
get selectionCount(): number;
getSelectedIndices(): number[];
}
Existing playlist-view patterns:
- Track selection uses
SelectionControllerwithactivePlaylistIndexscoping SelectionHostinterface is already implemented for track selection- Playlist context menu uses
playlistContextMenuOpen,playlistContextMenuIndex,playlistContextMenuPopup DeletePlaylist(id: number)is the Go backend binding (deletes one at a time)
New state:
@state() private selectedPlaylists: Set<number> = new Set();— stores indices of selected playlists in theentriesarrayprivate lastSelectedPlaylistIndex: number | null = null;— anchor for Shift+Click range selection
Modify handleToggle (line ~1025):
Rename to a new handlePlaylistHeaderClick(e: MouseEvent, index: number) that checks modifier keys:
- No modifier: Clear playlist selection, toggle expand/collapse as before (existing
handleTogglelogic). SetlastSelectedPlaylistIndex = null. - Ctrl/Cmd+Click (
e.ctrlKey || e.metaKey): Toggle the playlist atindexinselectedPlaylists. SetlastSelectedPlaylistIndex = index. Do NOT expand/collapse. - Shift+Click (
e.shiftKey): IflastSelectedPlaylistIndex !== null, select all playlists in range[lastSelectedPlaylistIndex, index](inclusive). Add to existing selection (like existing track selection behavior). Do NOT expand/collapse.
Clear playlist selection on appropriate events:
- When track selection starts (
ensureSelectionScope), clearselectedPlaylists— prevent having both playlist-level and track-level selections active simultaneously. - In the existing
clearSelectionHandler(line ~243), also clearselectedPlaylistswhen clicking outside.
Visual feedback — add CSS class:
Add a .playlist-header.selected style:
.playlist-header.selected {
background-color: var(--yj-selection-bg, rgba(100, 160, 255, 0.15));
}
Update renderPlaylistItem (line ~2387):
Add selected class to .playlist-header div when this.selectedPlaylists.has(index).
Wire the header's @click to the new handlePlaylistHeaderClick(e, index) instead of the old handleToggle(index).
cd frontend && npx tsc --noEmit --pretty 2>&1 | head -30
Ctrl+Click toggles playlist selection (blue highlight), Shift+Click range-selects playlists, plain click still expands/collapses. Track selection and playlist selection are mutually exclusive.
Modify the playlist context menu template (line ~2237, the #playlist-context-menu wa-popup):
Update the menu items based on selection count:
When selectedPlaylists.size > 1:
- Hide "Rename" (can't rename multiple playlists at once)
- Show "Delete N Playlists" (with count) instead of "Delete Playlist"
When selectedPlaylists.size <= 1 (single or none):
- Show "Rename" and "Delete Playlist" as before (existing behavior)
Modify onPlaylistContextAction (line ~1627):
For the 'delete' case:
- If
selectedPlaylists.size > 1, iterate over all selected playlist indices, callDeletePlaylist(entry.summary.ID)for each, thenrefreshPlaylists()once at the end. ClearselectedPlaylistsafter. - If single selection (existing behavior), delete just that one playlist as before.
Implementation for batch delete:
case 'delete': {
if (this.selectedPlaylists.size > 1) {
const ids = [...this.selectedPlaylists]
.map(i => this.entries[i])
.filter((e): e is PlaylistEntry => e !== undefined)
.map(e => e.summary.ID);
for (const id of ids) {
await DeletePlaylist(id);
}
this.selectedPlaylists = new Set();
await this.refreshPlaylists();
} else {
await this.handleDeletePlaylist(entry.summary.ID);
}
break;
}
Make onPlaylistContextAction async (it currently isn't — change signature to private async onPlaylistContextAction(action: string)).
Clear playlist selection after any context action completes (rename or delete). cd frontend && npx tsc --noEmit --pretty 2>&1 | head -30 Right-clicking with multiple playlists selected shows "Delete N Playlists" (no rename). Clicking delete removes all selected playlists. Right-clicking an unselected playlist replaces the selection. Single playlist context menu still shows rename + delete as before.
1. `cd frontend && npx tsc --noEmit` — TypeScript compilation passes with zero errors 2. Manual: Open playlist view, Ctrl+Click two playlist headers → both highlight blue 3. Manual: Shift+Click a third → range fills in 4. Manual: Right-click → context menu shows "Delete 3 Playlists" (no rename option) 5. Manual: Click delete → all three are removed 6. Manual: Plain click a playlist header → expands/collapses normally, no selection artifacts 7. Manual: Select tracks within an expanded playlist → playlist-level selection clears<success_criteria>
- Playlist headers support Ctrl+Click toggle and Shift+Click range selection with blue highlight
- Playlist context menu adapts: shows "Delete N Playlists" for multi-select, "Rename" + "Delete Playlist" for single
- Batch delete works — all selected playlists are removed
- Plain click still expands/collapses playlists
- Track-level multi-select still works independently
- TypeScript compiles cleanly </success_criteria>