وویس تایپینگ سانی اوکس مک
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

172 lines
6.9 KiB

import Cocoa
public final class StatusBarController {
private var statusItem: NSStatusItem?
public var onToggleRecording: (() -> Void)?
public init() {
setupStatusItem()
}
private func setupStatusItem() {
statusItem = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
updateIcon(state: .idle)
buildMenu()
}
public enum State {
case idle
case recording
case transcribing
}
public func updateIcon(state: State) {
guard let button = statusItem?.button else { return }
switch state {
case .idle:
if let image = NSImage(systemSymbolName: "mic", accessibilityDescription: "Soniox Voice") {
image.isTemplate = true
button.image = image
}
button.toolTip = "Soniox Voice (آماده)"
case .recording:
if let image = NSImage(systemSymbolName: "mic.fill", accessibilityDescription: "Recording") {
image.isTemplate = false
button.image = image
button.contentTintColor = NSColor.systemRed
}
button.toolTip = "در حال ضبط صدا..."
case .transcribing:
if let image = NSImage(systemSymbolName: "waveform", accessibilityDescription: "Transcribing") {
image.isTemplate = false
button.image = image
button.contentTintColor = NSColor.systemOrange
}
button.toolTip = "در حال تبدیل به متن..."
}
}
public func buildMenu(isRecording: Bool = false) {
let menu = NSMenu()
menu.autoenablesItems = false
// 1. Record Action
let recordTitle = isRecording ? "⏹️ توقف ضبط و درج متن" : "🎙️ شروع ضبط صدا (\(HotkeyManager.shared.currentPreset.rawValue))"
let recordItem = NSMenuItem(title: recordTitle, action: #selector(toggleRecordAction), keyEquivalent: "")
recordItem.target = self
menu.addItem(recordItem)
menu.addItem(NSMenuItem.separator())
// 2. Mode Submenu
let modeMenu = NSMenu()
for mode in DictationMode.allCases {
let item = NSMenuItem(title: mode.localizedTitle, action: #selector(selectModeAction(_:)), keyEquivalent: "")
item.target = self
item.representedObject = mode
item.state = (HotkeyManager.shared.currentMode == mode) ? .on : .off
modeMenu.addItem(item)
}
let modeMenuItem = NSMenuItem(title: "⚙️ حالت کارکرد", action: nil, keyEquivalent: "")
modeMenuItem.submenu = modeMenu
menu.addItem(modeMenuItem)
// 3. Hotkey Submenu
let hotkeyMenu = NSMenu()
for preset in HotkeyPreset.allCases {
let item = NSMenuItem(title: preset.rawValue, action: #selector(selectHotkeyAction(_:)), keyEquivalent: "")
item.target = self
item.representedObject = preset
item.state = (HotkeyManager.shared.currentPreset == preset) ? .on : .off
hotkeyMenu.addItem(item)
}
let hotkeyMenuItem = NSMenuItem(title: "⌨️ کلید میانبر (Hotkey)", action: nil, keyEquivalent: "")
hotkeyMenuItem.submenu = hotkeyMenu
menu.addItem(hotkeyMenuItem)
menu.addItem(NSMenuItem.separator())
// 4. Sound Effects Toggle
let soundsEnabled = UserDefaults.standard.bool(forKey: "SonioxPlaySounds")
let soundItem = NSMenuItem(title: "🔊 پخش افکت صوتی", action: #selector(toggleSoundsAction(_:)), keyEquivalent: "")
soundItem.target = self
soundItem.state = soundsEnabled ? .on : .off
menu.addItem(soundItem)
// 5. Accessibility Permission Check
let isAxTrusted = AXIsProcessTrusted()
let axTitle = isAxTrusted ? "✅ دسترسی Accessibility فعال است" : "🔑 اعطای دسترسی Accessibility..."
let axItem = NSMenuItem(title: axTitle, action: #selector(openAccessibilitySettings), keyEquivalent: "")
axItem.target = self
menu.addItem(axItem)
// 6. Launch at Login
let launchLogin = UserDefaults.standard.bool(forKey: "SonioxLaunchAtLogin")
let launchItem = NSMenuItem(title: "🚀 اجرا هنگام بالا آمدن سیستم", action: #selector(toggleLaunchAtLoginAction(_:)), keyEquivalent: "")
launchItem.target = self
launchItem.state = launchLogin ? .on : .off
menu.addItem(launchItem)
menu.addItem(NSMenuItem.separator())
// 7. About & Quit
let aboutItem = NSMenuItem(title: "ℹ️ درباره Soniox Voice", action: #selector(aboutAction), keyEquivalent: "")
aboutItem.target = self
menu.addItem(aboutItem)
let quitItem = NSMenuItem(title: "❌ خروج", action: #selector(quitAction), keyEquivalent: "q")
quitItem.target = self
menu.addItem(quitItem)
statusItem?.menu = menu
}
@objc private func toggleRecordAction() {
onToggleRecording?()
}
@objc private func selectModeAction(_ sender: NSMenuItem) {
if let mode = sender.representedObject as? DictationMode {
HotkeyManager.shared.currentMode = mode
buildMenu()
}
}
@objc private func selectHotkeyAction(_ sender: NSMenuItem) {
if let preset = sender.representedObject as? HotkeyPreset {
HotkeyManager.shared.currentPreset = preset
buildMenu()
}
}
@objc private func toggleSoundsAction(_ sender: NSMenuItem) {
let current = UserDefaults.standard.bool(forKey: "SonioxPlaySounds")
UserDefaults.standard.set(!current, forKey: "SonioxPlaySounds")
buildMenu()
}
@objc private func toggleLaunchAtLoginAction(_ sender: NSMenuItem) {
let current = UserDefaults.standard.bool(forKey: "SonioxLaunchAtLogin")
UserDefaults.standard.set(!current, forKey: "SonioxLaunchAtLogin")
buildMenu()
}
@objc private func openAccessibilitySettings() {
let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility")!
NSWorkspace.shared.open(url)
}
@objc private func aboutAction() {
let alert = NSAlert()
alert.messageText = "Soniox Voice v1.0"
alert.informativeText = "تبدیل بلادرنگ گفتار به متن فارسی و انگلیسی با موتور ابری فوق سریع Soniox.\n\nتوسعه یافته برای macOS."
alert.alertStyle = .informational
alert.addButton(withTitle: "باشه")
alert.runModal()
}
@objc private func quitAction() {
NSApplication.shared.terminate(nil)
}
}