Merge pull request #396 from Ebullioscopic/fix-pr-385

Fix pr 385
This commit is contained in:
Hariharan Mudaliar 2026-04-05 21:09:16 +05:30 • committed by GitHub
commit d9742dbacd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 102 additions and 69 deletions

View file

@ -1009,9 +1009,9 @@ struct ContentView: View {
Image(nsImage: musicManager.albumArt) Image(nsImage: musicManager.albumArt)
.resizable() .resizable()
.aspectRatio(contentMode: .fit) .aspectRatio(contentMode: .fit)
.clipShape(RoundedRectangle(cornerRadius: musicManager.albumArt.size.width/musicManager.albumArt.size.height > 1.0 ? MusicPlayerImageSizes.cornerRadiusInset.closed/3.0 : MusicPlayerImageSizes.cornerRadiusInset.closed))
) )
.clipped() .clipped()
.clipShape(RoundedRectangle(cornerRadius: MusicPlayerImageSizes.cornerRadiusInset.closed))
.matchedGeometryEffect(id: "albumArt", in: albumArtNamespace) .matchedGeometryEffect(id: "albumArt", in: albumArtNamespace)
.albumArtFlip(angle: musicManager.flipAngle) .albumArtFlip(angle: musicManager.flipAngle)
albumArtBadge(for: secondary, badgeSize: badgeDisplaySize) albumArtBadge(for: secondary, badgeSize: badgeDisplaySize)

View file

