fix: discover XPC-backed application bundles

This commit is contained in:
Valdemar Lorenzen
2026-10-15 10:04:08 +02:00
parent 7eb52bd1db
commit 796e8a820c
4 changed files with 25 additions and 1 deletions
+2
View File
@@ -67,6 +67,8 @@ Conjure recognizes the native [login-launch Apple event](https://developer.apple
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.
Discovery supports both standard (`APPL`) and XPC-backed (`XPC!`) `.app` bundles, including Apple Passwords, provided they contain a valid executable.
Search is case- and accent-insensitive. Ranking is exact name, full-name prefix, word prefix, ordered word initials, substring, then ordered-subsequence fuzzy matches. Fuzzy matches favor smaller gaps and earlier starts. Equally good text matches put favorites first, then order by successful launch count, most recent launch, and alphabetically. Neither favorites nor usage promote a weaker text match above a stronger one or change which applications match. Hover over a result to see its path. Launch failures appear in a native alert without dismissing the launcher.
Words are runs of letters or numbers separated by spaces, punctuation, or symbols. For example, `settings` matches the second word of System Settings, `gc` matches Google Chrome, and `ss` matches System Settings. Initials must occur in order but can skip words: both `vsc` and `vc` match Visual Studio Code.
+2 -1
View File
@@ -68,7 +68,8 @@ public enum ApplicationCatalog {
// Never expose helpers inside an application, even if its bundle is invalid.
guard visitedApplications.insert(url).inserted,
let bundle = Bundle(url: url),
bundle.object(forInfoDictionaryKey: "CFBundlePackageType") as? String == "APPL",
let packageType = bundle.object(forInfoDictionaryKey: "CFBundlePackageType") as? String,
packageType == "APPL" || packageType == "XPC!",
let executable = bundle.executableURL,
let executableValues = try? executable.resourceValues(forKeys: [.isRegularFileKey]),
executableValues.isRegularFile == true,
+7
View File
@@ -0,0 +1,7 @@
- [ ] add a translation "command"
- it should appear in the list as a "Command" instead of an "Application" (entry type/kind)
- it should be named/titled "Conjure Translate"
- applying the open action on the command will cause the launcher to morph into a translation tool (ui transition like the uninstall confirmation view)
- the search bar becomes the translate input
- first pass should translate from english to japanese
- it should use gpt-6-luna as the translation unit (requires the user to insert an api key)
@@ -103,6 +103,20 @@ import Testing
#expect(applications.map(\.url) == [alpha, display, fallback, parent].map { $0.resolvingSymlinksInPath() })
}
@Test func discoveryFindsSearchableXPCAppsButNotTheirHelpersOrInvalidBundles() throws {
let root = try temporaryDirectory()
defer { try? FileManager.default.removeItem(at: root) }
let passwords = try appBundle(in: root, filename: "Passwords", name: "Passwords", packageType: "XPC!")
_ = try appBundle(in: passwords.appendingPathComponent("Contents/Helpers"),
filename: "Helper", name: "Helper", packageType: "XPC!")
_ = try appBundle(in: root, filename: "NoExecutable", name: "NoExecutable",
executable: false, packageType: "XPC!")
let applications = ApplicationCatalog.discover(in: [root])
#expect(applications.map(\.url) == [passwords.resolvingSymlinksInPath()])
#expect(ApplicationSearch.results(for: "passwords", in: applications).map(\.name) == ["Passwords"])
}
@Test func discoveryCanonicalizesAliasesAndAvoidsDirectorySymlinkCycles() throws {
let root = try temporaryDirectory()
defer { try? FileManager.default.removeItem(at: root) }