From 501f5de3e5a379d6e602ae990176c151387d7f63 Mon Sep 17 00:00:00 2001 From: Johnny Wang Date: Fri, 26 Dec 2025 16:09:06 +0800 Subject: [PATCH] Fix popover crash on macOS Sequoia MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This fixes a crash where the popover would flash briefly but not display properly on macOS 15 (Sequoia), particularly on M4 Macs. Changes: - Replace deprecated MenuButton with modern Menu view - Use button action/target pattern instead of NSEvent.addLocalMonitorForEvents which can conflict with modern macOS event handling - Fix incorrect @ObservedObject usage in MainViewModel class (this property wrapper is only valid in SwiftUI Views) Tested on macOS 15 with M4 chip. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- Ports/Logic/StatusBarController.swift | 14 +++----------- Ports/Views/MainView.swift | 28 +++++++++++++-------------- 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/Ports/Logic/StatusBarController.swift b/Ports/Logic/StatusBarController.swift index e8b01ad..e369257 100644 --- a/Ports/Logic/StatusBarController.swift +++ b/Ports/Logic/StatusBarController.swift @@ -31,20 +31,12 @@ class StatusBarController: NSObject, NSPopoverDelegate { statusBarButton.image = NSImage(named: "MenuBarIcon") statusBarButton.image?.size = NSSize(width: 18.0, height: 18.0) statusBarButton.image?.isTemplate = true - statusBarButton.setButtonType(.onOff) - - NSEvent.addLocalMonitorForEvents(matching: .leftMouseDown) { [weak self] event in - if event.window == self?.statusItem.button?.window { - self?.togglePopover(sender: statusBarButton) - return nil - } - - return event - } + statusBarButton.action = #selector(togglePopover) + statusBarButton.target = self } } - @objc func togglePopover(sender: AnyObject) { + @objc func togglePopover(_ sender: AnyObject) { if popover.isShown { hidePopover(sender) } else { diff --git a/Ports/Views/MainView.swift b/Ports/Views/MainView.swift index 6cc9443..af3240e 100644 --- a/Ports/Views/MainView.swift +++ b/Ports/Views/MainView.swift @@ -31,18 +31,18 @@ struct MainView: View { .listStyle(.sidebar) HStack { Spacer() - MenuButton( - label: Image(systemName: "gearshape.fill"), - content: { - Button(localizedString("openWebsite")) { - NSWorkspace.shared.open(URL(string: "https://chaosspace.de/ports?utm_source=portsapp")!) - } - Button(localizedString("quit")) { - exit(0) - } - }) - .frame(width: 20, height: 20) - .menuButtonStyle(BorderlessButtonMenuButtonStyle()) + Menu { + Button(localizedString("openWebsite")) { + NSWorkspace.shared.open(URL(string: "https://chaosspace.de/ports?utm_source=portsapp")!) + } + Button(localizedString("quit")) { + exit(0) + } + } label: { + Image(systemName: "gearshape.fill") + } + .menuStyle(.borderlessButton) + .frame(width: 20, height: 20) .padding(EdgeInsets(top: 0, leading: 10, bottom: 10, trailing: 10)) } } @@ -61,12 +61,12 @@ protocol MainViewModelType: ObservableObject { } class MainViewModel: ObservableObject, MainViewModelType { - @ObservedObject var processManager: ProcessManager + private let processManager: ProcessManager @Published var processList: ProcessList init(processManager: ProcessManager) { self.processManager = processManager - self.processList = ProcessList(lastUpdated: Date(), processes: []) + self.processList = processManager.processList processManager.$processList.assign(to: &$processList) }