diff --git a/clients/macos/vellum-assistant/App/AppDelegate.swift b/clients/macos/vellum-assistant/App/AppDelegate.swift index e3027ba9b8e..6225cf98ed8 100644 --- a/clients/macos/vellum-assistant/App/AppDelegate.swift +++ b/clients/macos/vellum-assistant/App/AppDelegate.swift @@ -1360,7 +1360,8 @@ public final class AppDelegate: NSObject, NSApplicationDelegate { } let sensitivity = UserDefaults.standard.float(forKey: "wakeWordSensitivity") - let engine = PorcupineWakeWordEngine(sensitivity: sensitivity > 0 ? sensitivity : 0.5) + let keyword = UserDefaults.standard.string(forKey: "wakeWordKeyword") ?? "computer" + let engine = PorcupineWakeWordEngine(sensitivity: sensitivity > 0 ? sensitivity : 0.5, keyword: keyword) let audioMonitor = AlwaysOnAudioMonitor(engine: engine) let coordinator = WakeWordCoordinator( diff --git a/clients/macos/vellum-assistant/Features/Settings/WakeWordSettingsView.swift b/clients/macos/vellum-assistant/Features/Settings/WakeWordSettingsView.swift index 7a11141ead1..927e85de852 100644 --- a/clients/macos/vellum-assistant/Features/Settings/WakeWordSettingsView.swift +++ b/clients/macos/vellum-assistant/Features/Settings/WakeWordSettingsView.swift @@ -7,6 +7,7 @@ struct WakeWordSettingsView: View { @AppStorage("wakeWordEnabled") private var wakeWordEnabled: Bool = false @AppStorage("wakeWordSensitivity") private var wakeWordSensitivity: Double = 0.5 @AppStorage("wakeWordTimeoutSeconds") private var wakeWordTimeoutSeconds: Int = 30 + @AppStorage("wakeWordKeyword") private var wakeWordKeyword: String = "computer" @State private var picovoiceKeyText: String = "" @@ -14,6 +15,7 @@ struct WakeWordSettingsView: View { VStack(alignment: .leading, spacing: VSpacing.xl) { statusSection enableSection + keywordSection accessKeySection sensitivitySection timeoutSection @@ -31,7 +33,7 @@ struct WakeWordSettingsView: View { .font(.system(size: 14)) .foregroundColor(wakeWordEnabled ? VColor.success : VColor.textMuted) - Text(wakeWordEnabled ? "Listening for \"hey vellum\"" : "Wake word disabled") + Text(wakeWordEnabled ? "Listening for \"\(wakeWordKeyword)\"" : "Wake word disabled") .font(VFont.body) .foregroundColor(wakeWordEnabled ? VColor.textPrimary : VColor.textSecondary) @@ -54,7 +56,7 @@ struct WakeWordSettingsView: View { Text("Enable wake word listening") .font(VFont.body) .foregroundColor(VColor.textSecondary) - Text("Activate the assistant by saying \"hey vellum\" instead of using a keyboard shortcut.") + Text("Activate the assistant by saying the wake word instead of using a keyboard shortcut.") .font(VFont.caption) .foregroundColor(VColor.textMuted) } @@ -69,6 +71,45 @@ struct WakeWordSettingsView: View { .vCard(background: VColor.surfaceSubtle) } + // MARK: - Keyword + + private var keywordSection: some View { + VStack(alignment: .leading, spacing: VSpacing.md) { + Text("Keyword") + .font(VFont.sectionTitle) + .foregroundColor(VColor.textPrimary) + + HStack { + Text("Keyword") + .font(VFont.body) + .foregroundColor(VColor.textSecondary) + Spacer() + Picker("", selection: $wakeWordKeyword) { + Text("Computer").tag("computer") + Text("Jarvis").tag("jarvis") + Text("Alexa").tag("alexa") + Text("Hey Siri").tag("hey siri") + Text("Picovoice").tag("picovoice") + Text("Porcupine").tag("porcupine") + Text("Terminator").tag("terminator") + Text("Bumblebee").tag("bumblebee") + Text("Blueberry").tag("blueberry") + Text("Grapefruit").tag("grapefruit") + Text("Grasshopper").tag("grasshopper") + } + .pickerStyle(.menu) + .frame(width: 160) + .accessibilityLabel("Wake word keyword") + } + + Text("The keyword that triggers voice activation. Requires restart of wake word listening to take effect.") + .font(VFont.caption) + .foregroundColor(VColor.textMuted) + } + .padding(VSpacing.lg) + .vCard(background: VColor.surfaceSubtle) + } + // MARK: - Access Key private var accessKeySection: some View { diff --git a/clients/macos/vellum-assistant/Features/Voice/WakeWord/PorcupineWakeWordEngine.swift b/clients/macos/vellum-assistant/Features/Voice/WakeWord/PorcupineWakeWordEngine.swift index 50eeeefb3a8..168f3a36027 100644 --- a/clients/macos/vellum-assistant/Features/Voice/WakeWord/PorcupineWakeWordEngine.swift +++ b/clients/macos/vellum-assistant/Features/Voice/WakeWord/PorcupineWakeWordEngine.swift @@ -44,7 +44,7 @@ final class PorcupineWakeWordEngine: WakeWordEngine { guard !isRunning else { return } // 1. Access key - guard let accessKey = APIKeyManager.shared.getAPIKey(provider: "picovoice") else { + guard let accessKey = APIKeyManager.getKey(for: "picovoice") else { log.warning("Picovoice access key not found in keychain — wake word detection disabled") return }