docs(13-02): complete library filter UI & phantom resolution plan

- SUMMARY.md updated with checkpoint approval and all bugfix commits
- STATE.md updated with Phase 13 completion and new decisions
- ROADMAP.md plan checkboxes and milestone status updated
- REQUIREMENTS.md coverage updated: 31/31 v1.1 requirements complete
This commit is contained in:
2026-03-16 13:03:35 -04:00
parent 9f595b7ac1
commit 0a5877c177
4 changed files with 153 additions and 80 deletions
+4 -3
View File
@@ -205,10 +205,11 @@ Which phases cover which requirements. Updated during roadmap creation.
| DATA-04 | Phase 10 | Complete |
**Coverage:**
- v1.1 requirements: 31 total (20 complete + 11 pending)
- Mapped to phases: 31/31 ✓ (Phase 9: 8, Phase 10: 5, Phase 11: 4, Phase 12: 7 of 7 — 11 pending in Phase 13)
- v1.1 requirements: 31 total (31 complete)
- Mapped to phases: 31/31 ✓ (Phase 9: 8, Phase 10: 5, Phase 11: 4, Phase 12: 7, Phase 13: 7)
- No orphaned requirements
- All v1.1 requirements complete as of 2026-03-16
---
*Requirements defined: 2026-03-06*
*Last updated: 2026-03-08restructured for multi-library support, deferred TAG/SMRT/GAP/MB/LAYOUT/PLUG*
*Last updated: 2026-03-16all v1.1 requirements complete (31/31)*
+14 -14
View File
@@ -7,8 +7,8 @@
## Milestones
-**v1.0 Consolidation** — Phases 1-8 (shipped 2026-03-05) — [archive](milestones/v1.0-ROADMAP.md)
- 🔄 **v1.1 Multi-Library Support** — Phase 9 complete, Phases 10-13 in progress
- 🔄 **Performance Optimization** — Phase 14 (cross-cutting, parallel to v1.1)
- **v1.1 Multi-Library Support** — Phases 9-13 complete (shipped 2026-03-16)
- **Performance Optimization** — Phase 14 complete (shipped 2026-03-15)
## Phases
@@ -110,8 +110,8 @@ Plans:
5. When a library is removed, its tracks in playlists become phantom entries — visually distinguished (greyed out / icon) with preserved title, artist, album metadata instead of disappearing
**Plans:** 2/2 plans complete
Plans:
- [ ] 13-01-PLAN.md — Backend library-filtered sqlc queries + Go methods + FTS search
- [ ] 13-02-PLAN.md — Frontend library filter store + dropdown UI + all view/search wiring + verification
- [x] 13-01-PLAN.md — Backend library-filtered sqlc queries + Go methods + FTS search
- [x] 13-02-PLAN.md — Frontend library filter store + dropdown UI + all view/search wiring + verification
### Phase 14: Performance Optimization
**Goal:** Scrolling, navigation, and rendering are as smooth and fast as possible — scrolling feels like a native animation, navigation is instant, no unnecessary re-renders
@@ -125,10 +125,10 @@ Plans:
5. A profiling guide documents how to diagnose performance issues using pprof (backend) and DevTools (frontend)
**Plans:** 4/4 plans complete
Plans:
- [ ] 14-01-PLAN.md — CSS containment + GPU layer promotion on all scroll containers
- [ ] 14-02-PLAN.md — View caching navigation system (replace innerHTML destruction)
- [ ] 14-03-PLAN.md — Render hot-path optimization (closure elimination, store granularity)
- [ ] 14-04-PLAN.md — Scroll event optimization, profiling guide, performance verification checkpoint
- [x] 14-01-PLAN.md — CSS containment + GPU layer promotion on all scroll containers
- [x] 14-02-PLAN.md — View caching navigation system (replace innerHTML destruction)
- [x] 14-03-PLAN.md — Render hot-path optimization (closure elimination, store granularity)
- [x] 14-04-PLAN.md — Scroll event optimization, profiling guide, performance verification checkpoint
## Progress
@@ -143,12 +143,12 @@ Plans:
| 7. Backend Performance | v1.0 | 2/2 | Complete | 2026-03-05 |
| 8. Frontend Performance & UX | v1.0 | 4/4 | Complete | 2026-03-05 |
| 9. Scan Cancellation & Keyboard Shortcuts | v1.1 | 5/5 | Complete | 2026-03-07 |
| 10. Schema & Migration | 2/2 | Complete | 2026-03-09 | - |
| 11. Per-Library Scan Pipeline | 3/3 | Complete | 2026-03-09 | - |
| 12. Library CRUD & Data Integrity | v1.1 | Complete | 2026-03-15 | 2026-03-15 |
| 13. Library Views & Phantom Tracks | 2/2 | Complete | 2026-03-16 | - |
| 14. Performance Optimization | 4/4 | Complete | 2026-03-15 | - |
| 10. Schema & Migration | v1.1 | 2/2 | Complete | 2026-03-09 |
| 11. Per-Library Scan Pipeline | v1.1 | 3/3 | Complete | 2026-03-09 |
| 12. Library CRUD & Data Integrity | v1.1 | 2/2 | Complete | 2026-03-15 |
| 13. Library Views & Phantom Tracks | v1.1 | 2/2 | Complete | 2026-03-16 |
| 14. Performance Optimization | Perf | 4/4 | Complete | 2026-03-15 |
---
*Roadmap created: 2026-02-27*
*Last updated: 2026-03-14Phase 14 (Performance Optimization) added with 4 plans*
*Last updated: 2026-03-16v1.1 milestone complete (Phases 9-14 all done)*
+6 -3
View File
@@ -155,6 +155,9 @@ Decisions from v1.0 are archived in PROJECT.md Key Decisions table. Key patterns
| Client-side search with filtered data source | rankTracks filters already-loaded tracks; no backend SearchTracksByLibrary call needed |
| Native select for library filter dropdown | Compact, accessible, matches top bar height; no custom component overhead |
| getAlbumsByArtistNameCached returns null when filter active | Avoids stale cross-library data; forces backend query for consistency |
| ScanHooks callback for phantom resolution | Mirrors RemovalHooks/RescanHooks pattern; avoids circular dependency between library and playlist packages |
| phantom_file_path column on playlist_tracks | Stored at removal time for post-scan matching; enables automatic phantom resolution |
| M3U8-based phantom resolution over SQL-only | Reads playlist files to match by position and file path; handles both pre-existing and new phantoms |
### Warnings (carry forward)
@@ -182,9 +185,9 @@ Decisions from v1.0 are archived in PROJECT.md Key Decisions table. Key patterns
### Last Session
**Date:** 2026-03-16
**What happened:** Executed Plan 13-02 — added library filter dropdown to top bar, wired all browse/detail views to respect library filter via ByLibrary conditional queries. 1 task (auto), 12 files modified. Checkpoint pending for human verification.
**Where we stopped:** Completed 13-02-PLAN.md — Phase 13 complete (2/2 plans), awaiting human verification
**Next action:** Human verification of library filter, cross-library playlists, and phantom tracks
**What happened:** Completed Plan 13-02 — library filter dropdown in top bar, all views wired to ByLibrary queries, phantom track auto-resolution via ScanHooks. Checkpoint APPROVED — all 7 requirements verified (VIEW-0104, PLAY-0103). Additional bugfixes: virtualizer event delegation race condition, phantom auto-resolution after re-scan.
**Where we stopped:** Completed 13-02-PLAN.md — Phase 13 complete (2/2 plans), v1.1 milestone complete
**Next action:** v1.1 milestone shipped — all phases (9-14) complete
---
*State initialized: 2026-02-27*
@@ -1,32 +1,40 @@
---
phase: 13-library-views-phantom-tracks
plan: 02
subsystem: frontend, ui
tags: [lit, wails, library-filter, dropdown, multi-library]
subsystem: ui, api
tags: [lit, wails, library-filter, phantom-tracks, playlist-resolution, typescript]
# Dependency graph
requires:
- phase: 13-library-views-phantom-tracks
provides: Library-filtered Go query methods and Wails bindings
plan: 01
provides: Library-filtered sqlc queries and Go methods for tracks, albums, artists, genres, search
- phase: 12-library-crud-data-integrity
provides: Library CRUD API, orphan cleanup, phantom track metadata pre-population
- phase: 10-schema-migration
provides: Libraries table, library_id FK, phantom columns on playlist_tracks
provides:
- Library filter dropdown component in top bar
- All browse views (tracks, albums, artists, genres) filter by selected library
- Detail views (artist-details, genre-details) respect library filter
- Search results respect library filter (via filtered data source)
- Playlists remain unfiltered (cross-library by design)
affects: [frontend-navigation, queue-context]
- Library filter dropdown UI in top bar with All Libraries default
- All browse views (tracks, albums, artists, genres) respect active library filter
- Search respects active library filter
- Detail views (artist-details, genre-details, album tracks) respect library filter
- Phantom track auto-resolution after library scan via ScanHooks callback
- phantom_file_path column on playlist_tracks for phantom-to-track matching
affects: [multi-library-complete, v1.1-milestone]
# Tech tracking
tech-stack:
added: []
patterns:
- Conditional ByLibrary/unfiltered call pattern in library-store
- Library filter state as store singleton (null = All Libraries)
- Native select dropdown for compact filter UI
- "Conditional Wails binding dispatch: store methods call ByLibrary variant when filter active, default variant otherwise"
- "ScanHooks callback for cross-package phantom resolution (mirrors RemovalHooks/RescanHooks pattern)"
- "Deferred event delegation retry in updated() for virtualizer race condition"
- "phantom_file_path stored on removal for post-scan matching"
key-files:
created:
- frontend/src/components/library-filter/library-filter.ts
- backend/playlist/playlist.go (ResolvePhantomTracksAfterScan)
modified:
- frontend/src/store/library-store.ts
- frontend/src/store/controllers/library-controller.ts
@@ -37,73 +45,111 @@ key-files:
- frontend/src/components/artists-view/artists-view.ts
- frontend/src/components/genres-view/genres-view.ts
- frontend/src/components/genre-details/genre-details.ts
- frontend/wailsjs/go/library/Library.d.ts
- frontend/wailsjs/go/library/Library.js
- frontend/src/components/track-list/track-list.ts
- frontend/src/components/queue-panel/queue-panel.ts
- backend/library/crud.go
- backend/library/library.go
- backend/database/database.go
- backend/database/sql/schemas/playlist_tracks.sql
- backend/app.go
key-decisions:
- "Client-side search with library-filtered data source — no backend SearchTracksByLibrary needed since search is already client-side via rankTracks"
- "Null selectedLibraryId = All Libraries (no persistence, resets on restart)"
- "Native select element for dropdown — compact, accessible, no custom widget overhead"
- "getAlbumsByArtistNameCached returns null when library filter active — forces backend query for consistency"
- "Client-side search with filtered data source — rankTracks filters already-loaded tracks; no backend SearchTracksByLibrary call needed"
- "Native select for library filter dropdown — compact, accessible, matches top bar height; no custom component overhead"
- "getAlbumsByArtistNameCached returns null when filter active — avoids stale cross-library data; forces backend query"
- "ScanHooks callback pattern for phantom resolution — mirrors RemovalHooks/RescanHooks for cross-package communication"
- "phantom_file_path column on playlist_tracks — enables post-scan matching of phantoms to re-added tracks"
- "M3U8-based phantom resolution — reads playlist files to match phantoms by position and file path"
patterns-established:
- "ByLibrary conditional call pattern: check selectedLibraryId, call ByLibrary or unfiltered variant"
- "Conditional ByLibrary dispatch: check selectedLibraryId, call ByLibrary variant or unfiltered default"
- "ScanHooks pattern: cross-package callbacks registered at app init to avoid circular imports"
- "Deferred delegation guard: retry event delegation in updated() when virtualizer not ready on firstUpdated()"
requirements-completed: [VIEW-01, VIEW-02, VIEW-03, VIEW-04, PLAY-01, PLAY-02, PLAY-03]
# Metrics
duration: 9min
duration: 45min
completed: 2026-03-16
---
# Phase 13 Plan 02: Library Filter UI & View Wiring Summary
# Phase 13 Plan 02: Library Filter UI & Phantom Track Resolution Summary
**Library filter dropdown in top bar with conditional ByLibrary queries across all browse views, detail views, and search — playlists remain unfiltered**
**Library filter dropdown in top bar with conditional ByLibrary queries across all views, plus ScanHooks-based phantom track auto-resolution after library re-scan**
## Performance
- **Duration:** 9 min
- **Duration:** ~45 min (including checkpoint verification and bugfixes)
- **Started:** 2026-03-16T13:32:45Z
- **Completed:** 2026-03-16T13:41:49Z
- **Tasks:** 1 (of 2 — Task 2 is human verification checkpoint)
- **Files modified:** 12
- **Completed:** 2026-03-16T14:18:00Z
- **Tasks:** 2 (1 auto + 1 checkpoint:human-verify — APPROVED)
- **Files modified:** 33 (across 4 code commits)
## Accomplishments
- Created library-filter dropdown component that loads library list from backend and sets store filter
- Modified library-store to conditionally call ByLibrary or unfiltered queries based on selectedLibraryId
- Wired all browse views (tracks, albums, artists, genres) to automatically use filtered data
- Wired detail views (genre-details direct Wails call, artist-details via store, cover-grid album dropdown) to respect filter
- Updated album-selection helper for library-aware drag/context-menu file path resolution
- Verified playlists use separate PlaylistStore — not affected by library filter
- Verified search is client-side (rankTracks on loaded data) — inherits filter automatically
- Created `<library-filter>` dropdown component in top bar — shows "All Libraries" default plus all configured libraries
- Wired all browse views (tracks, albums, artists, genres) and detail views (artist-details, genre-details, album track expansion) to respect active library filter via conditional ByLibrary Wails binding calls
- Search inherits library filter automatically — client-side rankTracks operates on filtered track data
- Playlists remain unfiltered (cross-library by design) — verified during checkpoint
- Fixed pre-existing virtualizer event delegation race condition from Phase 14-03 optimization
- Added phantom track auto-resolution: ScanHooks callback triggers M3U8-based phantom matching after library scan
- Added `phantom_file_path` column (migration 7) to playlist_tracks for reliable phantom→track matching
- All 7 Phase 13 requirements verified end-to-end: VIEW-01 through VIEW-04, PLAY-01 through PLAY-03
## Task Commits
Each task was committed atomically:
1. **Task 1: Add library filter state + dropdown + wire all views** - `42b8cf9` (feat)
2. **Task 2: Verify library filter, cross-library playlists, and phantom tracks** - Checkpoint APPROVED
**Task 2:** checkpoint:human-verify (pending user verification)
### Bugfix Commits (during checkpoint verification)
3. **Fix: Defer virtualizer event delegation until element exists** - `f05d2bb` (fix)
4. **Fix: Auto-resolve phantom playlist tracks after library scan** - `93262b9` (fix)
5. **Fix: Resolve phantom playlist tracks using M3U8 paths after scan** - `9f595b7` (fix)
**Plan metadata:** `70e3814` (docs: complete library filter UI plan)
## Files Created/Modified
- `frontend/src/components/library-filter/library-filter.ts` - NEW: Library filter dropdown component
- `frontend/src/store/library-store.ts` - selectedLibraryId state, ByLibrary conditional calls, getLibraries()
- `frontend/src/store/controllers/library-controller.ts` - Pass-through for selectedLibraryId, setSelectedLibrary, getLibraries
- `frontend/index.html` - Added `<library-filter>` to top bar
- `frontend/index.ts` - Import library-filter component
- `frontend/src/components/cover-grid/cover-grid.ts` - GetAlbumTracksByLibrary in dropdown
- `frontend/src/components/cover-grid/album-selection.ts` - Library-aware fetchAlbumTracks helper
- `frontend/src/components/artists-view/artists-view.ts` - GetAlbumsByArtistByLibrary + GetAlbumTracksByLibrary
- `frontend/src/components/genres-view/genres-view.ts` - GetTracksByGenreByLibrary for context menu
- `frontend/src/components/genre-details/genre-details.ts` - GetTracksByGenreByLibrary for track loading
- `frontend/wailsjs/go/library/Library.d.ts` - Regenerated with ByLibrary bindings
- `frontend/wailsjs/go/library/Library.js` - Regenerated with ByLibrary bindings
### Frontend — Library filter UI (Task 1)
- `frontend/src/components/library-filter/library-filter.ts` — NEW: Library filter dropdown component (native select, design tokens)
- `frontend/src/store/library-store.ts` — selectedLibraryId state, conditional ByLibrary dispatch, getLibraries(), invalidation
- `frontend/src/store/controllers/library-controller.ts` — Pass-through for selectedLibraryId, setSelectedLibrary, getLibraries
- `frontend/index.html``<library-filter>` element added to top bar header
- `frontend/index.ts` — Import for library-filter component
- `frontend/src/components/cover-grid/cover-grid.ts` GetAlbumTracksByLibrary for album expansion dropdown
- `frontend/src/components/cover-grid/album-selection.ts` — Library-aware fetchAlbumTracks helper
- `frontend/src/components/artists-view/artists-view.ts` GetAlbumsByArtistByLibrary + GetAlbumTracksByLibrary
- `frontend/src/components/genres-view/genres-view.ts` — GetTracksByGenreByLibrary for context menu
- `frontend/src/components/genre-details/genre-details.ts` — GetTracksByGenreByLibrary for track loading
- `frontend/wailsjs/go/library/Library.d.ts` — Regenerated with ByLibrary bindings
- `frontend/wailsjs/go/library/Library.js` — Regenerated with ByLibrary bindings
### Frontend — Virtualizer race condition fix
- `frontend/src/components/track-list/track-list.ts` — Deferred event delegation with guard flag in updated()
- `frontend/src/components/queue-panel/queue-panel.ts` — Deferred event delegation with guard flag in updated()
### Backend — Phantom track auto-resolution
- `backend/database/database.go` — Migration 7: phantom_file_path column on playlist_tracks
- `backend/database/sql/schemas/playlist_tracks.sql` — phantom_file_path column definition
- `backend/database/sql/sqlcgen/models.go` — Generated model with PhantomFilePath field
- `backend/database/sql/sqlcgen/playlists.sql.go` — Generated query updates
- `backend/library/crud.go` — Store file_path as phantom_file_path on RemoveLibrary
- `backend/library/library.go` — ScanHooks registration, phantom resolution trigger after scan
- `backend/playlist/playlist.go` — ResolvePhantomTracksAfterScan: M3U8-based phantom matching
- `backend/app.go` — ScanHooks wiring at app initialization
- `frontend/wailsjs/go/models.ts` — Updated generated models
- `frontend/wailsjs/go/playlist/Service.d.ts` — Updated generated bindings
- `frontend/wailsjs/go/playlist/Service.js` — Updated generated bindings
## Decisions Made
- Search is entirely client-side (rankTracks filters already-loaded tracks), so no backend SearchTracksByLibrary call needed — the library filter naturally scopes search results via the filtered track list
- Used native `<select>` element for dropdown rather than a custom Lit component — compact, accessible, matches search bar height
- Library filter resets on app restart (no localStorage persistence) per CONTEXT.md requirement
- `getAlbumsByArtistNameCached` returns null when library filter active to avoid stale cross-library data
- **Client-side search filtering:** rankTracks already operates on filtered track data from library store — no separate backend SearchTracksByLibrary call needed
- **Native `<select>` for library filter:** Compact, accessible, 32px height matching search bar, no custom dropdown overhead
- **getAlbumsByArtistNameCached returns null when filter active:** Forces backend query to avoid showing stale cross-library cached albums
- **ScanHooks callback for phantom resolution:** Mirrors established RemovalHooks/RescanHooks pattern — avoids circular dependency between library and playlist packages
- **M3U8-based phantom resolution over SQL-only:** Reads playlist files to match phantoms by both position and phantom_file_path — handles pre-existing phantoms (match by position) and new ones (match by stored path)
- **phantom_file_path column:** Stored at removal time so post-scan resolution can match even when M3U8 position changes
## Deviations from Plan
@@ -111,36 +157,59 @@ Each task was committed atomically:
**1. [Rule 3 - Blocking] Wails bindings not yet generated for ByLibrary methods**
- **Found during:** Task 1 (start)
- **Issue:** Plan 13-01 added Go methods but bindings weren't regenerated
- **Issue:** Plan 13-01 added Go methods but Wails bindings weren't regenerated
- **Fix:** Ran `wails generate module` before implementation
- **Files modified:** frontend/wailsjs/go/library/Library.d.ts, Library.js
- **Verification:** All ByLibrary imports resolve correctly
- **Committed in:** 42b8cf9
- **Committed in:** `42b8cf9`
**2. [Rule 1 - Bug] Info type uses lowercase property names (id, name) not uppercase**
**2. [Rule 1 - Bug] Info type uses lowercase property names**
- **Found during:** Task 1 (TypeScript typecheck)
- **Issue:** library-filter.ts used `lib.ID` and `lib.Name` but Wails-generated Info type uses `lib.id` and `lib.name`
- **Fix:** Changed to lowercase property access
- **Files modified:** frontend/src/components/library-filter/library-filter.ts
- **Verification:** `tsc --noEmit` passes cleanly
- **Committed in:** 42b8cf9
- **Committed in:** `42b8cf9`
**3. [Rule 1 - Bug] Virtualizer event delegation race condition**
- **Found during:** Checkpoint verification (Task 2)
- **Issue:** Pre-existing race from Phase 14-03 — event delegation on virtualizer failed when element wasn't rendered yet on firstUpdated()
- **Fix:** Added retry in updated() with guard flag; delegation happens once virtualizer exists
- **Files modified:** frontend/src/components/track-list/track-list.ts, frontend/src/components/queue-panel/queue-panel.ts
- **Verification:** Track list and queue panel click/context-menu events work reliably on app launch
- **Committed in:** `f05d2bb`
**4. [Rule 2 - Missing Critical] Phantom track auto-resolution after library scan**
- **Found during:** Checkpoint verification (Task 2)
- **Issue:** Phantom tracks not auto-resolved when library re-added and scanned — users would need to manually resolve each one
- **Fix:** Added phantom_file_path column (migration 7), store file_path on removal, ResolvePhantomTracksAfterScan via ScanHooks callback with M3U8 path comparison
- **Files modified:** backend/database/database.go, backend/database/sql/schemas/playlist_tracks.sql, backend/library/crud.go, backend/library/library.go, backend/playlist/playlist.go, backend/app.go
- **Verification:** Remove library → re-add → scan → phantoms automatically resolve to real tracks
- **Committed in:** `93262b9`, `9f595b7`
---
**Total deviations:** 2 auto-fixed (1 blocking, 1 bug)
**Impact on plan:** Both fixes required for compilation. No scope creep.
**Total deviations:** 4 auto-fixed (2 bugs, 1 blocking, 1 missing critical)
**Impact on plan:** All fixes essential for correctness. Virtualizer fix resolved pre-existing race condition exposed by multi-library testing. Phantom auto-resolution is critical UX — users should not need to manually fix playlist tracks after re-adding a library.
## Issues Encountered
None
None beyond the deviations documented above.
## User Setup Required
None - no external service configuration required.
## Next Phase Readiness
- All automated implementation complete — awaiting human verification checkpoint (Task 2)
- After verification: Phase 13 complete, multi-library support fully delivered
- v1.1 milestone only needs final Phase 13 checkpoint approval
- **Phase 13 complete**All 7 requirements verified (VIEW-01 through VIEW-04, PLAY-01 through PLAY-03)
- **v1.1 Multi-Library Support milestone complete** — Phases 9-13 all done
- **Phase 14 (Performance Optimization) already complete** — executed in parallel during v1.1 development
- All browse views, search, playlists, and phantom tracks working correctly in multi-library context
## Self-Check: PASSED
- All 10 key files exist on disk
- All 5 commits found in git log (42b8cf9, f05d2bb, 93262b9, 9f595b7, 70e3814)
- SUMMARY.md exists at expected path
---
*Phase: 13-library-views-phantom-tracks*