Implement Apple Music-style mini player#246
Conversation
There was a problem hiding this comment.
Pull request overview
Note
Copilot was unable to run its full agentic suite in this review.
Adds a native Apple Music–style Mini Player window and integrates it with app/window state, settings, keyboard/menu commands, and tests.
Changes:
- Introduces a new
MiniPlayerWindowUI andMiniPlayerWindowControllerto manage a floating window with compact/expanded/lyrics layouts. - Extends
PlayerServiceandPlayerServiceProtocolwith mini-player visibility/mode/panel state and restore-intent handling, plus new tests/mocks. - Adds a “Keep Mini Player on Top” setting and a
⇧⌘Mkeyboard shortcut/menu item to switch to the Mini Player.
Reviewed changes
Copilot reviewed 16 out of 16 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/keyboard-shortcuts.md | Documents new ⇧⌘M shortcut for Mini Player. |
| Tests/KasetTests/PlayerServiceTests.swift | Adds tests for mini-player visibility/mode/panel and keep-on-top persistence. |
| Tests/KasetTests/Helpers/MockPlayerService.swift | Updates mock to support new mini-player protocol surface. |
| Sources/Kaset/Views/PlayerBar.swift | Adds toolbar button to switch/toggle Mini Player. |
| Sources/Kaset/Views/MiniPlayerViews.swift | Adds Mini Player window UI (controls, lyrics/queue panes, hover chrome). |
| Sources/Kaset/Views/LyricsView.swift | Makes header and width configurable for reuse inside Mini Player. |
| Sources/Kaset/Views/GeneralSettingsView.swift | Adds “Keep Mini Player on Top” toggle. |
| Sources/Kaset/Utilities/AccessibilityIdentifiers.swift | Adds identifiers for Mini Player UI and PlayerBar button. |
| Sources/Kaset/Services/SettingsManager.swift | Persists keepMiniPlayerOnTop setting. |
| Sources/Kaset/Services/Protocols.swift | Extends PlayerServiceProtocol with mini-player state and actions. |
| Sources/Kaset/Services/Player/PlayerService.swift | Adds mini-player state types and observable properties. |
| Sources/Kaset/Services/Player/PlayerService+PlaybackControls.swift | Implements open/toggle/close and panel toggling behavior. |
| Sources/Kaset/MiniPlayerWindowController.swift | New controller managing the floating mini-player NSWindow lifecycle, sizing, and z-level. |
| Sources/Kaset/KasetApp.swift | Wires mini-player state changes to window show/hide and adds menu command + shortcut. |
| Sources/Kaset/AppDelegate.swift | Ensures Dock/reopen actions foreground mini player when switched. |
| Scripts/build-app.sh | Exits early when building unsigned, skipping signing steps. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
|
Addressed the Copilot review feedback in What changed:
Validation:
|
|
@copilot what's left for merging? Is it a manual process? |
|
@sozercan could you please check this PR? |
|
@ComicBit thanks for the pr! i will review when i can |
# Conflicts: # Sources/Kaset/Views/GeneralSettingsView.swift
|
Addressed the latest review feedback in 392c558. Changes:
Verification:
|
# Conflicts: # Scripts/build-app.sh
| private func isAuxiliaryPlayerWindow(_ window: NSWindow) -> Bool { | ||
| window.identifier?.rawValue == AccessibilityID.VideoWindow.container || | ||
| window.identifier?.rawValue == AccessibilityID.MiniPlayer.container | ||
| } |
| let restoreMainWindow = self.restoreMainWindow | ||
| let shouldRestoreMainWindow = self.playerService?.closeMiniPlayer() ?? false | ||
| self.close() | ||
|
|
||
| if shouldRestoreMainWindow { | ||
| restoreMainWindow?() | ||
| } | ||
| } | ||
|
|
||
| func returnToMainWindowFromUserAction() { | ||
| let restoreMainWindow = self.restoreMainWindow | ||
| _ = self.playerService?.closeMiniPlayer(restoringMainWindow: true) ?? false | ||
| self.close() | ||
| restoreMainWindow?() |
| static let lyricsView = "miniPlayer.lyricsView" | ||
| static let queueButton = "miniPlayer.queue" | ||
| static let airplayButton = "miniPlayer.airplay" | ||
| static let volumeSlider = "miniPlayer.volumeSlider" |
|
Addressed the new review comments in 36a5272. Changes:
Verification:
|
Description
Adds a native Apple Music-style mini player for Kaset. The mini player is a separate floating macOS window that reuses the existing
PlayerService, hidden playback WebView, artwork loading, lyrics service, queue state, and transport controls instead of embedding YouTube Music UI.The implementation focuses on the Apple Music interaction model:
Switch to Mini Playerhides the main Kaset window, closing or toggling back restores it, Dock activation keeps focus on the mini player while switched, and the mini player supports compact, square artwork, and lyrics-expanded presentations.AI Prompt (Optional)
AI Prompt Used
AI Tool: ChatGPT / Codex
Type of Change
Related Issues
N/A
Changes Made
MiniPlayerWindowControllerfor the floating native mini-player window, including lifecycle cleanup, frame persistence, accessibility identifier, and keep-on-top window level support.PlayerServicefor opening, closing, toggling, switch-mode restore behavior, compact/expanded panel state, and one-shot main-window restore requests.Switch to Mini Playerwindow command and removed the separate auxiliaryMini Playercommand so there is only one mini-player entry point.Keep Mini Player on Topsetting and General Settings checkbox.docs/keyboard-shortcuts.md.KASET_SIGNING=unsigned.Testing
swift test --filter PlayerServiceTests)Also verified with:
swift buildswiftlint --strictswiftformat .UI tests were not run because the repository requires explicit human permission before launching UI tests.
Checklist
swiftlint --strict && swiftformat .Screenshots
Additional Notes
The implementation intentionally keeps playback in the existing singleton hidden WebView for DRM compatibility and builds the mini-player as native SwiftUI window chrome and controls on top of current playback state.