diff --git a/Info.plist b/Info.plist
index a2944e5..ee7475d 100644
--- a/Info.plist
+++ b/Info.plist
@@ -26,6 +26,8 @@
13.0
LSUIElement
+ NSSupportsAppNap
+
NSHighResolutionCapable
NSMicrophoneUsageDescription
diff --git a/package.sh b/package.sh
index 73682a6..5443d45 100755
--- a/package.sh
+++ b/package.sh
@@ -13,7 +13,7 @@ rm -rf "$APP_BUNDLE"
# 1. Compile Swift sources (Pure Native Swift)
swiftc -O -target arm64-apple-macosx13.0 \
- -framework Cocoa -framework AVFoundation -framework Carbon \
+ -framework Cocoa -framework AVFoundation -framework Carbon -framework AppIntents \
"$PROJECT_DIR"/src/*.swift \
-o "$BUILD_DIR/SonioxVoice"
@@ -29,6 +29,10 @@ cp "$BUILD_DIR/SonioxVoice" "$APP_BUNDLE/Contents/MacOS/SonioxVoice"
cp "$PROJECT_DIR/Info.plist" "$APP_BUNDLE/Contents/Info.plist"
cp "$PROJECT_DIR/resources/AppIcon.icns" "$APP_BUNDLE/Contents/Resources/AppIcon.icns"
+if [ -d "$PROJECT_DIR/resources/Metadata.appintents" ]; then
+ cp -R "$PROJECT_DIR/resources/Metadata.appintents" "$APP_BUNDLE/Contents/Resources/"
+fi
+
# 3. Ad-hoc Codesign
codesign --force --deep --sign - "$APP_BUNDLE"
diff --git a/resources/Metadata.appintents/extract.actionsdata b/resources/Metadata.appintents/extract.actionsdata
new file mode 100644
index 0000000..4b12228
--- /dev/null
+++ b/resources/Metadata.appintents/extract.actionsdata
@@ -0,0 +1 @@
+{"actions": {"StartRecordingIntent": {"actionConfiguration": {"actionSummary": {"wrapper": {"otherParameterIdentifiers": []}}}, "assistantDefinedSchemaTraits": [], "assistantDefinedSchemas": [], "authenticationPolicy": 0, "availabilityAnnotations": {"LNPlatformNameIOS": {"introducedVersion": "16.0"}, "LNPlatformNameMACOS": {"introducedVersion": "13.0"}, "LNPlatformNameWildcard": {"introducedVersion": "*"}}, "customIntentClassName": "StartRecordingIntent", "descriptionMetadata": {"descriptionText": {"alternatives": [], "key": "Starts voice recording and real-time word-by-word streaming."}, "searchKeywords": []}, "effectiveBundleIdentifiers": [], "fullyQualifiedTypeName": "SonioxVoice.StartRecordingIntent", "identifier": "StartRecordingIntent", "isAuthPolExplicit": false, "isDiscoverable": true, "mangledTypeName": "11SonioxVoice20StartRecordingIntentV", "mangledTypeNameByBundleIdentifier": {}, "mangledTypeNameByBundleIdentifierV2": {}, "mangledTypeNameV2": "11SonioxVoice20StartRecordingIntentV", "openAppWhenRun": false, "outputFlags": 0, "parameters": [], "presentationStyle": 0, "requiredCapabilities": [], "supportedModes": 1, "systemProtocolMetadata": [], "systemProtocolMetadataV2": [], "systemProtocols": [], "title": {"alternatives": [], "key": "Start Recording"}, "typeSpecificMetadata": [], "visibilityMetadata": {"assistantOnly": false, "isDiscoverable": true}}, "StopRecordingIntent": {"actionConfiguration": {"actionSummary": {"wrapper": {"otherParameterIdentifiers": []}}}, "assistantDefinedSchemaTraits": [], "assistantDefinedSchemas": [], "authenticationPolicy": 0, "availabilityAnnotations": {"LNPlatformNameIOS": {"introducedVersion": "16.0"}, "LNPlatformNameMACOS": {"introducedVersion": "13.0"}, "LNPlatformNameWildcard": {"introducedVersion": "*"}}, "customIntentClassName": "StopRecordingIntent", "descriptionMetadata": {"descriptionText": {"alternatives": [], "key": "Stops active recording and finalizes text paste."}, "searchKeywords": []}, "effectiveBundleIdentifiers": [], "fullyQualifiedTypeName": "SonioxVoice.StopRecordingIntent", "identifier": "StopRecordingIntent", "isAuthPolExplicit": false, "isDiscoverable": true, "mangledTypeName": "11SonioxVoice19StopRecordingIntentV", "mangledTypeNameByBundleIdentifier": {}, "mangledTypeNameByBundleIdentifierV2": {}, "mangledTypeNameV2": "11SonioxVoice19StopRecordingIntentV", "openAppWhenRun": false, "outputFlags": 0, "parameters": [], "presentationStyle": 0, "requiredCapabilities": [], "supportedModes": 1, "systemProtocolMetadata": [], "systemProtocolMetadataV2": [], "systemProtocols": [], "title": {"alternatives": [], "key": "Stop Recording"}, "typeSpecificMetadata": [], "visibilityMetadata": {"assistantOnly": false, "isDiscoverable": true}}, "ToggleRecordingIntent": {"actionConfiguration": {"actionSummary": {"wrapper": {"otherParameterIdentifiers": []}}}, "assistantDefinedSchemaTraits": [], "assistantDefinedSchemas": [], "authenticationPolicy": 0, "availabilityAnnotations": {"LNPlatformNameIOS": {"introducedVersion": "16.0"}, "LNPlatformNameMACOS": {"introducedVersion": "13.0"}, "LNPlatformNameWildcard": {"introducedVersion": "*"}}, "customIntentClassName": "ToggleRecordingIntent", "descriptionMetadata": {"descriptionText": {"alternatives": [], "key": "Toggles voice recording on or off."}, "searchKeywords": []}, "effectiveBundleIdentifiers": [], "fullyQualifiedTypeName": "SonioxVoice.ToggleRecordingIntent", "identifier": "ToggleRecordingIntent", "isAuthPolExplicit": false, "isDiscoverable": true, "mangledTypeName": "11SonioxVoice21ToggleRecordingIntentV", "mangledTypeNameByBundleIdentifier": {}, "mangledTypeNameByBundleIdentifierV2": {}, "mangledTypeNameV2": "11SonioxVoice21ToggleRecordingIntentV", "openAppWhenRun": false, "outputFlags": 0, "parameters": [], "presentationStyle": 0, "requiredCapabilities": [], "supportedModes": 1, "systemProtocolMetadata": [], "systemProtocolMetadataV2": [], "systemProtocols": [], "title": {"alternatives": [], "key": "Toggle Recording"}, "typeSpecificMetadata": [], "visibilityMetadata": {"assistantOnly": false, "isDiscoverable": true}}}, "assistantEntities": [], "assistantIntentNegativePhrases": [], "assistantIntents": [], "autoShortcutProviderMangledName": "", "autoShortcuts": [], "entities": {}, "enums": [], "generator": {"name": "xcode-tools", "version": "17C52"}, "negativePhrases": [], "queries": {}, "shortcutTileColor": 14, "version": 1}
\ No newline at end of file
diff --git a/resources/Metadata.appintents/version.json b/resources/Metadata.appintents/version.json
new file mode 100644
index 0000000..d072006
--- /dev/null
+++ b/resources/Metadata.appintents/version.json
@@ -0,0 +1,4 @@
+{
+ "version" : "3.0",
+ "toolsVersion" : "17C52"
+}
diff --git a/src/HotkeyManager.swift b/src/HotkeyManager.swift
index 1f6d103..c8056ce 100644
--- a/src/HotkeyManager.swift
+++ b/src/HotkeyManager.swift
@@ -74,6 +74,10 @@ public final class HotkeyManager {
private var isCapsLockPhysicallyDown = false
private var isF5PhysicallyDown = false
private var isFnPhysicallyDown = false
+
+ public var isPhysicalKeyDown: Bool {
+ return isOptionPhysicallyDown || isCapsLockPhysicallyDown || isF5PhysicallyDown || isFnPhysicallyDown
+ }
private var lastFnPressTime: TimeInterval = 0
private var lastTriggerTime: TimeInterval = 0
diff --git a/src/Intents.swift b/src/Intents.swift
new file mode 100644
index 0000000..e879aa0
--- /dev/null
+++ b/src/Intents.swift
@@ -0,0 +1,85 @@
+import Foundation
+import AppIntents
+
+@available(macOS 13.0, *)
+public struct StartRecordingIntent: AppIntent {
+ public static var title: LocalizedStringResource = "Start Recording"
+ public static var description = IntentDescription("Starts voice recording with Soniox Voice")
+ public static var openAppWhenRun: Bool = false
+
+ public init() {}
+
+ @MainActor
+ public func perform() async throws -> some IntentResult {
+ print("StartRecordingIntent: Executing startRecording via AppIntent...")
+
+ // 1. In-process direct execution if running in the main app
+ if let app = AppDelegate.shared {
+ app.startRecording()
+ }
+
+ // 2. Out-of-process IPC notification (for when Shortcuts runner executes this)
+ DistributedNotificationCenter.default().postNotificationName(
+ NSNotification.Name("com.soniox.voice.start"),
+ object: nil,
+ userInfo: nil,
+ deliverImmediately: true
+ )
+
+ return .result()
+ }
+}
+
+@available(macOS 13.0, *)
+public struct StopRecordingIntent: AppIntent {
+ public static var title: LocalizedStringResource = "Stop Recording"
+ public static var description = IntentDescription("Stops active voice recording and finalizes text paste")
+ public static var openAppWhenRun: Bool = false
+
+ public init() {}
+
+ @MainActor
+ public func perform() async throws -> some IntentResult {
+ print("StopRecordingIntent: Executing stopRecording via AppIntent...")
+
+ if let app = AppDelegate.shared {
+ app.stopRecording()
+ }
+
+ DistributedNotificationCenter.default().postNotificationName(
+ NSNotification.Name("com.soniox.voice.stop"),
+ object: nil,
+ userInfo: nil,
+ deliverImmediately: true
+ )
+
+ return .result()
+ }
+}
+
+@available(macOS 13.0, *)
+public struct ToggleRecordingIntent: AppIntent {
+ public static var title: LocalizedStringResource = "Toggle Recording"
+ public static var description = IntentDescription("Toggles voice recording on or off")
+ public static var openAppWhenRun: Bool = false
+
+ public init() {}
+
+ @MainActor
+ public func perform() async throws -> some IntentResult {
+ print("ToggleRecordingIntent: Executing toggleRecording via AppIntent...")
+
+ if let app = AppDelegate.shared {
+ app.toggleRecording()
+ }
+
+ DistributedNotificationCenter.default().postNotificationName(
+ NSNotification.Name("com.soniox.voice.toggle"),
+ object: nil,
+ userInfo: nil,
+ deliverImmediately: true
+ )
+
+ return .result()
+ }
+}