feat(ui): full-screen artwork viewer for album cover art - #440
Open
tsibog wants to merge 6 commits into
Open
Conversation
Contributor
Author
|
@codex review |
tsibog
added a commit
to tsibog/kaset
that referenced
this pull request
Aug 15, 2026
SwiftFormat lint flagged redundantViewBuilder on PlayerBar.swift:165. The Group already provides a @ViewBuilder context, so the attribute on the property is unnecessary. Fixes SwiftFormat CI failure on PR sozercan#440.
18 tasks
Add ArtworkViewer, a full-screen overlay accessible via the player bar thumbnail context menu (right-click > View Artwork). Shows the high-resolution thumbnail centered on a dark background with tap- to-dismiss and Escape support. Closes sozercan#432
fullScreenCover is unavailable on macOS. Use .overlay with a conditional ZStack and opacity transition instead. Also remove .ultraThinMaterial from background (AGENTS.md requires .glassEffect() for macOS 26+).
Claude Code review found .overlay constrained the viewer to PlayerBar's 60pt strip instead of full screen. .sheet provides proper full-screen layout, Escape dismissal, and key focus. Also guard nil artwork URL.
SwiftFormat lint flagged redundantViewBuilder on PlayerBar.swift:165. The Group already provides a @ViewBuilder context, so the attribute on the property is unnecessary. Fixes SwiftFormat CI failure on PR sozercan#440.
…s art The viewer passed a bare artwork URL to CachedAsyncImage with no fallback and no error branch, so any failed load left a spinner up forever. Every other thumbnail surface falls back to the public video thumbnail via SongThumbnailView; the viewer now does the same through ArtworkViewerSource, and shows an unavailable state when both sources fail. Sizing used maxWidth/maxHeight .infinity, which gave AppKit no ideal size and produced a small portrait sheet. Album art is square, so the viewer is now square and scaled against the active screen. Artwork was fetched via highQualityThumbnailURL, which caps at 226px, so the image was upscaled. Add URL.artworkURL(side:) to rewrite YouTube's dimension options and request 1200px, decoding at the display size. Failure handling keys on the pre-upgrade URL so the fallback chain still works.
tsibog
force-pushed
the
feat/artwork-viewer
branch
from
August 17, 2026 14:11
645f29c to
d796661
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds an artwork viewer accessible via the player bar thumbnail context menu (right-click > "View Artwork"). Shows the high-resolution album cover centered on a dark background. Dismiss with click, Escape, or the close button.
This keeps the existing click-to-navigate-to-album behavior on the thumbnail intact. The artwork viewer is a separate gesture (context menu) so both behaviors coexist.
AI Prompt (Optional)
🤖 AI Prompt Used
AI Tool: Claude
Type of Change
Related Issues
Closes #432
Depends on #447 for artwork that survives track changes
Changes Made
Sources/Kaset/Views/ArtworkViewer.swift— square artwork sheet withCachedAsyncImage, dark background, tap-to-dismiss, Escape support, and close buttonArtworkViewerSource: Resolves primary vs. fallback artwork, extracted so the selection logic is unit-testable (mirrors the existingSongThumbnailSourcepattern)PlayerBar.swift: Added@State showsArtworkViewer,.sheetpresentation, and the "View Artwork" context menu item on the thumbnail viewExtensions.swift: AddedURL.artworkURL(side:)to request full-size artworkLocalizable.xcstringsand all 17.lprojmirrorsArtworkViewerSourceTestsfor the fallback chain, plusExtensionsTestscoverage for the URL size rewritingFixes from manual testing
Three issues turned up while testing this on a packaged build:
1. Permanent spinner on some tracks. The viewer passed a bare URL to
CachedAsyncImagewith no fallback.CachedAsyncImagehas no error branch — on failure it keeps showingplaceholder(), so a failed load renders as a spinner that never resolves. Every other thumbnail surface avoids this by falling back toi.ytimg.com/vi/<id>/hqdefault.jpg, which is also why the small player-bar thumbnail still looked fine while the viewer hung. The viewer now uses the same fallback chain and shows an "Artwork Unavailable" state when both sources fail.The underlying cause of those failures —
currentTrack.thumbnailURLbeing set to the YouTube Music page URL — is a pre-existing bug onmainand is fixed separately in #447.2. Sheet was portrait, not square.
maxWidth/maxHeight: .infinitygave AppKit no ideal size, so the sheet collapsed to a small box and resolved width and height independently. Album art is square, so the viewer is now explicitly square and scaled against the active screen (62% of the shorter dimension, clamped 380–700).3. Artwork was upscaled. Art was fetched through
highQualityThumbnailURL, which only lifts list thumbnails to 226px, so the viewer displayed a soft, upscaled image.URL.artworkURL(side:)rewrites the dimension tokens in YouTube's option suffix (…=w544-h544-l90-rj→…=w1200-h1200-l90-rj) and the viewer decodes at its display size for a Retina-correct result. Hosts cap at the source resolution, so an oversized request degrades to the original artwork.highQualityThumbnailURLis untouched, so list thumbnails keep their cheap 226px fetches.Failure handling keys on the pre-upgrade URL, so the size rewriting does not break the fallback chain.
Screencast
kaset-440-artwork-viewer.mp4
Testing
swift test --skip KasetUITests) — 2963 tests in 233 suitesBuilt and tested locally on macOS 26 (Apple Silicon) in a packaged build: opened the viewer across multiple tracks and after track changes, confirmed the artwork is sharp and the sheet is square, and exercised all three dismissal paths.
Checklist
swiftlint --strict && swiftformat .— both cleanAdditional Notes
Uses
Color.black.opacity(0.85)for the background instead of.ultraThinMaterialper AGENTS.md coding rules. The context menu approach avoids conflicting with the existing click-to-navigate-to-album behavior on the thumbnail.This PR is functional on its own, but without #447 the artwork viewer will still show "Artwork Unavailable" on tracks whose thumbnail URL was corrupted by the observer script, since the fallback chain is the only thing rescuing them.