Move search indicator from absolute-positioned overlay into sort toolbar
(or dedicated search-bar-row for artists/genres views). Shows indicator
on empty-state screens. Fix cover art not displaying for expanded album
tracks by checking expandedAlbumId before the albumName guard. Add
ScanWarning model bindings.
- Add FavoritesController and classMap imports
- Add .fav-icon CSS with compact sizing (18px/11px) for dropdown context
- Insert heart/star icon between track number and title
- Click toggles favorite with stopPropagation to avoid track selection
Add live progress reporting during library scans:
- Pre-walk count: fast WalkDir to count audio files upfront for
percentage calculation (~1-2s overhead)
- Progress ticker: emits ScanProgress events every 300ms with
phase, file counts (added/skipped/updated), and total
- Phase labels: counting → scanning → thumbnails → orphans
- Frontend: progress bar with percentage, file counts breakdown,
and phase indicator in both config-page and library-manager
Replaces the static 'Scanning...' text with a live progress bar
showing e.g. '62% — Scanning... 1,247 / 2,013 files (891 new,
356 skipped)'
- Add height: 49px and overflow: hidden to .track-item CSS
- Add overflow: hidden to .track-details CSS
- Fixed height ensures lit-virtualizer _scrollSize is stable from first render
- Prevents scrollbar lag on large queues (20k+ tracks) caused by dynamic item size averaging
repeat() as child content of lit-virtualizer bypasses virtualization,
rendering ALL items instead of only visible ones. This caused 2+ minute
loading times and UI freezing with large libraries.
The correct approach: use lit-virtualizer's native .renderItem and
.keyFunction properties which integrate with its scroll-based viewport
management.
- cover-grid dynamic text tiers use --yj-text-xs/sm/md/lg tokens
- track-list sort toolbar, row text, and fav icon use design tokens
- queue-panel header and track text use type scale tokens
- track-details, track-info, artist-details, genre-details use tokens
- designTokens imported and prepended to static styles in all 8 files
- Sidebar: convert all em-based padding/gap to px values, icon sizes to --yj-icon-md
- Now-playing: cover placeholder icon to --yj-icon-lg, text sizes to type tokens
- Search-bar: search icon to --yj-icon-sm, input font to --yj-text-md
- Audio-player: convert em-based gap/padding/margin to px values
- Seek-bar: tooltip font to --yj-text-lg, margin from em to px
- Volume-control: padding/margin from em to px, thumb sizes from em to px
- All components import designTokens and include in static styles
- Hoist searchCtrl.term lookup outside per-column loop in renderTrackRow
- Search highlighting already short-circuits when search term is empty
- Import classMap in queue-panel.ts
- Replace array filter/join class construction with classMap in renderTrackItem
- Eliminates per-row array allocation in queue panel render hot path
- Import classMap from lit/directives/class-map.js
- Replace array filter/join class construction with classMap for track-row
- Convert fav-icon conditional class to classMap
- Convert cell alignment classes to classMap
- Eliminates per-row array allocation and string join in render hot path
- cover-grid: all 3 virtualizers (single, before-split, after-split) use repeat() with album.ID key
- artists-view uses repeat() with artist.ID key
- genres-view uses repeat() with genre.name key
- Removed gridKeyFunction (now inlined in repeat calls)
- Removed .renderItem and .keyFunction properties from all virtualizers
- New tokens.css.ts exports designTokens with --yj-icon-sm/md/lg and --yj-text-xs/sm/md/lg/xl
- Values derived from existing pixel sizes scattered across codebase
- :host scoped for component-level adoption via static styles array
- Library store notify() coalesces multiple calls per microtask tick into one subscriber notification
- Search input debounces store propagation by 150ms while keeping instant visual feedback
- Empty search input clears immediately for responsive UX
- Remove eagerFetch() call from LibraryStore constructor
- Add deferEagerFetch() that waits for DOMContentLoaded event
- App shell renders before backend data roundtrips begin
- All 4 data types (tracks, albums, artists, genres) still loaded eagerly
- invalidate() still calls eagerFetch() directly for post-scan refresh
- Add backend/events/cmd/genevents/main.go using go/ast for deterministic output
- Add //go:generate directive to backend/events/events.go
- Generate frontend/src/events.ts with all 21 constants including LibraryConfigChanged
- Atomic file writes via temp file + rename
- Comment groups preserved with trailing period stripping
- playlist-picker checks for duplicates before adding tracks
- playlist-view drag-drop handler checks for duplicates before adding
- Both show duplicate-tracks-dialog when duplicates found
- Non-duplicate tracks added directly when no duplicates exist
- Step through duplicate tracks one-by-one with Add/Skip buttons
- Apply to all remaining toggle for batch operations
- Dispatches playlist-action-complete when done
- Follows project wa-dialog patterns from track-details and phantom-resolver
- Add pinDefault state, getter, setter to favorites-store
- Add pinDefault getter and setPinDefault to favorites-controller
- Update sortedEntries in playlist-view to pin default playlist to top
- Add Pin to Top toggle in config page Favorites section
- Add Wails bindings for GetPinDefaultPlaylist/SetPinDefaultPlaylist
- React to PinDefault in FavoritesConfigChanged event payload
- Replace static list icon with conditional: favorites icon for default playlist, nothing for others
- Reduce playlist-body left padding from 42px to 32px for tighter alignment without icon
- Add 'set-default' case in onPlaylistContextAction handler
- Add wa-dropdown-item with star icon in single-select guard block
- Option only appears when right-clicking a single playlist
- Update handlePlaylistContextMenu to respect multi-selection on right-click
- Make onPlaylistContextAction async with batch delete support
- Show 'Delete N Playlists' in context menu when multiple playlists selected
- Hide 'Rename' option when multiple playlists are selected
- Clear playlist selection after any context action completes
- Add selectedPlaylists Set and lastSelectedPlaylistIndex for multi-select state
- Replace handleToggle with handlePlaylistHeaderClick supporting Ctrl/Shift+Click
- Clear playlist selection when entering track selection scope
- Clear playlist selection on outside clicks
- Add .playlist-header.selected CSS with selection background color
- Wire header click to new handler with selected class in template
- Wails bindings reflect new PlaylistFilePicker and ImportPlaylists signatures
- Frontend handleImportPlaylist now passes file array to ImportPlaylists