feat: keep launcher hidden when started at login

This commit is contained in:
2026-10-12 09:58:43 +02:00
parent ac74a26f3b
commit d4312eb839
2 changed files with 26 additions and 3 deletions
+6
View File
@@ -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. 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 ## 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. 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.
+19 -2
View File
@@ -93,6 +93,13 @@ private final class ApplicationDelegate: NSObject, NSApplicationDelegate {
private lazy var launcher = LauncherWindowController() private lazy var launcher = LauncherWindowController()
private lazy var shortcut = GlobalShortcut { [weak self] in self?.launcher.toggle() } private lazy var shortcut = GlobalShortcut { [weak self] in self?.launcher.toggle() }
private lazy var settings = SettingsWindowController(shortcut: shortcut) 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) { func applicationDidFinishLaunching(_ notification: Notification) {
let menu = NSMenu() 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") let quitItem = menu.addItem(withTitle: "Quit Conjure", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q")
quitItem.target = NSApplication.shared quitItem.target = NSApplication.shared
launcher.window?.contentView?.menu = menu launcher.window?.contentView?.menu = menu
if !isLoginLaunch {
launcher.present() launcher.present()
}
do { do {
try shortcut.start() try shortcut.start()
} catch { } catch {
settings.present(error: error.localizedDescription) startupShortcutError = error.localizedDescription
if !isLoginLaunch {
showSettings(nil)
}
} }
} }
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows: Bool) -> Bool { func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows: Bool) -> Bool {
guard !isLoginLaunch else { return false }
launcher.present() launcher.present()
if startupShortcutError != nil {
showSettings(nil)
}
return false return false
} }
@objc func showSettings(_ sender: Any?) { @objc func showSettings(_ sender: Any?) {
settings.present() settings.present(error: startupShortcutError)
startupShortcutError = nil
} }
func applicationWillTerminate(_ notification: Notification) { func applicationWillTerminate(_ notification: Notification) {