@ -982,6 +982,9 @@
} }
} }
} }
},
"%0.1f" : {
}, },
"%d%%" : { "%d%%" : {
"localizations" : { "localizations" : {
@ -13767,6 +13770,7 @@
} }
}, },
"Enable album art parallax effect" : { "Enable album art parallax effect" : {
"extractionState" : "stale",
"localizations" : { "localizations" : {
"ru" : { "ru" : {
"stringUnit" : { "stringUnit" : {
@ -25883,6 +25887,9 @@
} }
} }
} }
},
"Parallax Effect Intensity" : {
}, },
"Pause" : { "Pause" : {
"localizations" : { "localizations" : {

View file

@ -296,23 +296,24 @@ struct LockScreenMusicPanel: View {
private func albumArtButton(size: CGFloat, cornerRadius: CGFloat) -> some View { private func albumArtButton(size: CGFloat, cornerRadius: CGFloat) -> some View {
Button(action: toggleExpanded) { Button(action: toggleExpanded) {
ZStack(alignment: .bottomTrailing) { ZStack(alignment: .bottomTrailing) {
albumArtImage(size: size, cornerRadius: cornerRadius) albumArtImage(size: size, cornerRadius: cornerRadius)
if showAppIcon, let icon = lockScreenAppIcon { if showAppIcon, let icon = lockScreenAppIcon {
icon icon
.resizable() .resizable()
.aspectRatio(contentMode: .fill) .aspectRatio(contentMode: .fit)
.frame(width: appIconSize, height: appIconSize) .frame(width: appIconSize, height: appIconSize)
.clipShape(RoundedRectangle(cornerRadius: appIconCornerRadius, style: .continuous))
.shadow(color: Color.black.opacity(0.35), radius: 6, x: 0, y: 4) .shadow(color: Color.black.opacity(0.35), radius: 6, x: 0, y: 4)
.offset(x: appIconOffset, y: appIconOffset) .offset(x: appIconOffset, y: appIconOffset)
.transition(.scale.combined(with: .opacity)) .transition(.scale.combined(with: .opacity))
}
} }
} .albumArtFlip(angle: musicManager.flipAngle)
.albumArtFlip(angle: musicManager.flipAngle) .parallax3D(enableOverride: lockScreenParallaxEnabled, suspended: isParallaxSuspended)
.parallax3D(magnitude: 12, enableOverride: lockScreenParallaxEnabled, suspended: isParallaxSuspended) .frame(width: size)
.frame(width: size, height: size) .background(albumArtBackground(cornerRadius: cornerRadius))
.background(albumArtBackground(cornerRadius: cornerRadius)) .clipShape(RoundedRectangle(cornerRadius: musicManager.albumArt.size.width/musicManager.albumArt.size.height > 1.0 ? appIconCornerRadius/3 : appIconCornerRadius, style: .continuous))
} }
.buttonStyle(PlainButtonStyle()) .buttonStyle(PlainButtonStyle())
.opacity(musicManager.isPlaying ? 1 : 0.4) .opacity(musicManager.isPlaying ? 1 : 0.4)
@ -1050,8 +1051,7 @@ struct LockScreenMusicPanel: View {
Image(nsImage: musicManager.albumArt) Image(nsImage: musicManager.albumArt)
.resizable() .resizable()
.aspectRatio(contentMode: .fit) .aspectRatio(contentMode: .fit)
.frame(width: size, height: size) .clipShape(RoundedRectangle(cornerRadius: musicManager.albumArt.size.width/musicManager.albumArt.size.height > 1.0 ? cornerRadius/3 : cornerRadius))
.clipShape(RoundedRectangle(cornerRadius: cornerRadius, style: .continuous))
} }
@ViewBuilder @ViewBuilder
@ -1082,7 +1082,7 @@ struct LockScreenMusicPanel: View {
} }
private var appIconCornerRadius: CGFloat { private var appIconCornerRadius: CGFloat {
isExpanded ? 18 : 10 isExpanded ? 18 : 12
} }
private var appIconOffset: CGFloat { private var appIconOffset: CGFloat {

View file

@ -63,6 +63,7 @@ struct MinimalisticMusicView: View {
// Lyrics under the author name (same font size as author) // Lyrics under the author name (same font size as author)
if enableLyrics { if enableLyrics {
lyricsLineView lyricsLineView
.font(.system(size: 11, weight: .regular))
} }
} }
.frame(width: geo.size.width, height: geo.size.height, alignment: .center) .frame(width: geo.size.width, height: geo.size.height, alignment: .center)
@ -93,9 +94,10 @@ struct MinimalisticMusicView: View {
Image(nsImage: musicManager.albumArt) Image(nsImage: musicManager.albumArt)
.resizable() .resizable()
.aspectRatio(contentMode: .fit) .aspectRatio(contentMode: .fit)
.clipShape(RoundedRectangle(cornerRadius: musicManager.albumArt.size.width/musicManager.albumArt.size.height > 1.0 ? 4 : 12))
) )
.clipped() .clipped()
.clipShape(RoundedRectangle(cornerRadius: 18)) // Dramatically increased corner radius for minimalistic mode
.albumArtFlip(angle: musicManager.flipAngle) .albumArtFlip(angle: musicManager.flipAngle)
.frame(width: max(0, vm.effectiveClosedNotchHeight - 12), height: max(0, vm.effectiveClosedNotchHeight - 12)) .frame(width: max(0, vm.effectiveClosedNotchHeight - 12), height: max(0, vm.effectiveClosedNotchHeight - 12))
} }

View file

@ -34,6 +34,7 @@ struct DynamicIslandHeader: View {
@Default(.enableTimerFeature) var enableTimerFeature @Default(.enableTimerFeature) var enableTimerFeature
@Default(.timerDisplayMode) var timerDisplayMode @Default(.timerDisplayMode) var timerDisplayMode
@Default(.showClipboardIcon) var showClipboardIcon @Default(.showClipboardIcon) var showClipboardIcon
@Default(.showColorPickerIcon) var showColorPickerIcon
@Default(.clipboardDisplayMode) var clipboardDisplayMode @Default(.clipboardDisplayMode) var clipboardDisplayMode
var body: some View { var body: some View {
@ -50,6 +51,8 @@ struct DynamicIslandHeader: View {
.blur(radius: vm.notchState == .closed ? 20 : 0) .blur(radius: vm.notchState == .closed ? 20 : 0)
.animation(.smooth.delay(0.1), value: vm.notchState) .animation(.smooth.delay(0.1), value: vm.notchState)
.zIndex(2) .zIndex(2)
.padding(8)
} }
if vm.notchState == .open && !Defaults[.enableMinimalisticUI] { if vm.notchState == .open && !Defaults[.enableMinimalisticUI] {
@ -128,7 +131,7 @@ struct DynamicIslandHeader: View {
} }
// ColorPicker button // ColorPicker button
if Defaults[.enableColorPickerFeature] { if Defaults[.enableColorPickerFeature] && showColorPickerIcon{
Button(action: { Button(action: {
switch Defaults[.colorPickerDisplayMode] { switch Defaults[.colorPickerDisplayMode] {
case .panel: case .panel:
@ -293,7 +296,7 @@ private extension DynamicIslandHeader {
Defaults[.settingsIconInNotch] Defaults[.settingsIconInNotch]
&& Defaults[.enableClipboardManager] && Defaults[.enableClipboardManager]
&& Defaults[.showClipboardIcon] && Defaults[.showClipboardIcon]
&& Defaults[.enableColorPickerFeature] && Defaults[.showColorPickerIcon]
&& Defaults[.enableTimerFeature] && Defaults[.enableTimerFeature]
} }
} }

View file

@ -374,7 +374,7 @@ struct MinimalisticMusicPlayerView: View {
private struct MinimalisticReminderEventListView: View { private struct MinimalisticReminderEventListView: View {
let reminders: [ReminderLiveActivityManager.ReminderEntry] let reminders: [ReminderLiveActivityManager.ReminderEntry]
private let textFont = Font.system(size: 13, weight: .semibold) private let textFont = Font.system(size: 13, weight: .regular)
private let separatorSpacing: CGFloat = 10 private let separatorSpacing: CGFloat = 10
var body: some View { var body: some View {
@ -1128,7 +1128,7 @@ struct MinimalisticAlbumArtView: View {
.background( .background(
Image(nsImage: musicManager.albumArt) Image(nsImage: musicManager.albumArt)
.resizable() .resizable()
.aspectRatio(contentMode: .fit) .scaledToFill()
) )
.clipped() .clipped()
.clipShape(RoundedRectangle(cornerRadius: 12)) .clipShape(RoundedRectangle(cornerRadius: 12))
@ -1142,18 +1142,21 @@ struct MinimalisticAlbumArtView: View {
Button { Button {
musicManager.openMusicApp() musicManager.openMusicApp()
} label: { } label: {
Color.clear Color.clear
.aspectRatio(1, contentMode: .fit) .aspectRatio(1, contentMode: .fit)
.background( .background(
Image(nsImage: musicManager.albumArt)
.resizable() Image(nsImage: musicManager.albumArt)
.aspectRatio(contentMode: .fit) .resizable()
) .aspectRatio(contentMode: .fit)
.clipped() .clipShape(RoundedRectangle(cornerRadius: musicManager.albumArt.size.width/musicManager.albumArt.size.height > 1.0 ? 4 : 12))
.clipShape(RoundedRectangle(cornerRadius: 12))
.matchedGeometryEffect(id: "albumArt", in: albumArtNamespace)
.albumArtFlip(angle: musicManager.flipAngle) )
.parallax3D(magnitude: 12) .clipped()
.matchedGeometryEffect(id: "albumArt", in: albumArtNamespace)
.albumArtFlip(angle: musicManager.flipAngle)
.parallax3D()
} }
.buttonStyle(PlainButtonStyle()) .buttonStyle(PlainButtonStyle())
.opacity(musicManager.isPlaying ? 1 : 0.4) .opacity(musicManager.isPlaying ? 1 : 0.4)

View file

@ -33,7 +33,6 @@ struct MusicPlayerView: View {
var body: some View { var body: some View {
HStack(alignment: .top, spacing: 12) { HStack(alignment: .top, spacing: 12) {
AlbumArtView(vm: vm, albumArtNamespace: albumArtNamespace) AlbumArtView(vm: vm, albumArtNamespace: albumArtNamespace)
.padding(.all, 5)
MusicControlsView() MusicControlsView()
.frame(maxWidth: .infinity, alignment: .leading) .frame(maxWidth: .infinity, alignment: .leading)
} }
@ -62,14 +61,17 @@ struct AlbumArtView: View {
Image(nsImage: musicManager.albumArt) Image(nsImage: musicManager.albumArt)
.resizable() .resizable()
.aspectRatio(contentMode: .fit) .aspectRatio(contentMode: .fit)
.clipShape(
RoundedRectangle(
cornerRadius: Defaults[.cornerRadiusScaling]
? musicManager.albumArt.size.width/musicManager.albumArt.size.height > 1.0 ? MusicPlayerImageSizes.cornerRadiusInset.opened/3 : MusicPlayerImageSizes.cornerRadiusInset.opened
: musicManager.albumArt.size.width/musicManager.albumArt.size.height > 1.0 ? MusicPlayerImageSizes.cornerRadiusInset.closed/3 : MusicPlayerImageSizes.cornerRadiusInset.closed
)
)
) )
.clipped() .clipped()
.clipShape(
RoundedRectangle(
cornerRadius: Defaults[.cornerRadiusScaling]
? MusicPlayerImageSizes.cornerRadiusInset.opened
: MusicPlayerImageSizes.cornerRadiusInset.closed)
)
.scaleEffect(x: 1.3, y: 1.4) .scaleEffect(x: 1.3, y: 1.4)
.rotationEffect(.degrees(92)) .rotationEffect(.degrees(92))
.blur(radius: 40) .blur(radius: 40)
@ -86,7 +88,9 @@ struct AlbumArtView: View {
appIconOverlay appIconOverlay
} }
.albumArtFlip(angle: musicManager.flipAngle) .albumArtFlip(angle: musicManager.flipAngle)
.parallax3D(magnitude: 12) .parallax3D()
.padding(.bottom, -5)
} }
.buttonStyle(PlainButtonStyle()) .buttonStyle(PlainButtonStyle())
.scaleEffect(musicManager.isPlaying ? 1 : 0.85) .scaleEffect(musicManager.isPlaying ? 1 : 0.85)
@ -110,15 +114,18 @@ struct AlbumArtView: View {
Image(nsImage: musicManager.albumArt) Image(nsImage: musicManager.albumArt)
.resizable() .resizable()
.aspectRatio(contentMode: .fit) .aspectRatio(contentMode: .fit)
.clipShape(
RoundedRectangle(
cornerRadius: Defaults[.cornerRadiusScaling]
? musicManager.albumArt.size.width/musicManager.albumArt.size.height > 1.0 ? MusicPlayerImageSizes.cornerRadiusInset.opened/3 : MusicPlayerImageSizes.cornerRadiusInset.opened
: musicManager.albumArt.size.width/musicManager.albumArt.size.height > 1.0 ? MusicPlayerImageSizes.cornerRadiusInset.closed/3 : MusicPlayerImageSizes.cornerRadiusInset.closed
)
) // ensures that even the rectagular album cover is rounded just like the MacOS media player
} }
.matchedGeometryEffect(id: "albumArt", in: albumArtNamespace) .matchedGeometryEffect(id: "albumArt", in: albumArtNamespace)
.clipped() .clipped()
.clipShape(
RoundedRectangle(
cornerRadius: Defaults[.cornerRadiusScaling]
? MusicPlayerImageSizes.cornerRadiusInset.opened
: MusicPlayerImageSizes.cornerRadiusInset.closed)
)
} }
@ViewBuilder @ViewBuilder
@ -126,8 +133,8 @@ struct AlbumArtView: View {
if vm.notchState == .open && !musicManager.usingAppIconForArtwork { if vm.notchState == .open && !musicManager.usingAppIconForArtwork {
AppIcon(for: musicManager.bundleIdentifier ?? "com.apple.Music") AppIcon(for: musicManager.bundleIdentifier ?? "com.apple.Music")
.resizable() .resizable()
.aspectRatio(contentMode: .fill) .scaledToFill()
.frame(width: 30, height: 30) .frame(width: 36, height: 36)
.offset(x: 10, y: 10) .offset(x: 10, y: 10)
.transition(.scale.combined(with: .opacity).animation(.bouncy.delay(0.3))) .transition(.scale.combined(with: .opacity).animation(.bouncy.delay(0.3)))
.zIndex(2) .zIndex(2)
@ -207,9 +214,9 @@ struct MusicControlsView: View {
MarqueeText( MarqueeText(
lyricsBinding, lyricsBinding,
font: .headline, font: .system(size: 12, weight: .regular),
nsFont: .headline, nsFont: .headline,
textColor: .white.opacity(0.8), textColor: .white.opacity(0.7),
minDuration: 0.35, minDuration: 0.35,
frameWidth: width frameWidth: width
) )
@ -605,6 +612,7 @@ struct NotchHomeView: View {
.combined(with: .blurReplace.animation(.smooth.speed(0.9))) .combined(with: .blurReplace.animation(.smooth.speed(0.9)))
.combined(with: .move(edge: .top))) .combined(with: .move(edge: .top)))
.blur(radius: vm.notchState == .closed ? 30 : 0) .blur(radius: vm.notchState == .closed ? 30 : 0)
.padding(Defaults[.enableMinimalisticUI] ? 0 : 8) //Putting the main padding for home view here for consistency
} }
private var minimalisticOverridePayload: ExtensionNotchExperiencePayload? { private var minimalisticOverridePayload: ExtensionNotchExperiencePayload? {
@ -713,6 +721,7 @@ struct MusicSliderView: View {
LiveStreamProgressIndicator(tint: sliderTint) LiveStreamProgressIndicator(tint: sliderTint)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.frame(height: sliderFrameHeight) .frame(height: sliderFrameHeight)
case .inline: case .inline:
HStack(spacing: 10) { HStack(spacing: 10) {
Spacer() Spacer()
@ -720,6 +729,7 @@ struct MusicSliderView: View {
LiveStreamProgressIndicator(tint: sliderTint) LiveStreamProgressIndicator(tint: sliderTint)
.frame(maxWidth: .infinity) .frame(maxWidth: .infinity)
.frame(height: sliderFrameHeight) .frame(height: sliderFrameHeight)
Spacer() Spacer()
.frame(width: 48) .frame(width: 48)
} }
@ -748,10 +758,10 @@ struct MusicSliderView: View {
) )
} }
private var sliderTint: Color { private var sliderTint: Color {//
switch Defaults[.sliderColor] { switch Defaults[.sliderColor] {
case .albumArt: case .albumArt:
return Color(nsColor: color).ensureMinimumBrightness(factor: 0.8) return Color(nsColor: color).ensureMinimumBrightness(factor: 0.6)
case .accent: case .accent:
return .accentColor return .accentColor
case .white: case .white:

View file

@ -2444,6 +2444,9 @@ struct Media: View {
@Default(.lockScreenMusicAlbumParallaxEnabled) private var lockScreenMusicAlbumParallaxEnabled @Default(.lockScreenMusicAlbumParallaxEnabled) private var lockScreenMusicAlbumParallaxEnabled
@Default(.showStandardMediaControls) private var showStandardMediaControls @Default(.showStandardMediaControls) private var showStandardMediaControls
@Default(.autoHideInactiveNotchMediaPlayer) private var autoHideInactiveNotchMediaPlayer @Default(.autoHideInactiveNotchMediaPlayer) private var autoHideInactiveNotchMediaPlayer
@Default(.parallaxEffectIntensity) private var parallaxEffectIntensity
@ObservedObject private var musicManager = MusicManager.shared @ObservedObject private var musicManager = MusicManager.shared
private var isAppleMusicActive: Bool { private var isAppleMusicActive: Bool {
@ -2589,10 +2592,18 @@ struct Media: View {
Text("Enable lyrics") Text("Enable lyrics")
} }
.settingsHighlight(id: highlightID("Enable lyrics")) .settingsHighlight(id: highlightID("Enable lyrics"))
Defaults.Toggle(key: .enableParallaxEffect) {
Text("Enable album art parallax effect") //Parallax Effect Intensity to control how much parallax is wanted
Slider(value: $parallaxEffectIntensity, in: 0...12, step: 1.0) {
HStack {
Text("Parallax Effect Intensity")
Spacer()
Text("\(parallaxEffectIntensity, specifier: "%0.1f")")
.foregroundStyle(.secondary)
}
} }
.settingsHighlight(id: highlightID("Enable album art parallax effect")) .settingsHighlight(id: highlightID("Enable album art parallax effect"))
Picker("Sneak Peek Style", selection: $sneakPeekStyles){ Picker("Sneak Peek Style", selection: $sneakPeekStyles){
ForEach(SneakPeekStyle.allCases) { style in ForEach(SneakPeekStyle.allCases) { style in
Text(style.rawValue).tag(style) Text(style.rawValue).tag(style)

View file

@ -31,7 +31,6 @@ struct TabButton: View {
var body: some View { var body: some View {
Button(action: onClick) { Button(action: onClick) {
Image(systemName: icon) Image(systemName: icon)
.padding(.horizontal, 15)
.contentShape(Capsule()) .contentShape(Capsule())
} }
.buttonStyle(PlainButtonStyle()) .buttonStyle(PlainButtonStyle())

View file

@ -109,7 +109,7 @@ struct TabSelectionView: View {
return tabsArray return tabsArray
} }
var body: some View { var body: some View {
HStack(spacing: 0) { HStack(spacing: 24) {
ForEach(Array(tabs.enumerated()), id: \.element.id) { idx, tab in ForEach(Array(tabs.enumerated()), id: \.element.id) { idx, tab in
let isSelected = isSelected(tab) let isSelected = isSelected(tab)
let activeAccent = tab.accentColor ?? .white let activeAccent = tab.accentColor ?? .white

View file

@ -22,17 +22,16 @@ import AppKit
import Defaults import Defaults
struct ParallaxMotionModifier: ViewModifier { struct ParallaxMotionModifier: ViewModifier {
var magnitude: Double
var enableOverride: Bool? var enableOverride: Bool?
var isSuspended: Bool var isSuspended: Bool
@Default(.enableParallaxEffect) var enableParallaxEffect @Default(.parallaxEffectIntensity) var parallaxEffectIntensity
@State private var offset: CGSize = .zero @State private var offset: CGSize = .zero
@State private var isHovering = false @State private var isHovering = false
@State private var viewSize: CGSize = .zero @State private var viewSize: CGSize = .zero
func body(content: Content) -> some View { func body(content: Content) -> some View {
if isSuspended || !(enableOverride ?? enableParallaxEffect) { if isSuspended || !(enableOverride ?? (parallaxEffectIntensity > 0.0)) {
content content
} else { } else {
content content
@ -69,11 +68,11 @@ struct ParallaxMotionModifier: ViewModifier {
} }
} }
.rotation3DEffect( .rotation3DEffect(
.degrees(offset.height * magnitude), // Y movement rotates around X axis .degrees(offset.height * parallaxEffectIntensity), // Y movement rotates around X axis
axis: (x: 1, y: 0, z: 0) axis: (x: 1, y: 0, z: 0)
) )
.rotation3DEffect( .rotation3DEffect(
.degrees(offset.width * -magnitude), // X movement rotates around Y axis (inverted to look naturally) .degrees(offset.width * -parallaxEffectIntensity), // X movement rotates around Y axis (inverted to look naturally)
axis: (x: 0, y: 1, z: 0) axis: (x: 0, y: 1, z: 0)
) )
.scaleEffect(isHovering ? 1.02 : 1.0) // Subtle scale up on hover .scaleEffect(isHovering ? 1.02 : 1.0) // Subtle scale up on hover
@ -83,10 +82,9 @@ struct ParallaxMotionModifier: ViewModifier {
extension View { extension View {
func parallax3D( func parallax3D(
magnitude: Double = 10,
enableOverride: Bool? = nil, enableOverride: Bool? = nil,
suspended: Bool = false suspended: Bool = false
) -> some View { ) -> some View {
modifier(ParallaxMotionModifier(magnitude: magnitude, enableOverride: enableOverride, isSuspended: suspended)) modifier(ParallaxMotionModifier(enableOverride: enableOverride, isSuspended: suspended))
} }
} }

View file

@ -779,7 +779,7 @@ extension Defaults.Keys {
static let sneakPeekStyles = Key<SneakPeekStyle>("sneakPeekStyles", default: .standard) static let sneakPeekStyles = Key<SneakPeekStyle>("sneakPeekStyles", default: .standard)
static let showSneakPeekOnTrackChange = Key<Bool>("showSneakPeekOnTrackChange", default: true) static let showSneakPeekOnTrackChange = Key<Bool>("showSneakPeekOnTrackChange", default: true)
static let enableFullscreenMediaDetection = Key<Bool>("enableFullscreenMediaDetection", default: true) static let enableFullscreenMediaDetection = Key<Bool>("enableFullscreenMediaDetection", default: true)
static let enableParallaxEffect = Key<Bool>("enableParallaxEffect", default: true) static let parallaxEffectIntensity = Key<Double>("parallaxEffectIntensity", default: 6.0)
static let waitInterval = Key<Double>("waitInterval", default: 3) static let waitInterval = Key<Double>("waitInterval", default: 3)
static let showShuffleAndRepeat = Key<Bool>("showShuffleAndRepeat", default: true) static let showShuffleAndRepeat = Key<Bool>("showShuffleAndRepeat", default: true)
static let showMediaOutputControl = Key<Bool>("showMediaOutputControl", default: true) static let showMediaOutputControl = Key<Bool>("showMediaOutputControl", default: true)