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) } }