Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 16 additions & 4 deletions ComfyNotch/Handlers/PanelProximityHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ class PanelProximityHandler: NSObject {
private var padding: CGFloat = 15
private var distanceThreshold: CGFloat = 300

/// Throttling properties
private var lastProcessedTime: TimeInterval = 0
/// ~30fps (0.016 for 60)
private let throttleInterval: TimeInterval = 0.033
Copy link

Copilot AI Jul 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Magic number 0.033 should be extracted as a named constant or calculated from a frame rate constant to make the relationship clearer.

Suggested change
private let throttleInterval: TimeInterval = 0.033
private let frameRate: Double = 30.0
private let throttleInterval: TimeInterval = 1.0 / frameRate

Copilot uses AI. Check for mistakes.

private let uiManager = UIManager.shared

override init() {
Expand Down Expand Up @@ -74,7 +79,15 @@ class PanelProximityHandler: NSObject {
}

private func handleMouseMoved(_ event: NSEvent) {
guard let panel = panel else { return }
// Throttle the event processing
let currentTime = CACurrentMediaTime()
guard currentTime - lastProcessedTime > throttleInterval else { return }
lastProcessedTime = currentTime

// Early exit checks before expensive operations
guard let panel = panel,
UIManager.shared.panelState == .open,
!SettingsModel.shared.isSettingsWindowOpen else { return }

let mouseLocation = NSEvent.mouseLocation
let panelFrame = panel.frame
Expand All @@ -88,11 +101,10 @@ class PanelProximityHandler: NSObject {
)

/// Don't open the panel with proximity, only allow closing
if (UIManager.shared.panelState == .open
&& !paddedFrame.contains(mouseLocation)) {
if !paddedFrame.contains(mouseLocation) {
let distance = distanceFromPanel(to: mouseLocation, panelFrame: panelFrame)

if distance > distanceThreshold && !SettingsModel.shared.isSettingsWindowOpen {
if distance > distanceThreshold {

withAnimation(.easeInOut(duration: 0.1)) {

Expand Down
14 changes: 6 additions & 8 deletions ComfyNotch/Main/ComfyNotchView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ struct ComfyNotchView: View {
@ObservedObject private var uiManager = UIManager.shared
@ObservedObject private var settings = SettingsModel.shared

@State private var lastPanelState: PanelState = .closed

init() {
}

Expand All @@ -57,6 +59,10 @@ struct ComfyNotchView: View {
/// This manager was added in to make sure that the popInPresentation is playing
/// when we open it, it doesnt bug out
.onChange(of: uiManager.panelState) { _, newState in

guard newState != lastPanelState else { return }
lastPanelState = newState

if newState == .open {
if animationState.currentPanelState == .popInPresentation {
animationState.currentPanelState = .home
Expand Down Expand Up @@ -125,8 +131,6 @@ struct ComfyNotchView: View {
if restrictedStates.contains(animationState.currentPanelState) {
return
}

// print("translation \(translation)")
switch phase {
case .ended:
if translation > settings.notchScrollThreshold {
Expand Down Expand Up @@ -167,14 +171,8 @@ struct ComfyNotchView: View {

if animationState.isExpanded || animationState.currentPanelState == .popInPresentation {
/// see QuickAccessWidget.swift file to see how it works
// if settings.isFirstLaunch {
// Onboarding()
// .padding(.horizontal, 4)
// } else {
expandedView
.padding(.horizontal, 4)
// }

}

Spacer()
Expand Down
1 change: 0 additions & 1 deletion ComfyNotch/Managers/UIManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,6 @@ class UIManager: ObservableObject {

public func applyCompactWidgetLayout() {
/// When the notch is closed we wanna show the compact album on the left, and dots on the right and hide
/// The Settings Widget
DispatchQueue.main.async {
withAnimation(Anim.spring) {
self.compactWidgetStore.hideWidget(named: "QuickAccessWidget")
Expand Down
26 changes: 13 additions & 13 deletions ComfyNotch/Models/SettingsModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,19 +108,19 @@ class SettingsModel: ObservableObject {
private init() {
loadSettings()

$isSettingsWindowOpen
.receive(on: RunLoop.main)
.sink { isOpen in
if isOpen {
NSApp.setActivationPolicy(.regular)
NSApp.activate(ignoringOtherApps: true)
} else {
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
NSApp.setActivationPolicy(.accessory)
}
}
}
.store(in: &cancellables)
// $isSettingsWindowOpen
// .receive(on: RunLoop.main)
// .sink { isOpen in
// if isOpen {
// NSApp.setActivationPolicy(.regular)
// NSApp.activate(ignoringOtherApps: true)
// } else {
// DispatchQueue.main.asyncAfter(deadline: .now() + 5.0) {
// NSApp.setActivationPolicy(.accessory)
// }
// }
// }
// .store(in: &cancellables)
}

func checkForUpdates() {
Expand Down
56 changes: 37 additions & 19 deletions ComfyNotch/Widgets/CompactWidgets/CompactAlbumWidget.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,23 @@ import AppKit
import SwiftUI
import Combine

struct WidgetSizeConfig {
let width: CGFloat
let height: CGFloat
}

struct CompactAlbumWidget: View, Widget {
var alignment: WidgetAlignment? = .left
var name: String = "AlbumWidget"

var swiftUIView: AnyView {
AnyView(self)
}

@ObservedObject var model: MusicPlayerWidgetModel = .shared
@ObservedObject var panelAnimationState: PanelAnimationState = .shared
var scrollManager = ScrollHandler.shared

private let smallSizeWidth: CGFloat = 25
private let smallSizeHeight: CGFloat = 22
private let bigSizeWidth: CGFloat = 28
private let bigSizeHeight: CGFloat = 25

private var width: CGFloat {
panelAnimationState.hoverHandler.scaleHoverOverLeftItems ? bigSizeWidth : smallSizeWidth
}

private var height: CGFloat {
panelAnimationState.hoverHandler.scaleHoverOverLeftItems ? bigSizeHeight : smallSizeHeight
}

private var animationStiffness: CGFloat = 300
private var animationDamping: CGFloat = 15

Expand All @@ -33,15 +29,16 @@ struct CompactAlbumWidget: View, Widget {
panelAnimationState.hoverHandler.scaleHoverOverLeftItems ? 1 : 0
}


@State private var sizeConfig: WidgetSizeConfig = .init(width: 0, height: 0)

var body: some View {
panelButton {
Group {
if let artwork = model.nowPlayingInfo.artworkImage {
Image(nsImage: artwork)
.resizable()
.scaledToFit()
.frame(width: width, height: height)
.frame(width: sizeConfig.width, height: sizeConfig.height)
.cornerRadius(4)
.padding(.top, 2)
} else {
Expand All @@ -62,8 +59,12 @@ struct CompactAlbumWidget: View, Widget {
.interpolatingSpring(stiffness: animationStiffness, damping: animationDamping),
value: panelAnimationState.hoverHandler.scaleHoverOverLeftItems
)
.onAppear { sizeConfig = widgetSize() }
.onChange(of: panelAnimationState.hoverHandler.scaleHoverOverLeftItems) {
sizeConfig = widgetSize()
}
}

private func panelButton<Label: View>(@ViewBuilder label: () -> Label) -> some View {
Button(action: {
withAnimation(Anim.spring) {
Expand All @@ -80,8 +81,25 @@ struct CompactAlbumWidget: View, Widget {
}
.buttonStyle(.plain)
}

var swiftUIView: AnyView {
AnyView(self)

func widgetSize() -> WidgetSizeConfig {
guard let screen = DisplayManager.shared.selectedScreen else {
return .init(width: 22, height: 25)
}

let scale = screen.backingScaleFactor
let resolution = CGSize(width: screen.frame.width * scale,
height: screen.frame.height * scale)

let w = resolution.width
let isExpanded = panelAnimationState.hoverHandler.scaleHoverOverLeftItems

if w < 2800 {
return isExpanded ? .init(width: 20, height: 20) : .init(width: 15, height: 14)
} else if w <= 3500 {
return isExpanded ? .init(width: 26, height: 23) : .init(width: 17, height: 17)
} else {
return isExpanded ? .init(width: 28, height: 25) : .init(width: 22, height: 23)
}
}
}
Loading