Tell the user when Accessibility is missing
Without it the Shift-Shift gestures fail silently, so hiding the panel left no way to bring it back and the app looked broken. The app now says so on launch and points at Cmd+Shift+Space, which needs no permission, and --diag reports the permission state alongside the status item's placement so a lockout can be diagnosed instead of guessed at.
This commit is contained in:
@@ -27,6 +27,18 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
return
|
||||
}
|
||||
|
||||
// --diag reports where the status item landed. On a notched Mac a
|
||||
// brand-new item can be placed under the notch and be unclickable,
|
||||
// which locks the user out once the panel is hidden.
|
||||
if CommandLine.arguments.contains("--diag") {
|
||||
setupStatusItem()
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
|
||||
self.writeDiagnostics()
|
||||
NSApp.terminate(nil)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Single instance: stale copies keep their hotkeys alive after an
|
||||
// update and eat the events meant for the new instance.
|
||||
let myPid = ProcessInfo.processInfo.processIdentifier
|
||||
@@ -48,6 +60,7 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
setupStatusItem()
|
||||
setupMainMenu()
|
||||
promptForAccessibilityIfNeeded()
|
||||
|
||||
// Show the panel on launch so the app is discoverable (it has no Dock
|
||||
// icon and the menu bar icon can be hard to spot on a full bar).
|
||||
@@ -57,6 +70,28 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
/// Without Accessibility the Shift-Shift gestures fail silently, so the
|
||||
/// panel can be hidden with no obvious way back. Say so on launch and
|
||||
/// point at the shortcut that works without the permission.
|
||||
private func promptForAccessibilityIfNeeded() {
|
||||
guard !AXIsProcessTrusted() else { return }
|
||||
let alert = NSAlert()
|
||||
alert.messageText = "Prompt needs Accessibility access"
|
||||
alert.informativeText = """
|
||||
Capturing with Shift-Shift (and showing the panel with right Shift twice) \
|
||||
only works with Accessibility access.
|
||||
|
||||
Until it is granted, use Cmd+Shift+Space to show or hide the panel and \
|
||||
Cmd+Shift+C to capture, or click the menu bar icon.
|
||||
"""
|
||||
alert.addButton(withTitle: "Open Settings")
|
||||
alert.addButton(withTitle: "Later")
|
||||
if alert.runModal() == .alertFirstButtonReturn {
|
||||
let options = [kAXTrustedCheckOptionPrompt.takeUnretainedValue(): true] as CFDictionary
|
||||
_ = AXIsProcessTrustedWithOptions(options)
|
||||
}
|
||||
}
|
||||
|
||||
/// A launched-again app must reveal itself. Without this, `open` (or
|
||||
/// clicking it in Finder/Spotlight) hits the running instance, which does
|
||||
/// nothing, so the app looks like it never started.
|
||||
@@ -103,6 +138,26 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
PanelController.shared.hide()
|
||||
}
|
||||
|
||||
@MainActor
|
||||
private func writeDiagnostics() {
|
||||
var lines = ["Prompt diagnostics", "accessibility: \(AXIsProcessTrusted())"]
|
||||
if let window = statusItem?.button?.window {
|
||||
lines.append("status item frame: \(window.frame)")
|
||||
if let screen = window.screen ?? NSScreen.main {
|
||||
lines.append("screen: \(screen.frame) visible: \(screen.visibleFrame)")
|
||||
lines.append("notch inset: \(screen.safeAreaInsets)")
|
||||
let underNotch = window.frame.maxX > screen.frame.maxX - screen.safeAreaInsets.right
|
||||
lines.append("under the notch: \(underNotch)")
|
||||
}
|
||||
} else {
|
||||
lines.append("status item has no window (not in the menu bar)")
|
||||
}
|
||||
lines.append("preferred position: \(UserDefaults.standard.object(forKey: "NSStatusItem Preferred Position Prompt") ?? "unset")")
|
||||
try? lines.joined(separator: "\n").write(
|
||||
to: URL(fileURLWithPath: "/tmp/prompt-diag.txt"), atomically: true, encoding: .utf8
|
||||
)
|
||||
}
|
||||
|
||||
private func setupStatusItem() {
|
||||
let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.squareLength)
|
||||
item.autosaveName = "Prompt"
|
||||
|
||||
Reference in New Issue
Block a user