Working - need to fix ui issues

This commit is contained in:
Hariharan Mudaliar 2025-10-04 22:24:37 +05:30
commit fed1a43f88
12 changed files with 510 additions and 65 deletions

View file

@ -33,6 +33,9 @@ struct ContentView: View {
// Dynamic sizing based on view type and graph count with smooth transitions
var dynamicNotchSize: CGSize {
// Use minimalistic or normal size based on settings
let baseSize = Defaults[.enableMinimalisticUI] ? minimalisticOpenNotchSize : openNotchSize
// Always return the target size for the current view to ensure smooth transitions
let targetSize: CGSize
@ -48,14 +51,14 @@ struct ContentView: View {
if enabledGraphsCount > 3 {
// Expand height for 4+ graphs
targetSize = CGSize(
width: openNotchSize.width,
height: openNotchSize.height + 65 // Add extra height for second row
width: baseSize.width,
height: baseSize.height + 65 // Add extra height for second row
)
} else {
targetSize = openNotchSize
targetSize = baseSize
}
} else {
targetSize = openNotchSize
targetSize = baseSize
}
return targetSize
@ -85,25 +88,35 @@ struct ContentView: View {
@Default(.showNotHumanFace) var showNotHumanFace
@Default(.useModernCloseAnimation) var useModernCloseAnimation
@Default(.enableMinimalisticUI) var enableMinimalisticUI
private let extendedHoverPadding: CGFloat = 30
private let zeroHeightHoverPadding: CGFloat = 10
// Use minimalistic corner radius ONLY when opened, keep normal when closed
private var activeCornerRadiusInsets: (opened: (top: CGFloat, bottom: CGFloat), closed: (top: CGFloat, bottom: CGFloat)) {
if enableMinimalisticUI {
// Keep normal closed corner radius, use minimalistic when opened
return (opened: minimalisticCornerRadiusInsets.opened, closed: cornerRadiusInsets.closed)
}
return cornerRadiusInsets
}
var body: some View {
ZStack(alignment: .top) {
let mainLayout = NotchLayout()
.frame(alignment: .top)
.padding(.horizontal, vm.notchState == .open
? Defaults[.cornerRadiusScaling] ? (cornerRadiusInsets.opened.top - 5) : (cornerRadiusInsets.opened.bottom - 5)
: cornerRadiusInsets.closed.bottom
? Defaults[.cornerRadiusScaling] ? (activeCornerRadiusInsets.opened.top - 5) : (activeCornerRadiusInsets.opened.bottom - 5)
: activeCornerRadiusInsets.closed.bottom
)
.padding([.horizontal, .bottom], vm.notchState == .open ? 12 : 0)
.background(.black)
.mask {
((vm.notchState == .open) && Defaults[.cornerRadiusScaling])
? NotchShape(topCornerRadius: cornerRadiusInsets.opened.top, bottomCornerRadius: cornerRadiusInsets.opened.bottom)
? NotchShape(topCornerRadius: activeCornerRadiusInsets.opened.top, bottomCornerRadius: activeCornerRadiusInsets.opened.bottom)
.drawingGroup()
: NotchShape(topCornerRadius: cornerRadiusInsets.closed.top, bottomCornerRadius: cornerRadiusInsets.closed.bottom)
: NotchShape(topCornerRadius: activeCornerRadiusInsets.closed.top, bottomCornerRadius: activeCornerRadiusInsets.closed.bottom)
.drawingGroup()
}
.padding(.bottom, vm.notchState == .open && Defaults[.extendHoverArea] ? 0 : (vm.effectiveClosedNotchHeight == 0)
@ -589,7 +602,7 @@ struct ContentView: View {
@ViewBuilder
var dragDetector: some View {
if Defaults[.dynamicShelf] {
if Defaults[.dynamicShelf] && !Defaults[.enableMinimalisticUI] {
Color.clear
.frame(maxWidth: .infinity, maxHeight: .infinity)
.contentShape(Rectangle())

View file

@ -349,9 +349,27 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}
private func calculateRequiredNotchSize() -> CGSize {
// Check if inline sneak peek is showing and notch is closed
let isInlineSneakPeekActive = vm.notchState == .closed &&
coordinator.expandingView.show &&
(coordinator.expandingView.type == .music || coordinator.expandingView.type == .timer) &&
Defaults[.enableSneakPeek] &&
Defaults[.sneakPeekStyles] == .inline
// If inline sneak peek is active, use a wider width to accommodate the expanded content
if isInlineSneakPeekActive {
// Calculate required width for inline sneak peek:
// Album art (~32) + Middle section (380) + Visualizer (~32) + padding = ~450
let inlineSneakPeekWidth: CGFloat = 450
return CGSize(width: inlineSneakPeekWidth, height: vm.effectiveClosedNotchHeight)
}
// Use minimalistic or normal size based on settings
let baseSize = Defaults[.enableMinimalisticUI] ? minimalisticOpenNotchSize : openNotchSize
// Only apply dynamic sizing when on stats tab and stats are enabled
guard coordinator.currentView == .stats && Defaults[.enableStatsFeature] else {
return openNotchSize
return baseSize
}
let enabledGraphsCount = [
@ -363,16 +381,16 @@ class AppDelegate: NSObject, NSApplicationDelegate {
].filter { $0 }.count
// Calculate height based on layout: 1-3 graphs = single row, 4+ graphs = two rows
var requiredHeight = openNotchSize.height
var requiredHeight = baseSize.height
if enabledGraphsCount >= 4 {
// Two rows needed - add height for second row plus spacing
let extraHeight: CGFloat = 120 + 12 // Graph height + spacing
requiredHeight = openNotchSize.height + extraHeight
requiredHeight = baseSize.height + extraHeight
}
// Width stays constant - no horizontal expansion
return CGSize(width: openNotchSize.width, height: requiredHeight)
return CGSize(width: baseSize.width, height: requiredHeight)
}
func applicationDidFinishLaunching(_ notification: Notification) {
@ -423,6 +441,15 @@ class AppDelegate: NSObject, NSApplicationDelegate {
self?.debouncedUpdateWindowSize()
}.store(in: &cancellables)
// Observe minimalistic UI setting changes - trigger window resize
Defaults.publisher(.enableMinimalisticUI, options: []).sink { [weak self] change in
// Force sneak peek to standard mode when minimalistic UI is enabled
if change.newValue == true && Defaults[.sneakPeekStyles] != .standard {
Defaults[.sneakPeekStyles] = .standard
}
self?.debouncedUpdateWindowSize()
}.store(in: &cancellables)
// Observe screen recording settings changes
Defaults.publisher(.enableScreenRecordingDetection, options: []).sink { _ in
if Defaults[.enableScreenRecordingDetection] {

View file

@ -6,6 +6,10 @@
},
" – %lld" : {
},
"-%@" : {
"comment" : "A label displaying the duration of the current song. The argument is the duration of the song in seconds.",
"isCommentAutoGenerated" : true
},
"'Now Playing' was the only option on previous versions and works with all media apps." : {
@ -867,6 +871,9 @@
},
"min" : {
},
"Minimalistic mode focuses on media controls and system HUDs, hiding all extra features for a clean, focused experience. Automatically enables simpler animations." : {
},
"Minimum hover duration" : {
@ -1362,6 +1369,10 @@
},
"Sneak Peek Style" : {
},
"Sneak peek style is locked to Standard in minimalistic mode" : {
"comment" : "A message displayed when the sneak peek style is locked to standard in minimalistic UI mode.",
"isCommentAutoGenerated" : true
},
"Software updates" : {
"localizations" : {
@ -1510,6 +1521,9 @@
},
"Two-finger swipe up on notch to close, two-finger swipe down on notch to open when **Open notch on hover** option is disabled" : {
},
"UI Mode" : {
},
"Uninstall" : {

View file

@ -0,0 +1,83 @@
//
// MinimalisticMusicView.swift
// DynamicIsland
//
// Created for minimalistic UI mode
// A clean, focused music player for closed notch state
//
import SwiftUI
import Defaults
struct MinimalisticMusicView: View {
@EnvironmentObject var vm: DynamicIslandViewModel
@ObservedObject var musicManager = MusicManager.shared
@State private var isHovering: Bool = false
var body: some View {
HStack(spacing: 0) {
// Left: Album Art
albumArtView
// Middle: Song Title (scrolling if needed)
Rectangle()
.fill(.black)
.overlay(
HStack(alignment: .center) {
if !musicManager.songTitle.isEmpty {
MarqueeText(
.constant(musicManager.songTitle),
textColor: Defaults[.coloredSpectrogram] ? Color(nsColor: musicManager.avgColor) : Color.gray,
minDuration: 0.4,
frameWidth: 100
)
}
}
)
.frame(width: vm.closedNotchSize.width)
// Right: Music Visualizer
visualizerView
}
.frame(height: vm.effectiveClosedNotchHeight + (isHovering ? 8 : 0), alignment: .center)
.onHover { hovering in
isHovering = hovering
}
}
// MARK: - Album Art
private var albumArtView: some View {
HStack {
Color.clear
.aspectRatio(1, contentMode: .fit)
.background(
Image(nsImage: musicManager.albumArt)
.resizable()
.aspectRatio(contentMode: .fill)
)
.clipped()
.clipShape(RoundedRectangle(cornerRadius: 18)) // Dramatically increased corner radius for minimalistic mode
.frame(width: max(0, vm.effectiveClosedNotchHeight - 12), height: max(0, vm.effectiveClosedNotchHeight - 12))
}
.frame(width: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)), height: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)))
}
// MARK: - Visualizer
private var visualizerView: some View {
HStack {
Rectangle()
.fill(Defaults[.coloredSpectrogram] ? Color(nsColor: musicManager.avgColor).gradient : Color.gray.gradient)
.frame(width: 50, alignment: .center)
.mask {
AudioSpectrumView(isPlaying: $musicManager.isPlaying)
.frame(width: 16, height: 12)
}
.frame(width: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)),
height: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)), alignment: .center)
}
.frame(width: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)),
height: max(0, vm.effectiveClosedNotchHeight - (isHovering ? 0 : 12)), alignment: .center)
}
}

View file

@ -20,20 +20,22 @@ struct DynamicIslandHeader: View {
var body: some View {
HStack(spacing: 0) {
HStack {
if (!tvm.isEmpty || coordinator.alwaysShowTabs) && Defaults[.dynamicShelf] {
TabSelectionView()
} else if vm.notchState == .open {
EmptyView()
if !Defaults[.enableMinimalisticUI] {
HStack {
if (!tvm.isEmpty || coordinator.alwaysShowTabs) && Defaults[.dynamicShelf] {
TabSelectionView()
} else if vm.notchState == .open {
EmptyView()
}
}
.frame(maxWidth: .infinity, alignment: .leading)
.opacity(vm.notchState == .closed ? 0 : 1)
.blur(radius: vm.notchState == .closed ? 20 : 0)
.animation(.smooth.delay(0.1), value: vm.notchState)
.zIndex(2)
}
.frame(maxWidth: .infinity, alignment: .leading)
.opacity(vm.notchState == .closed ? 0 : 1)
.blur(radius: vm.notchState == .closed ? 20 : 0)
.animation(.smooth.delay(0.1), value: vm.notchState)
.zIndex(2)
if vm.notchState == .open {
if vm.notchState == .open && !Defaults[.enableMinimalisticUI] {
Rectangle()
.fill(NSScreen.screens
.first(where: { $0.localizedName == coordinator.selectedScreen })?.safeAreaInsets.top ?? 0 > 0 ? .black : .clear)
@ -44,7 +46,7 @@ struct DynamicIslandHeader: View {
}
HStack(spacing: 4) {
if vm.notchState == .open {
if vm.notchState == .open && !Defaults[.enableMinimalisticUI] {
if Defaults[.showMirror] {
Button(action: {
vm.toggleCameraPreview()

View file

@ -0,0 +1,254 @@
//
// MinimalisticMusicPlayerView.swift
// DynamicIsland
//
// Created for minimalistic UI mode - Open state music player
//
import SwiftUI
import Defaults
struct MinimalisticMusicPlayerView: View {
@EnvironmentObject var vm: DynamicIslandViewModel
let albumArtNamespace: Namespace.ID
var body: some View {
VStack(spacing: 6) {
// Music banner: Album art + Song info + Visualizer
musicBanner
// Full-width progress bar with time labels
progressBar
// Larger playback controls
playbackControls
}
.padding(.horizontal, 16)
.padding(.top, 4)
.padding(.bottom, 4)
.frame(maxWidth: .infinity)
}
// MARK: - Music Banner (Compact)
@Default(.useMusicVisualizer) var useMusicVisualizer
private var musicBanner: some View {
HStack(spacing: 10) {
// Album Art - Smaller
MinimalisticAlbumArtView(vm: vm, albumArtNamespace: albumArtNamespace)
.frame(width: 50, height: 50)
// Song Title and Artist
songInfo
Spacer()
// Visualizer from live activity (only if enabled)
if useMusicVisualizer {
visualizer
}
}
.frame(height: 50)
}
private var songInfo: some View {
VStack(alignment: .leading, spacing: 2) {
Text(MusicManager.shared.songTitle)
.font(.system(size: 14, weight: .semibold))
.foregroundColor(.white)
.lineLimit(1)
Text(MusicManager.shared.artistName)
.font(.system(size: 12, weight: .regular))
.foregroundColor(Defaults[.playerColorTinting] ? Color(nsColor: MusicManager.shared.avgColor).ensureMinimumBrightness(factor: 0.6) : .gray)
.lineLimit(1)
}
}
private var visualizer: some View {
Rectangle()
.fill(Defaults[.coloredSpectrogram] ? Color(nsColor: MusicManager.shared.avgColor).gradient : Color.gray.gradient)
.mask {
AudioSpectrumView(isPlaying: .constant(MusicManager.shared.isPlaying))
.frame(width: 20, height: 16)
}
.frame(width: 20, height: 16)
.matchedGeometryEffect(id: "spectrum", in: albumArtNamespace)
}
// MARK: - Progress Bar (Full Width)
@ObservedObject var musicManager = MusicManager.shared
@State private var sliderValue: Double = 0
@State private var dragging: Bool = false
@State private var lastDragged: Date = .distantPast
private var progressBar: some View {
HStack(spacing: 8) {
// Elapsed time - left
Text(formatTime(dragging ? sliderValue : musicManager.elapsedTime))
.font(.system(size: 11, weight: .medium, design: .monospaced))
.foregroundColor(.white.opacity(0.7))
.frame(width: 42, alignment: .leading)
// Progress bar
TimelineView(.animation(minimumInterval: 0.05)) { timeline in
GeometryReader { geometry in
ZStack(alignment: .leading) {
// Background track
RoundedRectangle(cornerRadius: 3)
.fill(Color.white.opacity(0.2))
.frame(height: 6)
// Filled portion
RoundedRectangle(cornerRadius: 3)
.fill(sliderColor)
.frame(width: max(0, geometry.size.width * (currentSliderValue(timeline.date) / max(musicManager.songDuration, 1))), height: 6)
}
.gesture(
DragGesture(minimumDistance: 0)
.onChanged { value in
dragging = true
let newValue = min(max(0, Double(value.location.x / geometry.size.width) * musicManager.songDuration), musicManager.songDuration)
sliderValue = newValue
lastDragged = Date()
}
.onEnded { _ in
musicManager.seek(to: sliderValue)
dragging = false
}
)
}
}
.frame(height: 6)
// Time remaining - right
Text("-\(formatTime(musicManager.songDuration - (dragging ? sliderValue : musicManager.elapsedTime)))")
.font(.system(size: 11, weight: .medium, design: .monospaced))
.foregroundColor(.white.opacity(0.7))
.frame(width: 48, alignment: .trailing)
}
.onAppear {
sliderValue = musicManager.elapsedTime
}
}
private func currentSliderValue(_ date: Date) -> Double {
if dragging {
return sliderValue
}
// Update slider value based on playback
if musicManager.isPlaying {
let timeSinceLastUpdate = date.timeIntervalSince(musicManager.lastUpdated)
let estimatedElapsed = musicManager.elapsedTime + (timeSinceLastUpdate * musicManager.playbackRate)
return min(estimatedElapsed, musicManager.songDuration)
}
return musicManager.elapsedTime
}
private var sliderColor: Color {
switch Defaults[.sliderColor] {
case .white:
return .white
case .albumArt:
return Color(nsColor: musicManager.avgColor)
case .accent:
return .accentColor
}
}
private func formatTime(_ time: TimeInterval) -> String {
let minutes = Int(time) / 60
let seconds = Int(time) % 60
return String(format: "%d:%02d", minutes, seconds)
}
// MARK: - Playback Controls (Larger)
private var playbackControls: some View {
HStack(spacing: 20) {
if Defaults[.showShuffleAndRepeat] {
controlButton(icon: "shuffle", isActive: musicManager.isShuffled) {
Task { await musicManager.toggleShuffle() }
}
}
controlButton(icon: "backward.fill", size: 18) {
Task { await musicManager.previousTrack() }
}
playPauseButton
controlButton(icon: "forward.fill", size: 18) {
Task { await musicManager.nextTrack() }
}
if Defaults[.showShuffleAndRepeat] {
controlButton(icon: repeatIcon, isActive: musicManager.repeatMode != .off) {
Task { await musicManager.toggleRepeat() }
}
}
}
.frame(maxWidth: .infinity, alignment: .center)
.padding(.top, 2)
}
private var playPauseButton: some View {
Button(action: {
Task { await musicManager.togglePlay() }
}) {
Image(systemName: musicManager.isPlaying ? "pause.fill" : "play.fill")
.font(.system(size: 24, weight: .semibold))
.foregroundColor(.white)
.frame(width: 50, height: 50)
}
.buttonStyle(PlainButtonStyle())
}
private func controlButton(icon: String, size: CGFloat = 18, isActive: Bool = false, action: @escaping () -> Void) -> some View {
Button(action: action) {
Image(systemName: icon)
.font(.system(size: size, weight: .medium))
.foregroundColor(isActive ? .red : .white.opacity(0.8))
}
.buttonStyle(PlainButtonStyle())
}
private var repeatIcon: String {
switch musicManager.repeatMode {
case .off: return "repeat"
case .all: return "repeat"
case .one: return "repeat.1"
}
}
}
// MARK: - Minimalistic Album Art
struct MinimalisticAlbumArtView: View {
@ObservedObject var musicManager = MusicManager.shared
@ObservedObject var vm: DynamicIslandViewModel
let albumArtNamespace: Namespace.ID
var body: some View {
Button {
musicManager.openMusicApp()
} label: {
Color.clear
.aspectRatio(1, contentMode: .fit)
.background(
Image(nsImage: musicManager.albumArt)
.resizable()
.aspectRatio(contentMode: .fill)
)
.clipped()
.clipShape(RoundedRectangle(cornerRadius: 10))
.matchedGeometryEffect(id: "albumArt", in: albumArtNamespace)
}
.buttonStyle(PlainButtonStyle())
.opacity(musicManager.isPlaying ? 1 : 0.4)
}
}

View file

@ -246,23 +246,29 @@ struct NotchHomeView: View {
private var mainContent: some View {
HStack(alignment: .top, spacing: 20) {
MusicPlayerView(albumArtNamespace: albumArtNamespace)
if Defaults[.showCalendar] {
CalendarView()
.onHover { isHovering in
vm.isHoveringCalendar = isHovering
}
.environmentObject(vm)
}
if Defaults[.showMirror],
webcamManager.cameraAvailable,
vm.notchState == .open {
CameraPreviewView(webcamManager: webcamManager)
.scaledToFit()
.opacity(vm.notchState == .closed ? 0 : 1)
.blur(radius: vm.notchState == .closed ? 20 : 0)
if Defaults[.enableMinimalisticUI] {
// Minimalistic mode: Only show compact music player
MinimalisticMusicPlayerView(albumArtNamespace: albumArtNamespace)
} else {
// Normal mode: Show full music player with optional calendar and webcam
MusicPlayerView(albumArtNamespace: albumArtNamespace)
if Defaults[.showCalendar] {
CalendarView()
.onHover { isHovering in
vm.isHoveringCalendar = isHovering
}
.environmentObject(vm)
}
if Defaults[.showMirror],
webcamManager.cameraAvailable,
vm.notchState == .open {
CameraPreviewView(webcamManager: webcamManager)
.scaledToFit()
.opacity(vm.notchState == .closed ? 0 : 1)
.blur(radius: vm.notchState == .closed ? 20 : 0)
}
}
}
.transition(.opacity.animation(.smooth.speed(0.9))

View file

@ -19,6 +19,7 @@ import UniformTypeIdentifiers
struct SettingsView: View {
@StateObject var extensionManager = DynamicIslandExtensionManager()
@State private var selectedTab = "General"
@Default(.enableMinimalisticUI) var enableMinimalisticUI
let updaterController: SPUStandardUpdaterController?
@ -38,8 +39,10 @@ struct SettingsView: View {
NavigationLink(value: "Media") {
Label("Media", systemImage: "play.laptopcomputer")
}
NavigationLink(value: "Calendar") {
Label("Calendar", systemImage: "calendar")
if !enableMinimalisticUI {
NavigationLink(value: "Calendar") {
Label("Calendar", systemImage: "calendar")
}
}
NavigationLink(value: "HUD") {
Label("HUDs", systemImage: "dial.medium.fill")
@ -47,20 +50,22 @@ struct SettingsView: View {
NavigationLink(value: "Battery") {
Label("Battery", systemImage: "battery.100.bolt")
}
NavigationLink(value: "Timer") {
Label("Timer", systemImage: "timer")
}
NavigationLink(value: "Stats") {
Label("Stats", systemImage: "chart.xyaxis.line")
}
NavigationLink(value: "Clipboard") {
Label("Clipboard", systemImage: "clipboard")
}
NavigationLink(value: "ScreenAssistant") {
Label("Screen Assistant", systemImage: "brain.head.profile")
}
NavigationLink(value: "ColorPicker") {
Label("Color Picker", systemImage: "eyedropper")
if !enableMinimalisticUI {
NavigationLink(value: "Timer") {
Label("Timer", systemImage: "timer")
}
NavigationLink(value: "Stats") {
Label("Stats", systemImage: "chart.xyaxis.line")
}
NavigationLink(value: "Clipboard") {
Label("Clipboard", systemImage: "clipboard")
}
NavigationLink(value: "ScreenAssistant") {
Label("Screen Assistant", systemImage: "brain.head.profile")
}
NavigationLink(value: "ColorPicker") {
Label("Color Picker", systemImage: "eyedropper")
}
}
if extensionManager.installedExtensions
.contains(where: { $0.bundleIdentifier == downloadManagerExtension }) {
@ -68,8 +73,10 @@ struct SettingsView: View {
Label("Downloads", systemImage: "square.and.arrow.down")
}
}
NavigationLink(value: "Shelf") {
Label("Shelf", systemImage: "books.vertical")
if !enableMinimalisticUI {
NavigationLink(value: "Shelf") {
Label("Shelf", systemImage: "books.vertical")
}
}
NavigationLink(value: "Shortcuts") {
Label("Shortcuts", systemImage: "keyboard")
@ -165,9 +172,24 @@ struct GeneralSettings: View {
@Default(.openNotchOnHover) var openNotchOnHover
@Default(.enableScreenRecordingDetection) var enableScreenRecordingDetection
@Default(.showRecordingIndicator) var showRecordingIndicator
@Default(.enableMinimalisticUI) var enableMinimalisticUI
var body: some View {
Form {
Section {
Defaults.Toggle("Enable Minimalistic UI", key: .enableMinimalisticUI)
.onChange(of: enableMinimalisticUI) { _, newValue in
if newValue {
// Auto-enable simpler animation mode
Defaults[.useModernCloseAnimation] = true
}
}
} header: {
Text("UI Mode")
} footer: {
Text("Minimalistic mode focuses on media controls and system HUDs, hiding all extra features for a clean, focused experience. Automatically enables simpler animations.")
}
Section {
Defaults.Toggle("Menubar icon", key: .menubarIcon)
LaunchAtLogin.Toggle("Launch at login")
@ -563,6 +585,7 @@ struct Media: View {
@Default(.hideNotchOption) var hideNotchOption
@Default(.enableSneakPeek) private var enableSneakPeek
@Default(.sneakPeekStyles) var sneakPeekStyles
@Default(.enableMinimalisticUI) var enableMinimalisticUI
var body: some View {
Form {
@ -616,7 +639,21 @@ struct Media: View {
ForEach(SneakPeekStyle.allCases) { style in
Text(style.rawValue).tag(style)
}
}.disabled(!enableSneakPeek)
}
.disabled(!enableSneakPeek || enableMinimalisticUI)
.onChange(of: enableMinimalisticUI) { _, isMinimalistic in
// Force standard sneak peek style when minimalistic UI is enabled
if isMinimalistic {
sneakPeekStyles = .standard
}
}
if enableMinimalisticUI {
Text("Sneak peek style is locked to Standard in minimalistic mode")
.font(.caption)
.foregroundStyle(.secondary)
}
HStack {
Stepper(value: $waitInterval, in: 0...10, step: 1) {
HStack {

View file

@ -349,6 +349,9 @@ extension Defaults.Keys {
// Polling removed - now uses event-driven private API detection (CGSIsScreenWatcherPresent)
// static let enableScreenRecordingPolling = Key<Bool>("enableScreenRecordingPolling", default: false)
// MARK: Minimalistic UI Mode
static let enableMinimalisticUI = Key<Bool>("enableMinimalisticUI", default: false)
// Helper to determine the default media controller based on macOS version
static var defaultMediaController: MediaControllerType {
if #available(macOS 15.4, *) {

View file

@ -132,9 +132,12 @@ class DynamicIslandViewModel: NSObject, ObservableObject {
}
private func calculateDynamicNotchSize() -> CGSize {
// Use minimalistic size if minimalistic UI is enabled
let baseSize = Defaults[.enableMinimalisticUI] ? minimalisticOpenNotchSize : openNotchSize
// Only apply dynamic sizing when on stats tab and stats are enabled
guard DynamicIslandViewCoordinator.shared.currentView == .stats && Defaults[.enableStatsFeature] else {
return openNotchSize
return baseSize
}
let enabledGraphsCount = [
@ -148,10 +151,10 @@ class DynamicIslandViewModel: NSObject, ObservableObject {
// If 4+ graphs are enabled, increase width
if enabledGraphsCount >= 4 {
let extraWidth: CGFloat = CGFloat(enabledGraphsCount - 3) * 120
return CGSize(width: openNotchSize.width + extraWidth, height: openNotchSize.height)
return CGSize(width: baseSize.width + extraWidth, height: baseSize.height)
}
return openNotchSize
return baseSize
}
func close() {

View file

@ -15,7 +15,9 @@ let downloadSneakSize: CGSize = .init(width: 65, height: 1)
let batterySneakSize: CGSize = .init(width: 160, height: 1)
let openNotchSize: CGSize = .init(width: 640, height: 190)
let minimalisticOpenNotchSize: CGSize = .init(width: 420, height: 190)
let cornerRadiusInsets: (opened: (top: CGFloat, bottom: CGFloat), closed: (top: CGFloat, bottom: CGFloat)) = (opened: (top: 19, bottom: 24), closed: (top: 6, bottom: 14))
let minimalisticCornerRadiusInsets: (opened: (top: CGFloat, bottom: CGFloat), closed: (top: CGFloat, bottom: CGFloat)) = (opened: (top: 35, bottom: 35), closed: cornerRadiusInsets.closed)
enum MusicPlayerImageSizes {
static let cornerRadiusInset: (opened: CGFloat, closed: CGFloat) = (opened: 13.0, closed: 4.0)