feat: add Finder, ranking reset, and quit actions

This commit is contained in:
2026-10-13 17:38:29 +02:00
parent 7cfd8096a4
commit 400917de3b
7 changed files with 90 additions and 4 deletions
+13
View File
@@ -18,4 +18,17 @@ enum AppLauncher {
}
}
}
static func quit(_ url: URL) throws {
let path = url.standardizedFileURL.resolvingSymlinksInPath().path
for application in NSWorkspace.shared.runningApplications {
guard application.bundleURL?.standardizedFileURL.resolvingSymlinksInPath().path == path,
!application.isTerminated else { continue }
guard application.terminate() else {
throw NSError(domain: "Conjure", code: 1, userInfo: [
NSLocalizedDescriptionKey: "The application refused the quit request. Save your work and quit it manually."
])
}
}
}
}
@@ -6,9 +6,12 @@ final class ApplicationActionsController: NSViewController, NSSearchFieldDelegat
private let search = ActionsSearchField()
private let entries = [
ActionEntry(title: "Open", symbol: "arrow.up.forward.app", color: SunsetPalette.text),
ActionEntry(title: "Show in Finder", symbol: "folder", color: SunsetPalette.text),
ActionEntry(title: "Reset ranking", symbol: "arrow.counterclockwise", color: SunsetPalette.text),
ActionEntry(title: "Quit application", symbol: "stop.circle", color: SunsetPalette.text),
ActionEntry(title: "Uninstall application", symbol: "trash", color: .systemRed)
]
private var visibleEntries = [0, 1]
private var visibleEntries: [Int] = []
private var selectedEntry: Int? = 0
private var rowTopConstraints: [NSLayoutConstraint] = []
private var heightConstraint: NSLayoutConstraint?
@@ -21,6 +24,9 @@ final class ApplicationActionsController: NSViewController, NSSearchFieldDelegat
private var transitionGeneration = 0
private var panel: ActionsPanelView { view as! ActionsPanelView }
var open: (() -> Void)?
var showInFinder: (() -> Void)?
var resetRanking: (() -> Void)?
var quit: (() -> Void)?
var uninstall: (() -> Void)?
var closed: (() -> Void)?
var isShown: Bool { viewIfLoaded?.superview != nil && !isClosing }
@@ -88,7 +94,7 @@ final class ApplicationActionsController: NSViewController, NSSearchFieldDelegat
top
])
}
let height = view.heightAnchor.constraint(equalToConstant: 116 + LauncherRowView.rowHeight)
let height = view.heightAnchor.constraint(equalToConstant: 116 + CGFloat(entries.count - 1) * LauncherRowView.rowHeight)
heightConstraint = height
NSLayoutConstraint.activate([
view.widthAnchor.constraint(equalToConstant: 360), height,
@@ -234,7 +240,14 @@ final class ApplicationActionsController: NSViewController, NSSearchFieldDelegat
private func chooseAction(at index: Int) {
guard isShown, visibleEntries.contains(index) else { return }
close()
if index == 0 { open?() } else { uninstall?() }
switch index {
case 0: open?()
case 1: showInFinder?()
case 2: resetRanking?()
case 3: quit?()
case 4: uninstall?()
default: break
}
}
}
@@ -505,6 +505,16 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
guard !isLaunching, launcherFrameBeforeReview == nil, window?.attachedSheet == nil, results.indices.contains(tableView.selectedRow) else { return }
let application = results[tableView.selectedRow]
actions.open = { [weak self] in self?.openApplication(application) }
actions.showInFinder = { [weak self] in
self?.dismiss(restoringFocus: false)
NSWorkspace.shared.activateFileViewerSelecting([application.url])
}
actions.resetRanking = { [weak self] in
guard let self else { return }
self.history.resetRanking(of: application)
self.updateResults(preservingSelection: true)
}
actions.quit = { [weak self] in self?.quitApplication(application) }
actions.uninstall = { [weak self] in self?.reviewUninstall(application) }
actions.show(for: application, relativeTo: actionsButton)
openButton.keyEquivalent = actions.isShown ? "" : "\r"
@@ -576,6 +586,17 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
openApplication(results[tableView.selectedRow])
}
private func quitApplication(_ application: Application) {
do {
try AppLauncher.quit(application.url)
dismiss()
} catch {
let alert = NSAlert(error: error)
alert.messageText = "Couldn’t quit \(application.name)"
if let window { alert.beginSheetModal(for: window) }
}
}
private func openApplication(_ application: Application) {
guard !isLaunching else { return }
isLaunching = true