feat: add system theme toggle command
This commit is contained in:
@@ -38,6 +38,7 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
||||
private var emojiPicker: EmojiPickerController?
|
||||
private var notes: NotesController?
|
||||
private let notesDirectory: URL?
|
||||
private let toggleSystemTheme: @MainActor () async throws -> Void
|
||||
private var isCommandOpen: Bool { activeCommand != nil }
|
||||
private var activeCommand: Application.Command?
|
||||
private var pendingPresentation: (() -> Void)?
|
||||
@@ -59,12 +60,14 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
||||
}
|
||||
|
||||
init(history: LaunchHistory = LaunchHistory(), uninstaller: ApplicationUninstaller = ApplicationUninstaller(),
|
||||
defaults: UserDefaults = .standard, pasteboard: NSPasteboard = .general, notesDirectory: URL? = nil) {
|
||||
defaults: UserDefaults = .standard, pasteboard: NSPasteboard = .general, notesDirectory: URL? = nil,
|
||||
toggleSystemTheme: @escaping @MainActor () async throws -> Void = { try await SystemTheme.toggle() }) {
|
||||
self.history = history
|
||||
self.uninstaller = uninstaller
|
||||
self.defaults = defaults
|
||||
self.pasteboard = pasteboard
|
||||
self.notesDirectory = notesDirectory
|
||||
self.toggleSystemTheme = toggleSystemTheme
|
||||
let window = LauncherPanel(
|
||||
contentRect: NSRect(x: 0, y: 0, width: 750, height: 474),
|
||||
styleMask: [.borderless],
|
||||
@@ -145,7 +148,6 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
||||
updateResults()
|
||||
if let command {
|
||||
showCommand(command)
|
||||
history.recordLaunch(of: Application(command: command))
|
||||
}
|
||||
window?.deminiaturize(nil)
|
||||
showWindow(nil)
|
||||
@@ -558,11 +560,14 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
||||
case .notes:
|
||||
cell.imageView?.image = Self.notesIcon
|
||||
cell.toolTip = "Write notes with Markdown and images"
|
||||
case .toggleTheme:
|
||||
cell.imageView?.image = NSImage(systemSymbolName: "circle.lefthalf.filled", accessibilityDescription: nil)
|
||||
cell.toolTip = "Switch macOS between light and dark appearance"
|
||||
default:
|
||||
cell.imageView?.image = Self.translateIcon
|
||||
cell.toolTip = "Translate English to Japanese"
|
||||
}
|
||||
cell.imageView?.contentTintColor = nil
|
||||
cell.imageView?.contentTintColor = entry.command == .toggleTheme ? SunsetPalette.accent : nil
|
||||
} else {
|
||||
let application = results[row]
|
||||
cell.textField?.stringValue = application.name
|
||||
@@ -734,7 +739,6 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
||||
guard !isLaunching, !isCommandOpen, launcherFrameBeforeReview == nil, window?.attachedSheet == nil else { return }
|
||||
if let command = application.command {
|
||||
showCommand(command)
|
||||
history.recordLaunch(of: application)
|
||||
return
|
||||
}
|
||||
(window as? LauncherPanel)?.resetNumberHints()
|
||||
@@ -800,9 +804,28 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
||||
return true
|
||||
}
|
||||
|
||||
private func toggleTheme() {
|
||||
actions.close(animated: false)
|
||||
isLaunching = true
|
||||
updateOpenButton()
|
||||
Task {
|
||||
do {
|
||||
try await toggleSystemTheme()
|
||||
history.recordLaunch(of: Application(command: .toggleTheme))
|
||||
isLaunching = false
|
||||
dismiss()
|
||||
} catch {
|
||||
isLaunching = false
|
||||
let alert = NSAlert(error: error)
|
||||
alert.messageText = "Couldn’t toggle system theme"
|
||||
if let window { await alert.beginSheetModal(for: window) }
|
||||
}
|
||||
updateOpenButton()
|
||||
}
|
||||
}
|
||||
|
||||
private func showCommand(_ command: Application.Command) {
|
||||
guard !isCommandOpen, let content = window?.contentView else { return }
|
||||
activeCommand = command
|
||||
let controller: NSViewController
|
||||
let placeholder: String
|
||||
let accessibilityLabel: String
|
||||
@@ -841,7 +864,12 @@ final class LauncherWindowController: NSWindowController, NSWindowDelegate, NSSe
|
||||
controller = tool
|
||||
placeholder = "Conjure Notes"
|
||||
accessibilityLabel = "Conjure Notes"
|
||||
case .toggleTheme:
|
||||
toggleTheme()
|
||||
return
|
||||
}
|
||||
activeCommand = command
|
||||
history.recordLaunch(of: Application(command: command))
|
||||
let previousQuery = searchField.stringValue
|
||||
let previousPlaceholder = searchField.placeholderAttributedString
|
||||
let close: () -> Void = { [weak self, weak controller] in
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
import Foundation
|
||||
|
||||
enum SystemTheme {
|
||||
static func toggle() async throws {
|
||||
try await Task.detached {
|
||||
guard let script = NSAppleScript(source: """
|
||||
tell application "System Events"
|
||||
tell appearance preferences
|
||||
set dark mode to not dark mode
|
||||
end tell
|
||||
end tell
|
||||
""") else {
|
||||
throw NSError(domain: "Conjure.SystemTheme", code: 1, userInfo: [
|
||||
NSLocalizedDescriptionKey: "Couldn’t create the system appearance script.",
|
||||
])
|
||||
}
|
||||
var error: NSDictionary?
|
||||
script.executeAndReturnError(&error)
|
||||
if let error {
|
||||
let code = error[NSAppleScript.errorNumber] as? Int ?? 1
|
||||
let message = code == -1743
|
||||
? "Allow Conjure to control System Events in System Settings → Privacy & Security → Automation, then try again."
|
||||
: error[NSAppleScript.errorMessage] as? String ?? "macOS couldn’t change the system appearance."
|
||||
throw NSError(domain: "Conjure.SystemTheme", code: code, userInfo: [NSLocalizedDescriptionKey: message])
|
||||
}
|
||||
}.value
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ public struct Application: Identifiable, Sendable {
|
||||
case translate
|
||||
case emoji
|
||||
case notes
|
||||
case toggleTheme
|
||||
}
|
||||
public static let commands = Command.allCases.map { Application(command: $0) }
|
||||
public let command: Command?
|
||||
@@ -22,6 +23,7 @@ public struct Application: Identifiable, Sendable {
|
||||
case .translate: "Conjure Translate"
|
||||
case .emoji: "Conjure Emoji"
|
||||
case .notes: "Conjure Notes"
|
||||
case .toggleTheme: "Conjure Toggle Light/Dark Theme"
|
||||
}
|
||||
self.init(canonicalURL: URL(string: "conjure-command://\(command.rawValue)")!, name: name, command: command)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user