Changelog
Pulled straight from the project's
CHANGELOG.md.
The format follows
Keep a Changelog;
versions follow SemVer.
Changelog
All notable changes to Bòcan are documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
0.7.0 (2026-05-09)
### Added
- scrobble: add pending indicator to transport strip (#175) (38b6dd1)
- scrobble: add Show Recent Scrobbles menu item and keyboard shortcut (#176) (f56c484)
- scrobble: implement RecentScrobblesView (#174) (5a120c3)
- ui: add drag-to-resize handle to VisualizerPane (#168) (5c57be3)
- ui: add now-playing overlay to visualizer pane and fullscreen (#169) (fea3627)
- ui: auto-simplify visualizer mode on sustained FPS drop (#172) (15134b1)
- ui: multi-display screen picker for fullscreen visualizer (#171) (94f9947)
- ui: remove Fluid Metal visualizer (fe50923)
- ui: show lyrics source badge in pane header (eadc958)
### Fixed
- audio: bypass EQ at Flat preset; skip redundant ramp tasks (d38dc5d)
- audio: bypass TimePitch at unity rate by default; add pump starvation logging (75b9770)
- audio: eliminate CoreAudio render-thread pops + community health files (978c0ad)
- audio: eliminate render-thread overhead and IIR pop sources (7bade91)
- audio: ensure each spectrum bar reads unique FFT bins (902c9df)
- audio: increase I/O buffer size to 1024 frames for pop resilience (1b398e6)
- audio: suppress file_length lint violation in AudioEngine.swift (f751bfb)
- library: pass resolved album title to LRClib fetch (218f01a)
- playback: honour startingAt when shuffle is enabled (a317121)
- playback: strip BookmarkBlob from queue persistence payload to prevent audio pops (255d7a8)
- ui: add .help() tooltips and .accessibilityHint to ScrobbleSettingsView (105363d)
- ui: add .help() tooltips and full accessibility labels to font size picker (d5381c1)
- ui: add accessibility labels and tooltips to ConnectSheet (bd43b60)
- ui: add lyrics actions to context menu and menu bar (c26c692)
- ui: add lyrics sync-offset slider to pane header (f3d878f)
- ui: add manual LRClib fetch and replace-lyrics buttons (a99e3db)
- ui: correct Edit Lyrics tooltip shortcut hint in pane header (a89c3cd)
- ui: detect LRC format when saving editor lyrics (7f1da9e)
- ui: fix Fluid Metal particle physics — correct bass direction, add ambient turbulence (9432d9c)
- ui: fix visualizer disconnect after track changes and size flutter (c352c6e)
- ui: make Fluid Metal audio reactivity direct and eliminate 30s burst (84c1ed2)
- ui: observe lyricsVM and visualizerVM in BocanCommands so menu labels update (7f5cf71)
- ui: ref-count tap so closing fullscreen doesn’t disconnect pane audio (1ecf01a)
- ui: remove ignoresSafeArea from VisualizerHost black fill (e03d212)
- ui: require confirmation before deleting lyrics in editor sheet (f131e9e)
- ui: show spinner instead of empty state while fetching lyrics (a48449f)
- ui: split lyrics pane header into two rows, add drag-to-resize handle (9b4fc61)
- ui: stop 60fps menu rebuilds from causing audio pops (902c9df)
- ui: stop Fluid Metal particles when no audio is playing (642ccbb)
- ui: unify lyrics font-size AppStorage key (6927e13)
- ui: use IOKit power source for battery detection in VisualizerViewModel (#170) (befa8c3)
- ui: wire audio analysis into Fluid Metal renderer each display tick (050daa3), closes #167
- ui: wire lyrics pane search bar to LyricsView filtering (ad01607)
### Changed
- audio: split AudioEngine.swift into extension files to fix file_length lint (d6942b8)
0.6.0 (2026-05-07)
### Added
- app: prompt before quit when scan or RG analysis is active (321ceb4)
- persistence,ui: add local backup with configurable rolling count (4e0c879)
- persistence,ui: wire iCloud backup toggle into Advanced Settings (2a15615)
- Phase 10 polish — mini player, quit guard, iCloud & local backups (0c5d06e)
- ui: add collapse/expand toggle to Playlists sidebar section (d6ef432)
- ui: add LoadingState and ErrorState reusable views (#145) (812feda)
- ui: implement MarqueeText scrolling for Mini Player and menu-bar extra (1e5e36f), closes #138
- ui: show scanning progress pane during initial library scan (854a18e)
- ui: spring-animate mini player layout transitions (ecbed96)
### Fixed
- audio: prevent pops from VFS contention and CPU bursts at playback start (c12c1ce)
- persistence: use requiresWriteAccess to avoid WAL snapshot deadlock (c6fda5e)
- ui: call windowMode.restoreIfNeeded() on launch to honour restore-last-mode setting (dc2a3f1), closes #139
- ui: convert HighContrastModifier comments to doc comments for SwiftFormat (5b36fdf)
- ui: inject libraryViewModel into DSP window; remove About from Settings tabs (6187626)
- ui: menu bar extra icon reflects playback state (7a311fd), closes #143
- ui: strengthen separators and materials under accessibilityIncreaseContrast (#141) (66b4f0e)
0.5.1 (2026-05-06)
### Fixed
- build: add stable Homebrew HEADER_SEARCH_PATHS to project.yml (a5099da)
- persistence: use .async(onQueue:main) scheduler to fix GRDB writer-queue deadlock (a2a8575)
0.5.0 (2026-05-06)
### Added
- ui: add accessibilityIdentifier to all NowPlayingStrip controls (2c20f4b)
- ui: add Compute Missing ReplayGain to Tools menu (8b43c9f)
- ui: add keyboard shortcuts for volume control (⌘↑/⌘↓) (346178b)
- ui: add mute button to transport bar (⌘⌥Z) (cc46904)
- ui: add Play Album, Shuffle Album, Play Artist to context menu and Track menu bar (8451bbf)
- ui: add Play Now, Play Next, Add to Queue to Track menu bar (9a8cc0d)
- ui: add Playback Speed menu and keyboard shortcuts (9f1aebb)
- ui: add Rate submenu to track context menu; remove dead ContextMenus.swift (2d28b80)
- ui: add Select All (⌘A) and Deselect All (⌘⇧A) to Track menu and table (e449c60)
- ui: add Sleep Timer submenu to Playback menu bar (4a54e16)
- ui: make artwork in NowPlayingStrip navigate to current album (05a2ef6)
- ui: make track title and artist clickable in NowPlayingStrip (23e2a5c)
- ui: previous button restarts track after 3 seconds (iTunes semantics) (ff79723)
- ui: replace DSP modal sheet with non-modal floating window (3d1650e)
### Fixed
- ci: redirect codesign stderr so hardened runtime grep works (cf03508)
- persistence: remove spurious await from DatabaseWriter.backup call (c43e521)
- persistence: set GRDB targetQueue to .userInitiated to prevent priority inversion (43ff5b2)
- scrobble: remove spurious await from synchronous authorisationURL call (1204638)
- ui: add VoiceOver accessibility labels to track table (b665a18)
- ui: copy action now includes all visible track fields as TSV (#98) (ceff30c)
- ui: DSP button shows persistent active state when EQ is processing (cd159cc)
- ui: fix love context-menu label for multi-track selection (2918bf1)
- ui: fix Swift 6 concurrency errors in MainWindowTracker.Coordinator (603fbaa)
- ui: migrate NowPlayingViewModel from ObservableObject to @Observable (#113) (e12e380)
- ui: migrate RouteViewModel to @Observable, remove @ObservedObject from usage sites (9d2fb83)
- ui: migrate TracksViewModel to @Observable to eliminate publish-during-update warnings (b385a2b)
- ui: persist playback rate to UserDefaults and restore on launch (636602c)
- ui: remove duplicate fade-out toggle from custom sleep timer popover (5e07473)
- ui: remove redundant as? URL cast in ArtworkEditor drop handler (c88d72b)
- ui: replace NSAlert.runModal() with non-blocking beginSheetModal continuations (1909661)
- ui: silence nonisolated(unsafe) warning on RouteViewModel.consumer (693dcf8)
- ui: use textTertiary for idle speed label instead of 0.4 opacity (84caa5d)
- ui: wire Love/Unlove context menu to toggleLovedForCurrentSelection (07e25f5)
- ui: wire Return/Enter key to play in track table (9ebed66)
0.4.0 (2026-05-04)
### Added
- tests: add DSPViewModel environment to NowPlayingStrip snapshots (4f2c448)
- ui: add ⌘⌥E keyboard shortcut and menu item for EQ/DSP panel (#94) (37d314b)
- ui: implement per-track and per-album EQ scope picker (#91) (7a1da10)
### Fixed
- audio: flush EQ IIR delay lines before un-bypass to prevent pop (#92) (5e82403)
- ui: A/B compare is press-and-hold, not a toggle (#93) (fe2379d)
- ui: add full EQ/Effects/ReplayGain tabs to DSP Settings view (#89) (3bf54a4)
- ui: eliminate ‘publishing during view update’ warnings in EQ scope picker (#95) (7b41aeb)
- ui: wire EQ output gain slider to preset mutation (#90) (8c18761)
0.3.0 (2026-05-04)
### Added
- playback: wire CrossfadeScheduler end-to-end (#87) (7a4f5ce)
- settings: add embed cover art preference (phase-8 audit H5) (08312f1), closes #67
- ui: add Bulk Actions section to multi-track editor (phase-8 audit H6) (0d987b1), closes #69
- ui: add CommandMenu(“Tools”) with batch cover art and duplicate finder (ea16b84), closes #68
- ui: add Compute Replay Gain to Track menu and right-click context menu (#88) (0f99e39)
- ui: add explicit Tab-key focus order to tag editor Details tab (#80) (768dc7d)
- ui: add File Info and Advanced tabs to tag editor (phase-8 audit) (ce942d0), closes #66
- ui: add Identify Track toolbar button (#83) (cf69fab)
- ui: add per-field apply-checkboxes for multi-track tag editing (cc9817c), closes #70
- ui: detect LRC timestamps in lyrics tab, save as synced lyrics (#74) (145a6a8)
- ui: show conflict-resolution banner in TagEditorSheet (#73) (2bde72e)
### Fixed
- app: declare playlist-drag UTType in Info.plist, fix conflict log level (3085d67)
- audio: ramp bass-boost gain/bypass to prevent audio pop (#86) (e4bb57f)
- dsp: improve EQ bypass transitions to prevent audible pops (f09e2d8)
- library: auto-renew stale security-scoped bookmarks on resolution (317f61c)
- library: hasCoverArt smart rule checks albums.cover_art_hash not tracks (e8ab684)
- library: stamp fileMtime/fileSize after tag write to prevent false-positive conflict (2340b1e)
- library: upgrade http CAA image URLs to https to satisfy ATS (73856a0)
- playback: fire-and-forget nowPlaying to unblock 15s playback delay (206cd24)
- ui,library: folder-not-found flash + recurring startup conflicts (5583df3)
- ui,library: properly fix folder-not-found flash and conflict re-flagging (ce4767e)
- ui,persistence: crash on playlist with duplicate track entries (9a0fdb9)
- ui: acquire security-scoped resource in .fileImporter completion (#78) (fa44a2f)
- ui: add .accessibilityLabel to TextField in TagFieldRow and IntFieldRow (#77) (bb091d7)
- ui: add .help() to CandidatePickerView buttons (#83) (394d099)
- ui: add .help() to IdentifyTrackSheet Close button (#83) (43e6a65)
- ui: add .help() tooltip to all ArtworkEditor action buttons (#82) (04d8816)
- ui: add low-confidence warning banner in CandidatePickerView (#83) (bb33629)
- ui: Edit Tags button in noMatchView opens tag editor (#83) (07730de)
- ui: enhance PlaylistSidebarViewModel to handle missing nodes gracefully (f09e2d8)
- ui: guard fieldBinding setter to prevent publish-during-render fault in lyrics tab (05881ce)
- ui: observe sidebar VM in ContentPane so isLoaded triggers re-render (90f6a34)
- ui: refactor TrackTable to simplify scroll view creation (f09e2d8)
- ui: remove duplicate ⌘I shortcut from context menu Get Info button (#81) (d9e2f9b)
- ui: replace DispatchQueue.main.async with Task in ArtworkEditor.handleDrop (a7b59c3), closes #71
- ui: replace NSOpenPanel.runModal() with .fileImporter() in ArtworkEditor (#76) (b7990ed)
- ui: streamline TrackTableCoordinator’s data handling (f09e2d8)
### Changed
0.2.0 (2026-05-01)
### Added
- acoustics: implement phase 8.5 AcoustID fingerprinting & MusicBrainz auto-tagging (2228b28)
- albums: artist + track count + exclude-from-shuffle toggle (b621b3b)
- app: add BocanApp entry point, RootView, resources, and UI test scaffold (3514516)
- app: expand Playback menu with Next/Previous/Shuffle/Repeat/Stop-After-Current/Clear Queue/Up Next (6fdbdab)
- app: phase-2 audit fixes #5 + #6 — vacuum on quit, iCloud backup at launch (c005e75)
- app: wire ⌘⇧N to File ▸ New Playlist… (1132639), closes #31
- audio: add AudioEngine module and integrate into project structure (511b965)
- audio: add AudioEngine module with AVFoundation and FFmpeg decoders (14b98aa)
- audio: implement Phase 9 DSP chain — EQ, crossfeed, stereo expander, limiter, ReplayGain (1f09183)
- docs: add post-phase checks for debugging and feature completeness (6432d16)
- enable column-header sorting on full Songs library (75ba115)
- import: add media to library — folder picker, file picker, drag-drop, scan banner (037aa46)
- library,ui: sort playlist contents by title / artist / date added (76122c9)
- library: add hasLyrics smart-playlist field (5ccac49)
- library: add inLastYears smart-playlist comparator (3b59d85)
- library: add Library scanning module (FileWalker, ChangeDetector, TrackImporter, FSWatcher, ScanCoordinator, LibraryScanner) (026d823)
- library: add PlaylistService with sparse-position reorder + folders (6204bcc)
- library: cap smart-playlist group nesting at 3 levels (133eebb)
- library: debounce smart playlist observation storms (a40fc7b)
- library: forbid smart-playlist refs from in_playlist rules (94712cd)
- library: graceful decode of unknown smart-playlist fields (a91f345)
- library: implement Phase 7 smart playlists (b7ffcca)
- library: live FSEvents watcher + move sources to Settings (54e68ed)
- library: Phase 5.5 – add/remove/rescan media, Track Inspector, force gapless per album (f796f30)
- library: scaffold playlist import/export (M3U, PLS, XSPF, CUE, iTunes XML) (12adfd5)
- library: snapshot mode for non-live smart playlists (ea56a01), closes #48
- lyrics: Phase 11 — lyrics display, editing, and LRClib fetch (fdc3b20)
- metadata: add TagLibBridge ObjC++ wrapper and Metadata Swift module (TagReader, ReplayGain, LRCParser, CoverArtExtractor) (7f3db4f)
- metadata: implement full metadata editor (phase 08) (09d5b41)
- metadata: preserve raw year/date tag text (cc4e5ed)
- mini-player: add shuffle toggle to all three layouts (0fd3263)
- mini-player: add track info button and album to compact/square layouts (11d27b7)
- mini-player: match main player order; add repeat & stop-after toggles (80807f4)
- observability: add AppLogger, LogCategory, Redaction, Telemetry, MetricKitListener (bd38754)
- persistence: add M012 scrobble dead-letter, unique queue index, submissions table (f746327)
- persistence: add M013 CUE virtual-track columns (bdfaae1)
- persistence: add Persistence module with SQLite/GRDB schema, repositories, FTS, and observation (418d965)
- persistence: add playlist kind + accent_color (M007) (1ddae2b)
- persistence: expose FTS search and smart-folder queries on repositories (dc8d65b)
- persistence: M002 migration — library_roots table and Track phase-3 fields (70567b1)
- playback: add Playback module — queue, shuffle, repeat, gapless, history, persistence (07d406e)
- playback: add Route, RouteManager, and CoreAudio output-device observer (d2f06e1)
- playback: expose ScrobbleSink hook from PlayHistoryRecorder + QueuePlayer (a1ea1ca)
- playback: persist and restore playback position across launches (f0dfa32)
- playback: Phase 5 – stop-after-current, playAlbum/playArtist, context menus, NowPlayingTests, gapless fixtures (1dfc97b)
- playlists: add smart reshuffle seed and creation-flow parity (9ccd43a)
- queue: show proper title/artist/genre in Up Next; fix percent-encoded filenames (53f9d41)
- scrobble: add Scrobble module with rules, providers, queue worker, service (389b2e3)
- scrobble: send now-playing on track start (4a9b42a)
- settings: add Smart Playlists preferences section (5f7a003)
- settings: add VSCode settings for terminal usage (9f9c78f)
- smart-playlists: implement true snapshot mode with refresh timestamp (7ae77a0)
- tracks: improved column layout, default sort, and column persistence (2f8bc9e)
- ui: add AirPlay route picker to now-playing strip (6cdd9ff)
- ui: add Sample Rate column to Songs table (a683b8d)
- ui: add UI module with library browser, search, and now-playing strip (6cd1194)
- ui: animated bouncing-bars now-playing indicator in QueueView (02c1ce6)
- ui: confirmation alerts before Remove from Library / Move to Trash (Phase 5 audit) (3795557)
- ui: fire track-change notifications when app is in background (4554240)
- ui: implement Phase 10 — mini player, speed control, sleep timer, settings window (7e32d1c)
- ui: manual playlist sidebar, detail view, and creation sheets (c8bac16)
- ui: mini player window toggling + menu bar extra wiring (69b68bc)
- ui: move scan progress to Library settings (9ff67e9)
- ui: new Songs table columns and Go-to context menu (5f3764b)
- ui: per-field opt-in selection for AcoustID identify sheet (5d6b86a)
- ui: persist expanded playlist folders in UIStateV2 (10e92b1)
- ui: persist Songs table column customization across launches (c968afc)
- ui: Phase 10 polish + fix @AppStorage startup freeze (5629308)
- ui: playlist cover art mosaic, user cover, and accent colour (d164107)
- ui: playlist drag-and-drop reparent to folder (05073be)
- ui: playlist import/export sheets and menu commands (da10c4a)
- ui: playlist picker for smart-playlist membership rules (bfe35cc), closes #49
- ui: polish playlist and smart-playlist creation flows (7d43a1b)
- ui: redesign Get Info window (f85d0a2)
- ui: richer Up Next row context menu (6fdbdab)
- ui: route playlist folders to dedicated PlaylistFolderView (a5cd060)
- ui: scrobble settings, connect sheet, app wiring (eb9a1a4)
- ui: toast on Re-scan success, error sheet on failure (Phase 5.5 audit M2) (8551904)
- ui: update app name and copyright in Info.plist; add settings.local.json for permissions (8a9a0c1)
- ui: wire drag-reorder for manual playlist tracks (cef2b9b), closes #30
- ui: wire QueuePlayer — Up Next view, transport controls, context menus (6b788ad)
- ui: wire up SleepTimerMenu Custom… button (e334587)
- use bundled AppIcon.icns for the app icon (a13abfb)
- visualizer: add fullscreen triggers — pane button + ⌘⇧F menu item (3834313)
- visualizer: implement Phase 12 — audio visualizer with Metal particle system (83d25ad)
### Fixed
- acoustics: bundle libchromaprint and add Homebrew sandbox exception (ebab225)
- acoustics: correct capitalized(with:) label in titleCased helper (d084814)
- acoustics: patch fpcalc rpath to resolve libchromaprint from Homebrew (e6dc546)
- acoustics: self-contained fpcalc bundle + AcoustID bug fixes (edd9885)
- acoustics: wire fpcalc binary, AcoustID key, and Secrets.xcconfig into build (40d3d28)
- app: declare Local Network usage and Bonjour services for AirPlay (cd4f57e)
- app: raise Task.detached priority to .userInitiated to prevent startup freeze (2c671f5)
- app: resolve MainActor deadlock and Swift 6 Sendability in app init (7fa5c85)
- app: stop CPU runaway and restore tracks view (ee108a3)
- audio: add userInitiated executor to FFmpegDecoder (9794760)
- audio: anti-pop fades, stereo-layout helper, insertion-point protocol (0926ea5)
- audio: convert AVFoundationDecoder to actor at userInitiated QoS (0cd4c7c)
- audio: don’t crash on negative frame delta in AVFoundationDecoder (7656e70)
- audio: fix ffError lint violations — optional_data_string_conversion, trailing_closure, file_length (d03ad2a)
- audio: guard empty sample input in EBUR128 and ReplayGain analyzer (deca1b2)
- audio: lower AudioEngine actor QoS to default to avoid priority inversions (a355dae)
- audio: move playerNode reconnection before engine.prepare() (ac205cc)
- audio: reconnect full playerNode→eq→mixer chain at hardware rate (2d9eee9)
- audio: reconnect playerNode at hardware rate after engine.prepare() (89d95a6)
- audio: resample decoded buffers to hardware rate in BufferPump (5c662d9)
- audio: resolve AVAudioFormat Sendable error and deprecated String(cString:) warning (500ee34)
- audio: resolve engine-not-running, pump deadlock, and resume judder (01bc19a)
- audio: return Bool from Task closures in AudioTapTests for Swift 6 Sendable (448a23e)
- audio: silence Swift 6 Sendable-capture warning in FormatConverter (97d3bf6)
- build errors in TrackTable + coordinator for Swift 6 strict concurrency (639dbc4)
- build: bundle Resources into app, declare Library dependency (1cf639d)
- build: rewrite @rpath refs to @loader_path in bundle-fpcalc script (b48fec9)
- build: run xcodegen generate after bundle-fpcalc (1cc07cd)
- column-header sort in NSTableView TrackTable (d43e2b3)
- docs: update minimum macOS version to 26.0 (Tahoe) (d122566)
- dsp: equaliser band sliders now persist and update the engine (ef51f4c)
- import: resolve scan infinite spinner, @retroactive conformances, nonisolated warning (f3194ae)
- library: allow tag editing for files added as individual roots (32c8d49)
- library: canonicalize symlinks via realpath in quick-scan seed (26fe1d8)
- library: disabled tracks disappear from FTS search; Remove From Library preserves search (1ebc627)
- library: handle unknown smart criteria enums safely (e0237e5)
- library: keep security scopes active for the duration of a scan (38ec74c)
- library: link cover art to albums, not just tracks (2fc121b)
- library: phase-3 audit H6 — multi-value tags + extended_tags column (b1cdda6)
- library: phase-3 audit high fixes (H1-H5, H7, H8) (8ec8e4e)
- library: phase-3 audit medium fixes (M1-M4) + L4 a11y (cbae7fb)
- library: re-enable user-edited tracks on rescan (00c0bdc)
- library: register UserDefaults defaults for all @AppStorage keys (dea4212)
- library: remove root soft-deletes tracks; FSEvents triggers UI reload (a921100)
- library: repair FSWatcher event delivery; clean Library warnings (7c1ffa5)
- library: replace deprecated String(cString:); restrict ARCHS to arm64 (6035c51)
- library: rescan security scope, disabled filter, gapless URL, inspector window (fd31970)
- library: scope change-detection to scanned roots only (e4327ef)
- library: skip redundant DB reads for smart playlists; suppress cancel error (eb17840)
- library: smart playlists exclude disabled tracks (09efdca)
- library: Unicode-aware case-insensitive text comparators (3d41284)
- library: Unrated preset matches NULL and 0 ratings (7f82670)
- lint: resolve all pre-existing SwiftLint violations (ee9b5fa)
- lyrics: move Show Lyrics to Window menu, fix LRClib bypass, implement file embed (b4f530d)
- lyrics: update non-goals section to clarify translation status (547c469)
- lyrics: wire LRClib auto-fetch on track change (d0d1352)
- menu,lyrics: stop menu redraw; source priority; auto-show pane (303e584)
- menu: extract commands to Commands struct to stop menu bar flashing (14db0dc)
- metadata-editor: Get Info shows correct rating, loved, and excluded-from-shuffle (f8237ed)
- metadata,library: phase-3 audit critical fixes (C1, C2, C3) (d9d409b)
- mini-player: increase compact layout height to 130 pt (f6dafef)
- mini-player: raise main window when info button is clicked (fc1b012)
- now-playing: show current track title/artist/artwork during playback (ef7f2df)
- observability: remove test for MXMetricPayload which is unavailable on macOS (c93c10d)
- observability: remove unavailable MXMetricPayload on macOS and fix OSSignpostIntervalState Sendable (f95a01a)
- persistence: phase-2 audit fixes #1–#4, #6, #7, #17 (be6bcc9)
- persistence: pin ValueObservation.start to non-MainActor scheduler (13695e1)
- persistence: update migration test assertions for M002 (c9cd53c)
- phase-1: debug audio view, WavPack fixture, TSan scheme patch (50ca5b9)
- playback: auto-load queue item on play, root-scope fallback for nil bookmarks, play-all from library (54f2fff)
- playback: credit outgoing play on gapless handoff so it scrobbles (454c9c2)
- playback: drop redundant ‘await’ on non-async engine.state access (7bbdf3e)
- playback: fallback to root bookmark when per-file bookmark is stale (766502f)
- playback: forward button, auto-repeat, gapless sandbox scope, UI sync (7df5457)
- playback: guard against double-advance when gapless transition + stale .ended race (4bc9da9)
- playback: harden bookmark fallback in QueuePlayer (3c90c7a)
- playback: honour gapless settings and detect missing restored items (95ac08f)
- playback: log stale root bookmark refresh failure instead of silencing (8cfa1c9)
- playback: pre-shuffle items before queue load so first track is random (3475f8f)
- playback: prevent double-song, stale-queue, and forward-button failures (17cbd7d)
- playback: queue full library on track play; wire volume slider to engine (16ff006)
- playback: replace lastGaplessAdvanceItemID with timestamp settle window (970dc4f)
- playback: restart exhausted queue on play; fix 1-item queue race; fix empty symbol fault (46a78bc)
- playback: root bookmark fallback was never matching due to URL vs path comparison (e6a80b7)
- playback: stop queue wrapping to index 0 on forward; add now-playing indicator (08976c6)
- plist: remove duplicate CFBundleIconFile key (7ad7402)
- prevent spurious queue.replace racing with track-end callbacks (2f4e3cd)
- project: disable sandbox/hardened-runtime on UITests target to fix ad-hoc signing mismatch (0039f30)
- project: update LastUpgradeCheck and MACOSX_DEPLOYMENT_TARGET values (36cb2bd)
- resolve Sendable warning in FormatConverter and pause blert (4a657cf)
- run AudioEngine and BufferPump actors at user-initiated QoS (2688f4d)
- search: correct reactivity, focus loss, and post-navigation state (83fd515)
- search: prefix matching per token; fix artwork frame in result rows (a3b82ca)
- search: stable focus via overlay; artist name + artwork in track rows (b6d159d)
- search: use .searchable() to permanently fix toolbar focus (0b9fa81)
- smart-playlists: show integer stepper for inLastDays / inLastMonths (d4eac1c)
- speed selector 1x corruption and playlist click passthrough (e829b39)
- tag-editor: normalise artist/album FKs on save; fix cover art, reload, and UI polish (d81e1b4)
- tests: fix snapshot flakiness under --enable-code-coverage (9ff1ef3)
- tooling: disable modifier_order lint rule to resolve SwiftFormat/SwiftLint conflict on nonisolated (aa59730)
- tooling: remove stale result bundle before test and add pipefail to Makefile (c97b3c7)
- tracks: bump UIState key to v2 to clear persisted addedAt sort (f45d83c)
- ui,audio: per-band spectrum normalization; fix UI test deps (0ee4721)
- ui: add @MainActor to notification delegate callbacks; add diagnostic logging (369c02e)
- ui: add hover text and accessibility metadata for normal playlist controls (d3dd773)
- ui: add hover text and accessibility metadata for smart rule editor controls (7f6cd5a)
- ui: announce Up Next sidebar row is also a drop target (Phase 5 audit L4) (7671e28)
- ui: attach playlist sheets to PlaylistSidebarSection; wire Add to Playlist menu (6806f37)
- ui: clarify Add Files / Add Folder picker copy (Phase 5.5 audit L1) (64ce199)
- ui: clearer ScanBanner summary wording with locale-aware numbers (Phase 5.5 audit M1) (535c97e)
- ui: copy artwork to temp dir before creating UNNotificationAttachment (e6759ff)
- ui: correct database filename in Reveal in Finder button (8532fc8)
- ui: cover art, self-load races, publishing warnings, styled artists/genres (1622b9e)
- ui: currently-playing row now highlights live, drop waveform icon (9e92952)
- ui: date-based default smart playlist names with sibling collision suffix (df25722)
- ui: defer onDidDelete to next tick to prevent crash during sheet dismiss (571871e)
- ui: defer selection publish in tableViewSelectionDidChange (17907da)
- ui: drag tracks to playlists, move-to-folder menu, confirm recursive delete (7a44cd0)
- ui: finish Phase 5 queue UX (drop-on-Up-Next, opt-dbl-click, missing items) (0e6c39d)
- ui: fix new-playlist-from-selection adding tracks and double-commit guard (b07acc5)
- ui: hoist playlist sidebar sheets onto parent List (819ceeb), closes #63
- ui: keep Artwork strictly square regardless of image aspect (db7fc12)
- ui: live watch toggle, transport hints, album multi-select & info, playlist menu (e7bba30)
- ui: make Track menu items reactive; fix sandbox file access in tag editor (e0afdad)
- ui: move Folders sidebar section below Playlists (0f8c937)
- ui: offer permanent-delete fallback when trash fails (Phase 5.5 audit M3) (c97f22f)
- ui: patch album in-memory instead of full reload on settings toggle (e3d4f03)
- ui: persist sidebar width, add Album shuffle, wire ⌘F to search (208acb5)
- ui: prevent hang when sorting large Songs table (491634a)
- ui: prewarm playlist sheet host to reduce first-surface audio hitch risk (b7ec875)
- ui: prewarm smart playlist sheets to reduce first-mount audio hitch risk (74ee75b)
- ui: QueueRow accessibility hint + double-click activation (Phase 5 audit L5) (c744884)
- ui: QueueView empty state offers Add-Music-Folder on fresh install (Phase 5 audit L*) (39602a3)
- ui: re-selection dead zone, UI test window query, cheap artist track count (fe5c40a)
- ui: remove .onDrag from Table cell — breaks row hit-testing (fdb989a)
- ui: remove auto-injected ‘Mini Player’ item from Window menu (0e6d73c)
- ui: remove dual-sort feedback loop in Songs table (923bc8f)
- ui: remove duplicate sidebar toggle button (0b82126)
- ui: resolve artist/album columns, file picker, playback bookmark, post-scan refresh (6b00dfb)
- ui: resolve menu shortcut conflicts and wire launchAtLogin (2783eb2)
- ui: row density and notification improvements (bd7f83b)
- ui: run Songs table sort off the main actor (6dda18b)
- ui: ScanBanner Cancel + Dismiss tooltips and a11y hints (Phase 5 audit L6) (bba3c11)
- ui: scrubber commits seek on release, not on every mouse move (69093b6)
- ui: set shuffle mode on QueuePlayer when Shuffle is pressed in SmartPlaylistDetailView (50d1e90)
- ui: simplify Songs table sort to avoid race conditions (0972828)
- ui: SleepTimerMenu accessibility — tooltips and a11y hint (Phase 5 audit L5) (da8104c)
- ui: stop crashing on launch when seeding sidebar autosave (7e86cec)
- ui: use app accent palette for shuffle/repeat/stop-after buttons (422c616)
- ui: use NSApp.appearance for theme switching to fix half-repaint bug (a5b92cd)
- ui: wire appearance settings to app UI (2a648d8)
- ui: wire TagEditorSheet to Get Info and ⌘I (2f4886e)
- visualizer: pass real AudioSamples to oscilloscope renderer (78f0060)
- visualizer: replace NSCursor.hide/unhide with setHiddenUntilMouseMoves (bbf8079)
- visualizer: rework FluidMetal — additive blending, larger points, correct HSV (8e3034d)
### Changed
- playback: eliminate DB round-trips when queueing full library (315b003)
- search: in-place filtering via the current view’s VM (e2ec51e)
- ui: coalesce scan progress + non-blocking Add Folder/Files panels (Phase 5.5 audit L2) (6c472b3)
- ui: remove unused read-only TrackInspectorPanel (Phase 5.5 audit H5) (b27fdb6)
- ui: replace PlaylistDetailView custom list with TracksView (d9be477)