feat: rank built-in commands alongside applications

This commit is contained in:
Valdemar Lorenzen
2026-10-15 11:23:50 +02:00
parent 31c651ecb1
commit e0f17c6339
7 changed files with 131 additions and 43 deletions
+29 -27
View File
@@ -10,7 +10,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
private let sectionLabel = NSTextField(labelWithString: "Applications")
private let openButton = NSButton(title: "Open Application", target: nil, action: nil)
private let history: LaunchHistory
private var applications: [Application] = []
private var applications: [Application] = Application.commands
private var results: [Application] = []
private var calculation: CalculatorResult?
private var isCurrencyCalculation = false
@@ -32,10 +32,6 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
private var launcherViews: [NSView] = []
private var launcherFrameBeforeReview: NSRect?
private var translation: TranslationController?
private var showsTranslationCommand = true
private var translationCommandRow: Int? {
calculation == nil && showsTranslationCommand ? results.count : nil
}
init(history: LaunchHistory = LaunchHistory(), uninstaller: ApplicationUninstaller = ApplicationUninstaller()) {
self.history = history
@@ -74,8 +70,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
window.numberHintsChanged = { [weak self] in self?.updateNumberHints() }
window.launchNumber = { [weak self] index in
guard let self else { return }
if index == self.translationCommandRow { self.showTranslation() }
else if self.results.indices.contains(index) { self.openApplication(self.results[index]) }
if self.results.indices.contains(index) { self.openApplication(self.results[index]) }
}
window.dragEnded = { [weak self] in
guard let self else { return }
@@ -300,8 +295,8 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
actionsButton.keyEquivalentModifierMask = .command
actionsButton.target = self
actionsButton.action = #selector(showActions)
actionsButton.setAccessibilityLabel("Application actions")
actionsButton.toolTip = "Actions for the selected application (Command-K)"
actionsButton.setAccessibilityLabel("Entry actions")
actionsButton.toolTip = "Actions for the selected application or command (Command-K)"
actionsButton.isEnabled = false
openButton.toolTip = "Open the selected application (Return). Use ↑ and ↓ to select."
let returnKey = ShortcutIndicator("↵")
@@ -375,7 +370,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
}.value
guard let self else { return }
guard !Task.isCancelled else { return }
self.applications = applications
self.applications = applications + Application.commands
self.updateLocationLabels()
self.discoveryTask = nil
self.updateResults(preservingSelection: true)
@@ -407,9 +402,9 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
private func updateLocationLabels() {
var nameCounts: [String: Int] = [:]
nameCounts.reserveCapacity(applications.count)
for application in applications { nameCounts[application.name, default: 0] += 1 }
for application in applications where application.command == nil { nameCounts[application.name, default: 0] += 1 }
var groups: [String: [(url: URL, folder: URL)]] = [:]
for application in applications where (nameCounts[application.name] ?? 0) > 1 {
for application in applications where application.command == nil && (nameCounts[application.name] ?? 0) > 1 {
groups[application.name, default: []].append(
(application.url, application.url.deletingLastPathComponent())
)
@@ -458,9 +453,6 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
detail: "\(answer.detail) · \(cacheStatus)"
)
}
showsTranslationCommand = query.split(whereSeparator: \.isWhitespace).allSatisfy {
"Conjure Translate".localizedStandardContains(String($0))
}
if calculation != nil { (window as? LauncherPanel)?.resetNumberHints() }
if calculation != nil {
results = []
@@ -472,7 +464,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
sectionLabel.stringValue = isCurrencyCalculation ? "Daily currency rates"
: calculation == nil ? "Applications & Commands" : "Calculator"
tableView.setAccessibilityLabel(isCurrencyCalculation ? "Daily currency conversion result"
: calculation == nil ? "Applications" : "Calculator result")
: calculation == nil ? "Applications and commands" : "Calculator result")
tableView.reloadData()
if resultCount > 0 {
let row = selectedURL.flatMap { url in results.firstIndex { $0.id == url } } ?? 0
@@ -486,8 +478,9 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
emptyLabel.stringValue = "Finding applications…"
statusLabel.stringValue = "Finding applications…"
} else {
emptyLabel.stringValue = applications.isEmpty ? "No applications found" : "No matching applications"
statusLabel.stringValue = "\(results.count) applications" + (showsTranslationCommand ? " · 1 command" : "")
emptyLabel.stringValue = "No matching applications or commands"
let commandCount = results.lazy.filter { $0.command != nil }.count
statusLabel.stringValue = "\(results.count - commandCount) applications · \(commandCount) commands"
}
updateOpenButton()
(window as? LauncherPanel)?.sampleNumberHints()
@@ -519,7 +512,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
return true
}
private var resultCount: Int { calculation == nil ? results.count + (showsTranslationCommand ? 1 : 0) : 1 }
private var resultCount: Int { calculation == nil ? results.count : 1 }
private func moveSelection(by offset: Int) {
guard translation == nil else { return }
@@ -531,12 +524,14 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
private func updateOpenButton() {
let canAct = !isLaunching && launcherFrameBeforeReview == nil && translation == nil && tableView.selectedRow >= 0
openButton.title = calculation == nil ? (tableView.selectedRow == translationCommandRow ? "Open Command" : "Open Application") : "Copy Answer"
openButton.title = calculation == nil
? (results.indices.contains(tableView.selectedRow) && results[tableView.selectedRow].command != nil
? "Open Command" : "Open Application") : "Copy Answer"
openButton.toolTip = calculation == nil
? "Open the selected entry (Return). Use ↑ and ↓ to select."
: "Copy the answer to the clipboard (Return)."
openButton.isEnabled = canAct && (calculation.map { $0.copyValue != nil }
?? (results.indices.contains(tableView.selectedRow) || tableView.selectedRow == translationCommandRow))
?? results.indices.contains(tableView.selectedRow))
actionsButton.isEnabled = canAct && calculation == nil && results.indices.contains(tableView.selectedRow)
}
@@ -574,11 +569,12 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
.withSymbolConfiguration(.init(pointSize: LauncherRowView.iconSize, weight: .regular))
cell.imageView?.contentTintColor = SunsetPalette.accent
cell.toolTip = calculation.detail
} else if row == translationCommandRow {
cell.textField?.stringValue = "Conjure Translate"
cell.textField?.setAccessibilityLabel(nil)
} else if results[row].command != nil {
let entry = results[row]
cell.textField?.stringValue = entry.name
cell.textField?.setAccessibilityLabel(history.isFavorite(entry) ? "\(entry.name), Favorite" : nil)
cell.locationLabel.isHidden = true
cell.kindLabel.stringValue = "Command"
cell.kindLabel.stringValue = history.isFavorite(entry) ? "★ Command" : "Command"
cell.imageView?.image = NSImage(systemSymbolName: "character.bubble", accessibilityDescription: nil)
cell.imageView?.contentTintColor = SunsetPalette.accent
cell.toolTip = "Translate English to Japanese"
@@ -723,7 +719,6 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
@objc private func openSelection() {
guard !isLaunching, translation == nil, launcherFrameBeforeReview == nil, window?.attachedSheet == nil else { return }
if tableView.selectedRow == translationCommandRow { showTranslation(); return }
if let calculation {
guard tableView.selectedRow == 0, let value = calculation.copyValue else { return }
let pasteboard = NSPasteboard.general
@@ -749,7 +744,14 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
}
private func openApplication(_ application: Application) {
guard !isLaunching else { return }
guard !isLaunching, translation == nil, launcherFrameBeforeReview == nil, window?.attachedSheet == nil else { return }
if let command = application.command {
switch command {
case .translate: showTranslation()
}
history.recordLaunch(of: application)
return
}
(window as? LauncherPanel)?.resetNumberHints()
isLaunching = true
updateOpenButton()