From d4312eb839689632270f17affe4b37766eb54655 Mon Sep 17 00:00:00 2001 From: hl-valdemar Date: Mon, 12 Oct 2026 09:58:43 +0200 Subject: [PATCH] feat: keep launcher hidden when started at login --- README.md | 6 ++++++ Sources/Conjure/ConjureApp.swift | 23 ++++++++++++++++++++--- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index d771eb0..c823235 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,12 @@ The global shortcut shows the launcher with an empty query and search focused. P Activation uses the native macOS hotkey API, without an Accessibility permission or a global keyboard monitor. It works only while Conjure is running; it does not launch a quit app or enable launch at login. +## Launch at login + +Add `/Applications/Conjure.app` in **System Settings → General → Login Items & Extensions → Open at Login**. Conjure recognizes the native [login-launch Apple event](https://developer.apple.com/documentation/coreservices/1556410-launch_apple_event_constants) and starts without showing a window or taking focus, while still registering its saved global shortcut. Opening it manually from Finder still shows the launcher, including when it is already running. + +If shortcut registration fails at login, the error is retained and shown in Settings when you next open Conjure manually. Normal manual launches continue to report startup errors immediately. Conjure does not manage the login-item setting itself. + ## Discovery and search Applications are discovered in `/Applications`, `/System/Applications`, `/System/Library/CoreServices/Applications`, and `~/Applications`, including nested folders. Discovery runs off the UI thread and refreshes whenever the launcher is presented. Hidden items, invalid bundles, and helpers inside application packages are excluded; symlinked applications are deduplicated by their canonical paths. diff --git a/Sources/Conjure/ConjureApp.swift b/Sources/Conjure/ConjureApp.swift index 8212144..4be9608 100644 --- a/Sources/Conjure/ConjureApp.swift +++ b/Sources/Conjure/ConjureApp.swift @@ -93,6 +93,13 @@ private final class ApplicationDelegate: NSObject, NSApplicationDelegate { private lazy var launcher = LauncherWindowController() private lazy var shortcut = GlobalShortcut { [weak self] in self?.launcher.toggle() } private lazy var settings = SettingsWindowController(shortcut: shortcut) + private var startupShortcutError: String? + + private var isLoginLaunch: Bool { + let event = NSAppleEventManager.shared().currentAppleEvent + return event?.eventID == kAEOpenApplication + && event?.paramDescriptor(forKeyword: keyAEPropData)?.enumCodeValue == keyAELaunchedAsLogInItem + } func applicationDidFinishLaunching(_ notification: Notification) { let menu = NSMenu() @@ -102,21 +109,31 @@ private final class ApplicationDelegate: NSObject, NSApplicationDelegate { let quitItem = menu.addItem(withTitle: "Quit Conjure", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q") quitItem.target = NSApplication.shared launcher.window?.contentView?.menu = menu - launcher.present() + if !isLoginLaunch { + launcher.present() + } do { try shortcut.start() } catch { - settings.present(error: error.localizedDescription) + startupShortcutError = error.localizedDescription + if !isLoginLaunch { + showSettings(nil) + } } } func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows: Bool) -> Bool { + guard !isLoginLaunch else { return false } launcher.present() + if startupShortcutError != nil { + showSettings(nil) + } return false } @objc func showSettings(_ sender: Any?) { - settings.present() + settings.present(error: startupShortcutError) + startupShortcutError = nil } func applicationWillTerminate(_ notification: Notification) {