Merge pull request #249 from gritse/feature/app-intents-support

Add App Intents support for mini recorder control
This commit is contained in:
Prakash Joshi Pax 2025-08-26 20:57:50 +05:45 committed by GitHub
commit 4dc50ec9ee
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 94 additions and 0 deletions

View File

@ -0,0 +1,33 @@
import AppIntents
import Foundation
struct AppShortcuts : AppShortcutsProvider {
@AppShortcutsBuilder
static var appShortcuts: [AppShortcut] {
AppShortcut(
intent: ToggleMiniRecorderIntent(),
phrases: [
"Toggle \(.applicationName) recorder",
"Start \(.applicationName) recording",
"Stop \(.applicationName) recording",
"Toggle recorder in \(.applicationName)",
"Start recording in \(.applicationName)",
"Stop recording in \(.applicationName)"
],
shortTitle: "Toggle Recorder",
systemImageName: "mic.circle"
)
AppShortcut(
intent: DismissMiniRecorderIntent(),
phrases: [
"Dismiss \(.applicationName) recorder",
"Cancel \(.applicationName) recording",
"Close \(.applicationName) recorder",
"Hide \(.applicationName) recorder"
],
shortTitle: "Dismiss Recorder",
systemImageName: "xmark.circle"
)
}
}

View File

@ -0,0 +1,18 @@
import AppIntents
import Foundation
import AppKit
struct DismissMiniRecorderIntent: AppIntent {
static var title: LocalizedStringResource = "Dismiss VoiceInk Recorder"
static var description = IntentDescription("Dismiss the VoiceInk mini recorder and cancel any active recording.")
static var openAppWhenRun: Bool = false
@MainActor
func perform() async throws -> some IntentResult & ProvidesDialog {
NotificationCenter.default.post(name: .dismissMiniRecorder, object: nil)
let dialog = IntentDialog(stringLiteral: "VoiceInk recorder dismissed")
return .result(dialog: dialog)
}
}

View File

@ -0,0 +1,32 @@
import AppIntents
import Foundation
import AppKit
struct ToggleMiniRecorderIntent: AppIntent {
static var title: LocalizedStringResource = "Toggle VoiceInk Recorder"
static var description = IntentDescription("Start or stop the VoiceInk mini recorder for voice transcription.")
static var openAppWhenRun: Bool = false
@MainActor
func perform() async throws -> some IntentResult & ProvidesDialog {
NotificationCenter.default.post(name: .toggleMiniRecorder, object: nil)
let dialog = IntentDialog(stringLiteral: "VoiceInk recorder toggled")
return .result(dialog: dialog)
}
}
enum IntentError: Error, LocalizedError {
case appNotAvailable
case serviceNotAvailable
var errorDescription: String? {
switch self {
case .appNotAvailable:
return "VoiceInk app is not available"
case .serviceNotAvailable:
return "VoiceInk recording service is not available"
}
}
}

View File

@ -5,6 +5,7 @@ extension Notification.Name {
static let languageDidChange = Notification.Name("languageDidChange")
static let promptDidChange = Notification.Name("promptDidChange")
static let toggleMiniRecorder = Notification.Name("toggleMiniRecorder")
static let dismissMiniRecorder = Notification.Name("dismissMiniRecorder")
static let didChangeModel = Notification.Name("didChangeModel")
static let aiProviderKeyChanged = Notification.Name("aiProviderKeyChanged")
static let licenseStatusChanged = Notification.Name("licenseStatusChanged")

View File

@ -3,6 +3,7 @@ import SwiftData
import Sparkle
import AppKit
import OSLog
import AppIntents
@main
struct VoiceInkApp: App {
@ -82,6 +83,8 @@ struct VoiceInkApp: App {
activeWindowService.configure(with: enhancementService)
activeWindowService.configureWhisperState(whisperState)
_activeWindowService = StateObject(wrappedValue: activeWindowService)
AppShortcuts.updateAppShortcutParameters()
}
var body: some Scene {

View File

@ -90,6 +90,7 @@ extension WhisperState {
func setupNotifications() {
NotificationCenter.default.addObserver(self, selector: #selector(handleToggleMiniRecorder), name: .toggleMiniRecorder, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleDismissMiniRecorder), name: .dismissMiniRecorder, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handleLicenseStatusChanged), name: .licenseStatusChanged, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(handlePromptChange), name: .promptDidChange, object: nil)
}
@ -100,6 +101,12 @@ extension WhisperState {
}
}
@objc public func handleDismissMiniRecorder() {
Task {
await dismissMiniRecorder()
}
}
@objc func handleLicenseStatusChanged() {
self.licenseViewModel = LicenseViewModel()
}