diff --git a/.planning/STATE.md b/.planning/STATE.md
index e6d4476..9d7a38d 100644
--- a/.planning/STATE.md
+++ b/.planning/STATE.md
@@ -79,7 +79,7 @@ None currently.
|---|-------------|------|--------|-----------|
| 001 | Multi-playlist import support | 2026-02-28 | 50c8a33 | [001-multi-playlist-import-support](./quick/001-multi-playlist-import-support/) |
| 002 | Auto-rename duplicate playlists on import | 2026-02-28 | 8ba8bbe | [002-auto-rename-duplicate-playlists-on-import](./quick/002-auto-rename-duplicate-playlists-on-import/) |
-| 003 | Add multi-select to playlist view with batch delete | 2026-02-28 | c92ced2 | [3-add-multi-select-to-playlist-view-with-c](./quick/3-add-multi-select-to-playlist-view-with-c/) |
+| 003 | Add multi-select to playlist view with context menu delete support | 2026-02-28 | c92ced2 | [3-add-multi-select-to-playlist-view-with-c](./quick/3-add-multi-select-to-playlist-view-with-c/) |
## Session Continuity
@@ -99,5 +99,5 @@ None currently.
---
*State initialized: 2026-02-27*
-Last activity: 2026-02-28 - Completed quick task 003: Add multi-select to playlist view with batch delete
+Last activity: 2026-02-28 - Completed quick task 003: Add multi-select to playlist view with context menu delete support
*Last updated: 2026-02-28*
diff --git a/.planning/quick/3-add-multi-select-to-playlist-view-with-c/3-PLAN.md b/.planning/quick/3-add-multi-select-to-playlist-view-with-c/3-PLAN.md
new file mode 100644
index 0000000..17f912a
--- /dev/null
+++ b/.planning/quick/3-add-multi-select-to-playlist-view-with-c/3-PLAN.md
@@ -0,0 +1,200 @@
+---
+phase: quick
+plan: 3
+type: execute
+wave: 1
+depends_on: []
+files_modified:
+ - frontend/src/components/playlist-view/playlist-view.ts
+autonomous: true
+requirements: [QUICK-03]
+
+must_haves:
+ truths:
+ - "User can Ctrl+Click to toggle-select multiple playlist headers"
+ - "User can Shift+Click to range-select playlists"
+ - "Right-click on a selected playlist shows context menu with 'Delete N Playlists' option"
+ - "Delete action removes all selected playlists and refreshes the list"
+ - "Clicking a single playlist header without modifier still expands/collapses normally"
+ - "Track-level multi-select within expanded playlists still works independently"
+ artifacts:
+ - path: "frontend/src/components/playlist-view/playlist-view.ts"
+ provides: "Playlist-level multi-select with batch delete"
+ key_links:
+ - from: "playlist-view.ts (handlePlaylistHeaderClick)"
+ to: "selectedPlaylistIndices state"
+ via: "Ctrl/Shift+Click modifiers"
+ - from: "playlist-view.ts (onPlaylistContextAction 'delete')"
+ to: "DeletePlaylist backend call"
+ via: "batch iteration over selected playlist IDs"
+---
+
+
+Add playlist-level multi-select to the playlist view, allowing users to Ctrl+Click or Shift+Click playlist headers to select multiple playlists, then right-click to batch-delete them via the context menu.
+
+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.
+
+
+
+@.planning/quick/3-add-multi-select-to-playlist-view-with-c/3-PLAN.md
+
+
+
+@frontend/src/components/playlist-view/playlist-view.ts
+@frontend/src/utils/selection-controller.ts
+@frontend/src/utils/context-menu-controller.ts
+
+
+
+
+From selection-controller.ts:
+```typescript
+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 `SelectionController` with `activePlaylistIndex` scoping
+- `SelectionHost` interface 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)
+
+
+
+
+
+
+ Task 1: Add playlist-level multi-select state and selection handling
+ frontend/src/components/playlist-view/playlist-view.ts
+
+Add playlist-level multi-select using a simple `Set` pattern (matching how cover-grid handles album selection — simpler than a second SelectionController since playlists use index-based identity and there are typically few of them).
+
+**New state:**
+- `@state() private selectedPlaylists: Set = new Set();` — stores indices of selected playlists in the `entries` array
+- `private 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 `handleToggle` logic). Set `lastSelectedPlaylistIndex = null`.
+- **Ctrl/Cmd+Click (`e.ctrlKey || e.metaKey`):** Toggle the playlist at `index` in `selectedPlaylists`. Set `lastSelectedPlaylistIndex = index`. Do NOT expand/collapse.
+- **Shift+Click (`e.shiftKey`):** If `lastSelectedPlaylistIndex !== 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`), clear `selectedPlaylists` — prevent having both playlist-level and track-level selections active simultaneously.
+- In the existing `clearSelectionHandler` (line ~243), also clear `selectedPlaylists` when clicking outside.
+
+**Visual feedback — add CSS class:**
+Add a `.playlist-header.selected` style:
+```css
+.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.
+
+
+
+ Task 2: Wire playlist context menu to support batch delete of selected playlists
+ frontend/src/components/playlist-view/playlist-view.ts
+
+**Modify `handlePlaylistContextMenu` (line ~1582):**
+When right-clicking a playlist header:
+- If the right-clicked playlist is NOT in `selectedPlaylists`, replace the selection with just that playlist (matching context menu convention — same as track selection's `handleContextMenu`).
+- If the right-clicked playlist IS in `selectedPlaylists`, preserve the current multi-selection.
+- Set `playlistContextMenuIndex` as before (for positioning).
+
+**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, call `DeletePlaylist(entry.summary.ID)` for each, then `refreshPlaylists()` once at the end. Clear `selectedPlaylists` after.
+- If single selection (existing behavior), delete just that one playlist as before.
+
+Implementation for batch delete:
+```typescript
+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
+
+
+
+- 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
+
+
+