Browse Source

feat: executive voice slash commands for instant app launching and tab control

main
Ali Alavi 16 hours ago
parent
commit
b4d67685f8
  1. 20
      src/AppDelegate.swift
  2. 10
      src/SonioxSettings.swift
  3. 10
      src/StatusBarController.swift
  4. 435
      src/VoiceCommandExecutor.swift

20
src/AppDelegate.swift

@ -120,13 +120,23 @@ public final class AppDelegate: NSObject, NSApplicationDelegate {
switch result {
case .success(let text):
let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines)
if !trimmed.isEmpty {
let processed = TextProcessor.shared.process(text: trimmed)
if !processed.isEmpty {
HistoryManager.shared.addEntry(processed)
self.pasteTextToFrontmostApp(text: processed)
guard !trimmed.isEmpty else { return }
// 1. Check for Executive Voice Slash Commands
if SonioxSettings.shared.voiceCommands && VoiceCommandExecutor.shared.isSlashCommand(text: trimmed) {
let handled = VoiceCommandExecutor.shared.execute(rawText: trimmed)
if handled {
print("AppDelegate: Successfully executed slash command for: \(trimmed)")
return // Handled as executive command do not paste text!
}
}
// 2. Normal dictation processing & paste
let processed = TextProcessor.shared.process(text: trimmed)
if !processed.isEmpty {
HistoryManager.shared.addEntry(processed)
self.pasteTextToFrontmostApp(text: processed)
}
case .failure(let error):
print("Soniox finalize error:", error)
}

10
src/SonioxSettings.swift

@ -94,6 +94,16 @@ public final class SonioxSettings {
}
}
public var voiceCommands: Bool {
get {
if defaults.object(forKey: "SonioxVoiceCommands") == nil { return true }
return defaults.bool(forKey: "SonioxVoiceCommands")
}
set {
defaults.set(newValue, forKey: "SonioxVoiceCommands")
}
}
public var domainVocabulary: Bool {
get {
if defaults.object(forKey: "SonioxDomainVocabulary") == nil { return true }

10
src/StatusBarController.swift

@ -164,6 +164,11 @@ public final class StatusBarController: NSObject, NSMenuDelegate {
// 8. Advanced Options Submenu
let advancedMenu = NSMenu()
let slashCmdItem = NSMenuItem(title: "⚡️ Executive Voice Slash Commands", action: #selector(toggleVoiceCommandsAction(_:)), keyEquivalent: "")
slashCmdItem.target = self
slashCmdItem.state = SonioxSettings.shared.voiceCommands ? .on : .off
advancedMenu.addItem(slashCmdItem)
let punctItem = NSMenuItem(title: "🗣️ Voice Punctuation Commands", action: #selector(togglePunctuationAction(_:)), keyEquivalent: "")
punctItem.target = self
punctItem.state = SonioxSettings.shared.voicePunctuation ? .on : .off
@ -351,6 +356,11 @@ public final class StatusBarController: NSObject, NSMenuDelegate {
buildMenu()
}
@objc private func toggleVoiceCommandsAction(_ sender: NSMenuItem) {
SonioxSettings.shared.voiceCommands.toggle()
buildMenu()
}
@objc private func togglePunctuationAction(_ sender: NSMenuItem) {
SonioxSettings.shared.voicePunctuation.toggle()
buildMenu()

435
src/VoiceCommandExecutor.swift

@ -0,0 +1,435 @@
import Cocoa
import Carbon
public final class VoiceCommandExecutor {
public static let shared = VoiceCommandExecutor()
private init() {}
public func isSlashCommand(text: String) -> Bool {
let trimmed = text.trimmingCharacters(in: .whitespacesAndNewlines).lowercased()
return trimmed.hasPrefix("اسلش") ||
trimmed.hasPrefix("slash") ||
trimmed.hasPrefix("/") ||
trimmed.hasPrefix("\\")
}
@discardableResult
public func execute(rawText: String) -> Bool {
let trimmed = rawText.trimmingCharacters(in: .whitespacesAndNewlines)
guard isSlashCommand(text: trimmed) else { return false }
// Strip trigger prefix
var body = trimmed
if body.lowercased().hasPrefix("اسلش") {
body = String(body.dropFirst(4)).trimmingCharacters(in: .whitespacesAndNewlines)
} else if body.lowercased().hasPrefix("slash") {
body = String(body.dropFirst(5)).trimmingCharacters(in: .whitespacesAndNewlines)
} else if body.hasPrefix("/") || body.hasPrefix("\\") {
body = String(body.dropFirst(1)).trimmingCharacters(in: .whitespacesAndNewlines)
}
body = body.trimmingCharacters(in: CharacterSet(charactersIn: ":,،.؛ -_"))
let lower = body.lowercased()
print("VoiceCommandExecutor: executing command for body: '\(body)'")
// 1. App with Tab combinations (e.g. "فایرفاکس تب ۱", "کروم تب ۲")
if handleAppWithTab(lower: lower, original: body) {
playConfirmationSound()
return true
}
// 2. Standalone Tab commands (e.g. "تب ۱", "تب جدید", "بستن تب")
if handleStandaloneTab(lower: lower) {
playConfirmationSound()
return true
}
// 3. System controls (volume, lock, screenshot, dark mode, clipboard)
if handleSystemControls(lower: lower) {
playConfirmationSound()
return true
}
// 4. Named applications (Antigravity IDE, Antigravity, Settings, Firefox, etc.)
if handleNamedApp(lower: lower, original: body) {
playConfirmationSound()
return true
}
// 5. Generic application launch fallback
if handleGenericApp(original: body) {
playConfirmationSound()
return true
}
return false
}
private func handleAppWithTab(lower: String, original: String) -> Bool {
var targetApp: String? = nil
if lower.contains("فایرفاکس") || lower.contains("firefox") {
targetApp = "Firefox"
} else if lower.contains("کروم") || lower.contains("chrome") {
targetApp = "Google Chrome"
} else if lower.contains("سافاری") || lower.contains("safari") {
targetApp = "Safari"
}
guard let appName = targetApp else { return false }
if let tabNum = extractTabNumber(from: lower) {
activateApp(named: appName) {
self.switchToTab(number: tabNum)
}
HistoryManager.shared.addEntry("⚡️ [Command] \(appName): Tab \(tabNum)")
return true
} else if lower.contains("تب جدید") || lower.contains("new tab") {
activateApp(named: appName) {
self.pressHotkey(keyCode: 17, modifiers: .maskCommand) // Cmd+T
}
HistoryManager.shared.addEntry("⚡️ [Command] \(appName): New Tab")
return true
} else if lower.contains("بستن تب") || lower.contains("close tab") {
activateApp(named: appName) {
self.pressHotkey(keyCode: 13, modifiers: .maskCommand) // Cmd+W
}
HistoryManager.shared.addEntry("⚡️ [Command] \(appName): Close Tab")
return true
}
return false
}
private func extractTabNumber(from text: String) -> Int? {
let t = text.lowercased()
if t.contains("تب ۱") || t.contains("تب 1") || t.contains("تب یک") || t.contains("تب اول") || t.contains("tab 1") || t.contains("tab one") {
return 1
}
if t.contains("تب ۲") || t.contains("تب 2") || t.contains("تب دو") || t.contains("تب دوم") || t.contains("tab 2") || t.contains("tab two") {
return 2
}
if t.contains("تب ۳") || t.contains("تب 3") || t.contains("تب سه") || t.contains("تب سوم") || t.contains("tab 3") || t.contains("tab three") {
return 3
}
if t.contains("تب ۴") || t.contains("تب 4") || t.contains("تب چهار") || t.contains("تب چهارم") || t.contains("tab 4") || t.contains("tab four") {
return 4
}
if t.contains("تب ۵") || t.contains("تب 5") || t.contains("تب پنج") || t.contains("تب پنجم") || t.contains("tab 5") || t.contains("tab five") {
return 5
}
if t.contains("تب ۶") || t.contains("تب 6") || t.contains("تب شش") || t.contains("تب ششم") || t.contains("tab 6") || t.contains("tab six") {
return 6
}
if t.contains("تب ۷") || t.contains("تب 7") || t.contains("تب هفت") || t.contains("تب هفتم") || t.contains("tab 7") || t.contains("tab seven") {
return 7
}
if t.contains("تب ۸") || t.contains("تب 8") || t.contains("تب هشت") || t.contains("تب هشتم") || t.contains("tab 8") || t.contains("tab eight") {
return 8
}
if t.contains("تب ۹") || t.contains("تب 9") || t.contains("تب نه") || t.contains("تب آخر") || t.contains("tab 9") || t.contains("tab last") {
return 9
}
return nil
}
private func handleStandaloneTab(lower: String) -> Bool {
if let num = extractTabNumber(from: lower) {
switchToTab(number: num)
HistoryManager.shared.addEntry("⚡️ [Command] Switch to Tab \(num)")
return true
}
if lower.contains("تب جدید") || lower.contains("نیو تب") || lower.contains("new tab") {
pressHotkey(keyCode: 17, modifiers: .maskCommand) // Cmd+T
HistoryManager.shared.addEntry("⚡️ [Command] New Tab")
return true
}
if lower.contains("بستن تب") || lower.contains("کلوز تب") || lower.contains("close tab") {
pressHotkey(keyCode: 13, modifiers: .maskCommand) // Cmd+W
HistoryManager.shared.addEntry("⚡️ [Command] Close Tab")
return true
}
if lower.contains("تب بعد") || lower.contains("تب بعدی") || lower.contains("next tab") {
pressHotkey(keyCode: 48, modifiers: .maskControl) // Ctrl+Tab
HistoryManager.shared.addEntry("⚡️ [Command] Next Tab")
return true
}
if lower.contains("تب قبل") || lower.contains("تب قبلی") || lower.contains("prev tab") || lower.contains("previous tab") {
pressHotkey(keyCode: 48, modifiers: [.maskControl, .maskShift]) // Ctrl+Shift+Tab
HistoryManager.shared.addEntry("⚡️ [Command] Previous Tab")
return true
}
if lower.contains("رفرش") || lower.contains("ریلود") || lower.contains("reload") || lower.contains("refresh") {
pressHotkey(keyCode: 15, modifiers: .maskCommand) // Cmd+R
HistoryManager.shared.addEntry("⚡️ [Command] Refresh Page")
return true
}
return false
}
private func handleNamedApp(lower: String, original: String) -> Bool {
// System Settings
if lower.contains("ستینگ") || lower.contains("settings") || lower.contains("تنظیمات") || lower.contains("سیستم ستینگ") {
launchOrActivateApp(named: "System Settings")
HistoryManager.shared.addEntry("⚡️ [Command] Open System Settings")
return true
}
// Antigravity IDE
if lower.contains("آنتی گرویتی آیدی ای") || lower.contains("آنتی گرویتی آی دی ای") || lower.contains("antigravity ide") || lower.contains("آنتیگرویتی آیدی ای") {
launchOrActivateApp(named: "Antigravity IDE")
HistoryManager.shared.addEntry("⚡️ [Command] Open Antigravity IDE")
return true
}
// Antigravity
if lower.contains("آنتی گرویتی") || lower.contains("انتی گرویتی") || lower.contains("آنتیگرویتی") || lower.contains("antigravity") {
launchOrActivateApp(named: "Antigravity")
HistoryManager.shared.addEntry("⚡️ [Command] Open Antigravity")
return true
}
// Firefox
if lower.contains("فایرفاکس") || lower.contains("firefox") {
launchOrActivateApp(named: "Firefox")
HistoryManager.shared.addEntry("⚡️ [Command] Open Firefox")
return true
}
// Google Chrome
if lower.contains("گوگل کروم") || lower.contains("کروم") || lower.contains("chrome") {
launchOrActivateApp(named: "Google Chrome")
HistoryManager.shared.addEntry("⚡️ [Command] Open Google Chrome")
return true
}
// Telegram
if lower.contains("تلگرام") || lower.contains("telegram") {
launchOrActivateApp(named: "Telegram")
HistoryManager.shared.addEntry("⚡️ [Command] Open Telegram")
return true
}
// TeamBy
if lower.contains("تیم بای") || lower.contains("تیمبی") || lower.contains("teamby") {
launchOrActivateApp(named: "TeamBy")
HistoryManager.shared.addEntry("⚡️ [Command] Open TeamBy")
return true
}
// Terminal
if lower.contains("ترمینال") || lower.contains("terminal") {
launchOrActivateApp(named: "Terminal")
HistoryManager.shared.addEntry("⚡️ [Command] Open Terminal")
return true
}
// Finder
if lower.contains("فایندر") || lower.contains("finder") {
launchOrActivateApp(named: "Finder")
HistoryManager.shared.addEntry("⚡️ [Command] Open Finder")
return true
}
// VS Code
if lower.contains("وی اس کد") || lower.contains("vscode") || lower == "کد" || lower == "code" {
launchOrActivateApp(named: "Visual Studio Code")
HistoryManager.shared.addEntry("⚡️ [Command] Open Visual Studio Code")
return true
}
// Figma
if lower.contains("فیگما") || lower.contains("figma") {
launchOrActivateApp(named: "Figma")
HistoryManager.shared.addEntry("⚡️ [Command] Open Figma")
return true
}
// Safari
if lower.contains("سافاری") || lower.contains("safari") {
launchOrActivateApp(named: "Safari")
HistoryManager.shared.addEntry("⚡️ [Command] Open Safari")
return true
}
// Calculator
if lower.contains("ماشین حساب") || lower.contains("calculator") {
launchOrActivateApp(named: "Calculator")
HistoryManager.shared.addEntry("⚡️ [Command] Open Calculator")
return true
}
// Calendar
if lower.contains("تقویم") || lower.contains("calendar") {
launchOrActivateApp(named: "Calendar")
HistoryManager.shared.addEntry("⚡️ [Command] Open Calendar")
return true
}
// Notes
if lower.contains("یادداشت") || lower.contains("نوت") || lower.contains("notes") {
launchOrActivateApp(named: "Notes")
HistoryManager.shared.addEntry("⚡️ [Command] Open Notes")
return true
}
return false
}
private func handleSystemControls(lower: String) -> Bool {
// Volume controls
if lower.contains("صدا زیاد") || lower.contains("افزایش صدا") || lower.contains("volume up") {
runAppleScript("set volume output volume ((output volume of (get volume settings)) + 15)")
HistoryManager.shared.addEntry("⚡️ [Command] Volume Up")
return true
}
if lower.contains("صدا کم") || lower.contains("کاهش صدا") || lower.contains("volume down") {
runAppleScript("set volume output volume ((output volume of (get volume settings)) - 15)")
HistoryManager.shared.addEntry("⚡️ [Command] Volume Down")
return true
}
if lower.contains("میوت") || lower.contains("بی صدا") || lower.contains("بی‌صدا") || lower.contains("mute") {
runAppleScript("set volume output muted not (output muted of (get volume settings))")
HistoryManager.shared.addEntry("⚡️ [Command] Toggle Mute")
return true
}
// Lock screen
if lower.contains("قفل") || lower.contains("لاک") || lower.contains("lock screen") || lower.contains("lock") {
let task = Process()
task.executableURL = URL(fileURLWithPath: "/usr/bin/pmset")
task.arguments = ["displaysleepnow"]
try? task.run()
HistoryManager.shared.addEntry("⚡️ [Command] Lock Screen")
return true
}
// Screenshot
if lower.contains("اسکرین شات") || lower.contains("عکس صفحه") || lower.contains("screenshot") {
let task = Process()
task.executableURL = URL(fileURLWithPath: "/usr/sbin/screencapture")
task.arguments = ["-i", "-c"]
try? task.run()
HistoryManager.shared.addEntry("⚡️ [Command] Interactive Screenshot")
return true
}
// Dark Mode toggle
if lower.contains("دارک مود") || lower.contains("لایت مود") || lower.contains("حالت شب") || lower.contains("dark mode") {
runAppleScript("tell application \"System Events\" to tell appearance preferences to set dark mode to not dark mode")
HistoryManager.shared.addEntry("⚡️ [Command] Toggle Dark Mode")
return true
}
// Editing controls
if lower == "کپی" || lower == "copy" {
pressHotkey(keyCode: 8, modifiers: .maskCommand) // Cmd+C
return true
}
if lower == "پیست" || lower == "paste" {
pressHotkey(keyCode: 9, modifiers: .maskCommand) // Cmd+V
return true
}
if lower == "کات" || lower == "cut" {
pressHotkey(keyCode: 7, modifiers: .maskCommand) // Cmd+X
return true
}
if lower.contains("انتخاب همه") || lower.contains("سلکت آل") || lower == "select all" {
pressHotkey(keyCode: 0, modifiers: .maskCommand) // Cmd+A
return true
}
if lower == "آندو" || lower == "برگرد" || lower == "undo" {
pressHotkey(keyCode: 6, modifiers: .maskCommand) // Cmd+Z
return true
}
if lower == "اینتر" || lower == "enter" {
pressHotkey(keyCode: 36, modifiers: []) // Return
return true
}
return false
}
private func handleGenericApp(original: String) -> Bool {
var appCandidate = original
if appCandidate.hasPrefix("باز کن ") || appCandidate.hasPrefix("بازکن ") {
appCandidate = String(appCandidate.dropFirst(7)).trimmingCharacters(in: .whitespacesAndNewlines)
} else if appCandidate.lowercased().hasPrefix("open ") {
appCandidate = String(appCandidate.dropFirst(5)).trimmingCharacters(in: .whitespacesAndNewlines)
}
guard !appCandidate.isEmpty else { return false }
let ws = NSWorkspace.shared
let appURL = ws.urlForApplication(withBundleIdentifier: appCandidate) ??
URL(fileURLWithPath: "/Applications/\(appCandidate).app")
if FileManager.default.fileExists(atPath: appURL.path) || ws.urlForApplication(withBundleIdentifier: appCandidate) != nil {
launchOrActivateApp(named: appCandidate)
HistoryManager.shared.addEntry("⚡️ [Command] Open \(appCandidate)")
return true
}
return false
}
public func switchToTab(number: Int) {
let keyCodes: [Int: CGKeyCode] = [
1: 18, 2: 19, 3: 20, 4: 21, 5: 23,
6: 22, 7: 26, 8: 28, 9: 25
]
guard let code = keyCodes[number] else { return }
pressHotkey(keyCode: code, modifiers: .maskCommand)
}
private func pressHotkey(keyCode: CGKeyCode, modifiers: CGEventFlags) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.08) {
let source = CGEventSource(stateID: .combinedSessionState)
if let down = CGEvent(keyboardEventSource: source, virtualKey: keyCode, keyDown: true),
let up = CGEvent(keyboardEventSource: source, virtualKey: keyCode, keyDown: false) {
down.flags = modifiers
up.flags = modifiers
down.post(tap: .cghidEventTap)
up.post(tap: .cghidEventTap)
}
}
}
private func launchOrActivateApp(named appName: String) {
let ws = NSWorkspace.shared
if let running = ws.runningApplications.first(where: {
$0.localizedName?.localizedCaseInsensitiveContains(appName) == true
}) {
running.activate(options: [.activateIgnoringOtherApps])
return
}
let task = Process()
task.executableURL = URL(fileURLWithPath: "/usr/bin/open")
task.arguments = ["-a", appName]
try? task.run()
}
private func activateApp(named appName: String, completion: @escaping () -> Void) {
launchOrActivateApp(named: appName)
DispatchQueue.main.asyncAfter(deadline: .now() + 0.22) {
completion()
}
}
private func runAppleScript(_ script: String) {
let full = "osascript -e '" + script.replacingOccurrences(of: "'", with: "'\\''") + "'"
let task = Process()
task.executableURL = URL(fileURLWithPath: "/bin/sh")
task.arguments = ["-c", full]
try? task.run()
}
private func playConfirmationSound() {
if SonioxSettings.shared.playSounds {
if !(NSSound(named: NSSound.Name("Hero"))?.play() ?? false) {
NSSound(named: NSSound.Name("Glass"))?.play()
}
}
}
}
Loading…
Cancel
Save