|
|
@ -662,35 +662,38 @@ public final class VoiceCommandExecutor { |
|
|
let t = text.lowercased() |
|
|
let t = text.lowercased() |
|
|
let words = t.components(separatedBy: .whitespacesAndNewlines) |
|
|
let words = t.components(separatedBy: .whitespacesAndNewlines) |
|
|
|
|
|
|
|
|
// 1. Direct digits (English and Persian) |
|
|
|
|
|
for i in 1...9 { |
|
|
|
|
|
let enDig = "\(i)" |
|
|
|
|
|
let faDigs = ["۱", "۲", "۳", "۴", "۵", "۶", "۷", "۸", "۹"] |
|
|
|
|
|
let faDig = faDigs[i - 1] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 1. Direct digits (English and Persian) anywhere inside words (e.g. "تلگرام ۱", "تلگرام1", "اکانت۱") |
|
|
for w in words { |
|
|
for w in words { |
|
|
let cleanW = w.trimmingCharacters(in: CharacterSet(charactersIn: ".,،؛:!؟?-_")) |
|
|
|
|
|
if cleanW == enDig || cleanW == faDig { |
|
|
|
|
|
return i |
|
|
|
|
|
|
|
|
let cleanW = w.trimmingCharacters(in: CharacterSet(charactersIn: ".,،؛:!؟?-_/\"'()[]{}«»—–")) |
|
|
|
|
|
for ch in cleanW { |
|
|
|
|
|
if let d = Int(String(ch)), d >= 1 && d <= 9 { |
|
|
|
|
|
return d |
|
|
|
|
|
} |
|
|
|
|
|
let faDigits: [Character: Int] = [ |
|
|
|
|
|
"۱": 1, "۲": 2, "۳": 3, "۴": 4, "۵": 5, "۶": 6, "۷": 7, "۸": 8, "۹": 9, |
|
|
|
|
|
"١": 1, "٢": 2, "٣": 3, "٤": 4, "٥": 5, "٦": 6, "٧": 7, "٨": 8, "٩": 9 |
|
|
|
|
|
] |
|
|
|
|
|
if let d = faDigits[ch] { |
|
|
|
|
|
return d |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// 2. Word numbers (Persian & English cardinal and ordinal) |
|
|
|
|
|
|
|
|
// 2. Comprehensive Word numbers (Persian & English cardinal, ordinal, colloquial) |
|
|
let numberWords: [(names: [String], num: Int)] = [ |
|
|
let numberWords: [(names: [String], num: Int)] = [ |
|
|
(["یک", "اول", "نخست", "ی", "one", "first"], 1), |
|
|
|
|
|
(["دو", "دوم", "د", "two", "second"], 2), |
|
|
|
|
|
(["سه", "سوم", "س", "three", "third"], 3), |
|
|
|
|
|
(["چهار", "چهارم", "four", "fourth"], 4), |
|
|
|
|
|
(["پنج", "پنجم", "five", "fifth"], 5), |
|
|
|
|
|
(["شش", "ششم", "six", "sixth"], 6), |
|
|
|
|
|
(["هفت", "هفتم", "seven", "seventh"], 7), |
|
|
|
|
|
(["هشت", "هشتم", "eight", "eighth"], 8), |
|
|
|
|
|
(["نه", "نهم", "اخر", "آخر", "nine", "ninth", "last"], 9) |
|
|
|
|
|
|
|
|
(["یک", "یکی", "اول", "اولی", "اولین", "یکمی", "نخست", "ی", "وان", "one", "first"], 1), |
|
|
|
|
|
(["دو", "دومی", "دومین", "دوتا", "دوتایی", "د", "تو", "two", "second"], 2), |
|
|
|
|
|
(["سه", "سومی", "سومین", "سه تا", "سه تایی", "س", "تری", "three", "third"], 3), |
|
|
|
|
|
(["چهار", "چهارم", "چهارمی", "چهارمین", "چار", "چاری", "چارم", "چارمی", "فور", "four", "fourth"], 4), |
|
|
|
|
|
(["پنج", "پنجم", "پنجمی", "پنجمین", "پنج تا", "فایو", "five", "fifth"], 5), |
|
|
|
|
|
(["شش", "ششم", "ششمی", "ششمین", "شیش", "شیشم", "شیشمی", "شیشمین", "سیکس", "six", "sixth"], 6), |
|
|
|
|
|
(["هفت", "هفتم", "هفتمی", "هفتمین", "سون", "seven", "seventh"], 7), |
|
|
|
|
|
(["هشت", "هشتم", "هشتمی", "هشتمین", "ایت", "eight", "eighth"], 8), |
|
|
|
|
|
(["نه", "نهم", "نهمی", "نهمین", "ناین", "اخر", "آخر", "اخری", "آخری", "آخرین", "nine", "ninth", "last"], 9) |
|
|
] |
|
|
] |
|
|
|
|
|
|
|
|
for w in words { |
|
|
for w in words { |
|
|
let cleanW = w.trimmingCharacters(in: CharacterSet(charactersIn: ".,،؛:!؟?-_")) |
|
|
|
|
|
|
|
|
let cleanW = w.trimmingCharacters(in: CharacterSet(charactersIn: ".,،؛:!؟?-_/\"'()[]{}«»—–")) |
|
|
for item in numberWords { |
|
|
for item in numberWords { |
|
|
if item.names.contains(cleanW) { |
|
|
if item.names.contains(cleanW) { |
|
|
return item.num |
|
|
return item.num |
|
|
@ -706,7 +709,8 @@ public final class VoiceCommandExecutor { |
|
|
var isTelegram = false |
|
|
var isTelegram = false |
|
|
var isAntigravity = false |
|
|
var isAntigravity = false |
|
|
|
|
|
|
|
|
if normalized.contains("تلگرام") || normalized.contains("telegram") { |
|
|
|
|
|
|
|
|
if normalized.contains("تلگرام") || normalized.contains("telegram") || |
|
|
|
|
|
normalized.contains("اکانت") || normalized.contains("account") || normalized.contains("حساب") { |
|
|
targetApp = "Telegram" |
|
|
targetApp = "Telegram" |
|
|
isTelegram = true |
|
|
isTelegram = true |
|
|
} else if normalized.contains("فایرفاکس") || normalized.contains("firefox") || normalized.contains("فایر فاکس") || normalized.contains("فایرفکس") { |
|
|
} else if normalized.contains("فایرفاکس") || normalized.contains("firefox") || normalized.contains("فایر فاکس") || normalized.contains("فایرفکس") { |
|
|
@ -804,11 +808,17 @@ public final class VoiceCommandExecutor { |
|
|
6: 22, 7: 26, 8: 28, 9: 25 |
|
|
6: 22, 7: 26, 8: 28, 9: 25 |
|
|
] |
|
|
] |
|
|
guard let code = keyCodes[number] else { return } |
|
|
guard let code = keyCodes[number] else { return } |
|
|
// For Telegram Desktop: Send Cmd+[number] and Cmd+Shift+[number] (configured via shortcuts-custom.json) |
|
|
|
|
|
|
|
|
// For Telegram Desktop: Send Cmd+[number], Cmd+Shift+[number], and Ctrl combinations (configured via shortcuts-custom.json) |
|
|
pressHotkey(keyCode: code, modifiers: .maskCommand) |
|
|
pressHotkey(keyCode: code, modifiers: .maskCommand) |
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.02) { |
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.02) { |
|
|
self.pressHotkey(keyCode: code, modifiers: [.maskCommand, .maskShift]) |
|
|
self.pressHotkey(keyCode: code, modifiers: [.maskCommand, .maskShift]) |
|
|
} |
|
|
} |
|
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { |
|
|
|
|
|
self.pressHotkey(keyCode: code, modifiers: .maskControl) |
|
|
|
|
|
} |
|
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.08) { |
|
|
|
|
|
self.pressHotkey(keyCode: code, modifiers: [.maskControl, .maskShift]) |
|
|
|
|
|
} |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
// MARK: - Standalone Tab Commands |
|
|
// MARK: - Standalone Tab Commands |
|
|
@ -1192,7 +1202,7 @@ public final class VoiceCommandExecutor { |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
if let comp = completion { |
|
|
if let comp = completion { |
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.03) { |
|
|
|
|
|
|
|
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.12) { |
|
|
comp() |
|
|
comp() |
|
|
} |
|
|
} |
|
|
} |
|
|
} |
|
|
|